Support for joining New Groups via invite links

This commit is contained in:
Scott Nonnenberg
2021-01-29 14:16:48 -08:00
committed by GitHub
parent c0510b08a5
commit a48b3e381e
41 changed files with 2532 additions and 381 deletions
+5
View File
@@ -22,6 +22,8 @@ import { makeLookup } from './makeLookup';
import { missingCaseError } from './missingCaseError';
import { parseRemoteClientExpiration } from './parseRemoteClientExpiration';
import { sleep } from './sleep';
import { longRunningTaskWrapper } from './longRunningTaskWrapper';
import { toWebSafeBase64, fromWebSafeBase64 } from './webSafeBase64';
import * as zkgroup from './zkgroup';
export {
@@ -31,6 +33,7 @@ export {
createWaitBatcher,
deleteForEveryone,
downloadAttachment,
fromWebSafeBase64,
generateSecurityNumber,
getSafetyNumberPlaceholder,
getStringForProfileChange,
@@ -39,10 +42,12 @@ export {
GoogleChrome,
hasExpired,
isFileDangerous,
longRunningTaskWrapper,
makeLookup,
missingCaseError,
parseRemoteClientExpiration,
Registration,
sleep,
toWebSafeBase64,
zkgroup,
};
+7 -7
View File
@@ -14478,7 +14478,7 @@
"rule": "DOM-innerHTML",
"path": "ts/components/CompositionArea.js",
"line": " el.innerHTML = '';",
"lineNumber": 41,
"lineNumber": 42,
"reasonCategory": "usageTrusted",
"updated": "2020-05-20T20:10:43.540Z",
"reasonDetail": "Our code, no user input, only clearing out the dom"
@@ -14487,7 +14487,7 @@
"rule": "React-useRef",
"path": "ts/components/CompositionArea.js",
"line": " const inputApiRef = React.useRef();",
"lineNumber": 59,
"lineNumber": 62,
"reasonCategory": "falseMatch",
"updated": "2020-10-26T19:12:24.410Z",
"reasonDetail": "Doesn't refer to a DOM element."
@@ -14496,7 +14496,7 @@
"rule": "React-useRef",
"path": "ts/components/CompositionArea.js",
"line": " const attSlotRef = React.useRef(null);",
"lineNumber": 82,
"lineNumber": 85,
"reasonCategory": "usageTrusted",
"updated": "2020-10-26T19:12:24.410Z",
"reasonDetail": "Needed for the composition area."
@@ -14505,7 +14505,7 @@
"rule": "React-useRef",
"path": "ts/components/CompositionArea.js",
"line": " const micCellRef = React.useRef(null);",
"lineNumber": 116,
"lineNumber": 119,
"reasonCategory": "usageTrusted",
"updated": "2020-10-26T19:12:24.410Z",
"reasonDetail": "Needed for the composition area."
@@ -14514,7 +14514,7 @@
"rule": "DOM-innerHTML",
"path": "ts/components/CompositionArea.tsx",
"line": " el.innerHTML = '';",
"lineNumber": 92,
"lineNumber": 98,
"reasonCategory": "usageTrusted",
"updated": "2020-06-03T19:23:21.195Z",
"reasonDetail": "Our code, no user input, only clearing out the dom"
@@ -15279,7 +15279,7 @@
"rule": "jQuery-wrap(",
"path": "ts/textsecure/WebAPI.js",
"line": " const byteBuffer = window.dcodeIO.ByteBuffer.wrap(quote, 'binary', window.dcodeIO.ByteBuffer.LITTLE_ENDIAN);",
"lineNumber": 1270,
"lineNumber": 1302,
"reasonCategory": "falseMatch",
"updated": "2020-09-08T23:07:22.682Z"
},
@@ -15287,7 +15287,7 @@
"rule": "jQuery-wrap(",
"path": "ts/textsecure/WebAPI.ts",
"line": " const byteBuffer = window.dcodeIO.ByteBuffer.wrap(",
"lineNumber": 2174,
"lineNumber": 2230,
"reasonCategory": "falseMatch",
"updated": "2020-09-08T23:07:22.682Z"
},
+90
View File
@@ -0,0 +1,90 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
export async function longRunningTaskWrapper<T>({
name,
idForLogging,
task,
suppressErrorDialog,
}: {
name: string;
idForLogging: string;
task: () => Promise<T>;
suppressErrorDialog?: boolean;
}): Promise<T> {
const idLog = `${name}/${idForLogging}`;
const ONE_SECOND = 1000;
const TWO_SECONDS = 2000;
let progressView: typeof Whisper.ReactWrapperView | undefined;
let spinnerStart;
let progressTimeout: NodeJS.Timeout | undefined = setTimeout(() => {
window.log.info(`longRunningTaskWrapper/${idLog}: Creating spinner`);
// Note: this component uses a portal to render itself into the top-level DOM. No
// need to attach it to the DOM here.
progressView = new Whisper.ReactWrapperView({
className: 'progress-modal-wrapper',
Component: window.Signal.Components.ProgressModal,
});
spinnerStart = Date.now();
}, TWO_SECONDS);
// Note: any task we put here needs to have its own safety valve; this function will
// show a spinner until it's done
try {
window.log.info(`longRunningTaskWrapper/${idLog}: Starting task`);
const result = await task();
window.log.info(
`longRunningTaskWrapper/${idLog}: Task completed successfully`
);
if (progressTimeout) {
clearTimeout(progressTimeout);
progressTimeout = undefined;
}
if (progressView) {
const now = Date.now();
if (spinnerStart && now - spinnerStart < ONE_SECOND) {
window.log.info(
`longRunningTaskWrapper/${idLog}: Spinner shown for less than second, showing for another second`
);
await window.Signal.Util.sleep(ONE_SECOND);
}
progressView.remove();
progressView = undefined;
}
return result;
} catch (error) {
window.log.error(
`longRunningTaskWrapper/${idLog}: Error!`,
error && error.stack ? error.stack : error
);
if (progressTimeout) {
clearTimeout(progressTimeout);
progressTimeout = undefined;
}
if (progressView) {
progressView.remove();
progressView = undefined;
}
if (!suppressErrorDialog) {
window.log.info(`longRunningTaskWrapper/${idLog}: Showing error dialog`);
// Note: this component uses a portal to render itself into the top-level DOM. No
// need to attach it to the DOM here.
const errorView = new Whisper.ReactWrapperView({
className: 'error-modal-wrapper',
Component: window.Signal.Components.ErrorModal,
props: {
onClose: () => errorView.remove(),
},
});
}
throw error;
}
}
+6 -2
View File
@@ -25,7 +25,7 @@ export function isSgnlHref(value: string | URL, logger: LoggerType): boolean {
type ParsedSgnlHref =
| { command: null; args: Map<never, never> }
| { command: string; args: Map<string, string> };
| { command: string; args: Map<string, string>; hash: string | undefined };
export function parseSgnlHref(
href: string,
logger: LoggerType
@@ -42,5 +42,9 @@ export function parseSgnlHref(
}
});
return { command: url.host, args };
return {
command: url.host,
args,
hash: url.hash ? url.hash.slice(1) : undefined,
};
}
+25
View File
@@ -0,0 +1,25 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
export function toWebSafeBase64(base64: string): string {
return base64.replace(/\//g, '_').replace(/\+/g, '-').replace(/=/g, '');
}
export function fromWebSafeBase64(webSafeBase64: string): string {
const base64 = webSafeBase64.replace(/_/g, '/').replace(/-/g, '+');
// Ensure that the character count is a multiple of four, filling in the extra
// space needed with '='
const remainder = base64.length % 4;
if (remainder === 3) {
return `${base64}=`;
}
if (remainder === 2) {
return `${base64}==`;
}
if (remainder === 1) {
return `${base64}===`;
}
return base64;
}