reupload avatar every 10 days

This commit is contained in:
Audric Ackermann 2021-05-25 13:19:34 +10:00
parent 558761ba31
commit 8c726fd745
No known key found for this signature in database
GPG Key ID: 999F434D76324AD4
8 changed files with 112 additions and 41 deletions

View File

@ -10,7 +10,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta http-equiv="Content-Security-Policy" content="default-src 'none';
child-src 'self';
connect-src 'self' https: wss:;
connect-src 'self' https: wss: blob:;
font-src 'self';
form-action 'self';
frame-src 'none';
@ -167,4 +167,4 @@
<script type='text/javascript' src='js/background.js'></script>
</body>
</html>
</html>

View File

@ -426,7 +426,7 @@
avatarPath,
onOk: async (newName, avatar) => {
let newAvatarPath = '';
let url = null;
let fileUrl = null;
let profileKey = null;
if (avatar) {
const data = await readFile({ file: avatar });
@ -464,22 +464,18 @@
profileKey
);
const avatarPointer = await libsession.Utils.AttachmentUtils.uploadAvatarV1({
...dataResized,
data: encryptedData,
size: encryptedData.byteLength,
});
const avatarPointer = await window.Fsv2.uploadFileToFsV2(encryptedData);
({ url } = avatarPointer);
({ fileUrl } = avatarPointer);
storage.put('profileKey', profileKey);
conversation.set('avatarPointer', url);
conversation.set('avatarPointer', fileUrl);
const upgraded = await Signal.Migrations.processNewAttachment({
isRaw: true,
data: data.data,
url,
url: fileUrl,
});
newAvatarPath = upgraded.path;
// Replace our temporary image with the attachment pointer from the server:
@ -514,14 +510,6 @@
// so we could disable this here
// or least it enable for the quickest response
window.lokiPublicChatAPI.setProfileName(newName);
if (avatar) {
window
.getConversationController()
.getConversations()
.filter(convo => convo.isPublic())
.forEach(convo => convo.trigger('ourAvatarChanged', { url, profileKey }));
}
},
});
}

View File

@ -13,4 +13,5 @@ export interface LibTextsecureCryptoInterface {
theirDigest: ArrayBuffer
): Promise<ArrayBuffer>;
decryptProfile(data: ArrayBuffer, key: ArrayBuffer): Promise<ArrayBuffer>;
encryptProfile(data: ArrayBuffer, key: ArrayBuffer): Promise<ArrayBuffer>;
}

View File

@ -398,6 +398,7 @@ window.addEventListener('contextmenu', e => {
});
window.NewReceiver = require('./ts/receiver/receiver');
window.Fsv2 = require('./ts/fileserver/FileServerApiV2');
window.DataMessageReceiver = require('./ts/receiver/dataMessage');
window.NewSnodeAPI = require('./ts/session/snode_api/serviceNodeAPI');
window.SnodePool = require('./ts/session/snode_api/snodePool');

View File

@ -91,7 +91,11 @@ export const Avatar = (props: Props) => {
// contentType is not important
const { urlToLoad } = useEncryptedFileFetch(avatarPath || '', '');
const handleImageError = () => {
window.log.warn('Avatar: Image failed to load; failing over to placeholder', urlToLoad);
window.log.warn(
'Avatar: Image failed to load; failing over to placeholder',
urlToLoad,
avatarPath
);
setImageBroken(true);
};

View File

@ -7,11 +7,14 @@ import { ConversationController } from '../../session/conversations';
import { UserUtils } from '../../session/utils';
import { syncConfigurationIfNeeded } from '../../session/utils/syncUtils';
import { DAYS, MINUTES } from '../../session/utils/Number';
import fse from 'fs-extra';
import {
createOrUpdateItem,
generateAttachmentKeyIfEmpty,
getItemById,
hasSyncedInitialConfigurationItem,
lastAvatarUploadTimestamp,
} from '../../data/data';
import { OnionPaths } from '../../session/onions';
import { getMessageQueue } from '../../session/sending';
@ -29,11 +32,18 @@ import { useInterval } from '../../hooks/useInterval';
import { clearSearch } from '../../state/ducks/search';
import { showLeftPaneSection } from '../../state/ducks/section';
import { cleanUpOldDecryptedMedias } from '../../session/crypto/DecryptedAttachmentsManager';
import {
cleanUpOldDecryptedMedias,
getDecryptedMediaUrl,
} from '../../session/crypto/DecryptedAttachmentsManager';
import { OpenGroupManagerV2 } from '../../opengroup/opengroupV2/OpenGroupManagerV2';
import { loadDefaultRooms } from '../../opengroup/opengroupV2/ApiUtil';
import { forceRefreshRandomSnodePool } from '../../session/snode_api/snodePool';
import { SwarmPolling } from '../../session/snode_api/swarmPolling';
import { IMAGE_JPEG } from '../../types/MIME';
import { FSv2 } from '../../fileserver';
import { stringToArrayBuffer } from '../../session/utils/String';
import { debounce } from 'underscore';
// tslint:disable-next-line: no-import-side-effect no-submodule-imports
export enum SectionType {
@ -163,6 +173,83 @@ const triggerSyncIfNeeded = async () => {
}
};
const triggerAvatarReUploadIfNeeded = async () => {
const lastTimeStampAvatarUpload = (await getItemById(lastAvatarUploadTimestamp))?.value || 0;
if (Date.now() - lastTimeStampAvatarUpload > 14 * DAYS) {
window.log.info('Reuploading avatar...');
// reupload the avatar
const ourConvo = await ConversationController.getInstance().get(
UserUtils.getOurPubKeyStrFromCache()
);
if (!ourConvo) {
window.log.warn('ourConvo not found... This is not a valid case');
return;
}
const profileKey = window.textsecure.storage.get('profileKey');
if (!profileKey) {
window.log.warn('our profileKey not found... This is not a valid case');
return;
}
const currentAttachmentPath = ourConvo.getAvatarPath();
if (!currentAttachmentPath) {
window.log.warn('No attachment currently set for our convo.. Nothing to do.');
return;
}
const decryptedAvatarUrl = await getDecryptedMediaUrl(currentAttachmentPath, IMAGE_JPEG);
if (!decryptedAvatarUrl) {
window.log.warn('Could not decrypt avatar stored locally..');
return;
}
const response = await fetch(decryptedAvatarUrl);
const blob = await response.blob();
const decryptedAvatarData = await blob.arrayBuffer();
if (!decryptedAvatarData?.byteLength) {
window.log.warn('Could not read blob of avatar locally..');
return;
}
const encryptedData = await window.textsecure.crypto.encryptProfile(
decryptedAvatarData,
profileKey
);
const avatarPointer = await FSv2.uploadFileToFsV2(encryptedData);
let fileUrl;
if (!avatarPointer) {
window.log.warn('failed to reupload avatar to fsv2');
return;
}
({ fileUrl } = avatarPointer);
ourConvo.set('avatarPointer', fileUrl);
// this encrypts and save the new avatar and returns a new attachment path
const upgraded = await window.Signal.Migrations.processNewAttachment({
isRaw: true,
data: decryptedAvatarData,
url: fileUrl,
});
const newAvatarPath = upgraded.path;
// Replace our temporary image with the attachment pointer from the server:
ourConvo.set('avatar', null);
const existingHash = ourConvo.get('avatarHash');
const displayName = ourConvo.get('profileName');
// this commits already
await ourConvo.setLokiProfile({ avatar: newAvatarPath, displayName, avatarHash: existingHash });
const newTimestampReupload = Date.now();
await createOrUpdateItem({ id: lastAvatarUploadTimestamp, value: newTimestampReupload });
window.log.info(
`Reuploading avatar finished at ${newTimestampReupload}, newAttachmentPointer ${fileUrl}`
);
}
};
/**
* This function is called only once: on app startup with a logged in user
*/
@ -193,6 +280,8 @@ const doAppStartUp = (dispatch: Dispatch<any>) => {
void loadDefaultRooms();
debounce(triggerAvatarReUploadIfNeeded, 200);
// TODO: Investigate the case where we reconnect
const ourKey = UserUtils.getOurPubKeyStrFromCache();
SwarmPolling.getInstance().addPubkey(ourKey);
@ -243,6 +332,11 @@ export const ActionsPanel = () => {
void forceRefreshRandomSnodePool();
}, DAYS * 1);
useInterval(() => {
// this won't be run every days, but if the app stays open for more than 10 days
void triggerAvatarReUploadIfNeeded();
}, DAYS * 1);
return (
<div className="module-left-pane__sections-container">
<Section type={SectionType.Profile} avatarPath={ourPrimaryConversation.avatarPath} />

View File

@ -60,6 +60,7 @@ export type ServerToken = {
};
export const hasSyncedInitialConfigurationItem = 'hasSyncedInitialConfigurationItem';
export const lastAvatarUploadTimestamp = 'lastAvatarUploadTimestamp';
const channelsToMake = {
shutdown,

View File

@ -179,7 +179,6 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
//start right away the function is called, and wait 1sec before calling it again
this.markRead = _.debounce(this.markReadBouncy, 1000, { leading: true });
// Listening for out-of-band data updates
this.on('ourAvatarChanged', avatar => this.updateAvatarOnPublicChat(avatar));
this.typingRefreshTimer = null;
this.typingPauseTimer = null;
@ -778,23 +777,6 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
return null;
}
public async updateAvatarOnPublicChat({ url, profileKey }: any) {
if (!this.isPublic()) {
return;
}
// Always share avatars on PublicChat
if (profileKey && typeof profileKey !== 'string') {
// eslint-disable-next-line no-param-reassign
// tslint:disable-next-line: no-parameter-reassignment
profileKey = fromArrayBufferToBase64(profileKey);
}
const serverAPI = await window.lokiPublicChatAPI.findOrCreateServer(this.get('server'));
if (!serverAPI) {
return;
}
await serverAPI.setAvatar(url, profileKey);
}
public async bouncyUpdateLastMessage() {
if (!this.id) {
return;
@ -1223,11 +1205,11 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
// Not sure if we care about updating the database
}
public async setProfileAvatar(avatar: any, avatarHash?: string) {
public async setProfileAvatar(avatar: null | { path: string }, avatarHash?: string) {
const profileAvatar = this.get('avatar');
const existingHash = this.get('avatarHash');
let shouldCommit = false;
if (profileAvatar !== avatar) {
if (!_.isEqual(profileAvatar, avatar)) {
this.set({ avatar });
shouldCommit = true;
}