diff --git a/packages/backend/src/CoreModule.js b/packages/backend/src/CoreModule.js index d8120521..a53e8cc2 100644 --- a/packages/backend/src/CoreModule.js +++ b/packages/backend/src/CoreModule.js @@ -242,6 +242,9 @@ const install = async ({ services, app, useapi }) => { const { BroadcastService } = require('./services/BroadcastService'); services.registerService('broadcast', BroadcastService); + + const { NotificationService } = require('./services/NotificationService'); + services.registerService('notification', NotificationService); } const install_legacy = async ({ services }) => { diff --git a/packages/backend/src/services/NotificationService.js b/packages/backend/src/services/NotificationService.js new file mode 100644 index 00000000..9dc27d43 --- /dev/null +++ b/packages/backend/src/services/NotificationService.js @@ -0,0 +1,65 @@ +const BaseService = require("./BaseService"); +const { DB_WRITE } = require("./database/consts"); + +const UsernameNotifSelector = username => async (self) => { + const svc_getUser = self.services.get('get-user'); + const user = await svc_getUser.get_user({ username }); + return [user.id]; +}; + +class NotificationService extends BaseService { + static MODULES = { + uuidv4: require('uuid').v4, + } + + async _init () { + const svc_database = this.services.get('database'); + this.db = svc_database.get(DB_WRITE, 'notification'); + + const svc_script = this.services.get('script'); + svc_script.register('test-notification', async ({ log }, [username, summary]) => { + log('creating notification: ' + summary); + + this.notify(UsernameNotifSelector(username), { summary }); + }); + } + async notify (selector, notification) { + const uid = this.modules.uuidv4(); + const svc_event = this.services.get('event'); + const user_id_list = await selector(this); + svc_event.emit('outer.gui.notif.message', { + user_id_list, + response: { + uid, + notification, + }, + }); + + const ll = o => { + this.log.noticeme('debug: ' + require('node:util').inspect(o)); + return o; + }; + + (async () => { + for ( const user_id of user_id_list ) { + await this.db.write(...ll([ + 'INSERT INTO `notification` ' + + '(`user_id`, `uid`, `value`) ' + + 'VALUES (?, ?, ?)', + [user_id, uid, JSON.stringify(notification)], + ])); + } + svc_event.emit('outer.gui.notif.persisted', { + user_id_list, + response: { + uid, + }, + }); + })(); + } +} + +module.exports = { + NotificationService, + UsernameNotifSelector, +}; diff --git a/packages/backend/src/services/WSPushService.js b/packages/backend/src/services/WSPushService.js index e563d2ff..7fcbff60 100644 --- a/packages/backend/src/services/WSPushService.js +++ b/packages/backend/src/services/WSPushService.js @@ -68,11 +68,6 @@ class WSPushService extends AdvancedBase { Object.assign(response, metadata); - const io = socketio.getio(); - for ( const user_id of user_id_list ) { - io.to(user_id).emit('item.added', response); - } - this.svc_event.emit('outer.gui.item.added', { user_id_list, response, @@ -107,11 +102,6 @@ class WSPushService extends AdvancedBase { Object.assign(response, metadata); - const io = socketio.getio(); - for ( const user_id of user_id_list ) { - io.to(user_id).emit('item.updated', response); - } - this.svc_event.emit('outer.gui.item.updated', { user_id_list, response, @@ -147,11 +137,6 @@ class WSPushService extends AdvancedBase { response.old_path = old_path; Object.assign(response, metadata); - const io = socketio.getio(); - for ( const user_id of user_id_list ) { - io.to(user_id).emit('item.moved', response); - } - this.svc_event.emit('outer.gui.item.moved', { user_id_list, response, @@ -185,10 +170,6 @@ class WSPushService extends AdvancedBase { Object.assign(response, metadata); - const io = socketio.getio(); - for ( const user_id of user_id_list ) { - io.to(user_id).emit('item.pending', response); - } this.svc_event.emit('outer.gui.item.pending', { user_id_list, response, @@ -248,8 +229,6 @@ class WSPushService extends AdvancedBase { } async _on_outer_gui (key, { user_id_list, response }, meta) { - if ( ! meta.from_outside ) return; - key = key.slice('outer.gui.'.length); const { socketio } = this.modules; diff --git a/packages/backend/src/services/database/SqliteDatabaseAccessService.js b/packages/backend/src/services/database/SqliteDatabaseAccessService.js index 200f670c..6a9e814f 100644 --- a/packages/backend/src/services/database/SqliteDatabaseAccessService.js +++ b/packages/backend/src/services/database/SqliteDatabaseAccessService.js @@ -42,7 +42,7 @@ class SqliteDatabaseAccessService extends BaseDatabaseAccessService { this.db = new Database(this.config.path); // Database upgrade logic - const TARGET_VERSION = 8; + const TARGET_VERSION = 9; if ( do_setup ) { this.log.noticeme(`SETUP: creating database at ${this.config.path}`); @@ -105,6 +105,10 @@ class SqliteDatabaseAccessService extends BaseDatabaseAccessService { upgrade_files.push('0010_add-git-app.sql'); } + if ( user_version <= 8 ) { + upgrade_files.push('0011_notification.sql'); + } + if ( upgrade_files.length > 0 ) { this.log.noticeme(`Database out of date: ${this.config.path}`); this.log.noticeme(`UPGRADING DATABASE: ${user_version} -> ${TARGET_VERSION}`); diff --git a/packages/backend/src/services/database/sqlite_setup/0011_notification.sql b/packages/backend/src/services/database/sqlite_setup/0011_notification.sql new file mode 100644 index 00000000..7c516f12 --- /dev/null +++ b/packages/backend/src/services/database/sqlite_setup/0011_notification.sql @@ -0,0 +1,8 @@ +CREATE TABLE `notification` ( + `id` INTEGER PRIMARY KEY AUTOINCREMENT, + `user_id` INTEGER NOT NULL, + `uid` TEXT NOT NULL UNIQUE, + `value` JSON NOT NULL, + `read` tinyint(1) DEFAULT '0', + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP +); diff --git a/src/UI/UIDesktop.js b/src/UI/UIDesktop.js index c6a7dfcf..873335be 100644 --- a/src/UI/UIDesktop.js +++ b/src/UI/UIDesktop.js @@ -110,6 +110,12 @@ async function UIDesktop(options){ if(msg.is_empty) $(`.window[data-path="${html_encode(window.trash_path)}" i]`).find('.item-container').empty(); }) + + window.socket.on('notif.message', ({ notification }) => { + UINotification({ + content: notification.summary + }); + }); window.socket.on('app.opened', async (app) => { // don't update if this is the original client that initiated the action