import { cloneDeep, isNumber, uniq } from 'lodash'; import { ConversationCollection } from '../models/conversation'; import { OpenGroupRequestCommonType } from '../session/apis/open_group_api/opengroupV2/ApiUtil'; import { isOpenGroupV2 } from '../session/apis/open_group_api/utils/OpenGroupUtils'; import { channels } from './channels'; export type OpenGroupV2RoomWithImageID = { serverUrl: string; /** this is actually shared for all this server's room */ serverPublicKey: string; roomId: string; /** the fileId to the group room's image */ imageID?: string; }; export type OpenGroupV2Room = { serverUrl: string; /** this is actually shared for all this server's room */ serverPublicKey: string; roomId: string; /** a user displayed name */ roomName?: string; /** the fileId of the group room's image */ imageID?: string; /** the linked ConversationModel.id */ conversationId?: string; maxMessageFetchedSeqNo?: number; lastInboxIdFetched?: number; lastOutboxIdFetched?: number; /** * This value is set with the current timestamp whenever we get new messages. */ lastFetchTimestamp?: number; /** * This is shared across all rooms in a server. */ capabilities?: Array; }; export const OpenGroupData = { getAllV2OpenGroupRoomsMap, opengroupRoomsLoad, getV2OpenGroupRoom, getV2OpenGroupRoomsByServerUrl, saveV2OpenGroupRoom, saveV2OpenGroupRooms, getV2OpenGroupRoomByRoomId, removeV2OpenGroupRoom, getAllOpenGroupV2Conversations, getAllOpengroupsServerPubkeys, getAllV2OpenGroupRooms, }; /** * * @returns a map containing as key the conversationId of the opengroup room and as value the OpenGroupV2Room details */ function getAllV2OpenGroupRoomsMap(): Map | undefined { const results = new Map(); throwIfNotLoaded().forEach(o => { if (o.conversationId) { results.set(o.conversationId, cloneDeep(o)); } }); return results; } // this is just to make testing and stubbing easier async function getAllV2OpenGroupRooms() { return channels.getAllV2OpenGroupRooms(); } // avoid doing fetches and write too often from the db by using a cache on the renderer side. let cachedRooms: Array | null = null; async function opengroupRoomsLoad() { if (cachedRooms !== null) { return; } const loadedFromDB = (await OpenGroupData.getAllV2OpenGroupRooms()) as | Array | undefined; if (loadedFromDB) { cachedRooms = new Array(); loadedFromDB.forEach(r => { try { cachedRooms?.push(r as any); } catch (e) { window.log.warn(e.message); } }); return; } cachedRooms = []; } function throwIfNotLoaded() { if (cachedRooms === null) { throw new Error('opengroupRoomsLoad must be called first'); } return cachedRooms; } function getV2OpenGroupRoom(conversationId: string): OpenGroupV2Room | undefined { if (!isOpenGroupV2(conversationId)) { throw new Error(`getV2OpenGroupRoom: this is not a valid v2 id: ${conversationId}`); } const found = throwIfNotLoaded().find(m => m.conversationId === conversationId); return (found && cloneDeep(found)) || undefined; } function getV2OpenGroupRoomsByServerUrl(serverUrl: string): Array | undefined { const found = throwIfNotLoaded().filter(m => m.serverUrl === serverUrl); return (found && cloneDeep(found)) || undefined; } function getV2OpenGroupRoomByRoomId( roomInfos: OpenGroupRequestCommonType ): OpenGroupV2Room | undefined { const found = throwIfNotLoaded().find( m => m.roomId === roomInfos.roomId && m.serverUrl === roomInfos.serverUrl ); return (found && cloneDeep(found)) || undefined; } async function saveV2OpenGroupRooms(rooms: Array): Promise { await Promise.all(rooms.map(saveV2OpenGroupRoom)); } async function saveV2OpenGroupRoom(room: OpenGroupV2Room): Promise { if (!room.conversationId || !room.roomId || !room.serverUrl || !room.serverPublicKey) { throw new Error('Cannot save v2 room, invalid data'); } const found = (room.conversationId && throwIfNotLoaded().find(m => m.conversationId === room.conversationId)) || undefined; if (!found) { await channels.saveV2OpenGroupRoom(room); throwIfNotLoaded().push(cloneDeep(room)); return; } // because isEqual is funky with pointer being changed, we have to do this for now if (JSON.stringify(room) !== JSON.stringify(found)) { await channels.saveV2OpenGroupRoom(room); const foundIndex = room.conversationId && throwIfNotLoaded().findIndex(m => m.conversationId === room.conversationId); if (isNumber(foundIndex) && foundIndex > -1) { throwIfNotLoaded()[foundIndex] = cloneDeep(room); } return; } } async function removeV2OpenGroupRoom(conversationId: string): Promise { await channels.removeV2OpenGroupRoom(conversationId); const foundIndex = conversationId && throwIfNotLoaded().findIndex(m => m.conversationId === conversationId); if (isNumber(foundIndex) && foundIndex > -1) { throwIfNotLoaded().splice(foundIndex, 1); } } async function getAllOpenGroupV2Conversations(): Promise { const conversations = await channels.getAllOpenGroupV2Conversations(); const collection = new ConversationCollection(); collection.add(conversations); return collection; } function getAllOpengroupsServerPubkeys(): Array { return uniq(throwIfNotLoaded().map(room => room.serverPublicKey)) || []; }