feat: check for v2 release correctly on startup

improvements to creating and updating closed groups with disappearing message settings
pull/2971/head
William Grant 2 years ago
parent 250e593831
commit 60e5503e6a

@ -46,6 +46,7 @@ import { isDarkTheme } from '../../state/selectors/theme';
import { ThemeStateType } from '../../themes/constants/colors'; import { ThemeStateType } from '../../themes/constants/colors';
import { switchThemeTo } from '../../themes/switchTheme'; import { switchThemeTo } from '../../themes/switchTheme';
import { ConfigurationSync } from '../../session/utils/job_runners/jobs/ConfigurationSyncJob'; import { ConfigurationSync } from '../../session/utils/job_runners/jobs/ConfigurationSyncJob';
import { ReleasedFeatures } from '../../util/releaseFeature';
const Section = (props: { type: SectionType }) => { const Section = (props: { type: SectionType }) => {
const ourNumber = useSelector(getOurNumber); const ourNumber = useSelector(getOurNumber);
@ -194,6 +195,9 @@ const doAppStartUp = async () => {
// this generates the key to encrypt attachments locally // this generates the key to encrypt attachments locally
await Data.generateAttachmentKeyIfEmpty(); await Data.generateAttachmentKeyIfEmpty();
// Feature Checks
await ReleasedFeatures.checkIsDisappearMessageV2FeatureReleased();
// trigger a sync message if needed for our other devices // trigger a sync message if needed for our other devices
void triggerSyncIfNeeded(); void triggerSyncIfNeeded();
void getSwarmPollingInstance().start(); void getSwarmPollingInstance().start();

@ -880,6 +880,7 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
let message: MessageModel | undefined = existingMessage || undefined; let message: MessageModel | undefined = existingMessage || undefined;
// 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 // 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
if (!fromConfigMessage) { if (!fromConfigMessage) {
const commonAttributes = { const commonAttributes = {
flags: SignalService.DataMessage.Flags.EXPIRATION_TIMER_UPDATE, flags: SignalService.DataMessage.Flags.EXPIRATION_TIMER_UPDATE,

@ -481,7 +481,7 @@ function saveConversation(data: ConversationAttributes): SaveConversationReturn
expirationType, expirationType,
expireTimer, expireTimer,
lastDisappearingMessageChangeTimestamp, lastDisappearingMessageChangeTimestamp,
hasOutdatedClient: hasOutdatedClient, hasOutdatedClient,
lastMessageStatus, lastMessageStatus,
lastMessage: shortenedLastMessage, lastMessage: shortenedLastMessage,

@ -323,7 +323,7 @@ export async function handleNewClosedGroup(
providedChangeTimestamp: GetNetworkTime.getNowWithNetworkOffset(), providedChangeTimestamp: GetNetworkTime.getNowWithNetworkOffset(),
providedSource: sender, providedSource: sender,
receivedAt: Date.now(), receivedAt: Date.now(),
fromConfigMessage: false, fromConfigMessage: fromLegacyConfig,
}); });
if (isKeyPairAlreadyHere) { if (isKeyPairAlreadyHere) {
@ -371,22 +371,13 @@ export async function handleNewClosedGroup(
// be sure to call this before sending the message. // be sure to call this before sending the message.
// the sending pipeline needs to know from GroupUtils when a message is for a medium group // the sending pipeline needs to know from GroupUtils when a message is for a medium group
await ClosedGroup.updateOrCreateClosedGroup(groupDetails); await ClosedGroup.updateOrCreateClosedGroup(groupDetails, fromLegacyConfig);
// ClosedGroup.updateOrCreateClosedGroup will mark the activeAt to Date.now if it's active // ClosedGroup.updateOrCreateClosedGroup will mark the activeAt to Date.now if it's active
// But we need to override this value with the sent timestamp of the message creating this group for us. // But we need to override this value with the sent timestamp of the message creating this group for us.
// Having that timestamp set will allow us to pickup incoming group update which were sent between // Having that timestamp set will allow us to pickup incoming group update which were sent between
// envelope.timestamp and Date.now(). And we need to listen to those (some might even remove us) // envelope.timestamp and Date.now(). And we need to listen to those (some might even remove us)
convo.set('lastJoinedTimestamp', envelopeTimestamp); convo.set('lastJoinedTimestamp', envelopeTimestamp);
// TODO This is only applicable for old closed groups - will be removed in future
await convo.updateExpireTimer({
providedExpirationType: expireTimer === 0 ? 'off' : 'deleteAfterSend',
providedExpireTimer: expireTimer,
providedChangeTimestamp: GetNetworkTime.getNowWithNetworkOffset(),
providedSource: sender,
receivedAt: envelopeTimestamp,
fromConfigMessage: fromLegacyConfig,
});
convo.updateLastMessage(); convo.updateLastMessage();
await convo.commit(); await convo.commit();

@ -36,7 +36,7 @@ import { ConfigMessageHandler } from './configMessage';
import { ECKeyPair } from './keypairs'; import { ECKeyPair } from './keypairs';
import { ContactsWrapperActions } from '../webworker/workers/browser/libsession_worker_interface'; import { ContactsWrapperActions } from '../webworker/workers/browser/libsession_worker_interface';
import { import {
checkForExpireUpdate, checkForExpireUpdateInContentMessage,
checkHasOutdatedClient, checkHasOutdatedClient,
isLegacyDisappearingModeEnabled, isLegacyDisappearingModeEnabled,
setExpirationStartTimestamp, setExpirationStartTimestamp,
@ -451,7 +451,10 @@ export async function innerHandleSwarmContentMessage(
content.dataMessage.profileKey = null; content.dataMessage.profileKey = null;
} }
const expireUpdate = await checkForExpireUpdate(conversationModelForUIUpdate, content); const expireUpdate = await checkForExpireUpdateInContentMessage(
conversationModelForUIUpdate,
content
);
// TODO legacy messages support will be removed in a future release // TODO legacy messages support will be removed in a future release
if (expireUpdate && !isEmpty(expireUpdate) && expireUpdate.isDisappearingMessagesV2Released) { if (expireUpdate && !isEmpty(expireUpdate) && expireUpdate.isDisappearingMessagesV2Released) {
@ -461,7 +464,10 @@ export async function innerHandleSwarmContentMessage(
expireUpdate expireUpdate
); );
if (expireUpdate.isLegacyConversationSettingMessage) { if (expireUpdate.isLegacyConversationSettingMessage) {
window.log.info('The legacy message is an expiration timer update. Ignoring it.'); window.log.debug(
'The legacy message is an expiration timer update. Ignoring it.',
content
);
return; return;
} }
} }

@ -232,7 +232,7 @@ export async function handleSwarmDataMessage(
} }
if (!messageHasVisibleContent(cleanDataMessage)) { if (!messageHasVisibleContent(cleanDataMessage)) {
window?.log?.warn(`Message ${getEnvelopeId(envelope)} ignored; it was empty`); window?.log?.debug(`WIP: Message ${getEnvelopeId(envelope)} ignored; it was empty`);
await removeFromCache(envelope); await removeFromCache(envelope);
return; return;
} }

@ -82,7 +82,7 @@ async function processExpirationResults(
// TODO need proper typing for swarm and results // TODO need proper typing for swarm and results
const results: Record<string, { hashes: Array<string>; expiry: number }> = {}; const results: Record<string, { hashes: Array<string>; expiry: number }> = {};
// window.log.debug(`processExpirationResults start`, swarm, messageHashes); window.log.debug(`WIP: processExpirationResults start`, swarm, messageHashes);
for (const nodeKey of Object.keys(swarm)) { for (const nodeKey of Object.keys(swarm)) {
if (!isEmpty(swarm[nodeKey].failed)) { if (!isEmpty(swarm[nodeKey].failed)) {

@ -101,6 +101,7 @@ async function generateUpdateExpirySignature({
timestamp, timestamp,
messageHashes, messageHashes,
}: { }: {
// NOTE empty string means we want to hardcode the expiry to a TTL value, otherwise it's a shorten or extension of the TTL
shortenOrExtend: 'extend' | 'shorten' | ''; shortenOrExtend: 'extend' | 'shorten' | '';
timestamp: number; timestamp: number;
messageHashes: Array<string>; messageHashes: Array<string>;

@ -375,7 +375,7 @@ export class SwarmPolling {
allDecryptedConfigMessages.push(asIncomingMsg); allDecryptedConfigMessages.push(asIncomingMsg);
} else { } else {
throw new Error( throw new Error(
'received a message to a namespace reserved for user config but not containign a sharedConfigMessage' 'received a message from the namespace reserved for user config but it did not contain a sharedConfigMessage'
); );
} }
} catch (e) { } catch (e) {
@ -451,7 +451,7 @@ export class SwarmPolling {
if (!results.length) { if (!results.length) {
return []; return [];
} }
// when we asked to extend the expiry of the config messages, exclude it from the list of results as we do not want to mess up the last hash tracking logic // NOTE when we asked to extend the expiry of the config messages, exclude it from the list of results as we do not want to mess up the last hash tracking logic
if (configHashesToBump.length) { if (configHashesToBump.length) {
try { try {
const lastResult = results[results.length - 1]; const lastResult = results[results.length - 1];

@ -38,7 +38,7 @@ export type GroupInfo = {
zombies?: Array<string>; zombies?: Array<string>;
activeAt?: number; activeAt?: number;
expirationType?: DisappearingMessageConversationType[0] | DisappearingMessageConversationType[2]; expirationType?: DisappearingMessageConversationType[0] | DisappearingMessageConversationType[2];
expireTimer?: number | null; expireTimer?: number;
admins?: Array<string>; admins?: Array<string>;
}; };
@ -222,7 +222,7 @@ function buildGroupDiff(convo: ConversationModel, update: GroupInfo): GroupDiff
return groupDiff; return groupDiff;
} }
export async function updateOrCreateClosedGroup(details: GroupInfo) { export async function updateOrCreateClosedGroup(details: GroupInfo, fromLegacyConfig?: boolean) {
const { id, expirationType, expireTimer } = details; const { id, expirationType, expireTimer } = details;
const conversation = await getConversationController().getOrCreateAndWait( const conversation = await getConversationController().getOrCreateAndWait(
@ -232,13 +232,7 @@ export async function updateOrCreateClosedGroup(details: GroupInfo) {
const updates: Pick< const updates: Pick<
ConversationAttributes, ConversationAttributes,
| 'type' 'type' | 'members' | 'displayNameInProfile' | 'active_at' | 'left'
| 'members'
| 'displayNameInProfile'
| 'active_at'
| 'left'
| 'expirationType'
| 'expireTimer'
> = { > = {
displayNameInProfile: details.name, displayNameInProfile: details.name,
members: details.members, members: details.members,
@ -246,8 +240,6 @@ export async function updateOrCreateClosedGroup(details: GroupInfo) {
type: ConversationTypeEnum.GROUP, type: ConversationTypeEnum.GROUP,
active_at: details.activeAt ? details.activeAt : 0, active_at: details.activeAt ? details.activeAt : 0,
left: !details.activeAt, left: !details.activeAt,
expirationType: details.expirationType || 'off',
expireTimer: details.expireTimer || 0,
}; };
conversation.set(updates); conversation.set(updates);
@ -259,25 +251,16 @@ export async function updateOrCreateClosedGroup(details: GroupInfo) {
await conversation.commit(); await conversation.commit();
if (
expirationType === undefined ||
expirationType === 'off' ||
expireTimer === undefined ||
typeof expireTimer !== 'number'
) {
return;
}
await conversation.updateExpireTimer({ await conversation.updateExpireTimer({
// TODO legacy messages support will be removed in a future release // TODO legacy messages support will be removed in a future release
// TODO What are we cleaning? providedExpirationType:
providedExpirationType: expirationType || 'deleteAfterSend', expirationType || (expireTimer && expireTimer > 0) ? 'deleteAfterSend' : 'off',
providedExpireTimer: expireTimer, providedExpireTimer: expireTimer || 0,
providedChangeTimestamp: GetNetworkTime.getNowWithNetworkOffset(), providedChangeTimestamp: GetNetworkTime.getNowWithNetworkOffset(),
providedSource: UserUtils.getOurPubKeyStrFromCache(), providedSource: UserUtils.getOurPubKeyStrFromCache(),
receivedAt: Date.now(), receivedAt: Date.now(),
fromSync: true, fromSync: true,
fromConfigMessage: false, fromConfigMessage: Boolean(fromLegacyConfig),
}); });
} }

@ -316,7 +316,7 @@ function checkIsLegacyDataMessage(dataMessage: SignalService.DataMessage): boole
} }
// TODO legacy messages support will be removed in a future release // TODO legacy messages support will be removed in a future release
export async function checkForExpireUpdate( export async function checkForExpireUpdateInContentMessage(
convoToUpdate: ConversationModel, convoToUpdate: ConversationModel,
content: SignalService.Content content: SignalService.Content
): Promise<DisappearingMessageUpdate | undefined> { ): Promise<DisappearingMessageUpdate | undefined> {
@ -338,12 +338,18 @@ export async function checkForExpireUpdate(
let expirationTimer = isLegacyDataMessage let expirationTimer = isLegacyDataMessage
? Number(dataMessage.expireTimer) ? Number(dataMessage.expireTimer)
: content.expirationTimer; : content.expirationTimer;
let expirationType = let expirationType =
expirationTimer > 0 expirationTimer > 0
? DisappearingMessageConversationSetting[ ? DisappearingMessageConversationSetting[
!isDisappearingMessagesV2Released || isLegacyContentMessage ? 3 : content.expirationType !isDisappearingMessagesV2Released || isLegacyContentMessage
? resolveLegacyDisappearingMode(convoToUpdate) === 'deleteAfterRead'
? 1
: 2
: content.expirationType
] ]
: DisappearingMessageConversationSetting[0]; : DisappearingMessageConversationSetting[0];
const lastDisappearingMessageChangeTimestamp = content.lastDisappearingMessageChangeTimestamp const lastDisappearingMessageChangeTimestamp = content.lastDisappearingMessageChangeTimestamp
? Number(content.lastDisappearingMessageChangeTimestamp) ? Number(content.lastDisappearingMessageChangeTimestamp)
: undefined; : undefined;

@ -106,6 +106,7 @@ function isUserConfigFeatureReleasedCached(): boolean {
return !!isUserConfigLibsessionFeatureReleased; return !!isUserConfigLibsessionFeatureReleased;
} }
// NOTE Make sure to call checkIsDisappearMessageV2FeatureReleased at least once and then use this. It's mostly used in components that are rendered where we don't want to do async calls
function isDisappearMessageV2FeatureReleasedCached(): boolean { function isDisappearMessageV2FeatureReleasedCached(): boolean {
return !!isDisappearingMessageFeatureReleased; return !!isDisappearingMessageFeatureReleased;
} }

Loading…
Cancel
Save