213 lines
7.6 KiB
JavaScript
213 lines
7.6 KiB
JavaScript
/**
|
||
* @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');
|