Changes to View Once
This commit is contained in:
parent
9d88abdb90
commit
d42eb2126e
14 changed files with 152 additions and 167 deletions
116
app/sql.js
116
app/sql.js
|
@ -94,7 +94,6 @@ module.exports = {
|
||||||
getOutgoingWithoutExpiresAt,
|
getOutgoingWithoutExpiresAt,
|
||||||
getNextExpiringMessage,
|
getNextExpiringMessage,
|
||||||
getMessagesByConversation,
|
getMessagesByConversation,
|
||||||
getNextTapToViewMessageToExpire,
|
|
||||||
getNextTapToViewMessageToAgeOut,
|
getNextTapToViewMessageToAgeOut,
|
||||||
getTapToViewMessagesNeedingErase,
|
getTapToViewMessagesNeedingErase,
|
||||||
|
|
||||||
|
@ -952,6 +951,68 @@ async function updateToSchemaVersion16(currentVersion, instance) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function updateToSchemaVersion17(currentVersion, instance) {
|
||||||
|
if (currentVersion >= 17) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('updateToSchemaVersion17: starting...');
|
||||||
|
await instance.run('BEGIN TRANSACTION;');
|
||||||
|
|
||||||
|
try {
|
||||||
|
await instance.run(
|
||||||
|
`ALTER TABLE messages
|
||||||
|
ADD COLUMN isViewOnce INTEGER;`
|
||||||
|
);
|
||||||
|
|
||||||
|
await instance.run('DROP INDEX messages_message_timer;');
|
||||||
|
|
||||||
|
await instance.run(`CREATE INDEX messages_view_once ON messages (
|
||||||
|
isErased
|
||||||
|
) WHERE isViewOnce = 1;`);
|
||||||
|
|
||||||
|
// Updating full-text triggers to avoid anything with isViewOnce = 1
|
||||||
|
|
||||||
|
await instance.run('DROP TRIGGER messages_on_insert;');
|
||||||
|
await instance.run('DROP TRIGGER messages_on_update;');
|
||||||
|
|
||||||
|
await instance.run(`
|
||||||
|
CREATE TRIGGER messages_on_insert AFTER INSERT ON messages
|
||||||
|
WHEN new.isViewOnce != 1
|
||||||
|
BEGIN
|
||||||
|
INSERT INTO messages_fts (
|
||||||
|
id,
|
||||||
|
body
|
||||||
|
) VALUES (
|
||||||
|
new.id,
|
||||||
|
new.body
|
||||||
|
);
|
||||||
|
END;
|
||||||
|
`);
|
||||||
|
await instance.run(`
|
||||||
|
CREATE TRIGGER messages_on_update AFTER UPDATE ON messages
|
||||||
|
WHEN new.isViewOnce != 1
|
||||||
|
BEGIN
|
||||||
|
DELETE FROM messages_fts WHERE id = old.id;
|
||||||
|
INSERT INTO messages_fts(
|
||||||
|
id,
|
||||||
|
body
|
||||||
|
) VALUES (
|
||||||
|
new.id,
|
||||||
|
new.body
|
||||||
|
);
|
||||||
|
END;
|
||||||
|
`);
|
||||||
|
|
||||||
|
await instance.run('PRAGMA schema_version = 17;');
|
||||||
|
await instance.run('COMMIT TRANSACTION;');
|
||||||
|
console.log('updateToSchemaVersion17: success!');
|
||||||
|
} catch (error) {
|
||||||
|
await instance.run('ROLLBACK;');
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const SCHEMA_VERSIONS = [
|
const SCHEMA_VERSIONS = [
|
||||||
updateToSchemaVersion1,
|
updateToSchemaVersion1,
|
||||||
updateToSchemaVersion2,
|
updateToSchemaVersion2,
|
||||||
|
@ -969,6 +1030,7 @@ const SCHEMA_VERSIONS = [
|
||||||
updateToSchemaVersion14,
|
updateToSchemaVersion14,
|
||||||
updateToSchemaVersion15,
|
updateToSchemaVersion15,
|
||||||
updateToSchemaVersion16,
|
updateToSchemaVersion16,
|
||||||
|
updateToSchemaVersion17,
|
||||||
];
|
];
|
||||||
|
|
||||||
async function updateSchema(instance) {
|
async function updateSchema(instance) {
|
||||||
|
@ -1566,9 +1628,7 @@ async function saveMessage(data, { forceSave } = {}) {
|
||||||
hasVisualMediaAttachments,
|
hasVisualMediaAttachments,
|
||||||
id,
|
id,
|
||||||
isErased,
|
isErased,
|
||||||
messageTimer,
|
isViewOnce,
|
||||||
messageTimerStart,
|
|
||||||
messageTimerExpiresAt,
|
|
||||||
// eslint-disable-next-line camelcase
|
// eslint-disable-next-line camelcase
|
||||||
received_at,
|
received_at,
|
||||||
schemaVersion,
|
schemaVersion,
|
||||||
|
@ -1595,9 +1655,7 @@ async function saveMessage(data, { forceSave } = {}) {
|
||||||
$hasFileAttachments: hasFileAttachments,
|
$hasFileAttachments: hasFileAttachments,
|
||||||
$hasVisualMediaAttachments: hasVisualMediaAttachments,
|
$hasVisualMediaAttachments: hasVisualMediaAttachments,
|
||||||
$isErased: isErased,
|
$isErased: isErased,
|
||||||
$messageTimer: messageTimer,
|
$isViewOnce: isViewOnce,
|
||||||
$messageTimerStart: messageTimerStart,
|
|
||||||
$messageTimerExpiresAt: messageTimerExpiresAt,
|
|
||||||
$received_at: received_at,
|
$received_at: received_at,
|
||||||
$schemaVersion: schemaVersion,
|
$schemaVersion: schemaVersion,
|
||||||
$sent_at: sent_at,
|
$sent_at: sent_at,
|
||||||
|
@ -1622,9 +1680,7 @@ async function saveMessage(data, { forceSave } = {}) {
|
||||||
hasFileAttachments = $hasFileAttachments,
|
hasFileAttachments = $hasFileAttachments,
|
||||||
hasVisualMediaAttachments = $hasVisualMediaAttachments,
|
hasVisualMediaAttachments = $hasVisualMediaAttachments,
|
||||||
isErased = $isErased,
|
isErased = $isErased,
|
||||||
messageTimer = $messageTimer,
|
isViewOnce = $isViewOnce,
|
||||||
messageTimerStart = $messageTimerStart,
|
|
||||||
messageTimerExpiresAt = $messageTimerExpiresAt,
|
|
||||||
received_at = $received_at,
|
received_at = $received_at,
|
||||||
schemaVersion = $schemaVersion,
|
schemaVersion = $schemaVersion,
|
||||||
sent_at = $sent_at,
|
sent_at = $sent_at,
|
||||||
|
@ -1658,9 +1714,7 @@ async function saveMessage(data, { forceSave } = {}) {
|
||||||
hasFileAttachments,
|
hasFileAttachments,
|
||||||
hasVisualMediaAttachments,
|
hasVisualMediaAttachments,
|
||||||
isErased,
|
isErased,
|
||||||
messageTimer,
|
isViewOnce,
|
||||||
messageTimerStart,
|
|
||||||
messageTimerExpiresAt,
|
|
||||||
received_at,
|
received_at,
|
||||||
schemaVersion,
|
schemaVersion,
|
||||||
sent_at,
|
sent_at,
|
||||||
|
@ -1681,9 +1735,7 @@ async function saveMessage(data, { forceSave } = {}) {
|
||||||
$hasFileAttachments,
|
$hasFileAttachments,
|
||||||
$hasVisualMediaAttachments,
|
$hasVisualMediaAttachments,
|
||||||
$isErased,
|
$isErased,
|
||||||
$messageTimer,
|
$isViewOnce,
|
||||||
$messageTimerStart,
|
|
||||||
$messageTimerExpiresAt,
|
|
||||||
$received_at,
|
$received_at,
|
||||||
$schemaVersion,
|
$schemaVersion,
|
||||||
$sent_at,
|
$sent_at,
|
||||||
|
@ -1862,31 +1914,11 @@ async function getNextExpiringMessage() {
|
||||||
return map(rows, row => jsonToObject(row.json));
|
return map(rows, row => jsonToObject(row.json));
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getNextTapToViewMessageToExpire() {
|
|
||||||
// Note: we avoid 'IS NOT NULL' here because it does seem to bypass our index
|
|
||||||
const rows = await db.all(`
|
|
||||||
SELECT json FROM messages
|
|
||||||
WHERE
|
|
||||||
messageTimer > 0
|
|
||||||
AND messageTimerExpiresAt > 0
|
|
||||||
AND (isErased IS NULL OR isErased != 1)
|
|
||||||
ORDER BY messageTimerExpiresAt ASC
|
|
||||||
LIMIT 1;
|
|
||||||
`);
|
|
||||||
|
|
||||||
if (!rows || rows.length < 1) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return jsonToObject(rows[0].json);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getNextTapToViewMessageToAgeOut() {
|
async function getNextTapToViewMessageToAgeOut() {
|
||||||
// Note: we avoid 'IS NOT NULL' here because it does seem to bypass our index
|
|
||||||
const rows = await db.all(`
|
const rows = await db.all(`
|
||||||
SELECT json FROM messages
|
SELECT json FROM messages
|
||||||
WHERE
|
WHERE
|
||||||
messageTimer > 0
|
isViewOnce = 1
|
||||||
AND (isErased IS NULL OR isErased != 1)
|
AND (isErased IS NULL OR isErased != 1)
|
||||||
ORDER BY received_at ASC
|
ORDER BY received_at ASC
|
||||||
LIMIT 1;
|
LIMIT 1;
|
||||||
|
@ -1903,18 +1935,12 @@ async function getTapToViewMessagesNeedingErase() {
|
||||||
const THIRTY_DAYS_AGO = Date.now() - 30 * 24 * 60 * 60 * 1000;
|
const THIRTY_DAYS_AGO = Date.now() - 30 * 24 * 60 * 60 * 1000;
|
||||||
const NOW = Date.now();
|
const NOW = Date.now();
|
||||||
|
|
||||||
// Note: we avoid 'IS NOT NULL' here because it does seem to bypass our index
|
|
||||||
const rows = await db.all(
|
const rows = await db.all(
|
||||||
`SELECT json FROM messages
|
`SELECT json FROM messages
|
||||||
WHERE
|
WHERE
|
||||||
messageTimer > 0
|
isViewOnce = 1
|
||||||
AND (isErased IS NULL OR isErased != 1)
|
AND (isErased IS NULL OR isErased != 1)
|
||||||
AND (
|
AND received_at <= $THIRTY_DAYS_AGO
|
||||||
(messageTimerExpiresAt > 0
|
|
||||||
AND messageTimerExpiresAt <= $NOW)
|
|
||||||
OR
|
|
||||||
received_at <= $THIRTY_DAYS_AGO
|
|
||||||
)
|
|
||||||
ORDER BY received_at ASC;`,
|
ORDER BY received_at ASC;`,
|
||||||
{
|
{
|
||||||
$NOW: NOW,
|
$NOW: NOW,
|
||||||
|
|
|
@ -1698,13 +1698,12 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
async function onViewSync(ev) {
|
async function onViewSync(ev) {
|
||||||
const { viewedAt, source, timestamp } = ev;
|
const { source, timestamp } = ev;
|
||||||
window.log.info(`view sync ${source} ${timestamp}, viewed at ${viewedAt}`);
|
window.log.info(`view sync ${source} ${timestamp}`);
|
||||||
|
|
||||||
const sync = Whisper.ViewSyncs.add({
|
const sync = Whisper.ViewSyncs.add({
|
||||||
source,
|
source,
|
||||||
timestamp,
|
timestamp,
|
||||||
viewedAt,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
sync.on('remove', ev.confirm);
|
sync.on('remove', ev.confirm);
|
||||||
|
|
|
@ -52,22 +52,12 @@
|
||||||
const toAgeOut = await window.Signal.Data.getNextTapToViewMessageToAgeOut({
|
const toAgeOut = await window.Signal.Data.getNextTapToViewMessageToAgeOut({
|
||||||
Message: Whisper.Message,
|
Message: Whisper.Message,
|
||||||
});
|
});
|
||||||
const toExpire = await window.Signal.Data.getNextTapToViewMessageToExpire({
|
|
||||||
Message: Whisper.Message,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!toAgeOut && !toExpire) {
|
if (!toAgeOut) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ageOutAt = toAgeOut
|
const nextCheck = toAgeOut.get('received_at') + THIRTY_DAYS;
|
||||||
? toAgeOut.get('received_at') + THIRTY_DAYS
|
|
||||||
: Number.MAX_VALUE;
|
|
||||||
const expireAt = toExpire
|
|
||||||
? toExpire.get('messageTimerExpiresAt')
|
|
||||||
: Number.MAX_VALUE;
|
|
||||||
|
|
||||||
const nextCheck = Math.min(ageOutAt, expireAt);
|
|
||||||
|
|
||||||
Whisper.TapToViewMessagesListener.nextCheck = nextCheck;
|
Whisper.TapToViewMessagesListener.nextCheck = nextCheck;
|
||||||
window.log.info(
|
window.log.info(
|
||||||
|
|
|
@ -495,8 +495,7 @@
|
||||||
expirationTimestamp,
|
expirationTimestamp,
|
||||||
|
|
||||||
isTapToView,
|
isTapToView,
|
||||||
isTapToViewExpired:
|
isTapToViewExpired: isTapToView && this.get('isErased'),
|
||||||
isTapToView && (this.get('isErased') || this.isTapToViewExpired()),
|
|
||||||
isTapToViewError:
|
isTapToViewError:
|
||||||
isTapToView && this.isIncoming() && this.get('isTapToViewInvalid'),
|
isTapToView && this.isIncoming() && this.get('isTapToViewInvalid'),
|
||||||
|
|
||||||
|
@ -870,7 +869,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
isTapToView() {
|
isTapToView() {
|
||||||
return Boolean(this.get('messageTimer'));
|
return Boolean(this.get('isViewOnce') || this.get('messageTimer'));
|
||||||
},
|
},
|
||||||
isValidTapToView() {
|
isValidTapToView() {
|
||||||
const body = this.get('body');
|
const body = this.get('body');
|
||||||
|
@ -908,66 +907,27 @@
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
isTapToViewExpired() {
|
async markViewed(options) {
|
||||||
const THIRTY_DAYS = 30 * 24 * 60 * 60 * 1000;
|
|
||||||
const now = Date.now();
|
|
||||||
|
|
||||||
const receivedAt = this.get('received_at');
|
|
||||||
if (now >= receivedAt + THIRTY_DAYS) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
const messageTimer = this.get('messageTimer');
|
|
||||||
const messageTimerStart = this.get('messageTimerStart');
|
|
||||||
if (!messageTimerStart) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const expiresAt = messageTimerStart + messageTimer * 1000;
|
|
||||||
if (now >= expiresAt) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
async startTapToViewTimer(viewedAt, options) {
|
|
||||||
const { fromSync } = options || {};
|
const { fromSync } = options || {};
|
||||||
|
|
||||||
|
if (!this.isValidTapToView()) {
|
||||||
|
window.log.warn(
|
||||||
|
`markViewed: Message ${this.idForLogging()} is not a valid tap to view message!`
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this.isErased()) {
|
||||||
|
window.log.warn(
|
||||||
|
`markViewed: Message ${this.idForLogging()} is already erased!`
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (this.get('unread')) {
|
if (this.get('unread')) {
|
||||||
await this.markRead();
|
await this.markRead();
|
||||||
}
|
}
|
||||||
|
|
||||||
const messageTimer = this.get('messageTimer');
|
await this.eraseContents();
|
||||||
if (!messageTimer) {
|
|
||||||
window.log.warn(
|
|
||||||
`startTapToViewTimer: Message ${this.idForLogging()} has no messageTimer!`
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const existingTimerStart = this.get('messageTimerStart');
|
|
||||||
const messageTimerStart = Math.min(
|
|
||||||
Date.now(),
|
|
||||||
viewedAt || Date.now(),
|
|
||||||
existingTimerStart || Date.now()
|
|
||||||
);
|
|
||||||
const messageTimerExpiresAt = messageTimerStart + messageTimer * 1000;
|
|
||||||
|
|
||||||
// Because we're not using Backbone-integrated saves, we need to manually
|
|
||||||
// clear the changed fields here so our hasChanged() check below is useful.
|
|
||||||
this.changed = {};
|
|
||||||
this.set({
|
|
||||||
messageTimerStart,
|
|
||||||
messageTimerExpiresAt,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!this.hasChanged()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await window.Signal.Data.saveMessage(this.attributes, {
|
|
||||||
Message: Whisper.Message,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!fromSync) {
|
if (!fromSync) {
|
||||||
const sender = this.getSource();
|
const sender = this.getSource();
|
||||||
|
@ -979,14 +939,13 @@
|
||||||
);
|
);
|
||||||
|
|
||||||
await wrap(
|
await wrap(
|
||||||
textsecure.messaging.syncMessageTimerRead(
|
textsecure.messaging.syncViewOnceOpen(sender, timestamp, sendOptions)
|
||||||
sender,
|
|
||||||
timestamp,
|
|
||||||
sendOptions
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
isErased() {
|
||||||
|
return Boolean(this.get('isErased'));
|
||||||
|
},
|
||||||
async eraseContents() {
|
async eraseContents() {
|
||||||
if (this.get('isErased')) {
|
if (this.get('isErased')) {
|
||||||
return;
|
return;
|
||||||
|
@ -1940,7 +1899,7 @@
|
||||||
hasAttachments: dataMessage.hasAttachments,
|
hasAttachments: dataMessage.hasAttachments,
|
||||||
hasFileAttachments: dataMessage.hasFileAttachments,
|
hasFileAttachments: dataMessage.hasFileAttachments,
|
||||||
hasVisualMediaAttachments: dataMessage.hasVisualMediaAttachments,
|
hasVisualMediaAttachments: dataMessage.hasVisualMediaAttachments,
|
||||||
messageTimer: dataMessage.messageTimer,
|
isViewOnce: Boolean(dataMessage.isViewOnce),
|
||||||
preview,
|
preview,
|
||||||
requiredProtocolVersion:
|
requiredProtocolVersion:
|
||||||
dataMessage.requiredProtocolVersion ||
|
dataMessage.requiredProtocolVersion ||
|
||||||
|
|
|
@ -715,7 +715,7 @@ async function exportConversation(conversation, options = {}) {
|
||||||
count += 1;
|
count += 1;
|
||||||
|
|
||||||
// skip message if it is disappearing, no matter the amount of time left
|
// skip message if it is disappearing, no matter the amount of time left
|
||||||
if (message.expireTimer || message.messageTimer) {
|
if (message.expireTimer || message.messageTimer || message.isViewOnce) {
|
||||||
// eslint-disable-next-line no-continue
|
// eslint-disable-next-line no-continue
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
|
@ -122,7 +122,6 @@ module.exports = {
|
||||||
getOutgoingWithoutExpiresAt,
|
getOutgoingWithoutExpiresAt,
|
||||||
getNextExpiringMessage,
|
getNextExpiringMessage,
|
||||||
getMessagesByConversation,
|
getMessagesByConversation,
|
||||||
getNextTapToViewMessageToExpire,
|
|
||||||
getNextTapToViewMessageToAgeOut,
|
getNextTapToViewMessageToAgeOut,
|
||||||
getTapToViewMessagesNeedingErase,
|
getTapToViewMessagesNeedingErase,
|
||||||
|
|
||||||
|
@ -842,14 +841,6 @@ async function getNextExpiringMessage({ MessageCollection }) {
|
||||||
return new MessageCollection(messages);
|
return new MessageCollection(messages);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getNextTapToViewMessageToExpire({ Message }) {
|
|
||||||
const message = await channels.getNextTapToViewMessageToExpire();
|
|
||||||
if (!message) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Message(message);
|
|
||||||
}
|
|
||||||
async function getNextTapToViewMessageToAgeOut({ Message }) {
|
async function getNextTapToViewMessageToAgeOut({ Message }) {
|
||||||
const message = await channels.getNextTapToViewMessageToAgeOut();
|
const message = await channels.getNextTapToViewMessageToAgeOut();
|
||||||
if (!message) {
|
if (!message) {
|
||||||
|
|
|
@ -169,10 +169,14 @@ function initializeMigrations({
|
||||||
const writeNewTempData = createWriterForNew(tempPath);
|
const writeNewTempData = createWriterForNew(tempPath);
|
||||||
const deleteTempFile = Attachments.createDeleter(tempPath);
|
const deleteTempFile = Attachments.createDeleter(tempPath);
|
||||||
const readTempData = createReader(tempPath);
|
const readTempData = createReader(tempPath);
|
||||||
|
const copyIntoTempDirectory = Attachments.copyIntoAttachmentsDirectory(
|
||||||
|
tempPath
|
||||||
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
attachmentsPath,
|
attachmentsPath,
|
||||||
copyIntoAttachmentsDirectory,
|
copyIntoAttachmentsDirectory,
|
||||||
|
copyIntoTempDirectory,
|
||||||
deleteAttachmentData: deleteOnDisk,
|
deleteAttachmentData: deleteOnDisk,
|
||||||
deleteExternalMessageFiles: MessageType.deleteAllExternalFiles({
|
deleteExternalMessageFiles: MessageType.deleteAllExternalFiles({
|
||||||
deleteAttachmentData: Type.deleteData(deleteOnDisk),
|
deleteAttachmentData: Type.deleteData(deleteOnDisk),
|
||||||
|
@ -182,6 +186,7 @@ function initializeMigrations({
|
||||||
deleteTempFile,
|
deleteTempFile,
|
||||||
getAbsoluteAttachmentPath,
|
getAbsoluteAttachmentPath,
|
||||||
getAbsoluteStickerPath,
|
getAbsoluteStickerPath,
|
||||||
|
getAbsoluteTempPath,
|
||||||
getPlaceholderMigrations,
|
getPlaceholderMigrations,
|
||||||
getCurrentVersion,
|
getCurrentVersion,
|
||||||
loadAttachmentData,
|
loadAttachmentData,
|
||||||
|
|
|
@ -51,9 +51,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
const message = MessageController.register(found.id, found);
|
const message = MessageController.register(found.id, found);
|
||||||
|
await message.markViewed({ fromSync: true });
|
||||||
const viewedAt = sync.get('viewedAt');
|
|
||||||
await message.startTapToViewTimer(viewedAt, { fromSync: true });
|
|
||||||
|
|
||||||
this.remove(sync);
|
this.remove(sync);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
|
@ -19,6 +19,9 @@
|
||||||
const {
|
const {
|
||||||
upgradeMessageSchema,
|
upgradeMessageSchema,
|
||||||
getAbsoluteAttachmentPath,
|
getAbsoluteAttachmentPath,
|
||||||
|
copyIntoTempDirectory,
|
||||||
|
getAbsoluteTempPath,
|
||||||
|
deleteTempFile,
|
||||||
} = window.Signal.Migrations;
|
} = window.Signal.Migrations;
|
||||||
|
|
||||||
Whisper.ExpiredToast = Whisper.ToastView.extend({
|
Whisper.ExpiredToast = Whisper.ToastView.extend({
|
||||||
|
@ -1324,17 +1327,33 @@
|
||||||
|
|
||||||
if (!message.isTapToView()) {
|
if (!message.isTapToView()) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`displayTapToViewMessage: Message ${message.idForLogging()} is not tap to view`
|
`displayTapToViewMessage: Message ${message.idForLogging()} is not a tap to view message`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (message.isTapToViewExpired()) {
|
if (message.isErased()) {
|
||||||
return;
|
throw new Error(
|
||||||
|
`displayTapToViewMessage: Message ${message.idForLogging()} is already erased`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
await message.startTapToViewTimer();
|
const firstAttachment = message.get('attachments')[0];
|
||||||
|
if (!firstAttachment || !firstAttachment.path) {
|
||||||
|
throw new Error(
|
||||||
|
`displayTapToViewMessage: Message ${message.idForLogging()} had no first attachment with path`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const closeLightbox = () => {
|
const absolutePath = getAbsoluteAttachmentPath(firstAttachment.path);
|
||||||
|
const tempPath = await copyIntoTempDirectory(absolutePath);
|
||||||
|
const tempAttachment = {
|
||||||
|
...firstAttachment,
|
||||||
|
path: tempPath,
|
||||||
|
};
|
||||||
|
|
||||||
|
await message.markViewed();
|
||||||
|
|
||||||
|
const closeLightbox = async () => {
|
||||||
if (!this.lightboxView) {
|
if (!this.lightboxView) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1345,6 +1364,8 @@
|
||||||
this.stopListening(message);
|
this.stopListening(message);
|
||||||
Signal.Backbone.Views.Lightbox.hide();
|
Signal.Backbone.Views.Lightbox.hide();
|
||||||
lightboxView.remove();
|
lightboxView.remove();
|
||||||
|
|
||||||
|
await deleteTempFile(tempPath);
|
||||||
};
|
};
|
||||||
this.listenTo(message, 'expired', closeLightbox);
|
this.listenTo(message, 'expired', closeLightbox);
|
||||||
this.listenTo(message, 'change', () => {
|
this.listenTo(message, 'change', () => {
|
||||||
|
@ -1354,14 +1375,11 @@
|
||||||
});
|
});
|
||||||
|
|
||||||
const getProps = () => {
|
const getProps = () => {
|
||||||
const firstAttachment = message.get('attachments')[0];
|
const { path, contentType } = tempAttachment;
|
||||||
const { path, contentType } = firstAttachment;
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
objectURL: getAbsoluteAttachmentPath(path),
|
objectURL: getAbsoluteTempPath(path),
|
||||||
contentType,
|
contentType,
|
||||||
timerExpiresAt: message.get('messageTimerExpiresAt'),
|
|
||||||
timerDuration: message.get('messageTimer') * 1000,
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
this.lightboxView = new Whisper.ReactWrapperView({
|
this.lightboxView = new Whisper.ReactWrapperView({
|
||||||
|
|
|
@ -1090,11 +1090,8 @@ MessageReceiver.prototype.extend({
|
||||||
envelope,
|
envelope,
|
||||||
syncMessage.stickerPackOperation
|
syncMessage.stickerPackOperation
|
||||||
);
|
);
|
||||||
} else if (syncMessage.messageTimerRead) {
|
} else if (syncMessage.viewOnceOpen) {
|
||||||
return this.handleMessageTimerRead(
|
return this.handleViewOnceOpen(envelope, syncMessage.viewOnceOpen);
|
||||||
envelope,
|
|
||||||
syncMessage.messageTimerRead
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
throw new Error('Got empty SyncMessage');
|
throw new Error('Got empty SyncMessage');
|
||||||
},
|
},
|
||||||
|
@ -1105,14 +1102,13 @@ MessageReceiver.prototype.extend({
|
||||||
ev.configuration = configuration;
|
ev.configuration = configuration;
|
||||||
return this.dispatchAndWait(ev);
|
return this.dispatchAndWait(ev);
|
||||||
},
|
},
|
||||||
handleMessageTimerRead(envelope, sync) {
|
handleViewOnceOpen(envelope, sync) {
|
||||||
window.log.info('got message timer read sync message');
|
window.log.info('got view once open sync message');
|
||||||
|
|
||||||
const ev = new Event('viewSync');
|
const ev = new Event('viewSync');
|
||||||
ev.confirm = this.removeFromCache.bind(this, envelope);
|
ev.confirm = this.removeFromCache.bind(this, envelope);
|
||||||
ev.source = sync.sender;
|
ev.source = sync.sender;
|
||||||
ev.timestamp = sync.timestamp ? sync.timestamp.toNumber() : null;
|
ev.timestamp = sync.timestamp ? sync.timestamp.toNumber() : null;
|
||||||
ev.viewedAt = envelope.timestamp;
|
|
||||||
|
|
||||||
return this.dispatchAndWait(ev);
|
return this.dispatchAndWait(ev);
|
||||||
},
|
},
|
||||||
|
|
|
@ -745,7 +745,7 @@ MessageSender.prototype = {
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
},
|
},
|
||||||
|
|
||||||
async syncMessageTimerRead(sender, timestamp, options) {
|
async syncViewOnceOpen(sender, timestamp, options) {
|
||||||
const myNumber = textsecure.storage.user.getNumber();
|
const myNumber = textsecure.storage.user.getNumber();
|
||||||
const myDevice = textsecure.storage.user.getDeviceId();
|
const myDevice = textsecure.storage.user.getDeviceId();
|
||||||
if (myDevice === 1 || myDevice === '1') {
|
if (myDevice === 1 || myDevice === '1') {
|
||||||
|
@ -754,10 +754,10 @@ MessageSender.prototype = {
|
||||||
|
|
||||||
const syncMessage = this.createSyncMessage();
|
const syncMessage = this.createSyncMessage();
|
||||||
|
|
||||||
const messageTimerRead = new textsecure.protobuf.SyncMessage.MessageTimerRead();
|
const viewOnceOpen = new textsecure.protobuf.SyncMessage.ViewOnceOpen();
|
||||||
messageTimerRead.sender = sender;
|
viewOnceOpen.sender = sender;
|
||||||
messageTimerRead.timestamp = timestamp;
|
viewOnceOpen.timestamp = timestamp;
|
||||||
syncMessage.messageTimerRead = messageTimerRead;
|
syncMessage.viewOnceOpen = viewOnceOpen;
|
||||||
|
|
||||||
const contentMessage = new textsecure.protobuf.Content();
|
const contentMessage = new textsecure.protobuf.Content();
|
||||||
contentMessage.syncMessage = syncMessage;
|
contentMessage.syncMessage = syncMessage;
|
||||||
|
@ -1260,7 +1260,7 @@ textsecure.MessageSender = function MessageSenderWrapper(username, password) {
|
||||||
this.getSticker = sender.getSticker.bind(sender);
|
this.getSticker = sender.getSticker.bind(sender);
|
||||||
this.getStickerPackManifest = sender.getStickerPackManifest.bind(sender);
|
this.getStickerPackManifest = sender.getStickerPackManifest.bind(sender);
|
||||||
this.sendStickerPackSync = sender.sendStickerPackSync.bind(sender);
|
this.sendStickerPackSync = sender.sendStickerPackSync.bind(sender);
|
||||||
this.syncMessageTimerRead = sender.syncMessageTimerRead.bind(sender);
|
this.syncViewOnceOpen = sender.syncViewOnceOpen.bind(sender);
|
||||||
};
|
};
|
||||||
|
|
||||||
textsecure.MessageSender.prototype = {
|
textsecure.MessageSender.prototype = {
|
||||||
|
|
|
@ -174,7 +174,8 @@ message DataMessage {
|
||||||
|
|
||||||
INITIAL = 0;
|
INITIAL = 0;
|
||||||
MESSAGE_TIMERS = 1;
|
MESSAGE_TIMERS = 1;
|
||||||
CURRENT = 1;
|
VIEW_ONCE = 2;
|
||||||
|
CURRENT = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
optional string body = 1;
|
optional string body = 1;
|
||||||
|
@ -189,7 +190,7 @@ message DataMessage {
|
||||||
repeated Preview preview = 10;
|
repeated Preview preview = 10;
|
||||||
optional Sticker sticker = 11;
|
optional Sticker sticker = 11;
|
||||||
optional uint32 requiredProtocolVersion = 12;
|
optional uint32 requiredProtocolVersion = 12;
|
||||||
optional uint32 messageTimer = 13;
|
optional bool isViewOnce = 14;
|
||||||
}
|
}
|
||||||
|
|
||||||
message NullMessage {
|
message NullMessage {
|
||||||
|
@ -293,7 +294,7 @@ message SyncMessage {
|
||||||
optional Type type = 3;
|
optional Type type = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
message MessageTimerRead {
|
message ViewOnceOpen {
|
||||||
optional string sender = 1;
|
optional string sender = 1;
|
||||||
optional uint64 timestamp = 2;
|
optional uint64 timestamp = 2;
|
||||||
}
|
}
|
||||||
|
@ -308,7 +309,7 @@ message SyncMessage {
|
||||||
optional Configuration configuration = 9;
|
optional Configuration configuration = 9;
|
||||||
optional bytes padding = 8;
|
optional bytes padding = 8;
|
||||||
repeated StickerPackOperation stickerPackOperation = 10;
|
repeated StickerPackOperation stickerPackOperation = 10;
|
||||||
optional MessageTimerRead messageTimerRead = 11;
|
optional ViewOnceOpen viewOnceOpen = 11;
|
||||||
}
|
}
|
||||||
|
|
||||||
message AttachmentPointer {
|
message AttachmentPointer {
|
||||||
|
|
|
@ -18,7 +18,8 @@ export type IncomingMessage = Readonly<
|
||||||
decrypted_at?: number;
|
decrypted_at?: number;
|
||||||
errors?: Array<any>;
|
errors?: Array<any>;
|
||||||
expireTimer?: number;
|
expireTimer?: number;
|
||||||
messageTimer?: number;
|
messageTimer?: number; // deprecated
|
||||||
|
isViewOnce?: number;
|
||||||
flags?: number;
|
flags?: number;
|
||||||
source?: string;
|
source?: string;
|
||||||
sourceDevice?: number;
|
sourceDevice?: number;
|
||||||
|
@ -47,7 +48,8 @@ export type OutgoingMessage = Readonly<
|
||||||
body?: string;
|
body?: string;
|
||||||
expires_at?: number;
|
expires_at?: number;
|
||||||
expireTimer?: number;
|
expireTimer?: number;
|
||||||
messageTimer?: number;
|
messageTimer?: number; // deprecated
|
||||||
|
isViewOnce?: number;
|
||||||
recipients?: Array<string>; // Array<PhoneNumber>
|
recipients?: Array<string>; // Array<PhoneNumber>
|
||||||
synced: boolean;
|
synced: boolean;
|
||||||
} & SharedMessageProperties &
|
} & SharedMessageProperties &
|
||||||
|
|
|
@ -16,7 +16,7 @@ export const initializeAttachmentMetadata = async (
|
||||||
if (message.type === 'verified-change') {
|
if (message.type === 'verified-change') {
|
||||||
return message;
|
return message;
|
||||||
}
|
}
|
||||||
if (message.messageTimer) {
|
if (message.messageTimer || message.isViewOnce) {
|
||||||
return message;
|
return message;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue