diff --git a/js/views/contact_list_view.js b/js/views/contact_list_view.js index 1065988136..d609124abf 100644 --- a/js/views/contact_list_view.js +++ b/js/views/contact_list_view.js @@ -16,6 +16,7 @@ initialize(options) { this.ourNumber = textsecure.storage.user.getNumber(); this.listenBack = options.listenBack; + this.loading = false; this.listenTo(this.model, 'change', this.render); }, @@ -39,19 +40,28 @@ profileName: this.model.getProfileName(), verified: this.model.isVerified(), onClick: this.showIdentity.bind(this), + disabled: this.loading, }, }); this.$el.append(this.contactView.el); return this; }, showIdentity() { - if (this.model.id === this.ourNumber) { + if (this.model.id === this.ourNumber || this.loading) { return; } + + this.loading = true; + this.render(); + const view = new Whisper.KeyVerificationPanelView({ model: this.model, + onLoad: () => { + this.loading = false; + this.listenBack(view); + this.render(); + }, }); - this.listenBack(view); }, }), }); diff --git a/js/views/conversation_view.js b/js/views/conversation_view.js index 948bd03361..37a706ecde 100644 --- a/js/views/conversation_view.js +++ b/js/views/conversation_view.js @@ -954,7 +954,7 @@ }; const view = new Whisper.ReactWrapperView({ - className: 'panel-wrapper', + className: 'panel', Component: Signal.Components.MediaGallery, props: await getProps(), onClose: () => { @@ -1537,7 +1537,7 @@ const props = message.getPropsForMessageDetail(); const view = new Whisper.ReactWrapperView({ - className: 'message-detail-wrapper', + className: 'panel message-detail-wrapper', Component: Signal.Components.MessageDetail, props, onClose, @@ -1597,11 +1597,11 @@ listenBack(view) { this.panels = this.panels || []; - if (this.panels.length > 0) { - this.panels[0].$el.hide(); - } this.panels.unshift(view); - view.$el.insertBefore(this.$('.panel').first()); + view.$el.insertAfter(this.$('.panel').last()); + view.$el.one('animationend', () => { + view.$el.addClass('panel--static'); + }); }, resetPanel() { if (!this.panels || !this.panels.length) { @@ -1611,14 +1611,15 @@ const view = this.panels.shift(); if (this.panels.length > 0) { - this.panels[0].$el.show(); - } - view.remove(); - - if (this.panels.length === 0) { - // Make sure poppers are positioned properly - window.dispatchEvent(new Event('resize')); + this.panels[0].$el.fadeIn(250); } + view.$el.addClass('panel--remove').one('transitionend', () => { + view.remove(); + if (this.panels.length === 0) { + // Make sure poppers are positioned properly + window.dispatchEvent(new Event('resize')); + } + }); }, endSession() { diff --git a/js/views/key_verification_view.js b/js/views/key_verification_view.js index 64a7e87fc6..665831ec96 100644 --- a/js/views/key_verification_view.js +++ b/js/views/key_verification_view.js @@ -22,6 +22,9 @@ this.loadKeys().then(() => { this.listenTo(this.model, 'change', this.render); + if (options.onLoad) { + options.onLoad(); + } }); }, loadKeys() { diff --git a/stylesheets/_conversation.scss b/stylesheets/_conversation.scss index 912d16d7ba..847e46680c 100644 --- a/stylesheets/_conversation.scss +++ b/stylesheets/_conversation.scss @@ -1,15 +1,56 @@ +@import './mixins'; + +@keyframes panel--in { + from { + transform: translateX(500px); + opacity: 0; + } + + to { + transform: translateX(0); + opacity: 1; + } +} + .conversation { background-color: $color-white; height: 100%; position: relative; - .panel, - .panel-wrapper { + .panel { height: calc(100% - #{$header-height}); overflow-y: scroll; + z-index: 1; + position: absolute; + left: 0; + top: 48px; + width: 100%; + height: calc(100% - 48px); + + @include light-theme() { + background-color: $color-white; + } + + @include dark-theme() { + background-color: $color-black; + } } .panel { + &:not(.main) { + animation: panel--in 250ms ease-out; + } + + &--static { + animation: none; + } + + &--remove { + transform: translateX(500px); + opacity: 0; + transition: all 250ms ease-out; + } + .container { padding-top: 20px; max-width: 750px; @@ -18,8 +59,7 @@ } } - .main.panel, - .panel-wrapper { + .main.panel { display: flex; flex-direction: column; overflow: initial; diff --git a/stylesheets/_modules.scss b/stylesheets/_modules.scss index 8adad359cf..7826b95338 100644 --- a/stylesheets/_modules.scss +++ b/stylesheets/_modules.scss @@ -1512,7 +1512,17 @@ height: 35px; min-width: 35px; vertical-align: text-bottom; - cursor: pointer; + border: none; + opacity: 0; + transition: opacity 250ms ease-out; + + &:disabled { + cursor: default; + } + + &--show { + opacity: 1; + } } .module-conversation-header__title-container { @@ -1595,7 +1605,17 @@ height: 20px; width: 20px; margin-left: 4px; - cursor: pointer; + border: none; + opacity: 0; + transition: opacity 250ms ease-out; + + &:disabled { + cursor: default; + } + + &--show { + opacity: 1; + } } // Module: Message Detail diff --git a/ts/components/conversation/ConversationHeader.tsx b/ts/components/conversation/ConversationHeader.tsx index 9b75793d56..7761bb2e4f 100644 --- a/ts/components/conversation/ConversationHeader.tsx +++ b/ts/components/conversation/ConversationHeader.tsx @@ -1,4 +1,5 @@ import React from 'react'; +import classnames from 'classnames'; import { Emojify } from './Emojify'; import { Avatar } from '../Avatar'; @@ -49,7 +50,7 @@ interface Props { } export class ConversationHeader extends React.Component { - public showMenuBound: (event: React.MouseEvent) => void; + public showMenuBound: (event: React.MouseEvent) => void; public menuTriggerRef: React.RefObject; public constructor(props: Props) { @@ -59,7 +60,7 @@ export class ConversationHeader extends React.Component { this.showMenuBound = this.showMenu.bind(this); } - public showMenu(event: React.MouseEvent) { + public showMenu(event: React.MouseEvent) { if (this.menuTriggerRef.current) { this.menuTriggerRef.current.handleContextClick(event); } @@ -68,15 +69,14 @@ export class ConversationHeader extends React.Component { public renderBackButton() { const { onGoBack, showBackButton } = this.props; - if (!showBackButton) { - return null; - } - return ( -
); } @@ -171,16 +171,17 @@ export class ConversationHeader extends React.Component { public renderGear(triggerId: string) { const { showBackButton } = this.props; - if (showBackButton) { - return null; - } - return ( -
);