Progress
Report modal style fix, chat updates, statusserializer revert, display name truncation
This commit is contained in:
parent
7ec426e3d8
commit
67eb9d5890
@ -9,7 +9,6 @@ class Api::V1::Accounts::StatusesController < Api::BaseController
|
|||||||
@statuses = load_statuses
|
@statuses = load_statuses
|
||||||
render json: @statuses,
|
render json: @statuses,
|
||||||
each_serializer: REST::StatusSerializer,
|
each_serializer: REST::StatusSerializer,
|
||||||
account_id: params[:account_id],
|
|
||||||
relationships: StatusRelationshipsPresenter.new(@statuses, current_user&.account_id)
|
relationships: StatusRelationshipsPresenter.new(@statuses, current_user&.account_id)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -25,6 +25,7 @@ class Api::V1::ChatConversationController < Api::BaseController
|
|||||||
# check if chat blocked
|
# check if chat blocked
|
||||||
# check if allow anyone to message then create with approved:true
|
# check if allow anyone to message then create with approved:true
|
||||||
# unique account id, participants
|
# unique account id, participants
|
||||||
|
|
||||||
chat_conversation_account = find_or_create_conversation
|
chat_conversation_account = find_or_create_conversation
|
||||||
render json: chat_conversation_account, each_serializer: REST::ChatConversationAccountSerializer
|
render json: chat_conversation_account, each_serializer: REST::ChatConversationAccountSerializer
|
||||||
end
|
end
|
||||||
@ -67,7 +68,6 @@ class Api::V1::ChatConversationController < Api::BaseController
|
|||||||
else
|
else
|
||||||
@expires_at = nil
|
@expires_at = nil
|
||||||
end
|
end
|
||||||
puts "tilly @expires_at: " + @expires_at.inspect
|
|
||||||
@chat_conversation_account.chat_message_expiration_policy = @expires_at
|
@chat_conversation_account.chat_message_expiration_policy = @expires_at
|
||||||
@chat_conversation_account.save!
|
@chat_conversation_account.save!
|
||||||
render json: @chat_conversation_account, serializer: REST::ChatConversationAccountSerializer
|
render json: @chat_conversation_account, serializer: REST::ChatConversationAccountSerializer
|
||||||
|
@ -13,7 +13,7 @@ class Api::V1::Timelines::GroupController < Api::BaseController
|
|||||||
if current_user
|
if current_user
|
||||||
render json: @statuses,
|
render json: @statuses,
|
||||||
each_serializer: REST::StatusSerializer,
|
each_serializer: REST::StatusSerializer,
|
||||||
group_id: params[:id],
|
group_id: params[:id], # : todo :
|
||||||
relationships: StatusRelationshipsPresenter.new(@statuses, current_user.account_id, group_id: @group.id)
|
relationships: StatusRelationshipsPresenter.new(@statuses, current_user.account_id, group_id: @group.id)
|
||||||
else
|
else
|
||||||
render json: @statuses, each_serializer: REST::StatusSerializer
|
render json: @statuses, each_serializer: REST::StatusSerializer
|
||||||
|
@ -10,7 +10,7 @@ class Api::V1::Timelines::PreviewCardController < Api::BaseController
|
|||||||
def show
|
def show
|
||||||
render json: @statuses,
|
render json: @statuses,
|
||||||
each_serializer: REST::StatusSerializer,
|
each_serializer: REST::StatusSerializer,
|
||||||
preview_card_id: params[:id],
|
preview_card_id: params[:id], # : todo :
|
||||||
relationships: StatusRelationshipsPresenter.new(@statuses, current_user.account_id)
|
relationships: StatusRelationshipsPresenter.new(@statuses, current_user.account_id)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
20
app/controllers/api/web/chat_settings_controller.rb
Normal file
20
app/controllers/api/web/chat_settings_controller.rb
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class Api::Web::ChatSettingsController < Api::Web::BaseController
|
||||||
|
before_action :require_user!
|
||||||
|
|
||||||
|
def update
|
||||||
|
setting.data = params[:data]
|
||||||
|
setting.save!
|
||||||
|
|
||||||
|
# todo validate all data objects
|
||||||
|
|
||||||
|
render_empty_success
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def setting
|
||||||
|
@_setting ||= ::Web::Setting.where(user: current_user).first_or_initialize(user: current_user)
|
||||||
|
end
|
||||||
|
end
|
@ -162,7 +162,6 @@ export const followAccount = (id, reblogs = true) => (dispatch, getState) => {
|
|||||||
dispatch(followAccountRequest(id, locked))
|
dispatch(followAccountRequest(id, locked))
|
||||||
|
|
||||||
api(getState).post(`/api/v1/accounts/${id}/follow`, { reblogs }).then((response) => {
|
api(getState).post(`/api/v1/accounts/${id}/follow`, { reblogs }).then((response) => {
|
||||||
console.log("response:", response)
|
|
||||||
dispatch(followAccountSuccess(response.data, alreadyFollowing))
|
dispatch(followAccountSuccess(response.data, alreadyFollowing))
|
||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
dispatch(followAccountFail(error, locked))
|
dispatch(followAccountFail(error, locked))
|
||||||
|
@ -258,7 +258,6 @@ export const deleteChatConversation = (chatConversationId) => (dispatch, getStat
|
|||||||
dispatch(deleteChatConversationRequest(conversationId))
|
dispatch(deleteChatConversationRequest(conversationId))
|
||||||
|
|
||||||
api(getState).delete(`/api/v1/chat_conversation/${chatConversationId}`).then((response) => {
|
api(getState).delete(`/api/v1/chat_conversation/${chatConversationId}`).then((response) => {
|
||||||
console.log("chat_conversations delete response: ", response)
|
|
||||||
dispatch(deleteChatConversationSuccess())
|
dispatch(deleteChatConversationSuccess())
|
||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
dispatch(deleteChatConversationFail(error))
|
dispatch(deleteChatConversationFail(error))
|
||||||
|
@ -62,11 +62,9 @@ const sendChatMessageFail = (error) => ({
|
|||||||
export const manageIncomingChatMessage = (chatMessage) => (dispatch, getState) => {
|
export const manageIncomingChatMessage = (chatMessage) => (dispatch, getState) => {
|
||||||
if (!chatMessage) return
|
if (!chatMessage) return
|
||||||
|
|
||||||
console.log("chatMessage:", chatMessage)
|
|
||||||
dispatch(sendChatMessageSuccess(chatMessage))
|
dispatch(sendChatMessageSuccess(chatMessage))
|
||||||
|
|
||||||
const isOnline = getState().getIn(['chat_conversation_messages', chatMessage.chat_conversation_id, 'online'])
|
const isOnline = getState().getIn(['chat_conversation_messages', chatMessage.chat_conversation_id, 'online'])
|
||||||
console.log("isOnline: ", isOnline)
|
|
||||||
|
|
||||||
// : todo :
|
// : todo :
|
||||||
// Check if is online for conversation, if not increase total/convo unread count
|
// Check if is online for conversation, if not increase total/convo unread count
|
||||||
|
35
app/javascript/gabsocial/actions/chat_settings.js
Normal file
35
app/javascript/gabsocial/actions/chat_settings.js
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import api from '../api'
|
||||||
|
import debounce from 'lodash.debounce'
|
||||||
|
import { me } from '../initial_state'
|
||||||
|
|
||||||
|
export const CHAT_SETTING_CHANGE = 'CHAT_SETTING_CHANGE'
|
||||||
|
export const CHAT_SETTING_SAVE = 'CHAT_SETTING_SAVE'
|
||||||
|
|
||||||
|
export const changeChatSetting = (path, value) => (dispatch) => {
|
||||||
|
dispatch({
|
||||||
|
type: CHAT_SETTING_CHANGE,
|
||||||
|
path,
|
||||||
|
value,
|
||||||
|
})
|
||||||
|
|
||||||
|
dispatch(saveChatSettings())
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
export const saveChatSettings = () => (dispatch, getState) => {
|
||||||
|
debouncedChatSettingsSave(dispatch, getState)
|
||||||
|
}
|
||||||
|
|
||||||
|
const debouncedChatSettingsSave = debounce((dispatch, getState) => {
|
||||||
|
if (!me) return
|
||||||
|
|
||||||
|
if (getState().getIn(['chat_settings', 'saved'])) return
|
||||||
|
|
||||||
|
const data = getState().get('chat_settings').filter((_, path) => path !== 'saved').toJS()
|
||||||
|
|
||||||
|
api().put('/api/web/chat_settings', { data })
|
||||||
|
.then(() => dispatch({ type: CHAT_SETTING_SAVE }))
|
||||||
|
.catch(() => { /* */ })
|
||||||
|
}, 350, { trailing: true })
|
@ -151,7 +151,6 @@ export const expandTimeline = (timelineId, path, params = {}, done = noop) => (d
|
|||||||
dispatch(expandTimelineRequest(timelineId, isLoadingMore))
|
dispatch(expandTimelineRequest(timelineId, isLoadingMore))
|
||||||
|
|
||||||
api(getState).get(path, { params }).then((response) => {
|
api(getState).get(path, { params }).then((response) => {
|
||||||
console.log("response:", response)
|
|
||||||
const next = getLinks(response).refs.find(link => link.rel === 'next')
|
const next = getLinks(response).refs.find(link => link.rel === 'next')
|
||||||
dispatch(importFetchedStatuses(response.data))
|
dispatch(importFetchedStatuses(response.data))
|
||||||
dispatch(expandTimelineSuccess(timelineId, response.data, next ? next.uri : null, response.code === 206, isLoadingRecent, isLoadingMore))
|
dispatch(expandTimelineSuccess(timelineId, response.data, next ? next.uri : null, response.code === 206, isLoadingRecent, isLoadingMore))
|
||||||
|
@ -106,8 +106,7 @@ class DisplayName extends ImmutablePureComponent {
|
|||||||
const usernameClasses = CX({
|
const usernameClasses = CX({
|
||||||
text: 1,
|
text: 1,
|
||||||
displayFlex: 1,
|
displayFlex: 1,
|
||||||
flexNormal: 1,
|
flexShrink0: 1,
|
||||||
flexShrink1: 1,
|
|
||||||
overflowWrapBreakWord: 1,
|
overflowWrapBreakWord: 1,
|
||||||
textOverflowEllipsis: 1,
|
textOverflowEllipsis: 1,
|
||||||
cSecondary: 1,
|
cSecondary: 1,
|
||||||
@ -131,17 +130,10 @@ class DisplayName extends ImmutablePureComponent {
|
|||||||
const isFollowedBy = (me !== accountId && account.getIn(['relationship', 'followed_by']))
|
const isFollowedBy = (me !== accountId && account.getIn(['relationship', 'followed_by']))
|
||||||
|
|
||||||
if (isFollowedBy) {
|
if (isFollowedBy) {
|
||||||
relationshipLabel = 'Follows you'//intl.formatMessage(messages.accountFollowsYou)
|
relationshipLabel = 'Follows you'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// {
|
|
||||||
// /* : todo : audio-mute, bot
|
|
||||||
// account.getIn(['relationship', 'muting'])
|
|
||||||
// */
|
|
||||||
// }
|
|
||||||
// bot: { id: 'account.badges.bot', defaultMessage: 'Bot' },
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={containerClassName}
|
className={containerClassName}
|
||||||
@ -149,7 +141,7 @@ class DisplayName extends ImmutablePureComponent {
|
|||||||
onMouseLeave={noHover ? undefined : this.handleMouseLeave}
|
onMouseLeave={noHover ? undefined : this.handleMouseLeave}
|
||||||
ref={this.setRef}
|
ref={this.setRef}
|
||||||
>
|
>
|
||||||
<span className={[_s.d, _s.flexRow, _s.aiCenter, _s.maxW100PC].join(' ')}>
|
<span className={[_s.d, _s.flexRow, _s.aiCenter, _s.maxW100PC, _s.flexShrink1, _s.overflowHidden].join(' ')}>
|
||||||
<bdi className={[_s.text, _s.whiteSpaceNoWrap, _s.textOverflowEllipsis].join(' ')}>
|
<bdi className={[_s.text, _s.whiteSpaceNoWrap, _s.textOverflowEllipsis].join(' ')}>
|
||||||
<strong
|
<strong
|
||||||
className={displayNameClasses}
|
className={displayNameClasses}
|
||||||
|
@ -55,6 +55,7 @@ class ReportModal extends ImmutablePureComponent {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<ModalLayout
|
<ModalLayout
|
||||||
|
width={760}
|
||||||
noPadding
|
noPadding
|
||||||
title={intl.formatMessage(messages.target, {
|
title={intl.formatMessage(messages.target, {
|
||||||
target: account.get('acct')
|
target: account.get('acct')
|
||||||
@ -67,7 +68,7 @@ class ReportModal extends ImmutablePureComponent {
|
|||||||
classNamesSmall={[_s.d, _s.flexColumnReverse].join(' ')}
|
classNamesSmall={[_s.d, _s.flexColumnReverse].join(' ')}
|
||||||
>
|
>
|
||||||
<ResponsiveClassesComponent
|
<ResponsiveClassesComponent
|
||||||
classNames={[_s.d, _s.w50PC, _s.py10, _s.px15, _s.borderRight1PX, _s.borderColorSecondary].join(' ')}
|
classNames={[_s.d, _s.maxW320PX, _s.py10, _s.px15, _s.borderRight1PX, _s.borderColorSecondary].join(' ')}
|
||||||
classNamesSmall={[_s.d, _s.w100PC, _s.py10, _s.px15, _s.borderTop1PX, _s.borderColorSecondary].join(' ')}
|
classNamesSmall={[_s.d, _s.w100PC, _s.py10, _s.px15, _s.borderTop1PX, _s.borderColorSecondary].join(' ')}
|
||||||
>
|
>
|
||||||
<Text color='secondary' size='small'>
|
<Text color='secondary' size='small'>
|
||||||
@ -95,13 +96,13 @@ class ReportModal extends ImmutablePureComponent {
|
|||||||
</ResponsiveClassesComponent>
|
</ResponsiveClassesComponent>
|
||||||
|
|
||||||
<ResponsiveClassesComponent
|
<ResponsiveClassesComponent
|
||||||
classNames={[_s.d, _s.w50PC, _s.maxH80VH].join(' ')}
|
classNames={[_s.d, _s.flexNormal, _s.maxH80VH].join(' ')}
|
||||||
classNamesSmall={[_s.d, _s.w100PC, _s.h260PX].join(' ')}
|
classNamesSmall={[_s.d, _s.w100PC, _s.h260PX].join(' ')}
|
||||||
>
|
>
|
||||||
<div className={[_s.d, _s.h100PC, _s.overflowYScroll, _s.pr15, _s.py10].join(' ')}>
|
<div className={[_s.d, _s.h100PC, _s.overflowYScroll].join(' ')}>
|
||||||
{
|
{
|
||||||
statusIds.map(statusId => (
|
statusIds.map((statusId) => (
|
||||||
<StatusCheckBox id={statusId} key={statusId} disabled={isSubmitting} />
|
<StatusCheckBox id={statusId} key={`reporting-${statusId}`} disabled={isSubmitting} />
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
@ -33,6 +33,9 @@ class ChatNavigationBar extends React.PureComponent {
|
|||||||
const otherAccounts = chatConversation ? chatConversation.get('other_accounts') : null
|
const otherAccounts = chatConversation ? chatConversation.get('other_accounts') : null
|
||||||
const nameHTML = !!otherAccounts ? otherAccounts.get(0).get('display_name_html') : ''
|
const nameHTML = !!otherAccounts ? otherAccounts.get(0).get('display_name_html') : ''
|
||||||
|
|
||||||
|
// : todo :
|
||||||
|
// fix padding on mobile device
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={[_s.d, _s.z4, _s.h53PX, _s.w100PC].join(' ')}>
|
<div className={[_s.d, _s.z4, _s.h53PX, _s.w100PC].join(' ')}>
|
||||||
<div className={[_s.d, _s.h53PX, _s.bgNavigation, _s.aiCenter, _s.z3, _s.top0, _s.right0, _s.left0, _s.posFixed].join(' ')} >
|
<div className={[_s.d, _s.h53PX, _s.bgNavigation, _s.aiCenter, _s.z3, _s.top0, _s.right0, _s.left0, _s.posFixed].join(' ')} >
|
||||||
@ -46,23 +49,29 @@ class ChatNavigationBar extends React.PureComponent {
|
|||||||
iconClassName={[_s.mr5, _s.fillNavigation].join(' ')}
|
iconClassName={[_s.mr5, _s.fillNavigation].join(' ')}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className={[_s.d, _s.h53PX, _s.flexRow, _s.jcCenter, _s.aiCenter, _s.mrAuto].join(' ')}>
|
<div className={[_s.d, _s.minH53PX, _s.flexRow, _s.aiCenter, _s.mrAuto, _s.flex1, _s.overflowHidden].join(' ')}>
|
||||||
<AvatarGroup accounts={otherAccounts} size={35} noHover />
|
<div className={[_s.d, _s.minH53PX, _s.jcCenter, _s.w100PC, _s.flexShrink1].join(' ')}>
|
||||||
<Heading size='h1'>
|
<Heading size='h1'>
|
||||||
<div className={[_s.dangerousContent, _s.colorNavigation, _s.pl10, _s.fs19PX].join(' ')} dangerouslySetInnerHTML={{ __html: nameHTML }} />
|
<div className={[_s.d, _s.w100PC].join(' ')}>
|
||||||
</Heading>
|
<span
|
||||||
|
className={[_s.w100PC, _s.textOverflowEllipsis, _s.colorNavigation].join(' ')}
|
||||||
|
dangerouslySetInnerHTML={{ __html: nameHTML }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Heading>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={[_s.d, _s.h53PX, _s.mlAuto, _s.aiCenter, _s.jcCenter, _s.mr15].join(' ')}>
|
<div className={[_s.d, _s.h53PX, _s.mlAuto, _s.aiCenter, _s.jcCenter, _s.mr15].join(' ')}>
|
||||||
<Button
|
<Button
|
||||||
isNarrow
|
isNarrow
|
||||||
backgroundColor='tertiary'
|
backgroundColor='none'
|
||||||
color='primary'
|
color='primary'
|
||||||
onClick={this.handleOnOpenChatConversationOptionsPopover}
|
onClick={this.handleOnOpenChatConversationOptionsPopover}
|
||||||
className={[_s.px5].join(' ')}
|
className={[_s.px5].join(' ')}
|
||||||
icon='ellipsis'
|
icon='ellipsis'
|
||||||
iconClassName={[_s.cSecondary, _s.px5, _s.py5].join(' ')}
|
iconClassName={[_s.colorNavigation, _s.px5, _s.py5].join(' ')}
|
||||||
iconSize='15px'
|
iconSize='26px'
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -24,6 +24,9 @@ class ProfileNavigationBar extends React.PureComponent {
|
|||||||
render() {
|
render() {
|
||||||
const { titleHTML } = this.props
|
const { titleHTML } = this.props
|
||||||
|
|
||||||
|
// : todo :
|
||||||
|
// fix padding on mobile device
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={[_s.d, _s.z4, _s.minH53PX, _s.w100PC].join(' ')}>
|
<div className={[_s.d, _s.z4, _s.minH53PX, _s.w100PC].join(' ')}>
|
||||||
<div className={[_s.d, _s.minH53PX, _s.bgNavigation, _s.aiCenter, _s.z3, _s.top0, _s.right0, _s.left0, _s.posFixed].join(' ')} >
|
<div className={[_s.d, _s.minH53PX, _s.bgNavigation, _s.aiCenter, _s.z3, _s.top0, _s.right0, _s.left0, _s.posFixed].join(' ')} >
|
||||||
@ -36,12 +39,17 @@ class ProfileNavigationBar extends React.PureComponent {
|
|||||||
iconClassName={[_s.mr5, _s.fillNavigation].join(' ')}
|
iconClassName={[_s.mr5, _s.fillNavigation].join(' ')}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className={[_s.d, _s.minH53PX, _s.jcCenter, _s.mrAuto].join(' ')}>
|
<div className={[_s.d, _s.minH53PX, _s.mrAuto, _s.flex1, _s.overflowHidden].join(' ')}>
|
||||||
<Heading size='h1'>
|
<div className={[_s.d, _s.minH53PX, _s.jcCenter, _s.w100PC].join(' ')}>
|
||||||
<span className={[_s.textOverflowEllipsis, _s.colorNavigation].join(' ')}>
|
<Heading size='h1'>
|
||||||
<div dangerouslySetInnerHTML={{ __html: titleHTML }} />
|
<div className={[_s.d, _s.w100PC].join(' ')}>
|
||||||
</span>
|
<span
|
||||||
</Heading>
|
className={[_s.w100PC, _s.textOverflowEllipsis, _s.colorNavigation].join(' ')}
|
||||||
|
dangerouslySetInnerHTML={{ __html: titleHTML }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Heading>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={[_s.d, _s.minH53PX, _s.jcCenter, _s.mr15].join(' ')}>
|
<div className={[_s.d, _s.minH53PX, _s.jcCenter, _s.mr15].join(' ')}>
|
||||||
|
@ -35,7 +35,6 @@ class ChatConversationExpirationOptionsPopover extends React.PureComponent {
|
|||||||
isXS,
|
isXS,
|
||||||
} = this.props
|
} = this.props
|
||||||
|
|
||||||
console.log("expiresAtValue:", expiresAtValue)
|
|
||||||
if (!chatConversationId) return <div/>
|
if (!chatConversationId) return <div/>
|
||||||
|
|
||||||
const listItems = [
|
const listItems = [
|
||||||
|
@ -6,6 +6,7 @@ import { connect } from 'react-redux'
|
|||||||
import { closePopover } from '../../actions/popover'
|
import { closePopover } from '../../actions/popover'
|
||||||
import { openModal } from '../../actions/modal'
|
import { openModal } from '../../actions/modal'
|
||||||
import { hideChatConversation } from '../../actions/chat_conversations'
|
import { hideChatConversation } from '../../actions/chat_conversations'
|
||||||
|
import { setChatConversationSelected } from '../../actions/chats'
|
||||||
import {
|
import {
|
||||||
muteChatConversation,
|
muteChatConversation,
|
||||||
unmuteChatConversation,
|
unmuteChatConversation,
|
||||||
@ -107,6 +108,7 @@ const mapDispatchToProps = (dispatch, { chatConversationId }) => ({
|
|||||||
},
|
},
|
||||||
onHide() {
|
onHide() {
|
||||||
dispatch(hideChatConversation(chatConversationId))
|
dispatch(hideChatConversation(chatConversationId))
|
||||||
|
dispatch(setChatConversationSelected(null))
|
||||||
},
|
},
|
||||||
onMute() {
|
onMute() {
|
||||||
dispatch(muteChatConversation(chatConversationId))
|
dispatch(muteChatConversation(chatConversationId))
|
||||||
|
@ -0,0 +1,54 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
import PopoverLayout from './popover_layout'
|
||||||
|
import List from '../list'
|
||||||
|
|
||||||
|
class ChatSettingsPopover extends React.PureComponent {
|
||||||
|
|
||||||
|
handleOnClosePopover = () => {
|
||||||
|
this.props.onClose()
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { intl, isXS } = this.props
|
||||||
|
|
||||||
|
return (
|
||||||
|
<PopoverLayout width={240} isXS={isXS}>
|
||||||
|
<List
|
||||||
|
size={isXS ? 'large' : 'small'}
|
||||||
|
scrollKey='profile_options'
|
||||||
|
items={[
|
||||||
|
{
|
||||||
|
title: 'Preferences',
|
||||||
|
to: '/messages/settings',
|
||||||
|
onClick: () => this.handleOnClosePopover(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Message Requests',
|
||||||
|
to: '/messages/requests',
|
||||||
|
onClick: () => this.handleOnClosePopover(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Blocked Chat Messengers',
|
||||||
|
to: '/messages/blocks',
|
||||||
|
onClick: () => this.handleOnClosePopover(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Muted Conversations',
|
||||||
|
to: '/messages/muted_conversations',
|
||||||
|
onClick: () => this.handleOnClosePopover(),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</PopoverLayout>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ChatSettingsPopover.propTypes = {
|
||||||
|
onClose: PropTypes.func.isRequired,
|
||||||
|
isXS: PropTypes.bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ChatSettingsPopover
|
@ -70,7 +70,7 @@ class DatePickerPopover extends React.PureComponent {
|
|||||||
<div className={[_s.d, _s.aiCenter, _s.flexRow, _s.px10, _s.py10, _s.borderTop1PX, _s.borderColorSecondary].join(' ')}>
|
<div className={[_s.d, _s.aiCenter, _s.flexRow, _s.px10, _s.py10, _s.borderTop1PX, _s.borderColorSecondary].join(' ')}>
|
||||||
<Text size='extraSmall' color='secondary'>
|
<Text size='extraSmall' color='secondary'>
|
||||||
<FormattedMessage id='scheduled_for_datetime' defaultMessage='Scheduled for {datetime}' values={{
|
<FormattedMessage id='scheduled_for_datetime' defaultMessage='Scheduled for {datetime}' values={{
|
||||||
datetime: moment.utc(date).format('lll'),
|
datetime: moment(date).format('lll'),
|
||||||
}}/>
|
}}/>
|
||||||
</Text>
|
</Text>
|
||||||
<div className={_s.mlAuto}>
|
<div className={_s.mlAuto}>
|
||||||
|
@ -3,6 +3,7 @@ import {
|
|||||||
POPOVER_CHAT_CONVERSATION_EXPIRATION_OPTIONS,
|
POPOVER_CHAT_CONVERSATION_EXPIRATION_OPTIONS,
|
||||||
POPOVER_CHAT_CONVERSATION_OPTIONS,
|
POPOVER_CHAT_CONVERSATION_OPTIONS,
|
||||||
POPOVER_CHAT_MESSAGE_OPTIONS,
|
POPOVER_CHAT_MESSAGE_OPTIONS,
|
||||||
|
POPOVER_CHAT_SETTINGS,
|
||||||
POPOVER_COMMENT_SORTING_OPTIONS,
|
POPOVER_COMMENT_SORTING_OPTIONS,
|
||||||
POPOVER_COMPOSE_POST_DESTINATION,
|
POPOVER_COMPOSE_POST_DESTINATION,
|
||||||
POPOVER_DATE_PICKER,
|
POPOVER_DATE_PICKER,
|
||||||
@ -27,6 +28,7 @@ import {
|
|||||||
ChatConversationExpirationOptionsPopover,
|
ChatConversationExpirationOptionsPopover,
|
||||||
ChatConversationOptionsPopover,
|
ChatConversationOptionsPopover,
|
||||||
ChatMessageOptionsPopover,
|
ChatMessageOptionsPopover,
|
||||||
|
ChatSettingsPopover,
|
||||||
CommentSortingOptionsPopover,
|
CommentSortingOptionsPopover,
|
||||||
ComposePostDesinationPopover,
|
ComposePostDesinationPopover,
|
||||||
DatePickerPopover,
|
DatePickerPopover,
|
||||||
@ -65,6 +67,7 @@ const POPOVER_COMPONENTS = {
|
|||||||
[POPOVER_CHAT_CONVERSATION_EXPIRATION_OPTIONS]: ChatConversationExpirationOptionsPopover,
|
[POPOVER_CHAT_CONVERSATION_EXPIRATION_OPTIONS]: ChatConversationExpirationOptionsPopover,
|
||||||
[POPOVER_CHAT_CONVERSATION_OPTIONS]: ChatConversationOptionsPopover,
|
[POPOVER_CHAT_CONVERSATION_OPTIONS]: ChatConversationOptionsPopover,
|
||||||
[POPOVER_CHAT_MESSAGE_OPTIONS]: ChatMessageOptionsPopover,
|
[POPOVER_CHAT_MESSAGE_OPTIONS]: ChatMessageOptionsPopover,
|
||||||
|
[POPOVER_CHAT_SETTINGS]: ChatSettingsPopover,
|
||||||
[POPOVER_COMMENT_SORTING_OPTIONS]: CommentSortingOptionsPopover,
|
[POPOVER_COMMENT_SORTING_OPTIONS]: CommentSortingOptionsPopover,
|
||||||
[POPOVER_COMPOSE_POST_DESTINATION]: ComposePostDesinationPopover,
|
[POPOVER_COMPOSE_POST_DESTINATION]: ComposePostDesinationPopover,
|
||||||
[POPOVER_DATE_PICKER]: DatePickerPopover,
|
[POPOVER_DATE_PICKER]: DatePickerPopover,
|
||||||
@ -161,7 +164,7 @@ class PopoverRoot extends React.PureComponent {
|
|||||||
renderDelay={150}
|
renderDelay={150}
|
||||||
>
|
>
|
||||||
{
|
{
|
||||||
(Component) => <Component innerRef={this.setRef} isXS={isXS} {...props} />
|
(Component) => <Component innerRef={this.setRef} isXS={isXS} onClose={onClose} {...props} />
|
||||||
}
|
}
|
||||||
</Bundle>
|
</Bundle>
|
||||||
}
|
}
|
||||||
|
@ -310,12 +310,15 @@ class Status extends ImmutablePureComponent {
|
|||||||
commentSortingType,
|
commentSortingType,
|
||||||
onOpenProModal,
|
onOpenProModal,
|
||||||
isDeckConnected,
|
isDeckConnected,
|
||||||
|
statusId,
|
||||||
} = this.props
|
} = this.props
|
||||||
// const { height } = this.state
|
// const { height } = this.state
|
||||||
|
|
||||||
let { status } = this.props
|
let { status } = this.props
|
||||||
|
|
||||||
if (!status) return null
|
if (!status) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
if (isComment && !ancestorStatus && !isChild) {
|
if (isComment && !ancestorStatus && !isChild) {
|
||||||
// Wait to load...
|
// Wait to load...
|
||||||
@ -331,7 +334,7 @@ class Status extends ImmutablePureComponent {
|
|||||||
if (ancestorStatus) {
|
if (ancestorStatus) {
|
||||||
status = ancestorStatus
|
status = ancestorStatus
|
||||||
} else {
|
} else {
|
||||||
if (status.get('reblog', null) !== null && typeof status.get('reblog') === 'object') {
|
if (status.get('reblog', null) !== null) {
|
||||||
rebloggedByText = intl.formatMessage(
|
rebloggedByText = intl.formatMessage(
|
||||||
{ id: 'status.reposted_by', defaultMessage: '{name} reposted' },
|
{ id: 'status.reposted_by', defaultMessage: '{name} reposted' },
|
||||||
{ name: status.getIn(['account', 'acct']) }
|
{ name: status.getIn(['account', 'acct']) }
|
||||||
|
@ -46,17 +46,27 @@ class StatusCheckBox extends ImmutablePureComponent {
|
|||||||
} else {
|
} else {
|
||||||
media = (
|
media = (
|
||||||
<Bundle fetchComponent={MediaGallery} loading={this.renderLoadingMediaGallery} >
|
<Bundle fetchComponent={MediaGallery} loading={this.renderLoadingMediaGallery} >
|
||||||
{Component => <Component media={status.get('media_attachments')} sensitive={status.get('sensitive')} onOpenMedia={noop} />}
|
{Component => (
|
||||||
|
<Component
|
||||||
|
media={status.get('media_attachments')}
|
||||||
|
sensitive={status.get('sensitive')}
|
||||||
|
onOpenMedia={noop}
|
||||||
|
width={239}
|
||||||
|
height={110}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</Bundle>
|
</Bundle>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={[_s.d, _s.flexRow].join(' ')}>
|
<div className={[_s.d, _s.flexRow, _s.flexWrap, _s.borderBottom1PX, _s.borderColorSecondary, _s.aiStart, _s.mb5, _s.pr15, _s.pt5, _s.w100PC].join(' ')}>
|
||||||
<div className={[_s.d].join(' ')}>
|
<div className={[_s.d, _s.pt5, _s.maxW100PC].join(' ')}>
|
||||||
<StatusContent status={status} />
|
<StatusContent status={status} />
|
||||||
{media}
|
<div className={_s.pl15}>
|
||||||
|
{media}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={[_s.d, _s.mlAuto].join(' ')}>
|
<div className={[_s.d, _s.mlAuto].join(' ')}>
|
||||||
|
@ -79,7 +79,8 @@ class StatusHeader extends ImmutablePureComponent {
|
|||||||
const textContainerClasses = CX({
|
const textContainerClasses = CX({
|
||||||
d: 1,
|
d: 1,
|
||||||
aiStart: 1,
|
aiStart: 1,
|
||||||
flexGrow1 :1,
|
flex1: 1,
|
||||||
|
overflowHidden: 1,
|
||||||
mt5: !isCompact,
|
mt5: !isCompact,
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -100,13 +101,15 @@ class StatusHeader extends ImmutablePureComponent {
|
|||||||
|
|
||||||
<div className={textContainerClasses}>
|
<div className={textContainerClasses}>
|
||||||
|
|
||||||
<div className={[_s.d, _s.flexRow, _s.w100PC, _s.aiStart].join(' ')}>
|
<div className={[_s.d, _s.flexRow, _s.w100PC, _s.aiStart, _s.overflowHidden].join(' ')}>
|
||||||
<NavLink
|
<NavLink
|
||||||
className={[_s.d, _s.flexRow, _s.aiStart, _s.noUnderline].join(' ')}
|
className={[_s.d, _s.flexRow, _s.aiStart, _s.noUnderline, _s.flex1, _s.maxW100PC30PX].join(' ')}
|
||||||
to={`/${status.getIn(['account', 'acct'])}`}
|
to={`/${status.getIn(['account', 'acct'])}`}
|
||||||
title={status.getIn(['account', 'acct'])}
|
title={status.getIn(['account', 'acct'])}
|
||||||
>
|
>
|
||||||
<DisplayName account={status.get('account')} noRelationship />
|
<div className={[_s.d, _s.w100PC, _s.overflowHidden].join(' ')}>
|
||||||
|
<DisplayName account={status.get('account')} noRelationship />
|
||||||
|
</div>
|
||||||
</NavLink>
|
</NavLink>
|
||||||
|
|
||||||
{
|
{
|
||||||
@ -118,7 +121,7 @@ class StatusHeader extends ImmutablePureComponent {
|
|||||||
icon='ellipsis'
|
icon='ellipsis'
|
||||||
iconSize='20px'
|
iconSize='20px'
|
||||||
iconClassName={_s.cSecondary}
|
iconClassName={_s.cSecondary}
|
||||||
className={_s.mlAuto}
|
className={[_s.mlAuto].join(' ')}
|
||||||
onClick={this.handleOpenStatusOptionsPopover}
|
onClick={this.handleOpenStatusOptionsPopover}
|
||||||
buttonRef={this.setStatusOptionsButton}
|
buttonRef={this.setStatusOptionsButton}
|
||||||
/>
|
/>
|
||||||
|
@ -44,7 +44,7 @@ class UserSuggestionsInjection extends ImmutablePureComponent {
|
|||||||
id={injectionId}
|
id={injectionId}
|
||||||
title={title}
|
title={title}
|
||||||
buttonLink='/suggestions'
|
buttonLink='/suggestions'
|
||||||
buttonTitle='See more reccomendations'
|
buttonTitle='See more recommendations'
|
||||||
isXS={isXS}
|
isXS={isXS}
|
||||||
>
|
>
|
||||||
{
|
{
|
||||||
|
@ -27,6 +27,7 @@ export const PLACEHOLDER_MISSING_HEADER_SRC = '/original/missing.png'
|
|||||||
export const POPOVER_CHAT_CONVERSATION_OPTIONS = 'CHAT_CONVERSATION_OPTIONS'
|
export const POPOVER_CHAT_CONVERSATION_OPTIONS = 'CHAT_CONVERSATION_OPTIONS'
|
||||||
export const POPOVER_CHAT_MESSAGE_OPTIONS = 'CHAT_MESSAGE_OPTIONS'
|
export const POPOVER_CHAT_MESSAGE_OPTIONS = 'CHAT_MESSAGE_OPTIONS'
|
||||||
export const POPOVER_CHAT_CONVERSATION_EXPIRATION_OPTIONS = 'CHAT_CONVERSATION_EXPIRATION_OPTIONS'
|
export const POPOVER_CHAT_CONVERSATION_EXPIRATION_OPTIONS = 'CHAT_CONVERSATION_EXPIRATION_OPTIONS'
|
||||||
|
export const POPOVER_CHAT_SETTINGS = 'CHAT_SETTINGS'
|
||||||
export const POPOVER_COMMENT_SORTING_OPTIONS = 'COMMENT_SORTING_OPTIONS'
|
export const POPOVER_COMMENT_SORTING_OPTIONS = 'COMMENT_SORTING_OPTIONS'
|
||||||
export const POPOVER_COMPOSE_POST_DESTINATION = 'COMPOSE_POST_DESTINATION'
|
export const POPOVER_COMPOSE_POST_DESTINATION = 'COMPOSE_POST_DESTINATION'
|
||||||
export const POPOVER_DATE_PICKER = 'DATE_PICKER'
|
export const POPOVER_DATE_PICKER = 'DATE_PICKER'
|
||||||
|
@ -37,31 +37,33 @@ class ChatConversationCreate extends React.PureComponent {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Form>
|
<Form>
|
||||||
<div className={[_s.d, _s.px15, _s.pt10].join(' ')}>
|
<div className={[_s.d, _s.bgPrimary, _s.w100PC, _s.borderBottom1PX, _s.borderColorSecondary].join(' ')}>
|
||||||
<Input
|
<div className={[_s.d, _s.px15, _s.pt10].join(' ')}>
|
||||||
title='Search for a user'
|
<Input
|
||||||
value={query}
|
title='Search for a user'
|
||||||
onChange={this.onChange}
|
value={query}
|
||||||
/>
|
onChange={this.onChange}
|
||||||
</div>
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className={[_s.d, _s.pt10].join(' ')}>
|
<div className={[_s.d, _s.pt10].join(' ')}>
|
||||||
<div className={[_s.d].join(' ')}>
|
<div className={[_s.d].join(' ')}>
|
||||||
<Text weight='bold' size='small' color='secondary' className={[_s.d, _s.px15, _s.ml15, _s.mt5, _s.mb15].join(' ')}>
|
<Text weight='bold' size='small' color='secondary' className={[_s.d, _s.px15, _s.ml15, _s.mt5, _s.mb15].join(' ')}>
|
||||||
Search results ({suggestionsIds.size})
|
Search results ({suggestionsIds.size})
|
||||||
</Text>
|
</Text>
|
||||||
{
|
{
|
||||||
suggestionsIds &&
|
suggestionsIds &&
|
||||||
suggestionsIds.map((accountId) => (
|
suggestionsIds.map((accountId) => (
|
||||||
<Account
|
<Account
|
||||||
compact
|
compact
|
||||||
key={`chat-conversation-account-create-${accountId}`}
|
key={`chat-conversation-account-create-${accountId}`}
|
||||||
id={accountId}
|
id={accountId}
|
||||||
onActionClick={() => this.handleOnCreateChatConversation(accountId)}
|
onActionClick={() => this.handleOnCreateChatConversation(accountId)}
|
||||||
actionIcon='add'
|
actionIcon='add'
|
||||||
/>
|
/>
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Form>
|
</Form>
|
||||||
|
21
app/javascript/gabsocial/features/chat_conversation_mutes.js
Normal file
21
app/javascript/gabsocial/features/chat_conversation_mutes.js
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
import BlockHeading from '../components/block_heading'
|
||||||
|
import ChatConversationsList from './messages/components/chat_conversations_list'
|
||||||
|
|
||||||
|
class ChatConversationMutes extends React.PureComponent {
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div className={[_s.d, _s.w100PC, _s.boxShadowNone].join(' ')}>
|
||||||
|
<div className={[_s.d, _s.h60PX, _s.w100PC, _s.px10, _s.py10, _s.borderBottom1PX, _s.borderColorSecondary].join(' ')}>
|
||||||
|
<BlockHeading title={'Muted Chat Conversations'} />
|
||||||
|
</div>
|
||||||
|
<ChatConversationsList source='mutes' />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ChatConversationMutes
|
@ -62,23 +62,20 @@ class GroupMembers extends ImmutablePureComponent {
|
|||||||
<Block>
|
<Block>
|
||||||
<BlockHeading title='Group Members' />
|
<BlockHeading title='Group Members' />
|
||||||
|
|
||||||
{
|
<div className={[_s.d, _s.jcCenter, _s.px15, _s.my5, _s.borderBottom1PX, _s.borderColorSecondary, _s.pt5, _s.pb15].join(' ')}>
|
||||||
/* : todo :
|
<Input
|
||||||
<div className={[_s.d, _s.jcCenter, _s.px15, _s.my5, _s.borderBottom1PX, _s.borderColorSecondary, _s.pt5, _s.pb15].join(' ')}>
|
id='group-member-search'
|
||||||
<Input
|
placeholder='Search group members'
|
||||||
id='group-member-search'
|
prependIcon='search'
|
||||||
placeholder='Search group members'
|
// value={value}
|
||||||
prependIcon='search'
|
onKeyUp={this.handleKeyUp}
|
||||||
// value={value}
|
onChange={this.handleOnChange}
|
||||||
onKeyUp={this.handleKeyUp}
|
onFocus={this.handleOnFocus}
|
||||||
onChange={this.handleOnChange}
|
onBlur={this.handleOnBlur}
|
||||||
onFocus={this.handleOnFocus}
|
autoComplete='off'
|
||||||
onBlur={this.handleOnBlur}
|
/>
|
||||||
autoComplete='off'
|
</div>
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
<div className={[_s.d].join(' ')}>
|
<div className={[_s.d].join(' ')}>
|
||||||
<ScrollableList
|
<ScrollableList
|
||||||
scrollKey='group-members'
|
scrollKey='group-members'
|
||||||
|
@ -14,6 +14,7 @@ import {
|
|||||||
import { FormattedMessage } from 'react-intl'
|
import { FormattedMessage } from 'react-intl'
|
||||||
import Account from '../components/account'
|
import Account from '../components/account'
|
||||||
import Block from '../components/block'
|
import Block from '../components/block'
|
||||||
|
import Input from '../components/input'
|
||||||
import BlockHeading from '../components/block_heading'
|
import BlockHeading from '../components/block_heading'
|
||||||
import ColumnIndicator from '../components/column_indicator'
|
import ColumnIndicator from '../components/column_indicator'
|
||||||
import ScrollableList from '../components/scrollable_list'
|
import ScrollableList from '../components/scrollable_list'
|
||||||
@ -49,6 +50,19 @@ class GroupRemovedAccounts extends ImmutablePureComponent {
|
|||||||
return (
|
return (
|
||||||
<Block>
|
<Block>
|
||||||
<BlockHeading title='Removed Accounts' />
|
<BlockHeading title='Removed Accounts' />
|
||||||
|
<div className={[_s.d, _s.jcCenter, _s.px15, _s.my5, _s.borderBottom1PX, _s.borderColorSecondary, _s.pt5, _s.pb15].join(' ')}>
|
||||||
|
<Input
|
||||||
|
id='group-member-search'
|
||||||
|
placeholder='Search removed group members'
|
||||||
|
prependIcon='search'
|
||||||
|
// value={value}
|
||||||
|
onKeyUp={this.handleKeyUp}
|
||||||
|
onChange={this.handleOnChange}
|
||||||
|
onFocus={this.handleOnFocus}
|
||||||
|
onBlur={this.handleOnBlur}
|
||||||
|
autoComplete='off'
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<ScrollableList
|
<ScrollableList
|
||||||
scrollKey='removed_accounts'
|
scrollKey='removed_accounts'
|
||||||
hasMore={hasMore}
|
hasMore={hasMore}
|
||||||
|
@ -27,6 +27,8 @@ class ChatMessagesComposeForm extends React.PureComponent {
|
|||||||
|
|
||||||
handleOnSendChatMessage = () => {
|
handleOnSendChatMessage = () => {
|
||||||
this.props.onSendChatMessage(this.state.value, this.props.chatConversationId)
|
this.props.onSendChatMessage(this.state.value, this.props.chatConversationId)
|
||||||
|
document.querySelector('#gabsocial').focus()
|
||||||
|
this.onBlur()
|
||||||
this.setState({ value: '' })
|
this.setState({ value: '' })
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -93,7 +95,7 @@ class ChatMessagesComposeForm extends React.PureComponent {
|
|||||||
expiresAtValue,
|
expiresAtValue,
|
||||||
chatConversationId,
|
chatConversationId,
|
||||||
} = this.props
|
} = this.props
|
||||||
const { value } = this.state
|
const { focused, value } = this.state
|
||||||
const disabled = false
|
const disabled = false
|
||||||
|
|
||||||
const textareaClasses = CX({
|
const textareaClasses = CX({
|
||||||
@ -141,6 +143,7 @@ class ChatMessagesComposeForm extends React.PureComponent {
|
|||||||
onFocus={this.onFocus}
|
onFocus={this.onFocus}
|
||||||
onBlur={this.onBlur}
|
onBlur={this.onBlur}
|
||||||
onKeyDown={this.onKeyDown}
|
onKeyDown={this.onKeyDown}
|
||||||
|
focused={focused}
|
||||||
aria-autocomplete='list'
|
aria-autocomplete='list'
|
||||||
maxLength={1600}
|
maxLength={1600}
|
||||||
/>
|
/>
|
||||||
@ -170,11 +173,11 @@ class ChatMessagesComposeForm extends React.PureComponent {
|
|||||||
|
|
||||||
if (isXS) {
|
if (isXS) {
|
||||||
return (
|
return (
|
||||||
<div className={[_s.d, _s.z4, _s.minH58PX, _s.w100PC].join(' ')}>
|
<div className={[_s.d, _s.z4, _s.minH58PX, _s.w100PC, _s.mtAuto].join(' ')}>
|
||||||
<div className={[_s.d, _s.minH58PX, _s.bgPrimary, _s.aiCenter, _s.z3, _s.bottom0, _s.right0, _s.left0, _s.posFixed].join(' ')} >
|
<div className={[_s.d, _s.minH58PX, _s.bgPrimary, _s.aiCenter, _s.z3, _s.bottom0, _s.right0, _s.left0, _s.posFixed].join(' ')} >
|
||||||
<div className={[_s.d, _s.w100PC, _s.pb5, _s.px15, _s.aiCenter, _s.jcCenter, _s.saveAreaInsetPB, _s.saveAreaInsetPL, _s.saveAreaInsetPR, _s.w100PC].join(' ')}>
|
<div className={[_s.d, _s.w100PC, _s.pb5, _s.px15, _s.aiCenter, _s.jcCenter, _s.saveAreaInsetPB, _s.saveAreaInsetPL, _s.saveAreaInsetPR, _s.w100PC].join(' ')}>
|
||||||
<div className={[_s.d, _s.flexRow, _s.aiCenter, _s.minH58PX, _s.w100PC, _s.borderTop1PX, _s.borderColorSecondary, _s.px10].join(' ')}>
|
<div className={[_s.d, _s.flexRow, _s.aiCenter, _s.minH58PX, _s.w100PC, _s.borderTop1PX, _s.borderColorSecondary, _s.px10].join(' ')}>
|
||||||
<div className={[_s.d, _s.flexRow, _s.radiusRounded, _s.border1PX, _s.borderColorSecondary, _s.overflowHidden].join(' ')}>
|
<div className={[_s.d, _s.flexRow, _s.flexGrow1, _s.radiusRounded, _s.border1PX, _s.borderColorSecondary, _s.overflowHidden].join(' ')}>
|
||||||
<div className={_s.d}>
|
<div className={_s.d}>
|
||||||
{expiresBtn}
|
{expiresBtn}
|
||||||
</div>
|
</div>
|
||||||
@ -182,7 +185,7 @@ class ChatMessagesComposeForm extends React.PureComponent {
|
|||||||
{textarea}
|
{textarea}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className={[_s.d, _s.h100PC, _s.aiCenter, _s.jcCenter].join(' ')}>
|
<div className={[_s.d, _s.pl10, _s.h100PC, _s.aiCenter, _s.jcCenter].join(' ')}>
|
||||||
{button}
|
{button}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -39,6 +39,7 @@ class ChatMessageScrollingList extends ImmutablePureComponent {
|
|||||||
componentDidMount () {
|
componentDidMount () {
|
||||||
const { chatConversationId } = this.props
|
const { chatConversationId } = this.props
|
||||||
this.props.onExpandChatMessages(chatConversationId)
|
this.props.onExpandChatMessages(chatConversationId)
|
||||||
|
this.scrollToBottom()
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
@ -63,10 +64,10 @@ class ChatMessageScrollingList extends ImmutablePureComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (prevProps.chatMessageIds.size === 0 && this.props.chatMessageIds.size > 0 && this.scrollContainerRef) {
|
if (prevProps.chatMessageIds.size === 0 && this.props.chatMessageIds.size > 0 && this.scrollContainerRef) {
|
||||||
this.scrollContainerRef.scrollTop = this.scrollContainerRef.scrollHeight
|
this.scrollToBottom()
|
||||||
this.props.onReadChatConversation(this.props.chatConversationId)
|
this.props.onReadChatConversation(this.props.chatConversationId)
|
||||||
} else if (prevProps.chatMessageIds.size < this.props.chatMessageIds.size && this.scrollContainerRef) {
|
} else if (prevProps.chatMessageIds.size < this.props.chatMessageIds.size && this.scrollContainerRef) {
|
||||||
this.scrollContainerRef.scrollTop = this.scrollContainerRef.scrollHeight
|
// this.setScrollTop(this.scrollContainerRef.scrollHeight)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -114,10 +115,15 @@ class ChatMessageScrollingList extends ImmutablePureComponent {
|
|||||||
if (!this.scrollContainerRef) return
|
if (!this.scrollContainerRef) return
|
||||||
if (this.scrollContainerRef.scrollTop !== newScrollTop) {
|
if (this.scrollContainerRef.scrollTop !== newScrollTop) {
|
||||||
this.lastScrollWasSynthetic = true
|
this.lastScrollWasSynthetic = true
|
||||||
|
console.log("newScrollTop:", newScrollTop)
|
||||||
this.scrollContainerRef.scrollTop = newScrollTop
|
this.scrollContainerRef.scrollTop = newScrollTop
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
scrollToBottom = () => {
|
||||||
|
this.messagesEnd.scrollIntoView({ behavior: 'smooth' });
|
||||||
|
}
|
||||||
|
|
||||||
_selectChild(index, align_top) {
|
_selectChild(index, align_top) {
|
||||||
const container = this.node.node
|
const container = this.node.node
|
||||||
const element = container.querySelector(`article:nth-of-type(${index + 1}) .focusable`)
|
const element = container.querySelector(`article:nth-of-type(${index + 1}) .focusable`)
|
||||||
@ -163,6 +169,7 @@ class ChatMessageScrollingList extends ImmutablePureComponent {
|
|||||||
|
|
||||||
handleWheel = throttle(() => {
|
handleWheel = throttle(() => {
|
||||||
this.scrollToTopOnMouseIdle = false
|
this.scrollToTopOnMouseIdle = false
|
||||||
|
this.handleScroll()
|
||||||
}, 150, {
|
}, 150, {
|
||||||
trailing: true,
|
trailing: true,
|
||||||
})
|
})
|
||||||
@ -190,7 +197,7 @@ class ChatMessageScrollingList extends ImmutablePureComponent {
|
|||||||
|
|
||||||
handleMouseIdle = () => {
|
handleMouseIdle = () => {
|
||||||
if (this.scrollToTopOnMouseIdle) {
|
if (this.scrollToTopOnMouseIdle) {
|
||||||
this.setScrollTop(0)
|
this.setScrollTop(this.scrollContainerRef.scrollHeight)
|
||||||
}
|
}
|
||||||
|
|
||||||
this.mouseMovedRecently = false
|
this.mouseMovedRecently = false
|
||||||
@ -320,6 +327,10 @@ class ChatMessageScrollingList extends ImmutablePureComponent {
|
|||||||
</IntersectionObserverArticle>
|
</IntersectionObserverArticle>
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
<div
|
||||||
|
style={{ float: 'left', clear: 'both' }}
|
||||||
|
ref={(el) => { this.messagesEnd = el }}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -38,7 +38,7 @@ class MessagesSettings extends ImmutablePureComponent {
|
|||||||
<BlockHeading title={'Chat Preferences'} />
|
<BlockHeading title={'Chat Preferences'} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={[_s.d, _s.px15, _s.py15].join(' ')}>
|
<div className={[_s.d, _s.px15, _s.py15, _s.overflowHidden].join(' ')}>
|
||||||
<Form>
|
<Form>
|
||||||
<Switch
|
<Switch
|
||||||
label='Restrict messages from people you dont follow'
|
label='Restrict messages from people you dont follow'
|
||||||
|
@ -61,6 +61,7 @@ const mapStateToProps = (state, props) => {
|
|||||||
const statusId = props.id || props.params.statusId
|
const statusId = props.id || props.params.statusId
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
statusId,
|
||||||
status: state.getIn(['statuses', statusId]),
|
status: state.getIn(['statuses', statusId]),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -68,6 +68,7 @@ import {
|
|||||||
ChatConversationCreate,
|
ChatConversationCreate,
|
||||||
ChatConversationRequests,
|
ChatConversationRequests,
|
||||||
ChatConversationBlockedAccounts,
|
ChatConversationBlockedAccounts,
|
||||||
|
ChatConversationMutes,
|
||||||
CommunityTimeline,
|
CommunityTimeline,
|
||||||
Compose,
|
Compose,
|
||||||
Deck,
|
Deck,
|
||||||
@ -221,7 +222,7 @@ class SwitchingArea extends React.PureComponent {
|
|||||||
<WrappedRoute path='/messages/settings' exact page={MessagesPage} component={MessagesSettings} content={children} componentParams={{ isSettings: true }} />
|
<WrappedRoute path='/messages/settings' exact page={MessagesPage} component={MessagesSettings} content={children} componentParams={{ isSettings: true }} />
|
||||||
<WrappedRoute path='/messages/requests' exact page={MessagesPage} component={ChatConversationRequests} content={children} componentParams={{ isSettings: true, source: 'requested' }} />
|
<WrappedRoute path='/messages/requests' exact page={MessagesPage} component={ChatConversationRequests} content={children} componentParams={{ isSettings: true, source: 'requested' }} />
|
||||||
<WrappedRoute path='/messages/blocks' exact page={MessagesPage} component={ChatConversationBlockedAccounts} content={children} componentParams={{ isSettings: true }} />
|
<WrappedRoute path='/messages/blocks' exact page={MessagesPage} component={ChatConversationBlockedAccounts} content={children} componentParams={{ isSettings: true }} />
|
||||||
<WrappedRoute path='/messages/muted_conversations' exact page={MessagesPage} component={ChatConversationBlockedAccounts} content={children} componentParams={{ isSettings: true }} />
|
<WrappedRoute path='/messages/muted_conversations' exact page={MessagesPage} component={ChatConversationMutes} content={children} componentParams={{ isSettings: true }} />
|
||||||
<WrappedRoute path='/messages/:chatConversationId' exact page={MessagesPage} component={Messages} content={children} componentParams={{ source: 'approved' }} />
|
<WrappedRoute path='/messages/:chatConversationId' exact page={MessagesPage} component={Messages} content={children} componentParams={{ source: 'approved' }} />
|
||||||
|
|
||||||
<WrappedRoute path='/timeline/all' exact page={CommunityPage} component={CommunityTimeline} content={children} componentParams={{ title: 'Community Feed' }} />
|
<WrappedRoute path='/timeline/all' exact page={CommunityPage} component={CommunityTimeline} content={children} componentParams={{ title: 'Community Feed' }} />
|
||||||
|
@ -25,7 +25,9 @@ export function ChatConversationDeleteModal() { return import(/* webpackChunkNam
|
|||||||
export function ChatConversationOptionsPopover() { return import(/* webpackChunkName: "components/chat_conversation_options_popover" */'../../../components/popover/chat_conversation_options_popover') }
|
export function ChatConversationOptionsPopover() { return import(/* webpackChunkName: "components/chat_conversation_options_popover" */'../../../components/popover/chat_conversation_options_popover') }
|
||||||
export function ChatConversationRequests() { return import(/* webpackChunkName: "features/chat_conversation_requests" */'../../chat_conversation_requests') }
|
export function ChatConversationRequests() { return import(/* webpackChunkName: "features/chat_conversation_requests" */'../../chat_conversation_requests') }
|
||||||
export function ChatConversationExpirationOptionsPopover() { return import(/* webpackChunkName: "components/chat_conversation_expiration_options_popover" */'../../../components/popover/chat_conversation_expiration_options_popover') }
|
export function ChatConversationExpirationOptionsPopover() { return import(/* webpackChunkName: "components/chat_conversation_expiration_options_popover" */'../../../components/popover/chat_conversation_expiration_options_popover') }
|
||||||
|
export function ChatConversationMutes() { return import(/* webpackChunkName: "features/chat_conversation_mutes" */'../../chat_conversation_mutes') }
|
||||||
export function ChatMessageOptionsPopover() { return import(/* webpackChunkName: "components/chat_message_options_popover" */'../../../components/popover/chat_message_options_popover') }
|
export function ChatMessageOptionsPopover() { return import(/* webpackChunkName: "components/chat_message_options_popover" */'../../../components/popover/chat_message_options_popover') }
|
||||||
|
export function ChatSettingsPopover() { return import(/* webpackChunkName: "components/chat_settings_popover" */'../../../components/popover/chat_settings_popover') }
|
||||||
export function CommentSortingOptionsPopover() { return import(/* webpackChunkName: "components/comment_sorting_options_popover" */'../../../components/popover/comment_sorting_options_popover') }
|
export function CommentSortingOptionsPopover() { return import(/* webpackChunkName: "components/comment_sorting_options_popover" */'../../../components/popover/comment_sorting_options_popover') }
|
||||||
export function CommunityTimeline() { return import(/* webpackChunkName: "features/community_timeline" */'../../community_timeline') }
|
export function CommunityTimeline() { return import(/* webpackChunkName: "features/community_timeline" */'../../community_timeline') }
|
||||||
export function Compose() { return import(/* webpackChunkName: "features/compose" */'../../compose') }
|
export function Compose() { return import(/* webpackChunkName: "features/compose" */'../../compose') }
|
||||||
|
@ -43,15 +43,6 @@ class WrappedRoute extends React.PureComponent {
|
|||||||
...rest
|
...rest
|
||||||
} = this.props
|
} = this.props
|
||||||
|
|
||||||
// : todo :
|
|
||||||
// api().get('/api/v1/accounts/verify_credentials')
|
|
||||||
// .then((res) => {
|
|
||||||
// console.log("res:", res)
|
|
||||||
// })
|
|
||||||
// .catch((err) => {
|
|
||||||
// console.log("err:", err)
|
|
||||||
// })
|
|
||||||
|
|
||||||
if (!publicRoute && !me) {
|
if (!publicRoute && !me) {
|
||||||
const actualUrl = encodeURIComponent(this.props.computedMatch.url)
|
const actualUrl = encodeURIComponent(this.props.computedMatch.url)
|
||||||
return <Route path={this.props.path} component={() => {
|
return <Route path={this.props.path} component={() => {
|
||||||
|
@ -3,9 +3,11 @@ import PropTypes from 'prop-types'
|
|||||||
import { connect } from 'react-redux'
|
import { connect } from 'react-redux'
|
||||||
import { me } from '../initial_state'
|
import { me } from '../initial_state'
|
||||||
import { openModal } from '../actions/modal'
|
import { openModal } from '../actions/modal'
|
||||||
|
import { openPopover } from '../actions/popover'
|
||||||
import {
|
import {
|
||||||
CX,
|
CX,
|
||||||
BREAKPOINT_EXTRA_SMALL,
|
BREAKPOINT_EXTRA_SMALL,
|
||||||
|
POPOVER_CHAT_SETTINGS,
|
||||||
MODAL_CHAT_CONVERSATION_CREATE,
|
MODAL_CHAT_CONVERSATION_CREATE,
|
||||||
} from '../constants'
|
} from '../constants'
|
||||||
import { getWindowDimension } from '../utils/is_mobile'
|
import { getWindowDimension } from '../utils/is_mobile'
|
||||||
@ -43,6 +45,10 @@ class MessagesLayout extends React.PureComponent {
|
|||||||
this.setState({ width })
|
this.setState({ width })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleOpenSettingsOptionsPopover = () => {
|
||||||
|
this.props.onOpenSettingsOptionsPopover()
|
||||||
|
}
|
||||||
|
|
||||||
onClickAdd = () => {
|
onClickAdd = () => {
|
||||||
this.props.onOpenChatConversationCreateModal()
|
this.props.onOpenChatConversationCreateModal()
|
||||||
}
|
}
|
||||||
@ -74,12 +80,12 @@ class MessagesLayout extends React.PureComponent {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: 'cog',
|
icon: 'cog',
|
||||||
to: '/messages/settings',
|
onClick: this.handleOpenSettingsOptionsPopover,
|
||||||
}
|
}
|
||||||
]}
|
]}
|
||||||
title={title}
|
title={title}
|
||||||
/>
|
/>
|
||||||
<main role='main' className={[_s.d, _s.w100PC].join(' ')}>
|
<main role='main' className={[_s.d, _s.w100PC, _s.flexGrow1, _s.bgPrimary, _s.borderBottom1PX, _s.borderColorSecondary].join(' ')}>
|
||||||
<div className={[_s.d, _s.w100PC, _s.flexRow, _s.pb15].join(' ')}>
|
<div className={[_s.d, _s.w100PC, _s.flexRow, _s.pb15].join(' ')}>
|
||||||
{
|
{
|
||||||
(isSettings || currentConversationIsRequest) &&
|
(isSettings || currentConversationIsRequest) &&
|
||||||
@ -98,7 +104,7 @@ class MessagesLayout extends React.PureComponent {
|
|||||||
return (
|
return (
|
||||||
<div className={[_s.d, _s.w100PC, _s.minH100VH, _s.bgTertiary].join(' ')}>
|
<div className={[_s.d, _s.w100PC, _s.minH100VH, _s.bgTertiary].join(' ')}>
|
||||||
<ChatNavigationBar chatConversationId={selectedChatConversationId} />
|
<ChatNavigationBar chatConversationId={selectedChatConversationId} />
|
||||||
<main role='main' className={[_s.d, _s.w100PC].join(' ')}>
|
<main role='main' className={[_s.d, _s.w100PC, _s.flexGrow1, _s.bgPrimary, _s.borderBottom1PX, _s.borderColorSecondary].join(' ')}>
|
||||||
<ChatMessageScrollingList chatConversationId={selectedChatConversationId} isXS={isXS} />
|
<ChatMessageScrollingList chatConversationId={selectedChatConversationId} isXS={isXS} />
|
||||||
</main>
|
</main>
|
||||||
{ currentConversationIsRequest && <ChatConversationRequestApproveBar /> }
|
{ currentConversationIsRequest && <ChatConversationRequestApproveBar /> }
|
||||||
@ -106,7 +112,17 @@ class MessagesLayout extends React.PureComponent {
|
|||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
//settings
|
return (
|
||||||
|
<div className={[_s.d, _s.w100PC, _s.minH100VH, _s.bgTertiary].join(' ')}>
|
||||||
|
<DefaultNavigationBar showBackBtn title={title} />
|
||||||
|
<main role='main' className={[_s.d, _s.w100PC, _s.bgPrimary, _s.borderBottom1PX, _s.borderColorSecondary].join(' ')}>
|
||||||
|
<div className={[_s.d, _s.w100PC, _s.flexRow, _s.pb15].join(' ')}>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
<FooterBar />
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -174,7 +190,10 @@ const mapStateToProps = (state) => {
|
|||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
onOpenChatConversationCreateModal() {
|
onOpenChatConversationCreateModal() {
|
||||||
dispatch(openModal(MODAL_CHAT_CONVERSATION_CREATE))
|
dispatch(openModal(MODAL_CHAT_CONVERSATION_CREATE))
|
||||||
}
|
},
|
||||||
|
onOpenSettingsOptionsPopover() {
|
||||||
|
dispatch(openPopover(POPOVER_CHAT_SETTINGS))
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
MessagesLayout.propTypes = {
|
MessagesLayout.propTypes = {
|
||||||
|
@ -19,6 +19,7 @@ class ModalPage extends React.PureComponent {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<DefaultLayout
|
<DefaultLayout
|
||||||
|
noComposeButton
|
||||||
title={title}
|
title={title}
|
||||||
page={page}
|
page={page}
|
||||||
showBackBtn
|
showBackBtn
|
||||||
|
26
app/javascript/gabsocial/reducers/chat_settings.js
Normal file
26
app/javascript/gabsocial/reducers/chat_settings.js
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import { SETTING_CHANGE, SETTING_SAVE } from '../actions/settings'
|
||||||
|
import {
|
||||||
|
CHAT_SETTING_CHANGE,
|
||||||
|
CHAT_SETTING_SAVE,
|
||||||
|
} from '../actions/chat_settings'
|
||||||
|
import { Map as ImmutableMap, List as ImmutableList, fromJS } from 'immutable'
|
||||||
|
import uuid from '../utils/uuid'
|
||||||
|
|
||||||
|
const initialState = ImmutableMap({
|
||||||
|
saved: false,
|
||||||
|
restrict_non_followers: true,
|
||||||
|
show_active: false,
|
||||||
|
read_receipts: false,
|
||||||
|
sounds: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
export default function chat_settings(state = initialState, action) {
|
||||||
|
switch(action.type) {
|
||||||
|
case CHAT_SETTING_CHANGE:
|
||||||
|
return state
|
||||||
|
.setIn(action.path, action.value)
|
||||||
|
.set('saved', false)
|
||||||
|
default:
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
}
|
@ -9,6 +9,7 @@ import chat_conversation_lists from './chat_conversation_lists'
|
|||||||
import chat_conversation_messages from './chat_conversation_messages'
|
import chat_conversation_messages from './chat_conversation_messages'
|
||||||
import chat_conversations from './chat_conversations'
|
import chat_conversations from './chat_conversations'
|
||||||
import chat_messages from './chat_messages'
|
import chat_messages from './chat_messages'
|
||||||
|
import chat_settings from './chat_settings'
|
||||||
import compose from './compose'
|
import compose from './compose'
|
||||||
import contexts from './contexts'
|
import contexts from './contexts'
|
||||||
import custom_emojis from './custom_emojis'
|
import custom_emojis from './custom_emojis'
|
||||||
@ -60,6 +61,7 @@ const reducers = {
|
|||||||
chat_conversation_messages,
|
chat_conversation_messages,
|
||||||
chat_conversations,
|
chat_conversations,
|
||||||
chat_messages,
|
chat_messages,
|
||||||
|
chat_settings,
|
||||||
compose,
|
compose,
|
||||||
contexts,
|
contexts,
|
||||||
custom_emojis,
|
custom_emojis,
|
||||||
|
@ -14,7 +14,6 @@ const initialState = ImmutableList([])
|
|||||||
export default function toasts(state = initialState, action) {
|
export default function toasts(state = initialState, action) {
|
||||||
switch(action.type) {
|
switch(action.type) {
|
||||||
case TOAST_SHOW:
|
case TOAST_SHOW:
|
||||||
console.log("action:", action)
|
|
||||||
return state.set(state.size, ImmutableMap({
|
return state.set(state.size, ImmutableMap({
|
||||||
key: state.size > 0 ? state.last().get('key') + 1 : 0,
|
key: state.size > 0 ? state.last().get('key') + 1 : 0,
|
||||||
message: makeMessageFromData(action.toastData),
|
message: makeMessageFromData(action.toastData),
|
||||||
|
@ -166,9 +166,6 @@ export const makeGetStatus = () => {
|
|||||||
quotedStatus = quotedStatus.set('account', accountQuoted);
|
quotedStatus = quotedStatus.set('account', accountQuoted);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// console.log("group:", group)
|
|
||||||
|
|
||||||
//Find ancestor status
|
//Find ancestor status
|
||||||
|
|
||||||
const regex = (accountRepost || accountBase).get('id') !== me && regexFromFilters(filters);
|
const regex = (accountRepost || accountBase).get('id') !== me && regexFromFilters(filters);
|
||||||
@ -247,14 +244,11 @@ export const getListOfGroups = createSelector([
|
|||||||
(state) => state.get('groups'),
|
(state) => state.get('groups'),
|
||||||
(state, { type }) => state.getIn(['group_lists', type, 'items']),
|
(state, { type }) => state.getIn(['group_lists', type, 'items']),
|
||||||
], (groups, groupIds) => {
|
], (groups, groupIds) => {
|
||||||
console.log("groupIds:", groupIds)
|
|
||||||
let list = ImmutableList()
|
let list = ImmutableList()
|
||||||
groupIds.forEach((id, i) => {
|
groupIds.forEach((id, i) => {
|
||||||
const group = groups.get(`${id}`)
|
const group = groups.get(`${id}`)
|
||||||
console.log("groupIds:", id, i, group)
|
|
||||||
list = list.set(i, group)
|
list = list.set(i, group)
|
||||||
})
|
})
|
||||||
console.log("list:", list)
|
|
||||||
|
|
||||||
return list
|
return list
|
||||||
})
|
})
|
@ -1,5 +1,5 @@
|
|||||||
// https://gist.github.com/andjosh/6764939
|
// https://gist.github.com/andjosh/6764939
|
||||||
export const scrollTo = (element, to, duration) => {
|
export const scrollTo = (element, to, duration = 0) => {
|
||||||
var start = element.scrollTop,
|
var start = element.scrollTop,
|
||||||
change = to - start,
|
change = to - start,
|
||||||
currentTime = 0,
|
currentTime = 0,
|
||||||
|
@ -314,6 +314,7 @@ pre {
|
|||||||
|
|
||||||
.flex1 { flex: 1; }
|
.flex1 { flex: 1; }
|
||||||
.flexGrow1 { flex-grow: 1; }
|
.flexGrow1 { flex-grow: 1; }
|
||||||
|
.flexShrink0 { flex-shrink: 0; }
|
||||||
.flexShrink1 { flex-shrink: 1; }
|
.flexShrink1 { flex-shrink: 1; }
|
||||||
|
|
||||||
.flexRow { flex-direction: row; }
|
.flexRow { flex-direction: row; }
|
||||||
@ -597,6 +598,8 @@ pre {
|
|||||||
.maxW100PC130PX { max-width: calc(100% - 130px); }
|
.maxW100PC130PX { max-width: calc(100% - 130px); }
|
||||||
.maxW100PC86PX { max-width: calc(100% - 86px); }
|
.maxW100PC86PX { max-width: calc(100% - 86px); }
|
||||||
.maxW100PC42PX { max-width: calc(100% - 42px); }
|
.maxW100PC42PX { max-width: calc(100% - 42px); }
|
||||||
|
.maxW100PC30PX { max-width: calc(100% - 30px); }
|
||||||
|
.maxW320PX { max-width: 320px; }
|
||||||
.maxW212PX { max-width: 212px; }
|
.maxW212PX { max-width: 212px; }
|
||||||
|
|
||||||
.minW330PX { min-width: 330px; }
|
.minW330PX { min-width: 330px; }
|
||||||
@ -1314,6 +1317,13 @@ pre {
|
|||||||
width: 100px !important;
|
width: 100px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
:global(.react-datepicker__day--disabled),
|
||||||
|
:global(.react-datepicker__month-text--disabled),
|
||||||
|
:global(.react-datepicker__quarter-text--disabled) {
|
||||||
|
cursor: not-allowed;
|
||||||
|
opacity: 0.2 !important;
|
||||||
|
}
|
||||||
|
|
||||||
@media (max-width: 500px) {
|
@media (max-width: 500px) {
|
||||||
:global(.react-datepicker) {
|
:global(.react-datepicker) {
|
||||||
flex-direction: column !important;
|
flex-direction: column !important;
|
||||||
|
@ -38,6 +38,8 @@ class ChatConversationAccount < ApplicationRecord
|
|||||||
belongs_to :chat_conversation
|
belongs_to :chat_conversation
|
||||||
belongs_to :last_chat_message, class_name: 'ChatMessage', optional: true
|
belongs_to :last_chat_message, class_name: 'ChatMessage', optional: true
|
||||||
|
|
||||||
|
validate :validate_total_limit
|
||||||
|
|
||||||
def participant_accounts
|
def participant_accounts
|
||||||
if participant_account_ids.empty?
|
if participant_account_ids.empty?
|
||||||
[account]
|
[account]
|
||||||
@ -47,4 +49,10 @@ class ChatConversationAccount < ApplicationRecord
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def validate_total_limit
|
||||||
|
# errors.add(:base, I18n.t('scheduled_statuses.over_total_limit', limit: PER_ACCOUNT_APPROVED_LIMIT)) if account.scheduled_statuses.count >= PER_ACCOUNT_APPROVED_LIMIT
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
@ -18,7 +18,6 @@ class HomeFeed < Feed
|
|||||||
private
|
private
|
||||||
|
|
||||||
def from_database(limit, max_id, since_id, min_id)
|
def from_database(limit, max_id, since_id, min_id)
|
||||||
puts "tilly from_database"
|
|
||||||
Status.as_home_timeline(@account)
|
Status.as_home_timeline(@account)
|
||||||
.paginate_by_id(limit, max_id: max_id, since_id: since_id, min_id: min_id)
|
.paginate_by_id(limit, max_id: max_id, since_id: since_id, min_id: min_id)
|
||||||
.reject { |status| FeedManager.instance.filter?(:home, status, @account.id) }
|
.reject { |status| FeedManager.instance.filter?(:home, status, @account.id) }
|
||||||
|
@ -9,10 +9,6 @@ class REST::StatusSerializer < ActiveModel::Serializer
|
|||||||
attribute :favourited, if: :current_user?
|
attribute :favourited, if: :current_user?
|
||||||
attribute :reblogged, if: :current_user?
|
attribute :reblogged, if: :current_user?
|
||||||
|
|
||||||
attribute :account_id, if: :account_id?
|
|
||||||
attribute :group_id, if: :group_id?
|
|
||||||
attribute :preview_card_id, if: :preview_card_id?
|
|
||||||
|
|
||||||
attribute :content, unless: :source_requested?
|
attribute :content, unless: :source_requested?
|
||||||
attribute :rich_content, unless: :source_requested?
|
attribute :rich_content, unless: :source_requested?
|
||||||
attribute :plain_markdown, unless: :source_requested?
|
attribute :plain_markdown, unless: :source_requested?
|
||||||
@ -20,15 +16,15 @@ class REST::StatusSerializer < ActiveModel::Serializer
|
|||||||
|
|
||||||
belongs_to :reblog, serializer: REST::StatusSerializer
|
belongs_to :reblog, serializer: REST::StatusSerializer
|
||||||
belongs_to :quote, serializer: REST::StatusSerializer
|
belongs_to :quote, serializer: REST::StatusSerializer
|
||||||
belongs_to :account, serializer: REST::AccountSerializer, unless: :account_id?
|
belongs_to :account, serializer: REST::AccountSerializer
|
||||||
belongs_to :group, serializer: REST::GroupSerializer, unless: :group_id?
|
belongs_to :group, serializer: REST::GroupSerializer
|
||||||
|
|
||||||
has_many :media_attachments, serializer: REST::MediaAttachmentSerializer
|
has_many :media_attachments, serializer: REST::MediaAttachmentSerializer
|
||||||
has_many :ordered_mentions, key: :mentions
|
has_many :ordered_mentions, key: :mentions
|
||||||
has_many :tags
|
has_many :tags
|
||||||
has_many :emojis, serializer: REST::CustomEmojiSerializer
|
has_many :emojis, serializer: REST::CustomEmojiSerializer
|
||||||
|
|
||||||
has_one :preview_card, key: :card, serializer: REST::PreviewCardSerializer, unless: :preview_card_id?
|
has_one :preview_card, key: :card, serializer: REST::PreviewCardSerializer
|
||||||
has_one :preloadable_poll, key: :poll, serializer: REST::PollSerializer
|
has_one :preloadable_poll, key: :poll, serializer: REST::PollSerializer
|
||||||
|
|
||||||
def id
|
def id
|
||||||
@ -51,30 +47,6 @@ class REST::StatusSerializer < ActiveModel::Serializer
|
|||||||
!current_user.nil?
|
!current_user.nil?
|
||||||
end
|
end
|
||||||
|
|
||||||
def account_id
|
|
||||||
instance_options[:account_id]
|
|
||||||
end
|
|
||||||
|
|
||||||
def account_id?
|
|
||||||
!instance_options[:account_id].nil?
|
|
||||||
end
|
|
||||||
|
|
||||||
def group_id
|
|
||||||
instance_options[:group_id]
|
|
||||||
end
|
|
||||||
|
|
||||||
def group_id?
|
|
||||||
!instance_options[:group_id].nil?
|
|
||||||
end
|
|
||||||
|
|
||||||
def preview_card_id
|
|
||||||
instance_options[:preview_card_id]
|
|
||||||
end
|
|
||||||
|
|
||||||
def preview_card_id?
|
|
||||||
!instance_options[:preview_card_id].nil?
|
|
||||||
end
|
|
||||||
|
|
||||||
def visibility
|
def visibility
|
||||||
# This visibility is masked behind "private"
|
# This visibility is masked behind "private"
|
||||||
# to avoid API changes because there are no
|
# to avoid API changes because there are no
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
%tr
|
%tr
|
||||||
%td
|
%td
|
||||||
= admin_account_link_to(account)
|
= admin_account_link_to(account)
|
||||||
|
- if account.is_flagged_as_spam
|
||||||
|
%span SPAM
|
||||||
%td
|
%td
|
||||||
- if account.user_current_sign_in_ip
|
- if account.user_current_sign_in_ip
|
||||||
%samp.ellipsized-ip{ title: account.user_current_sign_in_ip }= account.user_current_sign_in_ip
|
%samp.ellipsized-ip{ title: account.user_current_sign_in_ip }= account.user_current_sign_in_ip
|
||||||
|
@ -28,6 +28,7 @@ SimpleNavigation::Configuration.run do |navigation|
|
|||||||
n.item :development, safe_join([fa_icon('code fw'), t('settings.development')]), settings_applications_url, if: -> { current_user.staff? }
|
n.item :development, safe_join([fa_icon('code fw'), t('settings.development')]), settings_applications_url, if: -> { current_user.staff? }
|
||||||
|
|
||||||
n.item :moderation, safe_join([fa_icon('gavel fw'), t('moderation.title')]), admin_reports_url, if: proc { current_user.staff? } do |s|
|
n.item :moderation, safe_join([fa_icon('gavel fw'), t('moderation.title')]), admin_reports_url, if: proc { current_user.staff? } do |s|
|
||||||
|
s.item :accounts, safe_join([fa_icon('users fw'), t('admin.accounts.title')]), admin_accounts_url, highlights_on: %r{/admin/accounts|/admin/pending_accounts}
|
||||||
s.item :action_logs, safe_join([fa_icon('bars fw'), t('admin.action_logs.title')]), admin_action_logs_url
|
s.item :action_logs, safe_join([fa_icon('bars fw'), t('admin.action_logs.title')]), admin_action_logs_url
|
||||||
s.item :reports, safe_join([fa_icon('flag fw'), t('admin.reports.title')]), admin_reports_url, highlights_on: %r{/admin/reports}
|
s.item :reports, safe_join([fa_icon('flag fw'), t('admin.reports.title')]), admin_reports_url, highlights_on: %r{/admin/reports}
|
||||||
s.item :email_domain_blocks, safe_join([fa_icon('envelope fw'), t('admin.email_domain_blocks.title')]), admin_email_domain_blocks_url, highlights_on: %r{/admin/email_domain_blocks}, if: -> { current_user.admin? }
|
s.item :email_domain_blocks, safe_join([fa_icon('envelope fw'), t('admin.email_domain_blocks.title')]), admin_email_domain_blocks_url, highlights_on: %r{/admin/email_domain_blocks}, if: -> { current_user.admin? }
|
||||||
|
@ -382,6 +382,7 @@ Rails.application.routes.draw do
|
|||||||
|
|
||||||
namespace :web do
|
namespace :web do
|
||||||
resource :settings, only: [:update]
|
resource :settings, only: [:update]
|
||||||
|
resource :chat_settings, only: [:update]
|
||||||
resource :embed, only: [:create]
|
resource :embed, only: [:create]
|
||||||
resources :push_subscriptions, only: [:create] do
|
resources :push_subscriptions, only: [:create] do
|
||||||
member do
|
member do
|
||||||
|
Loading…
x
Reference in New Issue
Block a user