On send, pull data from target edit if sending edit
This commit is contained in:
parent
146b562c91
commit
48245eeea6
12 changed files with 529 additions and 135 deletions
143
ts/util/editHelpers.ts
Normal file
143
ts/util/editHelpers.ts
Normal file
|
@ -0,0 +1,143 @@
|
|||
// Copyright 2021 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import { isNumber, sortBy } from 'lodash';
|
||||
|
||||
import { strictAssert } from './assert';
|
||||
import { isSent } from '../messages/MessageSendState';
|
||||
|
||||
import type { EditHistoryType } from '../model-types';
|
||||
import type { MessageModel } from '../models/messages';
|
||||
import type { LoggerType } from '../types/Logging';
|
||||
|
||||
export function hasEditBeenSent(message: MessageModel): boolean {
|
||||
const originalTimestamp = message.get('timestamp');
|
||||
const editHistory = message.get('editHistory') || [];
|
||||
|
||||
return Boolean(
|
||||
editHistory.find(item => {
|
||||
if (item.timestamp === originalTimestamp) {
|
||||
return false;
|
||||
}
|
||||
return Object.values(item.sendStateByConversationId || {}).some(
|
||||
sendState => isSent(sendState.status)
|
||||
);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
// The tricky bit for this function is if we are on our second+ attempt to send a given
|
||||
// edit, we're still sending that edit.
|
||||
export function getTargetOfThisEditTimestamp({
|
||||
message,
|
||||
targetTimestamp,
|
||||
}: {
|
||||
message: MessageModel;
|
||||
targetTimestamp: number;
|
||||
}): number {
|
||||
const originalTimestamp = message.get('timestamp');
|
||||
const editHistory = message.get('editHistory') || [];
|
||||
|
||||
const sentItems = editHistory.filter(item => {
|
||||
return item.timestamp <= targetTimestamp;
|
||||
});
|
||||
const mostRecent = sortBy(
|
||||
sentItems,
|
||||
(item: EditHistoryType) => item.timestamp
|
||||
);
|
||||
|
||||
const { length } = mostRecent;
|
||||
|
||||
// We want the second-to-last item, because we may have partially sent targetTimestamp
|
||||
if (length > 1) {
|
||||
return mostRecent[length - 2].timestamp;
|
||||
}
|
||||
// If there's only one item, we'll use it
|
||||
if (length > 0) {
|
||||
return mostRecent[length - 1].timestamp;
|
||||
}
|
||||
|
||||
// This is a good failover in case we ever stop duplicating data in editHistory
|
||||
return originalTimestamp;
|
||||
}
|
||||
|
||||
export function getPropForTimestamp<T extends keyof EditHistoryType>({
|
||||
log,
|
||||
message,
|
||||
prop,
|
||||
targetTimestamp,
|
||||
}: {
|
||||
log: LoggerType;
|
||||
message: MessageModel;
|
||||
prop: T;
|
||||
targetTimestamp: number;
|
||||
}): EditHistoryType[T] {
|
||||
const logId = `getPropForTimestamp(${message.idForLogging()}, target=${targetTimestamp}})`;
|
||||
|
||||
const editHistory = message.get('editHistory');
|
||||
const targetEdit = editHistory?.find(
|
||||
item => item.timestamp === targetTimestamp
|
||||
);
|
||||
if (!targetEdit) {
|
||||
if (editHistory) {
|
||||
log.warn(`${logId}: No edit found, using top-level data`);
|
||||
}
|
||||
return message.get(prop);
|
||||
}
|
||||
|
||||
return targetEdit[prop];
|
||||
}
|
||||
|
||||
export function setPropForTimestamp<T extends keyof EditHistoryType>({
|
||||
log,
|
||||
message,
|
||||
prop,
|
||||
targetTimestamp,
|
||||
value,
|
||||
}: {
|
||||
log: LoggerType;
|
||||
message: MessageModel;
|
||||
prop: T;
|
||||
targetTimestamp: number;
|
||||
value: EditHistoryType[T];
|
||||
}): void {
|
||||
const logId = `setPropForTimestamp(${message.idForLogging()}, target=${targetTimestamp}})`;
|
||||
|
||||
const editHistory = message.get('editHistory');
|
||||
const targetEditIndex = editHistory?.findIndex(
|
||||
item => item.timestamp === targetTimestamp
|
||||
);
|
||||
const targetEdit =
|
||||
editHistory && isNumber(targetEditIndex)
|
||||
? editHistory[targetEditIndex]
|
||||
: undefined;
|
||||
|
||||
if (!targetEdit) {
|
||||
if (editHistory) {
|
||||
log.warn(`${logId}: No edit found, updating top-level data`);
|
||||
}
|
||||
message.set({
|
||||
[prop]: value,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
strictAssert(editHistory, 'Got targetEdit, but no editHistory');
|
||||
strictAssert(
|
||||
isNumber(targetEditIndex),
|
||||
'Got targetEdit, but no targetEditIndex'
|
||||
);
|
||||
|
||||
const newEditHistory = [...editHistory];
|
||||
newEditHistory[targetEditIndex] = { ...targetEdit, [prop]: value };
|
||||
|
||||
message.set('editHistory', newEditHistory);
|
||||
|
||||
// We always edit the top-level attribute if this is the most recent send
|
||||
const editMessageTimestamp = message.get('editMessageTimestamp');
|
||||
if (!editMessageTimestamp || editMessageTimestamp === targetTimestamp) {
|
||||
message.set({
|
||||
[prop]: value,
|
||||
});
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue