Улучшить открытие личного чата
This commit is contained in:
parent
0f3c4a621d
commit
827d2e9c3e
@ -3,6 +3,9 @@
|
||||
Доработан UX личного чата на мобильных устройствах:
|
||||
- при открытой экранной клавиатуре нижний тулбар на 5 кнопок временно скрывается;
|
||||
- после отправки собственного сообщения чат автоматически прокручивается вниз так, чтобы новое сообщение было видно сразу.
|
||||
- при открытии уже существующего личного чата стартовая позиция выбирается по хвосту переписки:
|
||||
- если есть непрочитанные сообщения, открытие происходит на линии `Новые сообщения`;
|
||||
- если непрочитанных нет, чат открывается сразу в самом низу.
|
||||
|
||||
## Что проверять
|
||||
|
||||
@ -12,12 +15,15 @@
|
||||
- отправить короткое сообщение, находясь не в самом низу переписки;
|
||||
- убедиться, что после отправки экран прокручивается вниз и новое сообщение видно сразу;
|
||||
- проверить то же поведение после прихода подтверждения отправки/перерисовки списка.
|
||||
- открыть существующий чат с непрочитанными сообщениями и убедиться, что видна линия `Новые сообщения` и сообщения ниже неё;
|
||||
- открыть существующий чат без непрочитанных сообщений и убедиться, что открыт конец переписки, а не начало.
|
||||
|
||||
## Ожидаемый результат
|
||||
|
||||
- клавиатура не конфликтует по высоте с нижним тулбаром;
|
||||
- при наборе доступно больше вертикального места;
|
||||
- собственное только что отправленное сообщение сразу попадает в видимую область.
|
||||
- при открытии чата пользователь сразу попадает в актуальную часть переписки.
|
||||
|
||||
## Статус
|
||||
|
||||
|
||||
@ -1,2 +1,2 @@
|
||||
client.version=1.2.264
|
||||
client.version=1.2.265
|
||||
server.version=1.2.248
|
||||
|
||||
@ -281,7 +281,27 @@ function scrollToLatestMessage(list) {
|
||||
window.setTimeout(apply, 260);
|
||||
}
|
||||
|
||||
function renderLog(list, chatId, { onOpenActions } = {}) {
|
||||
function scrollToUnreadSeparator(list) {
|
||||
if (!list) return false;
|
||||
const separator = list.querySelector('.chat-unread-separator');
|
||||
if (!separator) return false;
|
||||
const scrollContainer = list.closest('.screen-content') || list.parentElement || list;
|
||||
const apply = () => {
|
||||
if (separator?.scrollIntoView) {
|
||||
separator.scrollIntoView({ block: 'start', inline: 'nearest' });
|
||||
}
|
||||
const bottomSlack = 72;
|
||||
scrollContainer.scrollTop = Math.max(0, scrollContainer.scrollTop - bottomSlack);
|
||||
};
|
||||
apply();
|
||||
window.requestAnimationFrame(apply);
|
||||
window.requestAnimationFrame(() => window.requestAnimationFrame(apply));
|
||||
window.setTimeout(apply, 60);
|
||||
window.setTimeout(apply, 160);
|
||||
return true;
|
||||
}
|
||||
|
||||
function renderLog(list, chatId, { onOpenActions, markAsRead = true, scrollMode = 'latest' } = {}) {
|
||||
list.innerHTML = '';
|
||||
const messages = getChatMessages(chatId);
|
||||
let unreadSeparatorInserted = false;
|
||||
@ -336,8 +356,14 @@ function renderLog(list, chatId, { onOpenActions } = {}) {
|
||||
});
|
||||
list.append(bubble);
|
||||
});
|
||||
if (scrollMode === 'unread' && !scrollToUnreadSeparator(list)) {
|
||||
scrollToLatestMessage(list);
|
||||
} else if (scrollMode === 'latest') {
|
||||
scrollToLatestMessage(list);
|
||||
}
|
||||
if (markAsRead) {
|
||||
markChatRead(chatId);
|
||||
}
|
||||
}
|
||||
|
||||
function preserveComposerSelection(input, callback) {
|
||||
@ -383,6 +409,7 @@ export function render({ navigate, route }) {
|
||||
const isSpeechToTextReady = isSpeechToTextConfigured(state.entrySettings);
|
||||
const isTextToSpeechReady = isTextToSpeechConfigured(state.entrySettings);
|
||||
const isKnownContact = (state.contacts || []).some((x) => String(x || '').toLowerCase() === String(chatId || '').toLowerCase());
|
||||
const hasUnreadIncoming = getChatMessages(chatId).some((msg) => msg?.from === 'in' && msg?.unread);
|
||||
|
||||
const handleReadAloud = async (msg) => {
|
||||
if (!isTextToSpeechConfigured(state.entrySettings)) {
|
||||
@ -759,7 +786,7 @@ export function render({ navigate, route }) {
|
||||
const updatedChatId = normalizeDmChatId(event?.detail?.chatId);
|
||||
if (updatedChatId !== chatId) return;
|
||||
preserveComposerSelection(input, () => {
|
||||
renderLog(log, chatId, { onOpenActions: handleOpenActions });
|
||||
renderLog(log, chatId, { onOpenActions: handleOpenActions, scrollMode: 'latest' });
|
||||
});
|
||||
window.requestAnimationFrame(() => scrollToLatestMessage(log));
|
||||
void sendReadReceiptsForVisible(chatId);
|
||||
@ -771,9 +798,19 @@ export function render({ navigate, route }) {
|
||||
|
||||
wrap.append(log, form);
|
||||
screen.append(wrap);
|
||||
renderLog(log, chatId, { onOpenActions: handleOpenActions });
|
||||
renderLog(log, chatId, {
|
||||
onOpenActions: handleOpenActions,
|
||||
markAsRead: false,
|
||||
scrollMode: hasUnreadIncoming ? 'unread' : 'latest',
|
||||
});
|
||||
if (hasUnreadIncoming) {
|
||||
window.requestAnimationFrame(() => scrollToUnreadSeparator(log));
|
||||
window.setTimeout(() => scrollToUnreadSeparator(log), 180);
|
||||
} else {
|
||||
window.requestAnimationFrame(() => scrollToLatestMessage(log));
|
||||
window.setTimeout(() => scrollToLatestMessage(log), 180);
|
||||
}
|
||||
window.setTimeout(() => markChatRead(chatId), 220);
|
||||
void sendReadReceiptsForVisible(chatId);
|
||||
screen.cleanup = () => {
|
||||
setChatKeyboardOpen(false);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user