diff --git a/services/gunDB/Mediator/index.js b/services/gunDB/Mediator/index.js index abce3c6f..e213643d 100644 --- a/services/gunDB/Mediator/index.js +++ b/services/gunDB/Mediator/index.js @@ -441,27 +441,17 @@ class Mediator { this.socket.on(IS_GUN_AUTH, this.isGunAuth) - this.socket.on('SET_LAST_SEEN_APP', async body => { + this.socket.on(Action.SET_LAST_SEEN_APP, async body => { try { await throwOnInvalidToken(body.token) - await new Promise((res, rej) => { - getUser() - .get('lastSeenApp') - .put(Date.now(), ack => { - if (ack.err) { - rej(new Error(ack.err)) - } else { - res() - } - }) - }) - this.socket.emit('SET_LAST_SEEN_APP', { + await API.Actions.setLastSeenApp() + this.socket.emit(Action.SET_LAST_SEEN_APP, { ok: true, msg: null, origBody: body }) } catch (e) { - this.socket.emit('SET_LAST_SEEN_APP', { + this.socket.emit(Action.SET_LAST_SEEN_APP, { ok: false, msg: e.message, origBody: body diff --git a/services/gunDB/action-constants.js b/services/gunDB/action-constants.js index bcb52aed..4056ffec 100644 --- a/services/gunDB/action-constants.js +++ b/services/gunDB/action-constants.js @@ -9,7 +9,8 @@ const Actions = { SET_AVATAR: "SET_AVATAR", SET_DISPLAY_NAME: "SET_DISPLAY_NAME", SET_BIO: "SET_BIO", - DISCONNECT: "DISCONNECT" + DISCONNECT: "DISCONNECT", + SET_LAST_SEEN_APP: "SET_LAST_SEEN_APP" }; module.exports = Actions; diff --git a/services/gunDB/contact-api/actions.js b/services/gunDB/contact-api/actions.js index 25155229..183781c1 100644 --- a/services/gunDB/contact-api/actions.js +++ b/services/gunDB/contact-api/actions.js @@ -1186,6 +1186,23 @@ const disconnect = async pub => { await generateHandshakeAddress() } +/** + * @returns {Promise} + */ +const setLastSeenApp = () => + new Promise((res, rej) => { + require('../Mediator') + .getUser() + .get(Key.LAST_SEEN_APP) + .put(Date.now(), ack => { + if (ack.err) { + rej(new Error(ack.err)) + } else { + res() + } + }) + }) + module.exports = { __createOutgoingFeed, acceptRequest, @@ -1202,5 +1219,6 @@ module.exports = { generateOrderAddress, setBio, saveSeedBackup, - disconnect + disconnect, + setLastSeenApp } diff --git a/services/gunDB/contact-api/events/index.js b/services/gunDB/contact-api/events/index.js index 0f672754..ef50dfb9 100644 --- a/services/gunDB/contact-api/events/index.js +++ b/services/gunDB/contact-api/events/index.js @@ -457,6 +457,7 @@ const processChats = debounce(() => { const Streams = require('../streams') const pubToAvatar = Streams.getPubToAvatar() const pubToDn = Streams.getPubToDn() + const pubToLastSeenApp = Streams.getPubToLastSeenApp() const existingOutgoings = /** @type {[string, Outgoing][]} */ (Object.entries( getCurrentOutgoings() ).filter(([_, o]) => o !== null)) @@ -468,11 +469,15 @@ const processChats = debounce(() => { for (const [outID, out] of existingOutgoings) { if (typeof pubToAvatar[out.with] === 'undefined') { // eslint-disable-next-line no-empty-function - Streams.onAvatar(() => {}, out.with) + Streams.onAvatar(() => {}, out.with)() } if (typeof pubToDn[out.with] === 'undefined') { // eslint-disable-next-line no-empty-function - Streams.onDisplayName(() => {}, out.with) + Streams.onDisplayName(() => {}, out.with)() + } + if (typeof pubToLastSeenApp[out.with] === 'undefined') { + // eslint-disable-next-line no-empty-function + Streams.onPubToLastSeenApp(() => {}, out.with)() } /** @type {ChatMessage[]} */ @@ -496,7 +501,8 @@ const processChats = debounce(() => { id: out.with + outID, messages: msgs, recipientAvatar: pubToAvatar[out.with] || null, - recipientDisplayName: pubToDn[out.with] || null + recipientDisplayName: pubToDn[out.with] || null, + lastSeenApp: pubToLastSeenApp[out.with] || null } newChats.push(chat) @@ -531,6 +537,7 @@ const onChats = cb => { Streams.onAvatar(processChats) Streams.onDisplayName(processChats) Streams.onPubToFeed(processChats) + Streams.onPubToLastSeenApp(processChats) onChatsSubbed = true } diff --git a/services/gunDB/contact-api/key.js b/services/gunDB/contact-api/key.js index 169b89c5..6f380432 100644 --- a/services/gunDB/contact-api/key.js +++ b/services/gunDB/contact-api/key.js @@ -36,3 +36,5 @@ exports.ORDER_TO_RESPONSE = 'orderToResponse' exports.BIO = 'bio' exports.SEED_BACKUP = 'seedBackup' + +exports.LAST_SEEN_APP = 'lastSeenApp' diff --git a/services/gunDB/contact-api/schema.js b/services/gunDB/contact-api/schema.js index ca614dd1..d4f61f57 100644 --- a/services/gunDB/contact-api/schema.js +++ b/services/gunDB/contact-api/schema.js @@ -77,6 +77,7 @@ exports.isChatMessage = item => { * @prop {ChatMessage[]} messages Sorted from most recent to least recent. * @prop {string|null} recipientDisplayName * @prop {boolean} didDisconnect True if the recipient performed a disconnect. + * @prop {number|undefined|null} lastSeenApp */ /** diff --git a/services/gunDB/contact-api/streams/index.js b/services/gunDB/contact-api/streams/index.js index b1eb872f..7d296090 100644 --- a/services/gunDB/contact-api/streams/index.js +++ b/services/gunDB/contact-api/streams/index.js @@ -187,5 +187,8 @@ module.exports = { getAddresses: require('./addresses').getAddresses, onLastSentReqIDs: require('./lastSentReqID').onLastSentReqIDs, getSentReqIDs: require('./lastSentReqID').getSentReqIDs, - PubToIncoming: require('./pubToIncoming') + PubToIncoming: require('./pubToIncoming'), + + getPubToLastSeenApp: require('./pubToLastSeenApp').getPubToLastSeenApp, + onPubToLastSeenApp: require('./pubToLastSeenApp').on } diff --git a/services/gunDB/contact-api/streams/pubToLastSeenApp.js b/services/gunDB/contact-api/streams/pubToLastSeenApp.js new file mode 100644 index 00000000..729f5e59 --- /dev/null +++ b/services/gunDB/contact-api/streams/pubToLastSeenApp.js @@ -0,0 +1,49 @@ +const Key = require('../key') +/** + * @typedef {Record} Timestamps + * @typedef {(timestamps: Timestamps) => void} Listener + */ + +/** @type {Timestamps} */ +const pubToLastSeenApp = {} + +const getPubToLastSeenApp = () => pubToLastSeenApp + +/** @type {Set} */ +const listeners = new Set() + +const notifyListeners = () => { + listeners.forEach(l => l(pubToLastSeenApp)) +} + +/** @type {Set} */ +const pubsWithListeners = new Set() + +/** + * @param {Listener} cb + * @param {string=} pub + */ +const on = (cb, pub) => { + listeners.add(cb) + cb(pubToLastSeenApp) + if (pub && pubsWithListeners.add(pub)) { + pubToLastSeenApp[pub] = null; + notifyListeners() + require('../../Mediator') + .getGun() + .user(pub) + .get(Key.LAST_SEEN_APP) + .on(timestamp => { + pubToLastSeenApp[pub] = typeof timestamp === 'number' ? timestamp : undefined + notifyListeners() + }) + } + return () => { + listeners.delete(cb) + } +} + +module.exports = { + getPubToLastSeenApp, + on, +} \ No newline at end of file