From 12011a30d4a9a5a7d9c2201c37cd379b430a0fcd Mon Sep 17 00:00:00 2001 From: Vincent Date: Wed, 18 Dec 2019 11:50:19 +1100 Subject: [PATCH 1/8] Inital dropdown, modal and toggle --- background.html | 1 + js/background.js | 9 +- js/modules/signal.js | 4 + js/views/session_modal_view.js | 0 js/views/session_toggle_view.js | 31 +++++ stylesheets/_session.scss | 118 +++++++++++++++++- ts/components/UserDetailsDialog.tsx | 35 ++++++ ts/components/session/SessionDropdown.tsx | 45 +++++++ ts/components/session/SessionDropdownItem.tsx | 66 ++++++++++ ts/components/session/SessionModal.tsx | 43 +++++++ ts/components/session/SessionToggle.tsx | 48 +++++++ .../session/tools/ComponentTools.tsx | 7 ++ 12 files changed, 401 insertions(+), 6 deletions(-) create mode 100644 js/views/session_modal_view.js create mode 100644 js/views/session_toggle_view.js create mode 100644 ts/components/session/SessionDropdown.tsx create mode 100644 ts/components/session/SessionDropdownItem.tsx create mode 100644 ts/components/session/SessionModal.tsx create mode 100644 ts/components/session/SessionToggle.tsx create mode 100644 ts/components/session/tools/ComponentTools.tsx diff --git a/background.html b/background.html index d0232a1d5..536d1ad05 100644 --- a/background.html +++ b/background.html @@ -701,6 +701,7 @@ + diff --git a/js/background.js b/js/background.js index 7dd29881a..ca0bbcc90 100644 --- a/js/background.js +++ b/js/background.js @@ -802,6 +802,9 @@ appView.openConversation(groupId, {}); }; + + window.generateID = () => Math.random().toString(36).substring(3); + window.toasts = new Map(); window.pushToast = options => { // Setting toasts with the same ID can be used to prevent identical @@ -811,10 +814,7 @@ const params = { title: options.title, id: - options.id || - Math.random() - .toString(36) - .substring(3), + options.id || window.generateID(), description: options.description || '', type: options.type || '', }; @@ -825,7 +825,6 @@ if (toast) { window.toasts.get(toastID).update(params); } else { - // Make new Toast window.toasts.set( toastID, diff --git a/js/modules/signal.js b/js/modules/signal.js index a94083c13..d0e00e394 100644 --- a/js/modules/signal.js +++ b/js/modules/signal.js @@ -51,6 +51,8 @@ const { const { EditProfileDialog } = require('../../ts/components/EditProfileDialog'); const { UserDetailsDialog } = require('../../ts/components/UserDetailsDialog'); const { SessionToast } = require('../../ts/components/session/SessionToast'); +const { SessionToggle } = require('../../ts/components/session/SessionToggle'); +const { SessionModal } = require('../../ts/components/session/SessionModal'); const { SessionRegistrationView, } = require('../../ts/components/session/SessionRegistrationView'); @@ -250,6 +252,8 @@ exports.setup = (options = {}) => { GroupInvitation, BulkEdit, SessionToast, + SessionToggle, + SessionModal, MediaGallery, Message, MessageBody, diff --git a/js/views/session_modal_view.js b/js/views/session_modal_view.js new file mode 100644 index 000000000..e69de29bb diff --git a/js/views/session_toggle_view.js b/js/views/session_toggle_view.js new file mode 100644 index 000000000..60b5d7740 --- /dev/null +++ b/js/views/session_toggle_view.js @@ -0,0 +1,31 @@ +/* global Whisper */ + +// eslint-disable-next-line func-names +(function() { + 'use strict'; + + window.Whisper = window.Whisper || {}; + + Whisper.SessionToastView = Whisper.View.extend({ + initialize(options) { + this.props = { + active: options.active, + }; + }, + + render() { + this.toggleView = new Whisper.ReactWrapperView({ + className: 'session-toggle-wrapper', + Component: window.Signal.Components.SessionToast, + props: this.props, + }); + + this.$el.append(this.toggleView.el); + }, + + toggle() { + this.props.active = !this.props.active; + this.toggleView.update(this.props); + }, + }); +})(); diff --git a/stylesheets/_session.scss b/stylesheets/_session.scss index 730ab25e0..8c183e823 100644 --- a/stylesheets/_session.scss +++ b/stylesheets/_session.scss @@ -60,7 +60,7 @@ $session-opaque-dark-3: rgba(0, 0, 0, 0.5); $session-color-white: #fff; $session-color-dark-grey: #353535; $session-color-black: #000; -$session-color-danger: #ff4538; +$session-color-danger: #FF453A; $session-color-primary: $session-shade-13; $session-color-secondary: $session-shade-16; @@ -489,3 +489,119 @@ label { @include set-toast-theme($session-color-error); } } + +.session-modal { + z-index: 150; + position: fixed; + top: 50%; + left: 50%; + + box-sizing: border-box; + height: 529px; + width: 345px; + background-color: $session-shade-4; + border: 1px solid $session-shade-8; + + .header { + display: flex; + flex-direction: row; + justify-content: space-between; + padding: $session-margin-lg; + + font-family: 'Wasa'; + text-align: center; + line-height: 18px; + font-size: 15px; + font-weight: 700; + + .close, + .icons { + width: 70px; + } + .close > div { + float: left; + } + .icons > div { + float: right; + } + } +} + +.session-toggle { + width: 51px; + height: 31px; + border: 1.5px solid #e5e5ea; + border-radius: 16px; + position: relative; + + cursor: pointer; + background-color: rgba(0, 0, 0, 0); + + .knob { + position: absolute; + top: 0.5px; + left: 0.5px; + height: 27px; + width: 27px; + border-radius: 28px; + background-color: $session-color-white; + box-shadow: 0 0 3px 1px rgba(0, 0, 0, 0.05), 0 3px 1px 0 rgba(0, 0, 0, 0.05), + 0 2px 2px 0 rgba(0, 0, 0, 0.1), 0 3px 3px 0 rgba(0, 0, 0, 0.05); + + transition: transform 0.25s ease, background-color 0.25s ease; + } + + &.active { + background-color: $session-color-green; + border-color: $session-color-green; + + .knob { + transform: translateX(20px); + } + } +} + + +.session-dropdown{ + display: inline-block; + + ul { + display: block; + list-style: none; + padding: 0px; + margin: 0px; + + li { + cursor: pointer; + + height: 25px; + padding-right: 7px; + background-color: #1B1B1B; + + color: $session-color-white; + font-family: 'Wasa'; + font-size: 12px; + line-height: $session-icon-size-sm; + font-weight: 700; + + display: flex; + align-items: center; + + .session-icon { + margin-left: 6px; + } + .item-content { + margin-left: 6px; + } + + &.active, &:hover{ + background-color: $session-shade-7; + } + + &.danger { + color: $session-color-danger; + } + } + } + +} \ No newline at end of file diff --git a/ts/components/UserDetailsDialog.tsx b/ts/components/UserDetailsDialog.tsx index e52f49713..3f07d8631 100644 --- a/ts/components/UserDetailsDialog.tsx +++ b/ts/components/UserDetailsDialog.tsx @@ -1,6 +1,9 @@ import React from 'react'; import { Avatar } from './Avatar'; +import { SessionDropdown, SessionDropDownType } from './session/SessionDropdown'; +import { SessionIconType } from './session/icon'; + declare global { interface Window { displayNameRegex: any; @@ -33,8 +36,40 @@ export class UserDetailsDialog extends React.Component { const cancelText = i18n('cancel'); const startConversation = i18n('startConversation'); + const items = [ + { + id: "myid1", + content: "Copy Chat ID", + icon: SessionIconType.Eye, + type: SessionDropDownType.Default, + active: false + }, + { + id: "myid1", + content: "Invite Friends", + icon: null, + type: SessionDropDownType.Default, + active: false + }, + { + id: "myid1", + content: "Clear Chat History", + icon: SessionIconType.Check, + type: SessionDropDownType.Default, + active: true + }, + { + id: "myid1", + content: "Leave Group", + icon: SessionIconType.Check, + type: SessionDropDownType.Danger, + active: false + }, + ]; + return (
+
{this.renderAvatar()}
diff --git a/ts/components/session/SessionDropdown.tsx b/ts/components/session/SessionDropdown.tsx new file mode 100644 index 000000000..21b6a1f7f --- /dev/null +++ b/ts/components/session/SessionDropdown.tsx @@ -0,0 +1,45 @@ +import React from 'react'; +import classNames from 'classnames'; + +import { SessionDropdownItem, SessionDropDownItemType } from './SessionDropdownItem'; +import { SessionIconType } from './icon/'; + + +interface Props { + items: Array<{ + id: string, + content: string, + icon: SessionIconType | null, + type: SessionDropDownItemType, + active: boolean, + }>, + } + +export class SessionDropdown extends React.PureComponent { + public static readonly defaultProps = SessionDropdownItem.defaultProps; + + constructor(props: any) { + super(props); + } + + public render() { + const { items } = this.props; + + return ( +
+
    + {items.map((item: any) => + )} +
+
+ ); + } + +} + diff --git a/ts/components/session/SessionDropdownItem.tsx b/ts/components/session/SessionDropdownItem.tsx new file mode 100644 index 000000000..f8862f748 --- /dev/null +++ b/ts/components/session/SessionDropdownItem.tsx @@ -0,0 +1,66 @@ + +import React from 'react'; +import classNames from 'classnames'; + +import { SessionIcon, SessionIconSize, SessionIconType } from './icon/'; +import { generateID } from './tools/ComponentTools'; + + +export enum SessionDropDownItemType { + Default = 'default', + Danger = 'danger', +} + +interface Props { + id: string, + content: string, + type: SessionDropDownItemType, + icon: SessionIconType | null, + active: boolean, + onClick: any, +} + +export class SessionDropdownItem extends React.PureComponent { + public static defaultProps = { + id: generateID(), + type: SessionDropDownItemType.Default, + onClick: () => null, + }; + + constructor(props: any) { + super(props); + this.clickHandler = this.clickHandler.bind(this); + } + + public render() { + const { id, content, type, icon, active } = this.props; + + return ( +
  • + { icon ? + : '' + } +
    + {content} +
    +
  • + ); + } + + private clickHandler(e: any){ + if (this.props.onClick) { + e.stopPropagation(); + this.props.onClick(); + } + } + } + + + diff --git a/ts/components/session/SessionModal.tsx b/ts/components/session/SessionModal.tsx new file mode 100644 index 000000000..325f772ec --- /dev/null +++ b/ts/components/session/SessionModal.tsx @@ -0,0 +1,43 @@ +import React from 'react'; +import classNames from 'classnames'; + +import { SessionIconButton, SessionIconSize, SessionIconType } from './icon/'; + +interface Props { + title: string; + body: any; +} + +export class SessionModal extends React.PureComponent { + constructor(props: any) { + super(props); + } + + public render() { + const { title } = this.props; + + return ( +
    +
    +
    + +
    +
    {title}
    +
    + + +
    +
    +
    + ); + } +} diff --git a/ts/components/session/SessionToggle.tsx b/ts/components/session/SessionToggle.tsx new file mode 100644 index 000000000..7c169c1ed --- /dev/null +++ b/ts/components/session/SessionToggle.tsx @@ -0,0 +1,48 @@ +import React from 'react'; +import classNames from 'classnames'; + +interface Props { + active: boolean; +} + +interface State { + active: boolean; +} + +export class SessionToggle extends React.PureComponent { + public static readonly extendedDefaults = { + onClick: () => null, + }; + + constructor(props: any) { + super(props); + this.clickHandler = this.clickHandler.bind(this); + + const { active } = this.props; + + this.state = { + active: active, + }; + } + + public render() { + return ( +
    +
    +
    + ); + } + + private clickHandler() { + this.setState({ + active: !this.state.active, + }); + } +} diff --git a/ts/components/session/tools/ComponentTools.tsx b/ts/components/session/tools/ComponentTools.tsx new file mode 100644 index 000000000..7bdff6063 --- /dev/null +++ b/ts/components/session/tools/ComponentTools.tsx @@ -0,0 +1,7 @@ +// This is a collection of tools which can be ued to simplify the esign and rendering process of your components. + +export function generateID(){ + // Generates a unique ID for your component + return Math.random().toString(36).substring(3); +} + From e9d543fe4a4f86c60d197bf2e2da0279e8414ef1 Mon Sep 17 00:00:00 2001 From: Vincent Date: Wed, 18 Dec 2019 17:14:27 +1100 Subject: [PATCH 2/8] Dropdown generalisation and simplification --- background.html | 2 + js/background.js | 3 ++ js/modules/signal.js | 2 + js/views/conversation_view.js | 8 +-- js/views/session_dropdown_view.js | 38 ++++++++++++++ stylesheets/_session.scss | 33 ++++++++++++- stylesheets/_session_theme_dark.scss | 2 + ts/components/UserDetailsDialog.tsx | 49 +++++++++---------- ts/components/session/SessionDropdown.tsx | 36 ++++++++------ ts/components/session/SessionDropdownItem.tsx | 2 + 10 files changed, 131 insertions(+), 44 deletions(-) create mode 100644 js/views/session_dropdown_view.js diff --git a/background.html b/background.html index 536d1ad05..0a03d5540 100644 --- a/background.html +++ b/background.html @@ -702,6 +702,8 @@ + + diff --git a/js/background.js b/js/background.js index ca0bbcc90..42a278f70 100644 --- a/js/background.js +++ b/js/background.js @@ -819,6 +819,7 @@ type: options.type || '', }; + // Give all toasts an ID. User may define. const toastID = params.id; const toast = !!toastID && window.toasts.get(toastID); @@ -831,8 +832,10 @@ new Whisper.SessionToastView({ el: $('#session-toast-container'), }) + ); + window.toasts.get(toastID).render(); window.toasts.get(toastID).update(params); } diff --git a/js/modules/signal.js b/js/modules/signal.js index d0e00e394..1b0a6e10b 100644 --- a/js/modules/signal.js +++ b/js/modules/signal.js @@ -53,6 +53,7 @@ const { UserDetailsDialog } = require('../../ts/components/UserDetailsDialog'); const { SessionToast } = require('../../ts/components/session/SessionToast'); const { SessionToggle } = require('../../ts/components/session/SessionToggle'); const { SessionModal } = require('../../ts/components/session/SessionModal'); +const { SessionDropdown } = require('../../ts/components/session/SessionDropdown'); const { SessionRegistrationView, } = require('../../ts/components/session/SessionRegistrationView'); @@ -254,6 +255,7 @@ exports.setup = (options = {}) => { SessionToast, SessionToggle, SessionModal, + SessionDropdown, MediaGallery, Message, MessageBody, diff --git a/js/views/conversation_view.js b/js/views/conversation_view.js index 06df077dc..98893afcb 100644 --- a/js/views/conversation_view.js +++ b/js/views/conversation_view.js @@ -1375,11 +1375,11 @@ if (!isAllOurs && !isModerator) { window.pushToast({ - title: i18n('messageDeletionForbidden'), - type: 'error', - id: 'messageDeletionForbidden', + title: i18n('messageDeletionForbidden'), + type: 'error', + id: 'messageDeletionForbidden', }); - + return; } diff --git a/js/views/session_dropdown_view.js b/js/views/session_dropdown_view.js new file mode 100644 index 000000000..8446fbb09 --- /dev/null +++ b/js/views/session_dropdown_view.js @@ -0,0 +1,38 @@ +/* global Whisper */ + +// eslint-disable-next-line func-names +(function() { + 'use strict'; + + window.Whisper = window.Whisper || {}; + + Whisper.SessionDropdownView = Whisper.View.extend({ + initialize(options) { + this.props = { + items: options.items, + }; + + this.render(); + }, + + render() { + this.dropdownView = new Whisper.ReactWrapperView({ + className: 'session-dropdown-wrapper', + Component: window.Signal.Components.SessionDropdown, + props: this.props, + }); + + this.$el.append(this.dropdownView.el); + }, + + openDropdown() { + + }, + + closeDropdown() { + + }, + + }); + })(); + \ No newline at end of file diff --git a/stylesheets/_session.scss b/stylesheets/_session.scss index 8c183e823..37c640205 100644 --- a/stylesheets/_session.scss +++ b/stylesheets/_session.scss @@ -562,7 +562,39 @@ label { } +// .react-contextmenu { +// padding: 0px; +// margin: 0px; + +// border: none !important; +// border-radius: 0px; +// } + +// .react-contextmenu-item { +// display: flex; +// align-items: center; + +// height: 25px; +// padding: 0px 10px; + +// background-color: #1B1B1B; + +// color: $session-color-white; +// font-family: 'Wasa'; +// font-size: 12px; +// line-height: $session-icon-size-sm; +// font-weight: 700; + +// &--active, &--selected{ +// background-color: $session-shade-7 !important; +// } +// } + + .session-dropdown{ + position: absolute; + top: 50px; + left: 50px; display: inline-block; ul { @@ -603,5 +635,4 @@ label { } } } - } \ No newline at end of file diff --git a/stylesheets/_session_theme_dark.scss b/stylesheets/_session_theme_dark.scss index 291853042..08e836935 100644 --- a/stylesheets/_session_theme_dark.scss +++ b/stylesheets/_session_theme_dark.scss @@ -53,4 +53,6 @@ margin-left: 5px; } } + } + diff --git a/ts/components/UserDetailsDialog.tsx b/ts/components/UserDetailsDialog.tsx index 3f07d8631..9ed4e43dd 100644 --- a/ts/components/UserDetailsDialog.tsx +++ b/ts/components/UserDetailsDialog.tsx @@ -1,8 +1,7 @@ import React from 'react'; import { Avatar } from './Avatar'; -import { SessionDropdown, SessionDropDownType } from './session/SessionDropdown'; -import { SessionIconType } from './session/icon'; +import { SessionDropdown } from './session/SessionDropdown'; declare global { interface Window { @@ -35,41 +34,41 @@ export class UserDetailsDialog extends React.Component { const cancelText = i18n('cancel'); const startConversation = i18n('startConversation'); - + const items = [ { - id: "myid1", - content: "Copy Chat ID", - icon: SessionIconType.Eye, - type: SessionDropDownType.Default, - active: false + content: "sdgsdfg", + display: true,//!isPublic && !isMe, + }, + { + content: i18n('changeNickname'), + display: true,//!isPublic && !isMe, + }, + { + content: i18n('clearNickname'), + display: true,//!isPublic && !isMe && hasNickname, }, { - id: "myid1", - content: "Invite Friends", - icon: null, - type: SessionDropDownType.Default, - active: false + content: i18n('copyPublicKey'), + display: false,//!isPublic, }, { - id: "myid1", - content: "Clear Chat History", - icon: SessionIconType.Check, - type: SessionDropDownType.Default, - active: true + content: i18n('deleteMessages'), }, { - id: "myid1", - content: "Leave Group", - icon: SessionIconType.Check, - type: SessionDropDownType.Danger, - active: false + content: i18n('deleteContact'), + display: true,//!isMe && isClosable && !isPublic, + }, + { + content: i18n('deletePublicChannel'), + display: true,//!isMe && isClosable && !isPublic, }, ]; - + return (
    - + +
    {this.renderAvatar()}
    diff --git a/ts/components/session/SessionDropdown.tsx b/ts/components/session/SessionDropdown.tsx index 21b6a1f7f..e88e5f037 100644 --- a/ts/components/session/SessionDropdown.tsx +++ b/ts/components/session/SessionDropdown.tsx @@ -1,23 +1,26 @@ import React from 'react'; import classNames from 'classnames'; -import { SessionDropdownItem, SessionDropDownItemType } from './SessionDropdownItem'; import { SessionIconType } from './icon/'; +import { SessionDropdownItem, SessionDropDownItemType } from './SessionDropdownItem'; +// THIS IS A FUTURE-PROOFING ELEMENT TO REPLACE ELECTRON CONTEXTMENUS IN PRELOAD.JS interface Props { + id?: string, items: Array<{ - id: string, content: string, - icon: SessionIconType | null, - type: SessionDropDownItemType, - active: boolean, + id?: string, + icon?: SessionIconType | null, + type?: SessionDropDownItemType, + active?: boolean, + onClick?: any, + display?: boolean, }>, } export class SessionDropdown extends React.PureComponent { - public static readonly defaultProps = SessionDropdownItem.defaultProps; - + constructor(props: any) { super(props); } @@ -28,13 +31,18 @@ export class SessionDropdown extends React.PureComponent { return (
      - {items.map((item: any) => + {items.map((item: any) => { + return item.display ? ( + + ) : null + } )}
    diff --git a/ts/components/session/SessionDropdownItem.tsx b/ts/components/session/SessionDropdownItem.tsx index f8862f748..41341d681 100644 --- a/ts/components/session/SessionDropdownItem.tsx +++ b/ts/components/session/SessionDropdownItem.tsx @@ -24,6 +24,8 @@ export class SessionDropdownItem extends React.PureComponent { public static defaultProps = { id: generateID(), type: SessionDropDownItemType.Default, + icon: null, + active: false, onClick: () => null, }; From d0d5012e075dfc340a0995af02952f4849b7ff53 Mon Sep 17 00:00:00 2001 From: Vincent Date: Wed, 18 Dec 2019 17:48:37 +1100 Subject: [PATCH 3/8] Fixed breaking sessionToggle issues --- _locales/en/messages.json | 2 +- js/background.js | 17 ++--- js/views/session_toggle_view.js | 4 +- stylesheets/_session.scss | 2 +- ts/components/ConversationListItem.tsx | 101 ++++++++++++++++++------- 5 files changed, 85 insertions(+), 41 deletions(-) diff --git a/_locales/en/messages.json b/_locales/en/messages.json index 2a95145b3..cb1f2c7f6 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -1133,7 +1133,7 @@ "Confirmation dialog text that asks the user if they really wish to delete the conversation. Answer buttons use the strings 'ok' and 'cancel'. The deletion is permanent, i.e. it cannot be undone." }, "deletePublicChannel": { - "message": "Leave public channel", + "message": "Leave Channel", "description": "Confirmation dialog title that asks the user if they really wish to delete a public channel. Answer buttons use the strings 'ok' and 'cancel'. The deletion is permanent, i.e. it cannot be undone." }, diff --git a/js/background.js b/js/background.js index 42a278f70..37cef4843 100644 --- a/js/background.js +++ b/js/background.js @@ -813,18 +813,18 @@ const params = { title: options.title, - id: - options.id || window.generateID(), + id: options.id || window.generateID(), description: options.description || '', type: options.type || '', }; - - + // Give all toasts an ID. User may define. + let currentToast; const toastID = params.id; const toast = !!toastID && window.toasts.get(toastID); if (toast) { - window.toasts.get(toastID).update(params); + currentToast = window.toasts.get(toastID); + currentToast.update(params); } else { // Make new Toast window.toasts.set( @@ -832,12 +832,11 @@ new Whisper.SessionToastView({ el: $('#session-toast-container'), }) - ); - - window.toasts.get(toastID).render(); - window.toasts.get(toastID).update(params); + currentToast = window.toasts.get(toastID); + currentToast.render(); + currentToast.update(params); } // Remove some toasts if too many exist diff --git a/js/views/session_toggle_view.js b/js/views/session_toggle_view.js index 60b5d7740..8d0e35a46 100644 --- a/js/views/session_toggle_view.js +++ b/js/views/session_toggle_view.js @@ -6,7 +6,7 @@ window.Whisper = window.Whisper || {}; - Whisper.SessionToastView = Whisper.View.extend({ + Whisper.SessionToggleView = Whisper.View.extend({ initialize(options) { this.props = { active: options.active, @@ -16,7 +16,7 @@ render() { this.toggleView = new Whisper.ReactWrapperView({ className: 'session-toggle-wrapper', - Component: window.Signal.Components.SessionToast, + Component: window.Signal.Components.SessionToggle, props: this.props, }); diff --git a/stylesheets/_session.scss b/stylesheets/_session.scss index 37c640205..823b7ca14 100644 --- a/stylesheets/_session.scss +++ b/stylesheets/_session.scss @@ -612,7 +612,7 @@ label { color: $session-color-white; font-family: 'Wasa'; - font-size: 12px; + font-size: 10px; line-height: $session-icon-size-sm; font-weight: 700; diff --git a/ts/components/ConversationListItem.tsx b/ts/components/ConversationListItem.tsx index d26d1fe08..8e0789b76 100644 --- a/ts/components/ConversationListItem.tsx +++ b/ts/components/ConversationListItem.tsx @@ -12,6 +12,9 @@ import { TypingAnimation } from './conversation/TypingAnimation'; import { Colors, LocalizerType } from '../types/Util'; +import { SessionDropdown } from './session/SessionDropdown'; +import { SessionDropDownItemType } from './session/SessionDropdownItem'; + export type PropsData = { id: string; phoneNumber: string; @@ -188,36 +191,78 @@ export class ConversationListItem extends React.PureComponent { const blockTitle = isBlocked ? i18n('unblockUser') : i18n('blockUser'); const blockHandler = isBlocked ? onUnblockContact : onBlockContact; + + const items = [ + { + content: blockTitle, + display: true,//!isPublic && !isMe, + onClick: blockHandler + }, + { + content: i18n('changeNickname'), + display: true,//!isPublic && !isMe, + onClick: onChangeNickname, + }, + { + content: i18n('clearNickname'), + display: true,//!isPublic && !isMe && hasNickname, + onClick: onClearNickname, + }, + { + content: i18n('copyPublicKey'), + display: true,//!isPublic, + onClick: onCopyPublicKey, + }, + { + content: i18n('deleteMessages'), + onClick: onDeleteMessages, + }, + { + content: i18n('deleteContact'), + display: true,//!isMe && isClosable && !isPublic, + onClick: onDeleteContact, + }, + { + content: i18n('deletePublicChannel'), + display: true,//!isMe && isClosable && !isPublic, + type: SessionDropDownItemType.Default, + onClick: onDeleteContact, + }, + ]; + + return ( - - {!isPublic && !isMe ? ( - {blockTitle} - ) : null} - {!isPublic && !isMe ? ( - - {i18n('changeNickname')} - - ) : null} - {!isPublic && !isMe && hasNickname ? ( - {i18n('clearNickname')} - ) : null} - {!isPublic ? ( - {i18n('copyPublicKey')} - ) : null} - {i18n('deleteMessages')} - {!isMe && isClosable ? ( - !isPublic ? ( - - {i18n('deleteContact')} - - ) : ( - - {i18n('deletePublicChannel')} - - ) - ) : null} - + ); + // + // {!isPublic && !isMe ? ( + // {blockTitle} + // ) : null} + // {!isPublic && !isMe ? ( + // + // {i18n('changeNickname')} + // + // ) : null} + // {!isPublic && !isMe && hasNickname ? ( + // {i18n('clearNickname')} + // ) : null} + // {!isPublic ? ( + // {i18n('copyPublicKey')} + // ) : null} + // {i18n('deleteMessages')} + // {!isMe && isClosable ? ( + // !isPublic ? ( + // + // {i18n('deleteContact')} + // + // ) : ( + // + // {i18n('deletePublicChannel')} + // + // ) + // ) : null} + // + //); } public renderMessage() { From 9afb8b4d5e2cbeafabc0515b0edad7ef198d7ae2 Mon Sep 17 00:00:00 2001 From: Vincent Date: Fri, 20 Dec 2019 10:57:50 +1100 Subject: [PATCH 4/8] Major rework of context menus --- js/background.js | 8 +- js/modules/signal.js | 4 +- js/views/conversation_view.js | 8 +- js/views/session_dropdown_view.js | 62 +++++------ stylesheets/_session.scss | 58 +++++----- stylesheets/_session_theme_dark.scss | 2 - ts/components/ConversationListItem.tsx | 102 +++++------------- ts/components/UserDetailsDialog.tsx | 18 ++-- .../conversation/ConversationHeader.tsx | 6 +- ts/components/session/SessionDropdown.tsx | 86 ++++++++++----- ts/components/session/SessionDropdownItem.tsx | 97 ++++++++--------- .../session/SessionDropdownTrigger.tsx | 37 +++++++ .../session/tools/ComponentTools.tsx | 9 +- 13 files changed, 255 insertions(+), 242 deletions(-) create mode 100644 ts/components/session/SessionDropdownTrigger.tsx diff --git a/js/background.js b/js/background.js index 37cef4843..51e943424 100644 --- a/js/background.js +++ b/js/background.js @@ -802,8 +802,10 @@ appView.openConversation(groupId, {}); }; - - window.generateID = () => Math.random().toString(36).substring(3); + window.generateID = () => + Math.random() + .toString(36) + .substring(3); window.toasts = new Map(); window.pushToast = options => { @@ -817,7 +819,7 @@ description: options.description || '', type: options.type || '', }; - + // Give all toasts an ID. User may define. let currentToast; const toastID = params.id; diff --git a/js/modules/signal.js b/js/modules/signal.js index 1b0a6e10b..ecfee5e70 100644 --- a/js/modules/signal.js +++ b/js/modules/signal.js @@ -53,7 +53,9 @@ const { UserDetailsDialog } = require('../../ts/components/UserDetailsDialog'); const { SessionToast } = require('../../ts/components/session/SessionToast'); const { SessionToggle } = require('../../ts/components/session/SessionToggle'); const { SessionModal } = require('../../ts/components/session/SessionModal'); -const { SessionDropdown } = require('../../ts/components/session/SessionDropdown'); +const { + SessionDropdown, +} = require('../../ts/components/session/SessionDropdown'); const { SessionRegistrationView, } = require('../../ts/components/session/SessionRegistrationView'); diff --git a/js/views/conversation_view.js b/js/views/conversation_view.js index 98893afcb..06df077dc 100644 --- a/js/views/conversation_view.js +++ b/js/views/conversation_view.js @@ -1375,11 +1375,11 @@ if (!isAllOurs && !isModerator) { window.pushToast({ - title: i18n('messageDeletionForbidden'), - type: 'error', - id: 'messageDeletionForbidden', + title: i18n('messageDeletionForbidden'), + type: 'error', + id: 'messageDeletionForbidden', }); - + return; } diff --git a/js/views/session_dropdown_view.js b/js/views/session_dropdown_view.js index 8446fbb09..a655bad34 100644 --- a/js/views/session_dropdown_view.js +++ b/js/views/session_dropdown_view.js @@ -2,37 +2,31 @@ // eslint-disable-next-line func-names (function() { - 'use strict'; - - window.Whisper = window.Whisper || {}; - - Whisper.SessionDropdownView = Whisper.View.extend({ - initialize(options) { - this.props = { - items: options.items, - }; - - this.render(); - }, - - render() { - this.dropdownView = new Whisper.ReactWrapperView({ - className: 'session-dropdown-wrapper', - Component: window.Signal.Components.SessionDropdown, - props: this.props, - }); - - this.$el.append(this.dropdownView.el); - }, - - openDropdown() { - - }, - - closeDropdown() { - - }, - - }); - })(); - \ No newline at end of file + 'use strict'; + + window.Whisper = window.Whisper || {}; + + Whisper.SessionDropdownView = Whisper.View.extend({ + initialize(options) { + this.props = { + items: options.items, + }; + + this.render(); + }, + + render() { + this.dropdownView = new Whisper.ReactWrapperView({ + className: 'session-dropdown-wrapper', + Component: window.Signal.Components.SessionDropdown, + props: this.props, + }); + + this.$el.append(this.dropdownView.el); + }, + + openDropdown() {}, + + closeDropdown() {}, + }); +})(); diff --git a/stylesheets/_session.scss b/stylesheets/_session.scss index 823b7ca14..1316b1f10 100644 --- a/stylesheets/_session.scss +++ b/stylesheets/_session.scss @@ -60,7 +60,7 @@ $session-opaque-dark-3: rgba(0, 0, 0, 0.5); $session-color-white: #fff; $session-color-dark-grey: #353535; $session-color-black: #000; -$session-color-danger: #FF453A; +$session-color-danger: #ff453a; $session-color-primary: $session-shade-13; $session-color-secondary: $session-shade-16; @@ -561,37 +561,36 @@ label { } } +.react-contextmenu { + padding: 0px; + margin: 0px; -// .react-contextmenu { -// padding: 0px; -// margin: 0px; - -// border: none !important; -// border-radius: 0px; -// } - -// .react-contextmenu-item { -// display: flex; -// align-items: center; + border: none !important; + border-radius: 0px; +} -// height: 25px; -// padding: 0px 10px; +.react-contextmenu-item { + display: flex; + align-items: center; -// background-color: #1B1B1B; + height: 25px; + padding: 0px 10px; -// color: $session-color-white; -// font-family: 'Wasa'; -// font-size: 12px; -// line-height: $session-icon-size-sm; -// font-weight: 700; + background-color: #1b1b1b; -// &--active, &--selected{ -// background-color: $session-shade-7 !important; -// } -// } + color: $session-color-white; + font-family: 'Wasa'; + font-size: 12px; + line-height: $session-icon-size-sm; + font-weight: 700; + &--active, + &--selected { + background-color: $session-shade-7 !important; + } +} -.session-dropdown{ +.session-dropdown { position: absolute; top: 50px; left: 50px; @@ -608,7 +607,7 @@ label { height: 25px; padding-right: 7px; - background-color: #1B1B1B; + background-color: #1b1b1b; color: $session-color-white; font-family: 'Wasa'; @@ -618,7 +617,7 @@ label { display: flex; align-items: center; - + .session-icon { margin-left: 6px; } @@ -626,7 +625,8 @@ label { margin-left: 6px; } - &.active, &:hover{ + &.active, + &:hover { background-color: $session-shade-7; } @@ -635,4 +635,4 @@ label { } } } -} \ No newline at end of file +} diff --git a/stylesheets/_session_theme_dark.scss b/stylesheets/_session_theme_dark.scss index 08e836935..291853042 100644 --- a/stylesheets/_session_theme_dark.scss +++ b/stylesheets/_session_theme_dark.scss @@ -53,6 +53,4 @@ margin-left: 5px; } } - } - diff --git a/ts/components/ConversationListItem.tsx b/ts/components/ConversationListItem.tsx index 8e0789b76..8d33a6f1a 100644 --- a/ts/components/ConversationListItem.tsx +++ b/ts/components/ConversationListItem.tsx @@ -1,4 +1,5 @@ import React from 'react'; +import ReactDOM from 'react-dom'; import classNames from 'classnames'; import { isEmpty } from 'lodash'; import { ContextMenu, ContextMenuTrigger, MenuItem } from 'react-contextmenu'; @@ -12,9 +13,6 @@ import { TypingAnimation } from './conversation/TypingAnimation'; import { Colors, LocalizerType } from '../types/Util'; -import { SessionDropdown } from './session/SessionDropdown'; -import { SessionDropDownItemType } from './session/SessionDropdownItem'; - export type PropsData = { id: string; phoneNumber: string; @@ -191,78 +189,36 @@ export class ConversationListItem extends React.PureComponent { const blockTitle = isBlocked ? i18n('unblockUser') : i18n('blockUser'); const blockHandler = isBlocked ? onUnblockContact : onBlockContact; - - const items = [ - { - content: blockTitle, - display: true,//!isPublic && !isMe, - onClick: blockHandler - }, - { - content: i18n('changeNickname'), - display: true,//!isPublic && !isMe, - onClick: onChangeNickname, - }, - { - content: i18n('clearNickname'), - display: true,//!isPublic && !isMe && hasNickname, - onClick: onClearNickname, - }, - { - content: i18n('copyPublicKey'), - display: true,//!isPublic, - onClick: onCopyPublicKey, - }, - { - content: i18n('deleteMessages'), - onClick: onDeleteMessages, - }, - { - content: i18n('deleteContact'), - display: true,//!isMe && isClosable && !isPublic, - onClick: onDeleteContact, - }, - { - content: i18n('deletePublicChannel'), - display: true,//!isMe && isClosable && !isPublic, - type: SessionDropDownItemType.Default, - onClick: onDeleteContact, - }, - ]; - - return ( - + + {!isPublic && !isMe ? ( + {blockTitle} + ) : null} + {!isPublic && !isMe ? ( + + {i18n('changeNickname')} + + ) : null} + {!isPublic && !isMe && hasNickname ? ( + {i18n('clearNickname')} + ) : null} + {!isPublic ? ( + {i18n('copyPublicKey')} + ) : null} + {i18n('deleteMessages')} + {!isMe && isClosable ? ( + !isPublic ? ( + + {i18n('deleteContact')} + + ) : ( + + {i18n('deletePublicChannel')} + + ) + ) : null} + ); - // - // {!isPublic && !isMe ? ( - // {blockTitle} - // ) : null} - // {!isPublic && !isMe ? ( - // - // {i18n('changeNickname')} - // - // ) : null} - // {!isPublic && !isMe && hasNickname ? ( - // {i18n('clearNickname')} - // ) : null} - // {!isPublic ? ( - // {i18n('copyPublicKey')} - // ) : null} - // {i18n('deleteMessages')} - // {!isMe && isClosable ? ( - // !isPublic ? ( - // - // {i18n('deleteContact')} - // - // ) : ( - // - // {i18n('deletePublicChannel')} - // - // ) - // ) : null} - // - //); } public renderMessage() { diff --git a/ts/components/UserDetailsDialog.tsx b/ts/components/UserDetailsDialog.tsx index 9ed4e43dd..f0d765b96 100644 --- a/ts/components/UserDetailsDialog.tsx +++ b/ts/components/UserDetailsDialog.tsx @@ -34,40 +34,40 @@ export class UserDetailsDialog extends React.Component { const cancelText = i18n('cancel'); const startConversation = i18n('startConversation'); - + const items = [ { - content: "sdgsdfg", - display: true,//!isPublic && !isMe, + content: 'sdgsdfg', + display: true, //!isPublic && !isMe, }, { content: i18n('changeNickname'), - display: true,//!isPublic && !isMe, + display: true, //!isPublic && !isMe, }, { content: i18n('clearNickname'), - display: true,//!isPublic && !isMe && hasNickname, + display: true, //!isPublic && !isMe && hasNickname, }, { content: i18n('copyPublicKey'), - display: false,//!isPublic, + display: false, //!isPublic, }, { content: i18n('deleteMessages'), }, { content: i18n('deleteContact'), - display: true,//!isMe && isClosable && !isPublic, + display: true, //!isMe && isClosable && !isPublic, }, { content: i18n('deletePublicChannel'), - display: true,//!isMe && isClosable && !isPublic, + display: true, //!isMe && isClosable && !isPublic, }, ]; return (
    - +
    {this.renderAvatar()}
    diff --git a/ts/components/conversation/ConversationHeader.tsx b/ts/components/conversation/ConversationHeader.tsx index 8c071bb0d..fe065e6bc 100644 --- a/ts/components/conversation/ConversationHeader.tsx +++ b/ts/components/conversation/ConversationHeader.tsx @@ -22,6 +22,8 @@ import { SessionButtonType, } from '../session/SessionButton'; +import { SessionDropdownTrigger } from '../session/SessionDropdownTrigger'; + interface TimerOption { name: string; value: number; @@ -245,12 +247,12 @@ export class ConversationHeader extends React.Component { } return ( - + - + ); } diff --git a/ts/components/session/SessionDropdown.tsx b/ts/components/session/SessionDropdown.tsx index e88e5f037..8bbad8310 100644 --- a/ts/components/session/SessionDropdown.tsx +++ b/ts/components/session/SessionDropdown.tsx @@ -2,52 +2,80 @@ import React from 'react'; import classNames from 'classnames'; import { SessionIconType } from './icon/'; -import { SessionDropdownItem, SessionDropDownItemType } from './SessionDropdownItem'; +import { + SessionDropdownItem, + SessionDropDownItemType, +} from './SessionDropdownItem'; // THIS IS A FUTURE-PROOFING ELEMENT TO REPLACE ELECTRON CONTEXTMENUS IN PRELOAD.JS +interface State { + x: number; + y: number; + isVisible: boolean; +} + interface Props { - id?: string, - items: Array<{ - content: string, - id?: string, - icon?: SessionIconType | null, - type?: SessionDropDownItemType, - active?: boolean, - onClick?: any, - display?: boolean, - }>, - } + id?: string; + onClick?: any; + relativeTo: string | Array; + items: Array<{ + content: string; + id?: string; + icon?: SessionIconType | null; + type?: SessionDropDownItemType; + active?: boolean; + onClick?: any; + display?: boolean; + }>; +} -export class SessionDropdown extends React.PureComponent { - +export class SessionDropdown extends React.Component { constructor(props: any) { super(props); + + this.state = { + x: 0, + y: 0, + isVisible: false, + }; + } + + public show() { + this.setState({ + isVisible: true, + }); + } + + public hide() { + this.setState({ + isVisible: false, + }); } - + public render() { const { items } = this.props; + const { isVisible } = this.state; return (
      - {items.map((item: any) => { + {isVisible + ? items.map((item: any) => { return item.display ? ( - - ) : null - } - )} + + ) : null; + }) + : null}
    ); } - } - diff --git a/ts/components/session/SessionDropdownItem.tsx b/ts/components/session/SessionDropdownItem.tsx index 41341d681..2647339b2 100644 --- a/ts/components/session/SessionDropdownItem.tsx +++ b/ts/components/session/SessionDropdownItem.tsx @@ -1,68 +1,61 @@ - import React from 'react'; import classNames from 'classnames'; import { SessionIcon, SessionIconSize, SessionIconType } from './icon/'; import { generateID } from './tools/ComponentTools'; - export enum SessionDropDownItemType { - Default = 'default', - Danger = 'danger', + Default = 'default', + Danger = 'danger', } interface Props { - id: string, - content: string, - type: SessionDropDownItemType, - icon: SessionIconType | null, - active: boolean, - onClick: any, + id: string; + content: string; + type: SessionDropDownItemType; + icon: SessionIconType | null; + active: boolean; + onClick: any; } export class SessionDropdownItem extends React.PureComponent { - public static defaultProps = { - id: generateID(), - type: SessionDropDownItemType.Default, - icon: null, - active: false, - onClick: () => null, - }; - - constructor(props: any) { - super(props); - this.clickHandler = this.clickHandler.bind(this); - } - - public render() { - const { id, content, type, icon, active } = this.props; - - return ( -
  • - { icon ? - : '' - } -
    - {content} -
    -
  • - ); - } - - private clickHandler(e: any){ - if (this.props.onClick) { - e.stopPropagation(); - this.props.onClick(); - } - } + public static defaultProps = { + id: generateID(), + type: SessionDropDownItemType.Default, + icon: null, + active: false, + onClick: () => null, + }; + + constructor(props: any) { + super(props); + this.clickHandler = this.clickHandler.bind(this); } + public render() { + const { id, content, type, icon, active } = this.props; + + return ( +
  • + {icon ? ( + + ) : ( + '' + )} +
    {content}
    +
  • + ); + } - + private clickHandler(e: any) { + if (this.props.onClick) { + e.stopPropagation(); + this.props.onClick(); + } + } +} diff --git a/ts/components/session/SessionDropdownTrigger.tsx b/ts/components/session/SessionDropdownTrigger.tsx new file mode 100644 index 000000000..0a366388f --- /dev/null +++ b/ts/components/session/SessionDropdownTrigger.tsx @@ -0,0 +1,37 @@ +import React from 'react'; + +interface Props { + //mouseButton: Number; + posX: number; + posY: number; +} + +export class SessionDropdownTrigger extends React.Component { + public static defaultProps = { + //mouseButton: 2, // 0 is left click, 2 is right click + posX: 0, + posY: 0, + }; + constructor(props: any) { + super(props); + } + + public handleDropdownClick = (event: any) => { + event.preventDefault(); + event.stopPropagation(); + + let x = event.clientX || (event.touches && event.touches[0].pageX); + let y = event.clientY || (event.touches && event.touches[0].pageY); + + if (this.props.posX) { + x -= this.props.posX; + } + if (this.props.posY) { + y -= this.props.posY; + } + }; + + public render() { + return
    ; + } +} diff --git a/ts/components/session/tools/ComponentTools.tsx b/ts/components/session/tools/ComponentTools.tsx index 7bdff6063..e91b8a295 100644 --- a/ts/components/session/tools/ComponentTools.tsx +++ b/ts/components/session/tools/ComponentTools.tsx @@ -1,7 +1,8 @@ // This is a collection of tools which can be ued to simplify the esign and rendering process of your components. -export function generateID(){ - // Generates a unique ID for your component - return Math.random().toString(36).substring(3); -} +export function generateID() { + // Generates a unique ID for your component + const buffer = new Uint32Array(10); + return window.crypto.getRandomValues(buffer)[0].toString(); +} From 8aac656107b21dd569caa7ee93927fa8cc3e3464 Mon Sep 17 00:00:00 2001 From: Vincent Date: Fri, 20 Dec 2019 16:08:57 +1100 Subject: [PATCH 5/8] Modals finished. Need to xfer all to React --- _locales/en/messages.json | 10 +- js/modules/signal.js | 2 + js/views/device_pairing_dialog_view.js | 183 +----------------- js/views/device_pairing_dialog_view_old.js | 209 +++++++++++++++++++++ stylesheets/_session.scss | 69 ++++++- ts/components/ConfirmDialog.tsx | 28 ++- ts/components/DevicePairingDialog.tsx | 0 ts/components/EditProfileDialog.tsx | 60 ++++-- ts/components/UserDetailsDialog.tsx | 68 ++----- ts/components/session/SessionButton.tsx | 1 + ts/components/session/SessionModal.tsx | 96 +++++++--- ts/components/session/icon/Icons.tsx | 6 + 12 files changed, 439 insertions(+), 293 deletions(-) create mode 100644 js/views/device_pairing_dialog_view_old.js create mode 100644 ts/components/DevicePairingDialog.tsx diff --git a/_locales/en/messages.json b/_locales/en/messages.json index cb1f2c7f6..4ebee76d9 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -1817,7 +1817,7 @@ "description": "Label text for menu bar visibility setting" }, "startConversation": { - "message": "Start new conversation…", + "message": "Start New Conversation", "description": "Label underneath number a user enters that is not an existing contact" }, @@ -2259,6 +2259,14 @@ "editProfileDialogTitle": { "message": "Editing Profile" }, + + + "editProfileModalTitle": { + "message": "Edit Profile", + "description": "Title for the Edit Profile modal" + }, + + "profileName": { "message": "Profile Name" }, diff --git a/js/modules/signal.js b/js/modules/signal.js index ecfee5e70..0d5617e19 100644 --- a/js/modules/signal.js +++ b/js/modules/signal.js @@ -50,6 +50,7 @@ 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 { SessionToast } = require('../../ts/components/session/SessionToast'); const { SessionToggle } = require('../../ts/components/session/SessionToggle'); const { SessionModal } = require('../../ts/components/session/SessionModal'); @@ -248,6 +249,7 @@ exports.setup = (options = {}) => { CreateGroupDialog, EditProfileDialog, UserDetailsDialog, + DevicePairingDialog, SessionRegistrationView, ConfirmDialog, UpdateGroupDialog, diff --git a/js/views/device_pairing_dialog_view.js b/js/views/device_pairing_dialog_view.js index 80d6b4f61..32d44eed2 100644 --- a/js/views/device_pairing_dialog_view.js +++ b/js/views/device_pairing_dialog_view.js @@ -27,183 +27,12 @@ }); this.qr.makeCode(textsecure.storage.user.getNumber()); }, - reset() { - this.pubKey = null; - this.accepted = false; - this.isListening = false; - this.pubKeyToUnpair = null; - this.success = false; - }, - events: { - 'click #startPairing': 'startReceivingRequests', - 'click #close': 'close', - 'click .waitingForRequestView .cancel': 'stopReceivingRequests', - 'click .requestReceivedView .skip': 'skipDevice', - 'click #allowPairing': 'allowDevice', - 'click .requestAcceptedView .ok': 'stopReceivingRequests', - 'click .confirmUnpairView .cancel': 'stopReceivingRequests', - 'click .confirmUnpairView .unpairDevice': 'confirmUnpairDevice', - }, - render_attributes() { - return { - defaultTitle: i18n('pairedDevices'), - waitingForRequestTitle: i18n('waitingForDeviceToRegister'), - requestReceivedTitle: i18n('devicePairingReceived'), - requestAcceptedTitle: i18n('devicePairingAccepted'), - startPairingText: i18n('pairNewDevice'), - cancelText: i18n('cancel'), - unpairDevice: i18n('unpairDevice'), - closeText: i18n('close'), - skipText: i18n('skip'), - okText: i18n('ok'), - allowPairingText: i18n('allowPairing'), - confirmUnpairViewTitle: i18n('confirmUnpairingTitle'), - }; - }, - startReceivingRequests() { - this.trigger('startReceivingRequests'); - this.isListening = true; - this.showView(); - }, - stopReceivingRequests() { - if (this.success) { - const deviceAlias = this.$('#deviceAlias')[0].value.trim(); - const conv = ConversationController.get(this.pubKey); - if (conv) { - conv.setNickname(deviceAlias); - } - } - this.trigger('stopReceivingRequests'); - this.reset(); - this.showView(); - }, - requestReceived(secondaryDevicePubKey) { - // FIFO: push at the front of the array with unshift() - this.pubKeyRequests.unshift(secondaryDevicePubKey); - if (!this.pubKey) { - this.nextPubKey(); - this.showView('requestReceived'); - } - }, - allowDevice() { - this.accepted = true; - this.trigger('devicePairingRequestAccepted', this.pubKey, errors => - this.transmisssionCB(errors) - ); - this.showView(); - }, - transmisssionCB(errors) { - 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.success = true; - } else { - this.$('.transmissionStatus').text(errors); - this.$('.requestAcceptedView .ok').show(); - } - }, - skipDevice() { - this.trigger('devicePairingRequestRejected', this.pubKey); - this.nextPubKey(); - this.showView(); - }, - nextPubKey() { - // FIFO: pop at the back of the array using pop() - this.pubKey = this.pubKeyRequests.pop(); - }, - async confirmUnpairDevice() { - this.trigger('deviceUnpairingRequested', this.pubKeyToUnpair); - this.reset(); - this.showView(); - }, - requestUnpairDevice(pubKey) { - this.pubKeyToUnpair = pubKey; - this.showView(); - }, - getPubkeyName(pubKey) { - const secretWords = window.mnemonic.pubkey_to_secret_words(pubKey); - const conv = ConversationController.get(pubKey); - const deviceAlias = conv ? conv.getNickname() : 'Unnamed Device'; - return `${deviceAlias} (pairing secret: ${secretWords})`; - }, - async showView() { - const defaultView = this.$('.defaultView'); - const waitingForRequestView = this.$('.waitingForRequestView'); - const requestReceivedView = this.$('.requestReceivedView'); - const requestAcceptedView = this.$('.requestAcceptedView'); - const confirmUnpairView = this.$('.confirmUnpairView'); - if (this.pubKeyToUnpair) { - defaultView.hide(); - requestReceivedView.hide(); - waitingForRequestView.hide(); - requestAcceptedView.hide(); - confirmUnpairView.show(); - const name = this.getPubkeyName(this.pubKeyToUnpair); - this.$('.confirmUnpairView #pubkey').html(name); - } else if (!this.isListening) { - requestReceivedView.hide(); - waitingForRequestView.hide(); - requestAcceptedView.hide(); - confirmUnpairView.hide(); - - const ourPubKey = textsecure.storage.user.getNumber(); - defaultView.show(); - const pubKeys = await libloki.storage.getSecondaryDevicesFor(ourPubKey); - this.$('#pairedPubKeys').empty(); - if (pubKeys && pubKeys.length > 0) { - this.$('#startPairing').attr('disabled', true); - pubKeys.forEach(x => { - const name = this.getPubkeyName(x); - const li = $('
  • ').html(name); - if (window.lokiFeatureFlags.multiDeviceUnpairing) { - const link = $('') - .text('Unpair') - .attr('href', '#'); - link.on('click', () => this.requestUnpairDevice(x)); - li.append(' - '); - li.append(link); - } - this.$('#pairedPubKeys').append(li); - }); - } else { - this.$('#startPairing').removeAttr('disabled'); - this.$('#pairedPubKeys').append('
  • No paired devices
  • '); - } - } else if (this.accepted) { - defaultView.hide(); - requestReceivedView.hide(); - waitingForRequestView.hide(); - requestAcceptedView.show(); - } else if (this.pubKey) { - const secretWords = window.mnemonic.pubkey_to_secret_words(this.pubKey); - this.$('.secretWords').text(secretWords); - requestReceivedView.show(); - waitingForRequestView.hide(); - requestAcceptedView.hide(); - defaultView.hide(); - } else { - waitingForRequestView.show(); - requestReceivedView.hide(); - requestAcceptedView.hide(); - defaultView.hide(); - } - }, - close() { - this.remove(); - this.qr.clear(); - if (this.pubKey && !this.accepted) { - this.trigger('devicePairingRequestRejected', this.pubKey); - } - this.trigger('close'); + + render() { + this.dialogView = new Whisper.ReactWrapperView({ + className: 'device-pairing-dialog', + Component: window.Signal.Components.DevicePairingDialog, + }); }, }); })(); diff --git a/js/views/device_pairing_dialog_view_old.js b/js/views/device_pairing_dialog_view_old.js new file mode 100644 index 000000000..80d6b4f61 --- /dev/null +++ b/js/views/device_pairing_dialog_view_old.js @@ -0,0 +1,209 @@ +/* global + Whisper, + i18n, + libloki, + textsecure, + ConversationController, + $, + QRCode, +*/ + +// eslint-disable-next-line func-names +(function() { + 'use strict'; + + window.Whisper = window.Whisper || {}; + + Whisper.DevicePairingDialogView = Whisper.View.extend({ + className: 'loki-dialog device-pairing-dialog modal', + templateName: 'device-pairing-dialog', + initialize() { + this.pubKeyRequests = []; + this.reset(); + this.render(); + this.showView(); + this.qr = new QRCode(this.$('#qr')[0], { + correctLevel: QRCode.CorrectLevel.L, + }); + this.qr.makeCode(textsecure.storage.user.getNumber()); + }, + reset() { + this.pubKey = null; + this.accepted = false; + this.isListening = false; + this.pubKeyToUnpair = null; + this.success = false; + }, + events: { + 'click #startPairing': 'startReceivingRequests', + 'click #close': 'close', + 'click .waitingForRequestView .cancel': 'stopReceivingRequests', + 'click .requestReceivedView .skip': 'skipDevice', + 'click #allowPairing': 'allowDevice', + 'click .requestAcceptedView .ok': 'stopReceivingRequests', + 'click .confirmUnpairView .cancel': 'stopReceivingRequests', + 'click .confirmUnpairView .unpairDevice': 'confirmUnpairDevice', + }, + render_attributes() { + return { + defaultTitle: i18n('pairedDevices'), + waitingForRequestTitle: i18n('waitingForDeviceToRegister'), + requestReceivedTitle: i18n('devicePairingReceived'), + requestAcceptedTitle: i18n('devicePairingAccepted'), + startPairingText: i18n('pairNewDevice'), + cancelText: i18n('cancel'), + unpairDevice: i18n('unpairDevice'), + closeText: i18n('close'), + skipText: i18n('skip'), + okText: i18n('ok'), + allowPairingText: i18n('allowPairing'), + confirmUnpairViewTitle: i18n('confirmUnpairingTitle'), + }; + }, + startReceivingRequests() { + this.trigger('startReceivingRequests'); + this.isListening = true; + this.showView(); + }, + stopReceivingRequests() { + if (this.success) { + const deviceAlias = this.$('#deviceAlias')[0].value.trim(); + const conv = ConversationController.get(this.pubKey); + if (conv) { + conv.setNickname(deviceAlias); + } + } + this.trigger('stopReceivingRequests'); + this.reset(); + this.showView(); + }, + requestReceived(secondaryDevicePubKey) { + // FIFO: push at the front of the array with unshift() + this.pubKeyRequests.unshift(secondaryDevicePubKey); + if (!this.pubKey) { + this.nextPubKey(); + this.showView('requestReceived'); + } + }, + allowDevice() { + this.accepted = true; + this.trigger('devicePairingRequestAccepted', this.pubKey, errors => + this.transmisssionCB(errors) + ); + this.showView(); + }, + transmisssionCB(errors) { + 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.success = true; + } else { + this.$('.transmissionStatus').text(errors); + this.$('.requestAcceptedView .ok').show(); + } + }, + skipDevice() { + this.trigger('devicePairingRequestRejected', this.pubKey); + this.nextPubKey(); + this.showView(); + }, + nextPubKey() { + // FIFO: pop at the back of the array using pop() + this.pubKey = this.pubKeyRequests.pop(); + }, + async confirmUnpairDevice() { + this.trigger('deviceUnpairingRequested', this.pubKeyToUnpair); + this.reset(); + this.showView(); + }, + requestUnpairDevice(pubKey) { + this.pubKeyToUnpair = pubKey; + this.showView(); + }, + getPubkeyName(pubKey) { + const secretWords = window.mnemonic.pubkey_to_secret_words(pubKey); + const conv = ConversationController.get(pubKey); + const deviceAlias = conv ? conv.getNickname() : 'Unnamed Device'; + return `${deviceAlias} (pairing secret: ${secretWords})`; + }, + async showView() { + const defaultView = this.$('.defaultView'); + const waitingForRequestView = this.$('.waitingForRequestView'); + const requestReceivedView = this.$('.requestReceivedView'); + const requestAcceptedView = this.$('.requestAcceptedView'); + const confirmUnpairView = this.$('.confirmUnpairView'); + if (this.pubKeyToUnpair) { + defaultView.hide(); + requestReceivedView.hide(); + waitingForRequestView.hide(); + requestAcceptedView.hide(); + confirmUnpairView.show(); + const name = this.getPubkeyName(this.pubKeyToUnpair); + this.$('.confirmUnpairView #pubkey').html(name); + } else if (!this.isListening) { + requestReceivedView.hide(); + waitingForRequestView.hide(); + requestAcceptedView.hide(); + confirmUnpairView.hide(); + + const ourPubKey = textsecure.storage.user.getNumber(); + defaultView.show(); + const pubKeys = await libloki.storage.getSecondaryDevicesFor(ourPubKey); + this.$('#pairedPubKeys').empty(); + if (pubKeys && pubKeys.length > 0) { + this.$('#startPairing').attr('disabled', true); + pubKeys.forEach(x => { + const name = this.getPubkeyName(x); + const li = $('
  • ').html(name); + if (window.lokiFeatureFlags.multiDeviceUnpairing) { + const link = $('') + .text('Unpair') + .attr('href', '#'); + link.on('click', () => this.requestUnpairDevice(x)); + li.append(' - '); + li.append(link); + } + this.$('#pairedPubKeys').append(li); + }); + } else { + this.$('#startPairing').removeAttr('disabled'); + this.$('#pairedPubKeys').append('
  • No paired devices
  • '); + } + } else if (this.accepted) { + defaultView.hide(); + requestReceivedView.hide(); + waitingForRequestView.hide(); + requestAcceptedView.show(); + } else if (this.pubKey) { + const secretWords = window.mnemonic.pubkey_to_secret_words(this.pubKey); + this.$('.secretWords').text(secretWords); + requestReceivedView.show(); + waitingForRequestView.hide(); + requestAcceptedView.hide(); + defaultView.hide(); + } else { + waitingForRequestView.show(); + requestReceivedView.hide(); + requestAcceptedView.hide(); + defaultView.hide(); + } + }, + close() { + this.remove(); + this.qr.clear(); + if (this.pubKey && !this.accepted) { + this.trigger('devicePairingRequestRejected', this.pubKey); + } + this.trigger('close'); + }, + }); +})(); diff --git a/stylesheets/_session.scss b/stylesheets/_session.scss index 1316b1f10..e317f4b41 100644 --- a/stylesheets/_session.scss +++ b/stylesheets/_session.scss @@ -78,6 +78,16 @@ $session-margin-sm: 10px; $session-margin-md: 15px; $session-margin-lg: 20px; +div.spacer-sm { + height: $session-margin-sm; +} +div.spacer-md { + height: $session-margin-md; +} +div.spacer-lg { + height: $session-margin-lg; +} + @mixin session-color-subtle($color) { color: rgba($color, 0.6); } @@ -173,6 +183,9 @@ $session_message-container-border-radius: 5px; &.secondary { background-color: $session-color-secondary; } + &.success { + background-color: $session-color-success; + } &.danger { background-color: $session-color-danger; } @@ -493,19 +506,21 @@ label { .session-modal { z-index: 150; position: fixed; - top: 50%; left: 50%; + top: 50%; + transform: translate(-50%, -50%); box-sizing: border-box; - height: 529px; - width: 345px; + max-height: 70vh; + max-width: 70vw; background-color: $session-shade-4; border: 1px solid $session-shade-8; - .header { + &__header { display: flex; flex-direction: row; justify-content: space-between; + padding: $session-margin-lg; font-family: 'Wasa'; @@ -514,17 +529,45 @@ label { font-size: 15px; font-weight: 700; - .close, - .icons { - width: 70px; + &__icons, &__close { + width: 60px; } - .close > div { + &__icons { + float: right; + } + &__close > div { float: left; } - .icons > div { + &__icons > div { float: right; + padding-left: 10px; + } + } + + &__body { + padding: $session-margin-lg; + + font-family: 'Wasa'; + line-height: 16px; + font-size: 13px; + + .message{ + text-align: center; } } + + &__button-group { + display: flex; + justify-content: flex-end; + + &__center{ + align-items: center; + } + .session-button { + margin-left: $session-margin-sm; + } + } + } .session-toggle { @@ -636,3 +679,11 @@ label { } } } + + + +.edit-profile-dialog .image-upload-section { + position: absolute; + margin-top: 50px; + margin-left: 75px; +} \ No newline at end of file diff --git a/ts/components/ConfirmDialog.tsx b/ts/components/ConfirmDialog.tsx index 0a1cb510e..ef02efaf0 100644 --- a/ts/components/ConfirmDialog.tsx +++ b/ts/components/ConfirmDialog.tsx @@ -1,5 +1,8 @@ import React from 'react'; +import { SessionModal } from './session/SessionModal'; +import { SessionButton } from './session/SessionButton'; + interface Props { titleText: string; messageText: string; @@ -16,18 +19,23 @@ export class ConfirmDialog extends React.Component { public render() { return ( -
    -

    {this.props.titleText}

    + null} onOk={() => null}> +

    {this.props.messageText}

    -
    - - +
    + +
    + + +
    -
    +
    ); } } diff --git a/ts/components/DevicePairingDialog.tsx b/ts/components/DevicePairingDialog.tsx new file mode 100644 index 000000000..e69de29bb diff --git a/ts/components/EditProfileDialog.tsx b/ts/components/EditProfileDialog.tsx index 4c103b56d..438d937a9 100644 --- a/ts/components/EditProfileDialog.tsx +++ b/ts/components/EditProfileDialog.tsx @@ -2,6 +2,11 @@ import React from 'react'; import classNames from 'classnames'; import { Avatar } from './Avatar'; +import { SessionModal } from './session/SessionModal'; +import { SessionButton, SessionButtonColor } from './session/SessionButton'; +import { SessionIconButton, SessionIconType, SessionIconSize } from './session/icon'; + + declare global { interface Window { displayNameRegex: any; @@ -63,11 +68,15 @@ export class EditProfileDialog extends React.Component { ); return ( -
    +
    {this.renderAvatar()} -
    +
    { name="name" onChange={this.onFileSelected} /> -
    { - const el = this.inputEl.current; - if (el) { - el.click(); - } - }} - /> + + { + const el = this.inputEl.current; + if (el) { + el.click(); + } + }} + />
    + +
    + { />
    {i18n('editProfileDisplayNameWarning')}
    {this.state.errorMessage} -
    - - + +
    + +
    + +
    -
    + ); } diff --git a/ts/components/UserDetailsDialog.tsx b/ts/components/UserDetailsDialog.tsx index f0d765b96..b1a9b6020 100644 --- a/ts/components/UserDetailsDialog.tsx +++ b/ts/components/UserDetailsDialog.tsx @@ -1,7 +1,8 @@ import React from 'react'; import { Avatar } from './Avatar'; -import { SessionDropdown } from './session/SessionDropdown'; +import { SessionModal } from './session/SessionModal'; +import { SessionButton, SessionButtonType, SessionButtonColor } from './session/SessionButton'; declare global { interface Window { @@ -31,64 +32,27 @@ export class UserDetailsDialog extends React.Component { public render() { const i18n = this.props.i18n; - - const cancelText = i18n('cancel'); - const startConversation = i18n('startConversation'); - - const items = [ - { - content: 'sdgsdfg', - display: true, //!isPublic && !isMe, - }, - { - content: i18n('changeNickname'), - display: true, //!isPublic && !isMe, - }, - { - content: i18n('clearNickname'), - display: true, //!isPublic && !isMe && hasNickname, - }, - { - content: i18n('copyPublicKey'), - display: false, //!isPublic, - }, - { - content: i18n('deleteMessages'), - }, - { - content: i18n('deleteContact'), - display: true, //!isMe && isClosable && !isPublic, - }, - { - content: i18n('deletePublicChannel'), - display: true, //!isMe && isClosable && !isPublic, - }, - ]; - + return ( -
    - - + null} + onClose={this.closeDialog} + >
    {this.renderAvatar()}
    -
    {this.props.profileName}
    {this.props.pubkey}
    -
    - - - +
    +
    -
    +
    ); } diff --git a/ts/components/session/SessionButton.tsx b/ts/components/session/SessionButton.tsx index 62b0aebd2..99f92f9c9 100644 --- a/ts/components/session/SessionButton.tsx +++ b/ts/components/session/SessionButton.tsx @@ -16,6 +16,7 @@ export enum SessionButtonColor { White = 'white', Primary = 'primary', Secondary = 'secondary', + Success = 'success', Danger = 'danger', Warning = 'warning', } diff --git a/ts/components/session/SessionModal.tsx b/ts/components/session/SessionModal.tsx index 325f772ec..9217568b3 100644 --- a/ts/components/session/SessionModal.tsx +++ b/ts/components/session/SessionModal.tsx @@ -5,39 +5,87 @@ import { SessionIconButton, SessionIconSize, SessionIconType } from './icon/'; interface Props { title: string; - body: any; + onClose: any; + onOk: any; + //Maximum of two icons in header + headerIconButtons?: Array<{ + type: SessionIconType; + onClick?: any; + }>; } -export class SessionModal extends React.PureComponent { +interface State { + isVisible: boolean; +} + +export class SessionModal extends React.PureComponent { + constructor(props: any) { super(props); + this.state = { + isVisible: true, + }; + + this.close = this.close.bind(this); + this.onKeyUp = this.onKeyUp.bind(this); + + window.addEventListener('keyup', this.onKeyUp); } public render() { - const { title } = this.props; - - return ( -
    -
    -
    - + const { title, headerIconButtons } = this.props; + const { isVisible } = this.state; + + return isVisible ? ( +
    +
    +
    + +
    +
    {title}
    +
    + { headerIconButtons ? headerIconButtons.map((iconItem: any) => { + return ( + + ) + }) : null + } +
    -
    {title}
    -
    - - + +
    + {this.props.children}
    -
    - ); + ) : null; + } + + public close() { + this.setState({ + isVisible: false, + }); + + window.removeEventListener('keyup', this.onKeyUp); + this.props.onClose(); + } + + public onKeyUp(event: any){ + switch (event.key) { + case 'Enter': + this.props.onOk(); + break; + case 'Esc': + case 'Escape': + this.close(); + break; + default: + } } } diff --git a/ts/components/session/icon/Icons.tsx b/ts/components/session/icon/Icons.tsx index 4b4544907..05d098e4f 100644 --- a/ts/components/session/icon/Icons.tsx +++ b/ts/components/session/icon/Icons.tsx @@ -26,6 +26,7 @@ export enum SessionIconType { Star = 'star', QR = 'qr', Users = 'users', + Upload = 'upload', Warning = 'warning', } @@ -171,6 +172,11 @@ export const icons = { 'M9.38,2.17c-1.73,0-3.12,1.4-3.12,3.12s1.4,3.12,3.12,3.12s3.12-1.4,3.12-3.12S11.1,2.17,9.38,2.17z M16.93,0.25c2.3,0.59,3.92,2.67,3.92,5.05s-1.61,4.46-3.92,5.05c-0.56,0.14-1.12-0.19-1.27-0.75c-0.14-0.56,0.19-1.12,0.75-1.27 c1.38-0.35,2.35-1.6,2.35-3.03s-0.97-2.67-2.35-3.03c-0.56-0.14-0.9-0.71-0.75-1.27C15.8,0.44,16.37,0.11,16.93,0.25z M9.38,0.08 c2.88,0,5.21,2.33,5.21,5.21s-2.33,5.21-5.21,5.21S4.17,8.17,4.17,5.29C4.17,2.42,6.5,0.08,9.38,0.08z M21.09,12.75 c2.22,0.57,3.8,2.53,3.9,4.81L25,17.79v2.08c0,0.58-0.47,1.04-1.04,1.04c-0.54,0-0.98-0.41-1.04-0.93l-0.01-0.11v-2.08 c0-1.42-0.96-2.67-2.34-3.02c-0.56-0.14-0.89-0.71-0.75-1.27C19.97,12.94,20.54,12.61,21.09,12.75z M13.54,12.58 c2.8,0,5.09,2.21,5.2,4.99v0.22v2.08c0,0.58-0.47,1.04-1.04,1.04c-0.54,0-0.98-0.41-1.04-0.93l-0.01-0.11v-2.08 c0-1.67-1.3-3.03-2.95-3.12h-0.18H5.21c-1.67,0-3.03,1.3-3.12,2.95v0.18v2.08c0,0.58-0.47,1.04-1.04,1.04 c-0.54,0-0.98-0.41-1.04-0.93L0,19.88V17.8c0-2.8,2.21-5.09,4.99-5.2h0.22h8.33V12.58z', viewBox: '0 0 25 21', }, + [SessionIconType.Upload]: { + path: + 'M380.032,133.472l-112-128C264.992,2.016,260.608,0,256,0c-4.608,0-8.992,2.016-12.032,5.472l-112,128 c-4.128,4.736-5.152,11.424-2.528,17.152C132.032,156.32,137.728,160,144,160h64v208c0,8.832,7.168,16,16,16h64 c8.832,0,16-7.168,16-16V160h64c6.272,0,11.968-3.648,14.56-9.376C385.152,144.896,384.192,138.176,380.032,133.472z M432,352v96H80v-96H16v128c0,17.696,14.336,32,32,32h416c17.696,0,32-14.304,32-32V352H432z', + viewBox: '0 0 512 512', + }, [SessionIconType.Warning]: { path: 'M243.225,333.382c-13.6,0-25,11.4-25,25s11.4,25,25,25c13.1,0,25-11.4,24.4-24.4 C268.225,344.682,256.925,333.382,243.225,333.382z M474.625,421.982c15.7-27.1,15.8-59.4,0.2-86.4l-156.6-271.2c-15.5-27.3-43.5-43.5-74.9-43.5s-59.4,16.3-74.9,43.4 l-156.8,271.5c-15.6,27.3-15.5,59.8,0.3,86.9c15.6,26.8,43.5,42.9,74.7,42.9h312.8 C430.725,465.582,458.825,449.282,474.625,421.982z M440.625,402.382c-8.7,15-24.1,23.9-41.3,23.9h-312.8 c-17,0-32.3-8.7-40.8-23.4c-8.6-14.9-8.7-32.7-0.1-47.7l156.8-271.4c8.5-14.9,23.7-23.7,40.9-23.7c17.1,0,32.4,8.9,40.9,23.8 l156.7,271.4C449.325,369.882,449.225,387.482,440.625,402.382z M237.025,157.882c-11.9,3.4-19.3,14.2-19.3,27.3c0.6,7.9,1.1,15.9,1.7,23.8c1.7,30.1,3.4,59.6,5.1,89.7 c0.6,10.2,8.5,17.6,18.7,17.6c10.2,0,18.2-7.9,18.7-18.2c0-6.2,0-11.9,0.6-18.2c1.1-19.3,2.3-38.6,3.4-57.9 c0.6-12.5,1.7-25,2.3-37.5c0-4.5-0.6-8.5-2.3-12.5C260.825,160.782,248.925,155.082,237.025,157.882z', From 228e4ca81e6a520f30c8489a6a307511874c3de2 Mon Sep 17 00:00:00 2001 From: Vincent Date: Fri, 20 Dec 2019 18:24:21 +1100 Subject: [PATCH 6/8] Adding session settings to utilise toggles --- background.html | 2 + js/background.js | 11 ++ js/modules/signal.js | 2 + js/views/device_pairing_dialog_view.js | 183 +++++++++++++++++- js/views/device_pairing_dialog_view_old.js | 209 --------------------- js/views/session_settings_view.js | 31 +++ stylesheets/_session.scss | 6 + ts/components/session/SessionSettings.tsx | 28 +++ 8 files changed, 257 insertions(+), 215 deletions(-) delete mode 100644 js/views/device_pairing_dialog_view_old.js create mode 100644 js/views/session_settings_view.js create mode 100644 ts/components/session/SessionSettings.tsx diff --git a/background.html b/background.html index 0a03d5540..f3a7bc28c 100644 --- a/background.html +++ b/background.html @@ -60,6 +60,7 @@
    +
    @@ -700,6 +701,7 @@ + diff --git a/js/background.js b/js/background.js index 51e943424..a826bdd99 100644 --- a/js/background.js +++ b/js/background.js @@ -802,6 +802,17 @@ appView.openConversation(groupId, {}); }; + + + $(document).ready(() => { + window.settingsView = new Whisper.SessionSettingsView({ + el: $('#settings-container'), + }); + window.settingsView.render(); + }); + + + window.generateID = () => Math.random() .toString(36) diff --git a/js/modules/signal.js b/js/modules/signal.js index 0d5617e19..960f3a64c 100644 --- a/js/modules/signal.js +++ b/js/modules/signal.js @@ -51,6 +51,7 @@ const { const { EditProfileDialog } = require('../../ts/components/EditProfileDialog'); const { UserDetailsDialog } = require('../../ts/components/UserDetailsDialog'); const { DevicePairingDialog } = require('../../ts/components/DevicePairingDialog'); +const { SessionSettings } = require('../../ts/components/session/SessionSettings'); const { SessionToast } = require('../../ts/components/session/SessionToast'); const { SessionToggle } = require('../../ts/components/session/SessionToggle'); const { SessionModal } = require('../../ts/components/session/SessionModal'); @@ -256,6 +257,7 @@ exports.setup = (options = {}) => { InviteFriendsDialog, GroupInvitation, BulkEdit, + SessionSettings, SessionToast, SessionToggle, SessionModal, diff --git a/js/views/device_pairing_dialog_view.js b/js/views/device_pairing_dialog_view.js index 32d44eed2..80d6b4f61 100644 --- a/js/views/device_pairing_dialog_view.js +++ b/js/views/device_pairing_dialog_view.js @@ -27,12 +27,183 @@ }); this.qr.makeCode(textsecure.storage.user.getNumber()); }, - - render() { - this.dialogView = new Whisper.ReactWrapperView({ - className: 'device-pairing-dialog', - Component: window.Signal.Components.DevicePairingDialog, - }); + reset() { + this.pubKey = null; + this.accepted = false; + this.isListening = false; + this.pubKeyToUnpair = null; + this.success = false; + }, + events: { + 'click #startPairing': 'startReceivingRequests', + 'click #close': 'close', + 'click .waitingForRequestView .cancel': 'stopReceivingRequests', + 'click .requestReceivedView .skip': 'skipDevice', + 'click #allowPairing': 'allowDevice', + 'click .requestAcceptedView .ok': 'stopReceivingRequests', + 'click .confirmUnpairView .cancel': 'stopReceivingRequests', + 'click .confirmUnpairView .unpairDevice': 'confirmUnpairDevice', + }, + render_attributes() { + return { + defaultTitle: i18n('pairedDevices'), + waitingForRequestTitle: i18n('waitingForDeviceToRegister'), + requestReceivedTitle: i18n('devicePairingReceived'), + requestAcceptedTitle: i18n('devicePairingAccepted'), + startPairingText: i18n('pairNewDevice'), + cancelText: i18n('cancel'), + unpairDevice: i18n('unpairDevice'), + closeText: i18n('close'), + skipText: i18n('skip'), + okText: i18n('ok'), + allowPairingText: i18n('allowPairing'), + confirmUnpairViewTitle: i18n('confirmUnpairingTitle'), + }; + }, + startReceivingRequests() { + this.trigger('startReceivingRequests'); + this.isListening = true; + this.showView(); + }, + stopReceivingRequests() { + if (this.success) { + const deviceAlias = this.$('#deviceAlias')[0].value.trim(); + const conv = ConversationController.get(this.pubKey); + if (conv) { + conv.setNickname(deviceAlias); + } + } + this.trigger('stopReceivingRequests'); + this.reset(); + this.showView(); + }, + requestReceived(secondaryDevicePubKey) { + // FIFO: push at the front of the array with unshift() + this.pubKeyRequests.unshift(secondaryDevicePubKey); + if (!this.pubKey) { + this.nextPubKey(); + this.showView('requestReceived'); + } + }, + allowDevice() { + this.accepted = true; + this.trigger('devicePairingRequestAccepted', this.pubKey, errors => + this.transmisssionCB(errors) + ); + this.showView(); + }, + transmisssionCB(errors) { + 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.success = true; + } else { + this.$('.transmissionStatus').text(errors); + this.$('.requestAcceptedView .ok').show(); + } + }, + skipDevice() { + this.trigger('devicePairingRequestRejected', this.pubKey); + this.nextPubKey(); + this.showView(); + }, + nextPubKey() { + // FIFO: pop at the back of the array using pop() + this.pubKey = this.pubKeyRequests.pop(); + }, + async confirmUnpairDevice() { + this.trigger('deviceUnpairingRequested', this.pubKeyToUnpair); + this.reset(); + this.showView(); + }, + requestUnpairDevice(pubKey) { + this.pubKeyToUnpair = pubKey; + this.showView(); + }, + getPubkeyName(pubKey) { + const secretWords = window.mnemonic.pubkey_to_secret_words(pubKey); + const conv = ConversationController.get(pubKey); + const deviceAlias = conv ? conv.getNickname() : 'Unnamed Device'; + return `${deviceAlias} (pairing secret: ${secretWords})`; + }, + async showView() { + const defaultView = this.$('.defaultView'); + const waitingForRequestView = this.$('.waitingForRequestView'); + const requestReceivedView = this.$('.requestReceivedView'); + const requestAcceptedView = this.$('.requestAcceptedView'); + const confirmUnpairView = this.$('.confirmUnpairView'); + if (this.pubKeyToUnpair) { + defaultView.hide(); + requestReceivedView.hide(); + waitingForRequestView.hide(); + requestAcceptedView.hide(); + confirmUnpairView.show(); + const name = this.getPubkeyName(this.pubKeyToUnpair); + this.$('.confirmUnpairView #pubkey').html(name); + } else if (!this.isListening) { + requestReceivedView.hide(); + waitingForRequestView.hide(); + requestAcceptedView.hide(); + confirmUnpairView.hide(); + + const ourPubKey = textsecure.storage.user.getNumber(); + defaultView.show(); + const pubKeys = await libloki.storage.getSecondaryDevicesFor(ourPubKey); + this.$('#pairedPubKeys').empty(); + if (pubKeys && pubKeys.length > 0) { + this.$('#startPairing').attr('disabled', true); + pubKeys.forEach(x => { + const name = this.getPubkeyName(x); + const li = $('
  • ').html(name); + if (window.lokiFeatureFlags.multiDeviceUnpairing) { + const link = $('') + .text('Unpair') + .attr('href', '#'); + link.on('click', () => this.requestUnpairDevice(x)); + li.append(' - '); + li.append(link); + } + this.$('#pairedPubKeys').append(li); + }); + } else { + this.$('#startPairing').removeAttr('disabled'); + this.$('#pairedPubKeys').append('
  • No paired devices
  • '); + } + } else if (this.accepted) { + defaultView.hide(); + requestReceivedView.hide(); + waitingForRequestView.hide(); + requestAcceptedView.show(); + } else if (this.pubKey) { + const secretWords = window.mnemonic.pubkey_to_secret_words(this.pubKey); + this.$('.secretWords').text(secretWords); + requestReceivedView.show(); + waitingForRequestView.hide(); + requestAcceptedView.hide(); + defaultView.hide(); + } else { + waitingForRequestView.show(); + requestReceivedView.hide(); + requestAcceptedView.hide(); + defaultView.hide(); + } + }, + close() { + this.remove(); + this.qr.clear(); + if (this.pubKey && !this.accepted) { + this.trigger('devicePairingRequestRejected', this.pubKey); + } + this.trigger('close'); }, }); })(); diff --git a/js/views/device_pairing_dialog_view_old.js b/js/views/device_pairing_dialog_view_old.js deleted file mode 100644 index 80d6b4f61..000000000 --- a/js/views/device_pairing_dialog_view_old.js +++ /dev/null @@ -1,209 +0,0 @@ -/* global - Whisper, - i18n, - libloki, - textsecure, - ConversationController, - $, - QRCode, -*/ - -// eslint-disable-next-line func-names -(function() { - 'use strict'; - - window.Whisper = window.Whisper || {}; - - Whisper.DevicePairingDialogView = Whisper.View.extend({ - className: 'loki-dialog device-pairing-dialog modal', - templateName: 'device-pairing-dialog', - initialize() { - this.pubKeyRequests = []; - this.reset(); - this.render(); - this.showView(); - this.qr = new QRCode(this.$('#qr')[0], { - correctLevel: QRCode.CorrectLevel.L, - }); - this.qr.makeCode(textsecure.storage.user.getNumber()); - }, - reset() { - this.pubKey = null; - this.accepted = false; - this.isListening = false; - this.pubKeyToUnpair = null; - this.success = false; - }, - events: { - 'click #startPairing': 'startReceivingRequests', - 'click #close': 'close', - 'click .waitingForRequestView .cancel': 'stopReceivingRequests', - 'click .requestReceivedView .skip': 'skipDevice', - 'click #allowPairing': 'allowDevice', - 'click .requestAcceptedView .ok': 'stopReceivingRequests', - 'click .confirmUnpairView .cancel': 'stopReceivingRequests', - 'click .confirmUnpairView .unpairDevice': 'confirmUnpairDevice', - }, - render_attributes() { - return { - defaultTitle: i18n('pairedDevices'), - waitingForRequestTitle: i18n('waitingForDeviceToRegister'), - requestReceivedTitle: i18n('devicePairingReceived'), - requestAcceptedTitle: i18n('devicePairingAccepted'), - startPairingText: i18n('pairNewDevice'), - cancelText: i18n('cancel'), - unpairDevice: i18n('unpairDevice'), - closeText: i18n('close'), - skipText: i18n('skip'), - okText: i18n('ok'), - allowPairingText: i18n('allowPairing'), - confirmUnpairViewTitle: i18n('confirmUnpairingTitle'), - }; - }, - startReceivingRequests() { - this.trigger('startReceivingRequests'); - this.isListening = true; - this.showView(); - }, - stopReceivingRequests() { - if (this.success) { - const deviceAlias = this.$('#deviceAlias')[0].value.trim(); - const conv = ConversationController.get(this.pubKey); - if (conv) { - conv.setNickname(deviceAlias); - } - } - this.trigger('stopReceivingRequests'); - this.reset(); - this.showView(); - }, - requestReceived(secondaryDevicePubKey) { - // FIFO: push at the front of the array with unshift() - this.pubKeyRequests.unshift(secondaryDevicePubKey); - if (!this.pubKey) { - this.nextPubKey(); - this.showView('requestReceived'); - } - }, - allowDevice() { - this.accepted = true; - this.trigger('devicePairingRequestAccepted', this.pubKey, errors => - this.transmisssionCB(errors) - ); - this.showView(); - }, - transmisssionCB(errors) { - 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.success = true; - } else { - this.$('.transmissionStatus').text(errors); - this.$('.requestAcceptedView .ok').show(); - } - }, - skipDevice() { - this.trigger('devicePairingRequestRejected', this.pubKey); - this.nextPubKey(); - this.showView(); - }, - nextPubKey() { - // FIFO: pop at the back of the array using pop() - this.pubKey = this.pubKeyRequests.pop(); - }, - async confirmUnpairDevice() { - this.trigger('deviceUnpairingRequested', this.pubKeyToUnpair); - this.reset(); - this.showView(); - }, - requestUnpairDevice(pubKey) { - this.pubKeyToUnpair = pubKey; - this.showView(); - }, - getPubkeyName(pubKey) { - const secretWords = window.mnemonic.pubkey_to_secret_words(pubKey); - const conv = ConversationController.get(pubKey); - const deviceAlias = conv ? conv.getNickname() : 'Unnamed Device'; - return `${deviceAlias} (pairing secret: ${secretWords})`; - }, - async showView() { - const defaultView = this.$('.defaultView'); - const waitingForRequestView = this.$('.waitingForRequestView'); - const requestReceivedView = this.$('.requestReceivedView'); - const requestAcceptedView = this.$('.requestAcceptedView'); - const confirmUnpairView = this.$('.confirmUnpairView'); - if (this.pubKeyToUnpair) { - defaultView.hide(); - requestReceivedView.hide(); - waitingForRequestView.hide(); - requestAcceptedView.hide(); - confirmUnpairView.show(); - const name = this.getPubkeyName(this.pubKeyToUnpair); - this.$('.confirmUnpairView #pubkey').html(name); - } else if (!this.isListening) { - requestReceivedView.hide(); - waitingForRequestView.hide(); - requestAcceptedView.hide(); - confirmUnpairView.hide(); - - const ourPubKey = textsecure.storage.user.getNumber(); - defaultView.show(); - const pubKeys = await libloki.storage.getSecondaryDevicesFor(ourPubKey); - this.$('#pairedPubKeys').empty(); - if (pubKeys && pubKeys.length > 0) { - this.$('#startPairing').attr('disabled', true); - pubKeys.forEach(x => { - const name = this.getPubkeyName(x); - const li = $('
  • ').html(name); - if (window.lokiFeatureFlags.multiDeviceUnpairing) { - const link = $('') - .text('Unpair') - .attr('href', '#'); - link.on('click', () => this.requestUnpairDevice(x)); - li.append(' - '); - li.append(link); - } - this.$('#pairedPubKeys').append(li); - }); - } else { - this.$('#startPairing').removeAttr('disabled'); - this.$('#pairedPubKeys').append('
  • No paired devices
  • '); - } - } else if (this.accepted) { - defaultView.hide(); - requestReceivedView.hide(); - waitingForRequestView.hide(); - requestAcceptedView.show(); - } else if (this.pubKey) { - const secretWords = window.mnemonic.pubkey_to_secret_words(this.pubKey); - this.$('.secretWords').text(secretWords); - requestReceivedView.show(); - waitingForRequestView.hide(); - requestAcceptedView.hide(); - defaultView.hide(); - } else { - waitingForRequestView.show(); - requestReceivedView.hide(); - requestAcceptedView.hide(); - defaultView.hide(); - } - }, - close() { - this.remove(); - this.qr.clear(); - if (this.pubKey && !this.accepted) { - this.trigger('devicePairingRequestRejected', this.pubKey); - } - this.trigger('close'); - }, - }); -})(); diff --git a/js/views/session_settings_view.js b/js/views/session_settings_view.js new file mode 100644 index 000000000..1a566c990 --- /dev/null +++ b/js/views/session_settings_view.js @@ -0,0 +1,31 @@ +/* global i18n, Whisper */ + +// eslint-disable-next-line func-names +(function() { + 'use strict'; + + window.Whisper = window.Whisper || {}; + + Whisper.SessionSettingsView = Whisper.View.extend({ + initialize(options) { + console.log(options); + + this.render(); + }, + render() { + this.settingsView = new Whisper.ReactWrapperView({ + className: 'session-settings', + Component: window.Signal.Components.SessionSettings, + props: { + i18n, + }, + }); + + this.$el.append(this.settingsView.el); + }, + close() { + this.remove(); + }, + }); +})(); + diff --git a/stylesheets/_session.scss b/stylesheets/_session.scss index e317f4b41..0740db986 100644 --- a/stylesheets/_session.scss +++ b/stylesheets/_session.scss @@ -686,4 +686,10 @@ label { position: absolute; margin-top: 50px; margin-left: 75px; +} + +#settings-container { + width: 100%; + height: 100%; + background-color: blue; } \ No newline at end of file diff --git a/ts/components/session/SessionSettings.tsx b/ts/components/session/SessionSettings.tsx new file mode 100644 index 000000000..0f70cd4d7 --- /dev/null +++ b/ts/components/session/SessionSettings.tsx @@ -0,0 +1,28 @@ +import React from 'react'; + + +interface Props { + i18n: any; +} + +export class SessionSettings extends React.Component { + constructor(props: any) { + super(props); + } + + public render() { + const i18n = this.props.i18n; + + return ( +
    + gakslfadsnkjnbtbnl Lorem ipsum dolor sit amet consectetur, adipisicing elit. Earum sapiente adipisci molestias iusto aut harum vel, voluptas illo laboriosam, doloremque alias, ducimus tenetur ad! Saepe inventore at quas nulla ab! + gakslfadsnkjnbtbnl Lorem ipsum dolor sit amet consectetur, adipisicing elit. Earum sapiente adipisci molestias iusto aut harum vel, voluptas illo laboriosam, doloremque alias, ducimus tenetur ad! Saepe inventore at quas nulla ab! + gakslfadsnkjnbtbnl Lorem ipsum dolor sit amet consectetur, adipisicing elit. Earum sapiente adipisci molestias iusto aut harum vel, voluptas illo laboriosam, doloremque alias, ducimus tenetur ad! Saepe inventore at quas nulla ab! + gakslfadsnkjnbtbnl Lorem ipsum dolor sit amet consectetur, adipisicing elit. Earum sapiente adipisci molestias iusto aut harum vel, voluptas illo laboriosam, doloremque alias, ducimus tenetur ad! Saepe inventore at quas nulla ab! + gakslfadsnkjnbtbnl Lorem ipsum dolor sit amet consectetur, adipisicing elit. Earum sapiente adipisci molestias iusto aut harum vel, voluptas illo laboriosam, doloremque alias, ducimus tenetur ad! Saepe inventore at quas nulla ab! + gakslfadsnkjnbtbnl Lorem ipsum dolor sit amet consectetur, adipisicing elit. Earum sapiente adipisci molestias iusto aut harum vel, voluptas illo laboriosam, doloremque alias, ducimus tenetur ad! Saepe inventore at quas nulla ab! +
    + ); + } + +} From 241927260809ef8607fe7a0e63efbf808dfe2ab0 Mon Sep 17 00:00:00 2001 From: Vincent Date: Mon, 23 Dec 2019 09:44:18 +1100 Subject: [PATCH 7/8] Updated React dialogs to SessionModals --- _locales/en/messages.json | 2 - background.html | 2 - js/background.js | 16 +++--- js/modules/signal.js | 6 +-- js/views/session_settings_view.js | 47 ++++++++-------- stylesheets/_session.scss | 16 ++---- ts/components/ConfirmDialog.tsx | 24 +++++---- ts/components/DevicePairingDialog.tsx | 0 ts/components/EditProfileDialog.tsx | 39 +++++++------- ts/components/UserDetailsDialog.tsx | 16 +++--- .../conversation/CreateGroupDialog.tsx | 7 +-- .../conversation/UpdateGroupDialog.tsx | 11 ++-- ts/components/session/SessionModal.tsx | 53 +++++++++---------- ts/components/session/SessionSettings.tsx | 15 +----- 14 files changed, 120 insertions(+), 134 deletions(-) delete mode 100644 ts/components/DevicePairingDialog.tsx diff --git a/_locales/en/messages.json b/_locales/en/messages.json index 4ebee76d9..dc6d4183d 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -2260,13 +2260,11 @@ "message": "Editing Profile" }, - "editProfileModalTitle": { "message": "Edit Profile", "description": "Title for the Edit Profile modal" }, - "profileName": { "message": "Profile Name" }, diff --git a/background.html b/background.html index f3a7bc28c..0a03d5540 100644 --- a/background.html +++ b/background.html @@ -60,7 +60,6 @@
    -
    @@ -701,7 +700,6 @@ - diff --git a/js/background.js b/js/background.js index a826bdd99..94d98147b 100644 --- a/js/background.js +++ b/js/background.js @@ -802,17 +802,13 @@ appView.openConversation(groupId, {}); }; + // $(document).ready(() => { + // window.settingsView = new Whisper.SessionSettingsView({ + // el: $('#settings-container'), + // }); + // window.settingsView.render(); + // }); - - $(document).ready(() => { - window.settingsView = new Whisper.SessionSettingsView({ - el: $('#settings-container'), - }); - window.settingsView.render(); - }); - - - window.generateID = () => Math.random() .toString(36) diff --git a/js/modules/signal.js b/js/modules/signal.js index 960f3a64c..f13542db1 100644 --- a/js/modules/signal.js +++ b/js/modules/signal.js @@ -50,8 +50,9 @@ 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 } = require('../../ts/components/session/SessionSettings'); +const { + SessionSettings, +} = require('../../ts/components/session/SessionSettings'); const { SessionToast } = require('../../ts/components/session/SessionToast'); const { SessionToggle } = require('../../ts/components/session/SessionToggle'); const { SessionModal } = require('../../ts/components/session/SessionModal'); @@ -250,7 +251,6 @@ exports.setup = (options = {}) => { CreateGroupDialog, EditProfileDialog, UserDetailsDialog, - DevicePairingDialog, SessionRegistrationView, ConfirmDialog, UpdateGroupDialog, diff --git a/js/views/session_settings_view.js b/js/views/session_settings_view.js index 1a566c990..4d5dac9b2 100644 --- a/js/views/session_settings_view.js +++ b/js/views/session_settings_view.js @@ -2,30 +2,27 @@ // eslint-disable-next-line func-names (function() { - 'use strict'; - - window.Whisper = window.Whisper || {}; - - Whisper.SessionSettingsView = Whisper.View.extend({ - initialize(options) { - console.log(options); + 'use strict'; - this.render(); - }, - render() { - this.settingsView = new Whisper.ReactWrapperView({ - className: 'session-settings', - Component: window.Signal.Components.SessionSettings, - props: { - i18n, - }, - }); - - this.$el.append(this.settingsView.el); - }, - close() { - this.remove(); - }, - }); -})(); + window.Whisper = window.Whisper || {}; + + Whisper.SessionSettingsView = Whisper.View.extend({ + initialize() { + this.render(); + }, + render() { + this.settingsView = new Whisper.ReactWrapperView({ + className: 'session-settings', + Component: window.Signal.Components.SessionSettings, + props: { + i18n, + }, + }); + this.$el.append(this.settingsView.el); + }, + close() { + this.remove(); + }, + }); +})(); diff --git a/stylesheets/_session.scss b/stylesheets/_session.scss index 0740db986..76bdcc0f2 100644 --- a/stylesheets/_session.scss +++ b/stylesheets/_session.scss @@ -529,7 +529,8 @@ label { font-size: 15px; font-weight: 700; - &__icons, &__close { + &__icons, + &__close { width: 60px; } &__icons { @@ -551,7 +552,7 @@ label { line-height: 16px; font-size: 13px; - .message{ + .message { text-align: center; } } @@ -560,14 +561,13 @@ label { display: flex; justify-content: flex-end; - &__center{ + &__center { align-items: center; } .session-button { margin-left: $session-margin-sm; } } - } .session-toggle { @@ -680,16 +680,8 @@ label { } } - - .edit-profile-dialog .image-upload-section { position: absolute; margin-top: 50px; margin-left: 75px; } - -#settings-container { - width: 100%; - height: 100%; - background-color: blue; -} \ No newline at end of file diff --git a/ts/components/ConfirmDialog.tsx b/ts/components/ConfirmDialog.tsx index ef02efaf0..77f902680 100644 --- a/ts/components/ConfirmDialog.tsx +++ b/ts/components/ConfirmDialog.tsx @@ -19,20 +19,24 @@ export class ConfirmDialog extends React.Component { public render() { return ( - null} onOk={() => null}> -
    + null} + onOk={() => null} + > +

    {this.props.messageText}

    -
    +
    - - -
    diff --git a/ts/components/DevicePairingDialog.tsx b/ts/components/DevicePairingDialog.tsx deleted file mode 100644 index e69de29bb..000000000 diff --git a/ts/components/EditProfileDialog.tsx b/ts/components/EditProfileDialog.tsx index 438d937a9..671dab958 100644 --- a/ts/components/EditProfileDialog.tsx +++ b/ts/components/EditProfileDialog.tsx @@ -2,10 +2,13 @@ import React from 'react'; import classNames from 'classnames'; import { Avatar } from './Avatar'; -import { SessionModal } from './session/SessionModal'; import { SessionButton, SessionButtonColor } from './session/SessionButton'; -import { SessionIconButton, SessionIconType, SessionIconSize } from './session/icon'; - +import { + SessionIconButton, + SessionIconSize, + SessionIconType, +} from './session/icon'; +import { SessionModal } from './session/SessionModal'; declare global { interface Window { @@ -85,23 +88,23 @@ export class EditProfileDialog extends React.Component { name="name" onChange={this.onFileSelected} /> - - { - const el = this.inputEl.current; - if (el) { - el.click(); - } - }} - /> + + { + const el = this.inputEl.current; + if (el) { + el.click(); + } + }} + />
    -
    - +
    + { />
    {i18n('editProfileDisplayNameWarning')}
    {this.state.errorMessage} - -
    + +
    { public render() { const i18n = this.props.i18n; - + return ( {
    diff --git a/ts/components/conversation/CreateGroupDialog.tsx b/ts/components/conversation/CreateGroupDialog.tsx index 943ce22cc..6dd617bb3 100644 --- a/ts/components/conversation/CreateGroupDialog.tsx +++ b/ts/components/conversation/CreateGroupDialog.tsx @@ -2,6 +2,8 @@ import React from 'react'; import classNames from 'classnames'; import { Contact, MemberList } from './MemberList'; +import { SessionModal } from './../session/SessionModal'; + declare global { interface Window { Lodash: any; @@ -92,8 +94,7 @@ export class CreateGroupDialog extends React.Component { ); return ( -
    -

    {titleText}

    + null} onOk={() => null}>

    {this.state.errorMessage}

    { {okText}
    -
    + ); } diff --git a/ts/components/conversation/UpdateGroupDialog.tsx b/ts/components/conversation/UpdateGroupDialog.tsx index 0e0d2710b..15509adf8 100644 --- a/ts/components/conversation/UpdateGroupDialog.tsx +++ b/ts/components/conversation/UpdateGroupDialog.tsx @@ -2,6 +2,8 @@ import React from 'react'; import classNames from 'classnames'; import { Contact, MemberList } from './MemberList'; +import { SessionModal } from './../session/SessionModal'; + declare global { interface Window { SMALL_GROUP_SIZE_LIMIT: number; @@ -105,9 +107,11 @@ export class UpdateGroupDialog extends React.Component { ); return ( -
    -

    {titleText}

    + null} onOk={() => null}> +

    {errorMsg}

    +
    + { aria-required={true} autoFocus={true} /> +
    { {okText}
    -
    + ); } diff --git a/ts/components/session/SessionModal.tsx b/ts/components/session/SessionModal.tsx index 9217568b3..9977046d7 100644 --- a/ts/components/session/SessionModal.tsx +++ b/ts/components/session/SessionModal.tsx @@ -19,7 +19,6 @@ interface State { } export class SessionModal extends React.PureComponent { - constructor(props: any) { super(props); this.state = { @@ -37,34 +36,34 @@ export class SessionModal extends React.PureComponent { const { isVisible } = this.state; return isVisible ? ( -
    -
    -
    - -
    -
    {title}
    -
    - { headerIconButtons ? headerIconButtons.map((iconItem: any) => { - return ( - - ) - }) : null - } -
    +
    +
    +
    +
    - -
    - {this.props.children} +
    {title}
    +
    + {headerIconButtons + ? headerIconButtons.map((iconItem: any) => { + return ( + + ); + }) + : null}
    - ) : null; + +
    {this.props.children}
    +
    + ) : null; } public close() { @@ -76,7 +75,7 @@ export class SessionModal extends React.PureComponent { this.props.onClose(); } - public onKeyUp(event: any){ + public onKeyUp(event: any) { switch (event.key) { case 'Enter': this.props.onOk(); diff --git a/ts/components/session/SessionSettings.tsx b/ts/components/session/SessionSettings.tsx index 0f70cd4d7..eacd69b4c 100644 --- a/ts/components/session/SessionSettings.tsx +++ b/ts/components/session/SessionSettings.tsx @@ -1,6 +1,5 @@ import React from 'react'; - interface Props { i18n: any; } @@ -12,17 +11,7 @@ export class SessionSettings extends React.Component { public render() { const i18n = this.props.i18n; - - return ( -
    - gakslfadsnkjnbtbnl Lorem ipsum dolor sit amet consectetur, adipisicing elit. Earum sapiente adipisci molestias iusto aut harum vel, voluptas illo laboriosam, doloremque alias, ducimus tenetur ad! Saepe inventore at quas nulla ab! - gakslfadsnkjnbtbnl Lorem ipsum dolor sit amet consectetur, adipisicing elit. Earum sapiente adipisci molestias iusto aut harum vel, voluptas illo laboriosam, doloremque alias, ducimus tenetur ad! Saepe inventore at quas nulla ab! - gakslfadsnkjnbtbnl Lorem ipsum dolor sit amet consectetur, adipisicing elit. Earum sapiente adipisci molestias iusto aut harum vel, voluptas illo laboriosam, doloremque alias, ducimus tenetur ad! Saepe inventore at quas nulla ab! - gakslfadsnkjnbtbnl Lorem ipsum dolor sit amet consectetur, adipisicing elit. Earum sapiente adipisci molestias iusto aut harum vel, voluptas illo laboriosam, doloremque alias, ducimus tenetur ad! Saepe inventore at quas nulla ab! - gakslfadsnkjnbtbnl Lorem ipsum dolor sit amet consectetur, adipisicing elit. Earum sapiente adipisci molestias iusto aut harum vel, voluptas illo laboriosam, doloremque alias, ducimus tenetur ad! Saepe inventore at quas nulla ab! - gakslfadsnkjnbtbnl Lorem ipsum dolor sit amet consectetur, adipisicing elit. Earum sapiente adipisci molestias iusto aut harum vel, voluptas illo laboriosam, doloremque alias, ducimus tenetur ad! Saepe inventore at quas nulla ab! -
    - ); - } + return
    ; + } } From ff7dbd65625fce19cfeafe8c99f1ad1dcb89bad8 Mon Sep 17 00:00:00 2001 From: Vincent Date: Mon, 23 Dec 2019 11:58:03 +1100 Subject: [PATCH 8/8] Globalised consts --- stylesheets/_session.scss | 5 +++++ ts/components/ConversationListItem.tsx | 1 - ts/components/UserDetailsDialog.tsx | 6 ------ ts/components/session/SessionDropdownItem.tsx | 5 ++--- ts/components/session/SessionToggle.tsx | 10 ++++++++-- ts/components/session/tools/ComponentTools.tsx | 8 -------- ts/global.d.ts | 1 + 7 files changed, 16 insertions(+), 20 deletions(-) delete mode 100644 ts/components/session/tools/ComponentTools.tsx diff --git a/stylesheets/_session.scss b/stylesheets/_session.scss index 76bdcc0f2..5628d0c6f 100644 --- a/stylesheets/_session.scss +++ b/stylesheets/_session.scss @@ -631,6 +631,11 @@ label { &--selected { background-color: $session-shade-7 !important; } + &:active, + &:visited, + &:focus { + outline: none; + } } .session-dropdown { diff --git a/ts/components/ConversationListItem.tsx b/ts/components/ConversationListItem.tsx index 8d33a6f1a..d26d1fe08 100644 --- a/ts/components/ConversationListItem.tsx +++ b/ts/components/ConversationListItem.tsx @@ -1,5 +1,4 @@ import React from 'react'; -import ReactDOM from 'react-dom'; import classNames from 'classnames'; import { isEmpty } from 'lodash'; import { ContextMenu, ContextMenuTrigger, MenuItem } from 'react-contextmenu'; diff --git a/ts/components/UserDetailsDialog.tsx b/ts/components/UserDetailsDialog.tsx index 0f06304b0..99ca613bb 100644 --- a/ts/components/UserDetailsDialog.tsx +++ b/ts/components/UserDetailsDialog.tsx @@ -8,12 +8,6 @@ import { SessionButtonType, } from './session/SessionButton'; -declare global { - interface Window { - displayNameRegex: any; - } -} - interface Props { i18n: any; profileName: string; diff --git a/ts/components/session/SessionDropdownItem.tsx b/ts/components/session/SessionDropdownItem.tsx index 2647339b2..2ea9cde59 100644 --- a/ts/components/session/SessionDropdownItem.tsx +++ b/ts/components/session/SessionDropdownItem.tsx @@ -2,7 +2,6 @@ import React from 'react'; import classNames from 'classnames'; import { SessionIcon, SessionIconSize, SessionIconType } from './icon/'; -import { generateID } from './tools/ComponentTools'; export enum SessionDropDownItemType { Default = 'default', @@ -20,7 +19,6 @@ interface Props { export class SessionDropdownItem extends React.PureComponent { public static defaultProps = { - id: generateID(), type: SessionDropDownItemType.Default, icon: null, active: false, @@ -33,7 +31,8 @@ export class SessionDropdownItem extends React.PureComponent { } public render() { - const { id, content, type, icon, active } = this.props; + const { content, type, icon, active } = this.props; + const id = this.props.id || window.generateID(); return (
  • { - public static readonly extendedDefaults = { + public static defaultProps = { onClick: () => null, }; @@ -40,9 +41,14 @@ export class SessionToggle extends React.PureComponent { ); } - private clickHandler() { + private clickHandler(e: any) { this.setState({ active: !this.state.active, }); + + if (this.props.onClick) { + e.stopPropagation(); + this.props.onClick(); + } } } diff --git a/ts/components/session/tools/ComponentTools.tsx b/ts/components/session/tools/ComponentTools.tsx deleted file mode 100644 index e91b8a295..000000000 --- a/ts/components/session/tools/ComponentTools.tsx +++ /dev/null @@ -1,8 +0,0 @@ -// This is a collection of tools which can be ued to simplify the esign and rendering process of your components. - -export function generateID() { - // Generates a unique ID for your component - const buffer = new Uint32Array(10); - - return window.crypto.getRandomValues(buffer)[0].toString(); -} diff --git a/ts/global.d.ts b/ts/global.d.ts index 5817c1f87..3a85cc274 100644 --- a/ts/global.d.ts +++ b/ts/global.d.ts @@ -12,6 +12,7 @@ interface Window { textsecure: any; Session: any; i18n: any; + generateID: any; } interface Promise {