diff --git a/ts/components/conversation/right-panel/RightPanel.tsx b/ts/components/conversation/right-panel/RightPanel.tsx
index 8998a0bf9..1771eca69 100644
--- a/ts/components/conversation/right-panel/RightPanel.tsx
+++ b/ts/components/conversation/right-panel/RightPanel.tsx
@@ -8,7 +8,8 @@ import { OverlayRightPanelSettings } from './overlay/OverlayRightPanelSettings';
const ClosableOverlay = () => {
const rightOverlayMode = useSelector(getRightOverlayMode);
- const [showNewDisppearingMessageModes, setShowNewDisppearingMessageModes] = useState(false);
+ // TODO we can probably use the ReleasedFeatures.isDisappearMessageV2FeatureReleased instead here so we can remove the state.
+ const [showNewDisappearingMessageModes, setShowNewDisappearingMessageModes] = useState(false);
useEffect(() => {
let isCancelled = false;
@@ -17,7 +18,7 @@ const ClosableOverlay = () => {
if (isCancelled) {
return;
}
- setShowNewDisppearingMessageModes(result);
+ setShowNewDisappearingMessageModes(result);
})
.catch(() => {
if (isCancelled) {
@@ -33,7 +34,7 @@ const ClosableOverlay = () => {
switch (rightOverlayMode) {
case 'disappearing-messages':
// TODO legacy messages support will be removed in a future release
- return ;
+ return ;
default:
return ;
}
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 957fbffa4..75454aecc 100644
--- a/ts/components/conversation/right-panel/overlay/disappearing-messages/OverlayDisappearingMessages.tsx
+++ b/ts/components/conversation/right-panel/overlay/disappearing-messages/OverlayDisappearingMessages.tsx
@@ -1,27 +1,30 @@
import React, { useEffect, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import styled from 'styled-components';
+import { useTimerOptionsByMode } from '../../../../../hooks/useParamSelector';
import { setDisappearingMessagesByConvoId } from '../../../../../interactions/conversationInteractions';
+import { getConversationController } from '../../../../../session/conversations';
import { closeRightPanel } from '../../../../../state/ducks/conversations';
import { resetRightOverlayMode } from '../../../../../state/ducks/section';
-import { Flex } from '../../../../basic/Flex';
-import { SessionButton } from '../../../../basic/SessionButton';
-import { SpacerLG, SpacerXL } from '../../../../basic/Text';
import {
getSelectedConversationExpirationModes,
getSelectedConversationExpirationModesWithLegacy,
- getSelectedConversationExpirationSettings,
- getSelectedConversationKey,
-} from '../../../../../state/selectors/conversations';
+ useSelectedConversationKey,
+ useSelectedExpirationType,
+ useSelectedExpireTimer,
+ useSelectedIsGroup,
+ useSelectedWeAreAdmin,
+} from '../../../../../state/selectors/selectedConversation';
import {
DEFAULT_TIMER_OPTION,
DisappearingMessageConversationType,
} from '../../../../../util/expiringMessages';
-import { useTimerOptionsByMode } from '../../../../../hooks/useParamSelector';
-import { Header } from './Header';
+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 { getConversationController } from '../../../../../session/conversations';
const StyledScrollContainer = styled.div`
width: 100%;
@@ -67,12 +70,9 @@ export type PropsForExpirationSettings = {
weAreAdmin: boolean | undefined;
};
-type OverlayDisappearingMessagesProps = { unlockNewModes: boolean };
-
-export const OverlayDisappearingMessages = (props: OverlayDisappearingMessagesProps) => {
- const { unlockNewModes } = props;
+export const OverlayDisappearingMessages = ({ unlockNewModes }: { unlockNewModes: boolean }) => {
const dispatch = useDispatch();
- const selectedConversationKey = useSelector(getSelectedConversationKey);
+ const selectedConversationKey = useSelectedConversationKey();
const disappearingModeOptions = useSelector(
unlockNewModes
? getSelectedConversationExpirationModes
@@ -81,21 +81,20 @@ export const OverlayDisappearingMessages = (props: OverlayDisappearingMessagesPr
// NOTE if there is only 'off' and one disappearing message mode then we trigger single mode
const singleMode =
- disappearingModeOptions.off !== undefined && Object.keys(disappearingModeOptions).length === 2
+ disappearingModeOptions &&
+ disappearingModeOptions.off !== undefined &&
+ Object.keys(disappearingModeOptions).length === 2
? Object.keys(disappearingModeOptions)[1]
: undefined;
const hasOnlyOneMode = Boolean(singleMode && singleMode.length > 0);
- const convoProps = useSelector(getSelectedConversationExpirationSettings);
-
- if (!convoProps) {
- return null;
- }
-
- const { isGroup, weAreAdmin } = convoProps;
+ const isGroup = useSelectedIsGroup();
+ const expirationType = useSelectedExpirationType();
+ const expireTimer = useSelectedExpireTimer();
+ const weAreAdmin = useSelectedWeAreAdmin();
const [modeSelected, setModeSelected] = useState(
- convoProps.expirationType
+ expirationType
);
const [timeSelected, setTimeSelected] = useState(0);
const timerOptions = useTimerOptionsByMode(modeSelected, hasOnlyOneMode);
@@ -127,13 +126,11 @@ export const OverlayDisappearingMessages = (props: OverlayDisappearingMessagesPr
useEffect(() => {
// NOTE loads a time value from the conversation model or the default
handleSetTime(
- modeSelected === convoProps.expirationType &&
- convoProps.expireTimer &&
- convoProps.expireTimer > -1
- ? convoProps.expireTimer
+ modeSelected === expirationType && expireTimer && expireTimer > -1
+ ? expireTimer
: loadDefaultTimeValue(modeSelected)
);
- }, [convoProps.expirationType, convoProps.expireTimer, modeSelected]);
+ }, [expirationType, expireTimer, modeSelected]);
// TODO legacy messages support will be removed in a future
useEffect(() => {
@@ -141,7 +138,7 @@ export const OverlayDisappearingMessages = (props: OverlayDisappearingMessagesPr
const convo = getConversationController().get(selectedConversationKey);
if (convo) {
let defaultExpirationType: DisappearingMessageConversationType = 'deleteAfterRead';
- if (convo.isMe() || convo.isMediumGroup()) {
+ if (convo.isMe() || convo.isClosedGroup()) {
defaultExpirationType = 'deleteAfterSend';
}
convo.set('expirationType', defaultExpirationType);
@@ -150,6 +147,13 @@ export const OverlayDisappearingMessages = (props: OverlayDisappearingMessagesPr
}
}, [unlockNewModes, selectedConversationKey, modeSelected]);
+ if (!disappearingModeOptions) {
+ return null;
+ }
+
+ if (!selectedConversationKey) {
+ return null;
+ }
return (
diff --git a/ts/hooks/useParamSelector.ts b/ts/hooks/useParamSelector.ts
index b20862652..e5d4a5540 100644
--- a/ts/hooks/useParamSelector.ts
+++ b/ts/hooks/useParamSelector.ts
@@ -262,6 +262,7 @@ export function useMentionedUs(conversationId?: string): boolean {
export function useIsTyping(conversationId?: string): boolean {
return useConversationPropsById(conversationId)?.isTyping || false;
}
+
export function useMessageExpirationPropsById(messageId?: string) {
return useSelector((state: StateType) => {
if (!messageId) {
diff --git a/ts/interactions/conversationInteractions.ts b/ts/interactions/conversationInteractions.ts
index 091dd3bd9..dd4e96019 100644
--- a/ts/interactions/conversationInteractions.ts
+++ b/ts/interactions/conversationInteractions.ts
@@ -385,12 +385,14 @@ export async function setDisappearingMessagesByConvoId(
providedExpirationType: 'off',
providedExpireTimer: 0,
providedChangeTimestamp,
+ fromConfigMessage: false,
});
} else {
await conversation.updateExpireTimer({
providedExpirationType: expirationType,
providedExpireTimer: seconds,
providedChangeTimestamp,
+ fromConfigMessage: false,
});
}
}
diff --git a/ts/models/conversation.ts b/ts/models/conversation.ts
index b67900062..8b6084bd1 100644
--- a/ts/models/conversation.ts
+++ b/ts/models/conversation.ts
@@ -793,6 +793,7 @@ export class ConversationModel extends Backbone.Model {
providedChangeTimestamp,
providedSource,
receivedAt, // is set if it comes from outside
+ fromConfigMessage,
fromSync = false,
shouldCommit = true,
existingMessage,
@@ -803,6 +804,7 @@ export class ConversationModel extends Backbone.Model {
providedSource?: string;
receivedAt?: number; // is set if it comes from outside
fromSync?: boolean;
+ fromConfigMessage: boolean;
shouldCommit?: boolean;
existingMessage?: MessageModel;
}): Promise {
@@ -861,41 +863,43 @@ export class ConversationModel extends Backbone.Model {
lastDisappearingMessageChangeTimestamp,
source,
});
-
- const commonAttributes = {
- flags: SignalService.DataMessage.Flags.EXPIRATION_TIMER_UPDATE,
- expirationTimerUpdate: {
- expirationType,
- expireTimer,
- lastDisappearingMessageChangeTimestamp,
- source,
- fromSync,
- },
- // TODO legacy messages support will be removed in a future release
- expirationType:
- expirationType !== 'off' || isDisappearingMessagesV2Released ? expirationType : undefined,
- expireTimer:
- expirationType !== 'off' || isDisappearingMessagesV2Released ? expireTimer : undefined,
- };
-
let message: MessageModel | undefined = existingMessage || undefined;
- if (!message) {
- if (isOutgoing) {
- message = await this.addSingleOutgoingMessage({
- ...commonAttributes,
- sent_at: timestamp,
- });
- } else {
- message = await this.addSingleIncomingMessage({
- ...commonAttributes,
- // Even though this isn't reflected to the user, we want to place the last seen
- // indicator above it. We set it to 'unread' to trigger that placement.
- unread: READ_MESSAGE_STATE.unread,
+ // 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
+ if (!fromConfigMessage) {
+ const commonAttributes = {
+ flags: SignalService.DataMessage.Flags.EXPIRATION_TIMER_UPDATE,
+ expirationTimerUpdate: {
+ expirationType,
+ expireTimer,
+ lastDisappearingMessageChangeTimestamp,
source,
- sent_at: timestamp,
- received_at: timestamp,
- });
+ fromSync,
+ },
+ // TODO legacy messages support will be removed in a future release
+ expirationType:
+ expirationType !== 'off' || isDisappearingMessagesV2Released ? expirationType : undefined,
+ expireTimer:
+ expirationType !== 'off' || isDisappearingMessagesV2Released ? expireTimer : undefined,
+ };
+
+ if (!message) {
+ if (isOutgoing) {
+ message = await this.addSingleOutgoingMessage({
+ ...commonAttributes,
+ sent_at: timestamp,
+ });
+ } else {
+ message = await this.addSingleIncomingMessage({
+ ...commonAttributes,
+ // Even though this isn't reflected to the user, we want to place the last seen
+ // indicator above it. We set it to 'unread' to trigger that placement.
+ unread: READ_MESSAGE_STATE.unread,
+ source,
+ sent_at: timestamp,
+ received_at: timestamp,
+ });
+ }
}
}
@@ -908,8 +912,8 @@ export class ConversationModel extends Backbone.Model {
await this.commit();
}
- // if change was made remotely, don't send it to the number/group
- if (receivedAt) {
+ // if change was made remotely, don't send it to the contact/group
+ if (receivedAt || fromSync || fromConfigMessage) {
return;
}
@@ -930,7 +934,7 @@ export class ConversationModel extends Backbone.Model {
}
const expirationTimerMessage = new ExpirationTimerUpdateMessage(expireUpdate);
- return message.sendSyncMessageOnly(expirationTimerMessage);
+ return message?.sendSyncMessageOnly(expirationTimerMessage);
}
if (this.isPrivate()) {
diff --git a/ts/models/message.ts b/ts/models/message.ts
index eb9cb12e6..0060577b7 100644
--- a/ts/models/message.ts
+++ b/ts/models/message.ts
@@ -93,6 +93,7 @@ import { ReactionList } from '../types/Reaction';
import { roomHasBlindEnabled } from '../types/sqlSharedTypes';
import {
DisappearingMessageConversationSetting,
+ DisappearingMessageConversationType,
DisappearingMessageUpdate,
ExpirationTimerOptions,
setExpirationStartTimestamp,
@@ -1097,7 +1098,7 @@ export class MessageModel extends Backbone.Model {
dataMessage.flags === SignalService.DataMessage.Flags.EXPIRATION_TIMER_UPDATE)
);
- const expirationType = content.expirationType
+ const expirationType: DisappearingMessageConversationType = content.expirationType
? DisappearingMessageConversationSetting[content.expirationType]
: isLegacyDataMessage
? DisappearingMessageConversationSetting[3]
diff --git a/ts/receiver/closedGroups.ts b/ts/receiver/closedGroups.ts
index a539e184e..313c5417a 100644
--- a/ts/receiver/closedGroups.ts
+++ b/ts/receiver/closedGroups.ts
@@ -320,6 +320,7 @@ export async function handleNewClosedGroup(
providedChangeTimestamp: GetNetworkTime.getNowWithNetworkOffset(),
providedSource: sender,
receivedAt: Date.now(),
+ fromConfigMessage: false,
});
if (isKeyPairAlreadyHere) {
@@ -381,6 +382,7 @@ export async function handleNewClosedGroup(
providedChangeTimestamp: GetNetworkTime.getNowWithNetworkOffset(),
providedSource: sender,
receivedAt: envelopeTimestamp,
+ fromConfigMessage: fromLegacyConfig,
});
convo.updateLastMessage();
diff --git a/ts/receiver/configMessage.ts b/ts/receiver/configMessage.ts
index 8c51c87e8..e9ef3d79a 100644
--- a/ts/receiver/configMessage.ts
+++ b/ts/receiver/configMessage.ts
@@ -331,7 +331,14 @@ async function handleContactsUpdate(result: IncomingConfResult): Promise {
+ if (!convo) {
+ return null;
+ }
let modes = DisappearingMessageConversationSetting;
// TODO legacy messages support will be removed in a future release
// TODO remove legacy mode
modes = modes.slice(0, -1);
- if (!convo) {
- return DisappearingMessageConversationSetting;
- }
// Note to Self and Closed Groups only support deleteAfterSend
const isClosedGroup = !convo.isPrivate && !convo.isPublic;
if (convo?.isMe || isClosedGroup) {
modes = [modes[0], modes[2]];
}
- const modesWithDisabledState: DisappearingMessageModesWithState = {};
+ const modesWithDisabledState: Record = {};
if (modes && modes.length > 1) {
modes.forEach(mode => {
modesWithDisabledState[mode] = isClosedGroup ? !convo.weAreAdmin : false;
@@ -304,12 +301,12 @@ export const getSelectedConversationExpirationModes = createSelector(
export const getSelectedConversationExpirationModesWithLegacy = createSelector(
getSelectedConversation,
(convo: ReduxConversationType | undefined) => {
- let modes = DisappearingMessageConversationSetting;
-
// this just won't happen
if (!convo) {
- return DisappearingMessageConversationSetting;
+ return null;
}
+ let modes = DisappearingMessageConversationSetting;
+
// Note to Self and Closed Groups only support deleteAfterSend and legacy modes
const isClosedGroup = !convo.isPrivate && !convo.isPublic;
if (convo?.isMe || isClosedGroup) {
@@ -319,7 +316,8 @@ export const getSelectedConversationExpirationModesWithLegacy = createSelector(
// Legacy mode is the 2nd option in the UI
modes = [modes[0], modes[modes.length - 1], ...modes.slice(1, modes.length - 1)];
- const modesWithDisabledState: DisappearingMessageModesWithState = {};
+ // TODO it would be nice to type those with something else that string but it causes a lot of issues
+ const modesWithDisabledState: Record = {};
// The new modes are disabled by default
if (modes && modes.length > 1) {
modes.forEach(mode => {
@@ -332,13 +330,3 @@ export const getSelectedConversationExpirationModesWithLegacy = createSelector(
return modesWithDisabledState;
}
);
-
-export const getSelectedConversationExpirationSettings = createSelector(
- getSelectedConversation,
- (convo: ReduxConversationType | undefined): PropsForExpirationSettings => ({
- expirationType: convo?.expirationType,
- expireTimer: convo?.expireTimer,
- isGroup: !convo?.isPrivate,
- weAreAdmin: convo?.weAreAdmin,
- })
-);
diff --git a/ts/state/smart/SessionConversation.ts b/ts/state/smart/SessionConversation.ts
index 44ef5990e..c5850f2f1 100644
--- a/ts/state/smart/SessionConversation.ts
+++ b/ts/state/smart/SessionConversation.ts
@@ -14,7 +14,6 @@ import {
import {
getSelectedConversation,
getSelectedConversationKey,
- getSelectedConversationExpirationSettings,
} from '../selectors/selectedConversation';
import { getStagedAttachmentsForCurrentConversation } from '../selectors/stagedAttachments';
import { getTheme } from '../selectors/theme';
@@ -34,7 +33,6 @@ const mapStateToProps = (state: StateType) => {
stagedAttachments: getStagedAttachmentsForCurrentConversation(state),
hasOngoingCallWithFocusedConvo: getHasOngoingCallWithFocusedConvo(state),
isSelectedConvoInitialLoadingInProgress: getIsSelectedConvoInitialLoadingInProgress(state),
- selectedConversationExpirationSettings: getSelectedConversationExpirationSettings(state),
};
};
diff --git a/ts/util/expiringMessages.ts b/ts/util/expiringMessages.ts
index 3bf2e2f70..e1016b8fe 100644
--- a/ts/util/expiringMessages.ts
+++ b/ts/util/expiringMessages.ts
@@ -13,18 +13,13 @@ import { MessageModel } from '../models/message';
import { GetNetworkTime } from '../session/apis/snode_api/getNetworkTime';
import { ReleasedFeatures } from './releaseFeature';
-// TODO Might need to be improved by using an enum
-// TODO do we need to add legacy here now that it's explicitly in the protbuf?
+// TODO do we need to add legacy here now that it's explicitly in the protobuf?
export const DisappearingMessageMode = ['deleteAfterRead', 'deleteAfterSend'];
export type DisappearingMessageType = typeof DisappearingMessageMode[number] | null;
// TODO legacy messages support will be removed in a future release
export const DisappearingMessageConversationSetting = ['off', ...DisappearingMessageMode, 'legacy'];
export type DisappearingMessageConversationType = typeof DisappearingMessageConversationSetting[number]; // TODO we should make this type a bit more hardcoded than being just resolved as a string
-export type DisappearingMessageModesWithState = Record<
- DisappearingMessageConversationType,
- boolean
->;
export const DEFAULT_TIMER_OPTION = {
DELETE_AFTER_READ: 43200, // 12 hours