import { Injectable } from '@angular/core'; import { RoomService } from './room.service'; import { RochetChatConnectorService } from 'src/app/services/chat/rochet-chat-connector.service'; import { MessageService } from 'src/app/services/chat/message.service'; import { SessionStore } from 'src/app/store/session.service'; import { capitalizeTxt } from 'src/plugin/text'; import { Update as room } from 'src/app/models/chatMethod'; import { Storage } from '@ionic/storage'; import { Platform } from '@ionic/angular'; import { SqliteService } from 'src/app/services/sqlite.service'; import { ChatService } from 'src/app/services/chat.service'; import { NativeNotificationService } from 'src/app/services/native-notification.service'; import { SortService } from '../functions/sort.service'; import { chatUser } from 'src/app/models/chatMethod'; import { NfService } from 'src/app/services/chat/nf.service' import { ChangeProfileService } from '../change-profile.service'; import { ChatMethodsService } from './chat-methods.service'; import { AESEncrypt } from '../aesencrypt.service' import { AttachmentsService } from 'src/app/services/attachments.service'; import { NetworkServiceService } from 'src/app/services/network-service.service'; import { ViewedMessageService } from './viewed-message.service' import { NotificationsService } from '../notifications.service'; import { Subscribe } from '../subcribe'; import { Plugins } from '@capacitor/core'; const { App } = Plugins; @Injectable({ providedIn: 'root' }) export class ChatSystemService { dm: { [key: string]: RoomService } = {} group: { [key: string]: RoomService } = {} _dm: RoomService[] = [] _group: RoomService[] = [] loadingWholeList = false; dmCount = 0; groupCount = 0; currentRoom: RoomService = null users: chatUser[] = [] sessionStore = SessionStore delete = [] loadingUsers = false onRoomsLoad = new Subscribe({ execute: false, deleteOnExecute: true }) constructor( private RochetChatConnectorService: RochetChatConnectorService, private storage: Storage, private platform: Platform, private sqlservice: SqliteService, private NativeNotificationService: NativeNotificationService, private sortService: SortService, private ChatService: ChatService, private NfService: NfService, private changeProfileService: ChangeProfileService, private chatService: ChatService, private ChatMethodsService: ChatMethodsService, private AESEncrypt: AESEncrypt, private AttachmentsService: AttachmentsService, private NetworkServiceService: NetworkServiceService, private ViewedMessageService: ViewedMessageService, private notificationService: NotificationsService ) { this.RochetChatConnectorService.registerCallback({ type: 'reConnect', funx: async () => { /** * @description when the phone is in the background for a long time it could disconnects from the socket then the socket reconnects automatically, * when the connection is lost the subscribe is also lost, so we have to subscribe again when reconnection is establish. */ this.RochetChatConnectorService.setStatus('online') this.getUserStatus(); await this.chatService.refreshtoken(); this.getUser(); this.getAllRooms(); this.subscribeToRoom(); // if (this.currentRoom) { this.currentRoom.loadHistory({ forceUpdate: true }) } for (const id in this.dm) { this.dm[id].hasLoadHistory = false } for (const id in this.group) { this.group[id].hasLoadHistory = false } } }) if (this.sessionStore.user.Inactivity) { this.loadChat(); } if (SessionStore.user?.ChatData?.data) { this.restoreRooms(); } document.addEventListener('resume', () => { this.RochetChatConnectorService.setStatus('online') if (this._dm?.length == 0 && this._group?.length == 0) { if (SessionStore.user?.ChatData?.data) { this.getAllRooms(); } } }); try { if (!this.platform.is('desktop')) { App.addListener('appStateChange', ({ isActive }) => { if (isActive) { // The app is in the foreground. console.log('App is in the foreground'); if (SessionStore.user?.ChatData?.data) { this.currentRoom?.loadHistory({ forceUpdate: true }) } setTimeout(() => { if (SessionStore.user?.ChatData?.data) { this.subscribeToRoom() this.RochetChatConnectorService.setStatus('online') } }, 1000); /* this.reloadComponent(true) */ } else { // The app is in the background. console.log('App is in the background'); // You can perform actions specific to the background state here. } }); } } catch(error) {} } loadChat() { if (SessionStore.user?.ChatData?.data) { this.ReLoadChat() } } private async ReLoadChat() { if (SessionStore.user?.ChatData?.data) { this.getUserStatus(); await this.chatService.refreshtoken(); this.restoreUsers(); await this.getUser(); await this.restoreRooms(); await this.getAllRooms(); this.subscribeToRoom(); } // } clearChat() { this.dm = {} this.group = {} this._dm = [] this._group = [] this.loadingWholeList = false; this.dmCount = 0; this.groupCount = 0; this.currentRoom = null this.users = [] this.storage.remove('Users'); } openRoom(roomId) { if (this.currentRoom) { this.currentRoom.roomLeave() } if (this.getDmRoom(roomId)) { this.currentRoom = this.getDmRoom(roomId) } else if (this.getGroupRoom(roomId)) { this.currentRoom = this.getGroupRoom(roomId) } this.currentRoom.open() } getRoomById(roomId) { if (this.getDmRoom(roomId)) { return this.getDmRoom(roomId) } else if (this.getGroupRoom(roomId)) { return this.getGroupRoom(roomId) } } async restoreRooms() { try { const _rooms = await this.storage.get('Rooms'); if (_rooms) { for (let roomData of this.sortArrayISODate(_rooms)) { await this.prepareRoom(roomData); } } } catch (e) { } } async restoreUsers() { const users = await this.storage.get('Users'); if (users) { this.users = users } } sortArrayISODate(messages: any): any[] { return messages.sort((a, b) => new Date(b._updatedAt).getTime() - new Date(a._updatedAt).getTime()) } async getAllRooms(callback: Function = () => { }, roomIdCallback = "") { this.loadingWholeList = true var rooms; if (this.RochetChatConnectorService.isLogin) { try { rooms = await this.RochetChatConnectorService.getRooms(); } catch (error) { this.loadingWholeList = false console.error('chatgetrooms', error) } try { await this.storage.remove('Rooms'); } catch (e) { } } let index = 0 let _rooms = rooms?.result?.update if (_rooms) { _rooms = _rooms.map(e => { e["_updatedAt"] = e._updatedAt || e._updatedAt['$date'] return e }) for (let roomData of this.sortArrayISODate(_rooms)) { const roomId = this.getRoomId(roomData); if (roomData.t == 'd') { await this.prepareRoom(roomData); } else { if (roomData.t === 'p') { await this.prepareRoom(roomData); } else { await this.prepareRoom(roomData); } } if (roomId == roomIdCallback) { callback() } index++; } } this.loadingWholeList = false this.sortRoomList() await this.storage.set('Rooms', _rooms); this.onRoomsLoad.executor() } async getRoom(_rooms: any) { this.loadingWholeList = true let index = 0 if (_rooms) { _rooms = _rooms.map(e => { e["_updatedAt"] = e._updatedAt || e._updatedAt['$date'] return e }) for (let roomData of this.sortArrayISODate(_rooms)) { if (roomData.t == 'd') { await this.prepareRoom(roomData); } else { if (roomData.t === 'p') { await this.prepareRoom(roomData); } else { await this.prepareRoom(roomData); } } index++; } } this.loadingWholeList = false this.sortRoomList() this.onRoomsLoad.executor() } /** * @description sort room list by last message date */ sortRoomList = () => { this._dm = this.sortService.sortDate(this._dm, '_updatedAt').reverse() this._group = this.sortService.sortDate(this._group, '_updatedAt').reverse() } /** * @description subscribe all room */ subscribeToRoom() { if (SessionStore.user?.ChatData?.data) { for (const id in this.dm) { this.defaultSubtribe(id) } for (const id in this.group) { this.defaultSubtribe(id) } this.RochetChatConnectorService.streamNotifyLogged().then((subscription => { })) this.RochetChatConnectorService.subStreamMessageUser().then((subscription => { console.log({subscription}) })) } else { setTimeout(() => { throw ('No chat data'); }, 1000) } } /** * @description when a new room is create, needs to subtribe in order to receive updates * @param id * @param roomData */ subscribeToRoomUpdate(id, roomData) { this.defaultSubtribe(id); this.prepareRoom(roomData); this.getGroupRoom(id).loadHistory({}); } /** * @deprecated things a room need to subscribe on * @param id room id */ private defaultSubtribe(id: any) { const room = this.getRoomById(id); if (!room.subscribeAttempt) { try { room.subscribeAttempt = true; } catch (error) { console.log("error") } this.RochetChatConnectorService.streamRoomMessages(id).then((subscription) => { room.status.receive.message = true; }) this.RochetChatConnectorService.subStreamNotifyRoom(id, 'typing', false).then((subscription) => { room.status.receive.typing = true; // }) this.RochetChatConnectorService.subStreamNotifyRoom(id, 'readMessage', false).then((subscription) => { room.status.receive.readMessage = true; }) this.RochetChatConnectorService.streamNotifyRoomDeleteMessage(id).then((subscription) => { room.status.receive.deleteMessage = true; }) } } private fix_updatedAt(message) { if (message.result) { message.result._updatedAt = message.result._updatedAt['$date'] } else if (message._updatedAt) { if (message._updatedAt.hasOwnProperty('$date')) { message._updatedAt = message._updatedAt['$date'] } } return message } /** * @description create a representation of an room in these instance this.dm, this.group ... * @param roomData */ prepareRoom(roomData) { /** * @description data used to define or create room */ roomData = this.fix_updatedAt(roomData) const setData = { customFields: roomData.customFields, id: this.getRoomId(roomData), name: this.getRoomName(roomData), t: roomData.t, lastMessage: this.getRoomLastMessage(roomData), _updatedAt: new Date(roomData._updatedAt || roomData._updatedAt['$date']), u: roomData.u || {}, members: [], membersExcludeMe: [] } let roomId = this.getRoomId(roomData); let chat = false if (roomData?.usernames) { if (roomData?.usernames?.includes("chat.admin")) { chat = true } } if (setData.name != 'Rocket Cat' && setData.name != 'general' && chat == false) { // create room if (!this.roomExist(roomId)) { let room: RoomService = new RoomService(this.RochetChatConnectorService, new MessageService(this.NfService, this.RochetChatConnectorService, this.ChatMethodsService, this.AESEncrypt, this.AttachmentsService, this.NetworkServiceService, this, this.notificationService), this.storage, this.platform, this.sqlservice, this.NativeNotificationService, this.sortService, this.ChatService, this.NfService, this.ChatMethodsService, this.AESEncrypt, this.AttachmentsService, this.NetworkServiceService, this, this.ViewedMessageService, this.notificationService) room.setData(setData) room.receiveMessage() room.getAllUsers = this.getUsers room.receiveMessageDelete(); room.sortRoomList = this.sortRoomList room.chatServiceDeleteRoom = this.deleteRoom room.isGroup = !this.isIndividual(roomData) room.info() // create individual room if (this.isIndividual(roomData)) { this.dm[roomId] = room this._dm.push(room) this.dmCount++ } else { // create group room this.group[roomId] = room this._group.push(room) this.groupCount++ } this.defaultSubtribe(roomId) } else { // in this case room is already present, therefor it will only be necessary, // to redefine if (this.dm[roomId]) { this.dm[roomId].setData(setData) } else if (this.group[roomId]) { this.group[roomId].setData(setData) } } } } deleteRoom = (roomId) => { this.delete.push(roomId) delete this.group[roomId]; this._group = this._group.filter((e) => e.id != roomId); } deleteRecently(roomId) { return this.delete.includes(roomId) } roomExist(roomId) { return this.dm[roomId]?.id || this.group[roomId]?.id } getReceptorName(roomData) { try { return roomData.usernames.find((e) => e != SessionStore.user.UserName) } catch (e) { return '*' } } /** * @description update user status. this method is called once only * @param id user ID */ private getUserStatus(id?: string) { this.RochetChatConnectorService.getUserStatus(async (d) => { const userId = d.fields.args[0][0] const username = d.fields.args[0][1] let statusNum = d.fields.args[0][2] const statusText = this.statusNumberToText(statusNum) for (const user in this.users) { if (this.users[user]._id == userId) { this.users[user].status = statusText } } this.getUser() }) } getUserByName(username) { return this.users.find((user) => user.username == username) } /** * @description convert rocketchat statues num to readable string * @param text * @returns */ statusNumberToText(text) { if (text == '0') { return "offline" } else if (text == '1') { return "online" } else if (text == '2') { return "away" } else if (text == '3') { return "busy" } } deleteMessage(id?) { return this.RochetChatConnectorService.deleteMessage(id); } leaveRoom(id?) { return this.RochetChatConnectorService.leaveRoom(id); } async hideRoom(id) { this._dm.forEach((md, index) => { if (md.id == id) { this._dm.splice(index, 1) delete this.dm[id] } }); this._group.forEach((group, index) => { if (group.id == id) { this._group.splice(index, 1) delete this.group[id] } }) } hidingRoom(id?) { return this.RochetChatConnectorService.hidingRoom(id).then(() => { // this.hideRoom(id) }) } addRoomOwner(roomid, userId) { return this.RochetChatConnectorService.addRoomOwner(roomid, userId); } createPrivateRoom(groupName, username, customFields) { return this.RochetChatConnectorService.createPrivateRoom(groupName, username, customFields); } getDmRoom(id): RoomService { try { return this.dm[id] } catch (e) { } } getGroupRoom(id): RoomService { try { return this.group[id] } catch (e) { } } getRoomName(roomData: room): string { if (this.isIndividual(roomData)) { const names: String[] = roomData.usernames if (roomData.t == 'd') { const username = roomData.usernames.find(e => e != SessionStore.user.UserName) try { const firstName = capitalizeTxt(username.split('.')[0]) const lastName = capitalizeTxt(username.split('.')[1]) return firstName + ' ' + lastName } catch (error) { const username = roomData.usernames.find(e => e != SessionStore.user.UserName) const firstName = capitalizeTxt(username.split('.')[0]) return firstName } } else { if (roomData.t === 'p') { return 'Loading' } else { return 'Loading' } } } else { return roomData.fname } } getRoomId(roomData: room) { return roomData._id } getRoomLastMessage(roomData: room): any { return roomData.lastMessage } private isIndividual(roomData: room) { return !roomData.fname } getUsers = () => { return this.users } async getUser() { this.loadingUsers = true let _res try { _res = await this.ChatService.getAllUsers().toPromise(); } catch (error) { await this.chatService.refreshtoken(); _res = await this.ChatService.getAllUsers().toPromise(); } let users: chatUser[] = _res['users'].filter(data => data.username != SessionStore.user.UserName); users = users.filter((data) => !(data.name == 'Rocket.Cat' || data.name == 'Administrator')) const userIds = this.users.map((user) => user._id) for (let UserUpdate of users) { if (userIds.includes(UserUpdate._id)) { for (var index = 0; index < this.users.length; index++) { if (UserUpdate._id == this.users[index]._id) { this.users[index] = UserUpdate } } } else { this.users.push(UserUpdate) } } this.users = this.users.sort((a, b) => { if (a.name < b.name) { return -1; } if (a.name > b.name) { return 1; } return 0; }); await this.storage.set('Users', this.users); this.loadingUsers = false } getUserOfRoom(roomId) { return this.RochetChatConnectorService.getUserOfRoom(roomId); } async createGroup(name) { const res: any = await this.createPrivateRoom(name, SessionStore.user.UserName, {}); if (res?.result?.rid) { try { await this.getAllRooms(); } catch (e) { } return res } else { return res } } async createGroup__(name, customFields = {}) { const res: any = await this.createPrivateRoom(name, SessionStore.user.UserName, customFields); console.log('room is created', res) if (res?.result?.rid) { try { await this.getAllRooms(); return res } catch (e) { } console.log('room is loaded') return res } else { return res } } getGroupByName(name) { return this._group.find(e => e.name == name) } async waitRoomToCreate(rid): Promise { return new Promise(async (resolve, reject) => { let sub; sub = this.onRoomsLoad.subscribe(() => { const room = this.getRoomById(rid) if (room) { resolve(room) sub.unSubscribe() } }) try { await this.getAllRooms(); } catch (e) { } }) } searchContact(name, username) { return this.users.find(e => e.name == name || e.username == username) } }