feat: Smart button visibility and load-to-present on click
Two types of bottom: 1. Historical bottom (hasMoreAfter=true): - Button ALWAYS visible (even at scroll bottom) - Click → loads latest 50 messages to present time - Renders and scrolls to bottom 2. Present bottom (hasMoreAfter=false): - Button hidden when isNearBottom() - Click → smart scroll (to unread or bottom) Changes: - _setupSmartButtonVisibility() monitors hasMoreAfter - _handleScrollButtonClick() loads history if hasMoreAfter - Added public loadLatest() method - Emits 'chat:messagesLoaded' event for UI updates Result: - Historical view: button always shows, loads to present - Present view: button hides at bottom, smart unread scroll - No more stuck in history without way to present
This commit is contained in:
parent
3ba512a937
commit
1dd01ae2c1
@ -472,13 +472,13 @@ export class ChatControllerV2 {
|
||||
_handleWsNewMessage(detail) {
|
||||
const { message, chatId } = detail;
|
||||
if (chatId === this.chatId && message) {
|
||||
this.loader.handleNewMessage(message);
|
||||
|
||||
// 🎯 УМНЫЙ АВТОСКРОЛЛ: только если мы в "настоящем времени"
|
||||
const boundaries = this.loader.chatBoundaries.get(chatId);
|
||||
const isInPresentTime = boundaries && !boundaries.hasMoreAfter;
|
||||
|
||||
if (isInPresentTime) {
|
||||
// ✅ Настоящее время (hasMoreAfter=false) - обрабатываем как обычно
|
||||
this.loader.handleNewMessage(message);
|
||||
|
||||
const isNearBottom = this.scrollManager.isNearBottom();
|
||||
|
||||
if (isNearBottom) {
|
||||
@ -491,8 +491,12 @@ export class ChatControllerV2 {
|
||||
// ✅ Пользователь НЕ внизу → показываем badge
|
||||
this._incrementUnreadBadge();
|
||||
}
|
||||
} else {
|
||||
// ❌ Исторический просмотр (hasMoreAfter=true) - НЕ рендерим, только badge
|
||||
// Не добавляем в Store (не рендерим), только показываем что есть новые
|
||||
this._incrementUnreadBadge();
|
||||
console.log('[ChatControllerV2] New message in historical view - showing badge only');
|
||||
}
|
||||
// Если hasMoreAfter = true (исторический низ) - ничего не делаем
|
||||
}
|
||||
}
|
||||
|
||||
@ -677,16 +681,80 @@ export class ChatControllerV2 {
|
||||
|
||||
this._scrollBtn.addEventListener('click', () => this._handleScrollButtonClick());
|
||||
|
||||
// Настраиваем умную видимость
|
||||
this._setupSmartButtonVisibility();
|
||||
|
||||
console.log('[ChatControllerV2] Smart scroll button initialized');
|
||||
}
|
||||
|
||||
/**
|
||||
* Настраивает умную видимость кнопки
|
||||
* @private
|
||||
*/
|
||||
_setupSmartButtonVisibility() {
|
||||
if (!this._scrollBtn) return;
|
||||
|
||||
// Обновляем видимость при скролле
|
||||
const updateVisibility = () => {
|
||||
const boundaries = this.loader.chatBoundaries.get(this.chatId);
|
||||
const hasMoreAfter = boundaries?.hasMoreAfter || false;
|
||||
const isNearBottom = this.scrollManager.isNearBottom();
|
||||
|
||||
// Показываем кнопку если:
|
||||
// 1. Пользователь НЕ внизу (любой случай)
|
||||
// 2. ИЛИ hasMoreAfter=true (даже если внизу)
|
||||
const shouldShow = !isNearBottom || hasMoreAfter;
|
||||
|
||||
this._scrollBtn.classList.toggle('show', shouldShow);
|
||||
};
|
||||
|
||||
// Обновляем при скролле
|
||||
this.scrollElement.addEventListener('scroll', updateVisibility);
|
||||
|
||||
// Обновляем при загрузке новых сообщений
|
||||
window.addEventListener('chat:messagesLoaded', updateVisibility);
|
||||
|
||||
// Начальное обновление
|
||||
setTimeout(updateVisibility, 100);
|
||||
}
|
||||
|
||||
/**
|
||||
* Обработчик клика на умную кнопку
|
||||
* @private
|
||||
*/
|
||||
_handleScrollButtonClick() {
|
||||
async _handleScrollButtonClick() {
|
||||
const boundaries = this.loader.chatBoundaries.get(this.chatId);
|
||||
const isInPresentTime = boundaries && !boundaries.hasMoreAfter;
|
||||
const hasMoreAfter = boundaries?.hasMoreAfter || false;
|
||||
|
||||
if (hasMoreAfter) {
|
||||
// 📥 ИСТОРИЧЕСКИЙ НИЗ: Загружаем всю историю до конца
|
||||
console.log('[ChatControllerV2] Loading history to present time...');
|
||||
|
||||
try {
|
||||
// Загружаем последние сообщения (настоящее время)
|
||||
const result = await this.loader.loadLatest(this.chatId, 50);
|
||||
|
||||
// Рендерим новые сообщения
|
||||
await this.renderer.render(this.chatId);
|
||||
|
||||
// Скроллим в низ
|
||||
await new Promise(resolve => requestAnimationFrame(resolve));
|
||||
this.scrollElement.scrollTop = this.scrollElement.scrollHeight;
|
||||
|
||||
console.log('[ChatControllerV2] Loaded to present, messages:', result.messages.length);
|
||||
} catch (error) {
|
||||
console.error('[ChatControllerV2] Failed to load history:', error);
|
||||
// Если ошибка - просто скроллим в низ загруженного
|
||||
this.scrollElement.scrollTop = this.scrollElement.scrollHeight;
|
||||
}
|
||||
|
||||
// Сбрасываем badge
|
||||
this._resetUnreadBadge();
|
||||
return;
|
||||
}
|
||||
|
||||
// 🔵 НАСТОЯЩИЙ НИЗ: Умная логика
|
||||
const isInPresentTime = !hasMoreAfter;
|
||||
|
||||
if (this._unreadCount > 0 && this.lastReadMessageId && isInPresentTime) {
|
||||
// Режим "Перейти к непрочитанным"
|
||||
|
||||
@ -241,9 +241,37 @@ export class MessageLoaderV2 {
|
||||
* Загружает новые сообщения (после текущего newest)
|
||||
* Используется для прокрутки вниз после перехода по дате (как в Telegram)
|
||||
* @param {number} chatId - ID чата
|
||||
* @param {number} [limit] - Количество сообщений (опционально)
|
||||
* @returns {Promise<Array<Message>>}
|
||||
*/
|
||||
async loadNewer(chatId) {
|
||||
async loadNewer(chatId, limit = null) {
|
||||
|
||||
/**
|
||||
* Загружает последние сообщения (самые свежие в чате)
|
||||
* Используется для перехода к настоящему времени из исторического просмотра
|
||||
* @param {number} chatId - ID чата
|
||||
* @param {number} [limit] - Количество сообщений
|
||||
* @returns {Promise<LoadResult>}
|
||||
*/
|
||||
async loadLatest(chatId, limit = 50) {
|
||||
const requestKey = `latest_${chatId}`;
|
||||
|
||||
console.log('[MessageLoaderV2] Loading latest messages to present time');
|
||||
|
||||
try {
|
||||
const result = await this._loadLatest(chatId, limit, requestKey);
|
||||
|
||||
// Эмитим событие
|
||||
window.dispatchEvent(new CustomEvent('chat:messagesLoaded', {
|
||||
detail: { chatId, count: result.messages.length, type: 'latest' }
|
||||
}));
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
console.error('[MessageLoaderV2] Failed to load latest:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
const requestKey = `newer_${chatId}`;
|
||||
|
||||
const state = this._getLoadingState(chatId);
|
||||
@ -277,7 +305,7 @@ export class MessageLoaderV2 {
|
||||
const result = await this._fetchWithRetry(
|
||||
buildUrl(API_ENDPOINTS.MESSAGES(chatId), {
|
||||
after_id: boundaries.newestId,
|
||||
limit: this.config.HISTORY_LIMIT
|
||||
limit: limit || this.config.HISTORY_LIMIT
|
||||
}),
|
||||
requestKey
|
||||
);
|
||||
@ -302,6 +330,11 @@ export class MessageLoaderV2 {
|
||||
this._updateBoundaries(chatId, { hasMoreAfter: false });
|
||||
}
|
||||
|
||||
// Эмитим событие для обновления UI
|
||||
window.dispatchEvent(new CustomEvent('chat:messagesLoaded', {
|
||||
detail: { chatId, count: messages.length, type: 'newer' }
|
||||
}));
|
||||
|
||||
return messages;
|
||||
|
||||
} catch (error) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user