Enables ContextIsolation
This commit is contained in:
parent
4bbf5eb5d4
commit
9374832ea4
83 changed files with 1009 additions and 1073 deletions
14
app/main.ts
14
app/main.ts
|
@ -675,7 +675,7 @@ async function createWindow() {
|
||||||
nodeIntegration: false,
|
nodeIntegration: false,
|
||||||
nodeIntegrationInWorker: false,
|
nodeIntegrationInWorker: false,
|
||||||
sandbox: false,
|
sandbox: false,
|
||||||
contextIsolation: false,
|
contextIsolation: !isTestEnvironment(getEnvironment()),
|
||||||
preload: join(
|
preload: join(
|
||||||
__dirname,
|
__dirname,
|
||||||
usePreloadBundle
|
usePreloadBundle
|
||||||
|
@ -1138,7 +1138,7 @@ async function showScreenShareWindow(sourceName: string) {
|
||||||
...defaultWebPrefs,
|
...defaultWebPrefs,
|
||||||
nodeIntegration: false,
|
nodeIntegration: false,
|
||||||
nodeIntegrationInWorker: false,
|
nodeIntegrationInWorker: false,
|
||||||
sandbox: false,
|
sandbox: true,
|
||||||
contextIsolation: true,
|
contextIsolation: true,
|
||||||
preload: join(__dirname, '../ts/windows/screenShare/preload.js'),
|
preload: join(__dirname, '../ts/windows/screenShare/preload.js'),
|
||||||
},
|
},
|
||||||
|
@ -1192,7 +1192,7 @@ async function showAbout() {
|
||||||
...defaultWebPrefs,
|
...defaultWebPrefs,
|
||||||
nodeIntegration: false,
|
nodeIntegration: false,
|
||||||
nodeIntegrationInWorker: false,
|
nodeIntegrationInWorker: false,
|
||||||
sandbox: false,
|
sandbox: true,
|
||||||
contextIsolation: true,
|
contextIsolation: true,
|
||||||
preload: join(__dirname, '../ts/windows/about/preload.js'),
|
preload: join(__dirname, '../ts/windows/about/preload.js'),
|
||||||
nativeWindowOpen: true,
|
nativeWindowOpen: true,
|
||||||
|
@ -1240,7 +1240,7 @@ async function showSettingsWindow() {
|
||||||
...defaultWebPrefs,
|
...defaultWebPrefs,
|
||||||
nodeIntegration: false,
|
nodeIntegration: false,
|
||||||
nodeIntegrationInWorker: false,
|
nodeIntegrationInWorker: false,
|
||||||
sandbox: false,
|
sandbox: true,
|
||||||
contextIsolation: true,
|
contextIsolation: true,
|
||||||
preload: join(__dirname, '../ts/windows/settings/preload.js'),
|
preload: join(__dirname, '../ts/windows/settings/preload.js'),
|
||||||
nativeWindowOpen: true,
|
nativeWindowOpen: true,
|
||||||
|
@ -1379,7 +1379,7 @@ async function showDebugLogWindow() {
|
||||||
...defaultWebPrefs,
|
...defaultWebPrefs,
|
||||||
nodeIntegration: false,
|
nodeIntegration: false,
|
||||||
nodeIntegrationInWorker: false,
|
nodeIntegrationInWorker: false,
|
||||||
sandbox: false,
|
sandbox: true,
|
||||||
contextIsolation: true,
|
contextIsolation: true,
|
||||||
preload: join(__dirname, '../ts/windows/debuglog/preload.js'),
|
preload: join(__dirname, '../ts/windows/debuglog/preload.js'),
|
||||||
nativeWindowOpen: true,
|
nativeWindowOpen: true,
|
||||||
|
@ -1443,7 +1443,7 @@ function showPermissionsPopupWindow(forCalling: boolean, forCamera: boolean) {
|
||||||
...defaultWebPrefs,
|
...defaultWebPrefs,
|
||||||
nodeIntegration: false,
|
nodeIntegration: false,
|
||||||
nodeIntegrationInWorker: false,
|
nodeIntegrationInWorker: false,
|
||||||
sandbox: false,
|
sandbox: true,
|
||||||
contextIsolation: true,
|
contextIsolation: true,
|
||||||
preload: join(__dirname, '../ts/windows/permissions/preload.js'),
|
preload: join(__dirname, '../ts/windows/permissions/preload.js'),
|
||||||
nativeWindowOpen: true,
|
nativeWindowOpen: true,
|
||||||
|
@ -1784,7 +1784,7 @@ app.on('ready', async () => {
|
||||||
webPreferences: {
|
webPreferences: {
|
||||||
...defaultWebPrefs,
|
...defaultWebPrefs,
|
||||||
nodeIntegration: false,
|
nodeIntegration: false,
|
||||||
sandbox: false,
|
sandbox: true,
|
||||||
contextIsolation: true,
|
contextIsolation: true,
|
||||||
preload: join(__dirname, '../ts/windows/loading/preload.js'),
|
preload: join(__dirname, '../ts/windows/loading/preload.js'),
|
||||||
},
|
},
|
||||||
|
|
|
@ -118,22 +118,6 @@
|
||||||
.addEventListener('dblclick', () => window.showDebugLog());
|
.addEventListener('dblclick', () => window.showDebugLog());
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script type="text/javascript" src="js/components.js"></script>
|
|
||||||
<script type="text/javascript" src="ts/set_os_class.js"></script>
|
|
||||||
<script
|
|
||||||
type="text/javascript"
|
|
||||||
src="ts/manage_full_screen_class.js"
|
|
||||||
></script>
|
|
||||||
<script
|
|
||||||
type="text/javascript"
|
|
||||||
src="ts/backbone/reliable_trigger.js"
|
|
||||||
></script>
|
|
||||||
|
|
||||||
<script
|
|
||||||
type="text/javascript"
|
|
||||||
src="ts/shims/showConfirmationDialog.js"
|
|
||||||
></script>
|
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
Note: this inline script cannot be changed without also changing the hash in
|
Note: this inline script cannot be changed without also changing the hash in
|
||||||
the CSP at the top of this file
|
the CSP at the top of this file
|
||||||
|
|
|
@ -1,199 +0,0 @@
|
||||||
(function(window) {
|
|
||||||
// internal: same as jQuery.extend(true, args...)
|
|
||||||
var extend = function() {
|
|
||||||
var target = arguments[0],
|
|
||||||
sources = [].slice.call(arguments, 1);
|
|
||||||
for (var i = 0; i < sources.length; ++i) {
|
|
||||||
var src = sources[i];
|
|
||||||
for (key in src) {
|
|
||||||
var val = src[key];
|
|
||||||
target[key] = typeof val === "object"
|
|
||||||
? extend(typeof target[key] === "object" ? target[key] : {}, val)
|
|
||||||
: val;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return target;
|
|
||||||
};
|
|
||||||
|
|
||||||
var WORKER_FILE = {
|
|
||||||
wav: "WebAudioRecorderWav.js",
|
|
||||||
ogg: "WebAudioRecorderOgg.js",
|
|
||||||
mp3: "WebAudioRecorderMp3.js"
|
|
||||||
};
|
|
||||||
|
|
||||||
// default configs
|
|
||||||
var CONFIGS = {
|
|
||||||
workerDir: "/", // worker scripts dir (end with /)
|
|
||||||
numChannels: 2, // number of channels
|
|
||||||
encoding: "wav", // encoding (can be changed at runtime)
|
|
||||||
|
|
||||||
// runtime options
|
|
||||||
options: {
|
|
||||||
timeLimit: 300, // recording time limit (sec)
|
|
||||||
encodeAfterRecord: false, // process encoding after recording
|
|
||||||
progressInterval: 1000, // encoding progress report interval (millisec)
|
|
||||||
bufferSize: undefined, // buffer size (use browser default)
|
|
||||||
|
|
||||||
// encoding-specific options
|
|
||||||
wav: {
|
|
||||||
mimeType: "audio/wav"
|
|
||||||
},
|
|
||||||
ogg: {
|
|
||||||
mimeType: "audio/ogg",
|
|
||||||
quality: 0.5 // (VBR only): quality = [-0.1 .. 1]
|
|
||||||
},
|
|
||||||
mp3: {
|
|
||||||
mimeType: "audio/mpeg",
|
|
||||||
bitRate: 160 // (CBR only): bit rate = [64 .. 320]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// constructor
|
|
||||||
var WebAudioRecorder = function(sourceNode, configs) {
|
|
||||||
extend(this, CONFIGS, configs || {});
|
|
||||||
this.context = sourceNode.context;
|
|
||||||
if (this.context.createScriptProcessor == null)
|
|
||||||
this.context.createScriptProcessor = this.context.createJavaScriptNode;
|
|
||||||
this.input = this.context.createGain();
|
|
||||||
sourceNode.connect(this.input);
|
|
||||||
this.buffer = [];
|
|
||||||
this.initWorker();
|
|
||||||
};
|
|
||||||
|
|
||||||
// instance methods
|
|
||||||
extend(WebAudioRecorder.prototype, {
|
|
||||||
isRecording: function() { return this.processor != null; },
|
|
||||||
|
|
||||||
setEncoding: function(encoding) {
|
|
||||||
if (this.isRecording())
|
|
||||||
this.error("setEncoding: cannot set encoding during recording");
|
|
||||||
else if (this.encoding !== encoding) {
|
|
||||||
this.encoding = encoding;
|
|
||||||
this.initWorker();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
setOptions: function(options) {
|
|
||||||
if (this.isRecording())
|
|
||||||
this.error("setOptions: cannot set options during recording");
|
|
||||||
else {
|
|
||||||
extend(this.options, options);
|
|
||||||
this.worker.postMessage({ command: "options", options: this.options });
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
startRecording: function() {
|
|
||||||
if (this.isRecording())
|
|
||||||
this.error("startRecording: previous recording is running");
|
|
||||||
else {
|
|
||||||
var numChannels = this.numChannels,
|
|
||||||
buffer = this.buffer,
|
|
||||||
worker = this.worker;
|
|
||||||
this.processor = this.context.createScriptProcessor(
|
|
||||||
this.options.bufferSize,
|
|
||||||
this.numChannels, this.numChannels);
|
|
||||||
this.input.connect(this.processor);
|
|
||||||
this.processor.connect(this.context.destination);
|
|
||||||
this.processor.onaudioprocess = function(event) {
|
|
||||||
for (var ch = 0; ch < numChannels; ++ch)
|
|
||||||
buffer[ch] = event.inputBuffer.getChannelData(ch);
|
|
||||||
worker.postMessage({ command: "record", buffer: buffer });
|
|
||||||
};
|
|
||||||
this.worker.postMessage({
|
|
||||||
command: "start",
|
|
||||||
bufferSize: this.processor.bufferSize
|
|
||||||
});
|
|
||||||
this.startTime = Date.now();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
recordingTime: function() {
|
|
||||||
return this.isRecording() ? (Date.now() - this.startTime) * 0.001 : null;
|
|
||||||
},
|
|
||||||
|
|
||||||
cancelRecording: function() {
|
|
||||||
if (this.isRecording()) {
|
|
||||||
this.input.disconnect();
|
|
||||||
this.processor.disconnect();
|
|
||||||
delete this.processor;
|
|
||||||
this.worker.postMessage({ command: "cancel" });
|
|
||||||
} else
|
|
||||||
this.error("cancelRecording: no recording is running");
|
|
||||||
},
|
|
||||||
|
|
||||||
finishRecording: function() {
|
|
||||||
if (this.isRecording()) {
|
|
||||||
this.input.disconnect();
|
|
||||||
this.processor.disconnect();
|
|
||||||
delete this.processor;
|
|
||||||
this.worker.postMessage({ command: "finish" });
|
|
||||||
} else
|
|
||||||
this.error("finishRecording: no recording is running");
|
|
||||||
},
|
|
||||||
|
|
||||||
cancelEncoding: function() {
|
|
||||||
if (this.options.encodeAfterRecord)
|
|
||||||
if (this.isRecording())
|
|
||||||
this.error("cancelEncoding: recording is not finished");
|
|
||||||
else {
|
|
||||||
this.onEncodingCanceled(this);
|
|
||||||
this.initWorker();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
this.error("cancelEncoding: invalid method call");
|
|
||||||
},
|
|
||||||
|
|
||||||
initWorker: function() {
|
|
||||||
if (this.worker != null)
|
|
||||||
this.worker.terminate();
|
|
||||||
this.onEncoderLoading(this, this.encoding);
|
|
||||||
this.worker = new Worker(this.workerDir + WORKER_FILE[this.encoding]);
|
|
||||||
var _this = this;
|
|
||||||
this.worker.onmessage = function(event) {
|
|
||||||
var data = event.data;
|
|
||||||
switch (data.command) {
|
|
||||||
case "loaded":
|
|
||||||
_this.onEncoderLoaded(_this, _this.encoding);
|
|
||||||
break;
|
|
||||||
case "timeout":
|
|
||||||
_this.onTimeout(_this);
|
|
||||||
break;
|
|
||||||
case "progress":
|
|
||||||
_this.onEncodingProgress(_this, data.progress);
|
|
||||||
break;
|
|
||||||
case "complete":
|
|
||||||
_this.onComplete(_this, data.blob);
|
|
||||||
break;
|
|
||||||
case "error":
|
|
||||||
_this.error(data.message);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
this.worker.postMessage({
|
|
||||||
command: "init",
|
|
||||||
config: {
|
|
||||||
sampleRate: this.context.sampleRate,
|
|
||||||
numChannels: this.numChannels
|
|
||||||
},
|
|
||||||
options: this.options
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
error: function(message) {
|
|
||||||
this.onError(this, "WebAudioRecorder.js:" + message);
|
|
||||||
},
|
|
||||||
|
|
||||||
// event handlers
|
|
||||||
onEncoderLoading: function(recorder, encoding) {},
|
|
||||||
onEncoderLoaded: function(recorder, encoding) {},
|
|
||||||
onTimeout: function(recorder) { recorder.finishRecording(); },
|
|
||||||
onEncodingProgress: function (recorder, progress) {},
|
|
||||||
onEncodingCanceled: function(recorder) {},
|
|
||||||
onComplete: function(recorder, blob) {
|
|
||||||
recorder.onError(recorder, "WebAudioRecorder.js: You must override .onComplete event");
|
|
||||||
},
|
|
||||||
onError: function(recorder, message) { console.log(message); }
|
|
||||||
});
|
|
||||||
|
|
||||||
window.WebAudioRecorder = WebAudioRecorder;
|
|
||||||
})(window);
|
|
|
@ -16,14 +16,14 @@
|
||||||
"postinstall": "yarn build:acknowledgments && patch-package && yarn electron:install-app-deps && rimraf node_modules/dtrace-provider",
|
"postinstall": "yarn build:acknowledgments && patch-package && yarn electron:install-app-deps && rimraf node_modules/dtrace-provider",
|
||||||
"postuninstall": "yarn build:acknowledgments",
|
"postuninstall": "yarn build:acknowledgments",
|
||||||
"start": "electron .",
|
"start": "electron .",
|
||||||
"generate": "npm-run-all build-protobuf build:esbuild sass get-expire-time copy-and-concat",
|
"generate": "npm-run-all build-protobuf build:esbuild sass get-expire-time copy-components",
|
||||||
"build-release": "yarn run build",
|
"build-release": "yarn run build",
|
||||||
"sign-release": "node ts/updater/generateSignature.js",
|
"sign-release": "node ts/updater/generateSignature.js",
|
||||||
"notarize": "echo 'No longer necessary'",
|
"notarize": "echo 'No longer necessary'",
|
||||||
"get-strings": "node ts/scripts/get-strings.js",
|
"get-strings": "node ts/scripts/get-strings.js",
|
||||||
"push-strings": "node ts/scripts/push-strings.js",
|
"push-strings": "node ts/scripts/push-strings.js",
|
||||||
"get-expire-time": "node ts/scripts/get-expire-time.js",
|
"get-expire-time": "node ts/scripts/get-expire-time.js",
|
||||||
"copy-and-concat": "node ts/scripts/copy-and-concat.js",
|
"copy-components": "node ts/scripts/copy.js",
|
||||||
"sass": "sass stylesheets/manifest.scss:stylesheets/manifest.css stylesheets/manifest_bridge.scss:stylesheets/manifest_bridge.css",
|
"sass": "sass stylesheets/manifest.scss:stylesheets/manifest.css stylesheets/manifest_bridge.scss:stylesheets/manifest_bridge.css",
|
||||||
"build-module-protobuf": "pbjs --target static-module --force-long --no-verify --no-create --wrap commonjs --out ts/protobuf/compiled.js protos/*.proto && pbts --out ts/protobuf/compiled.d.ts ts/protobuf/compiled.js",
|
"build-module-protobuf": "pbjs --target static-module --force-long --no-verify --no-create --wrap commonjs --out ts/protobuf/compiled.js protos/*.proto && pbts --out ts/protobuf/compiled.d.ts ts/protobuf/compiled.js",
|
||||||
"clean-module-protobuf": "rm -f ts/protobuf/compiled.d.ts ts/protobuf/compiled.js",
|
"clean-module-protobuf": "rm -f ts/protobuf/compiled.d.ts ts/protobuf/compiled.js",
|
||||||
|
@ -190,7 +190,7 @@
|
||||||
"@babel/preset-typescript": "7.17.12",
|
"@babel/preset-typescript": "7.17.12",
|
||||||
"@electron/fuses": "1.5.0",
|
"@electron/fuses": "1.5.0",
|
||||||
"@mixer/parallel-prettier": "2.0.1",
|
"@mixer/parallel-prettier": "2.0.1",
|
||||||
"@signalapp/mock-server": "2.12.0",
|
"@signalapp/mock-server": "2.12.1",
|
||||||
"@storybook/addon-a11y": "6.5.6",
|
"@storybook/addon-a11y": "6.5.6",
|
||||||
"@storybook/addon-actions": "6.5.6",
|
"@storybook/addon-actions": "6.5.6",
|
||||||
"@storybook/addon-controls": "6.5.6",
|
"@storybook/addon-controls": "6.5.6",
|
||||||
|
@ -229,7 +229,6 @@
|
||||||
"@types/lru-cache": "5.1.0",
|
"@types/lru-cache": "5.1.0",
|
||||||
"@types/memoizee": "0.4.2",
|
"@types/memoizee": "0.4.2",
|
||||||
"@types/mocha": "9.0.0",
|
"@types/mocha": "9.0.0",
|
||||||
"@types/mustache": "4.1.2",
|
|
||||||
"@types/node": "16.18.3",
|
"@types/node": "16.18.3",
|
||||||
"@types/node-fetch": "2.6.2",
|
"@types/node-fetch": "2.6.2",
|
||||||
"@types/normalize-path": "3.0.0",
|
"@types/normalize-path": "3.0.0",
|
||||||
|
|
|
@ -11,53 +11,10 @@
|
||||||
<body>
|
<body>
|
||||||
<div id="mocha"></div>
|
<div id="mocha"></div>
|
||||||
<div id="tests"></div>
|
<div id="tests"></div>
|
||||||
|
|
||||||
<script type="text/javascript" src="../js/components.js"></script>
|
|
||||||
<script
|
|
||||||
type="text/javascript"
|
|
||||||
src="../ts/backbone/reliable_trigger.js"
|
|
||||||
></script>
|
|
||||||
<script
|
<script
|
||||||
type="text/javascript"
|
type="text/javascript"
|
||||||
src="../node_modules/mocha/mocha.js"
|
src="../node_modules/mocha/mocha.js"
|
||||||
></script>
|
></script>
|
||||||
<script type="text/javascript">
|
|
||||||
mocha.setup('bdd');
|
|
||||||
</script>
|
|
||||||
<script type="text/javascript" src="test.js"></script>
|
<script type="text/javascript" src="test.js"></script>
|
||||||
|
|
||||||
<script type="text/javascript">
|
|
||||||
window.Signal.conversationControllerStart();
|
|
||||||
|
|
||||||
window.testUtilities.prepareTests();
|
|
||||||
delete window.testUtilities.prepareTests;
|
|
||||||
|
|
||||||
!(function () {
|
|
||||||
const passed = [];
|
|
||||||
const failed = [];
|
|
||||||
|
|
||||||
class Reporter extends Mocha.reporters.HTML {
|
|
||||||
constructor(runner, options) {
|
|
||||||
super(runner, options);
|
|
||||||
|
|
||||||
runner.on('pass', test => passed.push(test.fullTitle()));
|
|
||||||
runner.on('fail', (test, error) => {
|
|
||||||
failed.push({
|
|
||||||
testName: test.fullTitle(),
|
|
||||||
error: error?.stack || String(error),
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
runner.on('end', () =>
|
|
||||||
window.testUtilities.onComplete({ passed, failed })
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mocha.reporter(Reporter);
|
|
||||||
|
|
||||||
mocha.run();
|
|
||||||
})();
|
|
||||||
</script>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
34
test/test.js
34
test/test.js
|
@ -7,6 +7,7 @@
|
||||||
* global helpers for tests
|
* global helpers for tests
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
mocha.setup('bdd');
|
||||||
mocha.setup({ timeout: 10000 });
|
mocha.setup({ timeout: 10000 });
|
||||||
|
|
||||||
function deleteIndexedDB() {
|
function deleteIndexedDB() {
|
||||||
|
@ -38,7 +39,34 @@ before(async () => {
|
||||||
await window.storage.fetch();
|
await window.storage.fetch();
|
||||||
});
|
});
|
||||||
|
|
||||||
window.Whisper = window.Whisper || {};
|
window.textsecure.storage.protocol = window.getSignalProtocolStore();
|
||||||
window.Whisper.events = { ...Backbone.Events };
|
|
||||||
|
|
||||||
window.textsecure.storage.protocol = new window.SignalProtocolStore();
|
window.testUtilities.prepareTests();
|
||||||
|
delete window.testUtilities.prepareTests;
|
||||||
|
|
||||||
|
!(function () {
|
||||||
|
const passed = [];
|
||||||
|
const failed = [];
|
||||||
|
|
||||||
|
class Reporter extends Mocha.reporters.HTML {
|
||||||
|
constructor(runner, options) {
|
||||||
|
super(runner, options);
|
||||||
|
|
||||||
|
runner.on('pass', test => passed.push(test.fullTitle()));
|
||||||
|
runner.on('fail', (test, error) => {
|
||||||
|
failed.push({
|
||||||
|
testName: test.fullTitle(),
|
||||||
|
error: error?.stack || String(error),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
runner.on('end', () =>
|
||||||
|
window.testUtilities.onComplete({ passed, failed })
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mocha.reporter(Reporter);
|
||||||
|
|
||||||
|
mocha.run();
|
||||||
|
})();
|
||||||
|
|
58
ts/CI.ts
58
ts/CI.ts
|
@ -10,28 +10,30 @@ import type { IPCResponse as ChallengeResponseType } from './challenge';
|
||||||
|
|
||||||
type ResolveType = (data: unknown) => void;
|
type ResolveType = (data: unknown) => void;
|
||||||
|
|
||||||
export class CI {
|
export type CIType = {
|
||||||
private readonly eventListeners = new Map<string, Array<ResolveType>>();
|
deviceName: string;
|
||||||
|
handleEvent: (event: string, data: unknown) => unknown;
|
||||||
|
setProvisioningURL: (url: string) => unknown;
|
||||||
|
solveChallenge: (response: ChallengeResponseType) => unknown;
|
||||||
|
waitForEvent: (event: string, timeout?: number) => unknown;
|
||||||
|
};
|
||||||
|
|
||||||
private readonly completedEvents = new Map<string, Array<unknown>>();
|
export function getCI(deviceName: string): CIType {
|
||||||
|
const eventListeners = new Map<string, Array<ResolveType>>();
|
||||||
|
const completedEvents = new Map<string, Array<unknown>>();
|
||||||
|
|
||||||
constructor(public readonly deviceName: string) {
|
ipcRenderer.on('ci:event', (_, event, data) => {
|
||||||
ipcRenderer.on('ci:event', (_, event, data) => {
|
handleEvent(event, data);
|
||||||
this.handleEvent(event, data);
|
});
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public async waitForEvent(
|
function waitForEvent(event: string, timeout = 60 * SECOND) {
|
||||||
event: string,
|
const pendingCompleted = completedEvents.get(event) || [];
|
||||||
timeout = 60 * SECOND
|
|
||||||
): Promise<unknown> {
|
|
||||||
const pendingCompleted = this.completedEvents.get(event) || [];
|
|
||||||
const pending = pendingCompleted.shift();
|
const pending = pendingCompleted.shift();
|
||||||
if (pending) {
|
if (pending) {
|
||||||
log.info(`CI: resolving pending result for ${event}`, pending);
|
log.info(`CI: resolving pending result for ${event}`, pending);
|
||||||
|
|
||||||
if (pendingCompleted.length === 0) {
|
if (pendingCompleted.length === 0) {
|
||||||
this.completedEvents.delete(event);
|
completedEvents.delete(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
return pending;
|
return pending;
|
||||||
|
@ -44,10 +46,10 @@ export class CI {
|
||||||
reject(new Error('Timed out'));
|
reject(new Error('Timed out'));
|
||||||
}, timeout);
|
}, timeout);
|
||||||
|
|
||||||
let list = this.eventListeners.get(event);
|
let list = eventListeners.get(event);
|
||||||
if (!list) {
|
if (!list) {
|
||||||
list = [];
|
list = [];
|
||||||
this.eventListeners.set(event, list);
|
eventListeners.set(event, list);
|
||||||
}
|
}
|
||||||
|
|
||||||
list.push((value: unknown) => {
|
list.push((value: unknown) => {
|
||||||
|
@ -58,17 +60,17 @@ export class CI {
|
||||||
return promise;
|
return promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
public setProvisioningURL(url: string): void {
|
function setProvisioningURL(url: string): void {
|
||||||
this.handleEvent('provisioning-url', url);
|
handleEvent('provisioning-url', url);
|
||||||
}
|
}
|
||||||
|
|
||||||
public handleEvent(event: string, data: unknown): void {
|
function handleEvent(event: string, data: unknown): void {
|
||||||
const list = this.eventListeners.get(event) || [];
|
const list = eventListeners.get(event) || [];
|
||||||
const resolve = list.shift();
|
const resolve = list.shift();
|
||||||
|
|
||||||
if (resolve) {
|
if (resolve) {
|
||||||
if (list.length === 0) {
|
if (list.length === 0) {
|
||||||
this.eventListeners.delete(event);
|
eventListeners.delete(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
log.info(`CI: got event ${event} with data`, data);
|
log.info(`CI: got event ${event} with data`, data);
|
||||||
|
@ -78,15 +80,23 @@ export class CI {
|
||||||
|
|
||||||
log.info(`CI: postponing event ${event}`);
|
log.info(`CI: postponing event ${event}`);
|
||||||
|
|
||||||
let resultList = this.completedEvents.get(event);
|
let resultList = completedEvents.get(event);
|
||||||
if (!resultList) {
|
if (!resultList) {
|
||||||
resultList = [];
|
resultList = [];
|
||||||
this.completedEvents.set(event, resultList);
|
completedEvents.set(event, resultList);
|
||||||
}
|
}
|
||||||
resultList.push(data);
|
resultList.push(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public solveChallenge(response: ChallengeResponseType): void {
|
function solveChallenge(response: ChallengeResponseType): void {
|
||||||
window.Signal.challengeHandler?.onResponse(response);
|
window.Signal.challengeHandler?.onResponse(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
deviceName,
|
||||||
|
handleEvent,
|
||||||
|
setProvisioningURL,
|
||||||
|
solveChallenge,
|
||||||
|
waitForEvent,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -194,13 +194,13 @@ export class ConversationController {
|
||||||
drop(window.storage.put('unreadCount', newUnreadCount));
|
drop(window.storage.put('unreadCount', newUnreadCount));
|
||||||
|
|
||||||
if (newUnreadCount > 0) {
|
if (newUnreadCount > 0) {
|
||||||
window.setBadgeCount(newUnreadCount);
|
window.IPC.setBadgeCount(newUnreadCount);
|
||||||
window.document.title = `${window.getTitle()} (${newUnreadCount})`;
|
window.document.title = `${window.getTitle()} (${newUnreadCount})`;
|
||||||
} else {
|
} else {
|
||||||
window.setBadgeCount(0);
|
window.IPC.setBadgeCount(0);
|
||||||
window.document.title = window.getTitle();
|
window.document.title = window.getTitle();
|
||||||
}
|
}
|
||||||
window.updateTrayIcon(newUnreadCount);
|
window.IPC.updateTrayIcon(newUnreadCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
onEmpty(): void {
|
onEmpty(): void {
|
||||||
|
|
|
@ -2320,4 +2320,8 @@ export class SignalProtocolStore extends EventEmitter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getSignalProtocolStore(): SignalProtocolStore {
|
||||||
|
return new SignalProtocolStore();
|
||||||
|
}
|
||||||
|
|
||||||
window.SignalProtocolStore = SignalProtocolStore;
|
window.SignalProtocolStore = SignalProtocolStore;
|
||||||
|
|
161
ts/WebAudioRecorder.ts
Normal file
161
ts/WebAudioRecorder.ts
Normal file
|
@ -0,0 +1,161 @@
|
||||||
|
// Copyright 2023 Signal Messenger, LLC
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
const DEFAULT_OPTIONS = {
|
||||||
|
bufferSize: undefined, // buffer size (use browser default)
|
||||||
|
encodeAfterRecord: false,
|
||||||
|
mp3: {
|
||||||
|
mimeType: 'audio/mpeg',
|
||||||
|
bitRate: 160, // (CBR only): bit rate = [64 .. 320]
|
||||||
|
},
|
||||||
|
numChannels: 2, // number of channels
|
||||||
|
progressInterval: 1000, // encoding progress report interval (millisec)
|
||||||
|
timeLimit: 300, // recording time limit (sec)
|
||||||
|
};
|
||||||
|
|
||||||
|
type OptionsType = {
|
||||||
|
bufferSize: number | undefined;
|
||||||
|
numChannels: number;
|
||||||
|
timeLimit?: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export class WebAudioRecorder {
|
||||||
|
private buffer: Array<Float32Array>;
|
||||||
|
private options: OptionsType;
|
||||||
|
private context: BaseAudioContext;
|
||||||
|
private input: GainNode;
|
||||||
|
private onComplete: (recorder: WebAudioRecorder, blob: Blob) => unknown;
|
||||||
|
private onError: (recorder: WebAudioRecorder, error: string) => unknown;
|
||||||
|
private processor?: ScriptProcessorNode;
|
||||||
|
public worker?: Worker;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
sourceNode: GainNode,
|
||||||
|
options: Pick<OptionsType, 'timeLimit'>,
|
||||||
|
callbacks: {
|
||||||
|
onComplete: (recorder: WebAudioRecorder, blob: Blob) => unknown;
|
||||||
|
onError: (recorder: WebAudioRecorder, error: string) => unknown;
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
this.options = {
|
||||||
|
...DEFAULT_OPTIONS,
|
||||||
|
...options,
|
||||||
|
};
|
||||||
|
|
||||||
|
this.context = sourceNode.context;
|
||||||
|
this.input = this.context.createGain();
|
||||||
|
sourceNode.connect(this.input);
|
||||||
|
this.buffer = [];
|
||||||
|
this.initWorker();
|
||||||
|
|
||||||
|
this.onComplete = callbacks.onComplete;
|
||||||
|
this.onError = callbacks.onError;
|
||||||
|
}
|
||||||
|
|
||||||
|
isRecording(): boolean {
|
||||||
|
return this.processor != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
startRecording(): void {
|
||||||
|
if (this.isRecording()) {
|
||||||
|
this.error('startRecording: previous recording is running');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { buffer, worker } = this;
|
||||||
|
const { bufferSize, numChannels } = this.options;
|
||||||
|
|
||||||
|
if (!worker) {
|
||||||
|
this.error('startRecording: worker not initialized');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.processor = this.context.createScriptProcessor(
|
||||||
|
bufferSize,
|
||||||
|
numChannels,
|
||||||
|
numChannels
|
||||||
|
);
|
||||||
|
this.input.connect(this.processor);
|
||||||
|
this.processor.connect(this.context.destination);
|
||||||
|
this.processor.onaudioprocess = event => {
|
||||||
|
// eslint-disable-next-line no-plusplus
|
||||||
|
for (let ch = 0; ch < numChannels; ++ch) {
|
||||||
|
buffer[ch] = event.inputBuffer.getChannelData(ch);
|
||||||
|
}
|
||||||
|
worker.postMessage({ command: 'record', buffer });
|
||||||
|
};
|
||||||
|
worker.postMessage({
|
||||||
|
command: 'start',
|
||||||
|
bufferSize: this.processor.bufferSize,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
cancelRecording(): void {
|
||||||
|
if (!this.isRecording()) {
|
||||||
|
this.error('cancelRecording: no recording is running');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.worker || !this.processor) {
|
||||||
|
this.error('startRecording: worker not initialized');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.input.disconnect();
|
||||||
|
this.processor.disconnect();
|
||||||
|
delete this.processor;
|
||||||
|
this.worker.postMessage({ command: 'cancel' });
|
||||||
|
}
|
||||||
|
|
||||||
|
finishRecording(): void {
|
||||||
|
if (!this.isRecording()) {
|
||||||
|
this.error('finishRecording: no recording is running');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.worker || !this.processor) {
|
||||||
|
this.error('startRecording: worker not initialized');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.input.disconnect();
|
||||||
|
this.processor.disconnect();
|
||||||
|
delete this.processor;
|
||||||
|
this.worker.postMessage({ command: 'finish' });
|
||||||
|
}
|
||||||
|
|
||||||
|
private initWorker(): void {
|
||||||
|
if (this.worker != null) {
|
||||||
|
this.worker.terminate();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.worker = new Worker('js/WebAudioRecorderMp3.js');
|
||||||
|
this.worker.onmessage = event => {
|
||||||
|
const { data } = event;
|
||||||
|
switch (data.command) {
|
||||||
|
case 'complete':
|
||||||
|
this.onComplete(this, data.blob);
|
||||||
|
break;
|
||||||
|
case 'error':
|
||||||
|
this.error(data.message);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
this.worker.postMessage({
|
||||||
|
command: 'init',
|
||||||
|
config: {
|
||||||
|
sampleRate: this.context.sampleRate,
|
||||||
|
numChannels: this.options.numChannels,
|
||||||
|
},
|
||||||
|
options: this.options,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
error(message: string): void {
|
||||||
|
this.onError(this, `WebAudioRecorder.js: ${message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.WebAudioRecorder = WebAudioRecorder;
|
104
ts/background.ts
104
ts/background.ts
|
@ -2,7 +2,7 @@
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
import { webFrame } from 'electron';
|
import { webFrame } from 'electron';
|
||||||
import { isNumber, clone, debounce } from 'lodash';
|
import { isNumber, debounce } from 'lodash';
|
||||||
import { bindActionCreators } from 'redux';
|
import { bindActionCreators } from 'redux';
|
||||||
import { render } from 'react-dom';
|
import { render } from 'react-dom';
|
||||||
import { batch as batchDispatch } from 'react-redux';
|
import { batch as batchDispatch } from 'react-redux';
|
||||||
|
@ -56,7 +56,7 @@ import { GROUP_CREDENTIALS_KEY } from './services/groupCredentialFetcher';
|
||||||
import * as KeyboardLayout from './services/keyboardLayout';
|
import * as KeyboardLayout from './services/keyboardLayout';
|
||||||
import * as StorageService from './services/storage';
|
import * as StorageService from './services/storage';
|
||||||
import { RoutineProfileRefresher } from './routineProfileRefresh';
|
import { RoutineProfileRefresher } from './routineProfileRefresh';
|
||||||
import { isMoreRecentThan, isOlderThan, toDayMillis } from './util/timestamp';
|
import { isOlderThan, toDayMillis } from './util/timestamp';
|
||||||
import { isValidReactionEmoji } from './reactions/isValidReactionEmoji';
|
import { isValidReactionEmoji } from './reactions/isValidReactionEmoji';
|
||||||
import type { ConversationModel } from './models/conversations';
|
import type { ConversationModel } from './models/conversations';
|
||||||
import { getContact, isIncoming } from './messages/helpers';
|
import { getContact, isIncoming } from './messages/helpers';
|
||||||
|
@ -161,10 +161,11 @@ import { clearConversationDraftAttachments } from './util/clearConversationDraft
|
||||||
import { removeLinkPreview } from './services/LinkPreview';
|
import { removeLinkPreview } from './services/LinkPreview';
|
||||||
import { PanelType } from './types/Panels';
|
import { PanelType } from './types/Panels';
|
||||||
import { getQuotedMessageSelector } from './state/selectors/composer';
|
import { getQuotedMessageSelector } from './state/selectors/composer';
|
||||||
|
import { flushAttachmentDownloadQueue } from './util/attachmentDownloadQueue';
|
||||||
|
import { StartupQueue } from './util/StartupQueue';
|
||||||
|
import { showConfirmationDialog } from './util/showConfirmationDialog';
|
||||||
import { onCallEventSync } from './util/onCallEventSync';
|
import { onCallEventSync } from './util/onCallEventSync';
|
||||||
|
|
||||||
const MAX_ATTACHMENT_DOWNLOAD_AGE = 3600 * 72 * 1000;
|
|
||||||
|
|
||||||
export function isOverHourIntoPast(timestamp: number): boolean {
|
export function isOverHourIntoPast(timestamp: number): boolean {
|
||||||
const HOUR = 1000 * 60 * 60;
|
const HOUR = 1000 * 60 * 60;
|
||||||
return isNumber(timestamp) && isOlderThan(timestamp, HOUR);
|
return isNumber(timestamp) && isOlderThan(timestamp, HOUR);
|
||||||
|
@ -201,15 +202,11 @@ export async function startApp(): Promise<void> {
|
||||||
|
|
||||||
await KeyboardLayout.initialize();
|
await KeyboardLayout.initialize();
|
||||||
|
|
||||||
window.Whisper.events = clone(window.Backbone.Events);
|
StartupQueue.initialize();
|
||||||
window.Signal.Util.MessageController.install();
|
|
||||||
window.Signal.conversationControllerStart();
|
|
||||||
window.startupProcessingQueue = new window.Signal.Util.StartupQueue();
|
|
||||||
notificationService.initialize({
|
notificationService.initialize({
|
||||||
i18n: window.i18n,
|
i18n: window.i18n,
|
||||||
storage: window.storage,
|
storage: window.storage,
|
||||||
});
|
});
|
||||||
window.attachmentDownloadQueue = [];
|
|
||||||
|
|
||||||
await window.Signal.Util.initializeMessageCounter();
|
await window.Signal.Util.initializeMessageCounter();
|
||||||
|
|
||||||
|
@ -249,8 +246,8 @@ export async function startApp(): Promise<void> {
|
||||||
},
|
},
|
||||||
|
|
||||||
requestChallenge(request) {
|
requestChallenge(request) {
|
||||||
if (window.CI) {
|
if (window.Signal.CI) {
|
||||||
window.CI.handleEvent('challenge', request);
|
window.Signal.CI.handleEvent('challenge', request);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
window.sendChallengeRequest(request);
|
window.sendChallengeRequest(request);
|
||||||
|
@ -494,7 +491,7 @@ export async function startApp(): Promise<void> {
|
||||||
window.addEventListener('dblclick', (event: Event) => {
|
window.addEventListener('dblclick', (event: Event) => {
|
||||||
const target = event.target as HTMLElement;
|
const target = event.target as HTMLElement;
|
||||||
if (isWindowDragElement(target)) {
|
if (isWindowDragElement(target)) {
|
||||||
window.titleBarDoubleClick();
|
window.IPC.titleBarDoubleClick();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -529,7 +526,7 @@ export async function startApp(): Promise<void> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const builtInImages = await window.getBuiltInImages();
|
const builtInImages = await window.IPC.getBuiltInImages();
|
||||||
preload(builtInImages);
|
preload(builtInImages);
|
||||||
|
|
||||||
// We add this to window here because the default Node context is erased at the end
|
// We add this to window here because the default Node context is erased at the end
|
||||||
|
@ -608,7 +605,7 @@ export async function startApp(): Promise<void> {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await new Promise<void>((resolve, reject) => {
|
await new Promise<void>((resolve, reject) => {
|
||||||
window.showConfirmationDialog({
|
showConfirmationDialog({
|
||||||
dialogName: 'deleteOldIndexedDBData',
|
dialogName: 'deleteOldIndexedDBData',
|
||||||
onTopOfEverything: true,
|
onTopOfEverything: true,
|
||||||
cancelText: window.i18n('quit'),
|
cancelText: window.i18n('quit'),
|
||||||
|
@ -624,7 +621,7 @@ export async function startApp(): Promise<void> {
|
||||||
'User chose not to delete old data. Shutting down.',
|
'User chose not to delete old data. Shutting down.',
|
||||||
Errors.toLogFormat(error)
|
Errors.toLogFormat(error)
|
||||||
);
|
);
|
||||||
window.shutdown();
|
window.IPC.shutdown();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -852,7 +849,7 @@ export async function startApp(): Promise<void> {
|
||||||
// This one should always be last - it could restart the app
|
// This one should always be last - it could restart the app
|
||||||
if (window.isBeforeVersion(lastVersion, 'v5.30.0-alpha')) {
|
if (window.isBeforeVersion(lastVersion, 'v5.30.0-alpha')) {
|
||||||
await deleteAllLogs();
|
await deleteAllLogs();
|
||||||
window.restart();
|
window.IPC.restart();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1253,22 +1250,6 @@ export async function startApp(): Promise<void> {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
window.Whisper.events.on(
|
|
||||||
'setWindowStats',
|
|
||||||
({
|
|
||||||
isFullScreen,
|
|
||||||
isMaximized,
|
|
||||||
}: {
|
|
||||||
isFullScreen: boolean;
|
|
||||||
isMaximized: boolean;
|
|
||||||
}) => {
|
|
||||||
window.reduxActions.user.userChanged({
|
|
||||||
isMainWindowMaximized: isMaximized,
|
|
||||||
isMainWindowFullScreen: isFullScreen,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
window.Whisper.events.on('setMenuOptions', (options: MenuOptionsType) => {
|
window.Whisper.events.on('setMenuOptions', (options: MenuOptionsType) => {
|
||||||
window.reduxActions.user.userChanged({ menuOptions: options });
|
window.reduxActions.user.userChanged({ menuOptions: options });
|
||||||
});
|
});
|
||||||
|
@ -1677,7 +1658,7 @@ export async function startApp(): Promise<void> {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
|
|
||||||
window.showConfirmationDialog({
|
showConfirmationDialog({
|
||||||
dialogName: 'deleteMessage',
|
dialogName: 'deleteMessage',
|
||||||
confirmStyle: 'negative',
|
confirmStyle: 'negative',
|
||||||
message: window.i18n('deleteWarning'),
|
message: window.i18n('deleteWarning'),
|
||||||
|
@ -1895,8 +1876,8 @@ export async function startApp(): Promise<void> {
|
||||||
document.getElementById('app-container')
|
document.getElementById('app-container')
|
||||||
);
|
);
|
||||||
const hideMenuBar = window.storage.get('hide-menu-bar', false);
|
const hideMenuBar = window.storage.get('hide-menu-bar', false);
|
||||||
window.setAutoHideMenuBar(hideMenuBar);
|
window.IPC.setAutoHideMenuBar(hideMenuBar);
|
||||||
window.setMenuBarVisibility(!hideMenuBar);
|
window.IPC.setMenuBarVisibility(!hideMenuBar);
|
||||||
|
|
||||||
startTimeTravelDetector(() => {
|
startTimeTravelDetector(() => {
|
||||||
window.Whisper.events.trigger('timetravel');
|
window.Whisper.events.trigger('timetravel');
|
||||||
|
@ -1927,7 +1908,7 @@ export async function startApp(): Promise<void> {
|
||||||
window.addEventListener('unload', () => notificationService.fastClear());
|
window.addEventListener('unload', () => notificationService.fastClear());
|
||||||
|
|
||||||
notificationService.on('click', (id, messageId, storyId) => {
|
notificationService.on('click', (id, messageId, storyId) => {
|
||||||
window.showWindow();
|
window.IPC.showWindow();
|
||||||
|
|
||||||
if (id) {
|
if (id) {
|
||||||
if (storyId) {
|
if (storyId) {
|
||||||
|
@ -2491,7 +2472,7 @@ export async function startApp(): Promise<void> {
|
||||||
window.flushAllWaitBatchers(),
|
window.flushAllWaitBatchers(),
|
||||||
]);
|
]);
|
||||||
log.info('onEmpty: All outstanding database requests complete');
|
log.info('onEmpty: All outstanding database requests complete');
|
||||||
window.readyForUpdates();
|
window.IPC.readyForUpdates();
|
||||||
window.ConversationController.onEmpty();
|
window.ConversationController.onEmpty();
|
||||||
|
|
||||||
// Start listeners here, after we get through our queue.
|
// Start listeners here, after we get through our queue.
|
||||||
|
@ -2512,7 +2493,7 @@ export async function startApp(): Promise<void> {
|
||||||
window.reduxActions.app.initialLoadComplete();
|
window.reduxActions.app.initialLoadComplete();
|
||||||
|
|
||||||
const processedCount = messageReceiver?.getAndResetProcessedCount() || 0;
|
const processedCount = messageReceiver?.getAndResetProcessedCount() || 0;
|
||||||
window.logAppLoadedEvent?.({
|
window.IPC.logAppLoadedEvent?.({
|
||||||
processedCount,
|
processedCount,
|
||||||
});
|
});
|
||||||
if (messageReceiver) {
|
if (messageReceiver) {
|
||||||
|
@ -2520,53 +2501,12 @@ export async function startApp(): Promise<void> {
|
||||||
}
|
}
|
||||||
|
|
||||||
window.Signal.Util.setBatchingStrategy(false);
|
window.Signal.Util.setBatchingStrategy(false);
|
||||||
|
StartupQueue.flush();
|
||||||
const attachmentDownloadQueue = window.attachmentDownloadQueue || [];
|
await flushAttachmentDownloadQueue();
|
||||||
|
|
||||||
// NOTE: ts/models/messages.ts expects this global to become undefined
|
|
||||||
// once we stop processing the queue.
|
|
||||||
window.attachmentDownloadQueue = undefined;
|
|
||||||
|
|
||||||
const MAX_ATTACHMENT_MSGS_TO_DOWNLOAD = 250;
|
|
||||||
const attachmentsToDownload = attachmentDownloadQueue.filter(
|
|
||||||
(message, index) =>
|
|
||||||
index <= MAX_ATTACHMENT_MSGS_TO_DOWNLOAD ||
|
|
||||||
isMoreRecentThan(
|
|
||||||
message.getReceivedAt(),
|
|
||||||
MAX_ATTACHMENT_DOWNLOAD_AGE
|
|
||||||
) ||
|
|
||||||
// Stickers and long text attachments has to be downloaded for UI
|
|
||||||
// to display the message properly.
|
|
||||||
message.hasRequiredAttachmentDownloads()
|
|
||||||
);
|
|
||||||
log.info(
|
|
||||||
'Downloading recent attachments of total attachments',
|
|
||||||
attachmentsToDownload.length,
|
|
||||||
attachmentDownloadQueue.length
|
|
||||||
);
|
|
||||||
|
|
||||||
if (window.startupProcessingQueue) {
|
|
||||||
window.startupProcessingQueue.flush();
|
|
||||||
window.startupProcessingQueue = undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
const messagesWithDownloads = await Promise.all(
|
|
||||||
attachmentsToDownload.map(message => message.queueAttachmentDownloads())
|
|
||||||
);
|
|
||||||
const messagesToSave: Array<MessageAttributesType> = [];
|
|
||||||
messagesWithDownloads.forEach((shouldSave, messageKey) => {
|
|
||||||
if (shouldSave) {
|
|
||||||
const message = attachmentsToDownload[messageKey];
|
|
||||||
messagesToSave.push(message.attributes);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
await window.Signal.Data.saveMessages(messagesToSave, {
|
|
||||||
ourUuid: storage.user.getCheckedUuid().toString(),
|
|
||||||
});
|
|
||||||
|
|
||||||
// Process crash reports if any
|
// Process crash reports if any
|
||||||
window.reduxActions.crashReports.setCrashReportCount(
|
window.reduxActions.crashReports.setCrashReportCount(
|
||||||
await window.crashReports.getCount()
|
await window.IPC.crashReports.getCount()
|
||||||
);
|
);
|
||||||
|
|
||||||
// Kick off a profile refresh if necessary, but don't wait for it, as failure is
|
// Kick off a profile refresh if necessary, but don't wait for it, as failure is
|
||||||
|
|
|
@ -43,6 +43,7 @@ type PropsType = {
|
||||||
onUndoArchive: (conversationId: string) => unknown;
|
onUndoArchive: (conversationId: string) => unknown;
|
||||||
openFileInFolder: (target: string) => unknown;
|
openFileInFolder: (target: string) => unknown;
|
||||||
hasCustomTitleBar: boolean;
|
hasCustomTitleBar: boolean;
|
||||||
|
osClassName: string;
|
||||||
hideMenuBar: boolean;
|
hideMenuBar: boolean;
|
||||||
|
|
||||||
executeMenuRole: ExecuteMenuRoleType;
|
executeMenuRole: ExecuteMenuRoleType;
|
||||||
|
@ -75,6 +76,7 @@ export function App({
|
||||||
onUndoArchive,
|
onUndoArchive,
|
||||||
openFileInFolder,
|
openFileInFolder,
|
||||||
openInbox,
|
openInbox,
|
||||||
|
osClassName,
|
||||||
registerSingleDevice,
|
registerSingleDevice,
|
||||||
renderCallManager,
|
renderCallManager,
|
||||||
renderGlobalModalContainer,
|
renderGlobalModalContainer,
|
||||||
|
@ -95,7 +97,7 @@ export function App({
|
||||||
contents = <SmartInstallScreen />;
|
contents = <SmartInstallScreen />;
|
||||||
} else if (appView === AppViewType.Standalone) {
|
} else if (appView === AppViewType.Standalone) {
|
||||||
const onComplete = () => {
|
const onComplete = () => {
|
||||||
window.removeSetupMenuItems();
|
window.IPC.removeSetupMenuItems();
|
||||||
openInbox();
|
openInbox();
|
||||||
};
|
};
|
||||||
contents = (
|
contents = (
|
||||||
|
@ -123,6 +125,19 @@ export function App({
|
||||||
}
|
}
|
||||||
}, [theme]);
|
}, [theme]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
document.body.classList.add(osClassName);
|
||||||
|
}, [osClassName]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
document.body.classList.toggle('os-has-custom-titlebar', hasCustomTitleBar);
|
||||||
|
}, [hasCustomTitleBar]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
document.body.classList.toggle('full-screen', isFullScreen);
|
||||||
|
document.body.classList.toggle('maximized', isMaximized);
|
||||||
|
}, [isFullScreen, isMaximized]);
|
||||||
|
|
||||||
const isPageVisible = usePageVisibility();
|
const isPageVisible = usePageVisibility();
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
document.body.classList.toggle('page-is-visible', isPageVisible);
|
document.body.classList.toggle('page-is-visible', isPageVisible);
|
||||||
|
|
|
@ -668,7 +668,11 @@ export function CompositionInput(props: Props): React.ReactElement {
|
||||||
<Manager>
|
<Manager>
|
||||||
<Reference>
|
<Reference>
|
||||||
{({ ref }) => (
|
{({ ref }) => (
|
||||||
<div className={getClassName('__input')} ref={ref}>
|
<div
|
||||||
|
className={getClassName('__input')}
|
||||||
|
ref={ref}
|
||||||
|
data-testid="CompositionInput"
|
||||||
|
>
|
||||||
{children}
|
{children}
|
||||||
<div
|
<div
|
||||||
ref={
|
ref={
|
||||||
|
|
|
@ -321,6 +321,7 @@ export function ConversationList({
|
||||||
'badges',
|
'badges',
|
||||||
'color',
|
'color',
|
||||||
'draftPreview',
|
'draftPreview',
|
||||||
|
'groupId',
|
||||||
'id',
|
'id',
|
||||||
'isMe',
|
'isMe',
|
||||||
'isSelected',
|
'isSelected',
|
||||||
|
|
|
@ -106,7 +106,7 @@ export function StandaloneRegistration({
|
||||||
registerSingleDevice: (number: string, code: string) => Promise<void>;
|
registerSingleDevice: (number: string, code: string) => Promise<void>;
|
||||||
}): JSX.Element {
|
}): JSX.Element {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
window.readyForUpdates();
|
window.IPC.readyForUpdates();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const [isValidNumber, setIsValidNumber] = useState(false);
|
const [isValidNumber, setIsValidNumber] = useState(false);
|
||||||
|
|
|
@ -162,7 +162,7 @@ export function ToastManager({
|
||||||
onClose={hideToast}
|
onClose={hideToast}
|
||||||
toastAction={{
|
toastAction={{
|
||||||
label: i18n('Toast--error--action'),
|
label: i18n('Toast--error--action'),
|
||||||
onClick: () => window.showDebugLog(),
|
onClick: () => window.IPC.showDebugLog(),
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{i18n('Toast--error')}
|
{i18n('Toast--error')}
|
||||||
|
|
|
@ -16,6 +16,7 @@ import {
|
||||||
} from '../../types/Attachment';
|
} from '../../types/Attachment';
|
||||||
import * as Errors from '../../types/errors';
|
import * as Errors from '../../types/errors';
|
||||||
import * as log from '../../logging/log';
|
import * as log from '../../logging/log';
|
||||||
|
import { useReducedMotion } from '../../hooks/useReducedMotion';
|
||||||
|
|
||||||
const MAX_GIF_REPEAT = 4;
|
const MAX_GIF_REPEAT = 4;
|
||||||
const MAX_GIF_TIME = 8;
|
const MAX_GIF_TIME = 8;
|
||||||
|
@ -28,8 +29,6 @@ export type Props = {
|
||||||
readonly i18n: LocalizerType;
|
readonly i18n: LocalizerType;
|
||||||
readonly theme?: ThemeType;
|
readonly theme?: ThemeType;
|
||||||
|
|
||||||
readonly reducedMotion?: boolean;
|
|
||||||
|
|
||||||
onError(): void;
|
onError(): void;
|
||||||
showVisualAttachment(): void;
|
showVisualAttachment(): void;
|
||||||
kickOffAttachmentDownload(): void;
|
kickOffAttachmentDownload(): void;
|
||||||
|
@ -46,16 +45,12 @@ export function GIF(props: Props): JSX.Element {
|
||||||
i18n,
|
i18n,
|
||||||
theme,
|
theme,
|
||||||
|
|
||||||
reducedMotion = Boolean(
|
|
||||||
window.Accessibility && window.Accessibility.reducedMotionSetting
|
|
||||||
),
|
|
||||||
|
|
||||||
onError,
|
onError,
|
||||||
showVisualAttachment,
|
showVisualAttachment,
|
||||||
kickOffAttachmentDownload,
|
kickOffAttachmentDownload,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const tapToPlay = reducedMotion;
|
const tapToPlay = useReducedMotion();
|
||||||
|
|
||||||
const videoRef = useRef<HTMLVideoElement | null>(null);
|
const videoRef = useRef<HTMLVideoElement | null>(null);
|
||||||
const { height, width } = getImageDimensions(attachment, size);
|
const { height, width } = getImageDimensions(attachment, size);
|
||||||
|
|
|
@ -221,7 +221,6 @@ export type PropsData = {
|
||||||
| 'title'
|
| 'title'
|
||||||
| 'unblurredAvatarPath'
|
| 'unblurredAvatarPath'
|
||||||
>;
|
>;
|
||||||
reducedMotion?: boolean;
|
|
||||||
conversationType: ConversationTypeType;
|
conversationType: ConversationTypeType;
|
||||||
attachments?: ReadonlyArray<AttachmentType>;
|
attachments?: ReadonlyArray<AttachmentType>;
|
||||||
giftBadge?: GiftBadgeType;
|
giftBadge?: GiftBadgeType;
|
||||||
|
@ -521,7 +520,7 @@ export class Message extends React.PureComponent<Props, State> {
|
||||||
status === 'viewed')
|
status === 'viewed')
|
||||||
) {
|
) {
|
||||||
const delta = Date.now() - timestamp;
|
const delta = Date.now() - timestamp;
|
||||||
window.CI?.handleEvent('message:send-complete', {
|
window.Signal.CI?.handleEvent('message:send-complete', {
|
||||||
timestamp,
|
timestamp,
|
||||||
delta,
|
delta,
|
||||||
});
|
});
|
||||||
|
@ -838,7 +837,6 @@ export class Message extends React.PureComponent<Props, State> {
|
||||||
pushPanelForConversation,
|
pushPanelForConversation,
|
||||||
quote,
|
quote,
|
||||||
readStatus,
|
readStatus,
|
||||||
reducedMotion,
|
|
||||||
renderAudioAttachment,
|
renderAudioAttachment,
|
||||||
renderingContext,
|
renderingContext,
|
||||||
shouldCollapseAbove,
|
shouldCollapseAbove,
|
||||||
|
@ -889,7 +887,6 @@ export class Message extends React.PureComponent<Props, State> {
|
||||||
theme={theme}
|
theme={theme}
|
||||||
i18n={i18n}
|
i18n={i18n}
|
||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
reducedMotion={reducedMotion}
|
|
||||||
onError={this.handleImageError}
|
onError={this.handleImageError}
|
||||||
showVisualAttachment={() => {
|
showVisualAttachment={() => {
|
||||||
showLightbox({
|
showLightbox({
|
||||||
|
@ -2529,10 +2526,11 @@ export class Message extends React.PureComponent<Props, State> {
|
||||||
attachments,
|
attachments,
|
||||||
direction,
|
direction,
|
||||||
isSticker,
|
isSticker,
|
||||||
|
onKeyDown,
|
||||||
|
renderMenu,
|
||||||
shouldCollapseAbove,
|
shouldCollapseAbove,
|
||||||
shouldCollapseBelow,
|
shouldCollapseBelow,
|
||||||
renderMenu,
|
timestamp,
|
||||||
onKeyDown,
|
|
||||||
} = this.props;
|
} = this.props;
|
||||||
const { expired, expiring, isSelected, imageBroken } = this.state;
|
const { expired, expiring, isSelected, imageBroken } = this.state;
|
||||||
|
|
||||||
|
@ -2554,6 +2552,7 @@ export class Message extends React.PureComponent<Props, State> {
|
||||||
isSelected ? 'module-message--selected' : null,
|
isSelected ? 'module-message--selected' : null,
|
||||||
expiring ? 'module-message--expired' : null
|
expiring ? 'module-message--expired' : null
|
||||||
)}
|
)}
|
||||||
|
data-testid={timestamp}
|
||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
// We need to have a role because screenreaders need to be able to focus here to
|
// We need to have a role because screenreaders need to be able to focus here to
|
||||||
// read the message, but we can't be a button; that would break inner buttons.
|
// read the message, but we can't be a button; that would break inner buttons.
|
||||||
|
|
|
@ -1147,5 +1147,5 @@ function getRowIndexFromElement(
|
||||||
}
|
}
|
||||||
|
|
||||||
function showDebugLog() {
|
function showDebugLog() {
|
||||||
window.showDebugLog();
|
window.IPC.showDebugLog();
|
||||||
}
|
}
|
||||||
|
|
|
@ -224,7 +224,6 @@ const renderAudioAttachment: Props['renderAudioAttachment'] = props => (
|
||||||
const createProps = (overrideProps: Partial<Props> = {}): Props => ({
|
const createProps = (overrideProps: Partial<Props> = {}): Props => ({
|
||||||
attachments: overrideProps.attachments,
|
attachments: overrideProps.attachments,
|
||||||
author: overrideProps.author || getDefaultConversation(),
|
author: overrideProps.author || getDefaultConversation(),
|
||||||
reducedMotion: boolean('reducedMotion', false),
|
|
||||||
bodyRanges: overrideProps.bodyRanges,
|
bodyRanges: overrideProps.bodyRanges,
|
||||||
canReact: true,
|
canReact: true,
|
||||||
canReply: true,
|
canReply: true,
|
||||||
|
|
|
@ -148,7 +148,7 @@ export function ConversationDetailsHeader({
|
||||||
|
|
||||||
if (canEdit) {
|
if (canEdit) {
|
||||||
return (
|
return (
|
||||||
<div className={bem('root')}>
|
<div className={bem('root')} data-testid="ConversationDetailsHeader">
|
||||||
{modal}
|
{modal}
|
||||||
{avatar}
|
{avatar}
|
||||||
<button
|
<button
|
||||||
|
@ -182,7 +182,7 @@ export function ConversationDetailsHeader({
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={bem('root')}>
|
<div className={bem('root')} data-testid="ConversationDetailsHeader">
|
||||||
{modal}
|
{modal}
|
||||||
{avatar}
|
{avatar}
|
||||||
{contents}
|
{contents}
|
||||||
|
|
|
@ -5,7 +5,6 @@ import type { ReactNode, FunctionComponent } from 'react';
|
||||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { isBoolean, isNumber } from 'lodash';
|
import { isBoolean, isNumber } from 'lodash';
|
||||||
import { v4 as uuid } from 'uuid';
|
|
||||||
|
|
||||||
import { Avatar, AvatarSize } from '../Avatar';
|
import { Avatar, AvatarSize } from '../Avatar';
|
||||||
import type { BadgeType } from '../../badges/types';
|
import type { BadgeType } from '../../badges/types';
|
||||||
|
@ -17,6 +16,7 @@ import { Spinner } from '../Spinner';
|
||||||
import { Time } from '../Time';
|
import { Time } from '../Time';
|
||||||
import { formatDateTimeShort } from '../../util/timestamp';
|
import { formatDateTimeShort } from '../../util/timestamp';
|
||||||
import * as durations from '../../util/durations';
|
import * as durations from '../../util/durations';
|
||||||
|
import { UUID } from '../../types/UUID';
|
||||||
|
|
||||||
const BASE_CLASS_NAME =
|
const BASE_CLASS_NAME =
|
||||||
'module-conversation-list__item--contact-or-conversation';
|
'module-conversation-list__item--contact-or-conversation';
|
||||||
|
@ -52,11 +52,13 @@ type PropsType = {
|
||||||
shouldShowSpinner?: boolean;
|
shouldShowSpinner?: boolean;
|
||||||
unreadCount?: number;
|
unreadCount?: number;
|
||||||
avatarSize?: AvatarSize;
|
avatarSize?: AvatarSize;
|
||||||
|
testId?: string;
|
||||||
} & Pick<
|
} & Pick<
|
||||||
ConversationType,
|
ConversationType,
|
||||||
| 'acceptedMessageRequest'
|
| 'acceptedMessageRequest'
|
||||||
| 'avatarPath'
|
| 'avatarPath'
|
||||||
| 'color'
|
| 'color'
|
||||||
|
| 'groupId'
|
||||||
| 'isMe'
|
| 'isMe'
|
||||||
| 'markedUnread'
|
| 'markedUnread'
|
||||||
| 'phoneNumber'
|
| 'phoneNumber'
|
||||||
|
@ -64,6 +66,7 @@ type PropsType = {
|
||||||
| 'sharedGroupNames'
|
| 'sharedGroupNames'
|
||||||
| 'title'
|
| 'title'
|
||||||
| 'unblurredAvatarPath'
|
| 'unblurredAvatarPath'
|
||||||
|
| 'uuid'
|
||||||
> &
|
> &
|
||||||
(
|
(
|
||||||
| { badge?: undefined; theme?: ThemeType }
|
| { badge?: undefined; theme?: ThemeType }
|
||||||
|
@ -80,6 +83,7 @@ export const BaseConversationListItem: FunctionComponent<PropsType> =
|
||||||
color,
|
color,
|
||||||
conversationType,
|
conversationType,
|
||||||
disabled,
|
disabled,
|
||||||
|
groupId,
|
||||||
headerDate,
|
headerDate,
|
||||||
headerName,
|
headerName,
|
||||||
i18n,
|
i18n,
|
||||||
|
@ -97,13 +101,16 @@ export const BaseConversationListItem: FunctionComponent<PropsType> =
|
||||||
profileName,
|
profileName,
|
||||||
sharedGroupNames,
|
sharedGroupNames,
|
||||||
shouldShowSpinner,
|
shouldShowSpinner,
|
||||||
|
testId: overrideTestId,
|
||||||
title,
|
title,
|
||||||
unblurredAvatarPath,
|
unblurredAvatarPath,
|
||||||
unreadCount,
|
unreadCount,
|
||||||
|
uuid,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const identifier = id ? cleanId(id) : undefined;
|
const identifier = id ? cleanId(id) : undefined;
|
||||||
const htmlId = useMemo(() => uuid(), []);
|
const htmlId = useMemo(() => UUID.generate().toString(), []);
|
||||||
|
const testId = overrideTestId || groupId || uuid;
|
||||||
const isUnread = isConversationUnread({ markedUnread, unreadCount });
|
const isUnread = isConversationUnread({ markedUnread, unreadCount });
|
||||||
|
|
||||||
const isAvatarNoteToSelf = isBoolean(isNoteToSelf)
|
const isAvatarNoteToSelf = isBoolean(isNoteToSelf)
|
||||||
|
@ -222,6 +229,7 @@ export const BaseConversationListItem: FunctionComponent<PropsType> =
|
||||||
{ [`${BASE_CLASS_NAME}--is-checkbox--disabled`]: disabled }
|
{ [`${BASE_CLASS_NAME}--is-checkbox--disabled`]: disabled }
|
||||||
)}
|
)}
|
||||||
data-id={identifier}
|
data-id={identifier}
|
||||||
|
data-testid={testId}
|
||||||
htmlFor={htmlId}
|
htmlFor={htmlId}
|
||||||
// `onClick` is will double-fire if we're enabled. We want it to fire when we're
|
// `onClick` is will double-fire if we're enabled. We want it to fire when we're
|
||||||
// disabled so we can show any "can't add contact" modals, etc. This won't
|
// disabled so we can show any "can't add contact" modals, etc. This won't
|
||||||
|
@ -242,6 +250,7 @@ export const BaseConversationListItem: FunctionComponent<PropsType> =
|
||||||
`${BASE_CLASS_NAME}--is-button`
|
`${BASE_CLASS_NAME}--is-button`
|
||||||
)}
|
)}
|
||||||
data-id={identifier}
|
data-id={identifier}
|
||||||
|
data-testid={testId}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
type="button"
|
type="button"
|
||||||
|
@ -252,7 +261,11 @@ export const BaseConversationListItem: FunctionComponent<PropsType> =
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={commonClassNames} data-id={identifier}>
|
<div
|
||||||
|
className={commonClassNames}
|
||||||
|
data-id={identifier}
|
||||||
|
data-testid={testId}
|
||||||
|
>
|
||||||
{contents}
|
{contents}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -30,6 +30,7 @@ export type PropsDataType = {
|
||||||
| 'acceptedMessageRequest'
|
| 'acceptedMessageRequest'
|
||||||
| 'avatarPath'
|
| 'avatarPath'
|
||||||
| 'color'
|
| 'color'
|
||||||
|
| 'groupId'
|
||||||
| 'id'
|
| 'id'
|
||||||
| 'isMe'
|
| 'isMe'
|
||||||
| 'phoneNumber'
|
| 'phoneNumber'
|
||||||
|
@ -38,6 +39,7 @@ export type PropsDataType = {
|
||||||
| 'title'
|
| 'title'
|
||||||
| 'type'
|
| 'type'
|
||||||
| 'unblurredAvatarPath'
|
| 'unblurredAvatarPath'
|
||||||
|
| 'uuid'
|
||||||
>;
|
>;
|
||||||
|
|
||||||
type PropsHousekeepingType = {
|
type PropsHousekeepingType = {
|
||||||
|
@ -59,6 +61,7 @@ export const ContactCheckbox: FunctionComponent<PropsType> = React.memo(
|
||||||
badge,
|
badge,
|
||||||
color,
|
color,
|
||||||
disabledReason,
|
disabledReason,
|
||||||
|
groupId,
|
||||||
i18n,
|
i18n,
|
||||||
id,
|
id,
|
||||||
isChecked,
|
isChecked,
|
||||||
|
@ -71,6 +74,7 @@ export const ContactCheckbox: FunctionComponent<PropsType> = React.memo(
|
||||||
title,
|
title,
|
||||||
type,
|
type,
|
||||||
unblurredAvatarPath,
|
unblurredAvatarPath,
|
||||||
|
uuid,
|
||||||
}) {
|
}) {
|
||||||
const disabled = Boolean(disabledReason);
|
const disabled = Boolean(disabledReason);
|
||||||
|
|
||||||
|
@ -104,6 +108,7 @@ export const ContactCheckbox: FunctionComponent<PropsType> = React.memo(
|
||||||
color={color}
|
color={color}
|
||||||
conversationType={type}
|
conversationType={type}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
|
groupId={groupId}
|
||||||
headerName={headerName}
|
headerName={headerName}
|
||||||
i18n={i18n}
|
i18n={i18n}
|
||||||
id={id}
|
id={id}
|
||||||
|
@ -117,6 +122,7 @@ export const ContactCheckbox: FunctionComponent<PropsType> = React.memo(
|
||||||
theme={theme}
|
theme={theme}
|
||||||
title={title}
|
title={title}
|
||||||
unblurredAvatarPath={unblurredAvatarPath}
|
unblurredAvatarPath={unblurredAvatarPath}
|
||||||
|
uuid={uuid}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@ export type ContactListItemConversationType = Pick<
|
||||||
| 'avatarPath'
|
| 'avatarPath'
|
||||||
| 'badges'
|
| 'badges'
|
||||||
| 'color'
|
| 'color'
|
||||||
|
| 'groupId'
|
||||||
| 'id'
|
| 'id'
|
||||||
| 'isMe'
|
| 'isMe'
|
||||||
| 'phoneNumber'
|
| 'phoneNumber'
|
||||||
|
@ -54,6 +55,7 @@ export const ContactListItem: FunctionComponent<PropsType> = React.memo(
|
||||||
avatarPath,
|
avatarPath,
|
||||||
badge,
|
badge,
|
||||||
color,
|
color,
|
||||||
|
groupId,
|
||||||
i18n,
|
i18n,
|
||||||
id,
|
id,
|
||||||
isMe,
|
isMe,
|
||||||
|
@ -89,6 +91,7 @@ export const ContactListItem: FunctionComponent<PropsType> = React.memo(
|
||||||
badge={badge}
|
badge={badge}
|
||||||
color={color}
|
color={color}
|
||||||
conversationType={type}
|
conversationType={type}
|
||||||
|
groupId={groupId}
|
||||||
headerName={headerName}
|
headerName={headerName}
|
||||||
i18n={i18n}
|
i18n={i18n}
|
||||||
id={id}
|
id={id}
|
||||||
|
@ -102,6 +105,7 @@ export const ContactListItem: FunctionComponent<PropsType> = React.memo(
|
||||||
theme={theme}
|
theme={theme}
|
||||||
title={title}
|
title={title}
|
||||||
unblurredAvatarPath={unblurredAvatarPath}
|
unblurredAvatarPath={unblurredAvatarPath}
|
||||||
|
uuid={uuid}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,6 +41,7 @@ export type PropsData = Pick<
|
||||||
| 'badges'
|
| 'badges'
|
||||||
| 'color'
|
| 'color'
|
||||||
| 'draftPreview'
|
| 'draftPreview'
|
||||||
|
| 'groupId'
|
||||||
| 'id'
|
| 'id'
|
||||||
| 'isMe'
|
| 'isMe'
|
||||||
// NOTE: Passed for CI, not used for rendering
|
// NOTE: Passed for CI, not used for rendering
|
||||||
|
@ -79,6 +80,7 @@ export const ConversationListItem: FunctionComponent<Props> = React.memo(
|
||||||
badge,
|
badge,
|
||||||
color,
|
color,
|
||||||
draftPreview,
|
draftPreview,
|
||||||
|
groupId,
|
||||||
i18n,
|
i18n,
|
||||||
id,
|
id,
|
||||||
isMe,
|
isMe,
|
||||||
|
@ -180,6 +182,7 @@ export const ConversationListItem: FunctionComponent<Props> = React.memo(
|
||||||
badge={badge}
|
badge={badge}
|
||||||
color={color}
|
color={color}
|
||||||
conversationType={type}
|
conversationType={type}
|
||||||
|
groupId={groupId}
|
||||||
headerDate={lastUpdated}
|
headerDate={lastUpdated}
|
||||||
headerName={headerName}
|
headerName={headerName}
|
||||||
i18n={i18n}
|
i18n={i18n}
|
||||||
|
@ -198,6 +201,7 @@ export const ConversationListItem: FunctionComponent<Props> = React.memo(
|
||||||
title={title}
|
title={title}
|
||||||
unreadCount={unreadCount}
|
unreadCount={unreadCount}
|
||||||
unblurredAvatarPath={unblurredAvatarPath}
|
unblurredAvatarPath={unblurredAvatarPath}
|
||||||
|
uuid={uuid}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,7 @@ export const CreateNewGroupButton: FunctionComponent<PropsType> = React.memo(
|
||||||
isSelected={false}
|
isSelected={false}
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
sharedGroupNames={[]}
|
sharedGroupNames={[]}
|
||||||
|
testId="CreateNewGroupButton"
|
||||||
title={title}
|
title={title}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
|
@ -82,7 +82,12 @@ export const StickerManager = React.memo(function StickerManagerInner({
|
||||||
uninstallStickerPack={uninstallStickerPack}
|
uninstallStickerPack={uninstallStickerPack}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
<div className="module-sticker-manager" tabIndex={-1} ref={focusRef}>
|
<div
|
||||||
|
className="module-sticker-manager"
|
||||||
|
data-testid="StickerManager"
|
||||||
|
tabIndex={-1}
|
||||||
|
ref={focusRef}
|
||||||
|
>
|
||||||
<Tabs
|
<Tabs
|
||||||
initialSelectedTab={TabViews.Available}
|
initialSelectedTab={TabViews.Available}
|
||||||
tabs={[
|
tabs={[
|
||||||
|
|
|
@ -113,6 +113,7 @@ export const StickerManagerPackRow = React.memo(
|
||||||
onKeyDown={handleKeyDown}
|
onKeyDown={handleKeyDown}
|
||||||
onClick={handleClickPreview}
|
onClick={handleClickPreview}
|
||||||
className="module-sticker-manager__pack-row"
|
className="module-sticker-manager__pack-row"
|
||||||
|
data-testid={id}
|
||||||
>
|
>
|
||||||
{pack.cover ? (
|
{pack.cover ? (
|
||||||
<img
|
<img
|
||||||
|
|
18
ts/groups.ts
18
ts/groups.ts
|
@ -2137,7 +2137,10 @@ export async function getGroupMigrationMembers(
|
||||||
`getGroupMigrationMembers/${logId}: membersV2 - missing local contact for ${e164}, skipping.`
|
`getGroupMigrationMembers/${logId}: membersV2 - missing local contact for ${e164}, skipping.`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (!isMe(contact.attributes) && window.GV2_MIGRATION_DISABLE_ADD) {
|
if (
|
||||||
|
!isMe(contact.attributes) &&
|
||||||
|
window.Flags.GV2_MIGRATION_DISABLE_ADD
|
||||||
|
) {
|
||||||
log.warn(
|
log.warn(
|
||||||
`getGroupMigrationMembers/${logId}: membersV2 - skipping ${e164} due to GV2_MIGRATION_DISABLE_ADD flag`
|
`getGroupMigrationMembers/${logId}: membersV2 - skipping ${e164} due to GV2_MIGRATION_DISABLE_ADD flag`
|
||||||
);
|
);
|
||||||
|
@ -2205,7 +2208,10 @@ export async function getGroupMigrationMembers(
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isMe(contact.attributes) && window.GV2_MIGRATION_DISABLE_INVITE) {
|
if (
|
||||||
|
!isMe(contact.attributes) &&
|
||||||
|
window.Flags.GV2_MIGRATION_DISABLE_INVITE
|
||||||
|
) {
|
||||||
log.warn(
|
log.warn(
|
||||||
`getGroupMigrationMembers/${logId}: pendingMembersV2 - skipping ${e164} due to GV2_MIGRATION_DISABLE_INVITE flag`
|
`getGroupMigrationMembers/${logId}: pendingMembersV2 - skipping ${e164} due to GV2_MIGRATION_DISABLE_INVITE flag`
|
||||||
);
|
);
|
||||||
|
@ -3402,7 +3408,7 @@ async function getGroupUpdates({
|
||||||
newRevision === currentRevision + 1;
|
newRevision === currentRevision + 1;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
window.GV2_ENABLE_SINGLE_CHANGE_PROCESSING &&
|
window.Flags.GV2_ENABLE_SINGLE_CHANGE_PROCESSING &&
|
||||||
wrappedGroupChange &&
|
wrappedGroupChange &&
|
||||||
isNumber(newRevision) &&
|
isNumber(newRevision) &&
|
||||||
(isInitialCreationMessage || weAreAwaitingApproval || isOneVersionUp)
|
(isInitialCreationMessage || weAreAwaitingApproval || isOneVersionUp)
|
||||||
|
@ -3455,7 +3461,7 @@ async function getGroupUpdates({
|
||||||
|
|
||||||
if (
|
if (
|
||||||
(!isFirstFetch || isNumber(newRevision)) &&
|
(!isFirstFetch || isNumber(newRevision)) &&
|
||||||
window.GV2_ENABLE_CHANGE_PROCESSING
|
window.Flags.GV2_ENABLE_CHANGE_PROCESSING
|
||||||
) {
|
) {
|
||||||
try {
|
try {
|
||||||
return await updateGroupViaLogs({
|
return await updateGroupViaLogs({
|
||||||
|
@ -3483,7 +3489,7 @@ async function getGroupUpdates({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (window.GV2_ENABLE_STATE_PROCESSING) {
|
if (window.Flags.GV2_ENABLE_STATE_PROCESSING) {
|
||||||
try {
|
try {
|
||||||
return await updateGroupViaState({
|
return await updateGroupViaState({
|
||||||
dropInitialJoinMessage,
|
dropInitialJoinMessage,
|
||||||
|
@ -3506,7 +3512,7 @@ async function getGroupUpdates({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (window.GV2_ENABLE_PRE_JOIN_FETCH) {
|
if (window.Flags.GV2_ENABLE_PRE_JOIN_FETCH) {
|
||||||
try {
|
try {
|
||||||
return await updateGroupViaPreJoinInfo({
|
return await updateGroupViaPreJoinInfo({
|
||||||
group,
|
group,
|
||||||
|
|
|
@ -1,14 +0,0 @@
|
||||||
// Copyright 2021 Signal Messenger, LLC
|
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
|
||||||
|
|
||||||
{
|
|
||||||
const updateFullScreenClass = (
|
|
||||||
isFullScreen: boolean,
|
|
||||||
isMaximized: boolean
|
|
||||||
) => {
|
|
||||||
document.body.classList.toggle('full-screen', isFullScreen);
|
|
||||||
document.body.classList.toggle('maximized', isMaximized);
|
|
||||||
};
|
|
||||||
updateFullScreenClass(window.isFullScreen(), window.isMaximized());
|
|
||||||
window.onFullScreenChange = updateFullScreenClass;
|
|
||||||
}
|
|
|
@ -266,7 +266,9 @@ export class MessageReceipts extends Collection<MessageReceiptModel> {
|
||||||
|
|
||||||
// We want the above call to not be delayed when testing with
|
// We want the above call to not be delayed when testing with
|
||||||
// CI.
|
// CI.
|
||||||
window.CI ? deleteSentProtoBatcher.flushAndWait() : Promise.resolve(),
|
window.Signal.CI
|
||||||
|
? deleteSentProtoBatcher.flushAndWait()
|
||||||
|
: Promise.resolve(),
|
||||||
]);
|
]);
|
||||||
} else {
|
} else {
|
||||||
log.warn(
|
log.warn(
|
||||||
|
|
|
@ -11,6 +11,7 @@ import { isMessageUnread } from '../util/isMessageUnread';
|
||||||
import { notificationService } from '../services/notifications';
|
import { notificationService } from '../services/notifications';
|
||||||
import * as log from '../logging/log';
|
import * as log from '../logging/log';
|
||||||
import * as Errors from '../types/errors';
|
import * as Errors from '../types/errors';
|
||||||
|
import { StartupQueue } from '../util/StartupQueue';
|
||||||
|
|
||||||
export type ReadSyncAttributesType = {
|
export type ReadSyncAttributesType = {
|
||||||
senderId: string;
|
senderId: string;
|
||||||
|
@ -119,10 +120,10 @@ export class ReadSyncs extends Collection {
|
||||||
void message.getConversation()?.onReadMessage(message, readAt);
|
void message.getConversation()?.onReadMessage(message, readAt);
|
||||||
};
|
};
|
||||||
|
|
||||||
if (window.startupProcessingQueue) {
|
if (StartupQueue.isReady()) {
|
||||||
const conversation = message.getConversation();
|
const conversation = message.getConversation();
|
||||||
if (conversation) {
|
if (conversation) {
|
||||||
window.startupProcessingQueue.add(
|
StartupQueue.add(
|
||||||
conversation.get('id'),
|
conversation.get('id'),
|
||||||
message.get('sent_at'),
|
message.get('sent_at'),
|
||||||
updateConversation
|
updateConversation
|
||||||
|
|
|
@ -158,6 +158,7 @@ import { isSignalConversation } from '../util/isSignalConversation';
|
||||||
import { isMemberRequestingToJoin } from '../util/isMemberRequestingToJoin';
|
import { isMemberRequestingToJoin } from '../util/isMemberRequestingToJoin';
|
||||||
import { removePendingMember } from '../util/removePendingMember';
|
import { removePendingMember } from '../util/removePendingMember';
|
||||||
import { isMemberPending } from '../util/isMemberPending';
|
import { isMemberPending } from '../util/isMemberPending';
|
||||||
|
import { imageToBlurHash } from '../util/imageToBlurHash';
|
||||||
|
|
||||||
const EMPTY_ARRAY: Readonly<[]> = [];
|
const EMPTY_ARRAY: Readonly<[]> = [];
|
||||||
const EMPTY_GROUP_COLLISIONS: GroupNameCollisionsWithIdsByTitle = {};
|
const EMPTY_GROUP_COLLISIONS: GroupNameCollisionsWithIdsByTitle = {};
|
||||||
|
@ -3890,7 +3891,7 @@ export class ConversationModel extends window.Backbone
|
||||||
contentType,
|
contentType,
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
blurHash: await window.imageToBlurHash(
|
blurHash: await imageToBlurHash(
|
||||||
new Blob([data], {
|
new Blob([data], {
|
||||||
type: IMAGE_JPEG,
|
type: IMAGE_JPEG,
|
||||||
})
|
})
|
||||||
|
@ -5527,7 +5528,7 @@ export class ConversationModel extends window.Backbone
|
||||||
const delta = now - startedAt;
|
const delta = now - startedAt;
|
||||||
|
|
||||||
log.info(`conversation ${this.idForLogging()} open took ${delta}ms`);
|
log.info(`conversation ${this.idForLogging()} open took ${delta}ms`);
|
||||||
window.CI?.handleEvent('conversation:open', { delta });
|
window.Signal.CI?.handleEvent('conversation:open', { delta });
|
||||||
}
|
}
|
||||||
|
|
||||||
async flushDebouncedUpdates(): Promise<void> {
|
async flushDebouncedUpdates(): Promise<void> {
|
||||||
|
|
|
@ -173,6 +173,10 @@ import { GiftBadgeStates } from '../components/conversation/Message';
|
||||||
import { downloadAttachment } from '../util/downloadAttachment';
|
import { downloadAttachment } from '../util/downloadAttachment';
|
||||||
import type { StickerWithHydratedData } from '../types/Stickers';
|
import type { StickerWithHydratedData } from '../types/Stickers';
|
||||||
import { getStringForConversationMerge } from '../util/getStringForConversationMerge';
|
import { getStringForConversationMerge } from '../util/getStringForConversationMerge';
|
||||||
|
import {
|
||||||
|
addToAttachmentDownloadQueue,
|
||||||
|
shouldUseAttachmentDownloadQueue,
|
||||||
|
} from '../util/attachmentDownloadQueue';
|
||||||
import { getTitleNoDefault, getNumber } from '../util/getTitle';
|
import { getTitleNoDefault, getNumber } from '../util/getTitle';
|
||||||
import dataInterface from '../sql/Client';
|
import dataInterface from '../sql/Client';
|
||||||
|
|
||||||
|
@ -346,6 +350,10 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
|
||||||
}
|
}
|
||||||
|
|
||||||
notifyRedux(): void {
|
notifyRedux(): void {
|
||||||
|
if (!window.reduxActions) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const { storyChanged } = window.reduxActions.stories;
|
const { storyChanged } = window.reduxActions.stories;
|
||||||
|
|
||||||
if (isStory(this.attributes)) {
|
if (isStory(this.attributes)) {
|
||||||
|
@ -2989,12 +2997,8 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
|
||||||
(conversation.getAccepted() || isOutgoing(message.attributes)) &&
|
(conversation.getAccepted() || isOutgoing(message.attributes)) &&
|
||||||
!shouldHoldOffDownload
|
!shouldHoldOffDownload
|
||||||
) {
|
) {
|
||||||
if (window.attachmentDownloadQueue) {
|
if (shouldUseAttachmentDownloadQueue()) {
|
||||||
window.attachmentDownloadQueue.unshift(message);
|
addToAttachmentDownloadQueue(idLog, message);
|
||||||
log.info(
|
|
||||||
`${idLog}: Adding to attachmentDownloadQueue`,
|
|
||||||
message.get('sent_at')
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
await message.queueAttachmentDownloads();
|
await message.queueAttachmentDownloads();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,30 +1,10 @@
|
||||||
// Copyright 2021 Signal Messenger, LLC
|
// Copyright 2021 Signal Messenger, LLC
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
import { basename, join } from 'path';
|
import { join } from 'path';
|
||||||
import { copyFileSync, readFileSync, writeFileSync } from 'fs';
|
import { copyFileSync } from 'fs';
|
||||||
|
|
||||||
// Concat
|
|
||||||
|
|
||||||
console.log('Concatenating...');
|
|
||||||
|
|
||||||
const BASE_BOWER = join(__dirname, '../../components');
|
const BASE_BOWER = join(__dirname, '../../components');
|
||||||
const CONCAT_TARGET = join(__dirname, '../../js/components.js');
|
|
||||||
const CONCAT_SOURCES = [
|
|
||||||
join(BASE_BOWER, 'webaudiorecorder/lib/WebAudioRecorder.js'),
|
|
||||||
];
|
|
||||||
|
|
||||||
let concat = '// concatenated components.js';
|
|
||||||
CONCAT_SOURCES.forEach(source => {
|
|
||||||
const contents = readFileSync(source, 'utf8');
|
|
||||||
const name = basename(source);
|
|
||||||
|
|
||||||
console.log(`Concatenating ${source}`);
|
|
||||||
concat += `\n\n// ${name}\n${contents}`;
|
|
||||||
});
|
|
||||||
|
|
||||||
console.log(`Writing to ${CONCAT_TARGET}`);
|
|
||||||
writeFileSync(CONCAT_TARGET, concat);
|
|
||||||
|
|
||||||
// Copy
|
// Copy
|
||||||
|
|
|
@ -24,6 +24,7 @@ import { SECOND } from '../util/durations';
|
||||||
import { autoScale } from '../util/handleImageAttachment';
|
import { autoScale } from '../util/handleImageAttachment';
|
||||||
import { dropNull } from '../util/dropNull';
|
import { dropNull } from '../util/dropNull';
|
||||||
import { fileToBytes } from '../util/fileToBytes';
|
import { fileToBytes } from '../util/fileToBytes';
|
||||||
|
import { imageToBlurHash } from '../util/imageToBlurHash';
|
||||||
import { maybeParseUrl } from '../util/url';
|
import { maybeParseUrl } from '../util/url';
|
||||||
import { sniffImageMimeType } from '../util/sniffImageMimeType';
|
import { sniffImageMimeType } from '../util/sniffImageMimeType';
|
||||||
import { drop } from '../util/drop';
|
import { drop } from '../util/drop';
|
||||||
|
@ -343,7 +344,7 @@ async function getPreview(
|
||||||
const data = await fileToBytes(withBlob.file);
|
const data = await fileToBytes(withBlob.file);
|
||||||
objectUrl = URL.createObjectURL(withBlob.file);
|
objectUrl = URL.createObjectURL(withBlob.file);
|
||||||
|
|
||||||
const blurHash = await window.imageToBlurHash(withBlob.file);
|
const blurHash = await imageToBlurHash(withBlob.file);
|
||||||
|
|
||||||
const dimensions = await VisualAttachment.getImageDimensions({
|
const dimensions = await VisualAttachment.getImageDimensions({
|
||||||
objectUrl,
|
objectUrl,
|
||||||
|
@ -535,7 +536,7 @@ async function getGroupPreview(
|
||||||
data,
|
data,
|
||||||
size: data.byteLength,
|
size: data.byteLength,
|
||||||
contentType: IMAGE_JPEG,
|
contentType: IMAGE_JPEG,
|
||||||
blurHash: await window.imageToBlurHash(
|
blurHash: await imageToBlurHash(
|
||||||
new Blob([data], {
|
new Blob([data], {
|
||||||
type: IMAGE_JPEG,
|
type: IMAGE_JPEG,
|
||||||
})
|
})
|
||||||
|
|
|
@ -3,13 +3,13 @@
|
||||||
|
|
||||||
import { requestMicrophonePermissions } from '../util/requestMicrophonePermissions';
|
import { requestMicrophonePermissions } from '../util/requestMicrophonePermissions';
|
||||||
import * as log from '../logging/log';
|
import * as log from '../logging/log';
|
||||||
import type { WebAudioRecorderClass } from '../window.d';
|
import type { WebAudioRecorder } from '../WebAudioRecorder';
|
||||||
import * as Errors from '../types/errors';
|
import * as Errors from '../types/errors';
|
||||||
|
|
||||||
export class RecorderClass {
|
export class RecorderClass {
|
||||||
private context?: AudioContext;
|
private context?: AudioContext;
|
||||||
private input?: GainNode;
|
private input?: GainNode;
|
||||||
private recorder?: WebAudioRecorderClass;
|
private recorder?: WebAudioRecorder;
|
||||||
private source?: MediaStreamAudioSourceNode;
|
private source?: MediaStreamAudioSourceNode;
|
||||||
private stream?: MediaStream;
|
private stream?: MediaStream;
|
||||||
private blob?: Blob;
|
private blob?: Blob;
|
||||||
|
@ -31,7 +31,7 @@ export class RecorderClass {
|
||||||
|
|
||||||
// Reach in and terminate the web worker used by WebAudioRecorder, otherwise
|
// Reach in and terminate the web worker used by WebAudioRecorder, otherwise
|
||||||
// it gets leaked due to a reference cycle with its onmessage listener
|
// it gets leaked due to a reference cycle with its onmessage listener
|
||||||
this.recorder.worker.terminate();
|
this.recorder.worker?.terminate();
|
||||||
this.recorder = undefined;
|
this.recorder = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,15 +58,16 @@ export class RecorderClass {
|
||||||
this.context = new AudioContext();
|
this.context = new AudioContext();
|
||||||
this.input = this.context.createGain();
|
this.input = this.context.createGain();
|
||||||
|
|
||||||
this.recorder = new window.WebAudioRecorder(this.input, {
|
this.recorder = new window.WebAudioRecorder(
|
||||||
encoding: 'mp3',
|
this.input,
|
||||||
workerDir: 'js/', // must end with slash
|
{
|
||||||
options: {
|
|
||||||
timeLimit: 60 + 3600, // one minute more than our UI-imposed limit
|
timeLimit: 60 + 3600, // one minute more than our UI-imposed limit
|
||||||
},
|
},
|
||||||
});
|
{
|
||||||
this.recorder.onComplete = this.onComplete.bind(this);
|
onComplete: this.onComplete.bind(this),
|
||||||
this.recorder.onError = this.onError.bind(this);
|
onError: this.onError.bind(this),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const stream = await navigator.mediaDevices.getUserMedia({
|
const stream = await navigator.mediaDevices.getUserMedia({
|
||||||
|
@ -78,7 +79,7 @@ export class RecorderClass {
|
||||||
const err = new Error(
|
const err = new Error(
|
||||||
'Recorder/getUserMedia/stream: Missing context or input!'
|
'Recorder/getUserMedia/stream: Missing context or input!'
|
||||||
);
|
);
|
||||||
this.onError(this.recorder, err);
|
this.onError(this.recorder, String(err));
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
this.source = this.context.createMediaStreamSource(stream);
|
this.source = this.context.createMediaStreamSource(stream);
|
||||||
|
@ -120,12 +121,12 @@ export class RecorderClass {
|
||||||
return promise;
|
return promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
onComplete(_recorder: WebAudioRecorderClass, blob: Blob): void {
|
onComplete(_recorder: WebAudioRecorder, blob: Blob): void {
|
||||||
this.blob = blob;
|
this.blob = blob;
|
||||||
this.resolve?.(blob);
|
this.resolve?.(blob);
|
||||||
}
|
}
|
||||||
|
|
||||||
onError(_recorder: WebAudioRecorderClass, error: Error): void {
|
onError(_recorder: WebAudioRecorder, error: string): void {
|
||||||
if (!this.recorder) {
|
if (!this.recorder) {
|
||||||
log.warn('Recorder/onError: Called with no recorder');
|
log.warn('Recorder/onError: Called with no recorder');
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -1620,12 +1620,12 @@ export class CallingClass {
|
||||||
}
|
}
|
||||||
|
|
||||||
private async requestCameraPermissions(): Promise<boolean> {
|
private async requestCameraPermissions(): Promise<boolean> {
|
||||||
const cameraPermission = await window.getMediaCameraPermissions();
|
const cameraPermission = await window.IPC.getMediaCameraPermissions();
|
||||||
if (!cameraPermission) {
|
if (!cameraPermission) {
|
||||||
await window.showPermissionsPopup(true, true);
|
await window.IPC.showPermissionsPopup(true, true);
|
||||||
|
|
||||||
// Check the setting again (from the source of truth).
|
// Check the setting again (from the source of truth).
|
||||||
return window.getMediaCameraPermissions();
|
return window.IPC.getMediaCameraPermissions();
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -260,7 +260,7 @@ class NotificationService extends EventEmitter {
|
||||||
);
|
);
|
||||||
if (shouldDrawAttention) {
|
if (shouldDrawAttention) {
|
||||||
log.info('NotificationService: drawing attention');
|
log.info('NotificationService: drawing attention');
|
||||||
window.drawAttention();
|
window.IPC.drawAttention();
|
||||||
}
|
}
|
||||||
|
|
||||||
let notificationTitle: string;
|
let notificationTitle: string;
|
||||||
|
|
|
@ -1733,8 +1733,8 @@ async function sync(
|
||||||
// We now know that we've successfully completed a storage service fetch
|
// We now know that we've successfully completed a storage service fetch
|
||||||
await window.storage.put('storageFetchComplete', true);
|
await window.storage.put('storageFetchComplete', true);
|
||||||
|
|
||||||
if (window.CI) {
|
if (window.Signal.CI) {
|
||||||
window.CI.handleEvent('storageServiceComplete', {
|
window.Signal.CI.handleEvent('storageServiceComplete', {
|
||||||
manifestVersion: version,
|
manifestVersion: version,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,21 +0,0 @@
|
||||||
// Copyright 2021 Signal Messenger, LLC
|
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
|
||||||
|
|
||||||
{
|
|
||||||
let className: string;
|
|
||||||
if (window.SignalContext.OS.isWindows()) {
|
|
||||||
className = 'os-windows';
|
|
||||||
} else if (window.SignalContext.OS.isMacOS()) {
|
|
||||||
className = 'os-macos';
|
|
||||||
} else if (window.SignalContext.OS.isLinux()) {
|
|
||||||
className = 'os-linux';
|
|
||||||
} else {
|
|
||||||
throw new Error('Unexpected operating system; not applying ');
|
|
||||||
}
|
|
||||||
|
|
||||||
document.body.classList.add(className);
|
|
||||||
|
|
||||||
if (window.SignalContext.OS.hasCustomTitleBar()) {
|
|
||||||
document.body.classList.add('os-has-custom-titlebar');
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -2,5 +2,5 @@
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
export function showSettings(): void {
|
export function showSettings(): void {
|
||||||
window.showSettings();
|
window.IPC.showSettings();
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,5 +32,5 @@ export async function deleteAllData(): Promise<void> {
|
||||||
Errors.toLogFormat(error)
|
Errors.toLogFormat(error)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
window.restart();
|
window.IPC.restart();
|
||||||
}
|
}
|
||||||
|
|
53
ts/signal.ts
53
ts/signal.ts
|
@ -13,36 +13,13 @@ import * as RemoteConfig from './RemoteConfig';
|
||||||
import * as Util from './util';
|
import * as Util from './util';
|
||||||
|
|
||||||
// Components
|
// Components
|
||||||
import { AttachmentList } from './components/conversation/AttachmentList';
|
|
||||||
import { ChatColorPicker } from './components/ChatColorPicker';
|
|
||||||
import { ConfirmationDialog } from './components/ConfirmationDialog';
|
import { ConfirmationDialog } from './components/ConfirmationDialog';
|
||||||
import { ContactModal } from './components/conversation/ContactModal';
|
|
||||||
import { Emojify } from './components/conversation/Emojify';
|
|
||||||
import { MessageDetail } from './components/conversation/MessageDetail';
|
|
||||||
import { Quote } from './components/conversation/Quote';
|
|
||||||
import { StagedLinkPreview } from './components/conversation/StagedLinkPreview';
|
|
||||||
import { DisappearingTimeDialog } from './components/DisappearingTimeDialog';
|
|
||||||
|
|
||||||
// State
|
// State
|
||||||
import { createApp } from './state/roots/createApp';
|
import { createApp } from './state/roots/createApp';
|
||||||
import { createSafetyNumberViewer } from './state/roots/createSafetyNumberViewer';
|
import { createSafetyNumberViewer } from './state/roots/createSafetyNumberViewer';
|
||||||
|
|
||||||
import { createStore } from './state/createStore';
|
import { createStore } from './state/createStore';
|
||||||
import * as appDuck from './state/ducks/app';
|
|
||||||
import * as callingDuck from './state/ducks/calling';
|
|
||||||
import * as conversationsDuck from './state/ducks/conversations';
|
|
||||||
import * as emojisDuck from './state/ducks/emojis';
|
|
||||||
import * as expirationDuck from './state/ducks/expiration';
|
|
||||||
import * as itemsDuck from './state/ducks/items';
|
|
||||||
import * as linkPreviewsDuck from './state/ducks/linkPreviews';
|
|
||||||
import * as networkDuck from './state/ducks/network';
|
|
||||||
import * as searchDuck from './state/ducks/search';
|
|
||||||
import * as stickersDuck from './state/ducks/stickers';
|
|
||||||
import * as updatesDuck from './state/ducks/updates';
|
|
||||||
import * as userDuck from './state/ducks/user';
|
|
||||||
|
|
||||||
import * as conversationsSelectors from './state/selectors/conversations';
|
|
||||||
import * as searchSelectors from './state/selectors/search';
|
|
||||||
|
|
||||||
// Types
|
// Types
|
||||||
import * as TypesAttachment from './types/Attachment';
|
import * as TypesAttachment from './types/Attachment';
|
||||||
|
@ -378,15 +355,7 @@ export const setup = (options: {
|
||||||
});
|
});
|
||||||
|
|
||||||
const Components = {
|
const Components = {
|
||||||
AttachmentList,
|
|
||||||
ChatColorPicker,
|
|
||||||
ConfirmationDialog,
|
ConfirmationDialog,
|
||||||
ContactModal,
|
|
||||||
Emojify,
|
|
||||||
MessageDetail,
|
|
||||||
Quote,
|
|
||||||
StagedLinkPreview,
|
|
||||||
DisappearingTimeDialog,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const Roots = {
|
const Roots = {
|
||||||
|
@ -394,26 +363,6 @@ export const setup = (options: {
|
||||||
createSafetyNumberViewer,
|
createSafetyNumberViewer,
|
||||||
};
|
};
|
||||||
|
|
||||||
const Ducks = {
|
|
||||||
app: appDuck,
|
|
||||||
calling: callingDuck,
|
|
||||||
conversations: conversationsDuck,
|
|
||||||
emojis: emojisDuck,
|
|
||||||
expiration: expirationDuck,
|
|
||||||
items: itemsDuck,
|
|
||||||
linkPreviews: linkPreviewsDuck,
|
|
||||||
network: networkDuck,
|
|
||||||
updates: updatesDuck,
|
|
||||||
user: userDuck,
|
|
||||||
search: searchDuck,
|
|
||||||
stickers: stickersDuck,
|
|
||||||
};
|
|
||||||
|
|
||||||
const Selectors = {
|
|
||||||
conversations: conversationsSelectors,
|
|
||||||
search: searchSelectors,
|
|
||||||
};
|
|
||||||
|
|
||||||
const Services = {
|
const Services = {
|
||||||
calling,
|
calling,
|
||||||
initializeGroupCredentialFetcher,
|
initializeGroupCredentialFetcher,
|
||||||
|
@ -427,8 +376,6 @@ export const setup = (options: {
|
||||||
const State = {
|
const State = {
|
||||||
createStore,
|
createStore,
|
||||||
Roots,
|
Roots,
|
||||||
Ducks,
|
|
||||||
Selectors,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const Types = {
|
const Types = {
|
||||||
|
|
|
@ -85,7 +85,7 @@ function openInstaller(): ThunkAction<
|
||||||
OpenInstallerActionType
|
OpenInstallerActionType
|
||||||
> {
|
> {
|
||||||
return dispatch => {
|
return dispatch => {
|
||||||
window.addSetupMenuItems();
|
window.IPC.addSetupMenuItems();
|
||||||
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: OPEN_INSTALLER,
|
type: OPEN_INSTALLER,
|
||||||
|
@ -104,7 +104,7 @@ function openStandalone(): ThunkAction<
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
window.addSetupMenuItems();
|
window.IPC.addSetupMenuItems();
|
||||||
dispatch({
|
dispatch({
|
||||||
type: OPEN_STANDALONE,
|
type: OPEN_STANDALONE,
|
||||||
});
|
});
|
||||||
|
|
|
@ -44,11 +44,11 @@ function setCrashReportCount(count: number): SetCrashReportCountActionType {
|
||||||
}
|
}
|
||||||
|
|
||||||
function uploadCrashReports(): PromiseAction<typeof UPLOAD> {
|
function uploadCrashReports(): PromiseAction<typeof UPLOAD> {
|
||||||
return { type: UPLOAD, payload: window.crashReports.upload() };
|
return { type: UPLOAD, payload: window.IPC.crashReports.upload() };
|
||||||
}
|
}
|
||||||
|
|
||||||
function eraseCrashReports(): PromiseAction<typeof ERASE> {
|
function eraseCrashReports(): PromiseAction<typeof ERASE> {
|
||||||
return { type: ERASE, payload: window.crashReports.erase() };
|
return { type: ERASE, payload: window.IPC.crashReports.erase() };
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reducer
|
// Reducer
|
||||||
|
|
|
@ -3,32 +3,34 @@
|
||||||
|
|
||||||
import { trigger } from '../../shims/events';
|
import { trigger } from '../../shims/events';
|
||||||
|
|
||||||
import type { NoopActionType } from './noop';
|
|
||||||
import type { LocalizerType } from '../../types/Util';
|
|
||||||
import type { LocaleMessagesType } from '../../types/I18N';
|
import type { LocaleMessagesType } from '../../types/I18N';
|
||||||
import { ThemeType } from '../../types/Util';
|
import type { LocalizerType } from '../../types/Util';
|
||||||
import type { UUIDStringType } from '../../types/UUID';
|
|
||||||
import type { MenuOptionsType } from '../../types/menu';
|
import type { MenuOptionsType } from '../../types/menu';
|
||||||
|
import type { NoopActionType } from './noop';
|
||||||
|
import type { UUIDStringType } from '../../types/UUID';
|
||||||
|
import * as OS from '../../OS';
|
||||||
|
import { ThemeType } from '../../types/Util';
|
||||||
|
|
||||||
// State
|
// State
|
||||||
|
|
||||||
export type UserStateType = {
|
export type UserStateType = {
|
||||||
attachmentsPath: string;
|
attachmentsPath: string;
|
||||||
stickersPath: string;
|
i18n: LocalizerType;
|
||||||
tempPath: string;
|
interactionMode: 'mouse' | 'keyboard';
|
||||||
|
isMainWindowFullScreen: boolean;
|
||||||
|
isMainWindowMaximized: boolean;
|
||||||
|
localeMessages: LocaleMessagesType;
|
||||||
|
menuOptions: MenuOptionsType;
|
||||||
|
osName: 'linux' | 'macos' | 'windows' | undefined;
|
||||||
|
ourACI: UUIDStringType | undefined;
|
||||||
ourConversationId: string | undefined;
|
ourConversationId: string | undefined;
|
||||||
ourDeviceId: number | undefined;
|
ourDeviceId: number | undefined;
|
||||||
ourACI: UUIDStringType | undefined;
|
|
||||||
ourPNI: UUIDStringType | undefined;
|
|
||||||
ourNumber: string | undefined;
|
ourNumber: string | undefined;
|
||||||
|
ourPNI: UUIDStringType | undefined;
|
||||||
platform: string;
|
platform: string;
|
||||||
regionCode: string | undefined;
|
regionCode: string | undefined;
|
||||||
i18n: LocalizerType;
|
stickersPath: string;
|
||||||
localeMessages: LocaleMessagesType;
|
tempPath: string;
|
||||||
interactionMode: 'mouse' | 'keyboard';
|
|
||||||
isMainWindowMaximized: boolean;
|
|
||||||
isMainWindowFullScreen: boolean;
|
|
||||||
menuOptions: MenuOptionsType;
|
|
||||||
theme: ThemeType;
|
theme: ThemeType;
|
||||||
version: string;
|
version: string;
|
||||||
};
|
};
|
||||||
|
@ -96,20 +98,27 @@ const intlNotSetup = () => {
|
||||||
// Reducer
|
// Reducer
|
||||||
|
|
||||||
export function getEmptyState(): UserStateType {
|
export function getEmptyState(): UserStateType {
|
||||||
|
let osName: 'windows' | 'macos' | 'linux' | undefined;
|
||||||
|
|
||||||
|
if (OS.isWindows()) {
|
||||||
|
osName = 'windows';
|
||||||
|
} else if (OS.isMacOS()) {
|
||||||
|
osName = 'macos';
|
||||||
|
} else if (OS.isLinux()) {
|
||||||
|
osName = 'linux';
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
attachmentsPath: 'missing',
|
attachmentsPath: 'missing',
|
||||||
stickersPath: 'missing',
|
i18n: Object.assign(intlNotSetup, {
|
||||||
tempPath: 'missing',
|
getLocale: intlNotSetup,
|
||||||
ourConversationId: 'missing',
|
getIntl: intlNotSetup,
|
||||||
ourDeviceId: 0,
|
isLegacyFormat: intlNotSetup,
|
||||||
ourACI: undefined,
|
}),
|
||||||
ourPNI: undefined,
|
|
||||||
ourNumber: 'missing',
|
|
||||||
regionCode: 'missing',
|
|
||||||
platform: 'missing',
|
|
||||||
interactionMode: 'mouse',
|
interactionMode: 'mouse',
|
||||||
isMainWindowMaximized: false,
|
isMainWindowMaximized: false,
|
||||||
isMainWindowFullScreen: false,
|
isMainWindowFullScreen: false,
|
||||||
|
localeMessages: {},
|
||||||
menuOptions: {
|
menuOptions: {
|
||||||
development: false,
|
development: false,
|
||||||
devTools: false,
|
devTools: false,
|
||||||
|
@ -117,13 +126,17 @@ export function getEmptyState(): UserStateType {
|
||||||
isProduction: true,
|
isProduction: true,
|
||||||
platform: 'unknown',
|
platform: 'unknown',
|
||||||
},
|
},
|
||||||
|
osName,
|
||||||
|
ourACI: undefined,
|
||||||
|
ourConversationId: 'missing',
|
||||||
|
ourDeviceId: 0,
|
||||||
|
ourNumber: 'missing',
|
||||||
|
ourPNI: undefined,
|
||||||
|
platform: 'missing',
|
||||||
|
regionCode: 'missing',
|
||||||
|
stickersPath: 'missing',
|
||||||
|
tempPath: 'missing',
|
||||||
theme: ThemeType.light,
|
theme: ThemeType.light,
|
||||||
i18n: Object.assign(intlNotSetup, {
|
|
||||||
getLocale: intlNotSetup,
|
|
||||||
getIntl: intlNotSetup,
|
|
||||||
isLegacyFormat: intlNotSetup,
|
|
||||||
}),
|
|
||||||
localeMessages: {},
|
|
||||||
version: '0.0.0',
|
version: '0.0.0',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,15 +26,15 @@ import { getEmptyState as user } from './ducks/user';
|
||||||
import { getEmptyState as username } from './ducks/username';
|
import { getEmptyState as username } from './ducks/username';
|
||||||
|
|
||||||
import type { StateType } from './reducer';
|
import type { StateType } from './reducer';
|
||||||
|
|
||||||
import type { BadgesStateType } from './ducks/badges';
|
import type { BadgesStateType } from './ducks/badges';
|
||||||
|
import type { MainWindowStatsType } from '../windows/context';
|
||||||
|
import type { MenuOptionsType } from '../types/menu';
|
||||||
import type { StoryDataType } from './ducks/stories';
|
import type { StoryDataType } from './ducks/stories';
|
||||||
import type { StoryDistributionListDataType } from './ducks/storyDistributionLists';
|
import type { StoryDistributionListDataType } from './ducks/storyDistributionLists';
|
||||||
import { getInitialState as stickers } from '../types/Stickers';
|
import * as OS from '../OS';
|
||||||
import type { MenuOptionsType } from '../types/menu';
|
|
||||||
import { UUIDKind } from '../types/UUID';
|
import { UUIDKind } from '../types/UUID';
|
||||||
import { getEmojiReducerState as emojis } from '../util/loadRecentEmojis';
|
import { getEmojiReducerState as emojis } from '../util/loadRecentEmojis';
|
||||||
import type { MainWindowStatsType } from '../windows/context';
|
import { getInitialState as stickers } from '../types/Stickers';
|
||||||
import { getThemeType } from '../util/getThemeType';
|
import { getThemeType } from '../util/getThemeType';
|
||||||
|
|
||||||
export function getInitialState({
|
export function getInitialState({
|
||||||
|
@ -69,6 +69,16 @@ export function getInitialState({
|
||||||
|
|
||||||
const theme = getThemeType();
|
const theme = getThemeType();
|
||||||
|
|
||||||
|
let osName: 'windows' | 'macos' | 'linux' | undefined;
|
||||||
|
|
||||||
|
if (OS.isWindows()) {
|
||||||
|
osName = 'windows';
|
||||||
|
} else if (OS.isMacOS()) {
|
||||||
|
osName = 'macos';
|
||||||
|
} else if (OS.isLinux()) {
|
||||||
|
osName = 'linux';
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
accounts: accounts(),
|
accounts: accounts(),
|
||||||
app: app(),
|
app: app(),
|
||||||
|
@ -125,24 +135,25 @@ export function getInitialState({
|
||||||
updates: updates(),
|
updates: updates(),
|
||||||
user: {
|
user: {
|
||||||
...user(),
|
...user(),
|
||||||
attachmentsPath: window.baseAttachmentsPath,
|
attachmentsPath: window.BasePaths.attachments,
|
||||||
stickersPath: window.baseStickersPath,
|
i18n: window.i18n,
|
||||||
tempPath: window.baseTempPath,
|
interactionMode: window.getInteractionMode(),
|
||||||
regionCode: window.storage.get('regionCode'),
|
isMainWindowFullScreen: mainWindowStats.isFullScreen,
|
||||||
|
isMainWindowMaximized: mainWindowStats.isMaximized,
|
||||||
|
localeMessages: window.SignalContext.localeMessages,
|
||||||
|
menuOptions,
|
||||||
|
osName,
|
||||||
|
ourACI,
|
||||||
ourConversationId,
|
ourConversationId,
|
||||||
ourDeviceId,
|
ourDeviceId,
|
||||||
ourNumber,
|
ourNumber,
|
||||||
ourACI,
|
|
||||||
ourPNI,
|
ourPNI,
|
||||||
platform: window.platform,
|
platform: window.platform,
|
||||||
i18n: window.i18n,
|
regionCode: window.storage.get('regionCode'),
|
||||||
localeMessages: window.SignalContext.localeMessages,
|
stickersPath: window.BasePaths.stickers,
|
||||||
interactionMode: window.getInteractionMode(),
|
tempPath: window.BasePaths.temp,
|
||||||
theme,
|
theme,
|
||||||
version: window.getVersion(),
|
version: window.getVersion(),
|
||||||
isMainWindowMaximized: mainWindowStats.isMaximized,
|
|
||||||
isMainWindowFullScreen: mainWindowStats.isFullScreen,
|
|
||||||
menuOptions,
|
|
||||||
},
|
},
|
||||||
username: username(),
|
username: username(),
|
||||||
};
|
};
|
||||||
|
|
|
@ -38,6 +38,18 @@ function renderInbox(): JSX.Element {
|
||||||
const mapStateToProps = (state: StateType) => {
|
const mapStateToProps = (state: StateType) => {
|
||||||
const i18n = getIntl(state);
|
const i18n = getIntl(state);
|
||||||
|
|
||||||
|
const { osName } = state.user;
|
||||||
|
|
||||||
|
let osClassName = '';
|
||||||
|
|
||||||
|
if (osName === 'windows') {
|
||||||
|
osClassName = 'os-windows';
|
||||||
|
} else if (osName === 'macos') {
|
||||||
|
osClassName = 'os-macos';
|
||||||
|
} else if (osName === 'linux') {
|
||||||
|
osClassName = 'os-linux';
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...state.app,
|
...state.app,
|
||||||
i18n,
|
i18n,
|
||||||
|
@ -46,6 +58,7 @@ const mapStateToProps = (state: StateType) => {
|
||||||
isFullScreen: getIsMainWindowFullScreen(state),
|
isFullScreen: getIsMainWindowFullScreen(state),
|
||||||
menuOptions: getMenuOptions(state),
|
menuOptions: getMenuOptions(state),
|
||||||
hasCustomTitleBar: window.SignalContext.OS.hasCustomTitleBar(),
|
hasCustomTitleBar: window.SignalContext.OS.hasCustomTitleBar(),
|
||||||
|
osClassName,
|
||||||
hideMenuBar: getHideMenuBar(state),
|
hideMenuBar: getHideMenuBar(state),
|
||||||
renderCallManager: () => (
|
renderCallManager: () => (
|
||||||
<ModalContainer className="module-calling__modal-container">
|
<ModalContainer className="module-calling__modal-container">
|
||||||
|
@ -92,7 +105,7 @@ const mapStateToProps = (state: StateType) => {
|
||||||
void window.SignalContext.executeMenuAction(action);
|
void window.SignalContext.executeMenuAction(action);
|
||||||
},
|
},
|
||||||
titleBarDoubleClick: (): void => {
|
titleBarDoubleClick: (): void => {
|
||||||
window.titleBarDoubleClick();
|
window.IPC.titleBarDoubleClick();
|
||||||
},
|
},
|
||||||
toast: state.toast.toast,
|
toast: state.toast.toast,
|
||||||
};
|
};
|
||||||
|
|
|
@ -87,7 +87,7 @@ async function notifyForCall(
|
||||||
isVideoCall ? 'incomingVideoCall' : 'incomingAudioCall'
|
isVideoCall ? 'incomingVideoCall' : 'incomingAudioCall'
|
||||||
),
|
),
|
||||||
onNotificationClick: () => {
|
onNotificationClick: () => {
|
||||||
window.showWindow();
|
window.IPC.showWindow();
|
||||||
},
|
},
|
||||||
silent: false,
|
silent: false,
|
||||||
});
|
});
|
||||||
|
|
|
@ -164,8 +164,10 @@ export function SmartInstallScreen(): ReactElement {
|
||||||
}
|
}
|
||||||
onQrCodeScanned();
|
onQrCodeScanned();
|
||||||
|
|
||||||
if (window.CI) {
|
if (window.Signal.CI) {
|
||||||
chooseDeviceNamePromiseWrapperRef.current.resolve(window.CI.deviceName);
|
chooseDeviceNamePromiseWrapperRef.current.resolve(
|
||||||
|
window.Signal.CI.deviceName
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await chooseDeviceNamePromiseWrapperRef.current.promise;
|
const result = await chooseDeviceNamePromiseWrapperRef.current.promise;
|
||||||
|
@ -203,7 +205,7 @@ export function SmartInstallScreen(): ReactElement {
|
||||||
confirmNumber
|
confirmNumber
|
||||||
);
|
);
|
||||||
|
|
||||||
window.removeSetupMenuItems();
|
window.IPC.removeSetupMenuItems();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
log.error(
|
log.error(
|
||||||
'account.registerSecondDevice: got an error',
|
'account.registerSecondDevice: got an error',
|
||||||
|
@ -233,7 +235,7 @@ export function SmartInstallScreen(): ReactElement {
|
||||||
screenSpecificProps: {
|
screenSpecificProps: {
|
||||||
i18n,
|
i18n,
|
||||||
error: state.error,
|
error: state.error,
|
||||||
quit: () => window.shutdown(),
|
quit: () => window.IPC.shutdown(),
|
||||||
tryAgain: () => setState(INITIAL_STATE),
|
tryAgain: () => setState(INITIAL_STATE),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -69,8 +69,7 @@ void (async () => {
|
||||||
const openConvo = async (contact: PrimaryDevice): Promise<void> => {
|
const openConvo = async (contact: PrimaryDevice): Promise<void> => {
|
||||||
debug('opening conversation', contact.profileName);
|
debug('opening conversation', contact.profileName);
|
||||||
const item = leftPane.locator(
|
const item = leftPane.locator(
|
||||||
'_react=BaseConversationListItem' +
|
`[data-testid="${contact.toContact().uuid}"]`
|
||||||
`[title = ${JSON.stringify(contact.profileName)}]`
|
|
||||||
);
|
);
|
||||||
|
|
||||||
await item.click();
|
await item.click();
|
||||||
|
|
|
@ -122,11 +122,9 @@ void (async () => {
|
||||||
{
|
{
|
||||||
const leftPane = window.locator('.left-pane-wrapper');
|
const leftPane = window.locator('.left-pane-wrapper');
|
||||||
|
|
||||||
const item = leftPane.locator(
|
const item = leftPane
|
||||||
'_react=BaseConversationListItem' +
|
.locator('.module-conversation-list__item--contact-or-conversation')
|
||||||
`[title = ${JSON.stringify(group.title)}] ` +
|
.first();
|
||||||
`>> text=${LAST_MESSAGE}`
|
|
||||||
);
|
|
||||||
await item.click();
|
await item.click();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -141,7 +139,7 @@ void (async () => {
|
||||||
'.composition-area-wrapper, .conversation .ConversationView'
|
'.composition-area-wrapper, .conversation .ConversationView'
|
||||||
);
|
);
|
||||||
|
|
||||||
const input = composeArea.locator('_react=CompositionInput');
|
const input = composeArea.locator('[data-testid=CompositionInput]');
|
||||||
|
|
||||||
debug('entering message text');
|
debug('entering message text');
|
||||||
await input.type(`my message ${runId}`);
|
await input.type(`my message ${runId}`);
|
||||||
|
@ -166,9 +164,7 @@ void (async () => {
|
||||||
await server.send(desktop, delivery);
|
await server.send(desktop, delivery);
|
||||||
|
|
||||||
debug('waiting for message state change');
|
debug('waiting for message state change');
|
||||||
const message = timeline.locator(
|
const message = timeline.locator(`[data-testid="${timestamp}"]`);
|
||||||
`_react=Message[timestamp = ${timestamp}][status = "delivered"]`
|
|
||||||
);
|
|
||||||
await message.waitFor();
|
await message.waitFor();
|
||||||
|
|
||||||
if (runId >= DISCARD_COUNT) {
|
if (runId >= DISCARD_COUNT) {
|
||||||
|
|
|
@ -75,9 +75,7 @@ void (async () => {
|
||||||
{
|
{
|
||||||
const leftPane = window.locator('.left-pane-wrapper');
|
const leftPane = window.locator('.left-pane-wrapper');
|
||||||
const item = leftPane.locator(
|
const item = leftPane.locator(
|
||||||
'_react=BaseConversationListItem' +
|
`[data-testid="${first.toContact().uuid}"]`
|
||||||
`[title = ${JSON.stringify(first.profileName)}]` +
|
|
||||||
`>> ${JSON.stringify(LAST_MESSAGE)}`
|
|
||||||
);
|
);
|
||||||
await item.click();
|
await item.click();
|
||||||
}
|
}
|
||||||
|
@ -92,7 +90,7 @@ void (async () => {
|
||||||
const composeArea = window.locator(
|
const composeArea = window.locator(
|
||||||
'.composition-area-wrapper, .conversation .ConversationView'
|
'.composition-area-wrapper, .conversation .ConversationView'
|
||||||
);
|
);
|
||||||
const input = composeArea.locator('_react=CompositionInput');
|
const input = composeArea.locator('[data-testid=CompositionInput]');
|
||||||
|
|
||||||
debug('entering message text');
|
debug('entering message text');
|
||||||
await input.type(`my message ${runId}`);
|
await input.type(`my message ${runId}`);
|
||||||
|
@ -116,9 +114,7 @@ void (async () => {
|
||||||
await server.send(desktop, delivery);
|
await server.send(desktop, delivery);
|
||||||
|
|
||||||
debug('waiting for message state change');
|
debug('waiting for message state change');
|
||||||
const message = timeline.locator(
|
const message = timeline.locator(`[data-testid="${timestamp}"]`);
|
||||||
`_react=Message[timestamp = ${timestamp}][status = "delivered"]`
|
|
||||||
);
|
|
||||||
await message.waitFor();
|
await message.waitFor();
|
||||||
|
|
||||||
if (runId >= DISCARD_COUNT) {
|
if (runId >= DISCARD_COUNT) {
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
/* eslint-disable no-await-in-loop, no-console */
|
/* eslint-disable no-await-in-loop, no-console */
|
||||||
|
|
||||||
|
import type { PrimaryDevice } from '@signalapp/mock-server';
|
||||||
import { StorageState } from '@signalapp/mock-server';
|
import { StorageState } from '@signalapp/mock-server';
|
||||||
|
|
||||||
import type { App } from './fixtures';
|
import type { App } from './fixtures';
|
||||||
|
@ -23,6 +24,7 @@ void (async () => {
|
||||||
const { phone, server } = bootstrap;
|
const { phone, server } = bootstrap;
|
||||||
|
|
||||||
let state = StorageState.getEmpty();
|
let state = StorageState.getEmpty();
|
||||||
|
let lastContact: PrimaryDevice | undefined;
|
||||||
for (const [i, profileName] of contactNames.entries()) {
|
for (const [i, profileName] of contactNames.entries()) {
|
||||||
const contact = await server.createPrimaryDevice({
|
const contact = await server.createPrimaryDevice({
|
||||||
profileName,
|
profileName,
|
||||||
|
@ -39,6 +41,10 @@ void (async () => {
|
||||||
if (i >= contactNames.length - 4) {
|
if (i >= contactNames.length - 4) {
|
||||||
state = state.pin(contact);
|
state = state.pin(contact);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (i === contactNames.length - 1) {
|
||||||
|
lastContact = contact;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await phone.setStorageState(state);
|
await phone.setStorageState(state);
|
||||||
|
@ -52,8 +58,7 @@ void (async () => {
|
||||||
const leftPane = window.locator('.left-pane-wrapper');
|
const leftPane = window.locator('.left-pane-wrapper');
|
||||||
|
|
||||||
const item = leftPane.locator(
|
const item = leftPane.locator(
|
||||||
'_react=BaseConversationListItem' +
|
`[data-testid="${lastContact?.toContact().uuid}"]`
|
||||||
`[title = ${JSON.stringify(contactNames[contactNames.length - 1])}]`
|
|
||||||
);
|
);
|
||||||
await item.waitFor();
|
await item.waitFor();
|
||||||
|
|
||||||
|
|
|
@ -89,7 +89,7 @@ export class App {
|
||||||
const window = await this.getWindow();
|
const window = await this.getWindow();
|
||||||
|
|
||||||
await window.evaluate(
|
await window.evaluate(
|
||||||
`window.CI.solveChallenge(${JSON.stringify(response)})`
|
`window.Signal.CI.solveChallenge(${JSON.stringify(response)})`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -105,7 +105,7 @@ export class App {
|
||||||
const window = await this.getWindow();
|
const window = await this.getWindow();
|
||||||
|
|
||||||
const result = await window.evaluate(
|
const result = await window.evaluate(
|
||||||
`window.CI.waitForEvent(${JSON.stringify(event)})`
|
`window.Signal.CI.waitForEvent(${JSON.stringify(event)})`
|
||||||
);
|
);
|
||||||
|
|
||||||
return result as T;
|
return result as T;
|
||||||
|
|
|
@ -52,12 +52,7 @@ describe('pnp/accept gv2 invite', function needsName() {
|
||||||
const leftPane = window.locator('.left-pane-wrapper');
|
const leftPane = window.locator('.left-pane-wrapper');
|
||||||
|
|
||||||
debug('Opening group');
|
debug('Opening group');
|
||||||
await leftPane
|
await leftPane.locator(`[data-testid="${group.id}"]`).click();
|
||||||
.locator(
|
|
||||||
'_react=ConversationListItem' +
|
|
||||||
`[title = ${JSON.stringify(group.title)}]`
|
|
||||||
)
|
|
||||||
.click();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(async function after() {
|
afterEach(async function after() {
|
||||||
|
@ -296,12 +291,7 @@ describe('pnp/accept gv2 invite', function needsName() {
|
||||||
const leftPane = window.locator('.left-pane-wrapper');
|
const leftPane = window.locator('.left-pane-wrapper');
|
||||||
|
|
||||||
debug('Opening new group');
|
debug('Opening new group');
|
||||||
await leftPane
|
await leftPane.locator(`[data-testid="${group.id}"]`).click();
|
||||||
.locator(
|
|
||||||
'_react=ConversationListItem' +
|
|
||||||
`[title = ${JSON.stringify(group.title)}]`
|
|
||||||
)
|
|
||||||
.click();
|
|
||||||
|
|
||||||
debug('Accepting remote invite');
|
debug('Accepting remote invite');
|
||||||
await second.acceptPniInvite(group, desktop, {
|
await second.acceptPniInvite(group, desktop, {
|
||||||
|
|
|
@ -64,13 +64,7 @@ describe('pnp/change number', function needsName() {
|
||||||
]);
|
]);
|
||||||
|
|
||||||
debug('opening conversation with the first contact');
|
debug('opening conversation with the first contact');
|
||||||
await leftPane
|
await leftPane.locator(`[data-testid="${first.toContact().uuid}"]`).click();
|
||||||
.locator(
|
|
||||||
'_react=ConversationListItem' +
|
|
||||||
`[title = ${JSON.stringify(first.profileName)}] ` +
|
|
||||||
' >> "After"'
|
|
||||||
)
|
|
||||||
.click();
|
|
||||||
|
|
||||||
debug('done');
|
debug('done');
|
||||||
});
|
});
|
||||||
|
|
|
@ -109,7 +109,7 @@ describe('pnp/merge', function needsName() {
|
||||||
|
|
||||||
debug('opening conversation with the aci contact');
|
debug('opening conversation with the aci contact');
|
||||||
await leftPane
|
await leftPane
|
||||||
.locator('_react=ConversationListItem[title = "ACI Contact"]')
|
.locator(`[data-testid="${pniContact.toContact().uuid}"]`)
|
||||||
.click();
|
.click();
|
||||||
|
|
||||||
await window.locator('.module-conversation-hero').waitFor();
|
await window.locator('.module-conversation-hero').waitFor();
|
||||||
|
@ -119,7 +119,9 @@ describe('pnp/merge', function needsName() {
|
||||||
const composeArea = window.locator(
|
const composeArea = window.locator(
|
||||||
'.composition-area-wrapper, .conversation .ConversationView'
|
'.composition-area-wrapper, .conversation .ConversationView'
|
||||||
);
|
);
|
||||||
const compositionInput = composeArea.locator('_react=CompositionInput');
|
const compositionInput = composeArea.locator(
|
||||||
|
'[data-testid=CompositionInput]'
|
||||||
|
);
|
||||||
|
|
||||||
await compositionInput.type('Hello ACI');
|
await compositionInput.type('Hello ACI');
|
||||||
await compositionInput.press('Enter');
|
await compositionInput.press('Enter');
|
||||||
|
@ -127,7 +129,8 @@ describe('pnp/merge', function needsName() {
|
||||||
|
|
||||||
debug('opening conversation with the pni contact');
|
debug('opening conversation with the pni contact');
|
||||||
await leftPane
|
await leftPane
|
||||||
.locator('_react=ConversationListItem[title = "PNI Contact"]')
|
.locator('.module-conversation-list__item--contact-or-conversation')
|
||||||
|
.first()
|
||||||
.click();
|
.click();
|
||||||
|
|
||||||
await window.locator('.module-conversation-hero').waitFor();
|
await window.locator('.module-conversation-hero').waitFor();
|
||||||
|
@ -152,7 +155,9 @@ describe('pnp/merge', function needsName() {
|
||||||
const composeArea = window.locator(
|
const composeArea = window.locator(
|
||||||
'.composition-area-wrapper, .conversation .ConversationView'
|
'.composition-area-wrapper, .conversation .ConversationView'
|
||||||
);
|
);
|
||||||
const compositionInput = composeArea.locator('_react=CompositionInput');
|
const compositionInput = composeArea.locator(
|
||||||
|
'[data-testid=CompositionInput]'
|
||||||
|
);
|
||||||
|
|
||||||
await compositionInput.type('Hello PNI');
|
await compositionInput.type('Hello PNI');
|
||||||
await compositionInput.press('Enter');
|
await compositionInput.press('Enter');
|
||||||
|
@ -161,7 +166,7 @@ describe('pnp/merge', function needsName() {
|
||||||
if (finalContact === UUIDKind.ACI) {
|
if (finalContact === UUIDKind.ACI) {
|
||||||
debug('switching back to ACI conversation');
|
debug('switching back to ACI conversation');
|
||||||
await leftPane
|
await leftPane
|
||||||
.locator('_react=ConversationListItem[title = "ACI Contact"]')
|
.locator(`[data-testid="${pniContact.toContact().uuid}"]`)
|
||||||
.click();
|
.click();
|
||||||
|
|
||||||
await window.locator('.module-conversation-hero').waitFor();
|
await window.locator('.module-conversation-hero').waitFor();
|
||||||
|
|
|
@ -80,7 +80,9 @@ describe('pnp/PNI Change', function needsName() {
|
||||||
const leftPane = window.locator('.left-pane-wrapper');
|
const leftPane = window.locator('.left-pane-wrapper');
|
||||||
|
|
||||||
await leftPane
|
await leftPane
|
||||||
.locator('_react=ConversationListItem[title = "ContactA"]')
|
.locator(
|
||||||
|
`[data-testid="${contactA.device.getUUIDByKind(UUIDKind.PNI)}"]`
|
||||||
|
)
|
||||||
.click();
|
.click();
|
||||||
|
|
||||||
await window.locator('.module-conversation-hero').waitFor();
|
await window.locator('.module-conversation-hero').waitFor();
|
||||||
|
@ -102,7 +104,9 @@ describe('pnp/PNI Change', function needsName() {
|
||||||
const composeArea = window.locator(
|
const composeArea = window.locator(
|
||||||
'.composition-area-wrapper, .conversation .ConversationView'
|
'.composition-area-wrapper, .conversation .ConversationView'
|
||||||
);
|
);
|
||||||
const compositionInput = composeArea.locator('_react=CompositionInput');
|
const compositionInput = composeArea.locator(
|
||||||
|
'[data-testid=CompositionInput]'
|
||||||
|
);
|
||||||
|
|
||||||
await compositionInput.type('message to contactA');
|
await compositionInput.type('message to contactA');
|
||||||
await compositionInput.press('Enter');
|
await compositionInput.press('Enter');
|
||||||
|
@ -181,7 +185,9 @@ describe('pnp/PNI Change', function needsName() {
|
||||||
const leftPane = window.locator('.left-pane-wrapper');
|
const leftPane = window.locator('.left-pane-wrapper');
|
||||||
|
|
||||||
await leftPane
|
await leftPane
|
||||||
.locator('_react=ConversationListItem[title = "ContactA"]')
|
.locator(
|
||||||
|
`[data-testid="${contactA.device.getUUIDByKind(UUIDKind.PNI)}"]`
|
||||||
|
)
|
||||||
.click();
|
.click();
|
||||||
|
|
||||||
await window.locator('.module-conversation-hero').waitFor();
|
await window.locator('.module-conversation-hero').waitFor();
|
||||||
|
@ -203,7 +209,9 @@ describe('pnp/PNI Change', function needsName() {
|
||||||
const composeArea = window.locator(
|
const composeArea = window.locator(
|
||||||
'.composition-area-wrapper, .conversation .ConversationView'
|
'.composition-area-wrapper, .conversation .ConversationView'
|
||||||
);
|
);
|
||||||
const compositionInput = composeArea.locator('_react=CompositionInput');
|
const compositionInput = composeArea.locator(
|
||||||
|
'[data-testid=CompositionInput]'
|
||||||
|
);
|
||||||
|
|
||||||
await compositionInput.type('message to contactA');
|
await compositionInput.type('message to contactA');
|
||||||
await compositionInput.press('Enter');
|
await compositionInput.press('Enter');
|
||||||
|
@ -284,7 +292,9 @@ describe('pnp/PNI Change', function needsName() {
|
||||||
const leftPane = window.locator('.left-pane-wrapper');
|
const leftPane = window.locator('.left-pane-wrapper');
|
||||||
|
|
||||||
await leftPane
|
await leftPane
|
||||||
.locator('_react=ConversationListItem[title = "ContactA"]')
|
.locator(
|
||||||
|
`[data-testid="${contactA.device.getUUIDByKind(UUIDKind.PNI)}"]`
|
||||||
|
)
|
||||||
.click();
|
.click();
|
||||||
|
|
||||||
await window.locator('.module-conversation-hero').waitFor();
|
await window.locator('.module-conversation-hero').waitFor();
|
||||||
|
@ -306,7 +316,9 @@ describe('pnp/PNI Change', function needsName() {
|
||||||
const composeArea = window.locator(
|
const composeArea = window.locator(
|
||||||
'.composition-area-wrapper, .conversation .ConversationView'
|
'.composition-area-wrapper, .conversation .ConversationView'
|
||||||
);
|
);
|
||||||
const compositionInput = composeArea.locator('_react=CompositionInput');
|
const compositionInput = composeArea.locator(
|
||||||
|
'[data-testid=CompositionInput]'
|
||||||
|
);
|
||||||
|
|
||||||
await compositionInput.type('message to contactA');
|
await compositionInput.type('message to contactA');
|
||||||
await compositionInput.press('Enter');
|
await compositionInput.press('Enter');
|
||||||
|
@ -366,7 +378,9 @@ describe('pnp/PNI Change', function needsName() {
|
||||||
const composeArea = window.locator(
|
const composeArea = window.locator(
|
||||||
'.composition-area-wrapper, .conversation .ConversationView'
|
'.composition-area-wrapper, .conversation .ConversationView'
|
||||||
);
|
);
|
||||||
const compositionInput = composeArea.locator('_react=CompositionInput');
|
const compositionInput = composeArea.locator(
|
||||||
|
'[data-testid=CompositionInput]'
|
||||||
|
);
|
||||||
|
|
||||||
await compositionInput.type('message to contactB');
|
await compositionInput.type('message to contactB');
|
||||||
await compositionInput.press('Enter');
|
await compositionInput.press('Enter');
|
||||||
|
@ -420,7 +434,9 @@ describe('pnp/PNI Change', function needsName() {
|
||||||
const leftPane = window.locator('.left-pane-wrapper');
|
const leftPane = window.locator('.left-pane-wrapper');
|
||||||
|
|
||||||
await leftPane
|
await leftPane
|
||||||
.locator('_react=ConversationListItem[title = "ContactA"]')
|
.locator(
|
||||||
|
`[data-testid="${contactA.device.getUUIDByKind(UUIDKind.PNI)}"]`
|
||||||
|
)
|
||||||
.click();
|
.click();
|
||||||
|
|
||||||
await window.locator('.module-conversation-hero').waitFor();
|
await window.locator('.module-conversation-hero').waitFor();
|
||||||
|
@ -442,7 +458,9 @@ describe('pnp/PNI Change', function needsName() {
|
||||||
const composeArea = window.locator(
|
const composeArea = window.locator(
|
||||||
'.composition-area-wrapper, .conversation .ConversationView'
|
'.composition-area-wrapper, .conversation .ConversationView'
|
||||||
);
|
);
|
||||||
const compositionInput = composeArea.locator('_react=CompositionInput');
|
const compositionInput = composeArea.locator(
|
||||||
|
'[data-testid=CompositionInput]'
|
||||||
|
);
|
||||||
|
|
||||||
await compositionInput.type('message to contactA');
|
await compositionInput.type('message to contactA');
|
||||||
await compositionInput.press('Enter');
|
await compositionInput.press('Enter');
|
||||||
|
@ -533,7 +551,9 @@ describe('pnp/PNI Change', function needsName() {
|
||||||
const composeArea = window.locator(
|
const composeArea = window.locator(
|
||||||
'.composition-area-wrapper, .conversation .ConversationView'
|
'.composition-area-wrapper, .conversation .ConversationView'
|
||||||
);
|
);
|
||||||
const compositionInput = composeArea.locator('_react=CompositionInput');
|
const compositionInput = composeArea.locator(
|
||||||
|
'[data-testid=CompositionInput]'
|
||||||
|
);
|
||||||
|
|
||||||
await compositionInput.type('second message to contactA');
|
await compositionInput.type('second message to contactA');
|
||||||
await compositionInput.press('Enter');
|
await compositionInput.press('Enter');
|
||||||
|
|
|
@ -153,10 +153,7 @@ describe('pnp/PNI Signature', function needsName() {
|
||||||
|
|
||||||
debug('opening conversation with the stranger');
|
debug('opening conversation with the stranger');
|
||||||
await leftPane
|
await leftPane
|
||||||
.locator(
|
.locator(`[data-testid="${stranger.toContact().uuid}"]`)
|
||||||
'_react=ConversationListItem' +
|
|
||||||
`[title = ${JSON.stringify(stranger.profileName)}]`
|
|
||||||
)
|
|
||||||
.click();
|
.click();
|
||||||
|
|
||||||
debug('Accept conversation from a stranger');
|
debug('Accept conversation from a stranger');
|
||||||
|
@ -173,7 +170,9 @@ describe('pnp/PNI Signature', function needsName() {
|
||||||
}
|
}
|
||||||
|
|
||||||
debug('Enter first message text');
|
debug('Enter first message text');
|
||||||
const compositionInput = composeArea.locator('_react=CompositionInput');
|
const compositionInput = composeArea.locator(
|
||||||
|
'[data-testid=CompositionInput]'
|
||||||
|
);
|
||||||
|
|
||||||
await compositionInput.type('first');
|
await compositionInput.type('first');
|
||||||
await compositionInput.press('Enter');
|
await compositionInput.press('Enter');
|
||||||
|
@ -273,11 +272,14 @@ describe('pnp/PNI Signature', function needsName() {
|
||||||
|
|
||||||
debug('opening conversation with the pni contact');
|
debug('opening conversation with the pni contact');
|
||||||
await leftPane
|
await leftPane
|
||||||
.locator('_react=ConversationListItem[title = "PNI Contact"]')
|
.locator('.module-conversation-list__item--contact-or-conversation')
|
||||||
|
.first()
|
||||||
.click();
|
.click();
|
||||||
|
|
||||||
debug('Enter a PNI message text');
|
debug('Enter a PNI message text');
|
||||||
const compositionInput = composeArea.locator('_react=CompositionInput');
|
const compositionInput = composeArea.locator(
|
||||||
|
'[data-testid=CompositionInput]'
|
||||||
|
);
|
||||||
|
|
||||||
await compositionInput.type('Hello PNI');
|
await compositionInput.type('Hello PNI');
|
||||||
await compositionInput.press('Enter');
|
await compositionInput.press('Enter');
|
||||||
|
@ -313,13 +315,11 @@ describe('pnp/PNI Signature', function needsName() {
|
||||||
|
|
||||||
debug('Wait for merge to happen');
|
debug('Wait for merge to happen');
|
||||||
await leftPane
|
await leftPane
|
||||||
.locator('_react=ConversationListItem[title = "ACI Contact"]')
|
.locator(`[data-testid="${pniContact.toContact().uuid}"]`)
|
||||||
.waitFor();
|
.waitFor();
|
||||||
|
|
||||||
debug('Wait for composition input to clear');
|
debug('Wait for composition input to clear');
|
||||||
await composeArea
|
await composeArea.locator('[data-testid=CompositionInput]').waitFor();
|
||||||
.locator('_react=CompositionInput[draftText = ""]')
|
|
||||||
.waitFor();
|
|
||||||
|
|
||||||
debug('Enter an ACI message text');
|
debug('Enter an ACI message text');
|
||||||
await compositionInput.type('Hello ACI');
|
await compositionInput.type('Hello ACI');
|
||||||
|
|
|
@ -105,9 +105,7 @@ describe('pnp/send gv2 invite', function needsName() {
|
||||||
|
|
||||||
await leftPane.locator('.module-main-header__compose-icon').click();
|
await leftPane.locator('.module-main-header__compose-icon').click();
|
||||||
|
|
||||||
await leftPane
|
await leftPane.locator('[data-testid=CreateNewGroupButton]').click();
|
||||||
.locator('_react=BaseConversationListItem[title = "New group"]')
|
|
||||||
.click();
|
|
||||||
|
|
||||||
debug('inviting ACI member');
|
debug('inviting ACI member');
|
||||||
|
|
||||||
|
@ -116,7 +114,7 @@ describe('pnp/send gv2 invite', function needsName() {
|
||||||
.fill('ACI');
|
.fill('ACI');
|
||||||
|
|
||||||
await leftPane
|
await leftPane
|
||||||
.locator('_react=BaseConversationListItem[title = "ACI Contact"]')
|
.locator(`[data-testid="${aciContact.toContact().uuid}"]`)
|
||||||
.click();
|
.click();
|
||||||
|
|
||||||
debug('inviting PNI member');
|
debug('inviting PNI member');
|
||||||
|
@ -126,7 +124,9 @@ describe('pnp/send gv2 invite', function needsName() {
|
||||||
.fill('PNI');
|
.fill('PNI');
|
||||||
|
|
||||||
await leftPane
|
await leftPane
|
||||||
.locator('_react=BaseConversationListItem[title = "PNI Contact"]')
|
.locator(
|
||||||
|
`[data-testid="${pniContact.device.getUUIDByKind(UUIDKind.PNI)}"]`
|
||||||
|
)
|
||||||
.click();
|
.click();
|
||||||
|
|
||||||
await leftPane
|
await leftPane
|
||||||
|
@ -180,7 +180,7 @@ describe('pnp/send gv2 invite', function needsName() {
|
||||||
debug('editing group title');
|
debug('editing group title');
|
||||||
{
|
{
|
||||||
const detailsHeader = conversationStack.locator(
|
const detailsHeader = conversationStack.locator(
|
||||||
'_react=ConversationDetailsHeader'
|
'[data-testid=ConversationDetailsHeader]'
|
||||||
);
|
);
|
||||||
await detailsHeader.locator('button >> "My group"').click();
|
await detailsHeader.locator('button >> "My group"').click();
|
||||||
|
|
||||||
|
|
|
@ -53,10 +53,7 @@ describe('storage service', function needsName() {
|
||||||
});
|
});
|
||||||
|
|
||||||
await leftPane
|
await leftPane
|
||||||
.locator(
|
.locator(`[data-testid="${firstContact.toContact().uuid}"]`)
|
||||||
'_react=ConversationListItem' +
|
|
||||||
`[title = ${JSON.stringify(firstContact.profileName)}]`
|
|
||||||
)
|
|
||||||
.waitFor({ state: 'hidden' });
|
.waitFor({ state: 'hidden' });
|
||||||
|
|
||||||
await leftPane
|
await leftPane
|
||||||
|
@ -76,11 +73,7 @@ describe('storage service', function needsName() {
|
||||||
});
|
});
|
||||||
|
|
||||||
await leftPane
|
await leftPane
|
||||||
.locator(
|
.locator(`[data-testid="${firstContact.toContact().uuid}"]`)
|
||||||
'_react=ConversationListItem' +
|
|
||||||
'[isPinned = true]' +
|
|
||||||
`[title = ${JSON.stringify(firstContact.profileName)}]`
|
|
||||||
)
|
|
||||||
.waitFor();
|
.waitFor();
|
||||||
|
|
||||||
await leftPane
|
await leftPane
|
||||||
|
@ -93,10 +86,7 @@ describe('storage service', function needsName() {
|
||||||
const state = await phone.expectStorageState('consistency check');
|
const state = await phone.expectStorageState('consistency check');
|
||||||
|
|
||||||
await leftPane
|
await leftPane
|
||||||
.locator(
|
.locator(`[data-testid="${firstContact.toContact().uuid}"]`)
|
||||||
'_react=ConversationListItem' +
|
|
||||||
`[title = ${JSON.stringify(firstContact.profileName)}]`
|
|
||||||
)
|
|
||||||
.click();
|
.click();
|
||||||
|
|
||||||
const moreButton = conversationStack.locator(
|
const moreButton = conversationStack.locator(
|
||||||
|
|
|
@ -48,11 +48,7 @@ describe('storage service', function needsName() {
|
||||||
|
|
||||||
debug('wait for first contact to be pinned in the left pane');
|
debug('wait for first contact to be pinned in the left pane');
|
||||||
await leftPane
|
await leftPane
|
||||||
.locator(
|
.locator(`[data-testid="${firstContact.toContact().uuid}"]`)
|
||||||
'_react=ConversationListItem' +
|
|
||||||
'[isPinned = true] ' +
|
|
||||||
`[title = ${JSON.stringify(firstContact.profileName)}]`
|
|
||||||
)
|
|
||||||
.waitFor();
|
.waitFor();
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -83,11 +79,7 @@ describe('storage service', function needsName() {
|
||||||
|
|
||||||
debug('wait for last contact to be pinned in the left pane');
|
debug('wait for last contact to be pinned in the left pane');
|
||||||
await leftPane
|
await leftPane
|
||||||
.locator(
|
.locator(`[data-testid="${lastContact.toContact().uuid}"]`)
|
||||||
'_react=ConversationListItem' +
|
|
||||||
'[isPinned = true] ' +
|
|
||||||
`[title = ${JSON.stringify(lastContact.profileName)}]`
|
|
||||||
)
|
|
||||||
.waitFor({ timeout: durations.MINUTE });
|
.waitFor({ timeout: durations.MINUTE });
|
||||||
|
|
||||||
debug('Verifying the final manifest version');
|
debug('Verifying the final manifest version');
|
||||||
|
|
|
@ -55,11 +55,9 @@ describe('storage service', function needsName() {
|
||||||
const conversationStack = window.locator('.conversation-stack');
|
const conversationStack = window.locator('.conversation-stack');
|
||||||
|
|
||||||
debug('Opening conversation with a stranger');
|
debug('Opening conversation with a stranger');
|
||||||
|
debug(stranger.toContact().uuid);
|
||||||
await leftPane
|
await leftPane
|
||||||
.locator(
|
.locator(`[data-testid="${stranger.toContact().uuid}"]`)
|
||||||
'_react=ConversationListItem' +
|
|
||||||
`[title = ${JSON.stringify(stranger.profileName)}]`
|
|
||||||
)
|
|
||||||
.click();
|
.click();
|
||||||
|
|
||||||
debug("Verify that we stored stranger's profile key");
|
debug("Verify that we stored stranger's profile key");
|
||||||
|
@ -124,7 +122,7 @@ describe('storage service', function needsName() {
|
||||||
const composeArea = window.locator(
|
const composeArea = window.locator(
|
||||||
'.composition-area-wrapper, .conversation .ConversationView'
|
'.composition-area-wrapper, .conversation .ConversationView'
|
||||||
);
|
);
|
||||||
const input = composeArea.locator('_react=CompositionInput');
|
const input = composeArea.locator('[data-testid=CompositionInput]');
|
||||||
|
|
||||||
await input.type('hello stranger!');
|
await input.type('hello stranger!');
|
||||||
await input.press('Enter');
|
await input.press('Enter');
|
||||||
|
|
|
@ -43,13 +43,7 @@ describe('storage service', function needsName() {
|
||||||
const conversationStack = window.locator('.conversation-stack');
|
const conversationStack = window.locator('.conversation-stack');
|
||||||
|
|
||||||
debug('Verifying that the group is pinned on startup');
|
debug('Verifying that the group is pinned on startup');
|
||||||
await leftPane
|
await leftPane.locator(`[data-testid="${group.id}"]`).waitFor();
|
||||||
.locator(
|
|
||||||
'_react=ConversationListItem' +
|
|
||||||
'[isPinned = true] ' +
|
|
||||||
`[title = ${JSON.stringify(group.title)}]`
|
|
||||||
)
|
|
||||||
.waitFor();
|
|
||||||
|
|
||||||
debug('Unpinning group via storage service');
|
debug('Unpinning group via storage service');
|
||||||
{
|
{
|
||||||
|
@ -60,24 +54,14 @@ describe('storage service', function needsName() {
|
||||||
timestamp: bootstrap.getTimestamp(),
|
timestamp: bootstrap.getTimestamp(),
|
||||||
});
|
});
|
||||||
|
|
||||||
await leftPane
|
await leftPane.locator(`[data-testid="${group.id}"]`).waitFor();
|
||||||
.locator(
|
|
||||||
'_react=ConversationListItem' +
|
|
||||||
'[isPinned = false] ' +
|
|
||||||
`[title = ${JSON.stringify(group.title)}]`
|
|
||||||
)
|
|
||||||
.waitFor();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
debug('Pinning group in the app');
|
debug('Pinning group in the app');
|
||||||
{
|
{
|
||||||
const state = await phone.expectStorageState('consistency check');
|
const state = await phone.expectStorageState('consistency check');
|
||||||
|
|
||||||
const convo = leftPane.locator(
|
const convo = leftPane.locator(`[data-testid="${group.id}"]`);
|
||||||
'_react=ConversationListItem' +
|
|
||||||
'[isPinned = false] ' +
|
|
||||||
`[title = ${JSON.stringify(group.title)}]`
|
|
||||||
);
|
|
||||||
await convo.click();
|
await convo.click();
|
||||||
|
|
||||||
const moreButton = conversationStack.locator(
|
const moreButton = conversationStack.locator(
|
||||||
|
@ -120,8 +104,7 @@ describe('storage service', function needsName() {
|
||||||
|
|
||||||
debug('pinning contact=%d', i);
|
debug('pinning contact=%d', i);
|
||||||
const convo = leftPane.locator(
|
const convo = leftPane.locator(
|
||||||
'_react=ConversationListItem' +
|
`[data-testid="${contact.toContact().uuid}"]`
|
||||||
`[title = ${JSON.stringify(contact.profileName)}]`
|
|
||||||
);
|
);
|
||||||
await convo.click();
|
await convo.click();
|
||||||
|
|
||||||
|
|
|
@ -129,10 +129,7 @@ describe('storage service', function needsName() {
|
||||||
);
|
);
|
||||||
|
|
||||||
await leftPane
|
await leftPane
|
||||||
.locator(
|
.locator(`[data-testid="${firstContact.toContact().uuid}"]`)
|
||||||
'_react=ConversationListItem' +
|
|
||||||
`[title = ${JSON.stringify(firstContact.profileName)}]`
|
|
||||||
)
|
|
||||||
.click();
|
.click();
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -235,7 +232,7 @@ describe('storage service', function needsName() {
|
||||||
.click();
|
.click();
|
||||||
|
|
||||||
const stickerManager = conversationView.locator(
|
const stickerManager = conversationView.locator(
|
||||||
'_react=StickerManagerInner'
|
'[data-testid=StickerManager]'
|
||||||
);
|
);
|
||||||
|
|
||||||
debug('switching to Installed tab');
|
debug('switching to Installed tab');
|
||||||
|
@ -265,10 +262,7 @@ describe('storage service', function needsName() {
|
||||||
|
|
||||||
debug('waiting for sticker pack to become visible');
|
debug('waiting for sticker pack to become visible');
|
||||||
await stickerManager
|
await stickerManager
|
||||||
.locator(
|
.locator(`[data-testid="${STICKER_PACKS[0].id.toString('hex')}"]`)
|
||||||
'_react=StickerManagerPackRowInner' +
|
|
||||||
`[pack.id="${STICKER_PACKS[0].id.toString('hex')}"]`
|
|
||||||
)
|
|
||||||
.waitFor();
|
.waitFor();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -285,10 +279,7 @@ describe('storage service', function needsName() {
|
||||||
|
|
||||||
debug('waiting for sticker pack to become visible');
|
debug('waiting for sticker pack to become visible');
|
||||||
await stickerManager
|
await stickerManager
|
||||||
.locator(
|
.locator(`[data-testid="${STICKER_PACKS[1].id.toString('hex')}"]`)
|
||||||
'_react=StickerManagerPackRowInner' +
|
|
||||||
`[pack.id="${STICKER_PACKS[1].id.toString('hex')}"]`
|
|
||||||
)
|
|
||||||
.waitFor();
|
.waitFor();
|
||||||
|
|
||||||
debug('waiting for storage service update');
|
debug('waiting for storage service update');
|
||||||
|
|
|
@ -224,7 +224,7 @@ export default class AccountManager extends EventTarget {
|
||||||
}
|
}
|
||||||
const url = getProvisioningUrl(uuid, pubKey);
|
const url = getProvisioningUrl(uuid, pubKey);
|
||||||
|
|
||||||
window.CI?.setProvisioningURL(url);
|
window.Signal.CI?.setProvisioningURL(url);
|
||||||
|
|
||||||
setProvisioningUrl(url);
|
setProvisioningUrl(url);
|
||||||
request.respond(200, 'OK');
|
request.respond(200, 'OK');
|
||||||
|
|
|
@ -9,6 +9,8 @@ type EntryType = Readonly<{
|
||||||
callback(): void;
|
callback(): void;
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
|
let startupProcessingQueue: StartupQueue | undefined;
|
||||||
|
|
||||||
export class StartupQueue {
|
export class StartupQueue {
|
||||||
private readonly map = new Map<string, EntryType>();
|
private readonly map = new Map<string, EntryType>();
|
||||||
|
|
||||||
|
@ -38,4 +40,21 @@ export class StartupQueue {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static initialize(): void {
|
||||||
|
startupProcessingQueue = new StartupQueue();
|
||||||
|
}
|
||||||
|
|
||||||
|
static isReady(): boolean {
|
||||||
|
return Boolean(startupProcessingQueue);
|
||||||
|
}
|
||||||
|
|
||||||
|
static add(id: string, value: number, f: () => void): void {
|
||||||
|
startupProcessingQueue?.add(id, value, f);
|
||||||
|
}
|
||||||
|
|
||||||
|
static flush(): void {
|
||||||
|
startupProcessingQueue?.flush();
|
||||||
|
startupProcessingQueue = undefined;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
74
ts/util/attachmentDownloadQueue.ts
Normal file
74
ts/util/attachmentDownloadQueue.ts
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
// Copyright 2023 Signal Messenger, LLC
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
import type { MessageAttributesType } from '../model-types.d';
|
||||||
|
import type { MessageModel } from '../models/messages';
|
||||||
|
import * as log from '../logging/log';
|
||||||
|
import { isMoreRecentThan } from './timestamp';
|
||||||
|
|
||||||
|
const MAX_ATTACHMENT_DOWNLOAD_AGE = 3600 * 72 * 1000;
|
||||||
|
const MAX_ATTACHMENT_MSGS_TO_DOWNLOAD = 250;
|
||||||
|
|
||||||
|
let isEnabled = true;
|
||||||
|
let attachmentDownloadQueue: Array<MessageModel> | undefined = [];
|
||||||
|
|
||||||
|
export function shouldUseAttachmentDownloadQueue(): boolean {
|
||||||
|
return isEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function addToAttachmentDownloadQueue(
|
||||||
|
idLog: string,
|
||||||
|
message: MessageModel
|
||||||
|
): void {
|
||||||
|
if (!attachmentDownloadQueue) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
attachmentDownloadQueue.unshift(message);
|
||||||
|
|
||||||
|
log.info(
|
||||||
|
`${idLog}: Adding to attachmentDownloadQueue`,
|
||||||
|
message.get('sent_at')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function flushAttachmentDownloadQueue(): Promise<void> {
|
||||||
|
if (!attachmentDownloadQueue) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: ts/models/messages.ts expects this global to become undefined
|
||||||
|
// once we stop processing the queue.
|
||||||
|
isEnabled = false;
|
||||||
|
|
||||||
|
const attachmentsToDownload = attachmentDownloadQueue.filter(
|
||||||
|
(message, index) =>
|
||||||
|
index <= MAX_ATTACHMENT_MSGS_TO_DOWNLOAD ||
|
||||||
|
isMoreRecentThan(message.getReceivedAt(), MAX_ATTACHMENT_DOWNLOAD_AGE) ||
|
||||||
|
// Stickers and long text attachments has to be downloaded for UI
|
||||||
|
// to display the message properly.
|
||||||
|
message.hasRequiredAttachmentDownloads()
|
||||||
|
);
|
||||||
|
|
||||||
|
log.info(
|
||||||
|
'Downloading recent attachments of total attachments',
|
||||||
|
attachmentsToDownload.length,
|
||||||
|
attachmentDownloadQueue.length
|
||||||
|
);
|
||||||
|
|
||||||
|
const messagesWithDownloads = await Promise.all(
|
||||||
|
attachmentsToDownload.map(message => message.queueAttachmentDownloads())
|
||||||
|
);
|
||||||
|
const messagesToSave: Array<MessageAttributesType> = [];
|
||||||
|
messagesWithDownloads.forEach((shouldSave, messageKey) => {
|
||||||
|
if (shouldSave) {
|
||||||
|
const message = attachmentsToDownload[messageKey];
|
||||||
|
messagesToSave.push(message.attributes);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
await window.Signal.Data.saveMessages(messagesToSave, {
|
||||||
|
ourUuid: window.storage.user.getCheckedUuid().toString(),
|
||||||
|
});
|
||||||
|
|
||||||
|
attachmentDownloadQueue = undefined;
|
||||||
|
}
|
|
@ -2,11 +2,11 @@
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
export async function requestCameraPermissions(): Promise<boolean> {
|
export async function requestCameraPermissions(): Promise<boolean> {
|
||||||
if (!(await window.getMediaCameraPermissions())) {
|
if (!(await window.IPC.getMediaCameraPermissions())) {
|
||||||
await window.showPermissionsPopup(true, true);
|
await window.IPC.showPermissionsPopup(true, true);
|
||||||
|
|
||||||
// Check the setting again (from the source of truth).
|
// Check the setting again (from the source of truth).
|
||||||
return window.getMediaCameraPermissions();
|
return window.IPC.getMediaCameraPermissions();
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -308,15 +308,15 @@ export function createIPCEvents(
|
||||||
getHideMenuBar: () => window.storage.get('hide-menu-bar'),
|
getHideMenuBar: () => window.storage.get('hide-menu-bar'),
|
||||||
setHideMenuBar: value => {
|
setHideMenuBar: value => {
|
||||||
const promise = window.storage.put('hide-menu-bar', value);
|
const promise = window.storage.put('hide-menu-bar', value);
|
||||||
window.setAutoHideMenuBar(value);
|
window.IPC.setAutoHideMenuBar(value);
|
||||||
window.setMenuBarVisibility(!value);
|
window.IPC.setMenuBarVisibility(!value);
|
||||||
return promise;
|
return promise;
|
||||||
},
|
},
|
||||||
getSystemTraySetting: () =>
|
getSystemTraySetting: () =>
|
||||||
parseSystemTraySetting(window.storage.get('system-tray-setting')),
|
parseSystemTraySetting(window.storage.get('system-tray-setting')),
|
||||||
setSystemTraySetting: value => {
|
setSystemTraySetting: value => {
|
||||||
const promise = window.storage.put('system-tray-setting', value);
|
const promise = window.storage.put('system-tray-setting', value);
|
||||||
window.updateSystemTraySetting(value);
|
window.IPC.updateSystemTraySetting(value);
|
||||||
return promise;
|
return promise;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -361,9 +361,9 @@ export function createIPCEvents(
|
||||||
setAlwaysRelayCalls: value =>
|
setAlwaysRelayCalls: value =>
|
||||||
window.storage.put('always-relay-calls', value),
|
window.storage.put('always-relay-calls', value),
|
||||||
|
|
||||||
getAutoLaunch: () => window.getAutoLaunch(),
|
getAutoLaunch: () => window.IPC.getAutoLaunch(),
|
||||||
setAutoLaunch: async (value: boolean) => {
|
setAutoLaunch: async (value: boolean) => {
|
||||||
return window.setAutoLaunch(value);
|
return window.IPC.setAutoLaunch(value);
|
||||||
},
|
},
|
||||||
|
|
||||||
isPhoneNumberSharingEnabled: () => isPhoneNumberSharingEnabled(),
|
isPhoneNumberSharingEnabled: () => isPhoneNumberSharingEnabled(),
|
||||||
|
@ -526,8 +526,8 @@ export function createIPCEvents(
|
||||||
showWhatsNewModal();
|
showWhatsNewModal();
|
||||||
},
|
},
|
||||||
|
|
||||||
getMediaPermissions: window.getMediaPermissions,
|
getMediaPermissions: window.IPC.getMediaPermissions,
|
||||||
getMediaCameraPermissions: window.getMediaCameraPermissions,
|
getMediaCameraPermissions: window.IPC.getMediaCameraPermissions,
|
||||||
|
|
||||||
persistZoomFactor: zoomFactor =>
|
persistZoomFactor: zoomFactor =>
|
||||||
window.storage.put('zoomFactor', zoomFactor),
|
window.storage.put('zoomFactor', zoomFactor),
|
||||||
|
|
|
@ -212,7 +212,7 @@ function maybeShowDecryptionToast(
|
||||||
kind: ToastInternalErrorKind.DecryptionError,
|
kind: ToastInternalErrorKind.DecryptionError,
|
||||||
deviceId,
|
deviceId,
|
||||||
name,
|
name,
|
||||||
onShowDebugLog: () => window.showDebugLog(),
|
onShowDebugLog: () => window.IPC.showDebugLog(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -38,7 +38,6 @@ import {
|
||||||
sessionStructureToBytes,
|
sessionStructureToBytes,
|
||||||
} from './sessionTranslation';
|
} from './sessionTranslation';
|
||||||
import * as zkgroup from './zkgroup';
|
import * as zkgroup from './zkgroup';
|
||||||
import { StartupQueue } from './StartupQueue';
|
|
||||||
import { sendToGroup, sendContentMessageToGroup } from './sendToGroup';
|
import { sendToGroup, sendContentMessageToGroup } from './sendToGroup';
|
||||||
import { RetryPlaceholders } from './retryPlaceholders';
|
import { RetryPlaceholders } from './retryPlaceholders';
|
||||||
import * as expirationTimer from './expirationTimer';
|
import * as expirationTimer from './expirationTimer';
|
||||||
|
@ -47,7 +46,6 @@ import { MessageController } from './MessageController';
|
||||||
export {
|
export {
|
||||||
GoogleChrome,
|
GoogleChrome,
|
||||||
Registration,
|
Registration,
|
||||||
StartupQueue,
|
|
||||||
arrayBufferToObjectURL,
|
arrayBufferToObjectURL,
|
||||||
combineNames,
|
combineNames,
|
||||||
createBatcher,
|
createBatcher,
|
||||||
|
|
|
@ -4,12 +4,12 @@
|
||||||
export async function requestMicrophonePermissions(
|
export async function requestMicrophonePermissions(
|
||||||
forCalling: boolean
|
forCalling: boolean
|
||||||
): Promise<boolean> {
|
): Promise<boolean> {
|
||||||
const microphonePermission = await window.getMediaPermissions();
|
const microphonePermission = await window.IPC.getMediaPermissions();
|
||||||
if (!microphonePermission) {
|
if (!microphonePermission) {
|
||||||
await window.showPermissionsPopup(forCalling, false);
|
await window.IPC.showPermissionsPopup(forCalling, false);
|
||||||
|
|
||||||
// Check the setting again (from the source of truth).
|
// Check the setting again (from the source of truth).
|
||||||
return window.getMediaPermissions();
|
return window.IPC.getMediaPermissions();
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -1,12 +1,9 @@
|
||||||
// Copyright 2015 Signal Messenger, LLC
|
// Copyright 2015 Signal Messenger, LLC
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
// This file is here temporarily while we're switching off of Backbone into
|
import React from 'react';
|
||||||
// React. In the future, and in React-land, please just import and use
|
import { render, unmountComponentAtNode } from 'react-dom';
|
||||||
// ConfirmationDialog directly. This is the thin API layer to bridge the gap
|
import { ConfirmationDialog } from '../components/ConfirmationDialog';
|
||||||
// while we convert things over. Please delete this file once all usages are
|
|
||||||
// ported over. Note: this file cannot have any imports/exports since it is
|
|
||||||
// being included in a <script /> tag.
|
|
||||||
|
|
||||||
type ConfirmationDialogViewProps = {
|
type ConfirmationDialogViewProps = {
|
||||||
onTopOfEverything?: boolean;
|
onTopOfEverything?: boolean;
|
||||||
|
@ -27,7 +24,7 @@ function removeConfirmationDialog() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
window.ReactDOM.unmountComponentAtNode(confirmationDialogViewNode);
|
unmountComponentAtNode(confirmationDialogViewNode);
|
||||||
document.body.removeChild(confirmationDialogViewNode);
|
document.body.removeChild(confirmationDialogViewNode);
|
||||||
|
|
||||||
if (
|
if (
|
||||||
|
@ -39,7 +36,9 @@ function removeConfirmationDialog() {
|
||||||
confirmationDialogViewNode = undefined;
|
confirmationDialogViewNode = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
function showConfirmationDialog(options: ConfirmationDialogViewProps) {
|
export function showConfirmationDialog(
|
||||||
|
options: ConfirmationDialogViewProps
|
||||||
|
): void {
|
||||||
if (confirmationDialogViewNode) {
|
if (confirmationDialogViewNode) {
|
||||||
removeConfirmationDialog();
|
removeConfirmationDialog();
|
||||||
}
|
}
|
||||||
|
@ -49,9 +48,8 @@ function showConfirmationDialog(options: ConfirmationDialogViewProps) {
|
||||||
|
|
||||||
confirmationDialogPreviousFocus = document.activeElement as HTMLElement;
|
confirmationDialogPreviousFocus = document.activeElement as HTMLElement;
|
||||||
|
|
||||||
window.ReactDOM.render(
|
render(
|
||||||
// eslint-disable-next-line react/react-in-jsx-scope, react/jsx-no-undef
|
<ConfirmationDialog
|
||||||
<window.Signal.Components.ConfirmationDialog
|
|
||||||
dialogName={options.dialogName}
|
dialogName={options.dialogName}
|
||||||
onTopOfEverything={options.onTopOfEverything}
|
onTopOfEverything={options.onTopOfEverything}
|
||||||
actions={[
|
actions={[
|
||||||
|
@ -78,5 +76,3 @@ function showConfirmationDialog(options: ConfirmationDialogViewProps) {
|
||||||
confirmationDialogViewNode
|
confirmationDialogViewNode
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
window.showConfirmationDialog = showConfirmationDialog;
|
|
243
ts/window.d.ts
vendored
243
ts/window.d.ts
vendored
|
@ -7,10 +7,8 @@ import type { Store } from 'redux';
|
||||||
import type * as Backbone from 'backbone';
|
import type * as Backbone from 'backbone';
|
||||||
import type PQueue from 'p-queue/dist';
|
import type PQueue from 'p-queue/dist';
|
||||||
import type { assert } from 'chai';
|
import type { assert } from 'chai';
|
||||||
import type * as Mustache from 'mustache';
|
|
||||||
|
|
||||||
import type { PhoneNumber, PhoneNumberFormat } from 'google-libphonenumber';
|
import type { PhoneNumber, PhoneNumberFormat } from 'google-libphonenumber';
|
||||||
import type { imageToBlurHash } from './util/imageToBlurHash';
|
|
||||||
import type * as Util from './util';
|
import type * as Util from './util';
|
||||||
import type {
|
import type {
|
||||||
ConversationModelCollectionType,
|
ConversationModelCollectionType,
|
||||||
|
@ -22,6 +20,7 @@ import type {
|
||||||
ChallengeHandler,
|
ChallengeHandler,
|
||||||
IPCRequest as IPCChallengeRequest,
|
IPCRequest as IPCChallengeRequest,
|
||||||
} from './challenge';
|
} from './challenge';
|
||||||
|
import type AccountManager from './textsecure/AccountManager';
|
||||||
import type { WebAPIConnectType } from './textsecure/WebAPI';
|
import type { WebAPIConnectType } from './textsecure/WebAPI';
|
||||||
import type { CallingClass } from './services/calling';
|
import type { CallingClass } from './services/calling';
|
||||||
import type * as StorageService from './services/storage';
|
import type * as StorageService from './services/storage';
|
||||||
|
@ -37,36 +36,12 @@ import type { ConversationController } from './ConversationController';
|
||||||
import type { ReduxActions } from './state/types';
|
import type { ReduxActions } from './state/types';
|
||||||
import type { createStore } from './state/createStore';
|
import type { createStore } from './state/createStore';
|
||||||
import type { createApp } from './state/roots/createApp';
|
import type { createApp } from './state/roots/createApp';
|
||||||
import type { createSafetyNumberViewer } from './state/roots/createSafetyNumberViewer';
|
|
||||||
import type * as appDuck from './state/ducks/app';
|
|
||||||
import type * as callingDuck from './state/ducks/calling';
|
|
||||||
import type * as conversationsDuck from './state/ducks/conversations';
|
|
||||||
import type * as emojisDuck from './state/ducks/emojis';
|
|
||||||
import type * as expirationDuck from './state/ducks/expiration';
|
|
||||||
import type * as itemsDuck from './state/ducks/items';
|
|
||||||
import type * as linkPreviewsDuck from './state/ducks/linkPreviews';
|
|
||||||
import type * as networkDuck from './state/ducks/network';
|
|
||||||
import type * as updatesDuck from './state/ducks/updates';
|
|
||||||
import type * as userDuck from './state/ducks/user';
|
|
||||||
import type * as searchDuck from './state/ducks/search';
|
|
||||||
import type * as stickersDuck from './state/ducks/stickers';
|
|
||||||
import type * as conversationsSelectors from './state/selectors/conversations';
|
|
||||||
import type * as searchSelectors from './state/selectors/search';
|
|
||||||
import type AccountManager from './textsecure/AccountManager';
|
|
||||||
import type Data from './sql/Client';
|
import type Data from './sql/Client';
|
||||||
import type { MessageModel } from './models/messages';
|
import type { MessageModel } from './models/messages';
|
||||||
import type { ConversationModel } from './models/conversations';
|
import type { ConversationModel } from './models/conversations';
|
||||||
import type { BatcherType } from './util/batcher';
|
import type { BatcherType } from './util/batcher';
|
||||||
import type { AttachmentList } from './components/conversation/AttachmentList';
|
|
||||||
import type { ChatColorPicker } from './components/ChatColorPicker';
|
|
||||||
import type { ConfirmationDialog } from './components/ConfirmationDialog';
|
import type { ConfirmationDialog } from './components/ConfirmationDialog';
|
||||||
import type { ContactModal } from './components/conversation/ContactModal';
|
|
||||||
import type { MessageDetail } from './components/conversation/MessageDetail';
|
|
||||||
import type { Quote } from './components/conversation/Quote';
|
|
||||||
import type { StagedLinkPreview } from './components/conversation/StagedLinkPreview';
|
|
||||||
import type { DisappearingTimeDialog } from './components/DisappearingTimeDialog';
|
|
||||||
import type { SignalProtocolStore } from './SignalProtocolStore';
|
import type { SignalProtocolStore } from './SignalProtocolStore';
|
||||||
import type { StartupQueue } from './util/StartupQueue';
|
|
||||||
import type { SocketStatus } from './types/SocketStatus';
|
import type { SocketStatus } from './types/SocketStatus';
|
||||||
import type SyncRequest from './textsecure/SyncRequest';
|
import type SyncRequest from './textsecure/SyncRequest';
|
||||||
import type { MessageController } from './util/MessageController';
|
import type { MessageController } from './util/MessageController';
|
||||||
|
@ -75,49 +50,49 @@ import type { SystemTraySetting } from './types/SystemTraySetting';
|
||||||
import type { UUID } from './types/UUID';
|
import type { UUID } from './types/UUID';
|
||||||
import type { Address } from './types/Address';
|
import type { Address } from './types/Address';
|
||||||
import type { QualifiedAddress } from './types/QualifiedAddress';
|
import type { QualifiedAddress } from './types/QualifiedAddress';
|
||||||
import type { CI } from './CI';
|
import type { CIType } from './CI';
|
||||||
import type { IPCEventsType } from './util/createIPCEvents';
|
import type { IPCEventsType } from './util/createIPCEvents';
|
||||||
import type { SignalContextType } from './windows/context';
|
import type { SignalContextType } from './windows/context';
|
||||||
import type * as Message2 from './types/Message2';
|
import type * as Message2 from './types/Message2';
|
||||||
import type { initializeMigrations } from './signal';
|
import type { initializeMigrations } from './signal';
|
||||||
|
import type { WebAudioRecorder } from './WebAudioRecorder';
|
||||||
|
|
||||||
export { Long } from 'long';
|
export { Long } from 'long';
|
||||||
|
|
||||||
// Synced with the type in ts/shims/showConfirmationDialog
|
export type IPCType = {
|
||||||
// we are duplicating it here because that file cannot import/export.
|
addSetupMenuItems: () => void;
|
||||||
type ConfirmationDialogViewProps = {
|
closeAbout: () => void;
|
||||||
dialogName: string;
|
crashReports: {
|
||||||
cancelText?: string;
|
getCount: () => Promise<number>;
|
||||||
confirmStyle?: 'affirmative' | 'negative';
|
upload: () => Promise<void>;
|
||||||
message: string;
|
erase: () => Promise<void>;
|
||||||
okText: string;
|
};
|
||||||
reject?: (error: Error) => void;
|
drawAttention: () => void;
|
||||||
resolve: () => void;
|
getAutoLaunch: () => Promise<boolean>;
|
||||||
|
getBuiltInImages: () => Promise<Array<string>>;
|
||||||
|
getMediaCameraPermissions: () => Promise<boolean>;
|
||||||
|
getMediaPermissions: () => Promise<boolean>;
|
||||||
|
logAppLoadedEvent?: (options: { processedCount?: number }) => void;
|
||||||
|
readyForUpdates: () => void;
|
||||||
|
removeSetupMenuItems: () => unknown;
|
||||||
|
restart: () => void;
|
||||||
|
setAutoHideMenuBar: (value: boolean) => void;
|
||||||
|
setAutoLaunch: (value: boolean) => Promise<void>;
|
||||||
|
setBadgeCount: (count: number) => void;
|
||||||
|
setMenuBarVisibility: (value: boolean) => void;
|
||||||
|
showDebugLog: () => void;
|
||||||
|
showPermissionsPopup: (
|
||||||
|
forCalling: boolean,
|
||||||
|
forCamera: boolean
|
||||||
|
) => Promise<void>;
|
||||||
|
showSettings: () => void;
|
||||||
|
showWindow: () => void;
|
||||||
|
shutdown: () => void;
|
||||||
|
titleBarDoubleClick: () => void;
|
||||||
|
updateSystemTraySetting: (value: SystemTraySetting) => void;
|
||||||
|
updateTrayIcon: (count: number) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export declare class WebAudioRecorderClass {
|
|
||||||
constructor(
|
|
||||||
node: GainNode,
|
|
||||||
options: {
|
|
||||||
encoding: string;
|
|
||||||
workerDir: string;
|
|
||||||
options?: { timeLimit?: number };
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
// Callbacks
|
|
||||||
onComplete?: (recorder: WebAudioRecorderClass, blob: Blob) => unknown;
|
|
||||||
onError?: (recorder: WebAudioRecorderClass, error: Error) => unknown;
|
|
||||||
onTimeout?: () => unknown;
|
|
||||||
|
|
||||||
// Class properties
|
|
||||||
startRecording: () => unknown;
|
|
||||||
finishRecording: () => unknown;
|
|
||||||
isRecording: () => boolean;
|
|
||||||
cancelRecording: () => unknown;
|
|
||||||
worker: Worker;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type SignalCoreType = {
|
export type SignalCoreType = {
|
||||||
Crypto: typeof Crypto;
|
Crypto: typeof Crypto;
|
||||||
Curve: typeof Curve;
|
Curve: typeof Curve;
|
||||||
|
@ -142,43 +117,20 @@ export type SignalCoreType = {
|
||||||
};
|
};
|
||||||
Util: typeof Util;
|
Util: typeof Util;
|
||||||
Components: {
|
Components: {
|
||||||
AttachmentList: typeof AttachmentList;
|
|
||||||
ChatColorPicker: typeof ChatColorPicker;
|
|
||||||
ConfirmationDialog: typeof ConfirmationDialog;
|
ConfirmationDialog: typeof ConfirmationDialog;
|
||||||
ContactModal: typeof ContactModal;
|
|
||||||
DisappearingTimeDialog: typeof DisappearingTimeDialog;
|
|
||||||
MessageDetail: typeof MessageDetail;
|
|
||||||
Quote: typeof Quote;
|
|
||||||
StagedLinkPreview: typeof StagedLinkPreview;
|
|
||||||
};
|
};
|
||||||
OS: typeof OS;
|
OS: typeof OS;
|
||||||
State: {
|
State: {
|
||||||
createStore: typeof createStore;
|
createStore: typeof createStore;
|
||||||
Roots: {
|
Roots: {
|
||||||
createApp: typeof createApp;
|
createApp: typeof createApp;
|
||||||
createSafetyNumberViewer: typeof createSafetyNumberViewer;
|
|
||||||
};
|
|
||||||
Ducks: {
|
|
||||||
app: typeof appDuck;
|
|
||||||
calling: typeof callingDuck;
|
|
||||||
conversations: typeof conversationsDuck;
|
|
||||||
emojis: typeof emojisDuck;
|
|
||||||
expiration: typeof expirationDuck;
|
|
||||||
items: typeof itemsDuck;
|
|
||||||
linkPreviews: typeof linkPreviewsDuck;
|
|
||||||
network: typeof networkDuck;
|
|
||||||
updates: typeof updatesDuck;
|
|
||||||
user: typeof userDuck;
|
|
||||||
search: typeof searchDuck;
|
|
||||||
stickers: typeof stickersDuck;
|
|
||||||
};
|
|
||||||
Selectors: {
|
|
||||||
conversations: typeof conversationsSelectors;
|
|
||||||
search: typeof searchSelectors;
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
conversationControllerStart: () => void;
|
conversationControllerStart: () => void;
|
||||||
challengeHandler?: ChallengeHandler;
|
challengeHandler?: ChallengeHandler;
|
||||||
|
|
||||||
|
// Test only
|
||||||
|
CI?: CIType;
|
||||||
};
|
};
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
|
@ -190,48 +142,12 @@ declare global {
|
||||||
// Used for sticker creator localization
|
// Used for sticker creator localization
|
||||||
localeMessages: { [key: string]: { message: string } };
|
localeMessages: { [key: string]: { message: string } };
|
||||||
|
|
||||||
// Note: used in background.html, and not type-checked
|
|
||||||
startApp: () => void;
|
|
||||||
|
|
||||||
preloadStartTime: number;
|
|
||||||
preloadEndTime: number;
|
|
||||||
|
|
||||||
removeSetupMenuItems: () => unknown;
|
|
||||||
showPermissionsPopup: (
|
|
||||||
forCalling: boolean,
|
|
||||||
forCamera: boolean
|
|
||||||
) => Promise<void>;
|
|
||||||
|
|
||||||
FontFace: typeof FontFace;
|
|
||||||
$: typeof jQuery;
|
|
||||||
|
|
||||||
imageToBlurHash: typeof imageToBlurHash;
|
|
||||||
isBehindProxy: () => boolean;
|
isBehindProxy: () => boolean;
|
||||||
getAutoLaunch: () => Promise<boolean>;
|
|
||||||
setAutoLaunch: (value: boolean) => Promise<void>;
|
|
||||||
|
|
||||||
Mustache: typeof Mustache;
|
|
||||||
WebAudioRecorder: typeof WebAudioRecorderClass;
|
|
||||||
|
|
||||||
addSetupMenuItems: () => void;
|
|
||||||
attachmentDownloadQueue: Array<MessageModel> | undefined;
|
|
||||||
startupProcessingQueue: StartupQueue | undefined;
|
|
||||||
baseAttachmentsPath: string;
|
|
||||||
baseStickersPath: string;
|
|
||||||
baseTempPath: string;
|
|
||||||
baseDraftPath: string;
|
|
||||||
closeAbout: () => void;
|
|
||||||
crashReports: {
|
|
||||||
getCount: () => Promise<number>;
|
|
||||||
upload: () => Promise<void>;
|
|
||||||
erase: () => Promise<void>;
|
|
||||||
};
|
|
||||||
drawAttention: () => void;
|
|
||||||
enterKeyboardMode: () => void;
|
enterKeyboardMode: () => void;
|
||||||
enterMouseMode: () => void;
|
enterMouseMode: () => void;
|
||||||
getAccountManager: () => AccountManager;
|
getAccountManager: () => AccountManager;
|
||||||
getAppInstance: () => string | undefined;
|
getAppInstance: () => string | undefined;
|
||||||
getBuiltInImages: () => Promise<Array<string>>;
|
|
||||||
getConversations: () => ConversationModelCollectionType;
|
getConversations: () => ConversationModelCollectionType;
|
||||||
getBuildCreation: () => number;
|
getBuildCreation: () => number;
|
||||||
getEnvironment: typeof getEnvironment;
|
getEnvironment: typeof getEnvironment;
|
||||||
|
@ -239,8 +155,6 @@ declare global {
|
||||||
getHostName: () => string;
|
getHostName: () => string;
|
||||||
getInteractionMode: () => 'mouse' | 'keyboard';
|
getInteractionMode: () => 'mouse' | 'keyboard';
|
||||||
getLocale: () => string;
|
getLocale: () => string;
|
||||||
getMediaCameraPermissions: () => Promise<boolean>;
|
|
||||||
getMediaPermissions: () => Promise<boolean>;
|
|
||||||
getServerPublicParams: () => string;
|
getServerPublicParams: () => string;
|
||||||
getSfuUrl: () => string;
|
getSfuUrl: () => string;
|
||||||
getSocketStatus: () => SocketStatus;
|
getSocketStatus: () => SocketStatus;
|
||||||
|
@ -248,11 +162,8 @@ declare global {
|
||||||
getTitle: () => string;
|
getTitle: () => string;
|
||||||
waitForEmptyEventQueue: () => Promise<void>;
|
waitForEmptyEventQueue: () => Promise<void>;
|
||||||
getVersion: () => string;
|
getVersion: () => string;
|
||||||
i18n: LocalizerType;
|
|
||||||
isAfterVersion: (version: string, anotherVersion: string) => boolean;
|
isAfterVersion: (version: string, anotherVersion: string) => boolean;
|
||||||
isBeforeVersion: (version: string, anotherVersion: string) => boolean;
|
isBeforeVersion: (version: string, anotherVersion: string) => boolean;
|
||||||
isFullScreen: () => boolean;
|
|
||||||
isMaximized: () => boolean;
|
|
||||||
initialTheme?: ThemeType;
|
initialTheme?: ThemeType;
|
||||||
libphonenumberInstance: {
|
libphonenumberInstance: {
|
||||||
parse: (number: string) => PhoneNumber;
|
parse: (number: string) => PhoneNumber;
|
||||||
|
@ -261,66 +172,74 @@ declare global {
|
||||||
};
|
};
|
||||||
libphonenumberFormat: typeof PhoneNumberFormat;
|
libphonenumberFormat: typeof PhoneNumberFormat;
|
||||||
nodeSetImmediate: typeof setImmediate;
|
nodeSetImmediate: typeof setImmediate;
|
||||||
onFullScreenChange: (fullScreen: boolean, maximized: boolean) => void;
|
|
||||||
platform: string;
|
platform: string;
|
||||||
preloadedImages: Array<HTMLImageElement>;
|
preloadedImages: Array<HTMLImageElement>;
|
||||||
reduxActions: ReduxActions;
|
|
||||||
reduxStore: Store<StateType>;
|
|
||||||
restart: () => void;
|
|
||||||
setImmediate: typeof setImmediate;
|
setImmediate: typeof setImmediate;
|
||||||
showWindow: () => void;
|
|
||||||
showSettings: () => void;
|
|
||||||
shutdown: () => void;
|
|
||||||
showDebugLog: () => void;
|
|
||||||
sendChallengeRequest: (request: IPCChallengeRequest) => void;
|
sendChallengeRequest: (request: IPCChallengeRequest) => void;
|
||||||
setAutoHideMenuBar: (value: boolean) => void;
|
|
||||||
setBadgeCount: (count: number) => void;
|
|
||||||
setMenuBarVisibility: (value: boolean) => void;
|
|
||||||
updateSystemTraySetting: (value: SystemTraySetting) => void;
|
|
||||||
showConfirmationDialog: (options: ConfirmationDialogViewProps) => void;
|
|
||||||
showKeyboardShortcuts: () => void;
|
showKeyboardShortcuts: () => void;
|
||||||
storage: Storage;
|
storage: Storage;
|
||||||
systemTheme: ThemeType;
|
systemTheme: ThemeType;
|
||||||
textsecure: typeof textsecure;
|
|
||||||
titleBarDoubleClick: () => void;
|
|
||||||
updateTrayIcon: (count: number) => void;
|
|
||||||
Backbone: typeof Backbone;
|
|
||||||
CI?: CI;
|
|
||||||
|
|
||||||
Accessibility: {
|
|
||||||
reducedMotionSetting: boolean;
|
|
||||||
};
|
|
||||||
Signal: SignalCoreType;
|
Signal: SignalCoreType;
|
||||||
|
|
||||||
|
getServerTrustRoot: () => string;
|
||||||
|
logAuthenticatedConnect?: () => void;
|
||||||
|
|
||||||
|
// ========================================================================
|
||||||
|
// The types below have been somewhat organized. See DESKTOP-4801
|
||||||
|
// ========================================================================
|
||||||
|
|
||||||
|
// Backbone
|
||||||
|
Backbone: typeof Backbone;
|
||||||
|
|
||||||
ConversationController: ConversationController;
|
ConversationController: ConversationController;
|
||||||
Events: IPCEventsType;
|
Events: IPCEventsType;
|
||||||
|
FontFace: typeof FontFace;
|
||||||
MessageController: MessageController;
|
MessageController: MessageController;
|
||||||
SignalProtocolStore: typeof SignalProtocolStore;
|
SignalProtocolStore: typeof SignalProtocolStore;
|
||||||
WebAPI: WebAPIConnectType;
|
WebAPI: WebAPIConnectType;
|
||||||
|
WebAudioRecorder: typeof WebAudioRecorder;
|
||||||
Whisper: WhisperType;
|
Whisper: WhisperType;
|
||||||
|
getSignalProtocolStore: () => SignalProtocolStore;
|
||||||
|
i18n: LocalizerType;
|
||||||
|
// Note: used in background.html, and not type-checked
|
||||||
|
startApp: () => void;
|
||||||
|
textsecure: typeof textsecure;
|
||||||
|
|
||||||
getServerTrustRoot: () => string;
|
// IPC
|
||||||
readyForUpdates: () => void;
|
IPC: IPCType;
|
||||||
logAppLoadedEvent?: (options: { processedCount?: number }) => void;
|
|
||||||
logAuthenticatedConnect?: () => void;
|
|
||||||
|
|
||||||
// Runtime Flags
|
// State
|
||||||
isShowingModal?: boolean;
|
reduxActions: ReduxActions;
|
||||||
|
reduxStore: Store<StateType>;
|
||||||
|
|
||||||
// Feature Flags
|
// Feature Flags
|
||||||
GV2_ENABLE_SINGLE_CHANGE_PROCESSING: boolean;
|
Flags: {
|
||||||
GV2_ENABLE_CHANGE_PROCESSING: boolean;
|
GV2_ENABLE_SINGLE_CHANGE_PROCESSING: boolean;
|
||||||
GV2_ENABLE_STATE_PROCESSING: boolean;
|
GV2_ENABLE_CHANGE_PROCESSING: boolean;
|
||||||
GV2_ENABLE_PRE_JOIN_FETCH: boolean;
|
GV2_ENABLE_STATE_PROCESSING: boolean;
|
||||||
GV2_MIGRATION_DISABLE_ADD: boolean;
|
GV2_ENABLE_PRE_JOIN_FETCH: boolean;
|
||||||
GV2_MIGRATION_DISABLE_INVITE: boolean;
|
GV2_MIGRATION_DISABLE_ADD: boolean;
|
||||||
|
GV2_MIGRATION_DISABLE_INVITE: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
RETRY_DELAY: boolean;
|
// Paths
|
||||||
|
BasePaths: {
|
||||||
|
attachments: string;
|
||||||
|
draft: string;
|
||||||
|
stickers: string;
|
||||||
|
temp: string;
|
||||||
|
};
|
||||||
|
|
||||||
// Context Isolation
|
// TODO DESKTOP-4801
|
||||||
SignalContext: SignalContextType;
|
SignalContext: SignalContextType;
|
||||||
|
|
||||||
|
// Used only in preload to calculate load time
|
||||||
|
preloadStartTime: number;
|
||||||
|
preloadEndTime: number;
|
||||||
|
|
||||||
// Test only
|
// Test only
|
||||||
|
RETRY_DELAY: boolean;
|
||||||
assert: typeof assert;
|
assert: typeof assert;
|
||||||
testUtilities: {
|
testUtilities: {
|
||||||
onComplete: (info: unknown) => void;
|
onComplete: (info: unknown) => void;
|
||||||
|
|
|
@ -3,8 +3,9 @@
|
||||||
|
|
||||||
import { ipcRenderer as ipc } from 'electron';
|
import { ipcRenderer as ipc } from 'electron';
|
||||||
import * as semver from 'semver';
|
import * as semver from 'semver';
|
||||||
import { mapValues, noop } from 'lodash';
|
import { mapValues } from 'lodash';
|
||||||
|
|
||||||
|
import type { IPCType } from '../../window.d';
|
||||||
import { parseIntWithFallback } from '../../util/parseIntWithFallback';
|
import { parseIntWithFallback } from '../../util/parseIntWithFallback';
|
||||||
import { UUIDKind } from '../../types/UUID';
|
import { UUIDKind } from '../../types/UUID';
|
||||||
import { ThemeType } from '../../types/Util';
|
import { ThemeType } from '../../types/Util';
|
||||||
|
@ -22,13 +23,16 @@ window.i18n = SignalContext.i18n;
|
||||||
const { config } = window.SignalContext;
|
const { config } = window.SignalContext;
|
||||||
|
|
||||||
// Flags for testing
|
// Flags for testing
|
||||||
window.GV2_ENABLE_SINGLE_CHANGE_PROCESSING = true;
|
const Flags = {
|
||||||
window.GV2_ENABLE_CHANGE_PROCESSING = true;
|
GV2_ENABLE_CHANGE_PROCESSING: true,
|
||||||
window.GV2_ENABLE_STATE_PROCESSING = true;
|
GV2_ENABLE_PRE_JOIN_FETCH: true,
|
||||||
window.GV2_ENABLE_PRE_JOIN_FETCH = true;
|
GV2_ENABLE_SINGLE_CHANGE_PROCESSING: true,
|
||||||
|
GV2_ENABLE_STATE_PROCESSING: true,
|
||||||
|
GV2_MIGRATION_DISABLE_ADD: false,
|
||||||
|
GV2_MIGRATION_DISABLE_INVITE: false,
|
||||||
|
};
|
||||||
|
|
||||||
window.GV2_MIGRATION_DISABLE_ADD = false;
|
window.Flags = Flags;
|
||||||
window.GV2_MIGRATION_DISABLE_INVITE = false;
|
|
||||||
|
|
||||||
window.RETRY_DELAY = false;
|
window.RETRY_DELAY = false;
|
||||||
|
|
||||||
|
@ -55,9 +59,6 @@ window.getExpiration = () => {
|
||||||
}
|
}
|
||||||
return localBuildExpiration;
|
return localBuildExpiration;
|
||||||
};
|
};
|
||||||
window.Accessibility = {
|
|
||||||
reducedMotionSetting: Boolean(config.reducedMotionSetting),
|
|
||||||
};
|
|
||||||
window.getHostName = () => config.hostname;
|
window.getHostName = () => config.hostname;
|
||||||
window.getServerTrustRoot = () => config.serverTrustRoot;
|
window.getServerTrustRoot = () => config.serverTrustRoot;
|
||||||
window.getServerPublicParams = () => config.serverPublicParams;
|
window.getServerPublicParams = () => config.serverPublicParams;
|
||||||
|
@ -78,13 +79,78 @@ if (config.theme === 'light') {
|
||||||
window.initialTheme = ThemeType.dark;
|
window.initialTheme = ThemeType.dark;
|
||||||
}
|
}
|
||||||
|
|
||||||
window.getAutoLaunch = () => {
|
const IPC: IPCType = {
|
||||||
return ipc.invoke('get-auto-launch');
|
addSetupMenuItems: () => ipc.send('add-setup-menu-items'),
|
||||||
};
|
closeAbout: () => ipc.send('close-about'),
|
||||||
window.setAutoLaunch = value => {
|
crashReports: {
|
||||||
return ipc.invoke('set-auto-launch', value);
|
getCount: () => ipc.invoke('crash-reports:get-count'),
|
||||||
|
upload: () => ipc.invoke('crash-reports:upload'),
|
||||||
|
erase: () => ipc.invoke('crash-reports:erase'),
|
||||||
|
},
|
||||||
|
drawAttention: () => {
|
||||||
|
log.info('draw attention');
|
||||||
|
ipc.send('draw-attention');
|
||||||
|
},
|
||||||
|
getAutoLaunch: () => ipc.invoke('get-auto-launch'),
|
||||||
|
getBuiltInImages: () =>
|
||||||
|
new Promise((resolve, reject) => {
|
||||||
|
ipc.once('get-success-built-in-images', (_event, error, value) => {
|
||||||
|
if (error) {
|
||||||
|
return reject(new Error(error));
|
||||||
|
}
|
||||||
|
|
||||||
|
return resolve(value);
|
||||||
|
});
|
||||||
|
ipc.send('get-built-in-images');
|
||||||
|
}),
|
||||||
|
getMediaPermissions: () => ipc.invoke('settings:get:mediaPermissions'),
|
||||||
|
getMediaCameraPermissions: () =>
|
||||||
|
ipc.invoke('settings:get:mediaCameraPermissions'),
|
||||||
|
logAppLoadedEvent: ({ processedCount }) =>
|
||||||
|
ipc.send('signal-app-loaded', {
|
||||||
|
preloadTime: window.preloadEndTime - window.preloadStartTime,
|
||||||
|
connectTime: preloadConnectTime - window.preloadEndTime,
|
||||||
|
processedCount,
|
||||||
|
}),
|
||||||
|
readyForUpdates: () => ipc.send('ready-for-updates'),
|
||||||
|
removeSetupMenuItems: () => ipc.send('remove-setup-menu-items'),
|
||||||
|
restart: () => {
|
||||||
|
log.info('restart');
|
||||||
|
ipc.send('restart');
|
||||||
|
},
|
||||||
|
setAutoHideMenuBar: autoHide => ipc.send('set-auto-hide-menu-bar', autoHide),
|
||||||
|
setAutoLaunch: value => ipc.invoke('set-auto-launch', value),
|
||||||
|
setBadgeCount: count => ipc.send('set-badge-count', count),
|
||||||
|
setMenuBarVisibility: visibility =>
|
||||||
|
ipc.send('set-menu-bar-visibility', visibility),
|
||||||
|
showDebugLog: () => {
|
||||||
|
log.info('showDebugLog');
|
||||||
|
ipc.send('show-debug-log');
|
||||||
|
},
|
||||||
|
showPermissionsPopup: (forCalling, forCamera) =>
|
||||||
|
ipc.invoke('show-permissions-popup', forCalling, forCamera),
|
||||||
|
showSettings: () => ipc.send('show-settings'),
|
||||||
|
showWindow: () => {
|
||||||
|
log.info('show window');
|
||||||
|
ipc.send('show-window');
|
||||||
|
},
|
||||||
|
shutdown: () => {
|
||||||
|
log.info('shutdown');
|
||||||
|
ipc.send('shutdown');
|
||||||
|
},
|
||||||
|
titleBarDoubleClick: () => {
|
||||||
|
ipc.send('title-bar-double-click');
|
||||||
|
},
|
||||||
|
updateSystemTraySetting: (
|
||||||
|
systemTraySetting /* : Readonly<SystemTraySetting> */
|
||||||
|
) => {
|
||||||
|
void ipc.invoke('update-system-tray-setting', systemTraySetting);
|
||||||
|
},
|
||||||
|
updateTrayIcon: unreadCount => ipc.send('update-tray-icon', unreadCount),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
window.IPC = IPC;
|
||||||
|
|
||||||
window.isBeforeVersion = (toCheck, baseVersion) => {
|
window.isBeforeVersion = (toCheck, baseVersion) => {
|
||||||
try {
|
try {
|
||||||
return semver.lt(toCheck, baseVersion);
|
return semver.lt(toCheck, baseVersion);
|
||||||
|
@ -108,8 +174,6 @@ window.isAfterVersion = (toCheck, baseVersion) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
window.setBadgeCount = count => ipc.send('set-badge-count', count);
|
|
||||||
|
|
||||||
let preloadConnectTime = 0;
|
let preloadConnectTime = 0;
|
||||||
window.logAuthenticatedConnect = () => {
|
window.logAuthenticatedConnect = () => {
|
||||||
if (preloadConnectTime === 0) {
|
if (preloadConnectTime === 0) {
|
||||||
|
@ -117,13 +181,6 @@ window.logAuthenticatedConnect = () => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
window.logAppLoadedEvent = ({ processedCount }) =>
|
|
||||||
ipc.send('signal-app-loaded', {
|
|
||||||
preloadTime: window.preloadEndTime - window.preloadStartTime,
|
|
||||||
connectTime: preloadConnectTime - window.preloadEndTime,
|
|
||||||
processedCount,
|
|
||||||
});
|
|
||||||
|
|
||||||
// We never do these in our code, so we'll prevent it everywhere
|
// We never do these in our code, so we'll prevent it everywhere
|
||||||
window.open = () => null;
|
window.open = () => null;
|
||||||
|
|
||||||
|
@ -133,50 +190,6 @@ if (!config.enableCI && config.environment !== 'test') {
|
||||||
window.eval = global.eval = () => null;
|
window.eval = global.eval = () => null;
|
||||||
}
|
}
|
||||||
|
|
||||||
window.drawAttention = () => {
|
|
||||||
log.info('draw attention');
|
|
||||||
ipc.send('draw-attention');
|
|
||||||
};
|
|
||||||
window.showWindow = () => {
|
|
||||||
log.info('show window');
|
|
||||||
ipc.send('show-window');
|
|
||||||
};
|
|
||||||
|
|
||||||
window.titleBarDoubleClick = () => {
|
|
||||||
ipc.send('title-bar-double-click');
|
|
||||||
};
|
|
||||||
|
|
||||||
window.setAutoHideMenuBar = autoHide =>
|
|
||||||
ipc.send('set-auto-hide-menu-bar', autoHide);
|
|
||||||
|
|
||||||
window.setMenuBarVisibility = visibility =>
|
|
||||||
ipc.send('set-menu-bar-visibility', visibility);
|
|
||||||
|
|
||||||
window.updateSystemTraySetting = (
|
|
||||||
systemTraySetting /* : Readonly<SystemTraySetting> */
|
|
||||||
) => {
|
|
||||||
void ipc.invoke('update-system-tray-setting', systemTraySetting);
|
|
||||||
};
|
|
||||||
|
|
||||||
window.restart = () => {
|
|
||||||
log.info('restart');
|
|
||||||
ipc.send('restart');
|
|
||||||
};
|
|
||||||
window.shutdown = () => {
|
|
||||||
log.info('shutdown');
|
|
||||||
ipc.send('shutdown');
|
|
||||||
};
|
|
||||||
window.showDebugLog = () => {
|
|
||||||
log.info('showDebugLog');
|
|
||||||
ipc.send('show-debug-log');
|
|
||||||
};
|
|
||||||
|
|
||||||
window.closeAbout = () => ipc.send('close-about');
|
|
||||||
window.readyForUpdates = () => ipc.send('ready-for-updates');
|
|
||||||
|
|
||||||
window.updateTrayIcon = unreadCount =>
|
|
||||||
ipc.send('update-tray-icon', unreadCount);
|
|
||||||
|
|
||||||
ipc.on('additional-log-data-request', async event => {
|
ipc.on('additional-log-data-request', async event => {
|
||||||
const ourConversation = window.ConversationController.getOurConversation();
|
const ourConversation = window.ConversationController.getOurConversation();
|
||||||
const ourCapabilities = ourConversation
|
const ourCapabilities = ourConversation
|
||||||
|
@ -238,10 +251,14 @@ ipc.on('power-channel:lock-screen', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
ipc.on('window:set-window-stats', (_event, stats) => {
|
ipc.on('window:set-window-stats', (_event, stats) => {
|
||||||
if (!window.Whisper.events) {
|
if (!window.reduxActions) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
window.Whisper.events.trigger('setWindowStats', stats);
|
|
||||||
|
window.reduxActions.user.userChanged({
|
||||||
|
isMainWindowMaximized: stats.isMaximized,
|
||||||
|
isMainWindowFullScreen: stats.isFullScreen,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
ipc.on('window:set-menu-options', (_event, options) => {
|
ipc.on('window:set-menu-options', (_event, options) => {
|
||||||
|
@ -253,28 +270,8 @@ ipc.on('window:set-menu-options', (_event, options) => {
|
||||||
|
|
||||||
window.sendChallengeRequest = request => ipc.send('challenge:request', request);
|
window.sendChallengeRequest = request => ipc.send('challenge:request', request);
|
||||||
|
|
||||||
{
|
|
||||||
let isFullScreen = Boolean(config.isMainWindowFullScreen);
|
|
||||||
let isMaximized = Boolean(config.isMainWindowMaximized);
|
|
||||||
|
|
||||||
window.isFullScreen = () => isFullScreen;
|
|
||||||
window.isMaximized = () => isMaximized;
|
|
||||||
// This is later overwritten.
|
|
||||||
window.onFullScreenChange = noop;
|
|
||||||
|
|
||||||
ipc.on('window:set-window-stats', (_event, stats) => {
|
|
||||||
isFullScreen = Boolean(stats.isFullScreen);
|
|
||||||
isMaximized = Boolean(stats.isMaximized);
|
|
||||||
window.onFullScreenChange(isFullScreen, isMaximized);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Settings-related events
|
// Settings-related events
|
||||||
|
|
||||||
window.showSettings = () => ipc.send('show-settings');
|
|
||||||
window.showPermissionsPopup = (forCalling, forCamera) =>
|
|
||||||
ipc.invoke('show-permissions-popup', forCalling, forCamera);
|
|
||||||
|
|
||||||
ipc.on('show-keyboard-shortcuts', () => {
|
ipc.on('show-keyboard-shortcuts', () => {
|
||||||
window.Events.showKeyboardShortcuts();
|
window.Events.showKeyboardShortcuts();
|
||||||
});
|
});
|
||||||
|
@ -285,18 +282,6 @@ ipc.on('remove-dark-overlay', () => {
|
||||||
window.Events.removeDarkOverlay();
|
window.Events.removeDarkOverlay();
|
||||||
});
|
});
|
||||||
|
|
||||||
window.getBuiltInImages = () =>
|
|
||||||
new Promise((resolve, reject) => {
|
|
||||||
ipc.once('get-success-built-in-images', (_event, error, value) => {
|
|
||||||
if (error) {
|
|
||||||
return reject(new Error(error));
|
|
||||||
}
|
|
||||||
|
|
||||||
return resolve(value);
|
|
||||||
});
|
|
||||||
ipc.send('get-built-in-images');
|
|
||||||
});
|
|
||||||
|
|
||||||
ipc.on('delete-all-data', async () => {
|
ipc.on('delete-all-data', async () => {
|
||||||
const { deleteAllData } = window.Events;
|
const { deleteAllData } = window.Events;
|
||||||
if (!deleteAllData) {
|
if (!deleteAllData) {
|
||||||
|
@ -373,6 +358,3 @@ ipc.on('show-release-notes', () => {
|
||||||
showReleaseNotes();
|
showReleaseNotes();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
window.addSetupMenuItems = () => ipc.send('add-setup-menu-items');
|
|
||||||
window.removeSetupMenuItems = () => ipc.send('remove-setup-menu-items');
|
|
||||||
|
|
|
@ -9,7 +9,6 @@ import * as moment from 'moment';
|
||||||
import 'moment/min/locales.min';
|
import 'moment/min/locales.min';
|
||||||
|
|
||||||
import { textsecure } from '../../textsecure';
|
import { textsecure } from '../../textsecure';
|
||||||
import { imageToBlurHash } from '../../util/imageToBlurHash';
|
|
||||||
import * as Attachments from '../attachments';
|
import * as Attachments from '../attachments';
|
||||||
import { setup } from '../../signal';
|
import { setup } from '../../signal';
|
||||||
import { addSensitivePath } from '../../util/privacy';
|
import { addSensitivePath } from '../../util/privacy';
|
||||||
|
@ -38,7 +37,6 @@ window.WebAPI = window.textsecure.WebAPI.initialize({
|
||||||
version: config.version,
|
version: config.version,
|
||||||
});
|
});
|
||||||
|
|
||||||
window.imageToBlurHash = imageToBlurHash;
|
|
||||||
window.libphonenumberInstance = PhoneNumberUtil.getInstance();
|
window.libphonenumberInstance = PhoneNumberUtil.getInstance();
|
||||||
window.libphonenumberFormat = PhoneNumberFormat;
|
window.libphonenumberFormat = PhoneNumberFormat;
|
||||||
|
|
||||||
|
@ -56,12 +54,14 @@ moment.updateLocale(locale, {
|
||||||
moment.locale(locale);
|
moment.locale(locale);
|
||||||
|
|
||||||
const userDataPath = SignalContext.getPath('userData');
|
const userDataPath = SignalContext.getPath('userData');
|
||||||
window.baseAttachmentsPath = Attachments.getPath(userDataPath);
|
window.BasePaths = {
|
||||||
window.baseStickersPath = Attachments.getStickersPath(userDataPath);
|
attachments: Attachments.getPath(userDataPath),
|
||||||
window.baseTempPath = Attachments.getTempPath(userDataPath);
|
draft: Attachments.getDraftPath(userDataPath),
|
||||||
window.baseDraftPath = Attachments.getDraftPath(userDataPath);
|
stickers: Attachments.getStickersPath(userDataPath),
|
||||||
|
temp: Attachments.getTempPath(userDataPath),
|
||||||
|
};
|
||||||
|
|
||||||
addSensitivePath(window.baseAttachmentsPath);
|
addSensitivePath(window.BasePaths.attachments);
|
||||||
if (config.crashDumpsPath) {
|
if (config.crashDumpsPath) {
|
||||||
addSensitivePath(config.crashDumpsPath);
|
addSensitivePath(config.crashDumpsPath);
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,6 @@ if (config.environment === 'test') {
|
||||||
}
|
}
|
||||||
if (config.enableCI) {
|
if (config.enableCI) {
|
||||||
console.log('Importing CI infrastructure...');
|
console.log('Importing CI infrastructure...');
|
||||||
const { CI } = require('../../CI');
|
const { getCI } = require('../../CI');
|
||||||
window.CI = new CI(window.getTitle());
|
window.Signal.CI = getCI(window.getTitle());
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,23 @@
|
||||||
// Copyright 2017 Signal Messenger, LLC
|
// Copyright 2017 Signal Messenger, LLC
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
import { clone } from 'lodash';
|
||||||
|
import { contextBridge } from 'electron';
|
||||||
|
|
||||||
import * as log from '../../logging/log';
|
import * as log from '../../logging/log';
|
||||||
|
import { SignalContext } from '../context';
|
||||||
|
|
||||||
import './phase1-ipc';
|
import './phase1-ipc';
|
||||||
import '../preload';
|
import '../preload';
|
||||||
import './phase2-dependencies';
|
import './phase2-dependencies';
|
||||||
import './phase3-post-signal';
|
import './phase3-post-signal';
|
||||||
import './phase4-test';
|
import './phase4-test';
|
||||||
|
import '../../backbone/reliable_trigger';
|
||||||
|
|
||||||
|
import { WebAudioRecorder } from '../../WebAudioRecorder';
|
||||||
|
import { getSignalProtocolStore } from '../../SignalProtocolStore';
|
||||||
|
import { start as startConversationController } from '../../ConversationController';
|
||||||
|
import { MessageController } from '../../util/MessageController';
|
||||||
|
|
||||||
window.addEventListener('contextmenu', e => {
|
window.addEventListener('contextmenu', e => {
|
||||||
const node = e.target as Element | null;
|
const node = e.target as Element | null;
|
||||||
|
@ -27,3 +37,122 @@ window.addEventListener('contextmenu', e => {
|
||||||
if (window.SignalContext.config.proxyUrl) {
|
if (window.SignalContext.config.proxyUrl) {
|
||||||
log.info('Using provided proxy url');
|
log.info('Using provided proxy url');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const isTestElectron = process.env.TEST_QUIT_ON_COMPLETE;
|
||||||
|
|
||||||
|
window.Whisper.events = clone(window.Backbone.Events);
|
||||||
|
MessageController.install();
|
||||||
|
startConversationController();
|
||||||
|
|
||||||
|
if (isTestElectron) {
|
||||||
|
window.getSignalProtocolStore = getSignalProtocolStore;
|
||||||
|
} else {
|
||||||
|
contextBridge.exposeInMainWorld('SignalContext', SignalContext);
|
||||||
|
|
||||||
|
contextBridge.exposeInMainWorld('Backbone', window.Backbone);
|
||||||
|
|
||||||
|
contextBridge.exposeInMainWorld('BasePaths', window.BasePaths);
|
||||||
|
contextBridge.exposeInMainWorld(
|
||||||
|
'ConversationController',
|
||||||
|
window.ConversationController
|
||||||
|
);
|
||||||
|
contextBridge.exposeInMainWorld('Events', window.Events);
|
||||||
|
contextBridge.exposeInMainWorld('Flags', window.Flags);
|
||||||
|
contextBridge.exposeInMainWorld('IPC', window.IPC);
|
||||||
|
contextBridge.exposeInMainWorld(
|
||||||
|
'SignalProtocolStore',
|
||||||
|
window.SignalProtocolStore
|
||||||
|
);
|
||||||
|
contextBridge.exposeInMainWorld(
|
||||||
|
'getSignalProtocolStore',
|
||||||
|
getSignalProtocolStore
|
||||||
|
);
|
||||||
|
contextBridge.exposeInMainWorld(
|
||||||
|
'MessageController',
|
||||||
|
window.MessageController
|
||||||
|
);
|
||||||
|
contextBridge.exposeInMainWorld('WebAudioRecorder', WebAudioRecorder);
|
||||||
|
contextBridge.exposeInMainWorld('WebAPI', window.WebAPI);
|
||||||
|
contextBridge.exposeInMainWorld('Whisper', window.Whisper);
|
||||||
|
contextBridge.exposeInMainWorld('i18n', window.i18n);
|
||||||
|
contextBridge.exposeInMainWorld('reduxActions', window.reduxActions);
|
||||||
|
contextBridge.exposeInMainWorld('reduxStore', window.reduxStore);
|
||||||
|
contextBridge.exposeInMainWorld('startApp', window.startApp);
|
||||||
|
contextBridge.exposeInMainWorld('textsecure', window.textsecure);
|
||||||
|
|
||||||
|
// TODO DESKTOP-4801
|
||||||
|
contextBridge.exposeInMainWorld('ROOT_PATH', window.ROOT_PATH);
|
||||||
|
contextBridge.exposeInMainWorld('Signal', window.Signal);
|
||||||
|
contextBridge.exposeInMainWorld(
|
||||||
|
'enterKeyboardMode',
|
||||||
|
window.enterKeyboardMode
|
||||||
|
);
|
||||||
|
contextBridge.exposeInMainWorld('enterMouseMode', window.enterMouseMode);
|
||||||
|
contextBridge.exposeInMainWorld(
|
||||||
|
'getAccountManager',
|
||||||
|
window.getAccountManager
|
||||||
|
);
|
||||||
|
contextBridge.exposeInMainWorld('getAppInstance', window.getAppInstance);
|
||||||
|
contextBridge.exposeInMainWorld('getBuildCreation', window.getBuildCreation);
|
||||||
|
contextBridge.exposeInMainWorld('getConversations', window.getConversations);
|
||||||
|
contextBridge.exposeInMainWorld('getEnvironment', window.getEnvironment);
|
||||||
|
contextBridge.exposeInMainWorld('getExpiration', window.getExpiration);
|
||||||
|
contextBridge.exposeInMainWorld('getHostName', window.getHostName);
|
||||||
|
contextBridge.exposeInMainWorld(
|
||||||
|
'getInteractionMode',
|
||||||
|
window.getInteractionMode
|
||||||
|
);
|
||||||
|
contextBridge.exposeInMainWorld('getLocale', window.getLocale);
|
||||||
|
contextBridge.exposeInMainWorld(
|
||||||
|
'getServerPublicParams',
|
||||||
|
window.getServerPublicParams
|
||||||
|
);
|
||||||
|
contextBridge.exposeInMainWorld(
|
||||||
|
'getServerTrustRoot',
|
||||||
|
window.getServerTrustRoot
|
||||||
|
);
|
||||||
|
contextBridge.exposeInMainWorld('getSfuUrl', window.getSfuUrl);
|
||||||
|
contextBridge.exposeInMainWorld('getSocketStatus', window.getSocketStatus);
|
||||||
|
contextBridge.exposeInMainWorld('getSyncRequest', window.getSyncRequest);
|
||||||
|
contextBridge.exposeInMainWorld('getTitle', window.getTitle);
|
||||||
|
contextBridge.exposeInMainWorld('getVersion', window.getVersion);
|
||||||
|
contextBridge.exposeInMainWorld('initialTheme', window.initialTheme);
|
||||||
|
contextBridge.exposeInMainWorld('isAfterVersion', window.isAfterVersion);
|
||||||
|
contextBridge.exposeInMainWorld('isBeforeVersion', window.isBeforeVersion);
|
||||||
|
contextBridge.exposeInMainWorld('isBehindProxy', window.isBehindProxy);
|
||||||
|
contextBridge.exposeInMainWorld(
|
||||||
|
'libphonenumberFormat',
|
||||||
|
window.libphonenumberFormat
|
||||||
|
);
|
||||||
|
contextBridge.exposeInMainWorld(
|
||||||
|
'libphonenumberInstance',
|
||||||
|
window.libphonenumberInstance
|
||||||
|
);
|
||||||
|
contextBridge.exposeInMainWorld('localeMessages', window.localeMessages);
|
||||||
|
contextBridge.exposeInMainWorld(
|
||||||
|
'logAuthenticatedConnect',
|
||||||
|
window.logAuthenticatedConnect
|
||||||
|
);
|
||||||
|
contextBridge.exposeInMainWorld('nodeSetImmediate', window.nodeSetImmediate);
|
||||||
|
contextBridge.exposeInMainWorld('platform', window.platform);
|
||||||
|
contextBridge.exposeInMainWorld('preloadedImages', window.preloadedImages);
|
||||||
|
contextBridge.exposeInMainWorld(
|
||||||
|
'sendChallengeRequest',
|
||||||
|
window.sendChallengeRequest
|
||||||
|
);
|
||||||
|
contextBridge.exposeInMainWorld('setImmediate', window.setImmediate);
|
||||||
|
contextBridge.exposeInMainWorld(
|
||||||
|
'showKeyboardShortcuts',
|
||||||
|
window.showKeyboardShortcuts
|
||||||
|
);
|
||||||
|
contextBridge.exposeInMainWorld('storage', window.storage);
|
||||||
|
contextBridge.exposeInMainWorld('systemTheme', window.systemTheme);
|
||||||
|
contextBridge.exposeInMainWorld(
|
||||||
|
'waitForEmptyEventQueue',
|
||||||
|
window.waitForEmptyEventQueue
|
||||||
|
);
|
||||||
|
|
||||||
|
contextBridge.exposeInMainWorld('assert', window.assert);
|
||||||
|
contextBridge.exposeInMainWorld('RETRY_DELAY', window.RETRY_DELAY);
|
||||||
|
contextBridge.exposeInMainWorld('testUtilities', window.testUtilities);
|
||||||
|
}
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
// Copyright 2017 Signal Messenger, LLC
|
// Copyright 2017 Signal Messenger, LLC
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
import { ipcRenderer as ipc } from 'electron';
|
|
||||||
|
|
||||||
import { installCallback, installSetting } from '../util/preload';
|
import { installCallback, installSetting } from '../util/preload';
|
||||||
|
|
||||||
// ChatColorPicker redux hookups
|
// ChatColorPicker redux hookups
|
||||||
|
@ -71,14 +69,3 @@ installCallback('getAvailableIODevices');
|
||||||
installSetting('preferredAudioInputDevice');
|
installSetting('preferredAudioInputDevice');
|
||||||
installSetting('preferredAudioOutputDevice');
|
installSetting('preferredAudioOutputDevice');
|
||||||
installSetting('preferredVideoInputDevice');
|
installSetting('preferredVideoInputDevice');
|
||||||
|
|
||||||
window.getMediaPermissions = () => ipc.invoke('settings:get:mediaPermissions');
|
|
||||||
|
|
||||||
window.getMediaCameraPermissions = () =>
|
|
||||||
ipc.invoke('settings:get:mediaCameraPermissions');
|
|
||||||
|
|
||||||
window.crashReports = {
|
|
||||||
getCount: () => ipc.invoke('crash-reports:get-count'),
|
|
||||||
upload: () => ipc.invoke('crash-reports:upload'),
|
|
||||||
erase: () => ipc.invoke('crash-reports:erase'),
|
|
||||||
};
|
|
||||||
|
|
13
yarn.lock
13
yarn.lock
|
@ -2178,10 +2178,10 @@
|
||||||
node-gyp-build "^4.2.3"
|
node-gyp-build "^4.2.3"
|
||||||
uuid "^8.3.0"
|
uuid "^8.3.0"
|
||||||
|
|
||||||
"@signalapp/mock-server@2.12.0":
|
"@signalapp/mock-server@2.12.1":
|
||||||
version "2.12.0"
|
version "2.12.1"
|
||||||
resolved "https://registry.yarnpkg.com/@signalapp/mock-server/-/mock-server-2.12.0.tgz#ca7ed46406603746d2cb49349d3fb843fe4b45eb"
|
resolved "https://registry.yarnpkg.com/@signalapp/mock-server/-/mock-server-2.12.1.tgz#456ee3c9458363d333bd803910f874d388f28d04"
|
||||||
integrity sha512-d6OFulnOWG0U3Xj0ChBGHThyBlno54Va+Cb/E0ggEx7PhD1Y9GX8gLc0V8HHP203b+1JThX4SdK03dpsOILxfQ==
|
integrity sha512-c8ndwTtDoRPRZAWjbBeVH79DQLA4UvKPztgJUqtDOqFCju1+NMKbjxrK+v1PJQvhhOMANbG6Z3qTCl4UGcm/zQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@signalapp/libsignal-client" "^0.20.0"
|
"@signalapp/libsignal-client" "^0.20.0"
|
||||||
debug "^4.3.2"
|
debug "^4.3.2"
|
||||||
|
@ -4032,11 +4032,6 @@
|
||||||
resolved "https://registry.yarnpkg.com/@types/ms/-/ms-0.7.31.tgz#31b7ca6407128a3d2bbc27fe2d21b345397f6197"
|
resolved "https://registry.yarnpkg.com/@types/ms/-/ms-0.7.31.tgz#31b7ca6407128a3d2bbc27fe2d21b345397f6197"
|
||||||
integrity sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==
|
integrity sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==
|
||||||
|
|
||||||
"@types/mustache@4.1.2":
|
|
||||||
version "4.1.2"
|
|
||||||
resolved "https://registry.yarnpkg.com/@types/mustache/-/mustache-4.1.2.tgz#d0e158013c81674a5b6d8780bc3fe234e1804eaf"
|
|
||||||
integrity sha512-c4OVMMcyodKQ9dpwBwh3ofK9P6U9ZktKU9S+p33UqwMNN1vlv2P0zJZUScTshnx7OEoIIRcCFNQ904sYxZz8kg==
|
|
||||||
|
|
||||||
"@types/node-fetch@2.6.2":
|
"@types/node-fetch@2.6.2":
|
||||||
version "2.6.2"
|
version "2.6.2"
|
||||||
resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.6.2.tgz#d1a9c5fd049d9415dce61571557104dec3ec81da"
|
resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.6.2.tgz#d1a9c5fd049d9415dce61571557104dec3ec81da"
|
||||||
|
|
Loading…
Reference in a new issue