mirror of
https://github.com/signalapp/Signal-Desktop.git
synced 2026-02-14 23:18:54 +00:00
Avoid race when downloading group avatars post-import
This commit is contained in:
@@ -11,6 +11,8 @@ import type { JOB_STATUS } from './JobQueue.std.js';
|
|||||||
import { JobQueue } from './JobQueue.std.js';
|
import { JobQueue } from './JobQueue.std.js';
|
||||||
import { jobQueueDatabaseStore } from './JobQueueDatabaseStore.preload.js';
|
import { jobQueueDatabaseStore } from './JobQueueDatabaseStore.preload.js';
|
||||||
import { parseUnknown } from '../util/schemas.std.js';
|
import { parseUnknown } from '../util/schemas.std.js';
|
||||||
|
import { waitForOnline } from '../util/waitForOnline.dom.js';
|
||||||
|
import { isOnline } from '../textsecure/WebAPI.preload.js';
|
||||||
|
|
||||||
const groupAvatarJobDataSchema = z.object({
|
const groupAvatarJobDataSchema = z.object({
|
||||||
conversationId: z.string(),
|
conversationId: z.string(),
|
||||||
@@ -29,6 +31,8 @@ export class GroupAvatarJobQueue extends JobQueue<GroupAvatarJobData> {
|
|||||||
{ attempt, log }: Readonly<{ attempt: number; log: LoggerType }>
|
{ attempt, log }: Readonly<{ attempt: number; log: LoggerType }>
|
||||||
): Promise<typeof JOB_STATUS.NEEDS_RETRY | undefined> {
|
): Promise<typeof JOB_STATUS.NEEDS_RETRY | undefined> {
|
||||||
const { conversationId, newAvatarUrl } = data;
|
const { conversationId, newAvatarUrl } = data;
|
||||||
|
await waitForOnline({ server: { isOnline } });
|
||||||
|
|
||||||
const logId = `groupAvatarJobQueue(${conversationId}, attempt=${attempt})`;
|
const logId = `groupAvatarJobQueue(${conversationId}, attempt=${attempt})`;
|
||||||
|
|
||||||
const convo = window.ConversationController.get(conversationId);
|
const convo = window.ConversationController.get(conversationId);
|
||||||
@@ -43,15 +47,21 @@ export class GroupAvatarJobQueue extends JobQueue<GroupAvatarJobData> {
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate correct attributes patch
|
await convo.queueJob('GroupAvatarJobQueue', async () => {
|
||||||
const patch = await applyNewAvatar({
|
if (convo.attributes.remoteAvatarUrl !== newAvatarUrl) {
|
||||||
newAvatarUrl,
|
return;
|
||||||
attributes,
|
}
|
||||||
logId,
|
|
||||||
});
|
|
||||||
|
|
||||||
convo.set(patch);
|
// Generate correct attributes patch
|
||||||
await DataWriter.updateConversation(convo.attributes);
|
const patch = await applyNewAvatar({
|
||||||
|
newAvatarUrl,
|
||||||
|
attributes,
|
||||||
|
logId,
|
||||||
|
});
|
||||||
|
|
||||||
|
convo.set(patch);
|
||||||
|
await DataWriter.updateConversation(convo.attributes);
|
||||||
|
});
|
||||||
|
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
@@ -60,5 +70,5 @@ export class GroupAvatarJobQueue extends JobQueue<GroupAvatarJobData> {
|
|||||||
export const groupAvatarJobQueue = new GroupAvatarJobQueue({
|
export const groupAvatarJobQueue = new GroupAvatarJobQueue({
|
||||||
store: jobQueueDatabaseStore,
|
store: jobQueueDatabaseStore,
|
||||||
queueType: 'groupAvatar',
|
queueType: 'groupAvatar',
|
||||||
maxAttempts: 25,
|
maxAttempts: 5,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -279,7 +279,6 @@ export class BackupImportStream extends Writable {
|
|||||||
#customColorById = new Map<number, CustomColorDataType>();
|
#customColorById = new Map<number, CustomColorDataType>();
|
||||||
#releaseNotesRecipientId: Long | undefined;
|
#releaseNotesRecipientId: Long | undefined;
|
||||||
#releaseNotesChatId: Long | undefined;
|
#releaseNotesChatId: Long | undefined;
|
||||||
#pendingGroupAvatars = new Map<string, string>();
|
|
||||||
#pinnedMessages: Array<PinnedMessageParams> = [];
|
#pinnedMessages: Array<PinnedMessageParams> = [];
|
||||||
#frameErrorCount: number = 0;
|
#frameErrorCount: number = 0;
|
||||||
#backupTier: BackupLevel | undefined;
|
#backupTier: BackupLevel | undefined;
|
||||||
@@ -422,7 +421,19 @@ export class BackupImportStream extends Writable {
|
|||||||
// conversation's last message, which uses redux selectors)
|
// conversation's last message, which uses redux selectors)
|
||||||
await loadAllAndReinitializeRedux();
|
await loadAllAndReinitializeRedux();
|
||||||
|
|
||||||
const allConversations = window.ConversationController.getAll();
|
const allConversations = window.ConversationController.getAll().sort(
|
||||||
|
(convoA, convoB) => {
|
||||||
|
if (convoA.get('isPinned')) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (convoB.get('isPinned')) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
(convoB.get('active_at') ?? 0) - (convoA.get('active_at') ?? 0)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
// Update last message in every active conversation now that we have
|
// Update last message in every active conversation now that we have
|
||||||
// them loaded into memory.
|
// them loaded into memory.
|
||||||
@@ -445,12 +456,21 @@ export class BackupImportStream extends Writable {
|
|||||||
|
|
||||||
// Schedule group avatar download.
|
// Schedule group avatar download.
|
||||||
await pMap(
|
await pMap(
|
||||||
[...this.#pendingGroupAvatars.entries()],
|
allConversations,
|
||||||
async ([conversationId, newAvatarUrl]) => {
|
async conversation => {
|
||||||
if (this.options.type === 'cross-client-integration-test') {
|
if (this.options.type === 'cross-client-integration-test') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await groupAvatarJobQueue.add({ conversationId, newAvatarUrl });
|
if (
|
||||||
|
!isGroup(conversation.attributes) ||
|
||||||
|
!conversation.get('remoteAvatarUrl')
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await groupAvatarJobQueue.add({
|
||||||
|
conversationId: conversation.get('id'),
|
||||||
|
newAvatarUrl: conversation.get('remoteAvatarUrl'),
|
||||||
|
});
|
||||||
},
|
},
|
||||||
{ concurrency: MAX_CONCURRENCY }
|
{ concurrency: MAX_CONCURRENCY }
|
||||||
);
|
);
|
||||||
@@ -1218,6 +1238,7 @@ export class BackupImportStream extends Writable {
|
|||||||
url: avatarUrl,
|
url: avatarUrl,
|
||||||
}
|
}
|
||||||
: undefined,
|
: undefined,
|
||||||
|
remoteAvatarUrl: dropNull(avatarUrl),
|
||||||
color: fromAvatarColor(group.avatarColor),
|
color: fromAvatarColor(group.avatarColor),
|
||||||
colorFromPrimary: dropNull(group.avatarColor),
|
colorFromPrimary: dropNull(group.avatarColor),
|
||||||
|
|
||||||
@@ -1319,9 +1340,7 @@ export class BackupImportStream extends Writable {
|
|||||||
: undefined,
|
: undefined,
|
||||||
announcementsOnly: dropNull(announcementsOnly),
|
announcementsOnly: dropNull(announcementsOnly),
|
||||||
};
|
};
|
||||||
if (avatarUrl) {
|
|
||||||
this.#pendingGroupAvatars.set(attrs.id, avatarUrl);
|
|
||||||
}
|
|
||||||
if (group.blocked) {
|
if (group.blocked) {
|
||||||
await itemStorage.blocked.addBlockedGroup(groupId);
|
await itemStorage.blocked.addBlockedGroup(groupId);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user