cleanup message.js and conversation_view.js

This commit is contained in:
Audric Ackermann 2020-11-12 15:40:07 +11:00
parent d94d2819ce
commit 2ae7a6dfe5
No known key found for this signature in database
GPG key ID: 999F434D76324AD4
4 changed files with 56 additions and 448 deletions

View file

@ -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(),
});

View file

@ -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

View file

@ -208,12 +208,7 @@ export class SessionCompositionBox extends React.Component<Props, State> {
}
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;

View file

@ -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;
},
},