import PubNub from 'pubnub' import { ChatMessage } from '../entities/index.js' const MAIN_CHANNEL = 'Channel-Siimee' /** * Provider method holder * We always reference this object so * we don't have to hardcode provider specific * methods for doing chat things. * * This gets overloaded later in the program */ const _providerMethods = { publish: () => console.error('no provider publish method set'), subscribe: () => console.error('no provider subscribe method set'), listen: () => console.error('no provider listen method set'), } /** * Breaking out as much pubnub specific flavor */ const _setPubnubKeys = testing => { let keys = {} if (testing) { keys = testing } else { ;(keys.publishKey = import.meta.env.VITE_PUBNUB_PUBLISH_KEY), (keys.subscribeKey = import.meta.env.VITE_PUBNUB_SUBSCRIBE_KEY) } return keys } const _setupPubnub = async (uuid, testing) => { if (!uuid) return console.error('no pubnub uuid set') const keys = _setPubnubKeys(testing) const pubnubClient = await new PubNub({ publishKey: keys.publishKey, subscribeKey: keys.subscribeKey, logVerbosity: false, uuid, }) // Pass pubnub specific methods to our placeholder obj _providerMethods['publish'] = pubnubClient.publish _providerMethods['subscribe'] = pubnubClient.subscribe _providerMethods['listen'] = pubnubClient.addListener return pubnubClient } /** Singleton that holds all our chat information */ class Chatter { /** * Create our chatter instance * @return {Chatter} our chatter instance object */ constructor() { // Our pubnub instance this.provider = null this.uuid = null this._subscriptions = [MAIN_CHANNEL] this.listeners = { status: async e => { if (e.category !== 'PNConnectedCategory') return }, message: null, // Set manually in chat view presence: this._onPresence, } } get subscriptions() { let truncated = this._subscriptions // Do NOT include the main channel as part of subscriptions const mainChanIndex = truncated.indexOf(MAIN_CHANNEL) if (mainChanIndex > -1) { truncated.splice(mainChanIndex, 1) } return truncated } async _onPresence(e) { console.log('presence :', e) return } setOnMessage(cb) { this.listeners.message = cb } async setup(uuid, groupings, testing = false) { this.uuid = `${uuid}` // Pass api keys in ugle fasion for testing purposes // TODO: refactor this with a mock this.provider = await _setupPubnub(this.uuid, testing) // step_1: build the this.groupings object from the backend // await for the groupings to be fetched before subscribing to channels await this._setupAllChannels(groupings) this._listenFor({ listeners: this.listeners }) this.subscribe({ channels: this._subscriptions }) return this.subscriptions } async getHistory(channel, count = 10) { console.warn('[chatter] grabbing history for channel:', channel) let pastMessages = null try { pastMessages = await this.provider.fetchMessages({ channels: [channel], count: count, includeMessageType: true, includeUUID: true, includeMeta: true, includeMessageActions: false, }) } catch (error) { console.error('[chatter]', error) } const channelHistory = pastMessages && pastMessages.channels ? pastMessages.channels[channel] : null console.log('channelHistory :>> ', channelHistory) return channelHistory ? channelHistory.map(msg => ({ publisher: msg.uuid, message: new ChatMessage(msg.message.description), timetoken: msg.timetoken, })) : [] } /** * Send a message to a channel * example = new ChatMessage({ title: 'example', description: 'ni' }) * Facade so we can hide provider specific methods * @param {string} channel * @param {ChatMessage} message * @return {object} timestamp */ async publish(channel, message) { return _providerMethods.publish({ channel, message: new ChatMessage(message), }) } /** * Subscribe to a channels * Facade so we can hide provider specific methods * @param {array} channels grouping name */ subscribe({ channels }) { _providerMethods.subscribe({ channels }) } /** * Listen to events and set callbacks * Facade so we can hide provider specific methods */ _listenFor({ listeners }) { _providerMethods.listen(listeners) } /** * Get all groupings for this profile and * then store them as subscriptions */ async _setupAllChannels(groupings) { groupings.forEach(grouping => { this._subscriptions.push(grouping.grouping_name) }) } stop() { this._subscriptions = [MAIN_CHANNEL] console.warn('chatter stop not implemented') } } export { Chatter, MAIN_CHANNEL }