From 9d8cca1970cfdc30bb61fc96923ce7f2a360fb89 Mon Sep 17 00:00:00 2001 From: William Grant Date: Fri, 1 Sep 2023 17:02:57 +1000 Subject: [PATCH] feat: convert between conversation and message model disappearing modes in key locations remove disappearing messages from convo header context menu --- .../header/ConversationHeaderTitle.tsx | 4 +- .../DisappearingModes.tsx | 10 +-- .../OverlayDisappearingMessages.tsx | 10 +-- ts/components/menu/ConversationHeaderMenu.tsx | 59 +---------------- ts/models/conversation.ts | 18 ++---- ts/models/message.ts | 33 +++++++--- ts/receiver/contentMessage.ts | 6 +- ts/receiver/queuedJob.ts | 7 +- ts/session/group/closed-group.ts | 3 +- ts/session/utils/calling/CallManager.ts | 24 ++++--- ts/state/selectors/selectedConversation.ts | 19 ++++-- ts/util/expiringMessages.ts | 64 +++++++++++++++---- 12 files changed, 138 insertions(+), 119 deletions(-) diff --git a/ts/components/conversation/header/ConversationHeaderTitle.tsx b/ts/components/conversation/header/ConversationHeaderTitle.tsx index fc139632e..2b136ba04 100644 --- a/ts/components/conversation/header/ConversationHeaderTitle.tsx +++ b/ts/components/conversation/header/ConversationHeaderTitle.tsx @@ -7,7 +7,7 @@ import { resetRightOverlayMode, setRightOverlayMode } from '../../../state/ducks import { isRightPanelShowing } from '../../../state/selectors/conversations'; import { useSelectedConversationKey, - useSelectedExpirationType, + useSelectedConversationExpirationType, useSelectedExpireTimer, useSelectedIsGroup, useSelectedIsKickedFromGroup, @@ -47,7 +47,7 @@ export const ConversationHeaderTitle = () => { const members = useSelectedMembers(); const expireTimer = useSelectedExpireTimer(); - const expirationType = useSelectedExpirationType(); + const expirationType = useSelectedConversationExpirationType(); const convoName = useConversationUsername(selectedConvoKey); const [visibleSubtitle, setVisibleSubtitle] = useState('notifications'); 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 8b8349b4a..69fc9a6d4 100644 --- a/ts/components/conversation/right-panel/overlay/disappearing-messages/DisappearingModes.tsx +++ b/ts/components/conversation/right-panel/overlay/disappearing-messages/DisappearingModes.tsx @@ -21,7 +21,7 @@ function loadDataTestId(mode: DisappearingMessageConversationType) { type DisappearingModesProps = { options: Record; selected?: DisappearingMessageConversationType; - setSelected: (value: string) => void; + setSelected: (value: DisappearingMessageConversationType) => void; hasOnlyOneMode?: boolean; }; @@ -36,7 +36,7 @@ export const DisappearingModes = (props: DisappearingModesProps) => { <> {window.i18n('disappearingMessagesModeLabel')} - {Object.keys(options).map((mode: DisappearingMessageConversationType) => { + {Object.keys(options).map(mode => { const optionI18n = mode === 'legacy' ? window.i18n('disappearingMessagesModeLegacy') @@ -63,11 +63,11 @@ export const DisappearingModes = (props: DisappearingModesProps) => { value={mode} isSelected={selected === mode} onSelect={() => { - setSelected(mode); + setSelected(mode as DisappearingMessageConversationType); }} - disabled={options[mode]} + disabled={options[mode as DisappearingMessageConversationType]} noBackgroundColor={true} - dataTestId={loadDataTestId(mode)} + dataTestId={loadDataTestId(mode as DisappearingMessageConversationType)} /> ); })} 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 5aebeff4b..0c3f8410f 100644 --- a/ts/components/conversation/right-panel/overlay/disappearing-messages/OverlayDisappearingMessages.tsx +++ b/ts/components/conversation/right-panel/overlay/disappearing-messages/OverlayDisappearingMessages.tsx @@ -8,7 +8,7 @@ import { resetRightOverlayMode } from '../../../../../state/ducks/section'; import { getSelectedConversationExpirationModes, useSelectedConversationKey, - useSelectedExpirationType, + useSelectedConversationExpirationType, useSelectedExpireTimer, useSelectedIsGroup, useSelectedWeAreAdmin, @@ -63,7 +63,7 @@ function loadDefaultTimeValue(modeSelected: DisappearingMessageConversationType } export type PropsForExpirationSettings = { - expirationType: string | undefined; + expirationType: DisappearingMessageConversationType | undefined; expireTimer: number | undefined; isGroup: boolean | undefined; weAreAdmin: boolean | undefined; @@ -75,16 +75,16 @@ export const OverlayDisappearingMessages = () => { const disappearingModeOptions = useSelector(getSelectedConversationExpirationModes); // NOTE if there is only 'off' and one disappearing message mode then we trigger single mode - const singleMode = + const singleMode: DisappearingMessageConversationType | undefined = disappearingModeOptions && disappearingModeOptions.off !== undefined && Object.keys(disappearingModeOptions).length === 2 - ? Object.keys(disappearingModeOptions)[1] + ? (Object.keys(disappearingModeOptions)[1] as DisappearingMessageConversationType) : undefined; const hasOnlyOneMode = Boolean(singleMode && singleMode.length > 0); const isGroup = useSelectedIsGroup(); - const expirationType = useSelectedExpirationType(); + const expirationType = useSelectedConversationExpirationType(); const expireTimer = useSelectedExpireTimer(); const weAreAdmin = useSelectedWeAreAdmin(); diff --git a/ts/components/menu/ConversationHeaderMenu.tsx b/ts/components/menu/ConversationHeaderMenu.tsx index acc679e37..b98cf8073 100644 --- a/ts/components/menu/ConversationHeaderMenu.tsx +++ b/ts/components/menu/ConversationHeaderMenu.tsx @@ -1,16 +1,11 @@ import React from 'react'; -import { animation, Item, Menu, Submenu } from 'react-contexify'; +import { animation, Menu } from 'react-contexify'; import { useSelector } from 'react-redux'; import { isSearching } from '../../state/selectors/search'; import { useSelectedConversationKey, - useSelectedIsActive, - useSelectedIsBlocked, - useSelectedIsKickedFromGroup, - useSelectedIsLeft, useSelectedIsPrivate, useSelectedIsPrivateFriend, - useSelectedIsPublic, } from '../../state/selectors/selectedConversation'; import { ContextConversationProvider } from '../leftpane/conversation-list-item/ConvoIdContext'; @@ -35,8 +30,6 @@ import { UnbanMenuItem, UpdateGroupNameMenuItem, } from './Menu'; -import { getTimerOptions } from '../../state/selectors/timerOptions'; -import { setDisappearingMessagesByConvoId } from '../../interactions/conversationInteractions'; export type PropsConversationHeaderMenu = { triggerId: string; @@ -66,7 +59,6 @@ export const ConversationHeaderMenu = (props: PropsConversationHeaderMenu) => { - @@ -90,52 +82,3 @@ export const ConversationHeaderMenu = (props: PropsConversationHeaderMenu) => { ); }; - -/** - * Only accessible through the triple dots menu on the conversation header. Not on the Conversation list item, because there is too much to check for before showing it - */ -const DisappearingMessageMenuItem = (): JSX.Element | null => { - const selectedConvoId = useSelectedConversationKey(); - const isBlocked = useSelectedIsBlocked(); - const isActive = useSelectedIsActive(); - const isPublic = useSelectedIsPublic(); - const isLeft = useSelectedIsLeft(); - const isKickedFromGroup = useSelectedIsKickedFromGroup(); - const timerOptions = useSelector(getTimerOptions).timerOptions; - const isFriend = useSelectedIsPrivateFriend(); - const isPrivate = useSelectedIsPrivate(); - - if ( - !selectedConvoId || - isPublic || - isLeft || - isKickedFromGroup || - isBlocked || - !isActive || - (isPrivate && !isFriend) - ) { - return null; - } - - // const isRtlMode = isRtlBody(); - - return ( - // Remove the && false to make context menu work with RTL support - - {timerOptions.map(item => ( - { - // TODO Confirm that this works? - void setDisappearingMessagesByConvoId(selectedConvoId, item.name, item.value); - }} - > - {item.name} - - ))} - - ); -}; diff --git a/ts/models/conversation.ts b/ts/models/conversation.ts index fa8dd4739..717162f86 100644 --- a/ts/models/conversation.ts +++ b/ts/models/conversation.ts @@ -119,7 +119,7 @@ import { import { DisappearingMessageConversationType, - resolveLegacyDisappearingMode, + changeToDisappearingMessageType, } from '../util/expiringMessages'; import { markAttributesAsReadIfNeeded } from './messageFactory'; import { ReleasedFeatures } from '../util/releaseFeature'; @@ -739,8 +739,9 @@ export class ConversationModel extends Backbone.Model { public async sendMessage(msg: SendMessageType) { const { attachments, body, groupInvitation, preview, quote } = msg; this.clearTypingTimers(); - const expirationType = this.get('expirationType'); const expireTimer = this.get('expireTimer'); + const expirationType = changeToDisappearingMessageType(this, this.get('expirationType')); + const networkTimestamp = GetNetworkTime.getNowWithNetworkOffset(); window?.log?.info( @@ -856,12 +857,6 @@ export class ConversationModel extends Backbone.Model { const isOutgoing = Boolean(!receivedAt); source = source || UserUtils.getOurPubKeyStrFromCache(); - // Note the legacy type should only be in the UI, it should change the the conversation type default before we send - if (expirationType === 'legacy') { - expirationType = resolveLegacyDisappearingMode(this, expireTimer); - window.log.debug(`WIP: resolving legacy disappearing mode to ${expirationType}`); - } - // When we add a disappearing messages notification to the conversation, we want it // to be above the message that initiated that change, hence the subtraction. const timestamp = (receivedAt || Date.now()) - 1; @@ -881,6 +876,7 @@ export class ConversationModel extends Backbone.Model { }); let message: MessageModel | undefined = existingMessage || undefined; + const messageExpirationType = changeToDisappearingMessageType(this, expirationType); // we don't have info about who made the change and when, when we get a change from a config message, so do not add a control message // TODO NOTE We might not show it in the UI but still need to process it using the senttimestamp as the lastchange timestamp for config messages @@ -888,7 +884,7 @@ export class ConversationModel extends Backbone.Model { const commonAttributes = { flags: SignalService.DataMessage.Flags.EXPIRATION_TIMER_UPDATE, expirationTimerUpdate: { - expirationType, + expirationType: messageExpirationType, expireTimer, lastDisappearingMessageChangeTimestamp, source, @@ -896,7 +892,7 @@ export class ConversationModel extends Backbone.Model { }, // TODO legacy messages support will be removed in a future release expirationType: ReleasedFeatures.isDisappearMessageV2FeatureReleasedCached() - ? expirationType + ? messageExpirationType : undefined, expireTimer: ReleasedFeatures.isDisappearMessageV2FeatureReleasedCached() ? expireTimer @@ -943,7 +939,7 @@ export class ConversationModel extends Backbone.Model { const expireUpdate = { identifier: message?.id, timestamp, - expirationType, + expirationType: messageExpirationType, expireTimer, lastDisappearingMessageChangeTimestamp, }; diff --git a/ts/models/message.ts b/ts/models/message.ts index d310cc01b..e5d1dca90 100644 --- a/ts/models/message.ts +++ b/ts/models/message.ts @@ -95,8 +95,8 @@ import { DisappearingMessageUpdate, ExpirationTimerOptions, isLegacyDisappearingModeEnabled, - resolveLegacyDisappearingMode, setExpirationStartTimestamp, + changeToDisappearingMessageConversationType, } from '../util/expiringMessages'; import { LinkPreviews } from '../util/linkPreviews'; import { Notifications } from '../util/notifications'; @@ -264,8 +264,18 @@ export class MessageModel extends Backbone.Model { } if (this.isExpirationTimerUpdate()) { const expireTimerUpdate = this.get('expirationTimerUpdate'); - const expirationType = expireTimerUpdate?.expirationType; const expireTimer = expireTimerUpdate?.expireTimer; + const convo = this.getConversation(); + if (!convo) { + return ''; + } + + const expirationType = changeToDisappearingMessageConversationType( + convo, + expireTimerUpdate?.expirationType, + expireTimer + ); + if (!expireTimerUpdate || expirationType === 'off' || !expireTimer || expireTimer === 0) { return window.i18n('disappearingMessagesDisabled'); } @@ -320,11 +330,19 @@ export class MessageModel extends Backbone.Model { } const timerUpdate = this.get('expirationTimerUpdate'); - if (!timerUpdate || !timerUpdate.source) { + const convo = this.getConversation(); + + if (!timerUpdate || !timerUpdate.source || !convo) { return null; } - const { expirationType, expireTimer, fromSync, source } = timerUpdate; + const { expireTimer, fromSync, source } = timerUpdate; + const expirationType = changeToDisappearingMessageConversationType( + convo, + timerUpdate?.expirationType || 'unknown', + expireTimer || 0 + ); + const timespan = ExpirationTimerOptions.getName(expireTimer || 0); const disabled = !expireTimer; @@ -1019,11 +1037,8 @@ export class MessageModel extends Backbone.Model { ? Number(dataMessage.expireTimer) : content.expirationTimer; - let expirationType: DisappearingMessageType = DisappearingMessageMode[content.expirationType]; - - if (isLegacyDisappearingDataMessage) { - expirationType = resolveLegacyDisappearingMode(conversation, expirationTimer); - } + const expirationType: DisappearingMessageType = + DisappearingMessageMode[content.expirationType]; const lastDisappearingMessageChangeTimestamp = content.lastDisappearingMessageChangeTimestamp ? Number(content.lastDisappearingMessageChangeTimestamp) diff --git a/ts/receiver/contentMessage.ts b/ts/receiver/contentMessage.ts index 7606f8a7c..5cef3580b 100644 --- a/ts/receiver/contentMessage.ts +++ b/ts/receiver/contentMessage.ts @@ -36,6 +36,7 @@ import { ConfigMessageHandler } from './configMessage'; import { ECKeyPair } from './keypairs'; import { ContactsWrapperActions } from '../webworker/workers/browser/libsession_worker_interface'; import { + changeToDisappearingMessageType, checkForExpireUpdateInContentMessage, checkHasOutdatedClient, isLegacyDisappearingModeEnabled, @@ -846,7 +847,10 @@ export async function handleDataExtractionNotification( }, unread: READ_MESSAGE_STATE.unread, // 1 means unread - expirationType: expirationType !== 'off' ? expirationType : undefined, + expirationType: + expirationType !== 'off' + ? changeToDisappearingMessageType(convo, expirationType) + : undefined, expireTimer: convo.get('expireTimer') ? convo.get('expireTimer') : 0, // TODO should this only be for delete after send? expirationStartTimestamp: diff --git a/ts/receiver/queuedJob.ts b/ts/receiver/queuedJob.ts index 9f87bf4f7..73190ba53 100644 --- a/ts/receiver/queuedJob.ts +++ b/ts/receiver/queuedJob.ts @@ -16,6 +16,7 @@ import { showMessageRequestBannerOutsideRedux } from '../state/ducks/userConfig' import { getHideMessageRequestBannerOutsideRedux } from '../state/selectors/userConfig'; import { GoogleChrome } from '../util'; import { + changeToDisappearingMessageConversationType, isLegacyDisappearingModeEnabled, setExpirationStartTimestamp, } from '../util/expiringMessages'; @@ -422,8 +423,12 @@ export async function handleMessageJob( return; } - const expirationType = expirationTimerUpdate?.expirationType || 'off'; const expireTimer = expirationTimerUpdate?.expireTimer || 0; + const expirationType = changeToDisappearingMessageConversationType( + conversation, + expirationTimerUpdate?.expirationType, + expireTimer + ); const lastDisappearingMessageChangeTimestamp = expirationTimerUpdate?.lastDisappearingMessageChangeTimestamp || GetNetworkTime.getNowWithNetworkOffset(); diff --git a/ts/session/group/closed-group.ts b/ts/session/group/closed-group.ts index d1fcc1ce8..f02b5ee2d 100644 --- a/ts/session/group/closed-group.ts +++ b/ts/session/group/closed-group.ts @@ -27,6 +27,7 @@ import { UserUtils } from '../utils'; import { fromHexToArray, toHex } from '../utils/String'; import { DisappearingMessageConversationType, + changeToDisappearingMessageType, isLegacyDisappearingModeEnabled, setExpirationStartTimestamp, } from '../../util/expiringMessages'; @@ -163,7 +164,7 @@ export async function addUpdateMessage( groupUpdate.kicked = diff.kickedMembers; } - const expirationType = convo.get('expirationType'); + const expirationType = changeToDisappearingMessageType(convo, convo.get('expirationType')); const expireTimer = convo.get('expireTimer'); const msgModel = { diff --git a/ts/session/utils/calling/CallManager.ts b/ts/session/utils/calling/CallManager.ts index 08bdbfd6b..a932a235d 100644 --- a/ts/session/utils/calling/CallManager.ts +++ b/ts/session/utils/calling/CallManager.ts @@ -34,6 +34,7 @@ import { GetNetworkTime } from '../../apis/snode_api/getNetworkTime'; import { SnodeNamespaces } from '../../apis/snode_api/namespaces'; import { READ_MESSAGE_STATE } from '../../../models/conversationAttributes'; import { + changeToDisappearingMessageType, isLegacyDisappearingModeEnabled, setExpirationStartTimestamp, } from '../../../util/expiringMessages'; @@ -518,11 +519,14 @@ export async function USER_callRecipient(recipient: string) { // TODO legacy messages support will be removed in a future release const isLegacyMode = isLegacyDisappearingModeEnabled(calledConvo.get('expirationType')); - const expirationType = !isLegacyMode ? calledConvo.get('expirationType') : 'deleteAfterSend'; + const expirationType = changeToDisappearingMessageType( + calledConvo, + calledConvo.get('expirationType') + ); await calledConvo?.addSingleOutgoingMessage({ callNotificationType: 'started-call', sent_at: now, - expirationType: expirationType !== 'off' ? expirationType : undefined, + expirationType: calledConvo.get('expireTimer') > 0 ? expirationType : undefined, expireTimer: calledConvo.get('expireTimer') ? calledConvo.get('expireTimer') : 0, expirationStartTimestamp: setExpirationStartTimestamp( expirationType, @@ -892,14 +896,17 @@ export async function USER_acceptIncomingCallRequest(fromSender: string) { // TODO legacy messages support will be removed in a future release const isLegacyMode = isLegacyDisappearingModeEnabled(callerConvo.get('expirationType')); - const expirationType = !isLegacyMode ? callerConvo.get('expirationType') : 'deleteAfterSend'; + const expirationType = changeToDisappearingMessageType( + callerConvo, + callerConvo.get('expirationType') + ); await callerConvo?.addSingleIncomingMessage({ callNotificationType: 'answered-a-call', source: UserUtils.getOurPubKeyStrFromCache(), sent_at: networkTimestamp, received_at: networkTimestamp, unread: READ_MESSAGE_STATE.read, - expirationType: expirationType !== 'off' ? expirationType : undefined, + expirationType: callerConvo.get('expireTimer') > 0 ? expirationType : undefined, expireTimer: callerConvo.get('expireTimer') ? callerConvo.get('expireTimer') : 0, expirationStartTimestamp: setExpirationStartTimestamp( expirationType, @@ -1240,16 +1247,17 @@ async function addMissedCallMessage(callerPubkey: string, sentAt: number) { const isLegacyMode = isLegacyDisappearingModeEnabled( incomingCallConversation.get('expirationType') ); - const expirationType = !isLegacyMode - ? incomingCallConversation.get('expirationType') - : 'deleteAfterSend'; + const expirationType = changeToDisappearingMessageType( + incomingCallConversation, + incomingCallConversation.get('expirationType') + ); await incomingCallConversation?.addSingleIncomingMessage({ callNotificationType: 'missed-call', source: callerPubkey, sent_at: sentAt, received_at: GetNetworkTime.getNowWithNetworkOffset(), unread: READ_MESSAGE_STATE.unread, - expirationType: expirationType !== 'off' ? expirationType : undefined, + expirationType: incomingCallConversation.get('expireTimer') > 0 ? expirationType : undefined, expireTimer: incomingCallConversation.get('expireTimer') ? incomingCallConversation.get('expireTimer') : 0, diff --git a/ts/state/selectors/selectedConversation.ts b/ts/state/selectors/selectedConversation.ts index 538f8d847..4e613dfd5 100644 --- a/ts/state/selectors/selectedConversation.ts +++ b/ts/state/selectors/selectedConversation.ts @@ -6,7 +6,10 @@ import { UserUtils } from '../../session/utils'; import { StateType } from '../reducer'; import { getCanWrite, getModerators, getSubscriberCount } from './sogsRoomInfo'; import { getIsMessageSelectionMode, getSelectedConversation } from './conversations'; -import { DisappearingMessageConversationSetting } from '../../util/expiringMessages'; +import { + DisappearingMessageConversationSetting, + DisappearingMessageConversationType, +} from '../../util/expiringMessages'; import { ReleasedFeatures } from '../../util/releaseFeature'; import { ReduxConversationType } from '../ducks/conversations'; @@ -163,7 +166,8 @@ const getSelectedConversationExpirationModesWithLegacy = (convo: ReduxConversati return undefined; } - let modes = DisappearingMessageConversationSetting; + // NOTE this needs to be as any because the number of modes can change depending on if v2 is released or we are in single mode + let modes: any = DisappearingMessageConversationSetting; // Note to Self and Closed Groups only support deleteAfterSend and legacy modes const isClosedGroup = !convo.isPrivate && !convo.isPublic; @@ -178,7 +182,7 @@ const getSelectedConversationExpirationModesWithLegacy = (convo: ReduxConversati const modesWithDisabledState: Record = {}; // The new modes are disabled by default if (modes && modes.length > 1) { - modes.forEach(mode => { + modes.forEach((mode: any) => { modesWithDisabledState[mode] = Boolean( (mode !== 'legacy' && mode !== 'off') || (isClosedGroup && !convo.weAreAdmin) ); @@ -198,7 +202,8 @@ export const getSelectedConversationExpirationModes = (state: StateType) => { return getSelectedConversationExpirationModesWithLegacy(convo); } - let modes = DisappearingMessageConversationSetting; + // NOTE this needs to be as any because the number of modes can change depending on if v2 is released or we are in single mode + let modes: any = DisappearingMessageConversationSetting; // TODO legacy messages support will be removed in a future release // TODO remove legacy mode modes = modes.slice(0, -1); @@ -211,7 +216,7 @@ export const getSelectedConversationExpirationModes = (state: StateType) => { const modesWithDisabledState: Record = {}; if (modes && modes.length > 1) { - modes.forEach(mode => { + modes.forEach((mode: any) => { modesWithDisabledState[mode] = isClosedGroup ? !convo.weAreAdmin : false; }); } @@ -308,7 +313,9 @@ export function useSelectedExpireTimer(): number | undefined { return useSelector((state: StateType) => getSelectedConversation(state)?.expireTimer); } -export function useSelectedExpirationType(): string | undefined { +export function useSelectedConversationExpirationType(): + | DisappearingMessageConversationType + | undefined { return useSelector((state: StateType) => getSelectedConversation(state)?.expirationType); } diff --git a/ts/util/expiringMessages.ts b/ts/util/expiringMessages.ts index 3d3262011..5a914eb13 100644 --- a/ts/util/expiringMessages.ts +++ b/ts/util/expiringMessages.ts @@ -15,12 +15,17 @@ import { ReleasedFeatures } from './releaseFeature'; // NOTE this must match Content.ExpirationType in the protobuf // TODO double check this -export const DisappearingMessageMode = ['unknown', 'deleteAfterRead', 'deleteAfterSend']; +export const DisappearingMessageMode = ['unknown', 'deleteAfterRead', 'deleteAfterSend'] as const; export type DisappearingMessageType = typeof DisappearingMessageMode[number]; // 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 DisappearingMessageConversationSetting = ['off', ...DisappearingMessageMode, 'legacy']; +export const DisappearingMessageConversationSetting = [ + 'off', + DisappearingMessageMode[1], // deleteAfterRead + DisappearingMessageMode[2], // deleteAfterSend + 'legacy', +] as const; export type DisappearingMessageConversationType = typeof DisappearingMessageConversationSetting[number]; // TODO we should make this type a bit more hardcoded than being just resolved as a string export const DEFAULT_TIMER_OPTION = { @@ -266,8 +271,11 @@ export function setExpirationStartTimestamp( isLegacyMode ? 'legacy ' : '' }delete after send message to ${new Date(expirationStartTimestamp).toLocaleTimeString()}` ); - } else if (mode === 'off') { - window.log.debug(`Disappearing message mode "${mode}" set. We can safely ignore this.`); + // TODO needs improvement + } else if (mode === 'unknown') { + window.log.debug( + `Disappearing message mode "${mode}"set. This could be a legacy message or we are turning off disappearing messages` + ); expirationStartTimestamp = undefined; } else { window.log.debug(`Invalid disappearing message mode "${mode}" set. Ignoring`); @@ -290,18 +298,19 @@ export function isLegacyDisappearingModeEnabled( // TODO legacy messages support will be removed in a future release /** - * This function is used to set the mode for legacy disappearing messages depending on the default for the conversation type + * Converts DisappearingMessageConversationType to DisappearingMessageType * - * NOTE Should only be used when sending or receiving data messages (protobuf) + * NOTE Used for sending or receiving data messages (protobuf) * * @param convo Conversation we want to set + * @param expirationType DisappearingMessageConversationType * @returns Disappearing mode we should use */ -export function resolveLegacyDisappearingMode( +export function changeToDisappearingMessageType( convo: ConversationModel, - expireTimer?: number + expirationType?: DisappearingMessageConversationType ): DisappearingMessageType { - if (expireTimer === 0) { + if (expirationType === 'off' || expirationType === 'legacy') { // NOTE we would want this to be undefined but because of an issue with the protobuf implement we need to have a value return 'unknown'; } @@ -312,6 +321,33 @@ export function resolveLegacyDisappearingMode( return 'deleteAfterRead'; } +// TODO legacy messages support will be removed in a future release +/** + * Converts DisappearingMessageType to DisappearingMessageConversationType + * + * NOTE Used for the UI + * + * @param convo Conversation we want to set + * @param expirationType DisappearingMessageType + * @param expireTimer + * @returns + */ +export function changeToDisappearingMessageConversationType( + convo: ConversationModel, + expirationType?: DisappearingMessageType, + expireTimer?: number +): DisappearingMessageConversationType { + if (expirationType === 'unknown') { + return expireTimer && expireTimer > 0 ? 'legacy' : 'off'; + } + + if (convo.isMe() || convo.isClosedGroup()) { + return 'deleteAfterSend'; + } + + return 'deleteAfterRead'; +} + // TODO legacy messages support will be removed in a future release // NOTE We need this to check for legacy disappearing messages where the expirationType and expireTimer should be undefined on the ContentMessage function checkIsLegacyContentMessage(contentMessage: SignalService.Content): boolean { @@ -352,11 +388,12 @@ export async function checkForExpireUpdateInContentMessage( ? Number(dataMessage.expireTimer) : content.expirationTimer; - let expirationType = + // NOTE This starts are a DisappearingMessageConversationType but we will convert it to a DisappearingMessageType + let expirationType: any = expirationTimer > 0 ? DisappearingMessageConversationSetting[ !isDisappearingMessagesV2Released || isLegacyContentMessage - ? resolveLegacyDisappearingMode(convoToUpdate) === 'deleteAfterRead' + ? changeToDisappearingMessageType(convoToUpdate) === 'deleteAfterRead' ? 1 : 2 : content.expirationType @@ -381,8 +418,11 @@ export async function checkForExpireUpdateInContentMessage( (isLegacyDataMessage || isLegacyConversationSettingMessage || shouldDisappearButIsntMessage) ) { window.log.warn('Received a legacy disappearing message after v2 was released.', content); - expirationType = convoToUpdate.get('expirationType'); expirationTimer = convoToUpdate.get('expireTimer'); + expirationType = changeToDisappearingMessageType( + convoToUpdate, + convoToUpdate.get('expirationType') + ); } const expireUpdate: DisappearingMessageUpdate = {