pgkiln 0.83.0 → 0.85.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/db/migrations/154_notifications.sql +186 -0
- package/docs/apex-feature-parity.md +3 -2
- package/docs/guide/03-builder.md +16 -5
- package/docs/guide/04-pages-and-regions.md +42 -0
- package/docs/guide/06-processing.md +22 -1
- package/docs/guide/07-dynamic-actions.md +1 -0
- package/docs/guide/09-reference.md +15 -0
- package/docs/guide/11-from-apex.md +1 -0
- package/docs/guide/12-development.md +13 -1
- package/package.json +1 -1
- package/public/app.css +29 -0
- package/public/app.js +78 -0
- package/public/sw.js +5 -1
- package/src/app.ts +11 -0
- package/src/builder/ai-agents.ts +86 -85
- package/src/builder/ai-builder.ts +30 -30
- package/src/builder/ai.ts +93 -92
- package/src/builder/api.ts +47 -47
- package/src/builder/app-groups.ts +33 -32
- package/src/builder/appfromfile.ts +57 -58
- package/src/builder/appsheets.ts +33 -32
- package/src/builder/appwizard.ts +65 -65
- package/src/builder/arrange.ts +23 -22
- package/src/builder/assistant.ts +39 -38
- package/src/builder/automations.ts +32 -31
- package/src/builder/availability.ts +24 -24
- package/src/builder/backups.ts +30 -31
- package/src/builder/blueprints.ts +62 -61
- package/src/builder/code-editor.ts +1 -1
- package/src/builder/components.ts +10 -6
- package/src/builder/create-page.ts +52 -50
- package/src/builder/cross-app.ts +37 -35
- package/src/builder/dataload.ts +2 -2
- package/src/builder/datasets.ts +107 -106
- package/src/builder/dependencies.ts +20 -19
- package/src/builder/designer.ts +36 -36
- package/src/builder/diagnostics.ts +58 -57
- package/src/builder/features.ts +39 -35
- package/src/builder/feedback.ts +23 -20
- package/src/builder/find-workspace.ts +28 -24
- package/src/builder/form-designer.ts +36 -35
- package/src/builder/gallery.ts +25 -29
- package/src/builder/globalization.ts +50 -49
- package/src/builder/i18n/de.ts +3461 -0
- package/src/builder/i18n/en.ts +3461 -0
- package/src/builder/i18n/es.ts +3461 -0
- package/src/builder/i18n/fr.ts +3461 -0
- package/src/builder/i18n/it.ts +3461 -0
- package/src/builder/i18n/ja.ts +3461 -0
- package/src/builder/i18n/ko.ts +3461 -0
- package/src/builder/i18n/nl.ts +3461 -0
- package/src/builder/i18n/pt.ts +3461 -0
- package/src/builder/i18n/zh-cn.ts +3461 -0
- package/src/builder/i18n/zh-tw.ts +3461 -0
- package/src/builder/i18n.ts +17 -3
- package/src/builder/json-sources.ts +63 -62
- package/src/builder/layouts.ts +12 -11
- package/src/builder/locks.ts +29 -28
- package/src/builder/login-page.ts +26 -25
- package/src/builder/mail.ts +148 -147
- package/src/builder/newapp.ts +15 -12
- package/src/builder/notification-settings.ts +27 -0
- package/src/builder/plugins.ts +47 -44
- package/src/builder/process-jobs.ts +11 -10
- package/src/builder/pwa.ts +22 -21
- package/src/builder/remote-databases.ts +55 -56
- package/src/builder/report-settings.ts +1 -1
- package/src/builder/reporter.ts +43 -42
- package/src/builder/rest-catalogs.ts +87 -86
- package/src/builder/rest.ts +9 -8
- package/src/builder/routes.ts +30 -27
- package/src/builder/search.ts +14 -13
- package/src/builder/static-files.ts +31 -32
- package/src/builder/subscriptions.ts +47 -45
- package/src/builder/supporting.ts +23 -18
- package/src/builder/team.ts +179 -179
- package/src/builder/telemetry.ts +59 -62
- package/src/builder/templates.ts +66 -65
- package/src/builder/themeroller.ts +60 -63
- package/src/builder/ui-defaults.ts +21 -20
- package/src/builder/ui.ts +2 -1
- package/src/builder/upgrade.ts +25 -24
- package/src/builder/utilities.ts +48 -47
- package/src/builder/vector-providers.ts +67 -66
- package/src/builder/websources.ts +43 -42
- package/src/builder/wizards.ts +95 -89
- package/src/builder/workflow-migrate.ts +30 -31
- package/src/builder/workflows.ts +40 -39
- package/src/builder/workingcopies.ts +41 -49
- package/src/builder/workspace-backups.ts +21 -20
- package/src/builder/workspace-mail.ts +17 -16
- package/src/builder/workspace-resources.ts +104 -112
- package/src/builder/workspace-transfer.ts +34 -26
- package/src/cli/replace.ts +2 -0
- package/src/i18n/ar.ts +9 -0
- package/src/i18n/cs.ts +9 -0
- package/src/i18n/da.ts +9 -0
- package/src/i18n/de.ts +9 -0
- package/src/i18n/el.ts +9 -0
- package/src/i18n/es.ts +9 -0
- package/src/i18n/fi.ts +9 -0
- package/src/i18n/fr.ts +9 -0
- package/src/i18n/he.ts +9 -0
- package/src/i18n/it.ts +9 -0
- package/src/i18n/ja.ts +9 -0
- package/src/i18n/ko.ts +9 -0
- package/src/i18n/nb.ts +9 -0
- package/src/i18n/pl.ts +9 -0
- package/src/i18n/pt.ts +9 -0
- package/src/i18n/ru.ts +9 -0
- package/src/i18n/sv.ts +9 -0
- package/src/i18n/tr.ts +9 -0
- package/src/i18n/uk.ts +9 -0
- package/src/i18n/zh.ts +9 -0
- package/src/i18n.ts +18 -0
- package/src/metadata.ts +9 -3
- package/src/runtime/engine.ts +2 -1
- package/src/runtime/feedback.ts +1 -1
- package/src/runtime/lists.ts +43 -11
- package/src/runtime/notifications.ts +237 -0
- package/src/runtime/processes.ts +45 -1
- package/src/runtime/regions.ts +11 -15
- package/src/runtime/render.ts +12 -3
- package/src/runtime/routes.ts +12 -4
- package/src/runtime/signin.ts +2 -1
- package/src/session.ts +13 -4
|
@@ -7,9 +7,10 @@ import { html } from '../html.ts';
|
|
|
7
7
|
import { icon } from '../icons.ts';
|
|
8
8
|
import { readMultipart } from '../runtime/files.ts';
|
|
9
9
|
import { clientIp, logActivity, type Session } from '../session.ts';
|
|
10
|
-
import { describeWorkspaceFile, exportWorkspace, importWorkspace,
|
|
10
|
+
import { describeWorkspaceFile, exportWorkspace, importWorkspace, PARTS, type Part } from '../workspace-export.ts';
|
|
11
11
|
import { back, BASE, csrf, developer, flash, region, send, shell, type Body, type Req } from './ui.ts';
|
|
12
12
|
import { currentWorkspace, workspaceAdmin } from './workspaces.ts';
|
|
13
|
+
import { scopedTx as tx, type BuilderKey } from './i18n.ts';
|
|
13
14
|
|
|
14
15
|
// Workspace utilities → Export / import (APEX: Workspace Utilities → Export):
|
|
15
16
|
// the current workspace's components as one file, and such a file imported
|
|
@@ -19,7 +20,14 @@ import { currentWorkspace, workspaceAdmin } from './workspaces.ts';
|
|
|
19
20
|
|
|
20
21
|
const RESULT = '__WS_IMPORT_RESULT';
|
|
21
22
|
const page = `${BASE}/workspace-export`;
|
|
22
|
-
const crumbs: [string, string?][]
|
|
23
|
+
const crumbs = (): [string, string?][] => [[tx('rail.app_builder'), BASE], [tx('wu.title'), `${BASE}/utilities`], [tx('wu.export')]];
|
|
24
|
+
/** The kinds of component in the developer's language (PART_LABELS in English: src/workspace-export.ts). */
|
|
25
|
+
const PART_KEYS: Record<Part, BuilderKey> = {
|
|
26
|
+
settings: 'wtr.part.settings', developers: 'wtr.part.developers', app_groups: 'wtr.part.app_groups', extension_links: 'wtr.part.extension_links',
|
|
27
|
+
rest_catalogs: 'wtr.part.rest_catalogs', sql_scripts: 'wtr.part.sql_scripts', blueprints: 'wtr.part.blueprints', data_generators: 'wtr.part.data_generators',
|
|
28
|
+
datasets: 'wtr.part.datasets', team: 'wtr.part.team', workspace_files: 'wtr.part.workspace_files', remote_servers: 'wtr.part.remote_servers',
|
|
29
|
+
web_credentials: 'wtr.part.web_credentials',
|
|
30
|
+
};
|
|
23
31
|
const MAX_MB = 20;
|
|
24
32
|
|
|
25
33
|
let version: string | null | undefined;
|
|
@@ -41,16 +49,16 @@ const chosen = (b: Body | undefined): Part[] => {
|
|
|
41
49
|
|
|
42
50
|
async function adminOr403(s: Session, reply: FastifyReply) {
|
|
43
51
|
if (s.workspace && (await workspaceAdmin(s.username, s.workspace.id))) return true;
|
|
44
|
-
await send(reply.code(403), s, shell(s, '
|
|
45
|
-
<div class="alert alert-error" role="alert"
|
|
52
|
+
await send(reply.code(403), s, shell(s, tx('wu.export'), crumbs(), html`<h1 class="u-mb1">${tx('wu.export')}</h1>
|
|
53
|
+
<div class="alert alert-error" role="alert">${tx('wtr.admins_only')}</div>`));
|
|
46
54
|
return false;
|
|
47
55
|
}
|
|
48
56
|
|
|
49
57
|
const partBoxes = (counts?: Partial<Record<Part, number>>) =>
|
|
50
|
-
html`<fieldset class="field" data-wide><legend class="label"
|
|
58
|
+
html`<fieldset class="field" data-wide><legend class="label">${tx('wtr.components')}</legend>${PARTS.map((p) =>
|
|
51
59
|
counts && counts[p] === undefined
|
|
52
60
|
? ''
|
|
53
|
-
: html`<label class="check"><input type="checkbox" name="part" value="${p}" checked> ${
|
|
61
|
+
: html`<label class="check"><input type="checkbox" name="part" value="${p}" checked> ${tx(PART_KEYS[p])}${counts ? html` <span class="muted">(${p === 'settings' ? tx('wtr.in_file') : p === 'team' ? tx('wtr.issues', { n: counts[p] }) : counts[p]})</span>` : ''}</label>`)}</fieldset>`;
|
|
54
62
|
|
|
55
63
|
export async function workspaceTransferRoutes(app: FastifyInstance) {
|
|
56
64
|
app.get(page, async (req: Req, reply) => {
|
|
@@ -59,22 +67,22 @@ export async function workspaceTransferRoutes(app: FastifyInstance) {
|
|
|
59
67
|
const result = s.state[RESULT] ? (JSON.parse(s.state[RESULT]!) as string[]) : null;
|
|
60
68
|
delete s.state[RESULT];
|
|
61
69
|
const name = s.workspace!.name;
|
|
62
|
-
const main = html`<div class="title-row"><h1
|
|
63
|
-
<p class="muted u-mt0"
|
|
64
|
-
${result ? html`<div class="alert alert-success" role="status"><b
|
|
65
|
-
${region('
|
|
70
|
+
const main = html`<div class="title-row"><h1>${tx('wu.export')}</h1></div>
|
|
71
|
+
<p class="muted u-mt0">${tx('wtr.intro', { name })}</p>
|
|
72
|
+
${result ? html`<div class="alert alert-success" role="status"><b>${tx('wtr.imported_into', { name })}</b><ul>${result.map((l) => html`<li>${l}</li>`)}</ul></div>` : ''}
|
|
73
|
+
${region(tx('wtr.export'), html`<form method="post" action="${page}">${csrf(s)}
|
|
66
74
|
${partBoxes()}
|
|
67
|
-
<p class="muted"
|
|
68
|
-
<div class="buttons"><button class="btn btn-hot">${icon('download')}
|
|
69
|
-
${region('
|
|
70
|
-
<p class="muted u-mt0"
|
|
75
|
+
<p class="muted">${tx('wtr.not_in_file')}</p>
|
|
76
|
+
<div class="buttons"><button class="btn btn-hot">${icon('download')} ${tx('wtr.export')}</button></div></form>`)}
|
|
77
|
+
${region(tx('wtr.import'), html`<form method="post" action="${BASE}/workspace-import" enctype="multipart/form-data">${csrf(s)}
|
|
78
|
+
<p class="muted u-mt0">${tx.rich('wtr.import_intro', { name: html`<b>${name}</b>` })}</p>
|
|
71
79
|
<div class="form-grid">
|
|
72
|
-
<div class="field" data-wide><label class="label" for="f_file"
|
|
80
|
+
<div class="field" data-wide><label class="label" for="f_file">${tx('wtr.file')}</label><input id="f_file" type="file" name="file" accept=".json,application/json" required></div>
|
|
73
81
|
${partBoxes()}
|
|
74
|
-
<div class="field" data-wide><label class="check"><input type="checkbox" name="replace" value="true">
|
|
82
|
+
<div class="field" data-wide><label class="check"><input type="checkbox" name="replace" value="true"> ${tx('wtr.replace')}</label></div>
|
|
75
83
|
</div>
|
|
76
|
-
<div class="buttons"><button class="btn">${icon('upload')}
|
|
77
|
-
return send(reply, s, shell(s, '
|
|
84
|
+
<div class="buttons"><button class="btn">${icon('upload')} ${tx('wtr.import')}</button></div></form>`)}`;
|
|
85
|
+
return send(reply, s, shell(s, tx('wu.export'), crumbs(), main));
|
|
78
86
|
});
|
|
79
87
|
|
|
80
88
|
app.post(page, async (req: Req, reply) => {
|
|
@@ -82,7 +90,7 @@ export async function workspaceTransferRoutes(app: FastifyInstance) {
|
|
|
82
90
|
if (!s || !(await adminOr403(s, reply))) return;
|
|
83
91
|
const parts = chosen(req.body);
|
|
84
92
|
if (!parts.length) {
|
|
85
|
-
flash(s, '
|
|
93
|
+
flash(s, tx('wtr.choose_part'), 'error');
|
|
86
94
|
return back(reply, s, page);
|
|
87
95
|
}
|
|
88
96
|
const doc = await exportWorkspace(currentWorkspace(s), parts, { version: pgkilnVersion() ?? undefined, by: s.username });
|
|
@@ -104,23 +112,23 @@ export async function workspaceTransferRoutes(app: FastifyInstance) {
|
|
|
104
112
|
const s = await developer(req, reply);
|
|
105
113
|
if (!s || !(await adminOr403(s, reply))) return;
|
|
106
114
|
try {
|
|
107
|
-
if (tooBig) throw new Error(
|
|
108
|
-
if (!text.trim()) throw new Error('
|
|
115
|
+
if (tooBig) throw new Error(tx('wtr.too_big', { n: MAX_MB }));
|
|
116
|
+
if (!text.trim()) throw new Error(tx('wtr.choose_file'));
|
|
109
117
|
let doc: unknown;
|
|
110
118
|
try {
|
|
111
119
|
doc = JSON.parse(text);
|
|
112
120
|
} catch {
|
|
113
|
-
throw new Error('
|
|
121
|
+
throw new Error(tx('wtr.not_json'));
|
|
114
122
|
}
|
|
115
123
|
describeWorkspaceFile(doc);
|
|
116
124
|
const parts = chosen(req.body);
|
|
117
|
-
if (!parts.length) throw new Error('
|
|
125
|
+
if (!parts.length) throw new Error(tx('wtr.choose_part'));
|
|
118
126
|
const ws = currentWorkspace(s);
|
|
119
127
|
const result = await owner.tx((c) => importWorkspace(c, ws, doc, parts, { replace: req.body?.replace === 'true', by: s.username }));
|
|
120
128
|
await logActivity({ appId: null, username: s.username, event: 'workspace', ip: clientIp(req), detail: `imported into workspace ${ws}: ${result.added} added, ${result.replaced} replaced, ${result.skipped} kept` });
|
|
121
129
|
s.state[RESULT] = JSON.stringify(result.lines);
|
|
122
130
|
} catch (e) {
|
|
123
|
-
flash(s,
|
|
131
|
+
flash(s, tx('wtr.failed', { reason: friendly(e) }), 'error');
|
|
124
132
|
}
|
|
125
133
|
return back(reply, s, page);
|
|
126
134
|
});
|
|
@@ -129,8 +137,8 @@ export async function workspaceTransferRoutes(app: FastifyInstance) {
|
|
|
129
137
|
/** A database refusal in words (the transaction rolled back). */
|
|
130
138
|
function friendly(e: unknown) {
|
|
131
139
|
const err = e as { message: string; constraint?: string; table?: string; code?: string };
|
|
132
|
-
if (err.code === '23514' || err.code === '23502' || err.code === '22P02') return
|
|
133
|
-
if (err.code === '23505') return
|
|
140
|
+
if (err.code === '23514' || err.code === '23502' || err.code === '22P02') return tx('wtr.refused_value', { detail: `${err.table ?? ''}${err.constraint ? `: ${err.constraint}` : ''}` });
|
|
141
|
+
if (err.code === '23505') return tx('wtr.duplicate', { detail: err.constraint ?? err.table ?? '' });
|
|
134
142
|
return err.message;
|
|
135
143
|
}
|
|
136
144
|
|
package/src/cli/replace.ts
CHANGED
|
@@ -78,6 +78,8 @@ export const KEPT = ['app_access', 'api_client', 'session', 'sso_pending', 'save
|
|
|
78
78
|
'rest_object',
|
|
79
79
|
// (121) the log of AI tool calls (the agents and tools themselves travel in meta.app)
|
|
80
80
|
'ai_tool_call',
|
|
81
|
+
// (154) users' in-app notifications
|
|
82
|
+
'notification',
|
|
81
83
|
// (122) Team Development issues of the workspace, linked to the application (workspace data: the workspace export)
|
|
82
84
|
'team_issue',
|
|
83
85
|
// (123) the log of running workflow instances migrated to a newer version (runtime data, like the instances)
|
package/src/i18n/ar.ts
CHANGED
|
@@ -310,6 +310,15 @@ export const ar: Record<MessageKey, string> = {
|
|
|
310
310
|
'account.devices_help': "المتصفحات التي اخترت فيها «إبقائي مسجّلًا»: {count}.",
|
|
311
311
|
'account.forget_devices': "تسجيل الخروج من كل الأجهزة",
|
|
312
312
|
'account.devices_forgotten': "تم تسجيل الخروج من كل الأجهزة.",
|
|
313
|
+
'notify.title': "الإشعارات",
|
|
314
|
+
'notify.none': "لا توجد إشعارات.",
|
|
315
|
+
'notify.mark_all_read': "تعليم الكل كمقروء",
|
|
316
|
+
'notify.all': "كل الإشعارات",
|
|
317
|
+
'notify.marked': "تم تعليم الإشعارات كمقروءة.",
|
|
318
|
+
'notify.gone': "هذا الإشعار لم يعد موجودًا.",
|
|
319
|
+
'notify.bell_one': "الإشعارات: 1 غير مقروء",
|
|
320
|
+
'notify.bell_n': "الإشعارات: {n} غير مقروءة",
|
|
321
|
+
'notify.unread_one': "غير مقروء",
|
|
313
322
|
'push.title': "الإشعارات",
|
|
314
323
|
'push.help': "تلقَّ إشعارات هذا التطبيق على هذا الجهاز، حتى عندما يكون التطبيق مغلقًا.",
|
|
315
324
|
'push.devices': "الأجهزة التي تتلقى الإشعارات: {n}.",
|
package/src/i18n/cs.ts
CHANGED
|
@@ -310,6 +310,15 @@ export const cs: Record<MessageKey, string> = {
|
|
|
310
310
|
'account.devices_help': "Prohlížeče, ve kterých jste zvolili „Zůstat přihlášen“: {count}.",
|
|
311
311
|
'account.forget_devices': "Odhlásit na všech zařízeních",
|
|
312
312
|
'account.devices_forgotten': "Odhlášeno na všech zařízeních.",
|
|
313
|
+
'notify.title': "Oznámení",
|
|
314
|
+
'notify.none': "Žádná oznámení.",
|
|
315
|
+
'notify.mark_all_read': "Označit vše jako přečtené",
|
|
316
|
+
'notify.all': "Všechna oznámení",
|
|
317
|
+
'notify.marked': "Oznámení označena jako přečtená.",
|
|
318
|
+
'notify.gone': "Toto oznámení už neexistuje.",
|
|
319
|
+
'notify.bell_one': "Oznámení: 1 nepřečtené",
|
|
320
|
+
'notify.bell_n': "Oznámení: nepřečtených {n}",
|
|
321
|
+
'notify.unread_one': "Nepřečtené",
|
|
313
322
|
'push.title': "Oznámení",
|
|
314
323
|
'push.help': "Dostávejte oznámení z této aplikace na tomto zařízení, i když je aplikace zavřená.",
|
|
315
324
|
'push.devices': "Zařízení s oznámeními: {n}.",
|
package/src/i18n/da.ts
CHANGED
|
@@ -310,6 +310,15 @@ export const da: Record<MessageKey, string> = {
|
|
|
310
310
|
'account.devices_help': "Browsere, hvor du valgte “Hold mig logget ind”: {count}.",
|
|
311
311
|
'account.forget_devices': "Log ud på alle enheder",
|
|
312
312
|
'account.devices_forgotten': "Logget ud på alle enheder.",
|
|
313
|
+
'notify.title': "Notifikationer",
|
|
314
|
+
'notify.none': "Ingen notifikationer.",
|
|
315
|
+
'notify.mark_all_read': "Markér alle som læst",
|
|
316
|
+
'notify.all': "Alle notifikationer",
|
|
317
|
+
'notify.marked': "Notifikationerne er markeret som læst.",
|
|
318
|
+
'notify.gone': "Denne notifikation findes ikke længere.",
|
|
319
|
+
'notify.bell_one': "Notifikationer: 1 ulæst",
|
|
320
|
+
'notify.bell_n': "Notifikationer: {n} ulæste",
|
|
321
|
+
'notify.unread_one': "Ulæst",
|
|
313
322
|
'push.title': "Notifikationer",
|
|
314
323
|
'push.help': "Få notifikationer fra denne app på denne enhed, også når appen er lukket.",
|
|
315
324
|
'push.devices': "Enheder med notifikationer: {n}.",
|
package/src/i18n/de.ts
CHANGED
|
@@ -310,6 +310,15 @@ export const de: Record<MessageKey, string> = {
|
|
|
310
310
|
'account.devices_help': 'Browser, in denen Sie „Angemeldet bleiben“ gewählt haben: {count}.',
|
|
311
311
|
'account.forget_devices': 'Auf allen Geräten abmelden',
|
|
312
312
|
'account.devices_forgotten': 'Auf allen Geräten abgemeldet.',
|
|
313
|
+
'notify.title': "Benachrichtigungen",
|
|
314
|
+
'notify.none': "Keine Benachrichtigungen.",
|
|
315
|
+
'notify.mark_all_read': "Alle als gelesen markieren",
|
|
316
|
+
'notify.all': "Alle Benachrichtigungen",
|
|
317
|
+
'notify.marked': "Benachrichtigungen als gelesen markiert.",
|
|
318
|
+
'notify.gone': "Diese Benachrichtigung gibt es nicht mehr.",
|
|
319
|
+
'notify.bell_one': "Benachrichtigungen: 1 ungelesen",
|
|
320
|
+
'notify.bell_n': "Benachrichtigungen: {n} ungelesen",
|
|
321
|
+
'notify.unread_one': "Ungelesen",
|
|
313
322
|
'push.title': "Benachrichtigungen",
|
|
314
323
|
'push.help': "Erhalten Sie Benachrichtigungen dieser App auf diesem Gerät, auch wenn die App geschlossen ist.",
|
|
315
324
|
'push.devices': "Geräte mit Benachrichtigungen: {n}.",
|
package/src/i18n/el.ts
CHANGED
|
@@ -310,6 +310,15 @@ export const el: Record<MessageKey, string> = {
|
|
|
310
310
|
'account.devices_help': "Προγράμματα περιήγησης όπου επιλέξατε «Να παραμείνω συνδεδεμένος»: {count}.",
|
|
311
311
|
'account.forget_devices': "Αποσύνδεση από όλες τις συσκευές",
|
|
312
312
|
'account.devices_forgotten': "Έγινε αποσύνδεση από όλες τις συσκευές.",
|
|
313
|
+
'notify.title': "Ειδοποιήσεις",
|
|
314
|
+
'notify.none': "Δεν υπάρχουν ειδοποιήσεις.",
|
|
315
|
+
'notify.mark_all_read': "Σήμανση όλων ως αναγνωσμένων",
|
|
316
|
+
'notify.all': "Όλες οι ειδοποιήσεις",
|
|
317
|
+
'notify.marked': "Οι ειδοποιήσεις σημάνθηκαν ως αναγνωσμένες.",
|
|
318
|
+
'notify.gone': "Αυτή η ειδοποίηση δεν υπάρχει πια.",
|
|
319
|
+
'notify.bell_one': "Ειδοποιήσεις: 1 μη αναγνωσμένη",
|
|
320
|
+
'notify.bell_n': "Ειδοποιήσεις: {n} μη αναγνωσμένες",
|
|
321
|
+
'notify.unread_one': "Μη αναγνωσμένη",
|
|
313
322
|
'push.title': "Ειδοποιήσεις",
|
|
314
323
|
'push.help': "Λάβετε ειδοποιήσεις από αυτήν την εφαρμογή σε αυτήν τη συσκευή, ακόμη και όταν η εφαρμογή είναι κλειστή.",
|
|
315
324
|
'push.devices': "Συσκευές με ειδοποιήσεις: {n}.",
|
package/src/i18n/es.ts
CHANGED
|
@@ -310,6 +310,15 @@ export const es: Record<MessageKey, string> = {
|
|
|
310
310
|
'account.devices_help': 'Navegadores en los que eligió "Mantener la sesión iniciada": {count}.',
|
|
311
311
|
'account.forget_devices': 'Cerrar sesión en todos los dispositivos',
|
|
312
312
|
'account.devices_forgotten': 'Sesión cerrada en todos los dispositivos.',
|
|
313
|
+
'notify.title': "Notificaciones",
|
|
314
|
+
'notify.none': "No hay notificaciones.",
|
|
315
|
+
'notify.mark_all_read': "Marcar todas como leídas",
|
|
316
|
+
'notify.all': "Todas las notificaciones",
|
|
317
|
+
'notify.marked': "Notificaciones marcadas como leídas.",
|
|
318
|
+
'notify.gone': "Esta notificación ya no existe.",
|
|
319
|
+
'notify.bell_one': "Notificaciones: 1 sin leer",
|
|
320
|
+
'notify.bell_n': "Notificaciones: {n} sin leer",
|
|
321
|
+
'notify.unread_one': "Sin leer",
|
|
313
322
|
'push.title': "Notificaciones",
|
|
314
323
|
'push.help': "Reciba notificaciones de esta aplicación en este dispositivo, también cuando la aplicación está cerrada.",
|
|
315
324
|
'push.devices': "Dispositivos con notificaciones: {n}.",
|
package/src/i18n/fi.ts
CHANGED
|
@@ -310,6 +310,15 @@ export const fi: Record<MessageKey, string> = {
|
|
|
310
310
|
'account.devices_help': "Selaimet, joissa valitsit ”Pidä minut kirjautuneena”: {count}.",
|
|
311
311
|
'account.forget_devices': "Kirjaudu ulos kaikilla laitteilla",
|
|
312
312
|
'account.devices_forgotten': "Kirjauduttu ulos kaikilla laitteilla.",
|
|
313
|
+
'notify.title': "Ilmoitukset",
|
|
314
|
+
'notify.none': "Ei ilmoituksia.",
|
|
315
|
+
'notify.mark_all_read': "Merkitse kaikki luetuiksi",
|
|
316
|
+
'notify.all': "Kaikki ilmoitukset",
|
|
317
|
+
'notify.marked': "Ilmoitukset merkittiin luetuiksi.",
|
|
318
|
+
'notify.gone': "Tätä ilmoitusta ei enää ole.",
|
|
319
|
+
'notify.bell_one': "Ilmoitukset: 1 lukematon",
|
|
320
|
+
'notify.bell_n': "Ilmoitukset: {n} lukematonta",
|
|
321
|
+
'notify.unread_one': "Lukematon",
|
|
313
322
|
'push.title': "Ilmoitukset",
|
|
314
323
|
'push.help': "Saa tämän sovelluksen ilmoitukset tälle laitteelle, myös kun sovellus on suljettu.",
|
|
315
324
|
'push.devices': "Laitteet, joilla on ilmoitukset: {n}.",
|
package/src/i18n/fr.ts
CHANGED
|
@@ -310,6 +310,15 @@ export const fr: Record<MessageKey, string> = {
|
|
|
310
310
|
'account.devices_help': 'Navigateurs où vous avez choisi « Rester connecté » : {count}.',
|
|
311
311
|
'account.forget_devices': 'Se déconnecter sur tous les appareils',
|
|
312
312
|
'account.devices_forgotten': 'Déconnecté sur tous les appareils.',
|
|
313
|
+
'notify.title': "Notifications",
|
|
314
|
+
'notify.none': "Aucune notification.",
|
|
315
|
+
'notify.mark_all_read': "Tout marquer comme lu",
|
|
316
|
+
'notify.all': "Toutes les notifications",
|
|
317
|
+
'notify.marked': "Notifications marquées comme lues.",
|
|
318
|
+
'notify.gone': "Cette notification n'existe plus.",
|
|
319
|
+
'notify.bell_one': "Notifications : 1 non lue",
|
|
320
|
+
'notify.bell_n': "Notifications : {n} non lues",
|
|
321
|
+
'notify.unread_one': "Non lue",
|
|
313
322
|
'push.title': "Notifications",
|
|
314
323
|
'push.help': "Recevez les notifications de cette application sur cet appareil, même lorsqu'elle est fermée.",
|
|
315
324
|
'push.devices': "Appareils avec notifications : {n}.",
|
package/src/i18n/he.ts
CHANGED
|
@@ -310,6 +310,15 @@ export const he: Record<MessageKey, string> = {
|
|
|
310
310
|
'account.devices_help': "דפדפנים שבהם בחרתם \"להשאיר אותי מחובר\": {count}.",
|
|
311
311
|
'account.forget_devices': "יציאה מכל המכשירים",
|
|
312
312
|
'account.devices_forgotten': "בוצעה יציאה מכל המכשירים.",
|
|
313
|
+
'notify.title': "התראות",
|
|
314
|
+
'notify.none': "אין התראות.",
|
|
315
|
+
'notify.mark_all_read': "סימון הכול כנקרא",
|
|
316
|
+
'notify.all': "כל ההתראות",
|
|
317
|
+
'notify.marked': "ההתראות סומנו כנקראו.",
|
|
318
|
+
'notify.gone': "ההתראה הזו כבר לא קיימת.",
|
|
319
|
+
'notify.bell_one': "התראות: 1 שלא נקראה",
|
|
320
|
+
'notify.bell_n': "התראות: {n} שלא נקראו",
|
|
321
|
+
'notify.unread_one': "לא נקראה",
|
|
313
322
|
'push.title': "התראות",
|
|
314
323
|
'push.help': "קבלו התראות מהאפליקציה הזו במכשיר הזה, גם כשהאפליקציה סגורה.",
|
|
315
324
|
'push.devices': "מכשירים עם התראות: {n}.",
|
package/src/i18n/it.ts
CHANGED
|
@@ -310,6 +310,15 @@ export const it: Record<MessageKey, string> = {
|
|
|
310
310
|
'account.devices_help': "Browser in cui hai scelto “Mantieni l'accesso”: {count}.",
|
|
311
311
|
'account.forget_devices': "Esci da tutti i dispositivi",
|
|
312
312
|
'account.devices_forgotten': "Uscita effettuata da tutti i dispositivi.",
|
|
313
|
+
'notify.title': "Notifiche",
|
|
314
|
+
'notify.none': "Nessuna notifica.",
|
|
315
|
+
'notify.mark_all_read': "Segna tutte come lette",
|
|
316
|
+
'notify.all': "Tutte le notifiche",
|
|
317
|
+
'notify.marked': "Notifiche segnate come lette.",
|
|
318
|
+
'notify.gone': "Questa notifica non esiste più.",
|
|
319
|
+
'notify.bell_one': "Notifiche: 1 da leggere",
|
|
320
|
+
'notify.bell_n': "Notifiche: {n} da leggere",
|
|
321
|
+
'notify.unread_one': "Da leggere",
|
|
313
322
|
'push.title': "Notifiche",
|
|
314
323
|
'push.help': "Ricevi le notifiche di questa app su questo dispositivo, anche quando l'app è chiusa.",
|
|
315
324
|
'push.devices': "Dispositivi con notifiche: {n}.",
|
package/src/i18n/ja.ts
CHANGED
|
@@ -310,6 +310,15 @@ export const ja: Record<MessageKey, string> = {
|
|
|
310
310
|
'account.devices_help': "「サインインしたままにする」を選んだブラウザー: {count}。",
|
|
311
311
|
'account.forget_devices': "すべてのデバイスでサインアウト",
|
|
312
312
|
'account.devices_forgotten': "すべてのデバイスでサインアウトしました。",
|
|
313
|
+
'notify.title': "通知",
|
|
314
|
+
'notify.none': "通知はありません。",
|
|
315
|
+
'notify.mark_all_read': "すべて既読にする",
|
|
316
|
+
'notify.all': "すべての通知",
|
|
317
|
+
'notify.marked': "通知を既読にしました。",
|
|
318
|
+
'notify.gone': "この通知はもう存在しません。",
|
|
319
|
+
'notify.bell_one': "通知: 未読 1 件",
|
|
320
|
+
'notify.bell_n': "通知: 未読 {n} 件",
|
|
321
|
+
'notify.unread_one': "未読",
|
|
313
322
|
'push.title': "通知",
|
|
314
323
|
'push.help': "アプリを閉じているときも、このデバイスでこのアプリの通知を受け取ります。",
|
|
315
324
|
'push.devices': "通知を受け取るデバイス: {n}。",
|
package/src/i18n/ko.ts
CHANGED
|
@@ -310,6 +310,15 @@ export const ko: Record<MessageKey, string> = {
|
|
|
310
310
|
'account.devices_help': "'로그인 유지'를 선택한 브라우저: {count}개.",
|
|
311
311
|
'account.forget_devices': "모든 기기에서 로그아웃",
|
|
312
312
|
'account.devices_forgotten': "모든 기기에서 로그아웃했습니다.",
|
|
313
|
+
'notify.title': "알림",
|
|
314
|
+
'notify.none': "알림이 없습니다.",
|
|
315
|
+
'notify.mark_all_read': "모두 읽음으로 표시",
|
|
316
|
+
'notify.all': "모든 알림",
|
|
317
|
+
'notify.marked': "알림을 읽음으로 표시했습니다.",
|
|
318
|
+
'notify.gone': "이 알림은 더 이상 없습니다.",
|
|
319
|
+
'notify.bell_one': "알림: 읽지 않음 1개",
|
|
320
|
+
'notify.bell_n': "알림: 읽지 않음 {n}개",
|
|
321
|
+
'notify.unread_one': "읽지 않음",
|
|
313
322
|
'push.title': "알림",
|
|
314
323
|
'push.help': "앱이 닫혀 있을 때도 이 기기에서 이 앱의 알림을 받습니다.",
|
|
315
324
|
'push.devices': "알림을 받는 기기: {n}.",
|
package/src/i18n/nb.ts
CHANGED
|
@@ -310,6 +310,15 @@ export const nb: Record<MessageKey, string> = {
|
|
|
310
310
|
'account.devices_help': "Nettlesere der du valgte «Hold meg pålogget»: {count}.",
|
|
311
311
|
'account.forget_devices': "Logg ut på alle enheter",
|
|
312
312
|
'account.devices_forgotten': "Logget ut på alle enheter.",
|
|
313
|
+
'notify.title': "Varsler",
|
|
314
|
+
'notify.none': "Ingen varsler.",
|
|
315
|
+
'notify.mark_all_read': "Merk alle som lest",
|
|
316
|
+
'notify.all': "Alle varsler",
|
|
317
|
+
'notify.marked': "Varslene er merket som lest.",
|
|
318
|
+
'notify.gone': "Dette varselet finnes ikke lenger.",
|
|
319
|
+
'notify.bell_one': "Varsler: 1 ulest",
|
|
320
|
+
'notify.bell_n': "Varsler: {n} uleste",
|
|
321
|
+
'notify.unread_one': "Ulest",
|
|
313
322
|
'push.title': "Varsler",
|
|
314
323
|
'push.help': "Få varsler fra denne appen på denne enheten, også når appen er lukket.",
|
|
315
324
|
'push.devices': "Enheter med varsler: {n}.",
|
package/src/i18n/pl.ts
CHANGED
|
@@ -310,6 +310,15 @@ export const pl: Record<MessageKey, string> = {
|
|
|
310
310
|
'account.devices_help': "Przeglądarki, w których wybrano „Nie wylogowuj mnie”: {count}.",
|
|
311
311
|
'account.forget_devices': "Wyloguj na wszystkich urządzeniach",
|
|
312
312
|
'account.devices_forgotten': "Wylogowano na wszystkich urządzeniach.",
|
|
313
|
+
'notify.title': "Powiadomienia",
|
|
314
|
+
'notify.none': "Brak powiadomień.",
|
|
315
|
+
'notify.mark_all_read': "Oznacz wszystkie jako przeczytane",
|
|
316
|
+
'notify.all': "Wszystkie powiadomienia",
|
|
317
|
+
'notify.marked': "Powiadomienia oznaczono jako przeczytane.",
|
|
318
|
+
'notify.gone': "To powiadomienie już nie istnieje.",
|
|
319
|
+
'notify.bell_one': "Powiadomienia: 1 nieprzeczytane",
|
|
320
|
+
'notify.bell_n': "Powiadomienia: nieprzeczytane: {n}",
|
|
321
|
+
'notify.unread_one': "Nieprzeczytane",
|
|
313
322
|
'push.title': "Powiadomienia",
|
|
314
323
|
'push.help': "Otrzymuj powiadomienia z tej aplikacji na tym urządzeniu, także gdy aplikacja jest zamknięta.",
|
|
315
324
|
'push.devices': "Urządzenia z powiadomieniami: {n}.",
|
package/src/i18n/pt.ts
CHANGED
|
@@ -310,6 +310,15 @@ export const pt: Record<MessageKey, string> = {
|
|
|
310
310
|
'account.devices_help': "Navegadores onde escolheu “Manter a sessão iniciada”: {count}.",
|
|
311
311
|
'account.forget_devices': "Terminar sessão em todos os dispositivos",
|
|
312
312
|
'account.devices_forgotten': "Sessão terminada em todos os dispositivos.",
|
|
313
|
+
'notify.title': "Notificações",
|
|
314
|
+
'notify.none': "Sem notificações.",
|
|
315
|
+
'notify.mark_all_read': "Marcar todas como lidas",
|
|
316
|
+
'notify.all': "Todas as notificações",
|
|
317
|
+
'notify.marked': "Notificações marcadas como lidas.",
|
|
318
|
+
'notify.gone': "Esta notificação já não existe.",
|
|
319
|
+
'notify.bell_one': "Notificações: 1 por ler",
|
|
320
|
+
'notify.bell_n': "Notificações: {n} por ler",
|
|
321
|
+
'notify.unread_one': "Por ler",
|
|
313
322
|
'push.title': "Notificações",
|
|
314
323
|
'push.help': "Receba notificações desta aplicação neste dispositivo, mesmo com a aplicação fechada.",
|
|
315
324
|
'push.devices': "Dispositivos com notificações: {n}.",
|
package/src/i18n/ru.ts
CHANGED
|
@@ -310,6 +310,15 @@ export const ru: Record<MessageKey, string> = {
|
|
|
310
310
|
'account.devices_help': "Браузеров, где выбрано «Не выходить»: {count}.",
|
|
311
311
|
'account.forget_devices': "Выйти на всех устройствах",
|
|
312
312
|
'account.devices_forgotten': "Выход выполнен на всех устройствах.",
|
|
313
|
+
'notify.title': "Уведомления",
|
|
314
|
+
'notify.none': "Уведомлений нет.",
|
|
315
|
+
'notify.mark_all_read': "Отметить все как прочитанные",
|
|
316
|
+
'notify.all': "Все уведомления",
|
|
317
|
+
'notify.marked': "Уведомления отмечены как прочитанные.",
|
|
318
|
+
'notify.gone': "Этого уведомления больше нет.",
|
|
319
|
+
'notify.bell_one': "Уведомления: 1 непрочитанное",
|
|
320
|
+
'notify.bell_n': "Уведомления: непрочитанных — {n}",
|
|
321
|
+
'notify.unread_one': "Непрочитанное",
|
|
313
322
|
'push.title': "Уведомления",
|
|
314
323
|
'push.help': "Получайте уведомления этого приложения на этом устройстве, даже когда приложение закрыто.",
|
|
315
324
|
'push.devices': "Устройства с уведомлениями: {n}.",
|
package/src/i18n/sv.ts
CHANGED
|
@@ -310,6 +310,15 @@ export const sv: Record<MessageKey, string> = {
|
|
|
310
310
|
'account.devices_help': "Webbläsare där du valde ”Håll mig inloggad”: {count}.",
|
|
311
311
|
'account.forget_devices': "Logga ut på alla enheter",
|
|
312
312
|
'account.devices_forgotten': "Utloggad på alla enheter.",
|
|
313
|
+
'notify.title': "Aviseringar",
|
|
314
|
+
'notify.none': "Inga aviseringar.",
|
|
315
|
+
'notify.mark_all_read': "Markera alla som lästa",
|
|
316
|
+
'notify.all': "Alla aviseringar",
|
|
317
|
+
'notify.marked': "Aviseringarna har markerats som lästa.",
|
|
318
|
+
'notify.gone': "Den här aviseringen finns inte längre.",
|
|
319
|
+
'notify.bell_one': "Aviseringar: 1 oläst",
|
|
320
|
+
'notify.bell_n': "Aviseringar: {n} olästa",
|
|
321
|
+
'notify.unread_one': "Oläst",
|
|
313
322
|
'push.title': "Aviseringar",
|
|
314
323
|
'push.help': "Få aviseringar från den här appen på den här enheten, även när appen är stängd.",
|
|
315
324
|
'push.devices': "Enheter med aviseringar: {n}.",
|
package/src/i18n/tr.ts
CHANGED
|
@@ -310,6 +310,15 @@ export const tr: Record<MessageKey, string> = {
|
|
|
310
310
|
'account.devices_help': "“Oturumum açık kalsın” seçtiğiniz tarayıcılar: {count}.",
|
|
311
311
|
'account.forget_devices': "Tüm cihazlarda oturumu kapat",
|
|
312
312
|
'account.devices_forgotten': "Tüm cihazlarda oturum kapatıldı.",
|
|
313
|
+
'notify.title': "Bildirimler",
|
|
314
|
+
'notify.none': "Bildirim yok.",
|
|
315
|
+
'notify.mark_all_read': "Tümünü okundu olarak işaretle",
|
|
316
|
+
'notify.all': "Tüm bildirimler",
|
|
317
|
+
'notify.marked': "Bildirimler okundu olarak işaretlendi.",
|
|
318
|
+
'notify.gone': "Bu bildirim artık yok.",
|
|
319
|
+
'notify.bell_one': "Bildirimler: 1 okunmamış",
|
|
320
|
+
'notify.bell_n': "Bildirimler: {n} okunmamış",
|
|
321
|
+
'notify.unread_one': "Okunmamış",
|
|
313
322
|
'push.title': "Bildirimler",
|
|
314
323
|
'push.help': "Uygulama kapalıyken de bu cihazda bu uygulamanın bildirimlerini alın.",
|
|
315
324
|
'push.devices': "Bildirim alan cihazlar: {n}.",
|
package/src/i18n/uk.ts
CHANGED
|
@@ -310,6 +310,15 @@ export const uk: Record<MessageKey, string> = {
|
|
|
310
310
|
'account.devices_help': "Браузерів, де вибрано «Не виходити»: {count}.",
|
|
311
311
|
'account.forget_devices': "Вийти на всіх пристроях",
|
|
312
312
|
'account.devices_forgotten': "Вихід виконано на всіх пристроях.",
|
|
313
|
+
'notify.title': "Сповіщення",
|
|
314
|
+
'notify.none': "Сповіщень немає.",
|
|
315
|
+
'notify.mark_all_read': "Позначити всі як прочитані",
|
|
316
|
+
'notify.all': "Усі сповіщення",
|
|
317
|
+
'notify.marked': "Сповіщення позначено як прочитані.",
|
|
318
|
+
'notify.gone': "Цього сповіщення більше немає.",
|
|
319
|
+
'notify.bell_one': "Сповіщення: 1 непрочитане",
|
|
320
|
+
'notify.bell_n': "Сповіщення: непрочитаних — {n}",
|
|
321
|
+
'notify.unread_one': "Непрочитане",
|
|
313
322
|
'push.title': "Сповіщення",
|
|
314
323
|
'push.help': "Отримуйте сповіщення цього застосунку на цьому пристрої, навіть коли застосунок закрито.",
|
|
315
324
|
'push.devices': "Пристрої зі сповіщеннями: {n}.",
|
package/src/i18n/zh.ts
CHANGED
|
@@ -310,6 +310,15 @@ export const zh: Record<MessageKey, string> = {
|
|
|
310
310
|
'account.devices_help': "您选择了“保持登录”的浏览器:{count}。",
|
|
311
311
|
'account.forget_devices': "在所有设备上退出登录",
|
|
312
312
|
'account.devices_forgotten': "已在所有设备上退出登录。",
|
|
313
|
+
'notify.title': "通知",
|
|
314
|
+
'notify.none': "没有通知。",
|
|
315
|
+
'notify.mark_all_read': "全部标为已读",
|
|
316
|
+
'notify.all': "所有通知",
|
|
317
|
+
'notify.marked': "通知已标为已读。",
|
|
318
|
+
'notify.gone': "此通知已不存在。",
|
|
319
|
+
'notify.bell_one': "通知:1 条未读",
|
|
320
|
+
'notify.bell_n': "通知:{n} 条未读",
|
|
321
|
+
'notify.unread_one': "未读",
|
|
313
322
|
'push.title': "通知",
|
|
314
323
|
'push.help': "在此设备上接收此应用的通知,即使应用已关闭。",
|
|
315
324
|
'push.devices': "接收通知的设备:{n}。",
|
package/src/i18n.ts
CHANGED
|
@@ -337,6 +337,15 @@ const en = {
|
|
|
337
337
|
'account.devices_help': 'Browsers where you chose "Keep me signed in": {count}.',
|
|
338
338
|
'account.forget_devices': 'Sign out on all devices',
|
|
339
339
|
'account.devices_forgotten': 'Signed out on all devices.',
|
|
340
|
+
'notify.title': "Notifications",
|
|
341
|
+
'notify.none': "No notifications.",
|
|
342
|
+
'notify.mark_all_read': "Mark all read",
|
|
343
|
+
'notify.all': "All notifications",
|
|
344
|
+
'notify.marked': "Notifications marked as read.",
|
|
345
|
+
'notify.gone': "This notification no longer exists.",
|
|
346
|
+
'notify.bell_one': "Notifications: 1 unread",
|
|
347
|
+
'notify.bell_n': "Notifications: {n} unread",
|
|
348
|
+
'notify.unread_one': "Unread",
|
|
340
349
|
'push.title': "Notifications",
|
|
341
350
|
'push.help': "Get notifications from this app on this device, also when the app is closed.",
|
|
342
351
|
'push.devices': "Devices with notifications: {n}.",
|
|
@@ -1247,6 +1256,15 @@ const nl: Record<MessageKey, string> = {
|
|
|
1247
1256
|
'account.devices_help': 'Browsers waarin je "Ingelogd blijven" koos: {count}.',
|
|
1248
1257
|
'account.forget_devices': 'Overal afmelden',
|
|
1249
1258
|
'account.devices_forgotten': 'Op alle apparaten afgemeld.',
|
|
1259
|
+
'notify.title': "Meldingen",
|
|
1260
|
+
'notify.none': "Geen meldingen.",
|
|
1261
|
+
'notify.mark_all_read': "Alles gelezen",
|
|
1262
|
+
'notify.all': "Alle meldingen",
|
|
1263
|
+
'notify.marked': "Meldingen als gelezen gemarkeerd.",
|
|
1264
|
+
'notify.gone': "Deze melding bestaat niet meer.",
|
|
1265
|
+
'notify.bell_one': "Meldingen: 1 ongelezen",
|
|
1266
|
+
'notify.bell_n': "Meldingen: {n} ongelezen",
|
|
1267
|
+
'notify.unread_one': "Ongelezen",
|
|
1250
1268
|
'push.title': "Meldingen",
|
|
1251
1269
|
'push.help': "Ontvang meldingen van deze app op dit apparaat, ook als de app gesloten is.",
|
|
1252
1270
|
'push.devices': "Apparaten met meldingen: {n}.",
|
package/src/metadata.ts
CHANGED
|
@@ -112,6 +112,10 @@ export interface App {
|
|
|
112
112
|
availability_users: string[];
|
|
113
113
|
/** (084) users can send feedback from the navigation bar */
|
|
114
114
|
feedback: boolean;
|
|
115
|
+
/** (154) the notification bell in the header */
|
|
116
|
+
notifications: boolean;
|
|
117
|
+
/** (154) refresh the badges every so many seconds (null: never) */
|
|
118
|
+
live_badges_s: number | null;
|
|
115
119
|
/** (107) the application's version (Release 1.0), shown in its pages' footer */
|
|
116
120
|
version: string | null;
|
|
117
121
|
/** (085) schema.function(jsonb) returns jsonb that rewrites error messages; seconds of the session timeout warning */
|
|
@@ -322,7 +326,7 @@ export interface DynamicAction {
|
|
|
322
326
|
/** (149) */
|
|
323
327
|
| 'double_click' | 'key_down' | 'key_press' | 'mouse_enter' | 'mouse_leave' | 'select' | 'resize' | 'scroll' | 'page_unload' | 'before_refresh' | 'facets_change'
|
|
324
328
|
/** (153) component events: trigger = the grid, calendar, map or tree region */
|
|
325
|
-
| 'grid_row_init' | 'grid_save' | 'calendar_date_select' | 'calendar_view_change' | 'map_object_click' | 'map_change' | 'tree_selection_change';
|
|
329
|
+
| 'grid_row_init' | 'grid_save' | 'calendar_date_select' | 'calendar_view_change' | 'map_object_click' | 'map_change' | 'tree_selection_change' | 'timer';
|
|
326
330
|
trigger_element: string | null;
|
|
327
331
|
condition_type: 'equals' | 'not_equals' | 'in_list' | 'is_null' | 'is_not_null' | 'greater' | 'greater_equal' | 'less' | 'less_equal' | 'not_in_list' | null;
|
|
328
332
|
condition_value: string | null;
|
|
@@ -383,7 +387,9 @@ export interface Process {
|
|
|
383
387
|
type: 'form_dml' | 'grid_dml' | 'sql' | 'data_load' | 'invoke_api' | 'download' | 'chain' | 'workflow' | 'ai_generate' | 'plugin' | 'send_push' | 'send_email'
|
|
384
388
|
/** (150) */
|
|
385
389
|
| 'close_dialog' | 'clear_session_state' | 'reset_pagination' | 'user_preferences' | 'server_geocoding' | 'print_report'
|
|
386
|
-
| 'human_task_create' | 'human_task_manage' | 'form_init'
|
|
390
|
+
| 'human_task_create' | 'human_task_manage' | 'form_init'
|
|
391
|
+
/** (154) */
|
|
392
|
+
| 'send_notification';
|
|
387
393
|
region_id: number | null;
|
|
388
394
|
code: string | null;
|
|
389
395
|
config: Record<string, unknown> | null;
|
|
@@ -443,7 +449,7 @@ const agg = (table: string, fk: string, parent: string, appId: string) =>
|
|
|
443
449
|
export async function loadApp(alias: string) {
|
|
444
450
|
return runtime.one<App>(
|
|
445
451
|
`select a.id, a.alias, a.name, a.version, a.home_page, a.authentication, a.access_control, a.sso_providers, a.local_login, a.remember_me_days, a.login_page, a.password_reset, a.session_group, a.mfa, a.mfa_roles, a.mfa_email, a.api_self_tokens, a.ldap_directories, a.header_name, a.header_auto_create, a.logout_url, a.db_auth_roles, a.db_auth_member_of, a.custom_auth_function, a.custom_auth_code, a.custom_auth_post_code, a.nav_list, a.navbar_list, a.pwa, a.pwa_short_name, a.pwa_icon is not null as pwa_has_icon, a.pwa_offline_pages, a.pwa_offline_submit, a.pwa_push, a.db_role, a.debug, a.debug_level, a.theme,
|
|
446
|
-
a.availability, a.availability_message, a.availability_users, a.feedback, a.error_function, a.session_warning_seconds, a.component_settings,
|
|
452
|
+
a.availability, a.availability_message, a.availability_users, a.feedback, a.notifications, a.live_badges_s, a.error_function, a.session_warning_seconds, a.component_settings,
|
|
447
453
|
a.language, a.languages, a.language_from, a.date_format, a.timestamp_format, a.time_zone, a.time_zone_auto, a.currency,
|
|
448
454
|
coalesce((select jsonb_agg(jsonb_build_object('name', l.name, 'query', l.query, 'rest_source', l.rest_source)) from meta.lov l where l.app_id = a.id), '[]') as lovs,
|
|
449
455
|
coalesce((select jsonb_agg(jsonb_build_object('page_no', p.page_no, 'name', p.name, 'title', p.title,
|
package/src/runtime/engine.ts
CHANGED
|
@@ -18,7 +18,7 @@ import { parseDateMask } from './format.ts';
|
|
|
18
18
|
import { conditionHolds, functionBody } from './logic.ts';
|
|
19
19
|
import { runPluginProcess } from './plugins.ts';
|
|
20
20
|
import { runDeclarative } from './process-types.ts';
|
|
21
|
-
import { BACKGROUND_TYPES, downloadFile, sendEmailProcess, sendPushProcess, workflowProcess, type ChainConfig } from './processes.ts';
|
|
21
|
+
import { BACKGROUND_TYPES, downloadFile, sendEmailProcess, sendNotificationProcess, sendPushProcess, workflowProcess, type ChainConfig } from './processes.ts';
|
|
22
22
|
import { bindValues, dbg, publicError, stripSemicolon, substitute, timed, toState, type Errors, type PageContext } from './context.ts';
|
|
23
23
|
|
|
24
24
|
const ident = pg.escapeIdentifier;
|
|
@@ -621,6 +621,7 @@ async function runOneStep(ctx: PageContext, p: Process, names: Set<string>, dept
|
|
|
621
621
|
case 'invoke_api': return await invokeApi(ctx, p, names);
|
|
622
622
|
case 'workflow': return await workflowProcess(ctx, p, names);
|
|
623
623
|
case 'send_push': return await sendPushProcess(ctx, p);
|
|
624
|
+
case 'send_notification': return await sendNotificationProcess(ctx, p);
|
|
624
625
|
case 'send_email': return await sendEmailProcess(ctx, p);
|
|
625
626
|
case 'ai_generate': return (await runAiProcess(ctx, p, names)).message;
|
|
626
627
|
case 'plugin': return await runPluginProcess(ctx, p.config, p.name);
|
package/src/runtime/feedback.ts
CHANGED
|
@@ -29,7 +29,7 @@ export function feedbackNode(ctx: PageContext): ListNode | null {
|
|
|
29
29
|
if (!ctx.app.feedback || ctx.dialog) return null;
|
|
30
30
|
return {
|
|
31
31
|
label: ctx.locale.t('feedback.link'), href: `${ctx.base}/${ctx.page.page_no}/feedback`, modal: false, external: false,
|
|
32
|
-
icon: 'chat', badge: null, description: null, current: false, inTrail: false, children: [],
|
|
32
|
+
icon: 'chat', badge: null, key: '__feedback', description: null, current: false, inTrail: false, children: [],
|
|
33
33
|
};
|
|
34
34
|
}
|
|
35
35
|
|