2022-03-04 21:14:52 +00:00
|
|
|
// Copyright 2022 Signal Messenger, LLC
|
|
|
|
// SPDX-License-Identifier: AGPL-3.0-only
|
|
|
|
|
|
|
|
import { pick } from 'lodash';
|
|
|
|
import type { MessageAttributesType } from '../model-types.d';
|
|
|
|
import type { StoryDataType } from '../state/ducks/stories';
|
2022-06-23 20:36:11 +00:00
|
|
|
import * as durations from '../util/durations';
|
2022-03-04 21:14:52 +00:00
|
|
|
import * as log from '../logging/log';
|
|
|
|
import dataInterface from '../sql/Client';
|
2022-10-31 21:28:28 +00:00
|
|
|
import {
|
|
|
|
getAttachmentsForMessage,
|
|
|
|
getPropsForAttachment,
|
|
|
|
} from '../state/selectors/message';
|
|
|
|
import type { LinkPreviewType } from '../types/message/LinkPreviews';
|
2022-03-04 21:14:52 +00:00
|
|
|
import { isNotNil } from '../util/isNotNil';
|
|
|
|
import { strictAssert } from '../util/assert';
|
2022-09-09 18:35:00 +00:00
|
|
|
import { dropNull } from '../util/dropNull';
|
2022-10-22 06:26:16 +00:00
|
|
|
import { isGroup } from '../util/whatTypeOfConversation';
|
2022-03-04 21:14:52 +00:00
|
|
|
|
2022-10-22 06:26:16 +00:00
|
|
|
let storyData:
|
|
|
|
| Array<
|
|
|
|
MessageAttributesType & {
|
|
|
|
hasReplies?: boolean;
|
|
|
|
hasRepliesFromSelf?: boolean;
|
|
|
|
}
|
|
|
|
>
|
|
|
|
| undefined;
|
2022-03-04 21:14:52 +00:00
|
|
|
|
|
|
|
export async function loadStories(): Promise<void> {
|
2022-10-22 06:26:16 +00:00
|
|
|
const stories = await dataInterface.getAllStories({});
|
|
|
|
|
|
|
|
storyData = await Promise.all(
|
|
|
|
stories.map(async story => {
|
|
|
|
const conversation = window.ConversationController.get(
|
|
|
|
story.conversationId
|
|
|
|
);
|
|
|
|
|
|
|
|
if (!isGroup(conversation?.attributes)) {
|
|
|
|
return story;
|
|
|
|
}
|
|
|
|
|
|
|
|
const [hasReplies, hasRepliesFromSelf] = await Promise.all([
|
|
|
|
dataInterface.hasStoryReplies(story.id),
|
|
|
|
dataInterface.hasStoryRepliesFromSelf(story.id),
|
|
|
|
]);
|
|
|
|
|
|
|
|
return {
|
|
|
|
...story,
|
|
|
|
hasReplies,
|
|
|
|
hasRepliesFromSelf,
|
|
|
|
};
|
|
|
|
})
|
|
|
|
);
|
|
|
|
|
2022-05-11 21:02:26 +00:00
|
|
|
await repairUnexpiredStories();
|
2022-03-04 21:14:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
export function getStoryDataFromMessageAttributes(
|
2022-10-22 06:26:16 +00:00
|
|
|
message: MessageAttributesType & {
|
|
|
|
hasReplies?: boolean;
|
|
|
|
hasRepliesFromSelf?: boolean;
|
|
|
|
}
|
2022-03-04 21:14:52 +00:00
|
|
|
): StoryDataType | undefined {
|
2022-07-01 00:52:03 +00:00
|
|
|
const { attachments, deletedForEveryone } = message;
|
2022-03-04 21:14:52 +00:00
|
|
|
const unresolvedAttachment = attachments ? attachments[0] : undefined;
|
2022-07-01 00:52:03 +00:00
|
|
|
if (!unresolvedAttachment && !deletedForEveryone) {
|
2022-03-04 21:14:52 +00:00
|
|
|
log.warn(
|
|
|
|
`getStoryDataFromMessageAttributes: ${message.id} does not have an attachment`
|
|
|
|
);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-10-31 21:28:28 +00:00
|
|
|
let [attachment] =
|
2022-07-01 00:52:03 +00:00
|
|
|
unresolvedAttachment && unresolvedAttachment.path
|
|
|
|
? getAttachmentsForMessage(message)
|
|
|
|
: [unresolvedAttachment];
|
2022-03-04 21:14:52 +00:00
|
|
|
|
2022-10-31 21:28:28 +00:00
|
|
|
let preview: LinkPreviewType | undefined;
|
|
|
|
if (message.preview?.length) {
|
|
|
|
strictAssert(
|
|
|
|
message.preview.length === 1,
|
|
|
|
'getStoryDataFromMessageAttributes: story can have only one preview'
|
|
|
|
);
|
|
|
|
[preview] = message.preview;
|
|
|
|
|
|
|
|
strictAssert(
|
|
|
|
attachment?.textAttachment,
|
|
|
|
'getStoryDataFromMessageAttributes: story must have a ' +
|
|
|
|
'textAttachment with preview'
|
|
|
|
);
|
|
|
|
attachment = {
|
|
|
|
...attachment,
|
|
|
|
textAttachment: {
|
|
|
|
...attachment.textAttachment,
|
|
|
|
preview: {
|
|
|
|
...preview,
|
|
|
|
image: preview.image && getPropsForAttachment(preview.image),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
};
|
|
|
|
} else if (attachment) {
|
|
|
|
attachment = getPropsForAttachment(attachment);
|
|
|
|
}
|
|
|
|
|
2022-03-04 21:14:52 +00:00
|
|
|
return {
|
|
|
|
attachment,
|
|
|
|
messageId: message.id,
|
|
|
|
...pick(message, [
|
2022-07-01 00:52:03 +00:00
|
|
|
'canReplyToStory',
|
2022-03-04 21:14:52 +00:00
|
|
|
'conversationId',
|
2022-04-15 00:08:46 +00:00
|
|
|
'deletedForEveryone',
|
2022-10-22 06:26:16 +00:00
|
|
|
'hasReplies',
|
|
|
|
'hasRepliesFromSelf',
|
2022-04-28 22:06:28 +00:00
|
|
|
'reactions',
|
2022-09-21 23:54:48 +00:00
|
|
|
'readAt',
|
2022-03-04 21:14:52 +00:00
|
|
|
'readStatus',
|
2022-04-15 00:08:46 +00:00
|
|
|
'sendStateByConversationId',
|
2022-03-04 21:14:52 +00:00
|
|
|
'source',
|
|
|
|
'sourceUuid',
|
2022-07-01 00:52:03 +00:00
|
|
|
'storyDistributionListId',
|
2022-03-04 21:14:52 +00:00
|
|
|
'timestamp',
|
2022-04-15 00:08:46 +00:00
|
|
|
'type',
|
2022-03-04 21:14:52 +00:00
|
|
|
]),
|
2022-09-09 18:35:00 +00:00
|
|
|
expireTimer: message.expireTimer,
|
|
|
|
expirationStartTimestamp: dropNull(message.expirationStartTimestamp),
|
2022-03-04 21:14:52 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
export function getStoriesForRedux(): Array<StoryDataType> {
|
|
|
|
strictAssert(storyData, 'storyData has not been loaded');
|
|
|
|
|
|
|
|
const stories = storyData
|
2022-04-28 22:06:28 +00:00
|
|
|
.map(getStoryDataFromMessageAttributes)
|
2022-03-04 21:14:52 +00:00
|
|
|
.filter(isNotNil);
|
|
|
|
|
|
|
|
storyData = undefined;
|
|
|
|
|
|
|
|
return stories;
|
|
|
|
}
|
2022-04-11 21:28:01 +00:00
|
|
|
|
2022-05-11 21:02:26 +00:00
|
|
|
async function repairUnexpiredStories(): Promise<void> {
|
2022-04-11 21:28:01 +00:00
|
|
|
strictAssert(storyData, 'Could not load stories');
|
|
|
|
|
2022-06-23 20:36:11 +00:00
|
|
|
const DAY_AS_SECONDS = durations.DAY / 1000;
|
|
|
|
|
2022-04-11 21:28:01 +00:00
|
|
|
const storiesWithExpiry = storyData
|
2022-06-23 20:36:11 +00:00
|
|
|
.filter(
|
|
|
|
story =>
|
|
|
|
!story.expirationStartTimestamp ||
|
|
|
|
!story.expireTimer ||
|
|
|
|
story.expireTimer > DAY_AS_SECONDS
|
|
|
|
)
|
2022-04-11 21:28:01 +00:00
|
|
|
.map(story => ({
|
|
|
|
...story,
|
2022-08-17 17:56:41 +00:00
|
|
|
expirationStartTimestamp: Math.min(story.timestamp, Date.now()),
|
2022-06-23 20:36:11 +00:00
|
|
|
expireTimer: Math.min(
|
|
|
|
Math.floor((story.timestamp + durations.DAY - Date.now()) / 1000),
|
|
|
|
DAY_AS_SECONDS
|
|
|
|
),
|
2022-04-11 21:28:01 +00:00
|
|
|
}));
|
|
|
|
|
2022-05-11 21:02:26 +00:00
|
|
|
if (!storiesWithExpiry.length) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-04-11 21:28:01 +00:00
|
|
|
log.info(
|
|
|
|
'repairUnexpiredStories: repairing number of stories',
|
|
|
|
storiesWithExpiry.length
|
|
|
|
);
|
|
|
|
|
|
|
|
await Promise.all(
|
|
|
|
storiesWithExpiry.map(messageAttributes => {
|
|
|
|
return window.Signal.Data.saveMessage(messageAttributes, {
|
|
|
|
ourUuid: window.textsecure.storage.user.getCheckedUuid().toString(),
|
|
|
|
});
|
|
|
|
})
|
|
|
|
);
|
|
|
|
}
|