diff --git a/js/models/conversations.js b/js/models/conversations.js index 5acb4bf74..8c7f9bbe2 100644 --- a/js/models/conversations.js +++ b/js/models/conversations.js @@ -567,6 +567,11 @@ ? `${message.get('source')}.${message.get('sourceDevice')}` : `${message.source}.${message.sourceDevice}`; this.clearContactTypingTimer(identifier); + + const model = this.addSingleMessage(message); + MessageController.register(model.id, model); + + this.trigger('change'); }, addSingleMessage(message, setToExpire = true) { const model = this.messageCollection.add(message, { merge: true }); diff --git a/js/models/messages.js b/js/models/messages.js index 15abc5ca2..2377bb67d 100644 --- a/js/models/messages.js +++ b/js/models/messages.js @@ -65,8 +65,6 @@ const generateProps = () => { if (this.isExpirationTimerUpdate()) { this.propsForTimerNotification = this.getPropsForTimerNotification(); - } else if (this.isKeyChange()) { - this.propsForSafetyNumberNotification = this.getPropsForSafetyNumberNotification(); } else if (this.isVerifiedChange()) { this.propsForVerificationNotification = this.getPropsForVerificationNotification(); } else if (this.isEndSession()) { @@ -344,19 +342,6 @@ return basicProps; }, - getPropsForSafetyNumberNotification() { - const conversation = this.getConversation(); - const isGroup = conversation && !conversation.isPrivate(); - const phoneNumber = this.get('key_changed'); - const onVerify = () => - this.trigger('show-identity', this.findContact(phoneNumber)); - - return { - isGroup, - contact: this.findAndFormatContact(phoneNumber), - onVerify, - }; - }, getPropsForVerificationNotification() { const type = this.get('verified') ? 'markVerified' : 'markNotVerified'; const isLocal = this.get('local'); diff --git a/js/modules/signal.js b/js/modules/signal.js index a992d2eee..25edd63bf 100644 --- a/js/modules/signal.js +++ b/js/modules/signal.js @@ -124,9 +124,6 @@ const { Quote } = require('../../ts/components/conversation/Quote'); const { ResetSessionNotification, } = require('../../ts/components/conversation/ResetSessionNotification'); -const { - SafetyNumberNotification, -} = require('../../ts/components/conversation/SafetyNumberNotification'); const { StagedLinkPreview, } = require('../../ts/components/conversation/StagedLinkPreview'); @@ -323,7 +320,6 @@ exports.setup = (options = {}) => { MessageDetail, Quote, ResetSessionNotification, - SafetyNumberNotification, StagedLinkPreview, TimerNotification, Types: { diff --git a/js/views/message_view.js b/js/views/message_view.js index 83e488348..cefb567dc 100644 --- a/js/views/message_view.js +++ b/js/views/message_view.js @@ -49,11 +49,6 @@ Component: Components.TimerNotification, props: this.model.propsForTimerNotification, }; - } else if (this.model.propsForSafetyNumberNotification) { - return { - Component: Components.SafetyNumberNotification, - props: this.model.propsForSafetyNumberNotification, - }; } else if (this.model.propsForVerificationNotification) { return { Component: Components.VerificationNotification, diff --git a/stylesheets/_session_conversation.scss b/stylesheets/_session_conversation.scss index 858ea41eb..8dc0810a4 100644 --- a/stylesheets/_session_conversation.scss +++ b/stylesheets/_session_conversation.scss @@ -40,6 +40,7 @@ $composition-container-height: 60px; .conversation-item { display: flex; + flex-direction: column; flex-grow: 1; height: 100%; outline: none; @@ -65,14 +66,6 @@ $composition-container-height: 60px; } } - &__content { - display: flex; - flex-direction: column; - height: 100%; - width: 100%; - outline: none; - } - &__options-pane { position: absolute; height: 100%; @@ -102,7 +95,7 @@ $composition-container-height: 60px; display: flex; left: 0px; right: 0px; - margin: 0px $session-margin-md; + padding: 0px $session-margin-md; align-items: center; justify-content: space-between; height: $main-view-header-height; @@ -138,15 +131,25 @@ $composition-container-height: 60px; flex-direction: column; position: relative; background-color: $session-background; + outline: none; .conversation-messages { display: flex; flex-direction: column; flex-grow: 1; - position: absolute; - height: 100%; width: 100%; + height: 0; background-color: inherit; + outline: none; + + &__blocking-overlay { + background-color: rgba(0, 0, 0, 0.8); + position: absolute; + top: 0px; + bottom: 0px; + left: 0px; + right: 0px; + } } .conversation-info-panel { @@ -167,22 +170,6 @@ $composition-container-height: 60px; } } -.messages-wrapper { - display: flex; - flex-grow: 1; - flex-direction: column; - position: relative; - height: 100%; - - &--blocking-overlay { - background-color: rgba(0, 0, 0, 0.8); - position: absolute; - top: 0px; - bottom: 0px; - left: 0px; - right: 0px; - } -} .messages-container { display: flex; flex-grow: 1; diff --git a/ts/components/conversation/ConversationHeader.tsx b/ts/components/conversation/ConversationHeader.tsx index 8d050d146..cb885290c 100644 --- a/ts/components/conversation/ConversationHeader.tsx +++ b/ts/components/conversation/ConversationHeader.tsx @@ -351,7 +351,6 @@ export class ConversationHeader extends React.Component { public renderSelectionOverlay() { const { onDeleteSelectedMessages, - onResetSession, onCloseOverlay, isPublic, i18n, @@ -367,7 +366,7 @@ export class ConversationHeader extends React.Component { @@ -394,17 +393,15 @@ export class ConversationHeader extends React.Component { {this.renderBackButton()}
- {this.renderOptions(triggerId)} + {!selectionMode && this.renderOptions(triggerId)} {this.renderTitle()} - {/* This might be redundant as we show the title in the title: */} - {/*isPrivateGroup ? this.renderMemberCount() : null*/}
{!isKickedFromGroup && this.renderExpirationLength()} - {!this.props.isRss && this.renderAvatar()} + {!this.props.isRss && !selectionMode && this.renderAvatar()} - {this.renderMenu(triggerId)} + {!selectionMode && this.renderMenu(triggerId)} {selectionMode && this.renderSelectionOverlay()} diff --git a/ts/components/conversation/Message.tsx b/ts/components/conversation/Message.tsx index 17e7089f2..4b759db4b 100644 --- a/ts/components/conversation/Message.tsx +++ b/ts/components/conversation/Message.tsx @@ -1186,7 +1186,7 @@ export class Message extends React.PureComponent { // User clicked on message body const target = event.target as HTMLDivElement; - if (target.className === 'text-selectable') { + if (!multiSelectMode && target.className === 'text-selectable') { return; } @@ -1196,9 +1196,8 @@ export class Message extends React.PureComponent { }} > {this.renderError(isIncoming)} - {isRss || isKickedFromGroup - ? null - : this.renderMenu(!isIncoming, triggerId)} + {enableContextMenu ? this.renderMenu(!isIncoming, triggerId) : null} +
- console.log('onVerify')} - /> - -``` - -### In one-on-one conversation - -```js - - console.log('onVerify')} - /> - -``` diff --git a/ts/components/conversation/SafetyNumberNotification.tsx b/ts/components/conversation/SafetyNumberNotification.tsx deleted file mode 100644 index 47e633994..000000000 --- a/ts/components/conversation/SafetyNumberNotification.tsx +++ /dev/null @@ -1,61 +0,0 @@ -import React from 'react'; -// import classNames from 'classnames'; - -import { ContactName } from './ContactName'; -import { Intl } from '../Intl'; -import { LocalizerType } from '../../types/Util'; - -interface Contact { - phoneNumber: string; - profileName?: string; - name?: string; -} - -interface Props { - isGroup: boolean; - contact: Contact; - i18n: LocalizerType; - onVerify: () => void; -} - -export class SafetyNumberNotification extends React.Component { - public render() { - const { contact, isGroup, i18n, onVerify } = this.props; - const changeKey = isGroup - ? 'safetyNumberChangedGroup' - : 'safetyNumberChanged'; - - return ( -
-
-
- - - , - ]} - i18n={i18n} - /> -
-
- {i18n('verifyNewNumber')} -
-
- ); - } -} diff --git a/ts/components/session/SessionKeyVerification.tsx b/ts/components/session/SessionKeyVerification.tsx index a7466a9c9..8ec2628fe 100644 --- a/ts/components/session/SessionKeyVerification.tsx +++ b/ts/components/session/SessionKeyVerification.tsx @@ -13,6 +13,7 @@ import { ConversationModel } from '../../../js/models/conversations'; import { SessionSpinner } from './SessionSpinner'; import classNames from 'classnames'; import { SessionIcon, SessionIconSize, SessionIconType } from './icon'; +import { Constants } from '../../session'; interface Props { conversation: ConversationModel; @@ -70,7 +71,9 @@ export class SessionKeyVerification extends React.Component { ); } - const verificationIconColor = isVerified ? '#00f782' : '#ff453a'; + const verificationIconColor = isVerified + ? Constants.UI.COLORS.GREEN + : Constants.UI.COLORS.DANGER; const verificationButtonColor = isVerified ? SessionButtonColor.Warning : SessionButtonColor.Success; @@ -112,7 +115,7 @@ export class SessionKeyVerification extends React.Component { {window.i18n( diff --git a/ts/components/session/SessionMemberListItem.tsx b/ts/components/session/SessionMemberListItem.tsx index 8e5cb965b..ff9f930be 100644 --- a/ts/components/session/SessionMemberListItem.tsx +++ b/ts/components/session/SessionMemberListItem.tsx @@ -3,6 +3,7 @@ import classNames from 'classnames'; import { Avatar } from '../Avatar'; import { SessionIcon, SessionIconSize, SessionIconType } from './icon'; +import { Constants } from '../../session'; export interface ContactType { id: string; @@ -79,7 +80,7 @@ export class SessionMemberListItem extends React.Component {
diff --git a/ts/components/session/SessionPasswordPrompt.tsx b/ts/components/session/SessionPasswordPrompt.tsx index 01bb0f9e1..6485e2ccb 100644 --- a/ts/components/session/SessionPasswordPrompt.tsx +++ b/ts/components/session/SessionPasswordPrompt.tsx @@ -7,6 +7,7 @@ import { SessionButtonColor, SessionButtonType, } from './SessionButton'; +import { Constants } from '../../session'; interface State { error: string; @@ -82,7 +83,7 @@ export class SessionPasswordPrompt extends React.PureComponent<{}, State> { ); const errorSection = !this.state.clearDataView && ( diff --git a/ts/components/session/SessionProgress.tsx b/ts/components/session/SessionProgress.tsx index f3bb8fffc..fff3258ec 100644 --- a/ts/components/session/SessionProgress.tsx +++ b/ts/components/session/SessionProgress.tsx @@ -1,5 +1,6 @@ import React from 'react'; import classNames from 'classnames'; +import { Constants } from '../../session'; interface Props { // Value ranges from 0 to 100 @@ -54,11 +55,8 @@ export class SessionProgress extends React.PureComponent { // 2. Transition duration scales with the // distance it needs to travel - // FIXME VINCE - globalise all JS color references - const sessionBrandColor = '#00f782'; - const sessionDangerAlt = '#ff4538'; - const successColor = sessionBrandColor; - const failureColor = sessionDangerAlt; + const successColor = Constants.UI.COLORS.GREEN; + const failureColor = Constants.UI.COLORS.DANGER_ALT; const backgroundColor = sendStatus === -1 ? failureColor : successColor; const shiftDurationMs = @@ -78,7 +76,7 @@ export class SessionProgress extends React.PureComponent { // 'transition-property': 'transform, opacity', 'transition-duration': `${shiftDurationMs}ms`, // 'transition-duration': `${shiftDurationMs}ms, ${showDurationMs}ms`, - 'transition-delay': `0ms`, + 'transition-delay': '0ms', // 'transition-delay': `0ms, ${showOffsetMs}ms`, 'transition-timing-funtion': 'cubic-bezier(0.25, 0.46, 0.45, 0.94)', //'transition-timing-funtion':'cubic-bezier(0.25, 0.46, 0.45, 0.94), linear', diff --git a/ts/components/session/conversation/SessionCompositionBox.tsx b/ts/components/session/conversation/SessionCompositionBox.tsx index 1d482a6f2..4f31277a1 100644 --- a/ts/components/session/conversation/SessionCompositionBox.tsx +++ b/ts/components/session/conversation/SessionCompositionBox.tsx @@ -205,7 +205,7 @@ export class SessionCompositionBox extends React.Component { diff --git a/ts/components/session/conversation/SessionConversation.tsx b/ts/components/session/conversation/SessionConversation.tsx index c6c52039c..16b473542 100644 --- a/ts/components/session/conversation/SessionConversation.tsx +++ b/ts/components/session/conversation/SessionConversation.tsx @@ -19,6 +19,7 @@ import { ResetSessionNotification } from '../../conversation/ResetSessionNotific import { Constants, getMessageQueue } from '../../../session'; import { MessageQueue } from '../../../session/sending'; import { SessionKeyVerification } from '../SessionKeyVerification'; +import _ from 'lodash'; interface State { conversationKey: string; @@ -113,6 +114,7 @@ export class SessionConversation extends React.Component { this.onMessageSending = this.onMessageSending.bind(this); this.onMessageSuccess = this.onMessageSuccess.bind(this); this.onMessageFailure = this.onMessageFailure.bind(this); + this.deleteSelectedMessages = this.deleteSelectedMessages.bind(this); this.messagesEndRef = React.createRef(); this.messageContainerRef = React.createRef(); @@ -126,7 +128,7 @@ export class SessionConversation extends React.Component { // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ public componentDidMount() { - this.getMessages() + this.loadInitialMessages() .then(() => { // Pause thread to wait for rendering to complete setTimeout(() => { @@ -158,6 +160,14 @@ export class SessionConversation extends React.Component { if (timestamp > this.state.messageFetchTimestamp) { await this.getMessages(); } + + // console.log('[vince] this.props.conversations:', this.props.conversations); + console.log(`[vince] Conversation changed from redux`); + + const conversationModel = window.ConversationController.get(this.state.conversationKey); + const messages = conversationModel.messageCollection; + + console.log('[vince] messages:', messages); } // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -172,7 +182,7 @@ export class SessionConversation extends React.Component { showOptionsPane, showScrollButton, } = this.state; - const loading = !doneInitialScroll || messages.length === 0; + const loading = !doneInitialScroll; const selectionMode = !!this.state.selectedMessages.length; const conversation = this.props.conversations.conversationLookup[ @@ -195,73 +205,69 @@ export class SessionConversation extends React.Component { return ( <> +
{this.renderHeader()}
+ + {/* */} +
-
{this.renderHeader()}
+
+ {showSafetyNumber && ( + + )} + {showMessageDetails && <> } +
- {/* */} +
+ {loading &&
} -
- {showSafetyNumber && ( - - )} - {showMessageDetails && <> } + {this.renderMessages()} +
-
-
- {loading &&
} - -
- {this.renderMessages()} -
-
- - - {showRecordingView && ( -
- )} -
- - {!isRss && ( - - )} -
+ + {showRecordingView && ( +
+ )}
+ + {!isRss && ( + + )}
{shouldRenderGroupSettings && ( @@ -281,6 +287,7 @@ export class SessionConversation extends React.Component { public renderMessages() { const { messages } = this.state; + const multiSelectMode = Boolean(this.state.selectedMessages.length); // FIXME VINCE: IF MESSAGE IS THE TOP OF UNREAD, THEN INSERT AN UNREAD BANNER return ( @@ -293,10 +300,6 @@ export class SessionConversation extends React.Component { i18n: window.i18n, ...message.propsForTimerNotification, }; - const friendRequestProps = message.propsForFriendRequest && { - i18n: window.i18n, - ...message.propsForFriendRequest, - }; const resetSessionProps = message.propsForResetSessionNotification && { i18n: window.i18n, ...message.propsForResetSessionNotification, @@ -309,12 +312,17 @@ export class SessionConversation extends React.Component { // firstMessageOfSeries tells us to render the avatar only for the first message // in a series of messages from the same user item = messageProps - ? this.renderMessage(messageProps, message.firstMessageOfSeries) + ? this.renderMessage( + messageProps, + message.firstMessageOfSeries, + multiSelectMode + ) : item; item = quoteProps ? this.renderMessage( timerProps, message.firstMessageOfSeries, + multiSelectMode, quoteProps ) : item; @@ -335,13 +343,13 @@ export class SessionConversation extends React.Component { public renderHeader() { const headerProps = this.getHeaderProps(); - return ; } public renderMessage( messageProps: any, firstMessageOfSeries: boolean, + multiSelectMode: boolean, quoteProps?: any ) { const selected = @@ -351,6 +359,7 @@ export class SessionConversation extends React.Component { messageProps.i18n = window.i18n; messageProps.selected = selected; messageProps.firstMessageOfSeries = firstMessageOfSeries; + messageProps.multiSelectMode = multiSelectMode; messageProps.onSelectMessage = (messageId: string) => { this.selectMessage(messageId); }; @@ -364,11 +373,38 @@ export class SessionConversation extends React.Component { // ~~~~~~~~~~~~~~ GETTER METHODS ~~~~~~~~~~~~~~ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + public async loadInitialMessages() { + // Grabs the initial set of messages and adds them to our conversation model. + // After the inital fetch, all new messages are automatically added from onNewMessage + // in the conversation model. + // The only time we need to call getMessages() is to grab more messages on scroll. + const { conversationKey } = this.state; + const conversationModel = window.ConversationController.get(conversationKey); + + const messageSet = await window.Signal.Data.getMessagesByConversation( + conversationKey, + { limit: Constants.CONVERSATION.DEFAULT_MESSAGE_FETCH_COUNT, MessageCollection: window.Whisper.MessageCollection } + ); + + const messageModels = messageSet.models; + const messages = messageModels.map((message: any) => message.id); + + this.setState({ messages }, () => { + if (this.state.isScrolledToBottom) { + this.updateReadMessages(); + } + }); + + // Add new messages to conversation collection + conversationModel.messageCollection = messageSet; + } + public async getMessages( numMessages?: number, fetchInterval = Constants.CONVERSATION.MESSAGE_FETCH_INTERVAL ) { const { conversationKey, messageFetchTimestamp } = this.state; + const conversationModel = window.ConversationController.get(conversationKey); const timestamp = getTimestamp(); // If we have pulled messages in the last interval, don't bother rescanning @@ -393,19 +429,19 @@ export class SessionConversation extends React.Component { ); // Set first member of series here. - const messageModels = messageSet.models; - const messages = []; - let previousSender; - for (let i = 0; i < messageModels.length; i++) { - // Handle firstMessageOfSeries for conditional avatar rendering - let firstMessageOfSeries = true; - if (i > 0 && previousSender === messageModels[i].authorPhoneNumber) { - firstMessageOfSeries = false; - } - - messages.push({ ...messageModels[i], firstMessageOfSeries }); - previousSender = messageModels[i].authorPhoneNumber; - } + // const messageModels = messageSet.models; + // const messages = []; + // let previousSender; + // for (let i = 0; i < messageModels.length; i++) { + // // Handle firstMessageOfSeries for conditional avatar rendering + // let firstMessageOfSeries = true; + // if (i > 0 && previousSender === messageModels[i].authorPhoneNumber) { + // firstMessageOfSeries = false; + // } + + // messages.push({ ...messageModels[i], firstMessageOfSeries }); + // previousSender = messageModels[i].authorPhoneNumber; + // } const previousTopMessage = this.state.messages[0]?.id; const newTopMessage = messages[0]?.id; @@ -416,6 +452,12 @@ export class SessionConversation extends React.Component { } }); + // Add new messages to conversation collection + // const newMessages = _.xor(messages, previousMessageSet); + // newMessages.forEach(message => conversationModel.addSingleMessage(message)); + + // console.log('[vince] conversationModel.messageCollection:', conversationModel.messageCollection); + return { newTopMessage, previousTopMessage }; } @@ -463,9 +505,14 @@ export class SessionConversation extends React.Component { onSetDisappearingMessages: (seconds: any) => conversation.updateExpirationTimer(seconds), - onDeleteMessages: () => conversation.destroyMessages(), - onDeleteSelectedMessages: () => conversation.deleteSelectedMessages(), - onCloseOverlay: () => conversation.resetMessageSelection(), + onDeleteMessages: () => null, + onDeleteSelectedMessages: async () => { + await this.deleteSelectedMessages(); + }, + onCloseOverlay: () => { + this.setState({ selectedMessages: [] }); + conversation.resetMessageSelection(); + }, onDeleteContact: () => conversation.deleteContact(), onResetSession: () => { conversation.endSession(); @@ -715,6 +762,97 @@ export class SessionConversation extends React.Component { return null; } + public async deleteSelectedMessages(onSuccess?: any) { + // Get message objects + const messageObjects = this.state.messages.filter(message => this.state.selectedMessages.find( + selectedMessage => selectedMessage === message.id + )); + // Get message model for each message + const messages = messageObjects.map(message => message?.collection?.models[0]); + + const { conversationKey } = this.state; + const conversationModel = window.ConversationController.get( + conversationKey + ); + + const multiple = messages.length > 1; + const isPublic = conversationModel.isPublic(); + + // In future, we may be able to unsend private messages also + // isServerDeletable also defined in ConversationHeader.tsx for + // future reference + const isServerDeletable = isPublic; + + const warningMessage = (() => { + if (isPublic) { + return multiple + ? window.i18n('deleteMultiplePublicWarning') + : window.i18n('deletePublicWarning'); + } + return multiple ? window.i18n('deleteMultipleWarning') : window.i18n('deleteWarning'); + })(); + + const doDelete = async () => { + let toDeleteLocally; + + console.log('[vince] conversationKey:', conversationKey); + console.log('[vince] conversationModel:', conversationModel); + console.log('[vince] messages:', messages); + + // VINCE TOOD: MARK TO-DELETE MESSAGES AS READ + + if (isPublic) { + toDeleteLocally = await conversationModel.deletePublicMessages(messages); + if (toDeleteLocally.length === 0) { + // Message failed to delete from server, show error? + return; + } + } else { + messages.forEach(m => conversationModel.messageCollection.remove(m.id)); + toDeleteLocally = messages; + } + + await Promise.all( + toDeleteLocally.map(async (message: any) => { + await window.Signal.Data.removeMessage(message.id, { + Message: window.Whisper.Message, + }); + message.trigger('unload'); + }) + ); + + if (onSuccess) { + onSuccess(); + } + }; + + // Only show a warning when at least one messages was successfully + // saved in on the server + if (!messages.some(m => !m.hasErrors())) { + await doDelete(); + return; + } + + // If removable from server, we "Unsend" - otherwise "Delete" + const pluralSuffix = multiple ? 's' : ''; + const title = window.i18n( + isPublic + ? `unsendMessage${pluralSuffix}` + : `deleteMessage${pluralSuffix}` + ); + + const okText = window.i18n(isServerDeletable ? 'unsend' : 'delete'); + + window.confirmationDialog({ + title, + message: warningMessage, + okText, + okTheme: 'danger', + resolve: doDelete, + centeredText: true, + }); + } + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // ~~~~~~~~~~~~ SCROLLING METHODS ~~~~~~~~~~~~~ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/ts/components/session/conversation/SessionEmojiPanel.tsx b/ts/components/session/conversation/SessionEmojiPanel.tsx index fc9b87a27..9657c15ca 100644 --- a/ts/components/session/conversation/SessionEmojiPanel.tsx +++ b/ts/components/session/conversation/SessionEmojiPanel.tsx @@ -1,6 +1,7 @@ import React from 'react'; import classNames from 'classnames'; import { Picker } from 'emoji-mart'; +import { Constants } from '../../../session'; interface Props { onEmojiClicked: (emoji: any) => void; @@ -31,7 +32,7 @@ export class SessionEmojiPanel extends React.Component { set={'twitter'} sheetSize={32} darkMode={true} - color={'#00F782'} + color={Constants.UI.COLORS.GREEN} showPreview={true} title={''} onSelect={onEmojiClicked} diff --git a/ts/components/session/conversation/SessionRecording.tsx b/ts/components/session/conversation/SessionRecording.tsx index c27f7f384..78836f054 100644 --- a/ts/components/session/conversation/SessionRecording.tsx +++ b/ts/components/session/conversation/SessionRecording.tsx @@ -115,8 +115,8 @@ export class SessionRecording extends React.Component { barRadius: 15, barWidth: 4, barPadding: 3, - barColorInit: '#AFAFAF', - barColorPlay: '#FFFFFF', + barColorInit: Constants.UI.COLORS.WHITE_PALE, + barColorPlay: Constants.UI.COLORS.WHITE, maxBarHeight: 30, minBarHeight: 3, }, @@ -194,8 +194,7 @@ export class SessionRecording extends React.Component { )} @@ -203,8 +202,7 @@ export class SessionRecording extends React.Component { )} @@ -249,7 +247,7 @@ export class SessionRecording extends React.Component { @@ -546,19 +544,21 @@ export class SessionRecording extends React.Component { const canvasContext = canvas && canvas.getContext('2d'); - for (var i = 0; i < volumeArray.length; i++) { + for (let i = 0; i < volumeArray.length; i++) { const barHeight = Math.ceil(volumeArray[i]); - const offset_x = Math.ceil(i * (barWidth + barPadding)); - const offset_y = Math.ceil(height / 2 - barHeight / 2); + const offsetX = Math.ceil(i * (barWidth + barPadding)); + const offsetY = Math.ceil(height / 2 - barHeight / 2); - // FIXME VINCE - Globalise JS references to colors - canvasContext && (canvasContext.fillStyle = barColorInit); - canvasContext && - this.drawRoundedRect(canvasContext, offset_x, offset_y, barHeight); + if (canvasContext) { + canvasContext.fillStyle = barColorInit; + this.drawRoundedRect(canvasContext, offsetX, offsetY, barHeight); + } } }; - this.state.isRecording && requestAnimationFrame(drawRecordingCanvas); + if (this.state.isRecording) { + requestAnimationFrame(drawRecordingCanvas); + } }; // Init listeners for visualisation @@ -663,7 +663,6 @@ export class SessionRecording extends React.Component { const offsetX = Math.ceil(i * (barWidth + barPadding)); const offsetY = Math.ceil(height / 2 - barHeight / 2); - // FIXME VINCE - Globalise JS references to colors canvasContext.fillStyle = barColorInit; this.drawRoundedRect(canvasContext, offsetX, offsetY, barHeight); diff --git a/ts/session/constants.ts b/ts/session/constants.ts index 75c14c998..423909a55 100644 --- a/ts/session/constants.ts +++ b/ts/session/constants.ts @@ -31,4 +31,51 @@ export const UI = { // Pixels (scroll) from the top of the top of message container // at which more messages should be loaded MESSAGE_CONTAINER_BUFFER_OFFSET_PX: 30, + + COLORS: { + // COMMON + WHITE: '#FFFFFF', + WHITE_PALE: '#AFAFAF', + LIGHT_GREY: '#A0A0A0', + DARK_GREY: '#353535', + BLACK: '#000000', + GREEN: '#00F782', + GREEN_ALT_1: '#00F480', + GREEN_ALT_2: '#00FD73', + GREEN_ALT_3: '#00F782', + BACKGROUND: '#121212', + + // SHADES + SHADE_1: '#0C0C0C', + SHADE_1_ALT: '#0F1011', + SHADE_2: '#161616', + SHADE_3: '#191818', + SHADE_4: '#1B1B1B', + SHADE_5: '#222325', + SHADE_6: '#232323', + SHADE_6_ALT: '#2C2C2C', + SHADE_7: '#2E2E2E', + SHADE_8: '#2F2F2F', + SHADE_9: '#313131', + SHADE_10: '#3E3E3E', + SHADE_11: '#3F3F3F', + SHADE_12: '#3F4146', + SHADE_13: '#474646', + SHADE_14: '#535865', + SHADE_15: '#5B6C72', + SHADE_16: '#979797', + SHADE_17: '#A0A0A0', + SHADE_18: '#141414', + + // SEMANTIC COLORS + INFO: '#3F3F3F', + SUCCESS: '#35D388', + ERROR: '#EDD422', + WARNING: '#A0A0A0', + WARNING_ALT: '#FF9D00', + DANGER: '#FF453A', + DANGER_ALT: '#FF4538', + PRIMARY: '#474646', + SECONDARY: '#232323', + }, };