From 239b2af553d5f60c6387d62357917cc55662e231 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Wed, 2 Nov 2022 13:53:41 -0500 Subject: [PATCH 1/7] Chats: make message_expiration optional (for Pleroma compatibility) --- .../components/chat-message-list-intro.tsx | 8 ++++--- .../chat-page/components/chat-page-main.tsx | 22 ++++++++++--------- .../components/chat-widget/chat-window.tsx | 8 ++++--- app/soapbox/queries/chats.ts | 2 +- 4 files changed, 23 insertions(+), 17 deletions(-) diff --git a/app/soapbox/features/chats/components/chat-message-list-intro.tsx b/app/soapbox/features/chats/components/chat-message-list-intro.tsx index dc6ddfd47..19efe998c 100644 --- a/app/soapbox/features/chats/components/chat-message-list-intro.tsx +++ b/app/soapbox/features/chats/components/chat-message-list-intro.tsx @@ -97,9 +97,11 @@ const ChatMessageListIntro = () => { ) : ( - - {intl.formatMessage(messages.messageLifespan, { day: secondsToDays(chat.message_expiration) })} - + {chat.message_expiration && ( + + {intl.formatMessage(messages.messageLifespan, { day: secondsToDays(chat.message_expiration) })} + + )} )} diff --git a/app/soapbox/features/chats/components/chat-page/components/chat-page-main.tsx b/app/soapbox/features/chats/components/chat-page/components/chat-page-main.tsx index cd0c2dc2e..90f7e6be9 100644 --- a/app/soapbox/features/chats/components/chat-page/components/chat-page-main.tsx +++ b/app/soapbox/features/chats/components/chat-page/components/chat-page-main.tsx @@ -105,16 +105,18 @@ const ChatPageMain = () => { {chat.account?.verified && } - - {intl.formatMessage(messages.autoDeleteMessage, { day: secondsToDays(chat.message_expiration) })} - + {chat.message_expiration && ( + + {intl.formatMessage(messages.autoDeleteMessage, { day: secondsToDays(chat.message_expiration) })} + + )} diff --git a/app/soapbox/features/chats/components/chat-widget/chat-window.tsx b/app/soapbox/features/chats/components/chat-widget/chat-window.tsx index 0dea7aac7..18ed38860 100644 --- a/app/soapbox/features/chats/components/chat-widget/chat-window.tsx +++ b/app/soapbox/features/chats/components/chat-widget/chat-window.tsx @@ -87,9 +87,11 @@ const ChatWindow = () => { {chat.account.display_name} {chat.account.verified && } - - {intl.formatMessage(messages.autoDeleteMessage, { day: secondsToDays(chat.message_expiration) })} - + {chat.message_expiration && ( + + {intl.formatMessage(messages.autoDeleteMessage, { day: secondsToDays(chat.message_expiration) })} + + )} diff --git a/app/soapbox/queries/chats.ts b/app/soapbox/queries/chats.ts index 182304c85..b9e6a36e3 100644 --- a/app/soapbox/queries/chats.ts +++ b/app/soapbox/queries/chats.ts @@ -44,7 +44,7 @@ export interface IChat { [id: number]: string }[] latest_read_message_created_at: null | string - message_expiration: MessageExpirationValues + message_expiration?: MessageExpirationValues unread: number } From 69d92b6f9540493d86763bd1380e09a9bf5fac4c Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Wed, 2 Nov 2022 13:58:02 -0500 Subject: [PATCH 2/7] Chats: make chatsExpiration a conditional feature --- .../chat-page/components/chat-page-main.tsx | 57 ++++++++++--------- .../components/chat-widget/chat-settings.tsx | 33 ++++++----- app/soapbox/utils/features.ts | 6 ++ 3 files changed, 54 insertions(+), 42 deletions(-) diff --git a/app/soapbox/features/chats/components/chat-page/components/chat-page-main.tsx b/app/soapbox/features/chats/components/chat-page/components/chat-page-main.tsx index 90f7e6be9..c4bc06105 100644 --- a/app/soapbox/features/chats/components/chat-page/components/chat-page-main.tsx +++ b/app/soapbox/features/chats/components/chat-page/components/chat-page-main.tsx @@ -7,7 +7,7 @@ import List, { ListItem } from 'soapbox/components/list'; import { Avatar, HStack, Icon, IconButton, Menu, MenuButton, MenuItem, MenuList, Stack, Text } from 'soapbox/components/ui'; import VerificationBadge from 'soapbox/components/verification_badge'; import { useChatContext } from 'soapbox/contexts/chat-context'; -import { useAppDispatch, useAppSelector } from 'soapbox/hooks'; +import { useAppDispatch, useAppSelector, useFeatures } from 'soapbox/hooks'; import { MessageExpirationValues, useChatActions } from 'soapbox/queries/chats'; import { secondsToDays } from 'soapbox/utils/numbers'; @@ -41,6 +41,7 @@ const messages = defineMessages({ const ChatPageMain = () => { const dispatch = useAppDispatch(); const intl = useIntl(); + const features = useFeatures(); const inputRef = useRef(null); @@ -138,32 +139,34 @@ const ChatPageMain = () => { - - - handleUpdateChat(MessageExpirationValues.SEVEN)} - isSelected={chat.message_expiration === MessageExpirationValues.SEVEN} - /> - handleUpdateChat(MessageExpirationValues.FOURTEEN)} - isSelected={chat.message_expiration === MessageExpirationValues.FOURTEEN} - /> - handleUpdateChat(MessageExpirationValues.THIRTY)} - isSelected={chat.message_expiration === MessageExpirationValues.THIRTY} - /> - handleUpdateChat(MessageExpirationValues.NINETY)} - isSelected={chat.message_expiration === MessageExpirationValues.NINETY} - /> - + {features.chatsExpiration && ( + + + handleUpdateChat(MessageExpirationValues.SEVEN)} + isSelected={chat.message_expiration === MessageExpirationValues.SEVEN} + /> + handleUpdateChat(MessageExpirationValues.FOURTEEN)} + isSelected={chat.message_expiration === MessageExpirationValues.FOURTEEN} + /> + handleUpdateChat(MessageExpirationValues.THIRTY)} + isSelected={chat.message_expiration === MessageExpirationValues.THIRTY} + /> + handleUpdateChat(MessageExpirationValues.NINETY)} + isSelected={chat.message_expiration === MessageExpirationValues.NINETY} + /> + + )} { const dispatch = useAppDispatch(); const intl = useIntl(); + const features = useFeatures(); const { chat, setEditing, toggleChatPane } = useChatContext(); const { deleteChat, updateChat } = useChatActions(chat?.id as string); @@ -113,21 +114,23 @@ const ChatSettings = () => { - - - handleUpdateChat(Number(event.target.value))}> + {messageExpirationOptions.map((duration) => { + const inDays = secondsToDays(duration); - return ( - - ); - })} - - - + return ( + + ); + })} + + + + )} - + {features.chatsDelete && ( + + )} diff --git a/app/soapbox/utils/features.ts b/app/soapbox/utils/features.ts index 2a8e002b1..7badea639 100644 --- a/app/soapbox/utils/features.ts +++ b/app/soapbox/utils/features.ts @@ -198,12 +198,24 @@ const getInstanceFeatures = (instance: Instance) => { v.software === PLEROMA, ]), + /** + * Ability to accept a chat. + * POST /api/v1/pleroma/chats/:id/accept + */ + chatAcceptance: v.software === TRUTHSOCIAL, + /** * Pleroma chats API. * @see {@link https://docs.pleroma.social/backend/development/API/chats/} */ chats: v.software === TRUTHSOCIAL || (v.software === PLEROMA && gte(v.version, '2.1.0')), + /** + * Ability to delete a chat. + * @see DELETE /api/v1/pleroma/chats/:id + */ + chatsDelete: v.software === TRUTHSOCIAL, + /** * Ability to set disappearing messages on chats. * @see PATCH /api/v1/pleroma/chats/:id From 4d9f7fec32b4694b209fcbda4797b4d4e6502b3c Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Wed, 2 Nov 2022 14:23:39 -0500 Subject: [PATCH 5/7] Chats: normalize chat messages again so attachments don't break --- app/soapbox/normalizers/chat_message.ts | 4 ++-- app/soapbox/queries/chats.ts | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/app/soapbox/normalizers/chat_message.ts b/app/soapbox/normalizers/chat_message.ts index bdf2653a9..393f950af 100644 --- a/app/soapbox/normalizers/chat_message.ts +++ b/app/soapbox/normalizers/chat_message.ts @@ -15,13 +15,13 @@ export const ChatMessageRecord = ImmutableRecord({ card: null as Card | null, chat_id: '', content: '', - created_at: new Date(), + created_at: '', emojis: ImmutableList(), id: '', unread: false, deleting: false, - pending: false, + pending: false as boolean | undefined, }); const normalizeMedia = (status: ImmutableMap) => { diff --git a/app/soapbox/queries/chats.ts b/app/soapbox/queries/chats.ts index b9e6a36e3..ec2c2542a 100644 --- a/app/soapbox/queries/chats.ts +++ b/app/soapbox/queries/chats.ts @@ -9,6 +9,7 @@ import compareId from 'soapbox/compare_id'; import { useChatContext } from 'soapbox/contexts/chat-context'; import { useStatContext } from 'soapbox/contexts/stat-context'; import { useApi, useAppDispatch, useAppSelector, useFeatures } from 'soapbox/hooks'; +import { normalizeChatMessage } from 'soapbox/normalizers'; import { flattenPages, PaginatedResult, updatePageItem } from 'soapbox/utils/queries'; import { queryClient } from './client'; @@ -91,7 +92,7 @@ const useChatMessages = (chat: IChat) => { const link = getNextLink(response); const hasMore = !!link; - const result = data.sort(reverseOrder); + const result = data.sort(reverseOrder).map(normalizeChatMessage); return { result, From 1873ba3e22f69040ec2f86e21fa6f18342179176 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Wed, 2 Nov 2022 14:30:07 -0500 Subject: [PATCH 6/7] Chats: feature-gate chat message reporting --- .../__tests__/chat-message-list.test.tsx | 5 ++++- .../chats/components/chat-message-list.tsx | 15 +++++++++------ app/soapbox/utils/features.ts | 15 +++++++++++---- 3 files changed, 24 insertions(+), 11 deletions(-) diff --git a/app/soapbox/features/chats/components/__tests__/chat-message-list.test.tsx b/app/soapbox/features/chats/components/__tests__/chat-message-list.test.tsx index 063302523..0ab2c5f7c 100644 --- a/app/soapbox/features/chats/components/__tests__/chat-message-list.test.tsx +++ b/app/soapbox/features/chats/components/__tests__/chat-message-list.test.tsx @@ -3,6 +3,7 @@ import React from 'react'; import { VirtuosoMockContext } from 'react-virtuoso'; import { ChatContext } from 'soapbox/contexts/chat-context'; +import { normalizeInstance } from 'soapbox/normalizers'; import { IAccount } from 'soapbox/queries/accounts'; import { __stub } from '../../../../api'; @@ -52,7 +53,9 @@ Object.assign(navigator, { }, }); -const store = rootState.set('me', '1'); +const store = rootState + .set('me', '1') + .set('instance', normalizeInstance({ version: '3.4.1 (compatible; TruthSocial 1.0.0)' })); const renderComponentWithChatContext = () => render( diff --git a/app/soapbox/features/chats/components/chat-message-list.tsx b/app/soapbox/features/chats/components/chat-message-list.tsx index 635f6c014..61bb6a488 100644 --- a/app/soapbox/features/chats/components/chat-message-list.tsx +++ b/app/soapbox/features/chats/components/chat-message-list.tsx @@ -14,7 +14,7 @@ import DropdownMenuContainer from 'soapbox/containers/dropdown_menu_container'; import PlaceholderChatMessage from 'soapbox/features/placeholder/components/placeholder-chat-message'; import Bundle from 'soapbox/features/ui/components/bundle'; import { MediaGallery } from 'soapbox/features/ui/util/async-components'; -import { useAppSelector, useAppDispatch, useOwnAccount } from 'soapbox/hooks'; +import { useAppSelector, useAppDispatch, useOwnAccount, useFeatures } from 'soapbox/hooks'; import { normalizeAccount } from 'soapbox/normalizers'; import { ChatKeys, IChat, IChatMessage, useChatActions, useChatMessages } from 'soapbox/queries/chats'; import { queryClient } from 'soapbox/queries/client'; @@ -73,6 +73,7 @@ const ChatMessageList: React.FC = ({ chat, autosize }) => { const intl = useIntl(); const dispatch = useAppDispatch(); const account = useOwnAccount(); + const features = useFeatures(); const node = useRef(null); const [firstItemIndex, setFirstItemIndex] = useState(START_INDEX - 20); @@ -239,11 +240,13 @@ const ChatMessageList: React.FC = ({ chat, autosize }) => { destructive: true, }); } else { - menu.push({ - text: intl.formatMessage(messages.report), - action: () => dispatch(initReport(normalizeAccount(chat.account) as any, { chatMessage })), - icon: require('@tabler/icons/flag.svg'), - }); + if (features.reportChats) { + menu.push({ + text: intl.formatMessage(messages.report), + action: () => dispatch(initReport(normalizeAccount(chat.account) as any, { chatMessage })), + icon: require('@tabler/icons/flag.svg'), + }); + } menu.push({ text: intl.formatMessage(messages.deleteForMe), action: () => handleDeleteMessage.mutate(chatMessage.id), diff --git a/app/soapbox/utils/features.ts b/app/soapbox/utils/features.ts index 7badea639..a245987bd 100644 --- a/app/soapbox/utils/features.ts +++ b/app/soapbox/utils/features.ts @@ -532,10 +532,17 @@ const getInstanceFeatures = (instance: Instance) => { v.software === PLEROMA && v.build === SOAPBOX && gte(v.version, '2.4.50'), ]), - reportMultipleStatuses: any([ - v.software === MASTODON, - v.software === PLEROMA, - ]), + /** + * Ability to report chat messages. + * @see POST /api/v1/reports + */ + reportChats: v.software === TRUTHSOCIAL, + + /** + * Ability to select more than one status when reporting. + * @see POST /api/v1/reports + */ + reportMultipleStatuses: v.software !== TRUTHSOCIAL, /** * Can request a password reset email through the API. From 8cd84b6128d72b6c7110b1efcf48d944b95d73f9 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Wed, 2 Nov 2022 15:10:13 -0500 Subject: [PATCH 7/7] Chats: accepting_messages --> accepts_chat_messages --- .../chat-page/components/chat-page-settings.tsx | 8 ++++---- .../chats/components/chat-page/components/welcome.tsx | 8 ++++---- .../features/settings/components/messages-settings.tsx | 4 ++-- app/soapbox/normalizers/account.ts | 9 ++++++++- app/soapbox/queries/accounts.ts | 2 +- 5 files changed, 19 insertions(+), 12 deletions(-) diff --git a/app/soapbox/features/chats/components/chat-page/components/chat-page-settings.tsx b/app/soapbox/features/chats/components/chat-page/components/chat-page-settings.tsx index 4c214ff07..e63aeee45 100644 --- a/app/soapbox/features/chats/components/chat-page/components/chat-page-settings.tsx +++ b/app/soapbox/features/chats/components/chat-page/components/chat-page-settings.tsx @@ -7,7 +7,7 @@ import { useOwnAccount } from 'soapbox/hooks'; import { useUpdateCredentials } from 'soapbox/queries/accounts'; type FormData = { - accepting_messages?: boolean + accepts_chat_messages?: boolean chats_onboarded: boolean } @@ -26,7 +26,7 @@ const ChatPageSettings = () => { const [data, setData] = useState({ chats_onboarded: true, - accepting_messages: account?.accepting_messages, + accepts_chat_messages: account?.accepts_chat_messages, }); const handleSubmit = (event: React.FormEvent) => { @@ -49,8 +49,8 @@ const ChatPageSettings = () => { hint={intl.formatMessage(messages.acceptingMessageHint)} > setData((prevData) => ({ ...prevData, accepting_messages: event.target.checked }))} + checked={data.accepts_chat_messages} + onChange={(event) => setData((prevData) => ({ ...prevData, accepts_chat_messages: event.target.checked }))} /> diff --git a/app/soapbox/features/chats/components/chat-page/components/welcome.tsx b/app/soapbox/features/chats/components/chat-page/components/welcome.tsx index d70cf7d93..3d2c3156f 100644 --- a/app/soapbox/features/chats/components/chat-page/components/welcome.tsx +++ b/app/soapbox/features/chats/components/chat-page/components/welcome.tsx @@ -7,7 +7,7 @@ import { useOwnAccount } from 'soapbox/hooks'; import { useUpdateCredentials } from 'soapbox/queries/accounts'; type FormData = { - accepting_messages?: boolean + accepts_chat_messages?: boolean chats_onboarded: boolean } @@ -27,7 +27,7 @@ const Welcome = () => { const [data, setData] = useState({ chats_onboarded: true, - accepting_messages: account?.accepting_messages, + accepts_chat_messages: account?.accepts_chat_messages, }); const handleSubmit = (event: React.FormEvent) => { @@ -65,8 +65,8 @@ const Welcome = () => { hint={intl.formatMessage(messages.acceptingMessageHint)} > setData((prevData) => ({ ...prevData, accepting_messages: event.target.checked }))} + checked={data.accepts_chat_messages} + onChange={(event) => setData((prevData) => ({ ...prevData, accepts_chat_messages: event.target.checked }))} /> diff --git a/app/soapbox/features/settings/components/messages-settings.tsx b/app/soapbox/features/settings/components/messages-settings.tsx index c96dc1468..f5706f058 100644 --- a/app/soapbox/features/settings/components/messages-settings.tsx +++ b/app/soapbox/features/settings/components/messages-settings.tsx @@ -17,7 +17,7 @@ const MessagesSettings = () => { const updateCredentials = useUpdateCredentials(); const handleChange = (event: React.ChangeEvent) => { - updateCredentials.mutate({ accepting_messages: event.target.checked }); + updateCredentials.mutate({ accepts_chat_messages: event.target.checked }); }; if (!account) { @@ -31,7 +31,7 @@ const MessagesSettings = () => { hint={intl.formatMessage(messages.hint)} > diff --git a/app/soapbox/normalizers/account.ts b/app/soapbox/normalizers/account.ts index 63ad77c48..30cfd512d 100644 --- a/app/soapbox/normalizers/account.ts +++ b/app/soapbox/normalizers/account.ts @@ -21,7 +21,7 @@ import type { Emoji, Field, EmbeddedEntity, Relationship } from 'soapbox/types/e // https://docs.joinmastodon.org/entities/account/ export const AccountRecord = ImmutableRecord({ - accepting_messages: false, + accepts_chat_messages: false, acct: '', avatar: '', avatar_static: '', @@ -264,6 +264,12 @@ const normalizeDiscoverable = (account: ImmutableMap) => { return account.set('discoverable', discoverable); }; +/** Normalize message acceptance between Pleroma and Truth Social. */ +const normalizeMessageAcceptance = (account: ImmutableMap) => { + const acceptance = Boolean(account.getIn(['pleroma', 'accepts_chat_messages']) || account.get('accepting_messages')); + return account.set('accepts_chat_messages', acceptance); +}; + /** Normalize undefined/null birthday to empty string. */ const fixBirthday = (account: ImmutableMap) => { const birthday = account.get('birthday'); @@ -285,6 +291,7 @@ export const normalizeAccount = (account: Record) => { normalizeFqn(account); normalizeFavicon(account); normalizeDiscoverable(account); + normalizeMessageAcceptance(account); addDomain(account); addStaffFields(account); fixUsername(account); diff --git a/app/soapbox/queries/accounts.ts b/app/soapbox/queries/accounts.ts index 228c9a2b1..75d1193f6 100644 --- a/app/soapbox/queries/accounts.ts +++ b/app/soapbox/queries/accounts.ts @@ -30,7 +30,7 @@ export type IAccount = { } type UpdateCredentialsData = { - accepting_messages?: boolean + accepts_chat_messages?: boolean chats_onboarded?: boolean }