Revert "When you send a message, scroll it into view"

This reverts commit a3525c16ef.
This commit is contained in:
Fedor Indutny
2021-11-23 14:09:07 +01:00
committed by GitHub
parent 3601279287
commit a52530262f
14 changed files with 438 additions and 387 deletions
+305 -8
View File
@@ -19,6 +19,7 @@ import { sniffImageMimeType } from '../util/sniffImageMimeType';
import type { ConversationModel } from '../models/conversations';
import type {
GroupV2PendingMemberType,
MessageModelCollectionType,
MessageAttributesType,
ConversationModelCollectionType,
QuotedMessageType,
@@ -48,6 +49,7 @@ import {
isOutgoing,
isTapToView,
} from '../state/selectors/message';
import { isMessageUnread } from '../util/isMessageUnread';
import {
getConversationSelector,
getMessagesByConversation,
@@ -136,7 +138,13 @@ const {
upgradeMessageSchema,
} = window.Signal.Migrations;
const { getMessageById, getMessagesBySentAt } = window.Signal.Data;
const {
getOlderMessagesByConversation,
getMessageMetricsForConversation,
getMessageById,
getMessagesBySentAt,
getNewerMessagesByConversation,
} = window.Signal.Data;
type MessageActionsType = {
deleteMessage: (messageId: string) => unknown;
@@ -466,6 +474,107 @@ export class ConversationView extends window.Backbone.View<ConversationModel> {
this.scrollToMessage(message.id);
};
const loadOlderMessages = async (oldestMessageId: string) => {
const { messagesAdded, setMessagesLoading, repairOldestMessage } =
window.reduxActions.conversations;
const conversationId = this.model.id;
setMessagesLoading(conversationId, true);
const finish = this.setInProgressFetch();
try {
const message = await getMessageById(oldestMessageId, {
Message: Whisper.Message,
});
if (!message) {
throw new Error(
`loadOlderMessages: failed to load message ${oldestMessageId}`
);
}
const receivedAt = message.get('received_at');
const sentAt = message.get('sent_at');
const models = await getOlderMessagesByConversation(conversationId, {
receivedAt,
sentAt,
messageId: oldestMessageId,
limit: 30,
MessageCollection: Whisper.MessageCollection,
});
if (models.length < 1) {
log.warn('loadOlderMessages: requested, but loaded no messages');
repairOldestMessage(conversationId);
return;
}
const cleaned = await this.cleanModels(models);
const isNewMessage = false;
messagesAdded(
this.model.id,
cleaned.map((messageModel: MessageModel) => ({
...messageModel.attributes,
})),
isNewMessage,
window.isActive()
);
} catch (error) {
setMessagesLoading(conversationId, true);
throw error;
} finally {
finish();
}
};
const loadNewerMessages = async (newestMessageId: string) => {
const { messagesAdded, setMessagesLoading, repairNewestMessage } =
window.reduxActions.conversations;
const conversationId = this.model.id;
setMessagesLoading(conversationId, true);
const finish = this.setInProgressFetch();
try {
const message = await getMessageById(newestMessageId, {
Message: Whisper.Message,
});
if (!message) {
throw new Error(
`loadNewerMessages: failed to load message ${newestMessageId}`
);
}
const receivedAt = message.get('received_at');
const sentAt = message.get('sent_at');
const models = await getNewerMessagesByConversation(conversationId, {
receivedAt,
sentAt,
limit: 30,
MessageCollection: Whisper.MessageCollection,
});
if (models.length < 1) {
log.warn('loadNewerMessages: requested, but loaded no messages');
repairNewestMessage(conversationId);
return;
}
const cleaned = await this.cleanModels(models);
const isNewMessage = false;
messagesAdded(
conversationId,
cleaned.map((messageModel: MessageModel) => ({
...messageModel.attributes,
})),
isNewMessage,
window.isActive()
);
} catch (error) {
setMessagesLoading(conversationId, false);
throw error;
} finally {
finish();
}
};
const markMessageRead = async (messageId: string) => {
if (!window.isActive()) {
return;
@@ -506,10 +615,10 @@ export class ConversationView extends window.Backbone.View<ConversationModel> {
},
contactSupport,
learnMoreAboutDeliveryIssue,
loadNewerMessages: this.model.loadNewerMessages.bind(this.model),
loadNewestMessages: this.model.loadNewestMessages.bind(this.model),
loadAndScroll: this.model.loadAndScroll.bind(this.model),
loadOlderMessages: this.model.loadOlderMessages.bind(this.model),
loadNewerMessages,
loadNewestMessages: this.loadNewestMessages.bind(this),
loadAndScroll: this.loadAndScroll.bind(this),
loadOlderMessages,
markMessageRead,
onBlock: createMessageRequestResponseHandler(
'onBlock',
@@ -882,6 +991,38 @@ export class ConversationView extends window.Backbone.View<ConversationModel> {
};
}
async cleanModels(
collection: MessageModelCollectionType | Array<MessageModel>
): Promise<Array<MessageModel>> {
const result = collection
.filter((message: MessageModel) => Boolean(message.id))
.map((message: MessageModel) =>
window.MessageController.register(message.id, message)
);
const eliminated = collection.length - result.length;
if (eliminated > 0) {
log.warn(`cleanModels: Eliminated ${eliminated} messages without an id`);
}
for (let max = result.length, i = 0; i < max; i += 1) {
const message = result[i];
const { attributes } = message;
const { schemaVersion } = attributes;
if (schemaVersion < Message.VERSION_NEEDED_FOR_DISPLAY) {
// Yep, we really do want to wait for each of these
// eslint-disable-next-line no-await-in-loop
const upgradedMessage = await upgradeMessageSchema(attributes);
message.set(upgradedMessage);
// eslint-disable-next-line no-await-in-loop
await window.Signal.Data.saveMessage(upgradedMessage);
}
}
return result;
}
async scrollToMessage(messageId: string): Promise<void> {
const message = await getMessageById(messageId, {
Message: Whisper.Message,
@@ -912,7 +1053,162 @@ export class ConversationView extends window.Backbone.View<ConversationModel> {
return;
}
this.model.loadAndScroll(messageId);
this.loadAndScroll(messageId);
}
setInProgressFetch(): () => unknown {
let resolvePromise: (value?: unknown) => void;
this.model.inProgressFetch = new Promise(resolve => {
resolvePromise = resolve;
});
const finish = () => {
resolvePromise();
this.model.inProgressFetch = undefined;
};
return finish;
}
async loadAndScroll(
messageId: string,
options?: { disableScroll?: boolean }
): Promise<void> {
const { messagesReset, setMessagesLoading } =
window.reduxActions.conversations;
const conversationId = this.model.id;
setMessagesLoading(conversationId, true);
const finish = this.setInProgressFetch();
try {
const message = await getMessageById(messageId, {
Message: Whisper.Message,
});
if (!message) {
throw new Error(
`loadMoreAndScroll: failed to load message ${messageId}`
);
}
const receivedAt = message.get('received_at');
const sentAt = message.get('sent_at');
const older = await getOlderMessagesByConversation(conversationId, {
limit: 30,
receivedAt,
sentAt,
messageId,
MessageCollection: Whisper.MessageCollection,
});
const newer = await getNewerMessagesByConversation(conversationId, {
limit: 30,
receivedAt,
sentAt,
MessageCollection: Whisper.MessageCollection,
});
const metrics = await getMessageMetricsForConversation(conversationId);
const all = [...older.models, message, ...newer.models];
const cleaned: Array<MessageModel> = await this.cleanModels(all);
const scrollToMessageId =
options && options.disableScroll ? undefined : messageId;
messagesReset(
conversationId,
cleaned.map((messageModel: MessageModel) => ({
...messageModel.attributes,
})),
metrics,
scrollToMessageId
);
} catch (error) {
setMessagesLoading(conversationId, false);
throw error;
} finally {
finish();
}
}
async loadNewestMessages(
newestMessageId: string | undefined,
setFocus: boolean | undefined
): Promise<void> {
const { messagesReset, setMessagesLoading } =
window.reduxActions.conversations;
const conversationId = this.model.id;
setMessagesLoading(conversationId, true);
const finish = this.setInProgressFetch();
try {
let scrollToLatestUnread = true;
if (newestMessageId) {
const newestInMemoryMessage = await getMessageById(newestMessageId, {
Message: Whisper.Message,
});
if (newestInMemoryMessage) {
// If newest in-memory message is unread, scrolling down would mean going to
// the very bottom, not the oldest unread.
if (isMessageUnread(newestInMemoryMessage.attributes)) {
scrollToLatestUnread = false;
}
} else {
log.warn(
`loadNewestMessages: did not find message ${newestMessageId}`
);
}
}
const metrics = await getMessageMetricsForConversation(conversationId);
// If this is a message request that has not yet been accepted, we always show the
// oldest messages, to ensure that the ConversationHero is shown. We don't want to
// scroll directly to the oldest message, because that could scroll the hero off
// the screen.
if (!newestMessageId && !this.model.getAccepted() && metrics.oldest) {
this.loadAndScroll(metrics.oldest.id, { disableScroll: true });
return;
}
if (scrollToLatestUnread && metrics.oldestUnread) {
this.loadAndScroll(metrics.oldestUnread.id, {
disableScroll: !setFocus,
});
return;
}
const messages = await getOlderMessagesByConversation(conversationId, {
limit: 30,
MessageCollection: Whisper.MessageCollection,
});
const cleaned: Array<MessageModel> = await this.cleanModels(messages);
const scrollToMessageId =
setFocus && metrics.newest ? metrics.newest.id : undefined;
// Because our `getOlderMessages` fetch above didn't specify a receivedAt, we got
// the most recent 30 messages in the conversation. If it has a conflict with
// metrics, fetched a bit before, that's likely a race condition. So we tell our
// reducer to trust the message set we just fetched for determining if we have
// the newest message loaded.
const unboundedFetch = true;
messagesReset(
conversationId,
cleaned.map((messageModel: MessageModel) => ({
...messageModel.attributes,
})),
metrics,
scrollToMessageId,
unboundedFetch
);
} catch (error) {
setMessagesLoading(conversationId, false);
throw error;
} finally {
finish();
}
}
async startMigrationToGV2(): Promise<void> {
@@ -1206,7 +1502,7 @@ export class ConversationView extends window.Backbone.View<ConversationModel> {
});
if (message) {
this.model.loadAndScroll(messageId);
this.loadAndScroll(messageId);
return;
}
@@ -1218,7 +1514,7 @@ export class ConversationView extends window.Backbone.View<ConversationModel> {
await retryPlaceholders.findByConversationAndMarkOpened(this.model.id);
}
this.model.loadNewestMessages(undefined, undefined);
this.loadNewestMessages(undefined, undefined);
this.model.updateLastMessage();
this.focusMessageField();
@@ -1286,6 +1582,7 @@ export class ConversationView extends window.Backbone.View<ConversationModel> {
window.reduxStore,
{
attachments: draftAttachments,
conversationId: this.model.id,
doForwardMessage: async (
conversationIds: Array<string>,
messageBody?: string,