Show progress dialog during delete
This commit is contained in:
parent
8c25ffd6f5
commit
8116a8561d
7 changed files with 135 additions and 130 deletions
|
@ -38,6 +38,7 @@ describe('KeyChangeListener', () => {
|
||||||
|
|
||||||
after(async () => {
|
after(async () => {
|
||||||
await window.Signal.Data.removeAllMessagesInConversation(convo.id, {
|
await window.Signal.Data.removeAllMessagesInConversation(convo.id, {
|
||||||
|
logId: phoneNumberWithKeyChange,
|
||||||
MessageCollection: Whisper.MessageCollection,
|
MessageCollection: Whisper.MessageCollection,
|
||||||
});
|
});
|
||||||
await window.Signal.Data.removeConversation(convo.id, {
|
await window.Signal.Data.removeConversation(convo.id, {
|
||||||
|
@ -78,6 +79,7 @@ describe('KeyChangeListener', () => {
|
||||||
});
|
});
|
||||||
after(async () => {
|
after(async () => {
|
||||||
await window.Signal.Data.removeAllMessagesInConversation(groupConvo.id, {
|
await window.Signal.Data.removeAllMessagesInConversation(groupConvo.id, {
|
||||||
|
logId: phoneNumberWithKeyChange,
|
||||||
MessageCollection: Whisper.MessageCollection,
|
MessageCollection: Whisper.MessageCollection,
|
||||||
});
|
});
|
||||||
await window.Signal.Data.removeConversation(groupConvo.id, {
|
await window.Signal.Data.removeConversation(groupConvo.id, {
|
||||||
|
|
|
@ -240,7 +240,7 @@ type WhatIsThis = import('./window.d').WhatIsThis;
|
||||||
if (_.isNumber(preMessageReceiverStatus)) {
|
if (_.isNumber(preMessageReceiverStatus)) {
|
||||||
return preMessageReceiverStatus;
|
return preMessageReceiverStatus;
|
||||||
}
|
}
|
||||||
return -1;
|
return WebSocket.CLOSED;
|
||||||
};
|
};
|
||||||
window.Whisper.events = _.clone(window.Backbone.Events);
|
window.Whisper.events = _.clone(window.Backbone.Events);
|
||||||
let accountManager: typeof window.textsecure.AccountManager;
|
let accountManager: typeof window.textsecure.AccountManager;
|
||||||
|
@ -1604,7 +1604,17 @@ type WhatIsThis = import('./window.d').WhatIsThis;
|
||||||
|
|
||||||
// Maybe refresh remote configuration when we become active
|
// Maybe refresh remote configuration when we become active
|
||||||
window.registerForActive(async () => {
|
window.registerForActive(async () => {
|
||||||
await window.Signal.RemoteConfig.maybeRefreshRemoteConfig();
|
try {
|
||||||
|
await window.Signal.RemoteConfig.maybeRefreshRemoteConfig();
|
||||||
|
} catch (error) {
|
||||||
|
if (error && window._.isNumber(error.code)) {
|
||||||
|
window.log.warn(
|
||||||
|
`registerForActive: Failed to to refresh remote config. Code: ${error.code}`
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Listen for changes to the `desktop.clientExpiration` remote flag
|
// Listen for changes to the `desktop.clientExpiration` remote flag
|
||||||
|
|
|
@ -4013,6 +4013,7 @@ export class ConversationModel extends window.Backbone.Model<
|
||||||
window.Signal.Data.updateConversation(this.attributes);
|
window.Signal.Data.updateConversation(this.attributes);
|
||||||
|
|
||||||
await window.Signal.Data.removeAllMessagesInConversation(this.id, {
|
await window.Signal.Data.removeAllMessagesInConversation(this.id, {
|
||||||
|
logId: this.idForLogging(),
|
||||||
MessageCollection: window.Whisper.MessageCollection,
|
MessageCollection: window.Whisper.MessageCollection,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -150,6 +150,7 @@ const dataInterface: ClientInterface = {
|
||||||
saveMessage,
|
saveMessage,
|
||||||
saveMessages,
|
saveMessages,
|
||||||
removeMessage,
|
removeMessage,
|
||||||
|
removeMessages,
|
||||||
getUnreadByConversation,
|
getUnreadByConversation,
|
||||||
|
|
||||||
getMessageBySender,
|
getMessageBySender,
|
||||||
|
@ -225,7 +226,6 @@ const dataInterface: ClientInterface = {
|
||||||
// Client-side only, and test-only
|
// Client-side only, and test-only
|
||||||
|
|
||||||
_removeConversations,
|
_removeConversations,
|
||||||
_removeMessages,
|
|
||||||
_cleanData,
|
_cleanData,
|
||||||
_jobs,
|
_jobs,
|
||||||
};
|
};
|
||||||
|
@ -903,8 +903,8 @@ async function removeMessage(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note: this method will not clean up external files, just delete from SQL
|
// Note: this method will not clean up external files, just delete from SQL
|
||||||
async function _removeMessages(ids: Array<string>) {
|
async function removeMessages(ids: Array<string>) {
|
||||||
await channels.removeMessage(ids);
|
await channels.removeMessages(ids);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getMessageById(
|
async function getMessageById(
|
||||||
|
@ -1074,15 +1074,23 @@ async function migrateConversationMessages(
|
||||||
async function removeAllMessagesInConversation(
|
async function removeAllMessagesInConversation(
|
||||||
conversationId: string,
|
conversationId: string,
|
||||||
{
|
{
|
||||||
|
logId,
|
||||||
MessageCollection,
|
MessageCollection,
|
||||||
}: { MessageCollection: typeof MessageModelCollectionType }
|
}: {
|
||||||
|
logId: string;
|
||||||
|
MessageCollection: typeof MessageModelCollectionType;
|
||||||
|
}
|
||||||
) {
|
) {
|
||||||
let messages;
|
let messages;
|
||||||
do {
|
do {
|
||||||
// Yes, we really want the await in the loop. We're deleting 100 at a
|
const chunkSize = 20;
|
||||||
|
window.log.info(
|
||||||
|
`removeAllMessagesInConversation/${logId}: Fetching chunk of ${chunkSize} messages`
|
||||||
|
);
|
||||||
|
// Yes, we really want the await in the loop. We're deleting a chunk at a
|
||||||
// time so we don't use too much memory.
|
// time so we don't use too much memory.
|
||||||
messages = await getOlderMessagesByConversation(conversationId, {
|
messages = await getOlderMessagesByConversation(conversationId, {
|
||||||
limit: 100,
|
limit: chunkSize,
|
||||||
MessageCollection,
|
MessageCollection,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1092,13 +1100,17 @@ async function removeAllMessagesInConversation(
|
||||||
|
|
||||||
const ids = messages.map((message: MessageModel) => message.id);
|
const ids = messages.map((message: MessageModel) => message.id);
|
||||||
|
|
||||||
|
window.log.info(`removeAllMessagesInConversation/${logId}: Cleanup...`);
|
||||||
// Note: It's very important that these models are fully hydrated because
|
// Note: It's very important that these models are fully hydrated because
|
||||||
// we need to delete all associated on-disk files along with the database delete.
|
// we need to delete all associated on-disk files along with the database delete.
|
||||||
await Promise.all(
|
const queue = new window.PQueue({ concurrency: 3, timeout: 1000 * 60 * 2 });
|
||||||
messages.map(async (message: MessageModel) => message.cleanup())
|
queue.addAll(
|
||||||
|
messages.map((message: MessageModel) => async () => message.cleanup())
|
||||||
);
|
);
|
||||||
|
await queue.onIdle();
|
||||||
|
|
||||||
await channels.removeMessage(ids);
|
window.log.info(`removeAllMessagesInConversation/${logId}: Deleting...`);
|
||||||
|
await channels.removeMessages(ids);
|
||||||
} while (messages.length > 0);
|
} while (messages.length > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -238,7 +238,8 @@ export type ServerInterface = DataInterface & {
|
||||||
conversationId: string
|
conversationId: string
|
||||||
) => Promise<Array<MessageType>>;
|
) => Promise<Array<MessageType>>;
|
||||||
removeConversation: (id: Array<string> | string) => Promise<void>;
|
removeConversation: (id: Array<string> | string) => Promise<void>;
|
||||||
removeMessage: (id: Array<string> | string) => Promise<void>;
|
removeMessage: (id: string) => Promise<void>;
|
||||||
|
removeMessages: (ids: Array<string>) => Promise<void>;
|
||||||
saveMessage: (
|
saveMessage: (
|
||||||
data: MessageType,
|
data: MessageType,
|
||||||
options: { forceSave?: boolean }
|
options: { forceSave?: boolean }
|
||||||
|
@ -266,51 +267,41 @@ export type ServerInterface = DataInterface & {
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ClientInterface = DataInterface & {
|
export type ClientInterface = DataInterface & {
|
||||||
getAllConversations: ({
|
getAllConversations: (options: {
|
||||||
ConversationCollection,
|
|
||||||
}: {
|
|
||||||
ConversationCollection: typeof ConversationModelCollectionType;
|
ConversationCollection: typeof ConversationModelCollectionType;
|
||||||
}) => Promise<ConversationModelCollectionType>;
|
}) => Promise<ConversationModelCollectionType>;
|
||||||
getAllGroupsInvolvingId: (
|
getAllGroupsInvolvingId: (
|
||||||
id: string,
|
id: string,
|
||||||
{
|
options: {
|
||||||
ConversationCollection,
|
|
||||||
}: {
|
|
||||||
ConversationCollection: typeof ConversationModelCollectionType;
|
ConversationCollection: typeof ConversationModelCollectionType;
|
||||||
}
|
}
|
||||||
) => Promise<ConversationModelCollectionType>;
|
) => Promise<ConversationModelCollectionType>;
|
||||||
getAllPrivateConversations: ({
|
getAllPrivateConversations: (options: {
|
||||||
ConversationCollection,
|
|
||||||
}: {
|
|
||||||
ConversationCollection: typeof ConversationModelCollectionType;
|
ConversationCollection: typeof ConversationModelCollectionType;
|
||||||
}) => Promise<ConversationModelCollectionType>;
|
}) => Promise<ConversationModelCollectionType>;
|
||||||
getConversationById: (
|
getConversationById: (
|
||||||
id: string,
|
id: string,
|
||||||
{ Conversation }: { Conversation: typeof ConversationModel }
|
options: { Conversation: typeof ConversationModel }
|
||||||
) => Promise<ConversationModel>;
|
) => Promise<ConversationModel>;
|
||||||
getExpiredMessages: ({
|
getExpiredMessages: (options: {
|
||||||
MessageCollection,
|
|
||||||
}: {
|
|
||||||
MessageCollection: typeof MessageModelCollectionType;
|
MessageCollection: typeof MessageModelCollectionType;
|
||||||
}) => Promise<MessageModelCollectionType>;
|
}) => Promise<MessageModelCollectionType>;
|
||||||
getMessageById: (
|
getMessageById: (
|
||||||
id: string,
|
id: string,
|
||||||
{ Message }: { Message: typeof MessageModel }
|
options: { Message: typeof MessageModel }
|
||||||
) => Promise<MessageType | undefined>;
|
) => Promise<MessageType | undefined>;
|
||||||
getMessageBySender: (
|
getMessageBySender: (
|
||||||
options: {
|
data: {
|
||||||
source: string;
|
source: string;
|
||||||
sourceUuid: string;
|
sourceUuid: string;
|
||||||
sourceDevice: string;
|
sourceDevice: string;
|
||||||
sent_at: number;
|
sent_at: number;
|
||||||
},
|
},
|
||||||
{ Message }: { Message: typeof MessageModel }
|
options: { Message: typeof MessageModel }
|
||||||
) => Promise<MessageModel | null>;
|
) => Promise<MessageModel | null>;
|
||||||
getMessagesBySentAt: (
|
getMessagesBySentAt: (
|
||||||
sentAt: number,
|
sentAt: number,
|
||||||
{
|
options: { MessageCollection: typeof MessageModelCollectionType }
|
||||||
MessageCollection,
|
|
||||||
}: { MessageCollection: typeof MessageModelCollectionType }
|
|
||||||
) => Promise<MessageModelCollectionType>;
|
) => Promise<MessageModelCollectionType>;
|
||||||
getOlderMessagesByConversation: (
|
getOlderMessagesByConversation: (
|
||||||
conversationId: string,
|
conversationId: string,
|
||||||
|
@ -341,39 +332,33 @@ export type ClientInterface = DataInterface & {
|
||||||
Message: typeof MessageModel;
|
Message: typeof MessageModel;
|
||||||
}
|
}
|
||||||
) => Promise<MessageModel | undefined>;
|
) => Promise<MessageModel | undefined>;
|
||||||
getNextExpiringMessage: ({
|
getNextExpiringMessage: (options: {
|
||||||
Message,
|
|
||||||
}: {
|
|
||||||
Message: typeof MessageModel;
|
Message: typeof MessageModel;
|
||||||
}) => Promise<MessageModel | null>;
|
}) => Promise<MessageModel | null>;
|
||||||
getNextTapToViewMessageToAgeOut: ({
|
getNextTapToViewMessageToAgeOut: (options: {
|
||||||
Message,
|
|
||||||
}: {
|
|
||||||
Message: typeof MessageModel;
|
Message: typeof MessageModel;
|
||||||
}) => Promise<MessageModel | null>;
|
}) => Promise<MessageModel | null>;
|
||||||
getOutgoingWithoutExpiresAt: ({
|
getOutgoingWithoutExpiresAt: (options: {
|
||||||
MessageCollection,
|
|
||||||
}: {
|
|
||||||
MessageCollection: typeof MessageModelCollectionType;
|
MessageCollection: typeof MessageModelCollectionType;
|
||||||
}) => Promise<MessageModelCollectionType>;
|
}) => Promise<MessageModelCollectionType>;
|
||||||
getTapToViewMessagesNeedingErase: ({
|
getTapToViewMessagesNeedingErase: (options: {
|
||||||
MessageCollection,
|
|
||||||
}: {
|
|
||||||
MessageCollection: typeof MessageModelCollectionType;
|
MessageCollection: typeof MessageModelCollectionType;
|
||||||
}) => Promise<MessageModelCollectionType>;
|
}) => Promise<MessageModelCollectionType>;
|
||||||
getUnreadByConversation: (
|
getUnreadByConversation: (
|
||||||
conversationId: string,
|
conversationId: string,
|
||||||
{
|
options: { MessageCollection: typeof MessageModelCollectionType }
|
||||||
MessageCollection,
|
|
||||||
}: { MessageCollection: typeof MessageModelCollectionType }
|
|
||||||
) => Promise<MessageModelCollectionType>;
|
) => Promise<MessageModelCollectionType>;
|
||||||
removeConversation: (
|
removeConversation: (
|
||||||
id: string,
|
id: string,
|
||||||
{ Conversation }: { Conversation: typeof ConversationModel }
|
options: { Conversation: typeof ConversationModel }
|
||||||
) => Promise<void>;
|
) => Promise<void>;
|
||||||
removeMessage: (
|
removeMessage: (
|
||||||
id: string,
|
id: string,
|
||||||
{ Message }: { Message: typeof MessageModel }
|
options: { Message: typeof MessageModel }
|
||||||
|
) => Promise<void>;
|
||||||
|
removeMessages: (
|
||||||
|
ids: Array<string>,
|
||||||
|
options: { Message: typeof MessageModel }
|
||||||
) => Promise<void>;
|
) => Promise<void>;
|
||||||
saveMessage: (
|
saveMessage: (
|
||||||
data: MessageType,
|
data: MessageType,
|
||||||
|
@ -383,9 +368,7 @@ export type ClientInterface = DataInterface & {
|
||||||
|
|
||||||
// Test-only
|
// Test-only
|
||||||
|
|
||||||
_getAllMessages: ({
|
_getAllMessages: (options: {
|
||||||
MessageCollection,
|
|
||||||
}: {
|
|
||||||
MessageCollection: typeof MessageModelCollectionType;
|
MessageCollection: typeof MessageModelCollectionType;
|
||||||
}) => Promise<MessageModelCollectionType>;
|
}) => Promise<MessageModelCollectionType>;
|
||||||
|
|
||||||
|
@ -394,9 +377,10 @@ export type ClientInterface = DataInterface & {
|
||||||
shutdown: () => Promise<void>;
|
shutdown: () => Promise<void>;
|
||||||
removeAllMessagesInConversation: (
|
removeAllMessagesInConversation: (
|
||||||
conversationId: string,
|
conversationId: string,
|
||||||
{
|
options: {
|
||||||
MessageCollection,
|
logId: string;
|
||||||
}: { MessageCollection: typeof MessageModelCollectionType }
|
MessageCollection: typeof MessageModelCollectionType;
|
||||||
|
}
|
||||||
) => Promise<void>;
|
) => Promise<void>;
|
||||||
removeOtherData: () => Promise<void>;
|
removeOtherData: () => Promise<void>;
|
||||||
cleanupOrphanedAttachments: () => Promise<void>;
|
cleanupOrphanedAttachments: () => Promise<void>;
|
||||||
|
@ -405,7 +389,6 @@ export type ClientInterface = DataInterface & {
|
||||||
// Client-side only, and test-only
|
// Client-side only, and test-only
|
||||||
|
|
||||||
_removeConversations: (ids: Array<string>) => Promise<void>;
|
_removeConversations: (ids: Array<string>) => Promise<void>;
|
||||||
_removeMessages: (ids: Array<string>) => Promise<void>;
|
|
||||||
_cleanData: (data: any, path?: string) => any;
|
_cleanData: (data: any, path?: string) => any;
|
||||||
_jobs: { [id: string]: ClientJobType };
|
_jobs: { [id: string]: ClientJobType };
|
||||||
};
|
};
|
||||||
|
|
129
ts/sql/Server.ts
129
ts/sql/Server.ts
|
@ -129,6 +129,7 @@ const dataInterface: ServerInterface = {
|
||||||
saveMessage,
|
saveMessage,
|
||||||
saveMessages,
|
saveMessages,
|
||||||
removeMessage,
|
removeMessage,
|
||||||
|
removeMessages,
|
||||||
getUnreadByConversation,
|
getUnreadByConversation,
|
||||||
getMessageBySender,
|
getMessageBySender,
|
||||||
getMessageById,
|
getMessageById,
|
||||||
|
@ -236,6 +237,7 @@ type PromisifiedSQLDatabase = {
|
||||||
statement: string,
|
statement: string,
|
||||||
params?: { [key: string]: any }
|
params?: { [key: string]: any }
|
||||||
) => Promise<Array<any>>;
|
) => Promise<Array<any>>;
|
||||||
|
on: (event: 'trace', handler: (sql: string) => void) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
function promisify(rawInstance: sql.Database): PromisifiedSQLDatabase {
|
function promisify(rawInstance: sql.Database): PromisifiedSQLDatabase {
|
||||||
|
@ -244,6 +246,7 @@ function promisify(rawInstance: sql.Database): PromisifiedSQLDatabase {
|
||||||
run: pify(rawInstance.run.bind(rawInstance)),
|
run: pify(rawInstance.run.bind(rawInstance)),
|
||||||
get: pify(rawInstance.get.bind(rawInstance)),
|
get: pify(rawInstance.get.bind(rawInstance)),
|
||||||
all: pify(rawInstance.all.bind(rawInstance)),
|
all: pify(rawInstance.all.bind(rawInstance)),
|
||||||
|
on: rawInstance.on.bind(rawInstance),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -928,51 +931,51 @@ async function updateToSchemaVersion12(
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await instance.run(`CREATE TABLE sticker_packs(
|
await instance.run(`CREATE TABLE sticker_packs(
|
||||||
id TEXT PRIMARY KEY,
|
id TEXT PRIMARY KEY,
|
||||||
key TEXT NOT NULL,
|
key TEXT NOT NULL,
|
||||||
|
|
||||||
author STRING,
|
author STRING,
|
||||||
coverStickerId INTEGER,
|
coverStickerId INTEGER,
|
||||||
createdAt INTEGER,
|
createdAt INTEGER,
|
||||||
downloadAttempts INTEGER,
|
downloadAttempts INTEGER,
|
||||||
installedAt INTEGER,
|
installedAt INTEGER,
|
||||||
lastUsed INTEGER,
|
lastUsed INTEGER,
|
||||||
status STRING,
|
status STRING,
|
||||||
stickerCount INTEGER,
|
stickerCount INTEGER,
|
||||||
title STRING
|
title STRING
|
||||||
);`);
|
);`);
|
||||||
|
|
||||||
await instance.run(`CREATE TABLE stickers(
|
await instance.run(`CREATE TABLE stickers(
|
||||||
id INTEGER NOT NULL,
|
id INTEGER NOT NULL,
|
||||||
packId TEXT NOT NULL,
|
packId TEXT NOT NULL,
|
||||||
|
|
||||||
emoji STRING,
|
emoji STRING,
|
||||||
height INTEGER,
|
height INTEGER,
|
||||||
isCoverOnly INTEGER,
|
isCoverOnly INTEGER,
|
||||||
lastUsed INTEGER,
|
lastUsed INTEGER,
|
||||||
path STRING,
|
path STRING,
|
||||||
width INTEGER,
|
width INTEGER,
|
||||||
|
|
||||||
PRIMARY KEY (id, packId),
|
PRIMARY KEY (id, packId),
|
||||||
CONSTRAINT stickers_fk
|
CONSTRAINT stickers_fk
|
||||||
FOREIGN KEY (packId)
|
FOREIGN KEY (packId)
|
||||||
REFERENCES sticker_packs(id)
|
REFERENCES sticker_packs(id)
|
||||||
ON DELETE CASCADE
|
ON DELETE CASCADE
|
||||||
);`);
|
);`);
|
||||||
|
|
||||||
await instance.run(`CREATE INDEX stickers_recents
|
await instance.run(`CREATE INDEX stickers_recents
|
||||||
ON stickers (
|
ON stickers (
|
||||||
lastUsed
|
lastUsed
|
||||||
) WHERE lastUsed IS NOT NULL;`);
|
) WHERE lastUsed IS NOT NULL;`);
|
||||||
|
|
||||||
await instance.run(`CREATE TABLE sticker_references(
|
await instance.run(`CREATE TABLE sticker_references(
|
||||||
messageId STRING,
|
messageId STRING,
|
||||||
packId TEXT,
|
packId TEXT,
|
||||||
CONSTRAINT sticker_references_fk
|
CONSTRAINT sticker_references_fk
|
||||||
FOREIGN KEY(packId)
|
FOREIGN KEY(packId)
|
||||||
REFERENCES sticker_packs(id)
|
REFERENCES sticker_packs(id)
|
||||||
ON DELETE CASCADE
|
ON DELETE CASCADE
|
||||||
);`);
|
);`);
|
||||||
|
|
||||||
await instance.run('PRAGMA user_version = 12;');
|
await instance.run('PRAGMA user_version = 12;');
|
||||||
await instance.run('COMMIT TRANSACTION;');
|
await instance.run('COMMIT TRANSACTION;');
|
||||||
|
@ -1685,24 +1688,26 @@ async function initialize({
|
||||||
try {
|
try {
|
||||||
promisified = await openAndSetUpSQLCipher(databaseFilePath, { key });
|
promisified = await openAndSetUpSQLCipher(databaseFilePath, { key });
|
||||||
|
|
||||||
// promisified.on('trace', async statement => {
|
// if (promisified) {
|
||||||
// if (
|
// promisified.on('trace', async statement => {
|
||||||
// !globalInstance ||
|
// if (
|
||||||
// statement.startsWith('--') ||
|
// !globalInstance ||
|
||||||
// statement.includes('COMMIT') ||
|
// statement.startsWith('--') ||
|
||||||
// statement.includes('BEGIN') ||
|
// statement.includes('COMMIT') ||
|
||||||
// statement.includes('ROLLBACK')
|
// statement.includes('BEGIN') ||
|
||||||
// ) {
|
// statement.includes('ROLLBACK')
|
||||||
// return;
|
// ) {
|
||||||
// }
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
// // Note that this causes problems when attempting to commit transactions - this
|
// // Note that this causes problems when attempting to commit transactions - this
|
||||||
// // statement is running, and we get at SQLITE_BUSY error. So we delay.
|
// // statement is running, and we get at SQLITE_BUSY error. So we delay.
|
||||||
// await new Promise(resolve => setTimeout(resolve, 1000));
|
// await new Promise(resolve => setTimeout(resolve, 1000));
|
||||||
|
|
||||||
// const data = await db.get(`EXPLAIN QUERY PLAN ${statement}`);
|
// const data = await promisified.get(`EXPLAIN QUERY PLAN ${statement}`);
|
||||||
// console._log(`EXPLAIN QUERY PLAN ${statement}\n`, data && data.detail);
|
// console._log(`EXPLAIN QUERY PLAN ${statement}\n`, data && data.detail);
|
||||||
// });
|
// });
|
||||||
|
// }
|
||||||
|
|
||||||
await updateSchema(promisified);
|
await updateSchema(promisified);
|
||||||
|
|
||||||
|
@ -2583,22 +2588,16 @@ async function saveMessages(
|
||||||
}
|
}
|
||||||
saveMessages.needsSerial = true;
|
saveMessages.needsSerial = true;
|
||||||
|
|
||||||
async function removeMessage(id: Array<string> | string) {
|
async function removeMessage(id: string) {
|
||||||
const db = getInstance();
|
const db = getInstance();
|
||||||
if (!Array.isArray(id)) {
|
await db.run('DELETE FROM messages WHERE id = $id;', { $id: id });
|
||||||
await db.run('DELETE FROM messages WHERE id = $id;', { $id: id });
|
}
|
||||||
|
|
||||||
return;
|
async function removeMessages(ids: Array<string>) {
|
||||||
}
|
const db = getInstance();
|
||||||
|
|
||||||
if (!id.length) {
|
|
||||||
throw new Error('removeMessages: No ids to delete!');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Our node interface doesn't seem to allow you to replace one single ? with an array
|
|
||||||
await db.run(
|
await db.run(
|
||||||
`DELETE FROM messages WHERE id IN ( ${id.map(() => '?').join(', ')} );`,
|
`DELETE FROM messages WHERE id IN ( ${ids.map(() => '?').join(', ')} );`,
|
||||||
id
|
ids
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2873,16 +2873,14 @@ Whisper.ConversationView = Whisper.View.extend({
|
||||||
async destroyMessages() {
|
async destroyMessages() {
|
||||||
try {
|
try {
|
||||||
await this.confirm(window.i18n('deleteConversationConfirmation'));
|
await this.confirm(window.i18n('deleteConversationConfirmation'));
|
||||||
try {
|
this.longRunningTaskWrapper({
|
||||||
this.model.trigger('unload', 'delete messages');
|
name: 'destroymessages',
|
||||||
await this.model.destroyMessages();
|
task: async () => {
|
||||||
this.model.updateLastMessage();
|
this.model.trigger('unload', 'delete messages');
|
||||||
} catch (error) {
|
await this.model.destroyMessages();
|
||||||
window.log.error(
|
this.model.updateLastMessage();
|
||||||
'destroyMessages: Failed to successfully delete conversation',
|
},
|
||||||
error && error.stack ? error.stack : error
|
});
|
||||||
);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// nothing to see here, user canceled out of dialog
|
// nothing to see here, user canceled out of dialog
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue