Changes to View Once

This commit is contained in:
Scott Nonnenberg 2019-08-05 13:53:15 -07:00
parent 9d88abdb90
commit d42eb2126e
14 changed files with 152 additions and 167 deletions

View file

@ -1698,13 +1698,12 @@
}
async function onViewSync(ev) {
const { viewedAt, source, timestamp } = ev;
window.log.info(`view sync ${source} ${timestamp}, viewed at ${viewedAt}`);
const { source, timestamp } = ev;
window.log.info(`view sync ${source} ${timestamp}`);
const sync = Whisper.ViewSyncs.add({
source,
timestamp,
viewedAt,
});
sync.on('remove', ev.confirm);

View file

@ -52,22 +52,12 @@
const toAgeOut = await window.Signal.Data.getNextTapToViewMessageToAgeOut({
Message: Whisper.Message,
});
const toExpire = await window.Signal.Data.getNextTapToViewMessageToExpire({
Message: Whisper.Message,
});
if (!toAgeOut && !toExpire) {
if (!toAgeOut) {
return;
}
const ageOutAt = toAgeOut
? toAgeOut.get('received_at') + THIRTY_DAYS
: Number.MAX_VALUE;
const expireAt = toExpire
? toExpire.get('messageTimerExpiresAt')
: Number.MAX_VALUE;
const nextCheck = Math.min(ageOutAt, expireAt);
const nextCheck = toAgeOut.get('received_at') + THIRTY_DAYS;
Whisper.TapToViewMessagesListener.nextCheck = nextCheck;
window.log.info(

View file

@ -495,8 +495,7 @@
expirationTimestamp,
isTapToView,
isTapToViewExpired:
isTapToView && (this.get('isErased') || this.isTapToViewExpired()),
isTapToViewExpired: isTapToView && this.get('isErased'),
isTapToViewError:
isTapToView && this.isIncoming() && this.get('isTapToViewInvalid'),
@ -870,7 +869,7 @@
}
},
isTapToView() {
return Boolean(this.get('messageTimer'));
return Boolean(this.get('isViewOnce') || this.get('messageTimer'));
},
isValidTapToView() {
const body = this.get('body');
@ -908,66 +907,27 @@
return true;
},
isTapToViewExpired() {
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) {
async markViewed(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')) {
await this.markRead();
}
const messageTimer = this.get('messageTimer');
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,
});
await this.eraseContents();
if (!fromSync) {
const sender = this.getSource();
@ -979,14 +939,13 @@
);
await wrap(
textsecure.messaging.syncMessageTimerRead(
sender,
timestamp,
sendOptions
)
textsecure.messaging.syncViewOnceOpen(sender, timestamp, sendOptions)
);
}
},
isErased() {
return Boolean(this.get('isErased'));
},
async eraseContents() {
if (this.get('isErased')) {
return;
@ -1940,7 +1899,7 @@
hasAttachments: dataMessage.hasAttachments,
hasFileAttachments: dataMessage.hasFileAttachments,
hasVisualMediaAttachments: dataMessage.hasVisualMediaAttachments,
messageTimer: dataMessage.messageTimer,
isViewOnce: Boolean(dataMessage.isViewOnce),
preview,
requiredProtocolVersion:
dataMessage.requiredProtocolVersion ||

View file

@ -715,7 +715,7 @@ async function exportConversation(conversation, options = {}) {
count += 1;
// 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
continue;
}

View file

@ -122,7 +122,6 @@ module.exports = {
getOutgoingWithoutExpiresAt,
getNextExpiringMessage,
getMessagesByConversation,
getNextTapToViewMessageToExpire,
getNextTapToViewMessageToAgeOut,
getTapToViewMessagesNeedingErase,
@ -842,14 +841,6 @@ async function getNextExpiringMessage({ MessageCollection }) {
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 }) {
const message = await channels.getNextTapToViewMessageToAgeOut();
if (!message) {

View file

@ -169,10 +169,14 @@ function initializeMigrations({
const writeNewTempData = createWriterForNew(tempPath);
const deleteTempFile = Attachments.createDeleter(tempPath);
const readTempData = createReader(tempPath);
const copyIntoTempDirectory = Attachments.copyIntoAttachmentsDirectory(
tempPath
);
return {
attachmentsPath,
copyIntoAttachmentsDirectory,
copyIntoTempDirectory,
deleteAttachmentData: deleteOnDisk,
deleteExternalMessageFiles: MessageType.deleteAllExternalFiles({
deleteAttachmentData: Type.deleteData(deleteOnDisk),
@ -182,6 +186,7 @@ function initializeMigrations({
deleteTempFile,
getAbsoluteAttachmentPath,
getAbsoluteStickerPath,
getAbsoluteTempPath,
getPlaceholderMigrations,
getCurrentVersion,
loadAttachmentData,

View file

@ -51,9 +51,7 @@
}
const message = MessageController.register(found.id, found);
const viewedAt = sync.get('viewedAt');
await message.startTapToViewTimer(viewedAt, { fromSync: true });
await message.markViewed({ fromSync: true });
this.remove(sync);
} catch (error) {

View file

@ -19,6 +19,9 @@
const {
upgradeMessageSchema,
getAbsoluteAttachmentPath,
copyIntoTempDirectory,
getAbsoluteTempPath,
deleteTempFile,
} = window.Signal.Migrations;
Whisper.ExpiredToast = Whisper.ToastView.extend({
@ -1324,17 +1327,33 @@
if (!message.isTapToView()) {
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()) {
return;
if (message.isErased()) {
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) {
return;
}
@ -1345,6 +1364,8 @@
this.stopListening(message);
Signal.Backbone.Views.Lightbox.hide();
lightboxView.remove();
await deleteTempFile(tempPath);
};
this.listenTo(message, 'expired', closeLightbox);
this.listenTo(message, 'change', () => {
@ -1354,14 +1375,11 @@
});
const getProps = () => {
const firstAttachment = message.get('attachments')[0];
const { path, contentType } = firstAttachment;
const { path, contentType } = tempAttachment;
return {
objectURL: getAbsoluteAttachmentPath(path),
objectURL: getAbsoluteTempPath(path),
contentType,
timerExpiresAt: message.get('messageTimerExpiresAt'),
timerDuration: message.get('messageTimer') * 1000,
};
};
this.lightboxView = new Whisper.ReactWrapperView({