Infinte duration toasts

pull/713/head
Vincent 5 years ago
parent 6ee5d041fb
commit 8074756dc2

@ -395,6 +395,9 @@
"description":
"When there are multiple previously-verified group members with safety number changes, a banner will be shown. The list of contacts with safety number changes is shown, and this text introduces that list."
},
"changedSinceVerifiedTitle": {
"message": "Safety Number Changed"
},
"changedSinceVerifiedMultiple": {
"message":
"Your safety numbers with multiple group members have changed since you last verified. This could mean that someone is trying to intercept your communication or that they have simply reinstalled Signal.",
@ -2024,7 +2027,7 @@
},
"banUser": {
"message": "Ban user",
"message": "Ban User",
"description": "Ban user from public chat by public key."
},

@ -802,8 +802,6 @@
appView.openConversation(groupId, {});
};
window.confirmationDialog = params => {
const confirmDialog = new Whisper.SessionConfirmView({
el: $('#session-confirm-container'),
@ -816,7 +814,7 @@
hideCancel: params.hideCancel || false,
});
confirmDialog.render();
}
};
window.generateID = () =>
Math.random()
@ -834,6 +832,7 @@
id: options.id || window.generateID(),
description: options.description || '',
type: options.type || '',
shouldFade: options.shouldFade,
};
// Give all toasts an ID. User may define.

@ -1030,9 +1030,10 @@
},
banUser() {
window.Whisper.events.trigger('showConfirmationDialog', {
window.confirmationDialog({
title: i18n('banUser'),
message: i18n('banUserConfirm'),
onOk: async () => {
resolve: async () => {
const source = this.get('source');
const conversation = this.getConversation();

@ -61,7 +61,9 @@ const {
const { SessionToast } = require('../../ts/components/session/SessionToast');
const { SessionToggle } = require('../../ts/components/session/SessionToggle');
const { SessionModal } = require('../../ts/components/session/SessionModal');
const { SessionConfirm } = require('../../ts/components/session/SessionConfirm');
const {
SessionConfirm,
} = require('../../ts/components/session/SessionConfirm');
const {
SessionDropdown,
} = require('../../ts/components/session/SessionDropdown');

@ -1,4 +1,4 @@
/* global $, Whisper, i18n */
/* global $, i18n */
$(document).on('keyup', e => {
'use strict';
@ -8,7 +8,7 @@ $(document).on('keyup', e => {
}
});
const dialogParams = {
window.confirmationDialog({
title: i18n('audioPermissionNeeded'),
okText: i18n('allowAccess'),
resolve: () => {
@ -18,6 +18,4 @@ const dialogParams = {
window.closePermissionsPopup();
},
onClose: window.closePermissionsPopup,
};
window.confirmationDialog(dialogParams);
});

@ -1308,7 +1308,7 @@
},
forceSend({ contact, message }) {
const dialog = new Whisper.ConfirmationDialogView({
window.confirmationDialog({
message: i18n('identityKeyErrorOnSend', [
contact.getTitle(),
contact.getTitle(),
@ -1328,11 +1328,7 @@
message.resend(contact.id);
},
});
this.$el.prepend(dialog.el);
dialog.focusCancel();
},
showSafetyNumber(providedModel) {
@ -1439,14 +1435,11 @@
return;
}
const dialog = new Whisper.ConfirmationDialogView({
window.confirmationDialog({
message: warningMessage,
okText: i18n('delete'),
resolve: doDelete,
});
this.$el.prepend(dialog.el);
dialog.focusCancel();
},
deleteMessage(message) {
@ -1670,7 +1663,8 @@
}
}
const dialog = new Whisper.ConfirmationDialogView({
window.confirmationDialog({
title: i18n('changedSinceVerifiedTitle'),
message,
okText: i18n('sendAnyway'),
resolve: () => {
@ -1680,9 +1674,6 @@
this.focusMessageFieldAndClearDisabled();
},
});
this.$el.prepend(dialog.el);
dialog.focusCancel();
},
stripQuery(text, cursorPos) {

@ -54,13 +54,12 @@
toast.render();
},
showConfirmationDialog({ title, message, onOk, onCancel }) {
const dialog = new Whisper.ConfirmationDialogView({
window.confirmationDialog({
title,
message,
resolve: onOk,
reject: onCancel,
});
this.el.append(dialog.el);
},
});

@ -61,16 +61,14 @@
onSafetyNumberChanged() {
this.model.getProfiles().then(this.loadKeys.bind(this));
const dialog = new Whisper.ConfirmationDialogView({
window.confirmationDialog({
title: i18n('changedSinceVerifiedTitle'),
message: i18n('changedRightAfterVerify', [
this.model.getTitle(),
this.model.getTitle(),
]),
hideCancel: true,
});
dialog.$el.insertBefore(this.el);
dialog.focusCancel();
},
toggleVerified() {
this.$('button.verify').attr('disabled', true);

@ -35,14 +35,14 @@
ok() {
this.$('.session-confirm-wrapper').remove();
if (this.props.resolve){
this.props.resolve()
if (this.props.resolve) {
this.props.resolve();
}
},
cancel() {
this.$('.session-confirm-wrapper').remove();
if (this.props.reject) {
this.props.reject()
this.props.reject();
}
},
onKeyup(event) {
@ -50,6 +50,5 @@
this.cancel();
}
},
});
})();

@ -32,13 +32,18 @@
this.props.id = options.id;
this.props.description = options.description || '';
this.props.type = options.type || '';
this.props.shouldFade = options.shouldFade !== false;
this.toastView.update(this.props);
this.showToast();
clearTimeout(this.timer);
this.timer = setTimeout(this.fadeToast.bind(this), 4000);
if (this.timer) {
clearTimeout(this.timer);
}
if (this.props.shouldFade) {
this.timer = setTimeout(this.fadeToast.bind(this), 4000);
}
},
showToast() {

@ -53,13 +53,12 @@
},
confirm(message, okText) {
return new Promise((resolve, reject) => {
const dialog = new Whisper.ConfirmationDialogView({
message,
window.confirmationDialog({
title: message,
okText,
resolve,
reject,
});
this.$el.append(dialog.el);
});
},
},

@ -11,49 +11,63 @@
},
"main": "main.js",
"scripts": {
"postinstall": "electron-builder install-app-deps && rimraf node_modules/dtrace-provider",
"postinstall":
"electron-builder install-app-deps && rimraf node_modules/dtrace-provider",
"start": "electron .",
"start-multi": "NODE_APP_INSTANCE=1 electron .",
"start-multi2": "NODE_APP_INSTANCE=2 electron .",
"start-prod": "NODE_ENV=production NODE_APP_INSTANCE=devprod LOKI_DEV=1 electron .",
"start-prod-multi": "NODE_ENV=production NODE_APP_INSTANCE=devprod1 LOKI_DEV=1 electron .",
"start-prod":
"NODE_ENV=production NODE_APP_INSTANCE=devprod LOKI_DEV=1 electron .",
"start-prod-multi":
"NODE_ENV=production NODE_APP_INSTANCE=devprod1 LOKI_DEV=1 electron .",
"grunt": "grunt",
"icon-gen": "electron-icon-maker --input=images/icon_1024.png --output=./build",
"icon-gen":
"electron-icon-maker --input=images/icon_1024.png --output=./build",
"generate": "yarn icon-gen && yarn grunt",
"build": "electron-builder --config.extraMetadata.environment=$SIGNAL_ENV",
"build-release": "export SIGNAL_ENV=production && npm run build -- --config.directories.output=release",
"build-release":
"export SIGNAL_ENV=production && npm run build -- --config.directories.output=release",
"sign-release": "node ts/updater/generateSignature.js",
"build-module-protobuf": "pbjs --target static-module --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",
"build-module-protobuf":
"pbjs --target static-module --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",
"build-protobuf": "yarn build-module-protobuf",
"clean-protobuf": "yarn clean-module-protobuf",
"prepare-beta-build": "node prepare_beta_build.js",
"prepare-import-build": "node prepare_import_build.js",
"publish-to-apt": "NAME=$npm_package_name VERSION=$npm_package_version ./aptly.sh",
"publish-to-apt":
"NAME=$npm_package_name VERSION=$npm_package_version ./aptly.sh",
"test": "yarn test-node && yarn test-electron",
"test-view": "NODE_ENV=test yarn run start",
"test-lib-view": "NODE_ENV=test-lib yarn run start",
"test-loki-view": "NODE_ENV=test-loki yarn run start",
"test-electron": "yarn grunt test",
"test-node": "mocha --recursive --exit test/app test/modules ts/test libloki/test/node",
"test-node-coverage": "nyc --reporter=lcov --reporter=text mocha --recursive test/app test/modules ts/test libloki/test/node",
"test-node-coverage-html": "nyc --reporter=lcov --reporter=html mocha --recursive test/app test/modules ts/test libloki/test/node",
"test-node":
"mocha --recursive --exit test/app test/modules ts/test libloki/test/node",
"test-node-coverage":
"nyc --reporter=lcov --reporter=text mocha --recursive test/app test/modules ts/test libloki/test/node",
"test-node-coverage-html":
"nyc --reporter=lcov --reporter=html mocha --recursive test/app test/modules ts/test libloki/test/node",
"eslint": "eslint .",
"lint": "yarn format --list-different && yarn lint-windows",
"dev-lint": "yarn format --list-different; yarn lint-windows",
"lint-windows": "yarn eslint && yarn tslint",
"lint-deps": "node ts/util/lint/linter.js",
"tslint": "tslint --format stylish --project .",
"format": "prettier --write \"*.{css,js,json,md,scss,ts,tsx}\" \"./**/*.{css,js,json,md,scss,ts,tsx}\"",
"format":
"prettier --write \"*.{css,js,json,md,scss,ts,tsx}\" \"./**/*.{css,js,json,md,scss,ts,tsx}\"",
"transpile": "tsc",
"clean-transpile": "rimraf ts/**/*.js && rimraf ts/*.js",
"open-coverage": "open coverage/lcov-report/index.html",
"styleguide": "styleguidist server",
"pow-metrics": "node metrics_app.js localhost 9000",
"ready": "yarn clean-transpile && yarn grunt && yarn lint && yarn test-node && yarn test-electron && yarn lint-deps"
"ready":
"yarn clean-transpile && yarn grunt && yarn lint && yarn test-node && yarn test-electron && yarn lint-deps"
},
"dependencies": {
"@journeyapps/sqlcipher": "https://github.com/scottnonnenberg-signal/node-sqlcipher.git#2e28733b61640556b0272a3bfc78b0357daf71e6",
"@journeyapps/sqlcipher":
"https://github.com/scottnonnenberg-signal/node-sqlcipher.git#2e28733b61640556b0272a3bfc78b0357daf71e6",
"@sindresorhus/is": "0.8.0",
"@types/dompurify": "^2.0.0",
"backbone": "1.3.3",
@ -72,7 +86,8 @@
"emoji-datasource": "4.0.0",
"emoji-datasource-apple": "4.0.0",
"emoji-js": "3.4.0",
"emoji-panel": "https://github.com/scottnonnenberg-signal/emoji-panel.git#v0.5.5",
"emoji-panel":
"https://github.com/scottnonnenberg-signal/emoji-panel.git#v0.5.5",
"filesize": "3.6.1",
"firstline": "1.2.1",
"form-data": "2.3.2",
@ -205,9 +220,7 @@
"artifactName": "${name}-mac-${version}.${ext}",
"category": "public.app-category.social-networking",
"icon": "build/icons/mac/icon.icns",
"target": [
"dmg"
],
"target": ["dmg"],
"bundleVersion": "1",
"hardenedRuntime": true,
"gatekeeperAssess": false,
@ -228,9 +241,7 @@
"url": "https://updates.signal.org/desktop"
}
],
"target": [
"nsis"
]
"target": ["nsis"]
},
"nsis": {
"deleteAppDataOnUninstall": true
@ -241,9 +252,7 @@
"StartupWMClass": "Loki Messenger"
},
"asarUnpack": "node_modules/spellchecker/vendor/hunspell_dictionaries",
"target": [
"deb"
],
"target": ["deb"],
"icon": "build/icons/png"
},
"deb": {

@ -739,14 +739,12 @@ label {
margin-left: 75px;
}
.session-loader {
display: inline-block;
position: relative;
width: 80px;
height: 80px;
div {
position: absolute;
top: 33px;

@ -37,7 +37,6 @@ export class AddServerDialog extends React.Component<Props, State> {
this.closeDialog = this.closeDialog.bind(this);
this.onKeyUp = this.onKeyUp.bind(this);
}
public render() {
@ -77,8 +76,8 @@ export class AddServerDialog extends React.Component<Props, State> {
{this.state.view === 'connecting' ? (
<>
<div className="session-modal__centered">
<div className="spacer-lg"></div>
<SessionSpinner/>
<div className="spacer-lg" />
<SessionSpinner />
<div className="spacer-lg" />
</div>
@ -108,18 +107,18 @@ export class AddServerDialog extends React.Component<Props, State> {
}
if (view === 'connecting') {
// TODO: Make this not hard coded
const channelId = 1;
const serverUrl = String($('.session-modal #server-url').val()).toLowerCase();
const serverUrl = String(
$('.session-modal #server-url').val()
).toLowerCase();
this.setState({
error: null,
serverUrl: serverUrl,
})
});
if (serverUrl.length == 0){
if (serverUrl.length == 0) {
this.setState({
error: i18n('noServerUrl'),
view: 'default',
@ -134,23 +133,22 @@ export class AddServerDialog extends React.Component<Props, State> {
connecting: true,
});
const connectionResult = this.attemptConnection(serverUrl, channelId);
// Give 5s maximum for promise to revole. Else, throw error.
const max_connection_duration = 5000;
const connectionTimeout = setTimeout(() => {
if (!this.state.success){
this.showView('default');
this.setState({
connecting: false,
success: false,
error: i18n('connectToServerFail'),
});
if (!this.state.success) {
this.showView('default');
return;
}
this.setState({
connecting: false,
success: false,
error: i18n('connectToServerFail'),
});
return;
}
}, max_connection_duration);
connectionResult
@ -160,7 +158,7 @@ export class AddServerDialog extends React.Component<Props, State> {
if (this.state.connecting) {
this.setState({
success: true,
})
});
window.pushToast({
title: i18n('connectToServerSuccess'),
id: 'connectToServerSuccess',
@ -169,9 +167,9 @@ export class AddServerDialog extends React.Component<Props, State> {
this.closeDialog();
}
})
.catch((error) => {
.catch(error => {
clearTimeout(connectionTimeout);
this.showView('default');
this.setState({
connecting: false,
@ -207,7 +205,7 @@ export class AddServerDialog extends React.Component<Props, State> {
private onKeyUp(event: any) {
switch (event.key) {
case 'Enter':
if (this.state.view == 'default'){
if (this.state.view == 'default') {
this.showView('connecting');
}
break;
@ -226,18 +224,20 @@ export class AddServerDialog extends React.Component<Props, State> {
private async attemptConnection(serverUrl: string, channelId: number) {
const { i18n } = this.props;
const rawServerUrl = serverUrl
.replace(/^https?:\/\//i, '')
.replace(/[/\\]+$/i, '');
const sslServerUrl = `https://${rawServerUrl}`;
const conversationId = `publicChat:${channelId}@${rawServerUrl}`;
const conversationExists = window.ConversationController.get(conversationId);
const conversationExists = window.ConversationController.get(
conversationId
);
if (conversationExists) {
// We are already a member of this public chat
// We are already a member of this public chat
return new Promise((resolve, reject) => {
if (false){
if (false) {
resolve();
}
reject(i18n('publicChatExists'));
@ -250,7 +250,7 @@ export class AddServerDialog extends React.Component<Props, State> {
if (!serverAPI) {
// Url incorrect or server not compatible
return new Promise((resolve, reject) => {
if (false){
if (false) {
resolve();
}
reject(i18n('connectToServerFail'));

@ -7,8 +7,8 @@ interface Props {
title: string;
onOk?: any;
onClose?: any;
onClickOk: any,
onClickClose: any,
onClickOk: any;
onClickClose: any;
okText?: string;
cancelText?: string;
hideCancel: boolean;
@ -18,59 +18,43 @@ export class SessionConfirm extends React.Component<Props> {
public static defaultProps = {
title: '',
hideCancel: false,
}
};
constructor(props: any) {
super(props);
}
public render() {
const { title,
message,
onClickOk,
onClickClose,
hideCancel,
} = this.props;
const { title, message, onClickOk, onClickClose, hideCancel } = this.props;
const okText = this.props.okText || window.i18n('ok');
const cancelText = this.props.cancelText || window.i18n('cancel');
const showHeader = !! this.props.title;
const showHeader = !!this.props.title;
return (
<SessionModal
title={title}
onClose={() => null}
onOk={() => null}
showExitIcon={false}
showHeader={showHeader}
>
{ showHeader ? null : (
<div className="spacer-lg"></div>
<SessionModal
title={title}
onClose={() => null}
onOk={() => null}
showExitIcon={false}
showHeader={showHeader}
>
{showHeader ? null : <div className="spacer-lg" />}
<div className="session-modal__centered">
<span className="text-subtle">{message}</span>
</div>
<div className="spacer-lg" />
<div className="session-modal__button-group">
<SessionButton text={okText} onClick={onClickOk} />
{hideCancel ? null : (
<SessionButton text={cancelText} onClick={onClickClose} />
)}
<div className="session-modal__centered">
<span className="text-subtle">
{message}
</span>
</div>
<div className="spacer-lg"></div>
<div className="session-modal__button-group">
<SessionButton
text={okText}
onClick={onClickOk}
/>
{ hideCancel ? null : (
<SessionButton
text={cancelText}
onClick={onClickClose}
/>
)}
</div>
</SessionModal>
</div>
</SessionModal>
);
}
}

@ -19,12 +19,12 @@ interface State {
isVisible: boolean;
}
export class SessionModal extends React.PureComponent<Props, State> {
export class SessionModal extends React.PureComponent<Props, State> {
public static defaultProps = {
showExitIcon: true,
showHeader: true,
};
constructor(props: any) {
super(props);
this.state = {
@ -43,7 +43,7 @@ export class SessionModal extends React.PureComponent<Props, State> {
return isVisible ? (
<div className={'session-modal'}>
{ showHeader ? (
{showHeader ? (
<>
<div className="session-modal__header">
<div className="session-modal__header__close">
@ -53,7 +53,7 @@ export class SessionModal extends React.PureComponent<Props, State> {
iconSize={SessionIconSize.Small}
onClick={this.close}
/>
) : null }
) : null}
</div>
<div className="session-modal__header__title">{title}</div>
<div className="session-modal__header__icons">
@ -71,7 +71,7 @@ export class SessionModal extends React.PureComponent<Props, State> {
</div>
</div>
</>
) : null }
) : null}
<div className="session-modal__body">{this.props.children}</div>
</div>

@ -1,14 +1,14 @@
import React from 'react';
interface Props {
loading: boolean;
loading: boolean;
}
export class SessionSpinner extends React.Component<Props> {
public static defaultProps = {
loading: true,
}
};
constructor(props: any) {
super(props);
}
@ -17,11 +17,16 @@ export class SessionSpinner extends React.Component<Props> {
const { loading } = this.props;
return (
<>
{ loading ? (
<div className="session-loader"><div></div><div></div><div></div><div></div></div>
) : null }
</>
<>
{loading ? (
<div className="session-loader">
<div />
<div />
<div />
<div />
</div>
) : null}
</>
);
}
}
}

Loading…
Cancel
Save