reupload avatar every 10 days
This commit is contained in:
parent
558761ba31
commit
8c726fd745
|
@ -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>
|
||||
|
|
|
@ -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 }));
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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>;
|
||||
}
|
||||
|
|
|
@ -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');
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
||||
|
|
|
@ -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} />
|
||||
|
|
|
@ -60,6 +60,7 @@ export type ServerToken = {
|
|||
};
|
||||
|
||||
export const hasSyncedInitialConfigurationItem = 'hasSyncedInitialConfigurationItem';
|
||||
export const lastAvatarUploadTimestamp = 'lastAvatarUploadTimestamp';
|
||||
|
||||
const channelsToMake = {
|
||||
shutdown,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue