Report modal style fix, chat updates, statusserializer revert, display name truncation
This commit is contained in:
mgabdev 2020-12-20 12:27:24 -05:00
parent 7ec426e3d8
commit 67eb9d5890
49 changed files with 369 additions and 158 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View 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

View File

@ -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))

View File

@ -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))

View File

@ -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

View 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 })

View File

@ -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))

View File

@ -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}

View File

@ -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>

View File

@ -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>

View File

@ -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(' ')}>

View File

@ -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 = [

View File

@ -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))

View File

@ -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

View File

@ -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}>

View File

@ -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>
} }

View File

@ -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']) }

View File

@ -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(' ')}>

View File

@ -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}
/> />

View File

@ -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}
> >
{ {

View File

@ -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'

View File

@ -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>

View 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

View File

@ -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'

View File

@ -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}

View File

@ -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>

View File

@ -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>

View File

@ -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'

View File

@ -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]),
} }
} }

View File

@ -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' }} />

View File

@ -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') }

View File

@ -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={() => {

View File

@ -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 = {

View File

@ -19,6 +19,7 @@ class ModalPage extends React.PureComponent {
return ( return (
<DefaultLayout <DefaultLayout
noComposeButton
title={title} title={title}
page={page} page={page}
showBackBtn showBackBtn

View 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
}
}

View File

@ -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,

View File

@ -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),

View File

@ -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
}) })

View File

@ -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,

View File

@ -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;

View File

@ -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

View File

@ -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) }

View File

@ -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

View File

@ -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

View File

@ -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? }

View File

@ -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