Corp/backend/static/js/utils/chatMigration.js

213 lines
7.6 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* @fileoverview Миграционный слой для перехода с ChatController на ChatControllerV2
* @module utils/chatMigration
*/
import { ChatControllerV2 } from '../controllers/chatControllerV2.js';
import { ChatController } from '../controllers/chatController.js';
/**
* Мигрирует существующий экземпляр ChatController на V2
* @param {ChatController} oldController - Старый контроллер
* @returns {ChatControllerV2} Новый контроллер V2
*/
export function migrateToV2(oldController) {
if (!oldController) {
throw new Error('[Migration] oldController is required');
}
console.log('[Migration] Starting migration to V2...');
// Извлекаем состояние из старого контроллера
const state = {
chatId: oldController.chatId,
currentUserId: oldController.currentUserId,
scrollElement: oldController.scrollElement,
containerId: oldController.containerId,
lastReadMessageId: oldController.lastReadTimestamp // V2 использует ID вместо timestamp
};
// Сохраняем состояние скролла
const scrollPosition = oldController.scrollElement.scrollTop;
const scrollHeight = oldController.scrollElement.scrollHeight;
const wasAtBottom = oldController.scrollManager?.isNearBottom() || false;
// Уничтожаем старый контроллер
oldController.destroy();
console.log('[Migration] Old controller destroyed');
// Создаем новый контроллер V2
const v2Controller = new ChatControllerV2(state);
console.log('[Migration] V2 controller created');
// Восстанавливаем позицию скролла после инициализации
v2Controller.init().then(() => {
if (wasAtBottom) {
v2Controller.scrollManager.scrollToBottom({ instant: true });
} else {
// Пытаемся восстановить относительную позицию
const relativePosition = scrollPosition / scrollHeight;
const newScrollTop = v2Controller.scrollElement.scrollHeight * relativePosition;
v2Controller.scrollElement.scrollTop = newScrollTop;
}
console.log('[Migration] ✅ Migration complete!');
});
return v2Controller;
}
/**
* Преобразует старые опции в новый формат
* @param {Object} legacyOptions - Опции в старом формате
* @returns {Object} Опции в формате V2
*/
export function migrateOptions(legacyOptions) {
const v2Options = { ...legacyOptions };
// Преобразуем lastReadTimestamp → lastReadMessageId
if (legacyOptions.lastReadTimestamp && !legacyOptions.lastReadMessageId) {
// В V2 используется ID, если у нас только timestamp - игнорируем
v2Options.lastReadMessageId = null;
}
// Удаляем устаревшие поля
delete v2Options.lastReadTimestamp;
return v2Options;
}
/**
* Compatibility wrapper - позволяет использовать V2 как старый ChatController
* Для постепенной миграции кода
*/
export class ChatControllerCompat extends ChatControllerV2 {
constructor(options) {
// Преобразуем опции
const v2Options = migrateOptions(options);
super(v2Options);
// Добавляем алиасы для обратной совместимости
this.scrollEl = this.scrollElement; // Старый алиас
this.initialized = false; // Публичный флаг вместо _initialized
this.isInitializing = false; // Публичный флаг вместо _initializing
// Проксируем приватные свойства для старого кода
Object.defineProperty(this, 'newMessagesCount', {
get: () => this._newMessagesCount,
set: (value) => { this._newMessagesCount = value; }
});
Object.defineProperty(this, 'newMessagesBtn', {
get: () => this._newMessagesBtn,
set: (value) => { this._newMessagesBtn = value; }
});
console.log('[ChatControllerCompat] Created with compatibility layer');
}
/**
* Переопределяем init() для обновления публичных флагов
*/
async init() {
this.isInitializing = true;
try {
await super.init();
this.initialized = true;
this.isInitializing = false;
} catch (error) {
this.isInitializing = false;
throw error;
}
}
/**
* Добавляем методы для обратной совместимости
*/
getStatus() {
return {
chatId: this.chatId,
initialized: this.initialized,
messageCount: this.store.messages.size,
isLoading: false, // V2 не имеет централизованного isLoading
hasMoreHistory: true, // V2 всегда может попробовать загрузить
scroll: {
isNearBottom: this.scrollManager.isNearBottom(),
scrollTop: this.scrollElement.scrollTop,
scrollHeight: this.scrollElement.scrollHeight
},
store: {
totalMessages: this.store.messages.size,
chats: this.store.chatIndex.size
}
};
}
/**
* Алиас для совместимости
*/
_dispatchEvent(eventName, detail) {
this._emit(eventName, detail);
}
}
/**
* Проверяет, является ли контроллер V2
* @param {Object} controller - Контроллер для проверки
* @returns {boolean}
*/
export function isV2Controller(controller) {
return controller instanceof ChatControllerV2;
}
/**
* Проверяет, является ли контроллер legacy (старым)
* @param {Object} controller - Контроллер для проверки
* @returns {boolean}
*/
export function isLegacyController(controller) {
return controller instanceof ChatController && !(controller instanceof ChatControllerV2);
}
/**
* Получает версию контроллера
* @param {Object} controller - Контроллер
* @returns {string} 'v2' | 'legacy' | 'unknown'
*/
export function getControllerVersion(controller) {
if (isV2Controller(controller)) return 'v2';
if (isLegacyController(controller)) return 'legacy';
return 'unknown';
}
/**
* Создает правильный контроллер в зависимости от флага миграции
* @param {Object} options - Опции создания
* @param {boolean} [options.useV2=false] - Использовать V2
* @returns {ChatController|ChatControllerV2}
*/
export function createChatController(options) {
const useV2 = options.useV2 || false;
delete options.useV2;
if (useV2) {
console.log('[Factory] Creating V2 controller');
return new ChatControllerV2(migrateOptions(options));
} else {
console.log('[Factory] Creating legacy controller');
return new ChatController(options);
}
}
// Экспорты
export default {
migrateToV2,
migrateOptions,
ChatControllerCompat,
isV2Controller,
isLegacyController,
getControllerVersion,
createChatController
};
console.log('✅ Chat migration utilities loaded');