From 648cc2339cd36b7944d19a307ef755666bda4e26 Mon Sep 17 00:00:00 2001 From: Vincent Date: Fri, 27 Dec 2019 17:55:28 +1100 Subject: [PATCH] Working with deps --- _locales/en/messages.json | 10 +- js/modules/signal.js | 11 +- js/views/add_server_dialog_view.js | 92 ++----- js/views/add_server_dialog_view_old.js | 92 +++++++ js/views/device_pairing_dialog_view.js | 14 +- package_bak.json | 318 +++++++++++++++++++++++++ stylesheets/_session.scss | 48 +++- ts/components/AddServerDialog.tsx | 171 +++++++++++++ ts/components/DevicePairingDialog.tsx | 312 +++++++++++++++++------- ts/global.d.ts | 1 + 10 files changed, 878 insertions(+), 191 deletions(-) create mode 100644 js/views/add_server_dialog_view_old.js create mode 100644 package_bak.json create mode 100644 ts/components/AddServerDialog.tsx diff --git a/_locales/en/messages.json b/_locales/en/messages.json index 5669e21c3..e0679dd49 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -660,6 +660,9 @@ "unableToLoadAttachment": { "message": "Unable to load selected attachment." }, + "connect": { + "message": "Connect" + }, "disconnected": { "message": "Disconnected", "description": @@ -946,7 +949,7 @@ "message": "Close" }, "pairNewDevice": { - "message": "Pair new Device" + "message": "Pair New Device" }, "devicePairingAccepted": { "message": "Device Pairing Accepted" @@ -957,6 +960,9 @@ "waitingForDeviceToRegister": { "message": "Waiting for device to register..." }, + "pairNewDevicePrompt": { + "message": "Scan the QR Code on your secondary device" + }, "pairedDevices": { "message": "Paired Devices" }, @@ -2107,7 +2113,7 @@ "Button action that the user can click to connect to a new public server" }, "addServerDialogTitle": { - "message": "Connect to new public server", + "message": "Connect To New Public Server", "description": "Title for the dialog box used to connect to a new public server" }, diff --git a/js/modules/signal.js b/js/modules/signal.js index d3d7380fe..0e8d4936f 100644 --- a/js/modules/signal.js +++ b/js/modules/signal.js @@ -50,9 +50,13 @@ const { } = require('../../ts/components/conversation/CreateGroupDialog'); const { EditProfileDialog } = require('../../ts/components/EditProfileDialog'); const { UserDetailsDialog } = require('../../ts/components/UserDetailsDialog'); -const { DevicePairingDialog } = require('../../ts/components/DevicePairingDialog'); - -const { SessionSettings, +const { + DevicePairingDialog, +} = require('../../ts/components/DevicePairingDialog'); +const { AddServerDialog } = require('../../ts/components/AddServerDialog'); + +const { + SessionSettings, } = require('../../ts/components/session/SessionSettings'); const { SessionToast } = require('../../ts/components/session/SessionToast'); const { SessionToggle } = require('../../ts/components/session/SessionToggle'); @@ -253,6 +257,7 @@ exports.setup = (options = {}) => { EditProfileDialog, UserDetailsDialog, DevicePairingDialog, + AddServerDialog, SessionRegistrationView, ConfirmDialog, UpdateGroupDialog, diff --git a/js/views/add_server_dialog_view.js b/js/views/add_server_dialog_view.js index ae05bb84e..e81c895ad 100644 --- a/js/views/add_server_dialog_view.js +++ b/js/views/add_server_dialog_view.js @@ -1,4 +1,4 @@ -/* global Whisper, i18n, _ */ +/* global Whisper, i18n, */ // eslint-disable-next-line func-names (function() { @@ -7,86 +7,28 @@ window.Whisper = window.Whisper || {}; Whisper.AddServerDialogView = Whisper.View.extend({ - templateName: 'add-server-template', - className: 'loki-dialog add-server modal', - initialize(options = {}) { - this.title = i18n('addServerDialogTitle'); - this.okText = options.okText || i18n('ok'); - this.cancelText = options.cancelText || i18n('cancel'); - this.$('input').focus(); + className: 'loki-dialog add-server-dialog modal', + initialize() { + this.close = this.close.bind(this); this.render(); }, - events: { - keyup: 'onKeyup', - 'click .ok': 'confirm', - 'click .cancel': 'close', - }, - render_attributes() { - return { - title: this.title, - ok: this.okText, - cancel: this.cancelText, - }; - }, - confirm() { - // Remove error if there is one - this.showError(null); - const serverUrl = this.$('#server-url') - .val() - .toLowerCase(); - // TODO: Make this not hard coded - const channelId = 1; - const dialog = new Whisper.ConnectingToServerDialogView({ - serverUrl, - channelId, - }); - const dialogDelayTimer = setTimeout(() => { - this.el.append(dialog.el); - }, 200); - dialog.once('connectionResult', result => { - clearTimeout(dialogDelayTimer); - if (result.cancelled) { - this.showError(null); - return; - } - if (result.errorCode) { - this.showError(result.errorCode); - return; - } - window.pushToast({ - title: i18n('connectToServerSuccess'), - type: 'success', - id: 'connectToServerSuccess', - }); - this.close(); + + render() { + this.dialogView = new Whisper.ReactWrapperView({ + className: 'add-server-dialog', + Component: window.Signal.Components.AddServerDialog, + props: { + i18n, + onClose: this.close, + }, }); - dialog.trigger('attemptConnection'); + + this.$el.append(this.dialogView.el); + return this; }, + close() { this.remove(); }, - showError(message) { - if (_.isEmpty(message)) { - this.$('.error').text(''); - this.$('.error').hide(); - } else { - this.$('.error').text(`Error: ${message}`); - this.$('.error').show(); - } - this.$('input').focus(); - }, - onKeyup(event) { - switch (event.key) { - case 'Enter': - this.confirm(); - break; - case 'Escape': - case 'Esc': - this.close(); - break; - default: - break; - } - }, }); })(); diff --git a/js/views/add_server_dialog_view_old.js b/js/views/add_server_dialog_view_old.js new file mode 100644 index 000000000..ae05bb84e --- /dev/null +++ b/js/views/add_server_dialog_view_old.js @@ -0,0 +1,92 @@ +/* global Whisper, i18n, _ */ + +// eslint-disable-next-line func-names +(function() { + 'use strict'; + + window.Whisper = window.Whisper || {}; + + Whisper.AddServerDialogView = Whisper.View.extend({ + templateName: 'add-server-template', + className: 'loki-dialog add-server modal', + initialize(options = {}) { + this.title = i18n('addServerDialogTitle'); + this.okText = options.okText || i18n('ok'); + this.cancelText = options.cancelText || i18n('cancel'); + this.$('input').focus(); + this.render(); + }, + events: { + keyup: 'onKeyup', + 'click .ok': 'confirm', + 'click .cancel': 'close', + }, + render_attributes() { + return { + title: this.title, + ok: this.okText, + cancel: this.cancelText, + }; + }, + confirm() { + // Remove error if there is one + this.showError(null); + const serverUrl = this.$('#server-url') + .val() + .toLowerCase(); + // TODO: Make this not hard coded + const channelId = 1; + const dialog = new Whisper.ConnectingToServerDialogView({ + serverUrl, + channelId, + }); + const dialogDelayTimer = setTimeout(() => { + this.el.append(dialog.el); + }, 200); + dialog.once('connectionResult', result => { + clearTimeout(dialogDelayTimer); + if (result.cancelled) { + this.showError(null); + return; + } + if (result.errorCode) { + this.showError(result.errorCode); + return; + } + window.pushToast({ + title: i18n('connectToServerSuccess'), + type: 'success', + id: 'connectToServerSuccess', + }); + this.close(); + }); + dialog.trigger('attemptConnection'); + }, + close() { + this.remove(); + }, + showError(message) { + if (_.isEmpty(message)) { + this.$('.error').text(''); + this.$('.error').hide(); + } else { + this.$('.error').text(`Error: ${message}`); + this.$('.error').show(); + } + this.$('input').focus(); + }, + onKeyup(event) { + switch (event.key) { + case 'Enter': + this.confirm(); + break; + case 'Escape': + case 'Esc': + this.close(); + break; + default: + break; + } + }, + }); +})(); diff --git a/js/views/device_pairing_dialog_view.js b/js/views/device_pairing_dialog_view.js index b371ac759..50ba4e9c2 100644 --- a/js/views/device_pairing_dialog_view.js +++ b/js/views/device_pairing_dialog_view.js @@ -1,11 +1,4 @@ -/* global - Whisper, - i18n, - libloki, - textsecure, - ConversationController, - $, -*/ +/* global Whisper, i18n, */ // eslint-disable-next-line func-names (function() { @@ -17,12 +10,10 @@ className: 'loki-dialog device-pairing-dialog modal', initialize() { this.close = this.close.bind(this); - this.render(); - }, - render(){ + render() { this.dialogView = new Whisper.ReactWrapperView({ className: 'device-pairing-dialog', Component: window.Signal.Components.DevicePairingDialog, @@ -36,7 +27,6 @@ return this; }, - close() { this.remove(); }, diff --git a/package_bak.json b/package_bak.json new file mode 100644 index 000000000..a2305a59a --- /dev/null +++ b/package_bak.json @@ -0,0 +1,318 @@ +{ + "name": "loki-messenger-desktop", + "productName": "Loki Messenger", + "description": "Private messaging from your desktop", + "repository": "https://github.com/loki-project/loki-messenger.git", + "version": "1.0.0-beta9", + "license": "GPL-3.0", + "author": { + "name": "Loki Project", + "email": "team@loki.network" + }, + "main": "main.js", + "scripts": { + "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 .", + "grunt": "grunt", + "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", + "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-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", + "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", + "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}\"", + "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" + }, + "dependencies": { + "@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", + "blob-util": "1.3.0", + "blueimp-canvas-to-blob": "3.14.0", + "blueimp-load-image": "2.18.0", + "buffer-crc32": "0.2.13", + "bunyan": "1.8.12", + "classnames": "2.2.5", + "color": "^3.1.2", + "config": "1.28.1", + "dompurify": "^2.0.7", + "electron-context-menu": "^0.15.0", + "electron-editor-context-menu": "1.1.1", + "electron-is-dev": "0.3.0", + "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", + "filesize": "3.6.1", + "firstline": "1.2.1", + "form-data": "2.3.2", + "fs-extra": "5.0.0", + "glob": "7.1.2", + "google-libphonenumber": "3.2.2", + "got": "8.2.0", + "he": "1.2.0", + "intl-tel-input": "12.1.15", + "jquery": "3.3.1", + "js-sha512": "0.8.0", + "js-yaml": "3.13.0", + "jsbn": "1.1.0", + "libsodium-wrappers": "^0.7.4", + "linkify-it": "2.0.3", + "lodash": "4.17.11", + "mixpanel": "^0.10.2", + "mkdirp": "0.5.1", + "moment": "2.21.0", + "mustache": "2.3.0", + "nat-upnp": "^1.1.1", + "node-fetch": "2.3.0", + "node-gyp": "3.8.0", + "node-sass": "4.9.3", + "os-locale": "2.1.0", + "pify": "3.0.0", + "protobufjs": "6.8.6", + "proxy-agent": "3.0.3", + "qrcode": "^1.4.4", + "react": "16.8.3", + "react-contextmenu": "2.11.0", + "react-dom": "16.8.3", + "react-portal": "^4.2.0", + "react-qrcode": "^0.2.0", + "react-redux": "6.0.1", + "react-virtualized": "9.21.0", + "read-last-lines": "1.3.0", + "redux": "4.0.1", + "redux-logger": "3.0.6", + "redux-promise-middleware": "6.1.0", + "reselect": "4.0.0", + "rimraf": "2.6.2", + "selfsigned": "^1.10.4", + "semver": "5.4.1", + "spellchecker": "3.5.1", + "tar": "4.4.8", + "testcheck": "1.0.0-rc.2", + "tmp": "0.0.33", + "to-arraybuffer": "1.0.1", + "underscore": "1.9.0", + "uuid": "3.3.2", + "websocket": "1.0.28" + }, + "devDependencies": { + "@types/chai": "4.1.2", + "@types/classnames": "2.2.3", + "@types/color": "^3.0.0", + "@types/config": "0.0.34", + "@types/filesize": "3.6.0", + "@types/fs-extra": "5.0.5", + "@types/google-libphonenumber": "7.4.14", + "@types/got": "9.4.1", + "@types/jquery": "3.3.29", + "@types/js-yaml": "3.12.0", + "@types/linkify-it": "2.0.3", + "@types/lodash": "4.14.106", + "@types/mkdirp": "0.5.2", + "@types/mocha": "5.0.0", + "@types/pify": "3.0.2", + "@types/qs": "6.5.1", + "@types/react": "16.8.5", + "@types/react-dom": "16.8.2", + "@types/react-portal": "^4.0.2", + "@types/react-redux": "7.0.1", + "@types/react-virtualized": "9.18.12", + "@types/redux-logger": "3.0.7", + "@types/rimraf": "2.0.2", + "@types/semver": "5.5.0", + "@types/sinon": "4.3.1", + "@types/uuid": "3.4.4", + "arraybuffer-loader": "1.0.3", + "asar": "0.14.0", + "axios": "0.18.0", + "bower": "1.8.2", + "chai": "4.1.2", + "dashdash": "1.14.1", + "electron": "4.1.2", + "electron-builder": "21.2.0", + "electron-icon-maker": "0.0.3", + "electron-notarize": "^0.2.0", + "eslint": "4.14.0", + "eslint-config-airbnb-base": "12.1.0", + "eslint-config-prettier": "2.9.0", + "eslint-plugin-import": "2.8.0", + "eslint-plugin-mocha": "4.12.1", + "eslint-plugin-more": "0.3.1", + "extract-zip": "1.6.6", + "grunt": "1.0.1", + "grunt-cli": "1.2.0", + "grunt-contrib-concat": "1.0.1", + "grunt-contrib-copy": "1.0.0", + "grunt-contrib-watch": "1.0.0", + "grunt-exec": "3.0.0", + "grunt-gitinfo": "0.1.7", + "grunt-sass": "3.0.1", + "mocha": "4.1.0", + "mocha-testcheck": "1.0.0-rc.0", + "node-sass-import-once": "1.2.0", + "nyc": "11.4.1", + "prettier": "1.12.0", + "qs": "6.5.1", + "react-docgen-typescript": "1.2.6", + "react-styleguidist": "7.0.1", + "sinon": "4.4.2", + "spectron": "5.0.0", + "ts-loader": "4.1.0", + "tslint": "5.13.0", + "tslint-microsoft-contrib": "6.0.0", + "tslint-react": "3.6.0", + "typescript": "3.3.3333", + "webpack": "4.4.1" + }, + "engines": { + "node": "10.13.0" + }, + "build": { + "appId": "com.loki-project.messenger-desktop", + "afterSign": "build/notarize.js", + "mac": { + "artifactName": "${name}-mac-${version}.${ext}", + "category": "public.app-category.social-networking", + "icon": "build/icons/mac/icon.icns", + "target": [ + "dmg" + ], + "bundleVersion": "1", + "hardenedRuntime": true, + "gatekeeperAssess": false, + "entitlements": "build/entitlements.mac.plist", + "entitlementsInherit": "build/entitlements.mac.plist" + }, + "dmg": { + "sign": false + }, + "win": { + "asarUnpack": "node_modules/spellchecker/vendor/hunspell_dictionaries", + "artifactName": "${name}-win-${version}.${ext}", + "publisherName": "Loki Project", + "icon": "build/icons/win/icon.ico", + "publish": [ + { + "provider": "generic", + "url": "https://updates.signal.org/desktop" + } + ], + "target": [ + "nsis" + ] + }, + "nsis": { + "deleteAppDataOnUninstall": true + }, + "linux": { + "category": "Network", + "desktop": { + "StartupWMClass": "Loki Messenger" + }, + "asarUnpack": "node_modules/spellchecker/vendor/hunspell_dictionaries", + "target": [ + "deb" + ], + "icon": "build/icons/png" + }, + "deb": { + "depends": [ + "libnotify4", + "libappindicator1", + "libxtst6", + "libnss3", + "libasound2", + "libxss1" + ] + }, + "files": [ + "package.json", + "config/default.json", + "config/${env.SIGNAL_ENV}.json", + "config/local-${env.SIGNAL_ENV}.json", + "background.html", + "about.html", + "settings.html", + "password.html", + "permissions_popup.html", + "debug_log.html", + "_locales/**", + "libloki/modules/*.js", + "mnemonic_languages/**", + "protos/*", + "js/**", + "ts/**/*.js", + "ts/*.js", + "stylesheets/*.css", + "!js/register.js", + "js/views/standalone_registration_view.js", + "app/*", + "preload.js", + "about_preload.js", + "settings_preload.js", + "permissions_popup_preload.js", + "debug_log_preload.js", + "password_preload.js", + "main.js", + "images/**", + "fonts/*", + "build/assets", + "node_modules/**", + "!node_modules/emoji-panel/dist/*", + "!node_modules/emoji-panel/lib/emoji-panel-emojione-*.css", + "!node_modules/emoji-panel/lib/emoji-panel-google-*.css", + "!node_modules/emoji-panel/lib/emoji-panel-twitter-*.css", + "!node_modules/emoji-panel/lib/emoji-panel-apple-{16,20,64}.css", + "!node_modules/emoji-datasource/emoji_pretty.json", + "!node_modules/emoji-datasource/*.png", + "!node_modules/emoji-datasource-apple/emoji_pretty.json", + "!node_modules/emoji-datasource-apple/img/apple/{sheets-128,sheets-256}/*.png", + "!node_modules/emoji-datasource-apple/img/apple/sheets/{16,20,32}.png", + "!node_modules/spellchecker/vendor/hunspell/**/*", + "!**/node_modules/*/{CHANGELOG.md,README.md,README,readme.md,readme,test,__tests__,tests,powered-test,example,examples,*.d.ts}", + "!**/node_modules/.bin", + "!**/node_modules/*/build/**", + "!**/*.{o,hprof,orig,pyc,pyo,rbc}", + "!**/._*", + "!**/{.DS_Store,.git,.hg,.svn,CVS,RCS,SCCS,__pycache__,thumbs.db,.gitignore,.gitattributes,.editorconfig,.flowconfig,.yarn-metadata.json,.idea,appveyor.yml,.travis.yml,circle.yml,npm-debug.log,.nyc_output,yarn.lock,.yarn-integrity}", + "node_modules/spellchecker/build/Release/*.node", + "node_modules/websocket/build/Release/*.node", + "node_modules/socks/build/*.js", + "node_modules/socks/build/common/*.js", + "node_modules/socks/build/client/*.js", + "node_modules/smart-buffer/build/*.js", + "!node_modules/@journeyapps/sqlcipher/deps/*" + ] + } +} diff --git a/stylesheets/_session.scss b/stylesheets/_session.scss index 1b9634cbd..8be37ef43 100644 --- a/stylesheets/_session.scss +++ b/stylesheets/_session.scss @@ -97,6 +97,14 @@ div.spacer-lg { opacity: 0.6; } +.text-soft { + opacity: 0.4; +} + +.fullwidth { + width: 100%; +} + $session-transition-duration: 0.25s; $session-icon-size-sm: 15px; @@ -256,6 +264,30 @@ $session_message-container-border-radius: 5px; } } +.session-label { + color: $session-color-white; + padding: $session-margin-sm; + width: 100%; + border-radius: 2px; + text-align: center; + + &.primary { + background-color: $session-color-primary; + } + &.secondary { + background-color: $session-color-secondary; + } + &.success { + background-color: $session-color-success; + } + &.danger { + background-color: $session-color-danger; + } + &.warning { + background-color: $session-color-warning; + } +} + @mixin set-icon-margin($size) { margin: $size / 3; } @@ -551,8 +583,7 @@ label { } &__body { - padding: $session-margin-lg; - + padding: 0px $session-margin-lg $session-margin-lg $session-margin-lg; font-family: 'Wasa'; line-height: 16px; font-size: 13px; @@ -562,10 +593,16 @@ label { } } + &__centered { + display: flex; + flex-direction: column; + align-items: center; + } + &__button-group { display: flex; justify-content: flex-end; - + .session-button { margin-left: $session-margin-sm; } @@ -574,14 +611,11 @@ label { display: flex; justify-content: center; } - + .session-button { margin: 0 $session-margin-xs; - } - } - } .session-toggle { diff --git a/ts/components/AddServerDialog.tsx b/ts/components/AddServerDialog.tsx new file mode 100644 index 000000000..747ff3c45 --- /dev/null +++ b/ts/components/AddServerDialog.tsx @@ -0,0 +1,171 @@ +import React from 'react'; +import { ImpulseSpinner } from 'react-spinners-kit'; + +import { SessionModal } from './session/SessionModal'; +import { SessionButton } from './session/SessionButton'; + +interface Props { + i18n: any; + onClose: any; +} + +interface State { + title: string; + error: string | null; + connecting: boolean; + view: 'connecting' | 'default'; +} + +export class AddServerDialog extends React.Component { + constructor(props: any) { + super(props); + + this.state = { + title: '', + error: null, + connecting: false, + view: 'default', + }; + + this.confirm = this.confirm.bind(this); + this.showError = this.showError.bind(this); + this.showView = this.showView.bind(this); + + this.closeDialog = this.closeDialog.bind(this); + this.onKeyUp = this.onKeyUp.bind(this); + } + + public render() { + const { i18n } = this.props; + + return ( + null} + onClose={this.closeDialog} + > + {this.state.view === 'default' ? ( + <> +
+ + +
+ + {this.showError()} + +
+ this.showView('connecting')} + /> + + +
+ + ) : null} + + {this.state.view === 'connecting' ? ( + <> + connecting! +
+ + + +
+
+ this.showView('default')} + /> +
+ + ) : null} + + ); + } + + private showView(view: 'default' | 'connecting') { + const { i18n } = this.props; + + if (view === 'default') { + this.setState({ + title: i18n('addServerDialogTitle'), + error: null, + view: 'default', + }); + } + + if (view === 'connecting') { + this.setState({ + title: i18n('connectingLoad'), + error: null, + view: 'connecting', + }); + } + } + + private confirm() { + // const { i18n } = this.props; + + // Remove error if there is one + this.setState({ + error: null, + }); + + const connected = false; + + const serverUrl = String( + $('.add-server-dialog #server-url').val() + ).toLowerCase(); + // // TODO: Make this not hard coded + const channelId = 1; + + console.log(serverUrl); + + if (connected) { + // window.pushToast({ + // title: i18n('connectToServerSuccess'), + // type: 'success', + // id: 'connectToServerSuccess', + // }); + this.closeDialog(); + } + } + + private showError() { + const message = this.state.error; + return ( + <> + {message ? ( + <> +
{message}
+
+ + ) : null} + + ); + // if (_.isEmpty(message)) { + // this.$('.error').text(''); + // this.$('.error').hide(); + // } else { + // this.$('.error').text(`Error: ${message}`); + // this.$('.error').show(); + // } + // $('input').focus(); + } + + private onKeyUp(event: any) { + switch (event.key) { + case 'Esc': + case 'Escape': + this.closeDialog(); + break; + default: + } + } + + private closeDialog() { + window.removeEventListener('keyup', this.onKeyUp); + this.props.onClose(); + } +} diff --git a/ts/components/DevicePairingDialog.tsx b/ts/components/DevicePairingDialog.tsx index 563cfc926..b188f9f5b 100644 --- a/ts/components/DevicePairingDialog.tsx +++ b/ts/components/DevicePairingDialog.tsx @@ -1,23 +1,30 @@ import React from 'react'; -import { QRCode } from 'react-qrcode' +import { QRCode } from 'react-qrcode'; import { SessionModal } from './session/SessionModal'; import { SessionButton } from './session/SessionButton'; - interface Props { - i18n: any, - onClose: any, - pubKeyToUnpair: string | null; - pubKey: string | null; + i18n: any; + onClose: any; + pubKeyToUnpair: string | null; + pubKey: string | null; } interface State { - accepted: boolean; - isListening: boolean; - success: boolean; - loading: boolean; - data: Array; + currentPubKey: string | null; + accepted: boolean; + isListening: boolean; + success: boolean; + loading: boolean; + view: + | 'default' + | 'waitingForRequest' + | 'requestReceived' + | 'requestAccepted' + | 'confirmUnpair'; + pubKeyRequests: Array; + data: Array; } export class DevicePairingDialog extends React.Component { @@ -31,122 +38,242 @@ export class DevicePairingDialog extends React.Component { this.getPubkeyName = this.getPubkeyName.bind(this); this.state = { - accepted: false, - isListening: false, - success: false, - loading: true, - data: [], + currentPubKey: this.props.pubKey, + accepted: false, + isListening: false, + success: false, + loading: true, + view: 'default', + pubKeyRequests: [], + data: [], }; } componentDidMount() { - this.getSecondaryDevices(); + this.getSecondaryDevices(); } - private async getSecondaryDevices(){ - const secondaryDevices = await window.libloki.storage.getSecondaryDevicesFor(this.props.pubKey); + private async getSecondaryDevices() { + const secondaryDevices = await window.libloki.storage.getSecondaryDevicesFor( + this.state.currentPubKey + ); this.setState({ - data: secondaryDevices, - loading: false + data: secondaryDevices, + loading: false, }); } public render() { - const {i18n, } = this.props; - + const { i18n } = this.props; - const newData = ['053e18835c106a5f9f463a44a9d7ff9a26281d529285a047bd969cfc59d4ab8607']; + const newData = [ + '053e18835c106a5f9f463a44a9d7ff9a26281d529285a047bd969cfc59d4ab8607', + '053e18835c106a5f9f463a44a9d7ff9a26281d529285a047bd969cfc59d4ab8604', + ]; setTimeout(() => { - this.setState({ - data: newData, - }); + this.setState({ + data: newData, + }); }, 2000); - + return ( - <> - { ! this.state.loading ? ( - null} - onClose={this.closeDialog} - > - { this.state.isListening ? ( -
- {i18n('waitingForDeviceToRegister')} -
- -
- -
+ <> + {!this.state.loading ? ( + null} + onClose={this.closeDialog} + > + {this.state.view === 'waitingForRequest' ? ( +
+

{i18n('waitingForDeviceToRegister')}

+ + {i18n('pairNewDevicePrompt')} + +
-
- ) - : ( - <> - {this.state.data.length == 0 ? ( - <> -
{i18n('noPairedDevices')}
-
- -
- -
- - ) - : ( - <> - { - this.state.data.map((pubKey: any) => { - const pubKeyInfo = this.getPubkeyName(pubKey); - return ( -

- { pubKeyInfo.deviceAlias } -
- Pairing Secret: { pubKeyInfo.secretWords } -

- ); - }) - } - - )} - - - +
+ +
+ +
+
+ +
+
+ ) : ( + <> + {this.state.data.length == 0 ? ( +
+
{i18n('noPairedDevices')}
+
+ ) : ( +
+ {this.state.data.map((pubKey: any) => { + const pubKeyInfo = this.getPubkeyName(pubKey); + const isFinalItem = + this.state.data[this.state.data.length - 1] === pubKey; + + return ( +
+

+ {pubKeyInfo.deviceAlias} +
+ + Pairing Secret: + {' '} + {pubKeyInfo.secretWords} +

+ {!isFinalItem ? ( +
+ ) : null} +
+ ); + })} +
)} - - ) : null } + +
+
+ +
+ + )} + + ) : null} ); } - + private showView( + view?: + | 'default' + | 'waitingForRequest' + | 'requestReceived' + | 'requestAccepted' + | 'confirmUnpair' + ) { + if (!view) { + this.setState({ + view: 'default', + }); + + return; + } + + this.setState({ view }); + } private startReceivingRequests() { this.setState({ - isListening: true, + isListening: true, }); + + this.showView('waitingForRequest'); + + //TESTING + //TESTING + //TESTING + setTimeout(() => { + this.setState({ + accepted: true, + success: true, + }); + }, 3000); } - private getPubkeyName(pubKey: string) { + private getPubkeyName(pubKey: string | null) { + if (!pubKey) { + return {}; + } + const secretWords = window.mnemonic.pubkey_to_secret_words(pubKey); - const conv = window.ConversationController.get(this.props.pubKey); + const conv = window.ConversationController.get(this.state.currentPubKey); const deviceAlias = conv ? conv.getNickname() : 'Unnamed Device'; - return {deviceAlias, secretWords}; + return { deviceAlias, secretWords }; } private stopReceivingRequests() { if (this.state.success) { - const conv = window.ConversationController.get(this.props.pubKey); - //if (conv) { - // conv.setNickname(this.props.deviceAlias); - //} + const deviceAlias = this.getPubkeyName(this.state.currentPubKey)[ + 'deviceAlias' + ]; + const conv = window.ConversationController.get(this.state.currentPubKey); + if (conv) { + conv.setNickname(deviceAlias); + } } - this.forceUpdate(); + + this.showView(); } + private requestReceived(secondaryDevicePubKey: string | EventHandlerNonNull) { + // FIFO: push at the front of the array with unshift() + this.state.pubKeyRequests.unshift(secondaryDevicePubKey); + if (!this.state.currentPubKey) { + this.nextPubKey(); + + this.showView('requestReceived'); + } + } + + allowDevice() { + this.setState({ + accepted: true, + }); + window.Whisper.trigger( + 'devicePairingRequestAccepted', + this.state.currentPubKey, + (errors: any) => this.transmisssionCB(errors) + ); + this.showView(); + } + + transmisssionCB(errors: any) { + if (!errors) { + // this.$('.transmissionStatus').text(i18n('provideDeviceAlias')); + // this.$('#deviceAliasView').show(); + // this.$('#deviceAlias').on('input', e => { + // if (e.target.value.trim()) { + // this.$('.requestAcceptedView .ok').removeAttr('disabled'); + // } else { + // this.$('.requestAcceptedView .ok').attr('disabled', true); + // } + // }); + // this.$('.requestAcceptedView .ok').show(); + // this.$('.requestAcceptedView .ok').attr('disabled', true); + + this.setState({ + success: true, + }); + } else { + // this.$('.transmissionStatus').text(errors); + // this.$('.requestAcceptedView .ok').show(); + } + } + + skipDevice() { + window.Whisper.trigger( + 'devicePairingRequestRejected', + this.state.currentPubKey + ); + this.nextPubKey(); + this.showView(); + } + + nextPubKey() { + // FIFO: pop at the back of the array using pop() + const pubKeyRequests = this.state.pubKeyRequests; + this.setState({ + currentPubKey: pubKeyRequests.pop(), + }); + } private onKeyUp(event: any) { switch (event.key) { @@ -160,6 +287,7 @@ export class DevicePairingDialog extends React.Component { private closeDialog() { window.removeEventListener('keyup', this.onKeyUp); + this.stopReceivingRequests(); this.props.onClose(); } -} \ No newline at end of file +} diff --git a/ts/global.d.ts b/ts/global.d.ts index 4a84f6da6..ade72de1d 100644 --- a/ts/global.d.ts +++ b/ts/global.d.ts @@ -14,6 +14,7 @@ interface Window { Session: any; i18n: any; generateID: any; + pushToast: any; } interface Promise {