From 0212166fcc3c60c8d2bd497d668ce41ebc890a2f Mon Sep 17 00:00:00 2001 From: William Grant Date: Thu, 12 Oct 2023 17:12:04 +1100 Subject: [PATCH] feat: moved disappearing logic to a feature folder only moved types and timerOptions so far --- .../header/ConversationHeaderTitle.tsx | 4 +- .../DisappearingModes.tsx | 2 +- .../OverlayDisappearingMessages.tsx | 18 +- .../disappearing-messages/TimeOptions.tsx | 2 +- ts/hooks/useParamSelector.ts | 25 +-- ts/interactions/conversationInteractions.ts | 4 +- ts/models/conversation.ts | 2 +- ts/models/conversationAttributes.ts | 2 +- ts/models/message.ts | 22 +-- ts/models/messageType.ts | 5 +- ts/receiver/dataMessage.ts | 3 +- ts/session/conversations/createClosedGroup.ts | 2 +- .../disappearing_messages/timerOptions.ts | 126 +++++++++++++ ts/session/disappearing_messages/types.ts | 39 ++++ ts/session/group/closed-group.ts | 12 +- .../messages/outgoing/ExpirableMessage.ts | 2 +- ts/session/utils/sync/syncUtils.ts | 20 +- ts/state/ducks/conversations.ts | 4 +- ts/state/selectors/selectedConversation.ts | 14 +- .../disappearing/DisappearingMessage_test.ts | 6 +- ts/test/test-utils/utils/message.ts | 21 ++- ts/util/expiringMessages.ts | 172 +----------------- 22 files changed, 258 insertions(+), 249 deletions(-) create mode 100644 ts/session/disappearing_messages/timerOptions.ts create mode 100644 ts/session/disappearing_messages/types.ts diff --git a/ts/components/conversation/header/ConversationHeaderTitle.tsx b/ts/components/conversation/header/ConversationHeaderTitle.tsx index 836ee5fc5..0c20b8587 100644 --- a/ts/components/conversation/header/ConversationHeaderTitle.tsx +++ b/ts/components/conversation/header/ConversationHeaderTitle.tsx @@ -2,6 +2,7 @@ import { isNumber } from 'lodash'; import React, { useEffect, useMemo, useState } from 'react'; import { useDispatch } from 'react-redux'; import { useIsRightPanelShowing } from '../../../hooks/useUI'; +import { TimerOptions } from '../../../session/disappearing_messages/timerOptions'; import { closeRightPanel, openRightPanel } from '../../../state/ducks/conversations'; import { resetRightOverlayMode, setRightOverlayMode } from '../../../state/ducks/section'; import { @@ -17,7 +18,6 @@ import { useSelectedNotificationSetting, useSelectedSubscriberCount, } from '../../../state/selectors/selectedConversation'; -import { ExpirationTimerOptions } from '../../../util/expiringMessages'; import { ConversationHeaderSubtitle } from './ConversationHeaderSubtitle'; export type SubtitleStrings = Record & { @@ -96,7 +96,7 @@ export const ConversationHeaderTitle = () => { : null; const abbreviatedExpireTime = isNumber(expireTimer) - ? ExpirationTimerOptions.getAbbreviated(expireTimer) + ? TimerOptions.getAbbreviated(expireTimer) : null; return expireTimer && disappearingMessageSettingText diff --git a/ts/components/conversation/right-panel/overlay/disappearing-messages/DisappearingModes.tsx b/ts/components/conversation/right-panel/overlay/disappearing-messages/DisappearingModes.tsx index cbb439c5d..083a8733a 100644 --- a/ts/components/conversation/right-panel/overlay/disappearing-messages/DisappearingModes.tsx +++ b/ts/components/conversation/right-panel/overlay/disappearing-messages/DisappearingModes.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { DisappearingMessageConversationModeType } from '../../../../../util/expiringMessages'; +import { DisappearingMessageConversationModeType } from '../../../../../session/disappearing_messages/types'; import { PanelButtonGroup, PanelLabel } from '../../../../buttons/PanelButton'; import { PanelRadioButton } from '../../../../buttons/PanelRadioButton'; diff --git a/ts/components/conversation/right-panel/overlay/disappearing-messages/OverlayDisappearingMessages.tsx b/ts/components/conversation/right-panel/overlay/disappearing-messages/OverlayDisappearingMessages.tsx index 2d51bd649..99f008ec9 100644 --- a/ts/components/conversation/right-panel/overlay/disappearing-messages/OverlayDisappearingMessages.tsx +++ b/ts/components/conversation/right-panel/overlay/disappearing-messages/OverlayDisappearingMessages.tsx @@ -3,28 +3,26 @@ import { useDispatch, useSelector } from 'react-redux'; import styled from 'styled-components'; import { useTimerOptionsByMode } from '../../../../../hooks/useParamSelector'; import { setDisappearingMessagesByConvoId } from '../../../../../interactions/conversationInteractions'; +import { TimerOptions } from '../../../../../session/disappearing_messages/timerOptions'; +import { DisappearingMessageConversationModeType } from '../../../../../session/disappearing_messages/types'; import { closeRightPanel } from '../../../../../state/ducks/conversations'; import { resetRightOverlayMode } from '../../../../../state/ducks/section'; import { getSelectedConversationExpirationModes, - useSelectedConversationKey, useSelectedConversationDisappearingMode, + useSelectedConversationKey, useSelectedExpireTimer, useSelectedIsGroup, - useSelectedWeAreAdmin, useSelectedIsNoteToSelf, + useSelectedWeAreAdmin, } from '../../../../../state/selectors/selectedConversation'; -import { - DEFAULT_TIMER_OPTION, - DisappearingMessageConversationModeType, -} from '../../../../../util/expiringMessages'; +import { ReleasedFeatures } from '../../../../../util/releaseFeature'; import { Flex } from '../../../../basic/Flex'; import { SessionButton } from '../../../../basic/SessionButton'; import { SpacerLG, SpacerXL } from '../../../../basic/Text'; import { DisappearingModes } from './DisappearingModes'; import { Header } from './Header'; import { TimeOptions } from './TimeOptions'; -import { ReleasedFeatures } from '../../../../../util/releaseFeature'; const StyledScrollContainer = styled.div` width: 100%; @@ -65,9 +63,9 @@ function loadDefaultTimeValue( return modeSelected !== 'off' ? modeSelected !== 'legacy' ? modeSelected === 'deleteAfterSend' - ? DEFAULT_TIMER_OPTION.DELETE_AFTER_SEND - : DEFAULT_TIMER_OPTION.DELETE_AFTER_READ - : DEFAULT_TIMER_OPTION.LEGACY + ? TimerOptions.DEFAULT_OPTIONS.DELETE_AFTER_SEND + : TimerOptions.DEFAULT_OPTIONS.DELETE_AFTER_READ + : TimerOptions.DEFAULT_OPTIONS.LEGACY : 0; } diff --git a/ts/components/conversation/right-panel/overlay/disappearing-messages/TimeOptions.tsx b/ts/components/conversation/right-panel/overlay/disappearing-messages/TimeOptions.tsx index 9717486ef..9590447c4 100644 --- a/ts/components/conversation/right-panel/overlay/disappearing-messages/TimeOptions.tsx +++ b/ts/components/conversation/right-panel/overlay/disappearing-messages/TimeOptions.tsx @@ -1,6 +1,6 @@ import { isEmpty } from 'lodash'; import React from 'react'; -import { TimerOptionsArray } from '../../../../../util/expiringMessages'; +import { TimerOptionsArray } from '../../../../../session/disappearing_messages/timerOptions'; import { PanelButtonGroup, PanelLabel } from '../../../../buttons/PanelButton'; import { PanelRadioButton } from '../../../../buttons/PanelRadioButton'; diff --git a/ts/hooks/useParamSelector.ts b/ts/hooks/useParamSelector.ts index 5cedf308a..903e95a5f 100644 --- a/ts/hooks/useParamSelector.ts +++ b/ts/hooks/useParamSelector.ts @@ -7,19 +7,12 @@ import { } from '../models/conversation'; import { isUsAnySogsFromCache } from '../session/apis/open_group_api/sogsv3/knownBlindedkeys'; import { CONVERSATION } from '../session/constants'; +import { TimerOptions, TimerOptionsArray } from '../session/disappearing_messages/timerOptions'; import { PubKey } from '../session/types'; import { UserUtils } from '../session/utils'; import { StateType } from '../state/reducer'; import { getMessageExpirationProps, getMessageReactsProps } from '../state/selectors/conversations'; import { isPrivateAndFriend } from '../state/selectors/selectedConversation'; -import { - DELETE_AFTER_READ_OPTIONS, - DELETE_AFTER_SEND_OPTIONS, - DELETE_LEGACY_OPTIONS, - ExpirationTimerOptions, - TIMER_OPTIONS, - TimerOptionsArray, -} from '../util/expiringMessages'; export function useAvatarPath(convoId: string | undefined) { const convoProps = useConversationPropsById(convoId); @@ -294,32 +287,32 @@ export function useTimerOptionsByMode(disappearingMessageMode?: string, hasOnlyO const options: TimerOptionsArray = []; if (hasOnlyOneMode) { options.push({ - name: ExpirationTimerOptions.getName(TIMER_OPTIONS[0]), - value: TIMER_OPTIONS[0], + name: TimerOptions.getName(TimerOptions.VALUES[0]), + value: TimerOptions.VALUES[0], }); } switch (disappearingMessageMode) { // TODO legacy messages support will be removed in a future release case 'legacy': options.push( - ...DELETE_LEGACY_OPTIONS.map(option => ({ - name: ExpirationTimerOptions.getName(option), + ...TimerOptions.DELETE_LEGACY.map(option => ({ + name: TimerOptions.getName(option), value: option, })) ); break; case 'deleteAfterRead': options.push( - ...DELETE_AFTER_READ_OPTIONS.map(option => ({ - name: ExpirationTimerOptions.getName(option), + ...TimerOptions.DELETE_AFTER_READ.map(option => ({ + name: TimerOptions.getName(option), value: option, })) ); break; case 'deleteAfterSend': options.push( - ...DELETE_AFTER_SEND_OPTIONS.map(option => ({ - name: ExpirationTimerOptions.getName(option), + ...TimerOptions.DELETE_AFTER_SEND.map(option => ({ + name: TimerOptions.getName(option), value: option, })) ); diff --git a/ts/interactions/conversationInteractions.ts b/ts/interactions/conversationInteractions.ts index 122bf88b1..1b310e351 100644 --- a/ts/interactions/conversationInteractions.ts +++ b/ts/interactions/conversationInteractions.ts @@ -11,9 +11,11 @@ import { Data } from '../data/data'; import { SettingsKey } from '../data/settings-key'; import { uploadFileToFsWithOnionV4 } from '../session/apis/file_server_api/FileServerApi'; import { OpenGroupUtils } from '../session/apis/open_group_api/utils'; +import { GetNetworkTime } from '../session/apis/snode_api/getNetworkTime'; import { getConversationController } from '../session/conversations'; import { getSodiumRenderer } from '../session/crypto'; import { getDecryptedMediaUrl } from '../session/crypto/DecryptedAttachmentsManager'; +import { DisappearingMessageConversationModeType } from '../session/disappearing_messages/types'; import { perfEnd, perfStart } from '../session/utils/Performance'; import { fromHexToArray, toHex } from '../session/utils/String'; import { ConfigurationSync } from '../session/utils/job_runners/jobs/ConfigurationSyncJob'; @@ -44,8 +46,6 @@ import { encryptProfile } from '../util/crypto/profileEncrypter'; import { ReleasedFeatures } from '../util/releaseFeature'; import { Storage, setLastProfileUpdateTimestamp } from '../util/storage'; import { UserGroupsWrapperActions } from '../webworker/workers/browser/libsession_worker_interface'; -import { DisappearingMessageConversationModeType } from '../util/expiringMessages'; -import { GetNetworkTime } from '../session/apis/snode_api/getNetworkTime'; export async function copyPublicKeyByConvoId(convoId: string) { if (OpenGroupUtils.isOpenGroupV2(convoId)) { diff --git a/ts/models/conversation.ts b/ts/models/conversation.ts index f973f7259..134b773c1 100644 --- a/ts/models/conversation.ts +++ b/ts/models/conversation.ts @@ -117,9 +117,9 @@ import { getSubscriberCountOutsideRedux, } from '../state/selectors/sogsRoomInfo'; // decide it it makes sense to move this to a redux slice? +import { DisappearingMessageConversationModeType } from '../session/disappearing_messages/types'; import { changeToDisappearingMessageType, - DisappearingMessageConversationModeType, setExpirationStartTimestamp, } from '../util/expiringMessages'; import { markAttributesAsReadIfNeeded } from './messageFactory'; diff --git a/ts/models/conversationAttributes.ts b/ts/models/conversationAttributes.ts index f99b69e29..6ab29b541 100644 --- a/ts/models/conversationAttributes.ts +++ b/ts/models/conversationAttributes.ts @@ -1,6 +1,6 @@ import { defaults } from 'lodash'; +import { DisappearingMessageConversationModeType } from '../session/disappearing_messages/types'; import { LastMessageStatusType } from '../state/ducks/conversations'; -import { DisappearingMessageConversationModeType } from '../util/expiringMessages'; /** * Private chats have always the type `Private` diff --git a/ts/models/message.ts b/ts/models/message.ts index 90cd73ae2..a36ee0a06 100644 --- a/ts/models/message.ts +++ b/ts/models/message.ts @@ -1,6 +1,7 @@ import Backbone from 'backbone'; import autoBind from 'auto-bind'; +import filesize from 'filesize'; import { cloneDeep, debounce, @@ -14,28 +15,27 @@ import { sortBy, uniq, } from 'lodash'; -import filesize from 'filesize'; import { SignalService } from '../protobuf'; import { getMessageQueue } from '../session'; import { getConversationController } from '../session/conversations'; import { ContentMessage } from '../session/messages/outgoing'; +import { ClosedGroupVisibleMessage } from '../session/messages/outgoing/visibleMessage/ClosedGroupVisibleMessage'; import { PubKey } from '../session/types'; import { + UserUtils, uploadAttachmentsToFileServer, uploadLinkPreviewToFileServer, uploadQuoteThumbnailsToFileServer, - UserUtils, } from '../session/utils'; -import { ClosedGroupVisibleMessage } from '../session/messages/outgoing/visibleMessage/ClosedGroupVisibleMessage'; import { DataExtractionNotificationMsg, - fillMessageAttributesWithDefaults, MessageAttributes, MessageAttributesOptionals, MessageGroupUpdate, MessageModelType, PropsForDataExtractionNotification, PropsForMessageRequestResponse, + fillMessageAttributesWithDefaults, } from './messageType'; import { Data } from '../data/data'; @@ -45,6 +45,7 @@ import { isUsAnySogsFromCache } from '../session/apis/open_group_api/sogsv3/know import { GetNetworkTime } from '../session/apis/snode_api/getNetworkTime'; import { SnodeNamespaces } from '../session/apis/snode_api/namespaces'; import { DURATION } from '../session/constants'; +import { TimerOptions } from '../session/disappearing_messages/timerOptions'; import { OpenGroupVisibleMessage } from '../session/messages/outgoing/visibleMessage/OpenGroupVisibleMessage'; import { VisibleMessage, @@ -56,14 +57,13 @@ import { uploadQuoteThumbnailsV3, } from '../session/utils/AttachmentsV2'; import { perfEnd, perfStart } from '../session/utils/Performance'; -import { buildSyncMessage } from '../session/utils/sync/syncUtils'; import { isUsFromCache } from '../session/utils/User'; +import { buildSyncMessage } from '../session/utils/sync/syncUtils'; import { FindAndFormatContactType, LastMessageStatusType, MessageModelPropsWithoutConvoProps, MessagePropsDetails, - messagesChanged, PropsForAttachment, PropsForExpirationTimer, PropsForExpiringMessage, @@ -76,9 +76,9 @@ import { PropsForGroupUpdateName, PropsForMessageWithoutConvoProps, PropsForQuote, + messagesChanged, } from '../state/ducks/conversations'; import { AttachmentTypeWithPath, isVoiceMessage } from '../types/Attachment'; -import { getAttachmentMetadata } from '../types/message/initializeAttachmentMetadata'; import { deleteExternalMessageFiles, getAbsoluteAttachmentPath, @@ -87,12 +87,12 @@ import { loadQuoteData, } from '../types/MessageAttachment'; import { ReactionList } from '../types/Reaction'; +import { getAttachmentMetadata } from '../types/message/initializeAttachmentMetadata'; import { roomHasBlindEnabled } from '../types/sqlSharedTypes'; import { - ExpirationTimerOptions, - setExpirationStartTimestamp, changeToDisappearingConversationMode, checkForExpireUpdateInContentMessage, + setExpirationStartTimestamp, updateMessageExpiryOnSwarm, } from '../util/expiringMessages'; import { LinkPreviews } from '../util/linkPreviews'; @@ -278,7 +278,7 @@ export class MessageModel extends Backbone.Model { } return window.i18n('timerSetTo', [ - ExpirationTimerOptions.getAbbreviated(expireTimerUpdate.expireTimer || 0), + TimerOptions.getAbbreviated(expireTimerUpdate.expireTimer || 0), ]); } @@ -340,7 +340,7 @@ export class MessageModel extends Backbone.Model { expireTimer || 0 ); - const timespan = ExpirationTimerOptions.getName(expireTimer || 0); + const timespan = TimerOptions.getName(expireTimer || 0); const disabled = !expireTimer; const basicProps: PropsForExpirationTimer = { diff --git a/ts/models/messageType.ts b/ts/models/messageType.ts index 29527a69d..883196af8 100644 --- a/ts/models/messageType.ts +++ b/ts/models/messageType.ts @@ -1,5 +1,9 @@ import { defaultsDeep } from 'lodash'; import { v4 as uuidv4 } from 'uuid'; +import { + DisappearingMessageType, + ExpirationTimerUpdate, +} from '../session/disappearing_messages/types'; import { CallNotificationType, LastMessageStatusType, @@ -8,7 +12,6 @@ import { import { AttachmentTypeWithPath } from '../types/Attachment'; import { Reaction, ReactionList, SortedReactionList } from '../types/Reaction'; import { READ_MESSAGE_STATE } from './conversationAttributes'; -import { DisappearingMessageType, ExpirationTimerUpdate } from '../util/expiringMessages'; export type MessageModelType = 'incoming' | 'outgoing'; diff --git a/ts/receiver/dataMessage.ts b/ts/receiver/dataMessage.ts index bbad20d9d..b3da4dada 100644 --- a/ts/receiver/dataMessage.ts +++ b/ts/receiver/dataMessage.ts @@ -20,12 +20,13 @@ import { createSwarmMessageSentFromNotUs, createSwarmMessageSentFromUs, } from '../models/messageFactory'; +import { DisappearingMessageUpdate } from '../session/disappearing_messages/types'; import { ProfileManager } from '../session/profile_manager/ProfileManager'; import { isUsFromCache } from '../session/utils/User'; import { Action, Reaction } from '../types/Reaction'; import { toLogFormat } from '../types/attachments/Errors'; +import { getMessageReadyToDisappear } from '../util/expiringMessages'; import { Reactions } from '../util/reactions'; -import { DisappearingMessageUpdate, getMessageReadyToDisappear } from '../util/expiringMessages'; function cleanAttachment(attachment: any) { return { diff --git a/ts/session/conversations/createClosedGroup.ts b/ts/session/conversations/createClosedGroup.ts index 10ced4836..ecbbd1e5e 100644 --- a/ts/session/conversations/createClosedGroup.ts +++ b/ts/session/conversations/createClosedGroup.ts @@ -8,6 +8,7 @@ import { updateConfirmModal } from '../../state/ducks/modalDialog'; import { getSwarmPollingInstance } from '../apis/snode_api'; import { SnodeNamespaces } from '../apis/snode_api/namespaces'; import { generateClosedGroupPublicKey, generateCurve25519KeyPairWithoutPrefix } from '../crypto'; +import { DisappearAfterSendOnly, DisappearingMessageType } from '../disappearing_messages/types'; import { ClosedGroupNewMessage, ClosedGroupNewMessageParams, @@ -16,7 +17,6 @@ import { PubKey } from '../types'; import { UserUtils } from '../utils'; import { forceSyncConfigurationNowIfNeeded } from '../utils/sync/syncUtils'; import { getConversationController } from './ConversationController'; -import { DisappearAfterSendOnly, DisappearingMessageType } from '../../util/expiringMessages'; export async function createClosedGroup(groupName: string, members: Array, isV3: boolean) { const setOfMembers = new Set(members); diff --git a/ts/session/disappearing_messages/timerOptions.ts b/ts/session/disappearing_messages/timerOptions.ts new file mode 100644 index 000000000..16f6f76c4 --- /dev/null +++ b/ts/session/disappearing_messages/timerOptions.ts @@ -0,0 +1,126 @@ +import moment from 'moment'; +import { LocalizerKeys } from '../../types/LocalizerKeys'; + +type TimerOptionsEntry = { name: string; value: number }; +export type TimerOptionsArray = Array; + +const timerOptionsDurations: Array<{ + time: number; + unit: moment.DurationInputArg2; + seconds: number; +}> = [ + { time: 0, unit: 'seconds' as moment.DurationInputArg2 }, + { time: 5, unit: 'seconds' as moment.DurationInputArg2 }, + { time: 10, unit: 'seconds' as moment.DurationInputArg2 }, + { time: 30, unit: 'seconds' as moment.DurationInputArg2 }, + { time: 1, unit: 'minute' as moment.DurationInputArg2 }, + { time: 5, unit: 'minutes' as moment.DurationInputArg2 }, + { time: 30, unit: 'minutes' as moment.DurationInputArg2 }, + { time: 1, unit: 'hour' as moment.DurationInputArg2 }, + { time: 6, unit: 'hours' as moment.DurationInputArg2 }, + { time: 12, unit: 'hours' as moment.DurationInputArg2 }, + { time: 1, unit: 'day' as moment.DurationInputArg2 }, + { time: 1, unit: 'week' as moment.DurationInputArg2 }, + { time: 2, unit: 'weeks' as moment.DurationInputArg2 }, +].map(o => { + const duration = moment.duration(o.time, o.unit); // 5, 'seconds' + return { + time: o.time, + unit: o.unit, + seconds: duration.asSeconds(), + }; +}); + +function getTimerOptionName(time: number, unit: moment.DurationInputArg2) { + return ( + window.i18n(['timerOption', time, unit].join('_') as LocalizerKeys) || + moment.duration(time, unit).humanize() + ); +} + +function getTimerOptionAbbreviated(time: number, unit: string) { + return window.i18n(['timerOption', time, unit, 'abbreviated'].join('_') as LocalizerKeys); +} + +function getName(seconds = 0) { + const o = timerOptionsDurations.find(m => m.seconds === seconds); + + if (o) { + return getTimerOptionName(o.time, o.unit); + } + return [seconds, 'seconds'].join(' '); +} + +function getAbbreviated(seconds = 0) { + const o = timerOptionsDurations.find(m => m.seconds === seconds); + + if (o) { + return getTimerOptionAbbreviated(o.time, o.unit); + } + + return [seconds, 's'].join(''); +} + +const VALUES: Array = timerOptionsDurations.map(t => { + return t.seconds; +}); + +const DELETE_AFTER_READ = VALUES.filter(option => { + return ( + option === 10 || // 10 seconds (for development or qa) + option === 30 || // 30 seconds (for development or qa) + option === 60 || // 1 minute (for testing) + option === 300 || // 5 minutes + option === 3600 || // 1 hour + option === 43200 || // 12 hours + option === 86400 || // 1 day + option === 604800 || // 1 week + option === 1209600 // 2 weeks + ); +}); + +const DELETE_AFTER_SEND = VALUES.filter(option => { + return ( + option === 10 || // 10 seconds (for development or qa) + option === 30 || // 30 seconds (for development or qa) + option === 60 || // 1 minute (for testing) + option === 43200 || // 12 hours + option === 86400 || // 1 day + option === 604800 || // 1 week + option === 1209600 // 2 weeks + ); +}); + +// TODO legacy messages support will be removed in a future release +const DELETE_LEGACY = VALUES.filter(option => { + return ( + option === 5 || // 5 seconds + option === 10 || // 10 seconds + option === 30 || // 30 seconds + option === 60 || // 1 minute + option === 300 || // 5 minutes + option === 1800 || // 30 minutes + option === 3600 || // 1 hour + option === 21600 || // 6 hours + option === 43200 || // 12 hours + option === 86400 || // 1 day + option === 604800 // 1 week + ); +}); + +const DEFAULT_OPTIONS = { + DELETE_AFTER_READ: 43200, // 12 hours + DELETE_AFTER_SEND: 86400, // 1 day + // TODO legacy messages support will be removed in a future release + LEGACY: 86400, // 1 day +}; + +export const TimerOptions = { + DEFAULT_OPTIONS, + VALUES, + DELETE_AFTER_READ, + DELETE_AFTER_SEND, + DELETE_LEGACY, + getName, + getAbbreviated, +}; diff --git a/ts/session/disappearing_messages/types.ts b/ts/session/disappearing_messages/types.ts new file mode 100644 index 000000000..10e842a5f --- /dev/null +++ b/ts/session/disappearing_messages/types.ts @@ -0,0 +1,39 @@ +// NOTE this must match Content.ExpirationType in the protobuf +export const DisappearingMessageMode = ['unknown', 'deleteAfterRead', 'deleteAfterSend'] as const; +export type DisappearingMessageType = typeof DisappearingMessageMode[number]; +export type DisappearAfterSendOnly = Exclude; + +// TODO NOTE legacy is strictly used in the UI and is not a valid disappearing message mode +export const DisappearingMessageConversationModes = [ + 'off', + DisappearingMessageMode[1], // deleteAfterRead + DisappearingMessageMode[2], // deleteAfterSend + // TODO legacy messages support will be removed in a future release + 'legacy', +] as const; +export type DisappearingMessageConversationModeType = typeof DisappearingMessageConversationModes[number]; // TODO we should make this type a bit more hardcoded than being just resolved as a string + +// TODO legacy messages support will be removed in a future release +// expirationType and lastDisappearingMessageChangeTimestamp will no longer have an undefined option +/** Used for setting disappearing messages in conversations */ +export type ExpirationTimerUpdate = { + expirationType: DisappearingMessageType | undefined; + expireTimer: number; + lastDisappearingMessageChangeTimestamp: number | undefined; + source: string; + /** updated setting from another device */ + fromSync?: boolean; +}; + +export type DisappearingMessageUpdate = { + expirationType: DisappearingMessageType; + expirationTimer: number; + // This is used for the expirationTimerUpdate + lastDisappearingMessageChangeTimestamp?: number; + // TODO legacy messages support will be removed in a future release + isLegacyConversationSettingMessage?: boolean; + isLegacyDataMessage?: boolean; + isDisappearingMessagesV2Released?: boolean; + shouldDisappearButIsntMessage?: boolean; + isOutdated?: boolean; +}; diff --git a/ts/session/group/closed-group.ts b/ts/session/group/closed-group.ts index 9e7f7a96b..bb8861cc4 100644 --- a/ts/session/group/closed-group.ts +++ b/ts/session/group/closed-group.ts @@ -1,7 +1,6 @@ import _ from 'lodash'; import { v4 as uuidv4 } from 'uuid'; -import { PubKey } from '../types'; import { getMessageQueue } from '..'; import { Data } from '../../data/data'; import { ConversationModel } from '../../models/conversation'; @@ -13,23 +12,24 @@ import { distributingClosedGroupEncryptionKeyPairs, } from '../../receiver/closedGroups'; import { ECKeyPair } from '../../receiver/keypairs'; +import { + changeToDisappearingMessageType, + setExpirationStartTimestamp, +} from '../../util/expiringMessages'; import { GetNetworkTime } from '../apis/snode_api/getNetworkTime'; import { SnodeNamespaces } from '../apis/snode_api/namespaces'; import { getConversationController } from '../conversations'; import { generateCurve25519KeyPairWithoutPrefix } from '../crypto'; import { encryptUsingSessionProtocol } from '../crypto/MessageEncrypter'; +import { DisappearAfterSendOnly } from '../disappearing_messages/types'; import { ClosedGroupAddedMembersMessage } from '../messages/outgoing/controlMessage/group/ClosedGroupAddedMembersMessage'; import { ClosedGroupEncryptionPairMessage } from '../messages/outgoing/controlMessage/group/ClosedGroupEncryptionPairMessage'; import { ClosedGroupNameChangeMessage } from '../messages/outgoing/controlMessage/group/ClosedGroupNameChangeMessage'; import { ClosedGroupNewMessage } from '../messages/outgoing/controlMessage/group/ClosedGroupNewMessage'; import { ClosedGroupRemovedMembersMessage } from '../messages/outgoing/controlMessage/group/ClosedGroupRemovedMembersMessage'; +import { PubKey } from '../types'; import { UserUtils } from '../utils'; import { fromHexToArray, toHex } from '../utils/String'; -import { - DisappearAfterSendOnly, - changeToDisappearingMessageType, - setExpirationStartTimestamp, -} from '../../util/expiringMessages'; export type GroupInfo = { id: string; diff --git a/ts/session/messages/outgoing/ExpirableMessage.ts b/ts/session/messages/outgoing/ExpirableMessage.ts index 54505de7f..81d29ca22 100644 --- a/ts/session/messages/outgoing/ExpirableMessage.ts +++ b/ts/session/messages/outgoing/ExpirableMessage.ts @@ -1,6 +1,6 @@ import { SignalService } from '../../../protobuf'; -import { DisappearingMessageType } from '../../../util/expiringMessages'; import { DURATION, TTL_DEFAULT } from '../../constants'; +import { DisappearingMessageType } from '../../disappearing_messages/types'; import { ContentMessage } from './ContentMessage'; import { MessageParams } from './Message'; diff --git a/ts/session/utils/sync/syncUtils.ts b/ts/session/utils/sync/syncUtils.ts index 76d0d2b08..cb4d13b8a 100644 --- a/ts/session/utils/sync/syncUtils.ts +++ b/ts/session/utils/sync/syncUtils.ts @@ -1,5 +1,5 @@ -import { v4 as uuidv4 } from 'uuid'; import _, { isEmpty } from 'lodash'; +import { v4 as uuidv4 } from 'uuid'; import { UserUtils } from '..'; import { getMessageQueue } from '../..'; import { Data } from '../../../data/data'; @@ -8,32 +8,32 @@ import { ConversationModel } from '../../../models/conversation'; import { SignalService } from '../../../protobuf'; import { ECKeyPair } from '../../../receiver/keypairs'; import { ConfigurationSyncJobDone } from '../../../shims/events'; +import { ReleasedFeatures } from '../../../util/releaseFeature'; +import { Storage } from '../../../util/storage'; +import { getCompleteUrlFromRoom } from '../../apis/open_group_api/utils/OpenGroupUtils'; import { SnodeNamespaces } from '../../apis/snode_api/namespaces'; import { DURATION } from '../../constants'; import { getConversationController } from '../../conversations'; +import { DisappearingMessageUpdate } from '../../disappearing_messages/types'; +import { DataMessage } from '../../messages/outgoing'; import { ConfigurationMessage, ConfigurationMessageClosedGroup, ConfigurationMessageContact, } from '../../messages/outgoing/controlMessage/ConfigurationMessage'; +import { ExpirationTimerUpdateMessage } from '../../messages/outgoing/controlMessage/ExpirationTimerUpdateMessage'; import { MessageRequestResponse } from '../../messages/outgoing/controlMessage/MessageRequestResponse'; import { SharedConfigMessage } from '../../messages/outgoing/controlMessage/SharedConfigMessage'; +import { UnsendMessage } from '../../messages/outgoing/controlMessage/UnsendMessage'; import { AttachmentPointerWithUrl, PreviewWithAttachmentUrl, Quote, VisibleMessage, } from '../../messages/outgoing/visibleMessage/VisibleMessage'; -import { ConfigurationSync } from '../job_runners/jobs/ConfigurationSyncJob'; -import { fromBase64ToArray, fromHexToArray } from '../String'; -import { Storage } from '../../../util/storage'; -import { ReleasedFeatures } from '../../../util/releaseFeature'; -import { getCompleteUrlFromRoom } from '../../apis/open_group_api/utils/OpenGroupUtils'; import { PubKey } from '../../types'; -import { DisappearingMessageUpdate } from '../../../util/expiringMessages'; -import { ExpirationTimerUpdateMessage } from '../../messages/outgoing/controlMessage/ExpirationTimerUpdateMessage'; -import { UnsendMessage } from '../../messages/outgoing/controlMessage/UnsendMessage'; -import { DataMessage } from '../../messages/outgoing'; +import { fromBase64ToArray, fromHexToArray } from '../String'; +import { ConfigurationSync } from '../job_runners/jobs/ConfigurationSyncJob'; const ITEM_ID_LAST_SYNC_TIMESTAMP = 'lastSyncedTimestamp'; diff --git a/ts/state/ducks/conversations.ts b/ts/state/ducks/conversations.ts index 02ee162fc..5e2b70b16 100644 --- a/ts/state/ducks/conversations.ts +++ b/ts/state/ducks/conversations.ts @@ -16,11 +16,11 @@ import { PropsForMessageRequestResponse, } from '../../models/messageType'; import { getConversationController } from '../../session/conversations'; -import { ReactionList } from '../../types/Reaction'; import { DisappearingMessageConversationModeType, DisappearingMessageType, -} from '../../util/expiringMessages'; +} from '../../session/disappearing_messages/types'; +import { ReactionList } from '../../types/Reaction'; export type CallNotificationType = 'missed-call' | 'started-call' | 'answered-a-call'; diff --git a/ts/state/selectors/selectedConversation.ts b/ts/state/selectors/selectedConversation.ts index 49c977216..17df5b03e 100644 --- a/ts/state/selectors/selectedConversation.ts +++ b/ts/state/selectors/selectedConversation.ts @@ -1,17 +1,17 @@ import { isString } from 'lodash'; import { useSelector } from 'react-redux'; import { ConversationTypeEnum, isOpenOrClosedGroup } from '../../models/conversationAttributes'; -import { PubKey } from '../../session/types'; -import { UserUtils } from '../../session/utils'; -import { StateType } from '../reducer'; -import { getCanWrite, getModerators, getSubscriberCount } from './sogsRoomInfo'; -import { getIsMessageSelectionMode, getSelectedConversation } from './conversations'; import { - DisappearingMessageConversationModes, DisappearingMessageConversationModeType, -} from '../../util/expiringMessages'; + DisappearingMessageConversationModes, +} from '../../session/disappearing_messages/types'; +import { PubKey } from '../../session/types'; +import { UserUtils } from '../../session/utils'; import { ReleasedFeatures } from '../../util/releaseFeature'; import { ReduxConversationType } from '../ducks/conversations'; +import { StateType } from '../reducer'; +import { getIsMessageSelectionMode, getSelectedConversation } from './conversations'; +import { getCanWrite, getModerators, getSubscriberCount } from './sogsRoomInfo'; /** * Returns the formatted text for notification setting. diff --git a/ts/test/session/unit/disappearing/DisappearingMessage_test.ts b/ts/test/session/unit/disappearing/DisappearingMessage_test.ts index 60d4f953c..f341bf3de 100644 --- a/ts/test/session/unit/disappearing/DisappearingMessage_test.ts +++ b/ts/test/session/unit/disappearing/DisappearingMessage_test.ts @@ -4,11 +4,13 @@ import Sinon from 'sinon'; import { ConversationModel } from '../../../../models/conversation'; import { ConversationTypeEnum } from '../../../../models/conversationAttributes'; import { GetNetworkTime } from '../../../../session/apis/snode_api/getNetworkTime'; -import { UserUtils } from '../../../../session/utils'; -import { isValidUnixTimestamp } from '../../../../session/utils/Timestamps'; import { DisappearingMessageConversationModeType, DisappearingMessageType, +} from '../../../../session/disappearing_messages/types'; +import { UserUtils } from '../../../../session/utils'; +import { isValidUnixTimestamp } from '../../../../session/utils/Timestamps'; +import { changeToDisappearingConversationMode, changeToDisappearingMessageType, checkForExpireUpdateInContentMessage, diff --git a/ts/test/test-utils/utils/message.ts b/ts/test/test-utils/utils/message.ts index 90e6ce24c..839d59b78 100644 --- a/ts/test/test-utils/utils/message.ts +++ b/ts/test/test-utils/utils/message.ts @@ -1,20 +1,23 @@ -import { v4 as uuid } from 'uuid'; import { isEmpty } from 'lodash'; -import { generateFakePubKey } from './pubkey'; -import { ClosedGroupVisibleMessage } from '../../../session/messages/outgoing/visibleMessage/ClosedGroupVisibleMessage'; -import { VisibleMessage } from '../../../session/messages/outgoing/visibleMessage/VisibleMessage'; -import { OpenGroupMessageV2 } from '../../../session/apis/open_group_api/opengroupV2/OpenGroupMessageV2'; +import { v4 as uuid } from 'uuid'; import { TestUtils } from '..'; -import { OpenGroupRequestCommonType } from '../../../session/apis/open_group_api/opengroupV2/ApiUtil'; -import { OpenGroupVisibleMessage } from '../../../session/messages/outgoing/visibleMessage/OpenGroupVisibleMessage'; import { MessageModel } from '../../../models/message'; +import { OpenGroupRequestCommonType } from '../../../session/apis/open_group_api/opengroupV2/ApiUtil'; +import { OpenGroupMessageV2 } from '../../../session/apis/open_group_api/opengroupV2/OpenGroupMessageV2'; import { OpenGroupMessageV4, OpenGroupReactionMessageV4, } from '../../../session/apis/open_group_api/opengroupV2/OpenGroupServerPoller'; -import { OpenGroupReaction } from '../../../types/Reaction'; -import { DisappearingMessageType, ExpirationTimerUpdate } from '../../../util/expiringMessages'; +import { + DisappearingMessageType, + ExpirationTimerUpdate, +} from '../../../session/disappearing_messages/types'; import { ExpirationTimerUpdateMessage } from '../../../session/messages/outgoing/controlMessage/ExpirationTimerUpdateMessage'; +import { ClosedGroupVisibleMessage } from '../../../session/messages/outgoing/visibleMessage/ClosedGroupVisibleMessage'; +import { OpenGroupVisibleMessage } from '../../../session/messages/outgoing/visibleMessage/OpenGroupVisibleMessage'; +import { VisibleMessage } from '../../../session/messages/outgoing/visibleMessage/VisibleMessage'; +import { OpenGroupReaction } from '../../../types/Reaction'; +import { generateFakePubKey } from './pubkey'; export function generateVisibleMessage({ identifier, diff --git a/ts/util/expiringMessages.ts b/ts/util/expiringMessages.ts index f658b48f4..cfb8a7bec 100644 --- a/ts/util/expiringMessages.ts +++ b/ts/util/expiringMessages.ts @@ -1,7 +1,5 @@ import { throttle, uniq } from 'lodash'; -import moment from 'moment'; import { messagesExpired } from '../state/ducks/conversations'; -import { LocalizerKeys } from '../types/LocalizerKeys'; import { initWallClockListener } from './wallClockListener'; import { Data } from '../data/data'; @@ -11,50 +9,15 @@ import { ProtobufUtils, SignalService } from '../protobuf'; import { expireMessageOnSnode } from '../session/apis/snode_api/expireRequest'; import { GetNetworkTime } from '../session/apis/snode_api/getNetworkTime'; import { getConversationController } from '../session/conversations'; +import { + DisappearingMessageConversationModeType, + DisappearingMessageMode, + DisappearingMessageType, + DisappearingMessageUpdate, +} from '../session/disappearing_messages/types'; import { isValidUnixTimestamp } from '../session/utils/Timestamps'; import { ReleasedFeatures } from './releaseFeature'; -// NOTE this must match Content.ExpirationType in the protobuf -// TODO double check this -export const DisappearingMessageMode = ['unknown', 'deleteAfterRead', 'deleteAfterSend'] as const; -export type DisappearingMessageType = typeof DisappearingMessageMode[number]; -export type DisappearAfterSendOnly = Exclude; -// NOTE these cannot be imported in the nodejs side yet. We need to move the types to the own file with no window imports -// TODO legacy messages support will be removed in a future release -// TODO NOTE legacy is strictly used in the UI and is not a valid disappearing message mode -export const DisappearingMessageConversationModes = [ - 'off', - DisappearingMessageMode[1], // deleteAfterRead - DisappearingMessageMode[2], // deleteAfterSend - 'legacy', -] as const; -export type DisappearingMessageConversationModeType = typeof DisappearingMessageConversationModes[number]; // TODO we should make this type a bit more hardcoded than being just resolved as a string - -// TODO legacy messages support will be removed in a future release -// expirationType and lastDisappearingMessageChangeTimestamp will no longer have an undefined option -/** Used for setting disappearing messages in conversations */ -export type ExpirationTimerUpdate = { - expirationType: DisappearingMessageType | undefined; - expireTimer: number; - lastDisappearingMessageChangeTimestamp: number | undefined; - source: string; - /** updated setting from another device */ - fromSync?: boolean; -}; - -export type DisappearingMessageUpdate = { - expirationType: DisappearingMessageType; - expirationTimer: number; - // This is used for the expirationTimerUpdate - lastDisappearingMessageChangeTimestamp?: number; - // TODO legacy messages support will be removed in a future release - isLegacyConversationSettingMessage?: boolean; - isLegacyDataMessage?: boolean; - isDisappearingMessagesV2Released?: boolean; - shouldDisappearButIsntMessage?: boolean; - isOutdated?: boolean; -}; - export async function destroyMessagesAndUpdateRedux( messages: Array<{ conversationKey: string; @@ -125,6 +88,7 @@ async function destroyExpiredMessages() { } let timeout: NodeJS.Timeout | undefined; + async function checkExpiringMessages() { // Look up the next expiring message and set a timer to destroy it const messages = await Data.getNextExpiringMessage(); @@ -178,131 +142,11 @@ const updateExpiringMessagesCheck = () => { void throttledCheckExpiringMessages(); }; -// #region Timer Options - -const timerOptionsDurations: Array<{ - time: number; - unit: moment.DurationInputArg2; - seconds: number; -}> = [ - { time: 0, unit: 'seconds' as moment.DurationInputArg2 }, - { time: 5, unit: 'seconds' as moment.DurationInputArg2 }, - { time: 10, unit: 'seconds' as moment.DurationInputArg2 }, - { time: 30, unit: 'seconds' as moment.DurationInputArg2 }, - { time: 1, unit: 'minute' as moment.DurationInputArg2 }, - { time: 5, unit: 'minutes' as moment.DurationInputArg2 }, - { time: 30, unit: 'minutes' as moment.DurationInputArg2 }, - { time: 1, unit: 'hour' as moment.DurationInputArg2 }, - { time: 6, unit: 'hours' as moment.DurationInputArg2 }, - { time: 12, unit: 'hours' as moment.DurationInputArg2 }, - { time: 1, unit: 'day' as moment.DurationInputArg2 }, - { time: 1, unit: 'week' as moment.DurationInputArg2 }, - { time: 2, unit: 'weeks' as moment.DurationInputArg2 }, -].map(o => { - const duration = moment.duration(o.time, o.unit); // 5, 'seconds' - return { - time: o.time, - unit: o.unit, - seconds: duration.asSeconds(), - }; -}); - -function getTimerOptionName(time: number, unit: moment.DurationInputArg2) { - return ( - window.i18n(['timerOption', time, unit].join('_') as LocalizerKeys) || - moment.duration(time, unit).humanize() - ); -} - -function getTimerOptionAbbreviated(time: number, unit: string) { - return window.i18n(['timerOption', time, unit, 'abbreviated'].join('_') as LocalizerKeys); -} - -function getName(seconds = 0) { - const o = timerOptionsDurations.find(m => m.seconds === seconds); - - if (o) { - return getTimerOptionName(o.time, o.unit); - } - return [seconds, 'seconds'].join(' '); -} - -function getAbbreviated(seconds = 0) { - const o = timerOptionsDurations.find(m => m.seconds === seconds); - - if (o) { - return getTimerOptionAbbreviated(o.time, o.unit); - } - - return [seconds, 's'].join(''); -} - -type TimerOptionsEntry = { name: string; value: number }; -export type TimerOptionsArray = Array; - -export const TIMER_OPTIONS: Array = timerOptionsDurations.map(t => { - return t.seconds; -}); - -export const DELETE_AFTER_READ_OPTIONS = TIMER_OPTIONS.filter(option => { - return ( - option === 10 || // 10 seconds (for development or qa) - option === 30 || // 30 seconds (for development or qa) - option === 60 || // 1 minute (for testing) - option === 300 || // 5 minutes - option === 3600 || // 1 hour - option === 43200 || // 12 hours - option === 86400 || // 1 day - option === 604800 || // 1 week - option === 1209600 // 2 weeks - ); -}); - -export const DELETE_AFTER_SEND_OPTIONS = TIMER_OPTIONS.filter(option => { - return ( - option === 10 || // 10 seconds (for development or qa) - option === 30 || // 30 seconds (for development or qa) - option === 60 || // 1 minute (for testing) - option === 43200 || // 12 hours - option === 86400 || // 1 day - option === 604800 || // 1 week - option === 1209600 // 2 weeks - ); -}); - -// TODO legacy messages support will be removed in a future release -export const DELETE_LEGACY_OPTIONS = TIMER_OPTIONS.filter(option => { - return ( - option === 5 || // 5 seconds - option === 10 || // 10 seconds - option === 30 || // 30 seconds - option === 60 || // 1 minute - option === 300 || // 5 minutes - option === 1800 || // 30 minutes - option === 3600 || // 1 hour - option === 21600 || // 6 hours - option === 43200 || // 12 hours - option === 86400 || // 1 day - option === 604800 // 1 week - ); -}); - -export const DEFAULT_TIMER_OPTION = { - DELETE_AFTER_READ: 43200, // 12 hours - DELETE_AFTER_SEND: 86400, // 1 day - // TODO legacy messages support will be removed in a future release - LEGACY: 86400, // 1 day -}; - export const ExpirationTimerOptions = { - getName, - getAbbreviated, - updateExpiringMessagesCheck, initExpiringMessageListener, + updateExpiringMessagesCheck, }; -// #endregion Timer Options - export function setExpirationStartTimestamp( mode: DisappearingMessageConversationModeType, timestamp?: number,