feat: fixed double expiration update message issue

started creating an inheritable expiration message class, improved start triggering logic
pull/2660/head
William Grant 2 years ago
parent 446752cf54
commit 97ecc9e521

@ -5,7 +5,7 @@ import {
PropsForGroupUpdateType,
} from '../../../../state/ducks/conversations';
import { NotificationBubble } from './notification-bubble/NotificationBubble';
import { ReadableMessage } from './ReadableMessage';
import { ExpirableReadableMessage } from './ExpirableReadableMessage';
import { arrayContainsUsOnly } from '../../../../models/message';
import { useConversationsUsernameWithQuoteOrFullPubkey } from '../../../../hooks/useParamSelector';
@ -71,16 +71,29 @@ const ChangeItem = (change: PropsForGroupUpdateType): string => {
};
export const GroupUpdateMessage = (props: PropsForGroupUpdate) => {
const { change, messageId, receivedAt, isUnread } = props;
const {
change,
messageId,
receivedAt,
isUnread,
direction,
expirationLength,
expirationTimestamp,
isExpired,
} = props;
return (
<ReadableMessage
<ExpirableReadableMessage
messageId={messageId}
receivedAt={receivedAt}
isUnread={isUnread}
direction={direction}
expirationLength={expirationLength}
expirationTimestamp={expirationTimestamp}
isExpired={isExpired}
key={`readable-message-${messageId}`}
>
<NotificationBubble notificationText={ChangeItem(change)} iconType="users" />
</ReadableMessage>
</ExpirableReadableMessage>
);
};

@ -362,6 +362,7 @@ export async function setDisappearingMessagesByConvoId(
if (!expirationType || expirationType === 'off' || !seconds || seconds <= 0) {
await conversation.updateExpireTimer({
providedExpirationType: 'off',
providedExpireTimer: 0,
providedChangeTimestamp,
});
} else {

@ -1036,17 +1036,20 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
receivedAt, // is set if it comes from outside
fromSync,
shouldCommit = true,
existingMessage,
}: {
providedExpirationType: DisappearingMessageConversationType;
providedExpireTimer?: number;
providedChangeTimestamp?: number;
providedChangeTimestamp: number;
providedSource?: string;
receivedAt?: number; // is set if it comes from outside
fromSync?: boolean;
shouldCommit?: boolean;
existingMessage?: MessageModel;
}): Promise<void> {
let expirationType = providedExpirationType;
let expireTimer = providedExpireTimer;
const lastDisappearingMessageChangeTimestamp = providedChangeTimestamp;
let source = providedSource;
defaults({ fromSync }, { fromSync: false });
@ -1057,12 +1060,19 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
}
// TODO does this actually work?
if (
this.get('lastDisappearingMessageChangeTimestamp') > lastDisappearingMessageChangeTimestamp
) {
window.log.info('WIP: updateExpireTimer() This is an outdated disappearing message setting');
return;
}
if (
isEqual(expirationType, this.get('expirationType')) &&
isEqual(expireTimer, this.get('expireTimer'))
) {
window.log.info(
'WIP: Dropping ExpireTimerUpdate message as we already have the same one set.'
'WIP:updateExpireTimer() Dropping ExpireTimerUpdate message as we already have the same one set.'
);
return;
}
@ -1077,17 +1087,17 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
this.set({
expirationType,
expireTimer,
lastDisappearingMessageChangeTimestamp: providedChangeTimestamp || undefined,
lastDisappearingMessageChangeTimestamp,
});
window?.log?.info('WIP: Updated conversation disappearing messages setting', {
window?.log?.info('WIP: Updating conversation disappearing messages setting', {
id: this.idForLogging(),
expirationType,
expireTimer,
lastDisappearingMessageChangeTimestamp,
source,
});
const lastDisappearingMessageChangeTimestamp = providedChangeTimestamp || 0;
const commonAttributes = {
flags: SignalService.DataMessage.Flags.EXPIRATION_TIMER_UPDATE,
expirationTimerUpdate: {
@ -1097,28 +1107,30 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
source,
fromSync,
},
expirationType,
expireTimer,
expirationType: expirationType !== 'off' ? expirationType : undefined,
expireTimer: expirationType !== 'off' ? expireTimer : undefined,
};
let message: MessageModel | undefined;
let message: MessageModel | undefined = existingMessage || undefined;
if (isOutgoing) {
message = await this.addSingleOutgoingMessage({
...commonAttributes,
sent_at: timestamp,
});
} else {
// TODO do we still want to handle expiration in incoming messages?
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: 1,
source,
sent_at: timestamp,
received_at: timestamp,
});
if (!message) {
if (isOutgoing) {
message = await this.addSingleOutgoingMessage({
...commonAttributes,
sent_at: timestamp,
});
} else {
// TODO do we still want to handle expiration in incoming messages?
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: 1,
source,
sent_at: timestamp,
received_at: timestamp,
});
}
}
if (this.isActive()) {
@ -1144,6 +1156,7 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
if (this.isMe()) {
// TODO Check that the args are correct
// This might be happening too late in the message pipeline. Maybe should be moved to handleExpirationTimerUpdateNoCommit()
if (expireUpdate.expirationType === 'deleteAfterRead') {
window.log.info('WIP: Note to Self messages cannot be delete after read!');
return;
@ -1159,7 +1172,6 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
const pubkey = new PubKey(this.get('id'));
await getMessageQueue().sendToPubKey(pubkey, expirationTimerMessage);
} else {
// TODO Check that the args are correct
// Cannot be an open group
window?.log?.warn('TODO: Expiration update for closed groups are to be updated');
const expireUpdateForGroup = {

@ -250,18 +250,12 @@ export class MessageModel extends Backbone.Model<MessageAttributes> {
return window.i18n('mediaMessage');
}
if (this.isExpirationTimerUpdate()) {
// Backwards compatibility for Disappearing Messages in old clients
// TODO Backwards compatibility for Disappearing Messages in old clients
// TODO What does this comment refer to mean?
const expireTimerUpdate = this.get('expirationTimerUpdate');
const expirationType = this.get('expirationType');
const expireTimer = this.get('expireTimer');
if (
!expireTimerUpdate ||
expireTimerUpdate.expirationType === 'off' ||
!expireTimerUpdate.expireTimer ||
expirationType === 'off' ||
!expireTimer ||
expireTimer === 0
) {
const expirationType = expireTimerUpdate?.expirationType;
const expireTimer = expireTimerUpdate?.expireTimer;
if (!expireTimerUpdate || expirationType === 'off' || !expireTimer || expireTimer === 0) {
return window.i18n('disappearingMessagesDisabled');
}
@ -417,6 +411,7 @@ export class MessageModel extends Backbone.Model<MessageAttributes> {
messageId: this.id,
isUnread: this.isUnread(),
receivedAt: this.get('received_at'),
...this.getPropsForExpiringMessage(),
};
if (groupUpdate.joined?.length) {
@ -1063,20 +1058,20 @@ export class MessageModel extends Backbone.Model<MessageAttributes> {
}
public async sendSyncMessageOnly(dataMessage: DataMessage) {
const contentMessage = dataMessage.contentProto();
const now = Date.now();
this.set({
sent_to: [UserUtils.getOurPubKeyStrFromCache()],
sent: true,
// NOTE if disappearing message is deleteAfterRead then we don't use this
expirationStartTimestamp: now,
});
const contentMessage = dataMessage.contentProto();
let expireUpdate = null;
const expirationType = dataMessage.getDisappearingMessageType();
if (contentMessage.expirationType && contentMessage.expirationTimer) {
if (expirationType && contentMessage.expirationTimer) {
expireUpdate = {
expirationType: contentMessage.expirationType,
expirationType,
expireTimer: contentMessage.expirationTimer,
lastDisappearingMessageChangeTimestamp:
contentMessage.lastDisappearingMessageChangeTimestamp,
@ -1091,6 +1086,7 @@ export class MessageModel extends Backbone.Model<MessageAttributes> {
public async sendSyncMessage(
data: DataMessage | SignalService.DataMessage,
sentTimestamp: number,
// TODO add proper types
expireUpdate?: any
) {
if (this.get('synced') || this.get('sentSync')) {
@ -1174,6 +1170,7 @@ export class MessageModel extends Backbone.Model<MessageAttributes> {
await this.commit();
// the line below makes sure that getNextExpiringMessage will find this message as expiring.
// getNextExpiringMessage is used on app start to clean already expired messages which should have been removed already, but are not
await this.setToExpire();
const convo = this.getConversation();

@ -3,7 +3,7 @@ import { handleSwarmDataMessage } from './dataMessage';
import { removeFromCache, updateCache } from './cache';
import { SignalService } from '../protobuf';
import { compact, flatten, identity, isEmpty, pickBy, toNumber } from 'lodash';
import { compact, flatten, identity, isEmpty, isEqual, pickBy, toNumber } from 'lodash';
import { KeyPrefixType, PubKey } from '../session/types';
import { BlockedNumberController } from '../util/blockedNumberController';
@ -406,31 +406,28 @@ export async function innerHandleSwarmContentMessage(
perfStart(`handleSwarmDataMessage-${envelope.id}`);
let expireUpdate = null;
const expirationType =
DisappearingMessageConversationSetting[content.expirationType] || 'off';
let expireTimer = content.expirationTimer || 0;
const expireUpdate = {
expirationType: DisappearingMessageConversationSetting[content.expirationType] || 'off',
// TODO rename to expirationTimer?
expireTimer: content.expirationTimer || 0,
// This is used for the expirationTimerUpdate
lastDisappearingMessageChangeTimestamp: content.lastDisappearingMessageChangeTimestamp
? Number(content.lastDisappearingMessageChangeTimestamp)
: undefined,
};
// TODO in the future we will remove the dataMessage expireTimer
// Backwards compatibility for Disappearing Messages in old clients
if (dataMessage.expireTimer) {
if (
expireUpdate.expireTimer > 0 &&
dataMessage.expireTimer &&
!isEqual(expireUpdate.expireTimer, dataMessage.expireTimer)
) {
// TODO Trigger banner in UI?
expireTimer = dataMessage.expireTimer;
expireUpdate.expireTimer = dataMessage.expireTimer;
window.log.info('WIP: Received outdated disappearing message data message', content);
}
// NOTE In the protobuf this is a long
const lastDisappearingMessageChangeTimestamp =
Number(content.lastDisappearingMessageChangeTimestamp) || null;
expireUpdate = {
expirationType,
// TODO rename to expirationTimer?
expireTimer,
lastDisappearingMessageChangeTimestamp,
};
await handleSwarmDataMessage(
envelope,
sentAtTimestamp,

@ -154,7 +154,8 @@ export async function handleSwarmDataMessage(
rawDataMessage: SignalService.DataMessage,
messageHash: string,
senderConversationModel: ConversationModel,
expireUpdate: any
// TODO add proper types
expireUpdate?: any
): Promise<void> {
window.log.info('handleSwarmDataMessage');
@ -245,6 +246,25 @@ export async function handleSwarmDataMessage(
if (isSyncedMessage) {
// TODO handle sync messages separately
window.log.info('WIP: Sync Message dropping');
} else {
const { expirationType, expireTimer, lastDisappearingMessageChangeTimestamp } = expireUpdate;
msgModel.set({
expirationType,
expireTimer,
});
// This message is conversation setting change message
if (expireUpdate.lastDisappearingMessageChangeTimestamp) {
msgModel.set({
expirationTimerUpdate: {
expirationType,
expireTimer,
lastDisappearingMessageChangeTimestamp,
source: msgModel.get('source'),
},
});
}
}
await handleSwarmMessage(
@ -253,8 +273,7 @@ export async function handleSwarmDataMessage(
sentAtTimestamp,
cleanDataMessage,
convoToAddMessageTo,
() => removeFromCache(envelope),
isSyncedMessage ? expireUpdate : null
() => removeFromCache(envelope)
);
}
@ -301,8 +320,7 @@ async function handleSwarmMessage(
sentAt: number,
rawDataMessage: SignalService.DataMessage,
convoToAddMessageTo: ConversationModel,
confirm: () => void,
expireUpdate?: any
confirm: () => void
): Promise<void> {
if (!rawDataMessage || !msgModel) {
window?.log?.warn('Invalid data passed to handleSwarmMessage.');
@ -350,8 +368,7 @@ async function handleSwarmMessage(
toRegularMessage(rawDataMessage),
confirm,
msgModel.get('source'),
messageHash,
expireUpdate
messageHash
);
});
}

@ -1,7 +1,7 @@
import { queueAttachmentDownloads } from './attachments';
import { Quote } from './types';
import _, { isEmpty, isEqual } from 'lodash';
import _, { isEqual } from 'lodash';
import { getConversationController } from '../session/conversations';
import { ConversationModel } from '../models/conversation';
import { MessageModel, sliceQuoteText } from '../models/message';
@ -308,16 +308,9 @@ async function handleExpirationTimerUpdateNoCommit(
message: MessageModel,
source: string,
expirationType: DisappearingMessageConversationType,
expireTimer: number
expireTimer: number,
lastDisappearingMessageChangeTimestamp: number
) {
const providedChangeTimestamp = getNowWithNetworkOffset();
// TODO Not entirely sure that this works
if (conversation.get('lastDisappearingMessageChangeTimestamp') > providedChangeTimestamp) {
window.log.info('WIP: This is an outdated disappearing message setting');
return;
}
message.set({
unread: 0, // mark the message as read.
});
@ -325,10 +318,11 @@ async function handleExpirationTimerUpdateNoCommit(
await conversation.updateExpireTimer({
providedExpirationType: expirationType,
providedExpireTimer: expireTimer,
providedChangeTimestamp,
providedChangeTimestamp: lastDisappearingMessageChangeTimestamp,
providedSource: source,
receivedAt: message.get('received_at'),
shouldCommit: false,
existingMessage: message,
});
}
@ -338,14 +332,14 @@ export async function handleMessageJob(
regularDataMessage: RegularMessageType,
confirm: () => void,
source: string,
messageHash: string,
expireUpdate?: any
messageHash: string
) {
window?.log?.info(
`Starting handleMessageJob for message ${messageModel.idForLogging()}, ${messageModel.get(
'serverTimestamp'
) || messageModel.get('timestamp')} in conversation ${conversation.idForLogging()}`
);
const sendingDeviceConversation = await getConversationController().getOrCreateAndWait(
source,
ConversationTypeEnum.PRIVATE
@ -353,36 +347,33 @@ export async function handleMessageJob(
try {
messageModel.set({ flags: regularDataMessage.flags });
if (!isEmpty(expireUpdate)) {
if (
messageModel.isIncoming() &&
messageModel.get('expirationType') === 'deleteAfterSend' &&
Boolean(messageModel.get('expirationStartTimestamp')) === false
) {
messageModel.set({
expirationType: expireUpdate.expirationType,
expireTimer: expireUpdate.expireTimer,
expirationStartTimestamp: setExpirationStartTimestamp(
'deleteAfterSend',
messageModel.get('sent_at')
),
});
if (
messageModel.isIncoming() &&
messageModel.get('expirationType') === 'deleteAfterSend' &&
Boolean(messageModel.get('expirationStartTimestamp')) === false
) {
messageModel.set({
expirationStartTimestamp: setExpirationStartTimestamp(
'deleteAfterSend',
messageModel.get('sent_at')
),
});
}
}
if (messageModel.isExpirationTimerUpdate()) {
// TODO account for lastDisappearingMessageChangeTimestamp
let expirationType = messageModel.get('expirationType');
const expireTimer = messageModel.get('expireTimer');
const expirationTimerUpdate = messageModel.get('expirationTimerUpdate');
let expirationType = expirationTimerUpdate?.expirationType;
const expireTimer = expirationTimerUpdate?.expireTimer || 0;
const lastDisappearingMessageChangeTimestamp =
expirationTimerUpdate?.lastDisappearingMessageChangeTimestamp || getNowWithNetworkOffset();
// TODO This could happen when we receive a legacy disappearing message
if (!expirationType) {
expirationType = conversation.isPrivate() ? 'deleteAfterRead' : 'deleteAfterSend';
}
// TODO compare types and change timestamps
// Compare mode and timestamp
const oldTypeValue = conversation.get('expirationType');
const oldTimerValue = conversation.get('expireTimer');
if (isEqual(expirationType, oldTypeValue) && isEqual(expireTimer, oldTimerValue)) {
@ -398,7 +389,8 @@ export async function handleMessageJob(
messageModel,
source,
expirationType,
expireTimer
expireTimer,
lastDisappearingMessageChangeTimestamp
);
} else {
// this does not commit to db nor UI unless we need to approve a convo

@ -10,5 +10,6 @@ export abstract class ContentMessage extends Message {
public ttl(): number {
return TTL_DEFAULT.TTL_MAX;
}
public abstract contentProto(): SignalService.Content;
}

@ -1,11 +1,12 @@
import { ContentMessage } from '.';
import { SignalService } from '../../../protobuf';
import { ExpirableMessage } from './ExpirableMessage';
export abstract class DataMessage extends ContentMessage {
export abstract class DataMessage extends ExpirableMessage {
public abstract dataProto(): SignalService.DataMessage;
public contentProto(): SignalService.Content {
return new SignalService.Content({
...super.contentProto(),
dataMessage: this.dataProto(),
});
}

@ -0,0 +1,34 @@
import { SignalService } from '../../../protobuf';
import { DisappearingMessageType } from '../../../util/expiringMessages';
import { ContentMessage } from './ContentMessage';
import { MessageParams } from './Message';
export interface ExpirableMessageParams extends MessageParams {
expirationType?: DisappearingMessageType;
expireTimer?: number;
}
export class ExpirableMessage extends ContentMessage {
public readonly expirationType?: DisappearingMessageType;
public readonly expireTimer?: number;
constructor(params: ExpirableMessageParams) {
super({ timestamp: params.timestamp, identifier: params.identifier });
this.expirationType = params.expirationType;
this.expireTimer = params.expireTimer;
}
public contentProto(): SignalService.Content {
return new SignalService.Content({
expirationType:
this.expirationType === 'deleteAfterSend'
? SignalService.Content.ExpirationType.DELETE_AFTER_SEND
: SignalService.Content.ExpirationType.DELETE_AFTER_READ,
expirationTimer: this.expireTimer,
});
}
public getDisappearingMessageType(): DisappearingMessageType | undefined {
return this.expirationType;
}
}

@ -1,23 +1,22 @@
import { SignalService } from '../../../../protobuf';
import { DisappearingMessageType } from '../../../../util/expiringMessages';
import { PubKey } from '../../../types';
import { StringUtils } from '../../../utils';
import { MessageParams } from '../Message';
import { VisibleMessage } from '../visibleMessage/VisibleMessage';
import { DataMessage } from '../DataMessage';
import { ExpirableMessageParams } from '../ExpirableMessage';
interface ExpirationTimerUpdateMessageParams extends MessageParams {
interface ExpirationTimerUpdateMessageParams extends ExpirableMessageParams {
groupId?: string | PubKey;
syncTarget?: string | PubKey;
expirationType: DisappearingMessageType | null;
expireTimer: number | null;
lastDisappearingMessageChangeTimestamp: number | null;
}
// Note the old disappearing messages used a data message for the expiration time.
// The new ones use properties on the Content Message
// We will remove support for the old one 2 weeks after the release
export class ExpirationTimerUpdateMessage extends VisibleMessage {
export class ExpirationTimerUpdateMessage extends DataMessage {
public readonly groupId?: PubKey;
public readonly syncTarget?: string;
// TODO should this typing be updated
public readonly lastDisappearingMessageChangeTimestamp: number | null;
constructor(params: ExpirationTimerUpdateMessageParams) {
@ -25,30 +24,26 @@ export class ExpirationTimerUpdateMessage extends VisibleMessage {
timestamp: params.timestamp,
identifier: params.identifier,
expirationType: params.expirationType,
expireTimer: params.expireTimer || undefined,
syncTarget: params.syncTarget ? PubKey.cast(params.syncTarget).key : undefined,
expireTimer: params.expireTimer,
});
this.lastDisappearingMessageChangeTimestamp = params.lastDisappearingMessageChangeTimestamp;
const { groupId } = params;
this.groupId = groupId ? PubKey.cast(groupId) : undefined;
this.syncTarget = params.syncTarget ? PubKey.cast(params.syncTarget).key : undefined;
}
public contentProto(): SignalService.Content {
return new SignalService.Content({
...super.contentProto(),
dataMessage: this.dataProto(),
expirationType:
this.expirationType === 'deleteAfterSend'
? SignalService.Content.ExpirationType.DELETE_AFTER_SEND
: SignalService.Content.ExpirationType.DELETE_AFTER_READ,
expirationTimer: this.expireTimer,
lastDisappearingMessageChangeTimestamp: this.lastDisappearingMessageChangeTimestamp,
});
}
public dataProto(): SignalService.DataMessage {
const data = super.dataProto();
const data = new SignalService.DataMessage();
data.flags = SignalService.DataMessage.Flags.EXPIRATION_TIMER_UPDATE;
@ -65,7 +60,11 @@ export class ExpirationTimerUpdateMessage extends VisibleMessage {
data.group = groupMessage;
}
// TODO remove 2 weeks after the release
if (this.syncTarget) {
data.syncTarget = this.syncTarget;
}
// TODO Legacy support remove 2 weeks after the release
if (this.expireTimer) {
data.expireTimer = this.expireTimer;
}

@ -1,12 +1,10 @@
import ByteBuffer from 'bytebuffer';
import { isEmpty } from 'lodash';
import { ContentMessage } from '..';
import { SignalService } from '../../../../protobuf';
import { LokiProfile } from '../../../../types/Message';
import { Reaction } from '../../../../types/Reaction';
import { DisappearingMessageType } from '../../../../util/expiringMessages';
import { DURATION, TTL_DEFAULT } from '../../../constants';
import { MessageParams } from '../Message';
import { ExpirableMessage, ExpirableMessageParams } from '../ExpirableMessage';
interface AttachmentPointerCommon {
contentType?: string;
@ -64,21 +62,17 @@ export interface Quote {
attachments?: Array<QuotedAttachmentWithUrl>;
}
export interface VisibleMessageParams extends MessageParams {
export interface VisibleMessageParams extends ExpirableMessageParams {
attachments?: Array<AttachmentPointerWithUrl>;
body?: string;
quote?: Quote;
expirationType?: DisappearingMessageType;
expireTimer?: number;
lokiProfile?: LokiProfile;
preview?: Array<PreviewWithAttachmentUrl>;
reaction?: Reaction;
syncTarget?: string; // undefined means it is not a synced message
}
export class VisibleMessage extends ContentMessage {
public readonly expirationType?: DisappearingMessageType;
public readonly expireTimer?: number;
export class VisibleMessage extends ExpirableMessage {
public readonly reaction?: Reaction;
private readonly attachments?: Array<AttachmentPointerWithUrl>;
@ -93,12 +87,15 @@ export class VisibleMessage extends ContentMessage {
private readonly syncTarget?: string;
constructor(params: VisibleMessageParams) {
super({ timestamp: params.timestamp, identifier: params.identifier });
super({
timestamp: params.timestamp,
identifier: params.identifier,
expirationType: params.expirationType,
expireTimer: params.expireTimer,
});
this.attachments = params.attachments;
this.body = params.body;
this.quote = params.quote;
this.expirationType = params.expirationType;
this.expireTimer = params.expireTimer;
const profile = buildProfileForOutgoingMessage(params);
@ -112,12 +109,8 @@ export class VisibleMessage extends ContentMessage {
public contentProto(): SignalService.Content {
return new SignalService.Content({
...super.contentProto(),
dataMessage: this.dataProto(),
expirationType:
this.expirationType === 'deleteAfterSend'
? SignalService.Content.ExpirationType.DELETE_AFTER_SEND
: SignalService.Content.ExpirationType.DELETE_AFTER_READ,
expirationTimer: this.expireTimer,
});
}
@ -200,6 +193,7 @@ export class VisibleMessage extends ContentMessage {
return this.identifier === comparator.identifier && this.timestamp === comparator.timestamp;
}
// TODO should this be on the Expirable message? Probably
public ttl(): number {
switch (this.expirationType) {
case 'deleteAfterSend':

@ -324,6 +324,7 @@ export const buildSyncMessage = (
data: DataMessage | SignalService.DataMessage,
syncTarget: string,
sentTimestamp: number,
// TODO add proper types
expireUpdate?: any
): VisibleMessage | ExpirationTimerUpdateMessage => {
if (

@ -130,12 +130,12 @@ export type PropsForGroupUpdateType =
| PropsForGroupUpdateName
| PropsForGroupUpdateLeft;
export type PropsForGroupUpdate = {
export interface PropsForGroupUpdate extends PropsForExpiringMessage {
change: PropsForGroupUpdateType;
messageId: string;
receivedAt: number | undefined;
isUnread: boolean;
};
}
export interface PropsForGroupInvitation extends PropsForExpiringMessage {
serverName: string;

Loading…
Cancel
Save