Skip to content

Commit 3fcc0e1

Browse files
authored
Merge pull request #1668 from session-foundation/dev
Session 1.17.2
2 parents 1948066 + 132737b commit 3fcc0e1

File tree

69 files changed

+1651
-1021
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

69 files changed

+1651
-1021
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@
7676
"fs-extra": "11.3.0",
7777
"glob": "10.4.5",
7878
"image-type": "^4.1.0",
79-
"libsession_util_nodejs": "https://github.com/session-foundation/libsession-util-nodejs/releases/download/v0.5.8/libsession_util_nodejs-v0.5.8.tar.gz",
79+
"libsession_util_nodejs": "https://github.com/session-foundation/libsession-util-nodejs/releases/download/v0.5.9/libsession_util_nodejs-v0.5.9.tar.gz",
8080
"libsodium-wrappers-sumo": "^0.7.15",
8181
"linkify-it": "^5.0.0",
8282
"lodash": "^4.17.21",

preload.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ window.sessionFeatureFlags = {
5757
replaceLocalizedStringsWithKeys: false,
5858
// Hooks
5959
useClosedGroupV2QAButtons: false, // TODO DO NOT MERGE
60+
useDeterministicEncryption: !isEmpty(process.env.SESSION_ATTACH_DETERMINISTIC_ENCRYPTION),
6061
useOnionRequests: true,
6162
useTestNet: isTestNet() || isTestIntegration(),
6263
useLocalDevNet: !isEmpty(process.env.LOCAL_DEVNET_SEED_URL)

protos/SignalService.proto

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -178,17 +178,11 @@ message DataMessage {
178178
}
179179

180180
message Quote {
181-
182-
message QuotedAttachment {
183-
optional string contentType = 1;
184-
optional string fileName = 2;
185-
optional AttachmentPointer thumbnail = 3;
186-
}
181+
reserved 3, 4;
182+
reserved "text", "attachments";
187183

188184
required uint64 id = 1;
189185
required string author = 2;
190-
optional string text = 3;
191-
repeated QuotedAttachment attachments = 4;
192186
}
193187

194188
message Preview {
@@ -268,16 +262,22 @@ message AttachmentPointer {
268262

269263
// @required
270264
required fixed64 deprecated_id = 1;
271-
optional string contentType = 2;
272-
optional bytes key = 3;
273-
optional uint32 size = 4;
274-
optional bytes digest = 6;
275-
optional string fileName = 7;
276-
optional uint32 flags = 8;
277-
optional uint32 width = 9;
278-
optional uint32 height = 10;
279-
optional string caption = 11;
280-
optional string url = 101;
265+
optional string contentType = 2;
266+
optional bytes key = 3;
267+
optional uint32 size = 4;
268+
optional bytes digest = 6;
269+
optional string fileName = 7;
270+
optional uint32 flags = 8;
271+
optional uint32 width = 9;
272+
optional uint32 height = 10;
273+
optional string caption = 11;
274+
/**
275+
* This field can be just an url to the file, or have a fragment appended to it that can contain:
276+
* - `p=<server_pubkey_hex>` // hex encoded pubkey of the file server
277+
* - `d=` // if the file is deterministically encrypted, this field is present, otherwise it is not
278+
* If needed, those fields are a &, and can be parsed/built with the usual URLSearchParams logic
279+
*/
280+
optional string url = 101;
281281
}
282282

283283

ts/components/conversation/composition/CompositionBox.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,10 +83,10 @@ export interface StagedLinkPreviewData {
8383
scaledDown: ProcessedLinkPreviewThumbnailType | null;
8484
}
8585

86-
export interface StagedAttachmentType extends AttachmentType {
86+
export type StagedAttachmentType = AttachmentType & {
8787
file: File;
8888
path?: string; // a bit hacky, but this is the only way to make our sending audio message be playable, this must be used only for those message
89-
}
89+
};
9090

9191
export type SendMessageType = {
9292
conversationId: string;

ts/components/conversation/message/message-content/MessageContextMenu.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,10 @@ export const MessageContextMenu = (props: Props) => {
216216
}, [isSelectedBlocked, messageId]);
217217

218218
const copyText = useCallback(() => {
219-
MessageInteraction.copyBodyToClipboard(text);
219+
const selection = window.getSelection();
220+
const selectedText = selection?.toString().trim();
221+
// Note: we want to allow to copy through the "Copy" menu item the currently selected text, if any.
222+
MessageInteraction.copyBodyToClipboard(selectedText || text);
220223
}, [text]);
221224

222225
const onSelect = useCallback(() => {

ts/components/conversation/right-panel/overlay/message-info/OverlayMessageInfo.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,9 +117,12 @@ async function getPropsForMessageInfo(
117117
const found = await Data.getMessageById(messageId);
118118
const attachmentsWithMediaDetails: Array<PropsForAttachment> = [];
119119
if (found) {
120+
const attachmentsInMsg = found.get('attachments') || [];
121+
120122
// process attachments so we have the fileSize, url and screenshots
121123
for (let i = 0; i < attachments.length; i++) {
122124
const props = found.getPropsForAttachment(attachments[i]);
125+
const fsUrl = attachmentsInMsg?.[i].url;
123126
if (
124127
props?.contentType &&
125128
GoogleChrome.isVideoTypeSupported(props?.contentType) &&
@@ -134,6 +137,7 @@ async function getPropsForMessageInfo(
134137
attachmentsWithMediaDetails.push({
135138
...props,
136139
duration,
140+
url: fsUrl,
137141
});
138142
} else if (props?.contentType && isAudio(props.contentType) && !props.duration && props.url) {
139143
// eslint-disable-next-line no-await-in-loop
@@ -145,9 +149,10 @@ async function getPropsForMessageInfo(
145149
attachmentsWithMediaDetails.push({
146150
...props,
147151
duration,
152+
url: fsUrl,
148153
});
149154
} else if (props) {
150-
attachmentsWithMediaDetails.push(props);
155+
attachmentsWithMediaDetails.push({ ...props, url: fsUrl });
151156
}
152157
}
153158

ts/components/conversation/right-panel/overlay/message-info/components/AttachmentInfo.tsx

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import { PropsForAttachment } from '../../../../../../state/ducks/conversations'
44
import { Flex } from '../../../../../basic/Flex';
55
import { tr } from '../../../../../../localization/localeTools';
66
import { saveLogToDesktop } from '../../../../../../util/logger/renderer_process_logging';
7+
import { extractDetailsFromUrlFragment } from '../../../../../../session/url';
8+
import { isDevProd } from '../../../../../../shared/env_vars';
79

810
type Props = {
911
attachment: PropsForAttachment;
@@ -16,7 +18,7 @@ const StyledLabelContainer = styled(Flex)`
1618
}
1719
`;
1820

19-
function formatAttachmentUrl(attachment: PropsForAttachment) {
21+
function formatAttachmentUrl(attachment: Pick<PropsForAttachment, 'url'>) {
2022
// Note: desktop overwrites the url with the local path once the file is downloaded,
2123
// and I think this is how we know the file was downloaded.
2224

@@ -28,7 +30,8 @@ function formatAttachmentUrl(attachment: PropsForAttachment) {
2830
return tr('attachmentsNa');
2931
}
3032

31-
const fileId = attachment.url.split('/').pop() || '';
33+
const fileUrl = URL.canParse(attachment.url) && new URL(attachment.url);
34+
const fileId = fileUrl ? fileUrl?.pathname.split('/').pop() || '' : '';
3235

3336
if (!fileId) {
3437
return tr('attachmentsNa');
@@ -37,12 +40,23 @@ function formatAttachmentUrl(attachment: PropsForAttachment) {
3740
return fileId;
3841
}
3942

43+
function extractAttachmentDetails(attachment: Pick<PropsForAttachment, 'url'>) {
44+
const fileUrl = URL.canParse(attachment?.url) && new URL(attachment.url);
45+
return {
46+
deterministicEncryption:
47+
(fileUrl && extractDetailsFromUrlFragment(fileUrl)?.deterministicEncryption) || false,
48+
fsHost: fileUrl ? fileUrl.hostname : tr('attachmentsNa'),
49+
};
50+
}
51+
4052
export const AttachmentInfo = (props: Props) => {
4153
const { attachment } = props;
4254

4355
// NOTE the attachment.url will be an empty string if the attachment is broken
4456
const hasError = attachment.error || attachment.url === '';
4557

58+
const { deterministicEncryption, fsHost } = extractAttachmentDetails(attachment);
59+
4660
return (
4761
<Flex $container={true} $flexDirection="column" $flexGap="var(--margins-xs)">
4862
<LabelWithInfo label={tr('attachmentsFileId')} info={formatAttachmentUrl(attachment)} />
@@ -78,6 +92,15 @@ export const AttachmentInfo = (props: Props) => {
7892
}}
7993
/>
8094
) : null}
95+
{isDevProd() ? (
96+
<>
97+
<LabelWithInfo
98+
label="Uses Deterministic Encryption"
99+
info={deterministicEncryption ? 'Yes' : 'No'}
100+
/>
101+
<LabelWithInfo label="Fs host" info={fsHost} />
102+
</>
103+
) : null}
81104
</StyledLabelContainer>
82105
</Flex>
83106
);

ts/components/dialog/EditProfilePictureModal.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ export const EditProfilePictureModal = ({ conversationId }: EditProfilePictureMo
141141
const ourAvatarIsUploading = useOurAvatarIsUploading();
142142
const ourAvatarUploadFailed = useOurAvatarUploadFailed();
143143
const sogsAvatarIsUploading = useAvatarOfRoomIsUploading(conversationId);
144+
const [isProcessing, setIsProcessing] = useState(false);
144145

145146
const [newAvatarObjectUrl, setNewAvatarObjectUrl] = useState<string | null>(avatarPath);
146147
const [isNewAvatarAnimated, setIsNewAvatarAnimated] = useState<boolean>(false);
@@ -169,7 +170,7 @@ export const EditProfilePictureModal = ({ conversationId }: EditProfilePictureMo
169170
const isPublic = useIsPublic(conversationId);
170171

171172
const handleAvatarClick = async () => {
172-
const res = await pickFileForAvatar();
173+
const res = await pickFileForAvatar(setIsProcessing);
173174

174175
if (!res) {
175176
window.log.error('Failed to pick avatar');
@@ -210,7 +211,8 @@ export const EditProfilePictureModal = ({ conversationId }: EditProfilePictureMo
210211
await triggerUploadProfileAvatar(newAvatarObjectUrl, conversationId, dispatch);
211212
};
212213

213-
const loading = ourAvatarIsUploading || groupAvatarChangePending || sogsAvatarIsUploading;
214+
const loading =
215+
ourAvatarIsUploading || groupAvatarChangePending || sogsAvatarIsUploading || isProcessing;
214216

215217
const newAvatarLoaded = newAvatarObjectUrl !== avatarPath;
216218

@@ -328,7 +330,7 @@ export const EditProfilePictureModal = ({ conversationId }: EditProfilePictureMo
328330
{loading ? (
329331
<>
330332
<SpacerSM />
331-
{isMe ? <Localizer token="updating" /> : null}
333+
{isMe && !isProcessing ? <Localizer token="updating" /> : null}
332334
<SessionSpinner loading={loading} height="30px" />
333335
</>
334336
) : (

ts/components/leftpane/ActionsPanel.tsx

Lines changed: 3 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ import { DecryptedAttachmentsManager } from '../../session/crypto/DecryptedAttac
2222

2323
import { DURATION } from '../../session/constants';
2424

25-
import { reuploadCurrentAvatarUs } from '../../interactions/avatar-interactions/nts-avatar-interactions';
2625
import {
2726
onionPathModal,
2827
updateDebugMenuModal,
@@ -52,14 +51,13 @@ import { useDebugMode } from '../../state/selectors/debug';
5251
import { networkDataActions } from '../../state/ducks/networkData';
5352
import { LUCIDE_ICONS_UNICODE } from '../icon/lucide';
5453
import { AvatarMigrate } from '../../session/utils/job_runners/jobs/AvatarMigrateJob';
55-
import { NetworkTime } from '../../util/NetworkTime';
5654
import { Storage } from '../../util/storage';
57-
import { getFileInfoFromFileServer } from '../../session/apis/file_server_api/FileServerApi';
5855
import { themesArray } from '../../themes/constants/colors';
5956
import { isDebugMode, isDevProd } from '../../shared/env_vars';
6057
import { GearAvatarButton } from '../buttons/avatar/GearAvatarButton';
6158
import { useZoomShortcuts } from '../../hooks/useZoomingShortcut';
6259
import { OnionStatusLight } from '../dialog/OnionStatusPathDialog';
60+
import { AvatarReupload } from '../../session/utils/job_runners/jobs/AvatarReuploadJob';
6361

6462
const StyledContainerAvatar = styled.div`
6563
padding: var(--margins-lg);
@@ -98,17 +96,6 @@ const triggerSyncIfNeeded = async () => {
9896
}
9997
};
10098

101-
const triggerAvatarReUploadIfNeeded = async () => {
102-
const lastAvatarUploadExpiryMs =
103-
(await Data.getItemById(SettingsKey.ntsAvatarExpiryMs))?.value || Number.MAX_SAFE_INTEGER;
104-
105-
if (NetworkTime.now() > lastAvatarUploadExpiryMs) {
106-
window.log.info('Reuploading avatar...');
107-
// reupload the avatar
108-
await reuploadCurrentAvatarUs();
109-
}
110-
};
111-
11299
/**
113100
* This function is called only once: on app startup with a logged in user
114101
*/
@@ -127,9 +114,8 @@ const doAppStartUp = async () => {
127114
}); // refresh our swarm on start to speed up the first message fetching event
128115
void Data.cleanupOrphanedAttachments();
129116

130-
// TODOLATER make this a job of the JobRunner
131117
// Note: do not make this a debounce call (as for some reason it doesn't work with promises)
132-
void triggerAvatarReUploadIfNeeded();
118+
await AvatarReupload.addAvatarReuploadJob();
133119

134120
/* Postpone a little bit of the polling of sogs messages to let the swarm messages come in first. */
135121
global.setTimeout(() => {
@@ -147,17 +133,6 @@ const doAppStartUp = async () => {
147133
// Schedule a confSyncJob in some time to let anything incoming from the network be applied and see if there is a push needed
148134
// Note: this also starts periodic jobs, so we don't need to keep doing it
149135
await UserSync.queueNewJobIfNeeded();
150-
151-
// on app startup, check that the avatar expiry on the file server
152-
const avatarPointer = ConvoHub.use()
153-
.get(UserUtils.getOurPubKeyStrFromCache())
154-
.getAvatarPointer();
155-
if (avatarPointer) {
156-
const details = await getFileInfoFromFileServer(avatarPointer);
157-
if (details?.expiryMs) {
158-
await Storage.put(SettingsKey.ntsAvatarExpiryMs, details.expiryMs);
159-
}
160-
}
161136
}, 20000);
162137

163138
global.setTimeout(() => {
@@ -283,8 +258,7 @@ export const ActionsPanel = () => {
283258
if (!ourPrimaryConversation) {
284259
return;
285260
}
286-
// this won't be run every days, but if the app stays open for more than 10 days
287-
void triggerAvatarReUploadIfNeeded();
261+
void AvatarReupload.addAvatarReuploadJob();
288262
},
289263
window.sessionFeatureFlags.fsTTL30s ? DURATION.SECONDS * 1 : DURATION.DAYS * 1
290264
);

ts/data/settings-key.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ const settingsOpengroupPruning = 'prune-setting';
1111
const settingsNotification = 'notification-setting';
1212
const settingsAudioNotification = 'audio-notification-setting';
1313
const hasSyncedInitialConfigurationItem = 'hasSyncedInitialConfigurationItem';
14-
const ntsAvatarExpiryMs = 'ntsAvatarExpiryMs';
1514
const hasLinkPreviewPopupBeenDisplayed = 'hasLinkPreviewPopupBeenDisplayed';
1615
const hasFollowSystemThemeEnabled = 'hasFollowSystemThemeEnabled';
1716
const hideRecoveryPassword = 'hideRecoveryPassword';
@@ -44,7 +43,6 @@ export const SettingsKey = {
4443
settingsNotification,
4544
settingsAudioNotification,
4645
hasSyncedInitialConfigurationItem,
47-
ntsAvatarExpiryMs,
4846
hasLinkPreviewPopupBeenDisplayed,
4947
latestUserProfileEnvelopeTimestamp,
5048
latestUserGroupEnvelopeTimestamp,

0 commit comments

Comments
 (0)