plugins: api.notifyClient + HFS.getNotifications

Massimo Melina committed Jun 4, 2024 at 12:40 UTC 0be2f0e8407fb3fb6f03a21d028ef740f07b2f21
7 files changed +42 -10
dev-plugins.md
+30 -1
@@ -203,6 +203,8 @@ The `api` object you get as parameter of the `init` contains the following:
203 The specified file name will be stored in the "storage" folder of the plugin, by default.
204 Refer to [dedicated documentation](https://www.npmjs.com/package/@rejetto/kvstorage) for details.
205
206 +- `notifyClient(channel: string, eventName: string, data?: any)` send a message to those frontends that are on the same channel.
207 +
208 ## Front-end specific
209
210 The following information applies to the default front-end, and may not apply to a custom one.
@@ -235,6 +237,8 @@ The HFS objects contains many properties:
237 - `Icon: ReactComponent` Properties:
238 - `name: string` refer to file `icons.ts` for names, but you can also enter an emoji instead.
239 - `useBatch: (worker, job) => any`
240 +- `getNotifications(channel: string, cb: (eventName: string, data:any) => void)`
241 + receive messages when the backend uses `notifyClient` on the same channel.
242
243 The following properties are accessible only immediately at top-level; don't call it later in a callback.
244 - `getPluginConfig()` returns object of all config keys that are declared frontend-accessible by this plugin.
@@ -421,6 +425,30 @@ This section is still partially documented, and you may need to have a look at t
425 - `publicIpsChanged`
426 - parameters: { IPs, IP4, IP6, IPX }
427
428 +# Notifications (backend-to-frontend events)
429 +
430 +You can send messages from the backend (plugin.js) using `api.notifyClient`, and receive on the frontend
431 +using `HFS.getNotifications`. Find details in the reference above.
432 +
433 +Example:
434 +
435 +`plugin.js`
436 +```js
437 +exports.init = api => {
438 + const t = setInterval(() => api.notifyClient('test', 'message', 'hello'), 5000)
439 + return {
440 + frontend_js: 'main.js',
441 + unload() {
442 + clearInterval(t)
443 + }
444 + }
445 +}
446 +```
447 +`public/main.js`
448 +```js
449 +HFS.getNotifications('test', console.log)
450 +```
451 +
452 # The `ctx` object
453
454 HFS is currently based on [Koa](https://koajs.com), so you'll see some things related to it in the backend API.
@@ -546,13 +574,14 @@ If you want to override a text regardless of the language, use the special langu
574
575 ## API version history
576
549 -- 8.82 (v0.53.0)
577 +- 8.84 (v0.53.0)
578 - api.openDb
579 - frontend event: menuZip
580 - config.type:username
581 - api.events class has changed
582 - frontend event "fileMenu": changed props format
583 - api.getConfig() without parameters
584 + - api.notifyClient + HFS.getNotifications
585 - 8.72 (v0.52.0)
586 - HFS.toast
587 - HFS.misc functions
frontend/src/misc.ts
+2 -1
@@ -6,7 +6,7 @@ import { newDialog, toast } from './dialog'
6 import { Icon } from './icons'
7 import { Dict, getHFS, HTTP_MESSAGES, useBatch } from '@hfs/shared'
8 import * as misc from '../../src/cross'
9 -import { apiCall, useApi } from '@hfs/shared/api'
9 +import { apiCall, getNotifications, useApi } from '@hfs/shared/api'
10 import { state } from './state'
11 import { t } from './i18n'
12 import * as dialogLib from './dialog'
@@ -66,6 +66,7 @@ const tools = {
66 Object.assign(getHFS(), {
67 ...tools,
68 emit: hfsEvent,
69 + getNotifications,
70 onEvent(name: string, cb: (params:any, tools: any, output:any) => any) {
71 const key = 'hfs.' + name
72 document.addEventListener(key, wrapper)
frontend/src/upload.ts
+2 -2
@@ -11,7 +11,7 @@ import _ from 'lodash'
11 import { INTERNAL_Snapshot, proxy, ref, snapshot, subscribe, useSnapshot } from 'valtio'
12 import { alertDialog, confirmDialog, promptDialog, toast } from './dialog'
13 import { reloadList } from './useFetchList'
14 -import { apiCall, getNotification } from '@hfs/shared/api'
14 +import { apiCall, getNotifications } from '@hfs/shared/api'
15 import { state, useSnapState } from './state'
16 import { Link } from 'react-router-dom'
17 import { t } from './i18n'
@@ -345,7 +345,7 @@ async function startUpload(toUpload: ToUpload, to: string, resume=0) {
345 async function subscribeNotifications() {
346 if (notificationChannel) return
347 notificationChannel = 'upload-' + randomId()
348 - notificationSource = await getNotification(notificationChannel, async (name, data) => {
348 + notificationSource = await getNotifications(notificationChannel, async (name, data) => {
349 const {uploading} = uploadState
350 if (!uploading) return
351 if (name === 'upload.resumable') {
shared/api.ts
+1 -1
@@ -172,7 +172,7 @@ export function useApiEvents<T=any>(cmd: string, params: Dict={}) {
172 return { data, loading, error }
173 }
174
175 -export async function getNotification(channel: string, cb: (name: string, data:any) => void): Promise<EventSource> {
175 +export async function getNotifications(channel: string, cb: (name: string, data:any) => void): Promise<EventSource> {
176 return new Promise(resolve => {
177 const ret = apiEvents('get_notifications', { channel }, (type, entries) => {
178 if (type === 'connected')
src/const.ts
+1 -1
@@ -7,7 +7,7 @@ import { mkdirSync } from 'fs'
7 import { basename, dirname, join } from 'path'
8 export * from './cross-const'
9
10 -export const API_VERSION = 8.83
10 +export const API_VERSION = 8.84
11 export const COMPATIBLE_API_VERSION = 1 // while changes in the api are not breaking, this number stays the same, otherwise it is made equal to API_VERSION
12 export const HFS_REPO = 'rejetto/hfs'
13
src/frontEndApis.ts
+4 -4
@@ -149,10 +149,10 @@ export const frontEndApis: ApiHandlers = {
149 },
150 }
151
152 -export function notifyClient(ctx: Koa.Context, name: string, data: any) {
153 - const {notificationChannel} = ctx.query
154 - if (notificationChannel)
155 - events.emit(NOTIFICATION_PREFIX + notificationChannel, name, data)
152 +export function notifyClient(channel: string | Koa.Context, name: string, data: any) {
153 + if (typeof channel !== 'string')
154 + channel = String(channel.query.notificationChannel)
155 + events.emit(NOTIFICATION_PREFIX + channel, name, data)
156 }
157
158 const NOTIFICATION_PREFIX = 'notificationChannel:'
src/plugins.ts
+2
@@ -23,6 +23,7 @@ import { dirname, join, resolve } from 'path'
23 import { watchLoadCustomHtml } from './customHtml'
24 import { KvStorage, KvStorageOptions } from '@rejetto/kvstorage'
25 import { onProcessExit } from './first'
26 +import { notifyClient } from './frontEndApis'
27
28 export const PATH = 'plugins'
29 export const DISABLING_SUFFIX = '-disabled'
@@ -110,6 +111,7 @@ async function initPlugin<T>(pl: any, morePassedToInit?: T) {
111 log: console.log,
112 getHfsConfig: getConfig,
113 customApiCall,
114 + notifyClient,
115 ...morePassedToInit
116 }))
117 }