From 2ae7a6dfe533b8269a40bb4c9cdd13772f767bff Mon Sep 17 00:00:00 2001 From: Audric Ackermann Date: Thu, 12 Nov 2020 15:40:07 +1100 Subject: [PATCH] cleanup message.js and conversation_view.js --- js/models/messages.js | 79 ++-- js/views/conversation_view.js | 379 ------------------ .../conversation/SessionCompositionBox.tsx | 7 +- ts/state/ducks/messages.ts | 39 +- 4 files changed, 56 insertions(+), 448 deletions(-) diff --git a/js/models/messages.js b/js/models/messages.js index 44898a3cf..381026463 100644 --- a/js/models/messages.js +++ b/js/models/messages.js @@ -53,8 +53,6 @@ ); } - this.OUR_NUMBER = textsecure.storage.user.getNumber(); - this.on('destroy', this.onDestroy); this.on('change:expirationStartTimestamp', this.setToExpire); this.on('change:expireTimer', this.setToExpire); @@ -62,7 +60,7 @@ this.on('expired', this.onExpired); this.setToExpire(); // Keep props ready - const generateProps = () => { + const generateProps = (triggerEvent = true) => { if (this.isExpirationTimerUpdate()) { this.propsForTimerNotification = this.getPropsForTimerNotification(); } else if (this.isVerifiedChange()) { @@ -79,32 +77,30 @@ this.propsForSearchResult = this.getPropsForSearchResult(); this.propsForMessage = this.getPropsForMessage(); } - Whisper.events.trigger('messageChanged', this); + if (triggerEvent) { + Whisper.events.trigger('messageChanged', this); + } }; this.on('change', generateProps); - const applicableConversationChanges = - 'change:color change:name change:number change:profileName change:profileAvatar'; - + // const applicableConversationChanges = + // 'change:color change:name change:number change:profileName change:profileAvatar'; + // FIXME AUDRIC const conversation = this.getConversation(); - const fromContact = this.getIncomingContact(); - this.listenTo(conversation, applicableConversationChanges, generateProps); + // const fromContact = this.getIncomingContact(); + // this.listenTo(conversation, applicableConversationChanges, generateProps); - // trigger a change event on this component. - // this will call generateProps and refresh the Message.tsx component with new props - this.listenTo(conversation, 'disable:input', () => this.trigger('change')); - if (fromContact) { - this.listenTo( - fromContact, - applicableConversationChanges, - generateProps - ); - } + // if (fromContact) { + // this.listenTo( + // fromContact, + // applicableConversationChanges, + // generateProps + // ); + // } - this.selected = false; window.contextMenuShown = false; - generateProps(); + generateProps(false); }, idForLogging() { return `${this.get('source')}.${this.get('sourceDevice')} ${this.get( @@ -340,7 +336,7 @@ ...basicProps, type: 'fromSync', }; - } else if (source === this.OUR_NUMBER) { + } else if (source === textsecure.storage.user.getNumber()) { return { ...basicProps, type: 'fromMe', @@ -524,13 +520,13 @@ getPropsForSearchResult() { const fromNumber = this.getSource(); const from = this.findAndFormatContact(fromNumber); - if (fromNumber === this.OUR_NUMBER) { + if (fromNumber === textsecure.storage.user.getNumber()) { from.isMe = true; } const toNumber = this.get('conversationId'); let to = this.findAndFormatContact(toNumber); - if (toNumber === this.OUR_NUMBER) { + if (toNumber === textsecure.storage.user.getNumber()) { to.isMe = true; } else if (fromNumber === toNumber) { to = { @@ -571,7 +567,8 @@ const conversation = this.getConversation(); const isModerator = - conversation && !!conversation.isModerator(this.OUR_NUMBER); + conversation && + !!conversation.isModerator(textsecure.storage.user.getNumber()); const convoId = conversation ? conversation.id : undefined; const isGroup = !!conversation && !conversation.isPrivate(); @@ -601,7 +598,6 @@ isExpired: this.hasExpired, expirationLength, expirationTimestamp, - selected: this.selected, multiSelectMode: conversation && conversation.selectedMessages.size > 0, isPublic: !!this.get('isPublic'), isRss: !!this.get('isRss'), @@ -614,11 +610,10 @@ isDeletable: !this.get('isPublic') || isModerator || - phoneNumber === this.OUR_NUMBER, + phoneNumber === textsecure.storage.user.getNumber(), isModerator, onCopyText: () => this.copyText(), - onSelectMessageUnchecked: () => this.selectMessageUnchecked(), onCopyPubKey: () => this.copyPubKey(), onBanUser: () => this.banUser(), onRetrySend: () => this.retrySend(), @@ -742,7 +737,9 @@ ourRegionCode: regionCode, }); const authorName = contact ? contact.getName() : null; - const isFromMe = contact ? contact.id === this.OUR_NUMBER : false; + const isFromMe = contact + ? contact.id === textsecure.storage.user.getNumber() + : false; const onClick = noClick ? null : event => { @@ -906,7 +903,7 @@ if (this.isIncoming()) { clipboard.writeText(this.get('source')); } else { - clipboard.writeText(this.OUR_NUMBER); + clipboard.writeText(textsecure.storage.user.getNumber()); } window.libsession.Utils.ToastUtils.pushCopiedToClipBoard(); @@ -932,20 +929,6 @@ }); }, - // Select message even if the context menu is shown - selectMessageUnchecked() { - this.selected = !this.selected; - - const convo = this.getConversation(); - - if (this.selected) { - convo.addMessageSelection(this); - } else { - convo.removeMessageSelection(this); - } - this.trigger('change'); - }, - copyText() { clipboard.writeText(this.get('body')); @@ -1142,7 +1125,7 @@ }); // Special-case the self-send case - we send only a sync message - if (number === this.OUR_NUMBER) { + if (number === textsecure.storage.user.getNumber()) { return this.sendSyncMessageOnly(chatMessage); } @@ -1399,7 +1382,7 @@ return this.get('source'); } - return this.OUR_NUMBER; + return textsecure.storage.user.getNumber(); }, getContact() { const source = this.getSource(); @@ -1487,7 +1470,7 @@ async sendSyncMessageOnly(dataMessage) { this.set({ - sent_to: [this.OUR_NUMBER], + sent_to: [textsecure.storage.user.getNumber()], sent: true, expirationStartTimestamp: Date.now(), }); @@ -1550,7 +1533,7 @@ this.set({ // These are the same as a normal send() dataMessage, - sent_to: [this.OUR_NUMBER], + sent_to: [textsecure.storage.user.getNumber()], sent: true, expirationStartTimestamp: Date.now(), }); diff --git a/js/views/conversation_view.js b/js/views/conversation_view.js index e11a073f8..a1bd49337 100644 --- a/js/views/conversation_view.js +++ b/js/views/conversation_view.js @@ -5,7 +5,6 @@ i18n, Signal, storage, - textsecure, Whisper, ConversationController, */ @@ -214,25 +213,6 @@ } }, - onChangePlaceholder(type) { - if (!this.$messageField) { - return; - } - let placeholder; - switch (type) { - case 'left-group': - placeholder = i18n('sendMessageLeftGroup'); - break; - case 'blocked-user': - placeholder = i18n('sendMessageBlockedUser'); - break; - default: - placeholder = i18n('sendMessage'); - break; - } - this.$messageField.attr('placeholder', placeholder); - }, - unload(reason) { window.log.info( 'unloading conversation', @@ -390,79 +370,6 @@ } }, - async toggleMicrophone() { - // FIXME audric hide microphone for now until refactor branch is merged - // const allowMicrophone = await window.getMediaPermissions(); - // if ( - // !allowMicrophone || - // this.$('.send-message').val().length > 0 || - // this.fileInput.hasFiles() - // ) { - this.$('.capture-audio').hide(); - // } else { - // this.$('.capture-audio').show(); - // } - }, - captureAudio(e) { - e.preventDefault(); - - if (this.fileInput.hasFiles()) { - // pushToast() this toast too - const toast = new Whisper.VoiceNoteMustBeOnlyAttachmentToast(); - toast.$el.appendTo(this.$el); - toast.render(); - return; - } - - // Note - clicking anywhere will close the audio capture panel, due to - // the onClick handler in InboxView, which calls its closeRecording method. - - if (this.captureAudioView) { - this.captureAudioView.remove(); - this.captureAudioView = null; - } - - this.captureAudioView = new Whisper.RecorderView(); - - const view = this.captureAudioView; - view.render(); - view.on('send', this.handleAudioCapture.bind(this)); - view.on('closed', this.endCaptureAudio.bind(this)); - view.$el.appendTo(this.$('.capture-audio')); - - this.$('.send-message').attr('disabled', true); - this.$('.microphone').hide(); - }, - handleAudioCapture(blob) { - this.fileInput.addAttachment({ - contentType: blob.type, - file: blob, - isVoiceNote: true, - }); - this.$('.bottom-bar form').submit(); - }, - endCaptureAudio() { - this.$('.send-message').removeAttr('disabled'); - this.$('.microphone').show(); - this.captureAudioView = null; - }, - - unfocusBottomBar() { - this.$('.bottom-bar form').removeClass('active'); - }, - focusBottomBar() { - this.$('.bottom-bar form').addClass('active'); - }, - - onLazyScroll() { - // The in-progress fetch check is important, because while that happens, lots - // of messages are added to the DOM, one by one, changing window size and - // generating scroll events. - if (!this.isHidden() && window.isFocused() && !this.inProgressFetch) { - this.lastActivity = Date.now(); - this.markRead(); - } - }, updateUnread() { this.resetLastSeenIndicator(); // Waiting for scrolling caused by resetLastSeenIndicator to settle down @@ -533,107 +440,6 @@ } }, - addScrollDownButtonWithCount() { - this.updateScrollDownButton(1); - }, - - addScrollDownButton() { - if (!this.scrollDownButton) { - this.updateScrollDownButton(); - } - }, - - updateScrollDownButton(count) { - if (!this.scrollDownButton) { - this.scrollDownButton = new Whisper.ScrollDownButtonView({ count }); - this.scrollDownButton.render(); - const container = this.$('.discussion-container'); - container.append(this.scrollDownButton.el); - } - }, - - removeScrollDownButton() { - if (this.scrollDownButton) { - const button = this.scrollDownButton; - this.scrollDownButton = null; - button.remove(); - } - }, - - removeLastSeenIndicator() { - if (this.lastSeenIndicator) { - const indicator = this.lastSeenIndicator; - this.lastSeenIndicator = null; - indicator.remove(); - } - }, - - scrollToBottom() { - // If we're above the last seen indicator, we should scroll there instead - // Note: if we don't end up at the bottom of the conversation, button won't go away! - if (this.lastSeenIndicator) { - const location = this.lastSeenIndicator.$el.position().top; - if (location > 0) { - this.lastSeenIndicator.el.scrollIntoView(); - return; - } - this.removeLastSeenIndicator(); - } - this.view.scrollToBottom(); - }, - - resetLastSeenIndicator(options = {}) { - _.defaults(options, { scroll: true }); - - let unreadCount = 0; - let oldestUnread = null; - - // We need to iterate here because unseen non-messages do not contribute to - // the badge number, but should be reflected in the indicator's count. - this.model.messageCollection.forEach(model => { - if (!model.get('unread')) { - return; - } - - unreadCount += 1; - if (!oldestUnread) { - oldestUnread = model; - } - }); - - this.removeLastSeenIndicator(); - - if (oldestUnread) { - this.lastSeenIndicator = new Whisper.LastSeenIndicatorView({ - count: unreadCount, - }); - const lastSeenEl = this.lastSeenIndicator.render().$el; - - lastSeenEl.insertBefore(this.$(`#${oldestUnread.get('id')}`)); - - if (this.view.atBottom() || options.scroll) { - lastSeenEl[0].scrollIntoView(); - } - } else if (this.view.atBottom()) { - // If we already thought we were at the bottom, then ensure that's the case. - // Attempting to account for unpredictable completion of message rendering. - setTimeout(() => this.view.scrollToBottom(), 1); - } - }, - - focusMessageField() { - if (this.panels && this.panels.length) { - return; - } - - this.$messageField.focus(); - }, - - focusMessageFieldAndClearDisabled() { - this.$messageField.removeAttr('disabled'); - this.$messageField.focus(); - }, - async loadMoreMessages() { if (this.inProgressFetch) { return; @@ -1091,191 +897,6 @@ onKeyUp() { this.maybeBumpTyping(); - this.debouncedMaybeGrabLinkPreview(); - }, - - maybeGrabLinkPreview() { - // Don't generate link previews if user has turned them off - if (!storage.get('link-preview-setting', false)) { - return; - } - // Do nothing if we're offline - if (!textsecure.messaging) { - return; - } - // If we have attachments, don't add link preview - if (this.fileInput.hasFiles()) { - return; - } - // If we're behind a user-configured proxy, we don't support link previews - if (window.isBehindProxy()) { - return; - } - - const messageText = this.$messageField.val().trim(); - const caretLocation = this.$messageField.get(0).selectionStart; - - if (!messageText) { - this.resetLinkPreview(); - return; - } - if (this.disableLinkPreviews) { - return; - } - - const links = window.Signal.LinkPreviews.findLinks( - messageText, - caretLocation - ); - const { currentlyMatchedLink } = this; - if (links.includes(currentlyMatchedLink)) { - return; - } - - this.currentlyMatchedLink = null; - this.excludedPreviewUrls = this.excludedPreviewUrls || []; - - const link = links.find( - item => - window.Signal.LinkPreviews.isLinkInWhitelist(item) && - !this.excludedPreviewUrls.includes(item) - ); - if (!link) { - this.removeLinkPreview(); - return; - } - - this.currentlyMatchedLink = link; - this.addLinkPreview(link); - }, - - resetLinkPreview() { - this.disableLinkPreviews = false; - this.excludedPreviewUrls = []; - this.removeLinkPreview(); - }, - - removeLinkPreview() { - (this.preview || []).forEach(item => { - if (item.url) { - URL.revokeObjectURL(item.url); - } - }); - this.preview = null; - this.previewLoading = null; - this.currentlyMatchedLink = false; - this.renderLinkPreview(); - }, - - async addLinkPreview(url) { - (this.preview || []).forEach(item => { - if (item.url) { - URL.revokeObjectURL(item.url); - } - }); - this.preview = null; - - this.currentlyMatchedLink = url; - this.previewLoading = this.getPreview(url); - const promise = this.previewLoading; - this.renderLinkPreview(); - - try { - const result = await promise; - - if ( - url !== this.currentlyMatchedLink || - promise !== this.previewLoading - ) { - // another request was started, or this was canceled - return; - } - - // If we couldn't pull down the initial URL - if (!result) { - this.excludedPreviewUrls.push(url); - this.removeLinkPreview(); - return; - } - - if (result.image) { - const blob = new Blob([result.image.data], { - type: result.image.contentType, - }); - result.image.url = URL.createObjectURL(blob); - } else if (!result.title) { - // A link preview isn't worth showing unless we have either a title or an image - this.removeLinkPreview(); - return; - } - - this.preview = [result]; - this.renderLinkPreview(); - } catch (error) { - window.log.error( - 'Problem loading link preview, disabling.', - error && error.stack ? error.stack : error - ); - this.disableLinkPreviews = true; - this.removeLinkPreview(); - } - }, - - renderLinkPreview() { - if (this.previewView) { - this.previewView.remove(); - this.previewView = null; - } - if (!this.currentlyMatchedLink) { - this.view.restoreBottomOffset(); - this.updateMessageFieldSize({}); - return; - } - - const first = (this.preview && this.preview[0]) || null; - const props = { - ...first, - domain: first && window.Signal.LinkPreviews.getDomain(first.url), - isLoaded: Boolean(first), - onClose: () => { - this.disableLinkPreviews = true; - this.removeLinkPreview(); - }, - }; - - this.previewView = new Whisper.ReactWrapperView({ - className: 'preview-wrapper', - Component: window.Signal.Components.StagedLinkPreview, - elCallback: el => this.$('.send').prepend(el), - props, - onInitialRender: () => { - this.view.restoreBottomOffset(); - this.updateMessageFieldSize({}); - }, - }); - }, - - getLinkPreview() { - // Don't generate link previews if user has turned them off - if (!storage.get('link-preview-setting', false)) { - return []; - } - - if (!this.preview) { - return []; - } - - return this.preview.map(item => { - if (item.image) { - // We eliminate the ObjectURL here, unneeded for send or save - return { - ...item, - image: _.omit(item.image, 'url'), - }; - } - - return item; - }); }, // Called whenever the user changes the message composition field. But only diff --git a/ts/components/session/conversation/SessionCompositionBox.tsx b/ts/components/session/conversation/SessionCompositionBox.tsx index bd7a56cf7..d6a0b3c53 100644 --- a/ts/components/session/conversation/SessionCompositionBox.tsx +++ b/ts/components/session/conversation/SessionCompositionBox.tsx @@ -208,12 +208,7 @@ export class SessionCompositionBox extends React.Component { } private renderCompositionView() { - const { - isBlocked, - isKickedFromGroup, - leftGroup, - isPrivate, - } = this.props; + const { isBlocked, isKickedFromGroup, leftGroup, isPrivate } = this.props; const { showEmojiPanel, message } = this.state; const typingEnabled = !(isBlocked || isKickedFromGroup || leftGroup); const { i18n } = window; diff --git a/ts/state/ducks/messages.ts b/ts/state/ducks/messages.ts index 126678279..c33efaf27 100644 --- a/ts/state/ducks/messages.ts +++ b/ts/state/ducks/messages.ts @@ -39,7 +39,7 @@ export async function getMessages( const messages = []; // no need to do that `firstMessageOfSeries` on a private chat if (conversation.isPrivate()) { - return messageSet.models; + return messageModels; } // messages are got from the more recent to the oldest, so we need to check if @@ -75,29 +75,35 @@ const fetchMessagesForConversation = createAsyncThunk( } ); -const toOmitFromMessageModel = [ - 'cid', - 'collection', - // 'changing', - // 'previousAttributes', - '_events', - '_listeningTo', +const toPickFromMessageModel = [ + 'attributes', + 'id', + 'propsForSearchResult', + 'propsForMessage', + 'receivedAt', + 'conversationId', + 'firstMessageOfSeries', + 'propsForGroupInvitation', + 'propsForTimerNotification', + 'propsForVerificationNotification', + 'propsForResetSessionNotification', + 'propsForGroupNotification', ]; const messageSlice = createSlice({ name: 'messages', initialState: [] as MessagesStateType, reducers: { - messageChanged(state, action) { - // console.log('message changed ', action); + messageChanged(state: MessagesStateType, action) { + console.log('message changed ', action); const messageInStoreIndex = state.findIndex( m => m.id === action.payload.id ); if (messageInStoreIndex >= 0) { - state[messageInStoreIndex] = _.omit( + state[messageInStoreIndex] = _.pick( action.payload, - toOmitFromMessageModel - ); + toPickFromMessageModel + ) as MessageType; } return state; }, @@ -105,9 +111,12 @@ const messageSlice = createSlice({ extraReducers: { // Add reducers for additional action types here, and handle loading state as needed [fetchMessagesForConversation.fulfilled.type]: (state, action) => { + // console.log('fetchMessagesForConversatio0 NON LIGHT', action.payload); + const lightMessages = action.payload.map((m: any) => - _.omit(m, toOmitFromMessageModel) - ); + _.pick(m, toPickFromMessageModel) + ) as MessagesStateType; + // console.log('fetchMessagesForConversation', lightMessages); return lightMessages; }, },