@wssto2/vue-core 0.4.28 → 0.5.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/CHANGELOG.md +67 -0
- package/dist/{AccessGate.vue_vue_type_script_setup_true_lang-J5HoyGNJ.js → AccessGate.vue_vue_type_script_setup_true_lang-Nw1IsB8N.js} +1 -1
- package/dist/{ActionRow.vue_vue_type_script_setup_true_lang-QPvYudWc.js → ActionRow.vue_vue_type_script_setup_true_lang-BCgKzUvG.js} +1 -1
- package/dist/{Activity-BWkVX1Nx.js → Activity-CoGI2_GL.js} +7 -7
- package/dist/{Changes-MG77TDwz.js → Changes-Cwfe6wbX.js} +6 -6
- package/dist/{CollectionPage.vue_vue_type_script_setup_true_lang-Bnpv9TEj.js → CollectionPage.vue_vue_type_script_setup_true_lang-CtvHtFLy.js} +4 -4
- package/dist/{DateField.vue_vue_type_script_setup_true_lang-CpuH0WTl.js → DateField.vue_vue_type_script_setup_true_lang-DRuWzSaX.js} +1 -1
- package/dist/{DeadLetters-CkDE71B3.js → DeadLetters-DyA5Dqn0.js} +7 -7
- package/dist/{FormErrors.vue_vue_type_script_setup_true_lang-DGOSc-PS.js → FormErrors.vue_vue_type_script_setup_true_lang-D7edzNTL.js} +2 -2
- package/dist/{FormGroup.vue_vue_type_script_setup_true_lang-DSwSRsA5.js → FormGroup.vue_vue_type_script_setup_true_lang-BPchlRCl.js} +2 -2
- package/dist/{FormView.vue_vue_type_script_setup_true_lang-BUrYkjmF.js → FormView.vue_vue_type_script_setup_true_lang-CPSYV7iQ.js} +2 -2
- package/dist/{General-UTBVEXbN.js → General-DTEfxWls.js} +10 -10
- package/dist/MenuButton.vue_vue_type_script_setup_true_lang-AGbN9r5q.js +28 -0
- package/dist/{NewRolePage-B7DwFWPk.js → NewRolePage-CRZoUajV.js} +3 -3
- package/dist/{NumberField.vue_vue_type_script_setup_true_lang-DaE5BLm4.js → NumberField.vue_vue_type_script_setup_true_lang-ButMV_sf.js} +1 -1
- package/dist/ProfilePage-DLY81mvN.js +764 -0
- package/dist/RoleEditor.vue_vue_type_script_setup_true_lang-gXbxXG2R.js +325 -0
- package/dist/RolePage-B9t586Q8.js +314 -0
- package/dist/{RolesPage-B5RwSZ89.js → RolesPage-BRdG-OSL.js} +6 -6
- package/dist/{SectionNavigator.vue_vue_type_script_setup_true_lang-Dmv6TpCi.js → SectionNavigator.vue_vue_type_script_setup_true_lang-Cy9Oi0ts.js} +1 -1
- package/dist/{SegmentedField.vue_vue_type_script_setup_true_lang-Dg-_ZjgR.js → SegmentedField.vue_vue_type_script_setup_true_lang-BwyMvkis.js} +1 -1
- package/dist/{SelectField.vue_vue_type_script_setup_true_lang-C3FbRQW7.js → SelectField.vue_vue_type_script_setup_true_lang-BzQC1qDQ.js} +11 -12
- package/dist/SessionList.vue_vue_type_script_setup_true_lang-DhoTd5D7.js +133 -0
- package/dist/{Sessions-DWj2OGo0.js → Sessions-vGyUOnPG.js} +5 -5
- package/dist/SettingsPage-V8WeADZZ.js +632 -0
- package/dist/{SignIn-COThB9XN.js → SignIn-C-3i8RG6.js} +7 -7
- package/dist/{SignInPage-CdzgSvRm.js → SignInPage-D2NA3VKU.js} +4 -4
- package/dist/SignIns-eoYPB6kB.js +121 -0
- package/dist/{SwitchField.vue_vue_type_script_setup_true_lang-ALvJisiP.js → SwitchField.vue_vue_type_script_setup_true_lang-D_-HACqT.js} +1 -1
- package/dist/{TextField.vue_vue_type_script_setup_true_lang-CP2WeU36.js → TextField.vue_vue_type_script_setup_true_lang-DTAQPbW0.js} +2 -2
- package/dist/{TextareaField.vue_vue_type_script_setup_true_lang-BJ4__cvJ.js → TextareaField.vue_vue_type_script_setup_true_lang-DsjK2C9T.js} +1 -1
- package/dist/{TimeField.vue_vue_type_script_setup_true_lang-DBxKAWRo.js → TimeField.vue_vue_type_script_setup_true_lang-Cz7KhUqt.js} +1 -1
- package/dist/{UserRecord-BWMnXz4q.js → UserRecord-BaQl2zzQ.js} +13 -13
- package/dist/{UsersList-Chq8yM5C.js → UsersList-BfXWgH5K.js} +9 -9
- package/dist/access/RoleCompareDialog.vue.d.ts +1 -1
- package/dist/access/RoleEditor.vue.d.ts +2 -0
- package/dist/access/constraints.d.ts +28 -0
- package/dist/access/context.d.ts +5 -0
- package/dist/access/feature.d.ts +17 -0
- package/dist/access/index.d.ts +3 -1
- package/dist/access/index.js +11 -9
- package/dist/access/refusals.d.ts +20 -2
- package/dist/app/index.js +6 -6
- package/dist/client/index.js +19 -11
- package/dist/{client-k9euDWF4.js → client-BHPFfBj3.js} +20 -20
- package/dist/collection/index.js +3 -3
- package/dist/content/index.js +66 -37
- package/dist/{contributions-BVBZP5Jb.js → contributions-DVQPcjJz.js} +1 -1
- package/dist/{describeError-B-NMa--w.js → describeError-BqGAUZgX.js} +1 -1
- package/dist/{error-CD7x0Jrz.js → error-Bb_dr_Ay.js} +2 -2
- package/dist/{fieldMessages-6HVzSDqG.js → fieldMessages-DTXpWnBd.js} +7 -7
- package/dist/form/index.js +1235 -1412
- package/dist/{guards-BJZjc3FZ.js → guards-DRh4zmxO.js} +1 -1
- package/dist/{http-BFfkA7gN.js → http-D_pBg22D.js} +1 -1
- package/dist/i18n/index.d.ts +2 -2
- package/dist/i18n/index.js +30 -30
- package/dist/identity/index.d.ts +2 -0
- package/dist/identity/index.js +8 -6
- package/dist/identity/profile/DetailsFields.vue.d.ts +8 -0
- package/dist/identity/profile/{DetailsPanel.vue.d.ts → DetailsGroup.vue.d.ts} +4 -1
- package/dist/identity/profile/{EmailPanel.vue.d.ts → EmailGroup.vue.d.ts} +1 -1
- package/dist/identity/profile/SignInsGroup.vue.d.ts +2 -0
- package/dist/identity/profile/profileSections.d.ts +37 -0
- package/dist/identity/shared/SignInList.vue.d.ts +22 -0
- package/dist/identity/shared/deviceLabel.d.ts +7 -0
- package/dist/identity/useReturnToOwnAccount.d.ts +10 -0
- package/dist/identity/users/context.d.ts +3 -0
- package/dist/identity/users/feature.d.ts +3 -0
- package/dist/index-WkbzC_ee.js +745 -0
- package/dist/index-g-qbUeTk.js +630 -0
- package/dist/index-kqk7Mclb.js +480 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/modules/notification/entities.d.ts +19 -0
- package/dist/modules/notification/routes.d.ts +25 -1
- package/dist/modules/notification/schemas.d.ts +24 -0
- package/dist/{navigation-DpcDF0rX.js → navigation-DuQwti76.js} +1 -1
- package/dist/{neighbors-RVQ4O3Qv.js → neighbors-IMRQ4rkm.js} +22 -22
- package/dist/notifications/context.d.ts +24 -1
- package/dist/notifications/feature.d.ts +10 -1
- package/dist/notifications/index.d.ts +1 -1
- package/dist/notifications/index.js +1 -1
- package/dist/notifications/push/DevicesSection.vue.d.ts +12 -0
- package/dist/notifications/push/ThisDeviceSection.vue.d.ts +15 -0
- package/dist/notifications/push/browser.d.ts +40 -0
- package/dist/notifications/push/devices.d.ts +15 -0
- package/dist/notifications/push/thisDevice.d.ts +28 -0
- package/dist/notifications/settings/ChannelsSection.vue.d.ts +16 -0
- package/dist/notifications/settings/{QuietHoursPanel.vue.d.ts → QuietHoursSection.vue.d.ts} +3 -1
- package/dist/notifications/settings/SettingsBody.vue.d.ts +17 -0
- package/dist/notifications/settings/settings.d.ts +4 -3
- package/dist/notifications/settings/state.d.ts +4 -2
- package/dist/options-g8RwEhBk.js +366 -0
- package/dist/overlay/index.js +37 -59
- package/dist/page/index.js +1 -1
- package/dist/phone/index.js +10 -11
- package/dist/platform/index.js +3 -3
- package/dist/{platform-PZpUkkMS.js → platform-Dl1fbScN.js} +2 -2
- package/dist/push-worker/index.d.ts +91 -0
- package/dist/push-worker/index.js +54 -0
- package/dist/resource/index.js +1 -1
- package/dist/{routeResource-DlTsGTg_.js → routeResource-CcYA75T2.js} +2 -2
- package/dist/router/index.js +3 -3
- package/dist/{sections-CeZl-Jxg.js → sections-D5rgQ4YB.js} +1 -1
- package/dist/shell/index.js +3 -3
- package/dist/signInEvents-DWShrJox.js +30 -0
- package/dist/state/index.js +1 -1
- package/dist/{status-dGvfh1TP.js → status-C2gHqkj6.js} +2 -2
- package/dist/styles.css +1 -1
- package/dist/{suggestions-DHJFKB26.js → suggestions-CfFTHi8y.js} +2 -2
- package/dist/testing/index.js +1 -1
- package/dist/{useCommand-Cj-APj-P.js → useCommand-CtqheKUv.js} +2 -2
- package/dist/useGroupSheet-ATvc0IiM.js +278 -0
- package/dist/{useLoad-M7V-AVwR.js → useLoad-W4tfxmm5.js} +1 -1
- package/dist/{useResourceForm-tGYTfKhH.js → useResourceForm-CJfl8rj_.js} +1 -1
- package/package.json +6 -2
- package/dist/KeyValue.vue_vue_type_script_setup_true_lang-wcZcvnow.js +0 -35
- package/dist/OtpInput.vue_vue_type_script_setup_true_lang-gp4FfWAH.js +0 -95
- package/dist/PanelActions.vue_vue_type_script_setup_true_lang-E9VdIGkf.js +0 -46
- package/dist/ProfilePage-C8VhiKIh.js +0 -654
- package/dist/RoleEditor.vue_vue_type_script_setup_true_lang-DRHmjA9w.js +0 -300
- package/dist/RolePage-ODFu68Ce.js +0 -315
- package/dist/SessionList.vue_vue_type_script_setup_true_lang-D5qm77Zo.js +0 -111
- package/dist/SettingsPage-C3fPSmi7.js +0 -255
- package/dist/SignInTable.vue_vue_type_script_setup_true_lang-Bs0yTi2E.js +0 -79
- package/dist/SignIns-cztICmqt.js +0 -66
- package/dist/control-B4SdblL6.js +0 -350
- package/dist/identity/profile/PanelActions.vue.d.ts +0 -17
- package/dist/index-BhtJR4c_.js +0 -459
- package/dist/index-C3E6DcO0.js +0 -706
- package/dist/index-sTBBHqNG.js +0 -504
- package/dist/notifications/settings/EmailPanel.vue.d.ts +0 -14
- package/dist/options-Ba_0uLWN.js +0 -18
- package/dist/route-goWMxDUN.js +0 -13
- package/dist/transport-BnP7WC0Y.js +0 -4
- /package/dist/identity/{profile/PasswordPanel.vue.d.ts → ReturnMenuItem.vue.d.ts} +0 -0
- /package/dist/identity/profile/{SessionsPanel.vue.d.ts → PasswordGroup.vue.d.ts} +0 -0
- /package/dist/identity/profile/{SignInsPanel.vue.d.ts → SessionsGroup.vue.d.ts} +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { computed as j, ref as z, shallowRef as I, watch as F, getCurrentScope as J, onScopeDispose as Q } from "vue";
|
|
2
2
|
import { useRoute as O } from "vue-router";
|
|
3
|
-
import {
|
|
4
|
-
import { L as k, d as B, e as D, o as U, q as V } from "./http-
|
|
3
|
+
import { a as N } from "./error-Bb_dr_Ay.js";
|
|
4
|
+
import { L as k, d as B, e as D, o as U, q as V } from "./http-D_pBg22D.js";
|
|
5
5
|
function K(l, t, n, g) {
|
|
6
6
|
return typeof t == "string" ? t : { ...t, query: { ...t.query, [n]: D(l, g) } };
|
|
7
7
|
}
|
|
@@ -18,13 +18,13 @@ function le(l, t) {
|
|
|
18
18
|
};
|
|
19
19
|
}
|
|
20
20
|
const W = 8;
|
|
21
|
-
function
|
|
21
|
+
function ae(l, t) {
|
|
22
22
|
const n = O(), g = t.from ?? (() => typeof n.query[k] == "string" ? n.query[k] : null), q = t.stateKey ?? "query", f = z(null), p = z(0), h = I(null), m = I(null), S = z(!1), x = I(null), o = /* @__PURE__ */ new Map();
|
|
23
23
|
let d = 0, C = new AbortController(), E = !1;
|
|
24
|
-
function L(w, b,
|
|
24
|
+
function L(w, b, r) {
|
|
25
25
|
const v = { ...w, page: b }, i = V(v);
|
|
26
26
|
let u = o.get(i);
|
|
27
|
-
for (u ? o.delete(i) : (u = (async () => l.load(v, { signal:
|
|
27
|
+
for (u ? o.delete(i) : (u = (async () => l.load(v, { signal: r }))(), u.catch(() => {
|
|
28
28
|
o.get(i) === u && o.delete(i);
|
|
29
29
|
})), o.set(i, u); o.size > W; ) o.delete(o.keys().next().value);
|
|
30
30
|
return u.catch((e) => {
|
|
@@ -36,48 +36,48 @@ function re(l, t) {
|
|
|
36
36
|
f.value = null, h.value = null, m.value = null, p.value = 0;
|
|
37
37
|
}
|
|
38
38
|
async function _() {
|
|
39
|
-
const w = ++d, b = C.signal,
|
|
40
|
-
if (x.value =
|
|
39
|
+
const w = ++d, b = C.signal, r = B(l, g()), v = t.current();
|
|
40
|
+
if (x.value = r ? K(l, t.list, q, r) : null, !r || v === null || v === void 0 || v === "") {
|
|
41
41
|
T(), S.value = !1;
|
|
42
42
|
return;
|
|
43
43
|
}
|
|
44
44
|
S.value = !0, h.value = null, m.value = null;
|
|
45
|
-
const i = (e) => e ? e.rows.findIndex((
|
|
46
|
-
const s = D(l, { ...
|
|
45
|
+
const i = (e) => e ? e.rows.findIndex((a) => String(l.key(a)) === String(v)) : -1, u = (e, a) => {
|
|
46
|
+
const s = D(l, { ...r, page: e });
|
|
47
47
|
return {
|
|
48
|
-
key:
|
|
48
|
+
key: a,
|
|
49
49
|
get to() {
|
|
50
|
-
return { name: n.name ?? void 0, params: { ...n.params, [t.param]: String(
|
|
50
|
+
return { name: n.name ?? void 0, params: { ...n.params, [t.param]: String(a) }, query: { ...n.query, [k]: s }, hash: n.hash };
|
|
51
51
|
}
|
|
52
52
|
};
|
|
53
53
|
};
|
|
54
54
|
try {
|
|
55
|
-
let e =
|
|
55
|
+
let e = r.page, a = await L(r, e, b), s = i(a);
|
|
56
56
|
for (const y of [e - 1, e + 1]) {
|
|
57
|
-
if (s !== -1 || !
|
|
58
|
-
const c = await L(
|
|
59
|
-
c && M !== -1 && (e = y,
|
|
57
|
+
if (s !== -1 || !a || y < 1 || y > a.lastPage) continue;
|
|
58
|
+
const c = await L(r, y, b), M = i(c);
|
|
59
|
+
c && M !== -1 && (e = y, a = c, s = M);
|
|
60
60
|
}
|
|
61
61
|
if (w !== d) return;
|
|
62
|
-
if (!
|
|
62
|
+
if (!a || s === -1) {
|
|
63
63
|
T();
|
|
64
64
|
return;
|
|
65
65
|
}
|
|
66
|
-
const X =
|
|
66
|
+
const X = a.pageSize || r.pageSize, A = a.rows;
|
|
67
67
|
let P = null;
|
|
68
68
|
if (s > 0) P = u(e, l.key(A[s - 1]));
|
|
69
69
|
else if (e > 1) {
|
|
70
|
-
const y = await L(
|
|
70
|
+
const y = await L(r, e - 1, b), c = y?.rows[y.rows.length - 1];
|
|
71
71
|
c !== void 0 && (P = u(e - 1, l.key(c)));
|
|
72
72
|
}
|
|
73
73
|
let R = null;
|
|
74
74
|
if (s < A.length - 1) R = u(e, l.key(A[s + 1]));
|
|
75
|
-
else if (e <
|
|
76
|
-
const c = (await L(
|
|
75
|
+
else if (e < a.lastPage) {
|
|
76
|
+
const c = (await L(r, e + 1, b))?.rows[0];
|
|
77
77
|
c !== void 0 && (R = u(e + 1, l.key(c)));
|
|
78
78
|
}
|
|
79
79
|
if (w !== d) return;
|
|
80
|
-
p.value =
|
|
80
|
+
p.value = a.total, f.value = (e - 1) * X + s + 1, h.value = P, m.value = R, x.value = K(l, t.list, q, { ...r, page: e });
|
|
81
81
|
} catch (e) {
|
|
82
82
|
if (!N(e)) throw e;
|
|
83
83
|
} finally {
|
|
@@ -100,6 +100,6 @@ function re(l, t) {
|
|
|
100
100
|
} };
|
|
101
101
|
}
|
|
102
102
|
export {
|
|
103
|
-
|
|
103
|
+
ae as a,
|
|
104
104
|
le as u
|
|
105
105
|
};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import type { ShallowRef } from "vue";
|
|
1
|
+
import type { Component, ShallowRef } from "vue";
|
|
2
2
|
import type { IconName } from "../icon";
|
|
3
|
+
import type { Preferences } from "../modules/notification/entities";
|
|
3
4
|
import type { Hue } from "../state";
|
|
4
5
|
import type { Inbox } from "./inbox";
|
|
5
6
|
/** How a category of notification is drawn in the inbox: the icon of its row and the colour behind it. */
|
|
@@ -9,6 +10,24 @@ export interface NotificationCategoryLook {
|
|
|
9
10
|
/** One of the category hues; default neutral. */
|
|
10
11
|
hue?: Hue;
|
|
11
12
|
}
|
|
13
|
+
/**
|
|
14
|
+
* A section an application adds to the notification settings page: a title (and a line under it) in the page's own style, and the
|
|
15
|
+
* component, which renders its groups (`FormGroup`s of rows) and is given the person's `preferences` as a prop.
|
|
16
|
+
* The built-in sections sit at 0 (channels), 10 (quiet hours), 20 (this device) and 30 (devices); a section without an `order` comes
|
|
17
|
+
* after them, in the order given.
|
|
18
|
+
*/
|
|
19
|
+
export interface NotificationSettingsSection {
|
|
20
|
+
/** Unique among the sections and not one of the built-in ids (`channels`, `quiet`, `device`, `devices`). */
|
|
21
|
+
readonly id: string;
|
|
22
|
+
/** The application's message key of the section's title. */
|
|
23
|
+
readonly titleKey: string;
|
|
24
|
+
/** The key of the line under the title. */
|
|
25
|
+
readonly descriptionKey?: string;
|
|
26
|
+
readonly component: Component;
|
|
27
|
+
readonly order?: number;
|
|
28
|
+
/** Leaves the section out for these preferences. Default: always shown. */
|
|
29
|
+
readonly visible?: (preferences: Preferences) => boolean;
|
|
30
|
+
}
|
|
12
31
|
/** What the bell and the inbox read from the feature that installed them. */
|
|
13
32
|
export interface NotificationsContext {
|
|
14
33
|
/** The signed-in session's inbox; null while nobody is signed in (it is made and ended with the session). */
|
|
@@ -16,5 +35,9 @@ export interface NotificationsContext {
|
|
|
16
35
|
readonly categories: Readonly<Record<string, NotificationCategoryLook>>;
|
|
17
36
|
/** Whether the settings page is installed: the inbox shows its gear. */
|
|
18
37
|
readonly settings: boolean;
|
|
38
|
+
/** Whether web push is installed: its sections on the settings page (shown when the server has push) and the sync of this browser's subscription. */
|
|
39
|
+
readonly push: boolean;
|
|
40
|
+
/** The sections the application added to the settings page. */
|
|
41
|
+
readonly sections: readonly NotificationSettingsSection[];
|
|
19
42
|
}
|
|
20
43
|
export declare const notificationsContextKey: import("vue").InjectionKey<NotificationsContext>, useNotificationsContext: () => NotificationsContext;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type Feature } from "../app";
|
|
2
2
|
import type { Destination } from "../router";
|
|
3
|
-
import { type NotificationCategoryLook } from "./context";
|
|
3
|
+
import { type NotificationCategoryLook, type NotificationSettingsSection } from "./context";
|
|
4
4
|
export interface NotificationsFeatureOptions {
|
|
5
5
|
/** How each category the application declared in go-core is drawn: the icon of its rows and the colour behind it. A category without an entry gets the bell on a neutral tile. */
|
|
6
6
|
categories?: Readonly<Record<string, NotificationCategoryLook>>;
|
|
@@ -20,6 +20,15 @@ export interface NotificationsFeatureOptions {
|
|
|
20
20
|
* split at its dots; a category without a text is shown by its code. `false` leaves the page, the menu entry and the gear out. Default true.
|
|
21
21
|
*/
|
|
22
22
|
settings?: boolean;
|
|
23
|
+
/** Sections of the application's own on the settings page (`NotificationSettingsSection`: a title, groups of rows), placed by `order` among the built-in ones. */
|
|
24
|
+
settingsSections?: readonly NotificationSettingsSection[];
|
|
25
|
+
/**
|
|
26
|
+
* Web push: on the settings page the "This device" section (turn push on for this browser) and the list of devices, both shown only
|
|
27
|
+
* when the server has push (`push_available`), the push switch next to e-mail for each category, and, for a signed-in person whose
|
|
28
|
+
* browser already holds a subscription, registering it again once per session (and removing it when they sign out). The
|
|
29
|
+
* service worker is the application's own: it imports `@wssto2/vue-core/push-worker`. `false` leaves all of it out. Default true.
|
|
30
|
+
*/
|
|
31
|
+
push?: boolean;
|
|
23
32
|
}
|
|
24
33
|
/**
|
|
25
34
|
* The in-app notifications of go-core's notification module, one feature to install: the bell with the
|
|
@@ -6,7 +6,7 @@ export type * as DeadLetterInputs from "../modules/events/schemas";
|
|
|
6
6
|
export { eventsRoutes } from "../modules/events/routes";
|
|
7
7
|
export { notificationsFeature } from "./feature";
|
|
8
8
|
export type { NotificationsFeatureOptions } from "./feature";
|
|
9
|
-
export type { NotificationCategoryLook } from "./context";
|
|
9
|
+
export type { NotificationCategoryLook, NotificationSettingsSection } from "./context";
|
|
10
10
|
export { notificationSettingsRoutes } from "./settings/routes";
|
|
11
11
|
export type { NotificationSettingsRoutes } from "./settings/routes";
|
|
12
12
|
export { deadLettersRoutes } from "./deadletters/routes";
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Devices } from "./devices";
|
|
2
|
+
/**
|
|
3
|
+
* The browsers where the person turned push on, one row each: the name (the server's, from the browser), "This device" on
|
|
4
|
+
* the one in use, "Disabled" on one the server gave up on, when it was last used and last got a push. Rename in place,
|
|
5
|
+
* send a test (not to a disabled device), remove after a confirmation: the three are the row's "⋯" menu.
|
|
6
|
+
*/
|
|
7
|
+
type __VLS_Props = {
|
|
8
|
+
devices: Devices;
|
|
9
|
+
hash: string | null;
|
|
10
|
+
};
|
|
11
|
+
declare const _default: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
12
|
+
export default _default;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { ThisDevice } from "./thisDevice";
|
|
2
|
+
/**
|
|
3
|
+
* Push on this browser, one state at a time: still reading it, not supported, an iPhone Safari tab (add the app to the
|
|
4
|
+
* home screen first), blocked (how to allow it again), off (turn it on from a tap), on. The permission prompt only ever
|
|
5
|
+
* comes from the action row.
|
|
6
|
+
*/
|
|
7
|
+
type __VLS_Props = {
|
|
8
|
+
device: ThisDevice;
|
|
9
|
+
};
|
|
10
|
+
declare const _default: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
11
|
+
changed: () => any;
|
|
12
|
+
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
13
|
+
onChanged?: (() => any) | undefined;
|
|
14
|
+
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
15
|
+
export default _default;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { Subscription } from "../../modules/notification/schemas";
|
|
2
|
+
/**
|
|
3
|
+
* What this browser can do with Web Push:
|
|
4
|
+
* - `supported`: the permission can be asked for from a tap;
|
|
5
|
+
* - `ios-browser`: an iPhone or iPad Safari tab, where push exists only in the app added to the home screen;
|
|
6
|
+
* - `unsupported`: no Push API or no service worker (an old browser, a dev server without a worker, an iframe).
|
|
7
|
+
*/
|
|
8
|
+
export type PushSupport = "supported" | "ios-browser" | "unsupported";
|
|
9
|
+
/** The browser's side of push, so the logic around it is testable and the real thing is one small adapter. */
|
|
10
|
+
export interface PushBrowser {
|
|
11
|
+
support(): PushSupport;
|
|
12
|
+
/** `unknown` where there is no Notification API. */
|
|
13
|
+
permission(): NotificationPermission | "unknown";
|
|
14
|
+
/** Must be called from a tap. */
|
|
15
|
+
requestPermission(): Promise<NotificationPermission>;
|
|
16
|
+
/** This browser's subscription with the application's service worker; null when there is none (or no worker). */
|
|
17
|
+
subscription(): Promise<PushSubscription | null>;
|
|
18
|
+
/** Subscribes with the server's public key (base64url); null without a worker. */
|
|
19
|
+
subscribe(publicKey: string): Promise<PushSubscription | null>;
|
|
20
|
+
}
|
|
21
|
+
/** The VAPID public key (base64url) as the Push API wants it. */
|
|
22
|
+
export declare function applicationServerKey(base64url: string): Uint8Array<ArrayBuffer>;
|
|
23
|
+
/** Whether a subscription was made with this key; a rotated key needs a new subscription. A browser that does not report its key counts as matching. */
|
|
24
|
+
export declare function subscribedWithKey(subscription: PushSubscription, base64url: string): boolean;
|
|
25
|
+
/** The body go-core registers a device with; null for a subscription without keys. */
|
|
26
|
+
export declare function toSubscription(subscription: PushSubscription): Subscription | null;
|
|
27
|
+
/** Hex SHA-256 of the endpoint: the `endpoint_hash` go-core keeps for a device, which tells "this device" in the list. */
|
|
28
|
+
export declare function endpointHash(endpoint: string): Promise<string>;
|
|
29
|
+
interface Environment {
|
|
30
|
+
userAgent: string;
|
|
31
|
+
maxTouchPoints: number;
|
|
32
|
+
standalone: boolean;
|
|
33
|
+
hasPushManager: boolean;
|
|
34
|
+
hasNotification: boolean;
|
|
35
|
+
hasServiceWorker: boolean;
|
|
36
|
+
}
|
|
37
|
+
export declare function detectPushSupport(env: Environment): PushSupport;
|
|
38
|
+
/** The real browser. Reads the globals when asked, never on import. */
|
|
39
|
+
export declare function browserPush(): PushBrowser;
|
|
40
|
+
export {};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type ComputedRef } from "vue";
|
|
2
|
+
import type { HttpClient } from "../../client";
|
|
3
|
+
import type { Device } from "../../modules/notification/entities";
|
|
4
|
+
import { type Load } from "../../state";
|
|
5
|
+
/** The devices where the person turned push on, and what can be done with each. */
|
|
6
|
+
export interface Devices {
|
|
7
|
+
readonly loaded: Load<Device[]>;
|
|
8
|
+
readonly list: ComputedRef<readonly Device[]>;
|
|
9
|
+
rename(id: number, label: string): Promise<void>;
|
|
10
|
+
remove(id: number): Promise<void>;
|
|
11
|
+
/** Sends a test push to one device; a refusal or a failure rejects. */
|
|
12
|
+
test(id: number): Promise<void>;
|
|
13
|
+
}
|
|
14
|
+
/** The devices of the signed-in person, from `GET /v1/notifications/devices`. */
|
|
15
|
+
export declare function useDevices(http?: HttpClient): Devices;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { type Ref } from "vue";
|
|
2
|
+
import type { HttpClient } from "../../client";
|
|
3
|
+
import { type PushBrowser } from "./browser";
|
|
4
|
+
/** What the "this device" panel shows: one state at a time. */
|
|
5
|
+
export type ThisDeviceState = "loading" | "unsupported" | "ios-browser" | "denied" | "off" | "on";
|
|
6
|
+
export interface ThisDevice {
|
|
7
|
+
readonly state: Readonly<Ref<ThisDeviceState>>;
|
|
8
|
+
/** `endpoint_hash` of this browser's subscription, to mark its row in the device list; null without one. */
|
|
9
|
+
readonly hash: Readonly<Ref<string | null>>;
|
|
10
|
+
readonly busy: Readonly<Ref<boolean>>;
|
|
11
|
+
/** Reads this browser's state; registers nothing. */
|
|
12
|
+
inspect(): Promise<void>;
|
|
13
|
+
/** Asks for the permission and registers this browser. Call it from a tap. Resolves to the state it ended in. */
|
|
14
|
+
enable(): Promise<ThisDeviceState>;
|
|
15
|
+
/** Stops push on this browser only: the device is removed from the account and the subscription cancelled. */
|
|
16
|
+
disable(): Promise<void>;
|
|
17
|
+
}
|
|
18
|
+
/** Cancels this browser's subscription at the account and in the browser; a failing server call never keeps the browser subscribed. */
|
|
19
|
+
export declare function unsubscribeThisBrowser(http: HttpClient, browser?: PushBrowser, signal?: AbortSignal): Promise<void>;
|
|
20
|
+
/**
|
|
21
|
+
* Re-registers a subscription this browser already holds, for the person who just signed in: go-core makes registering
|
|
22
|
+
* again idempotent, and it is what refreshes the device's last use, re-enables a disabled one and moves a shared computer's
|
|
23
|
+
* endpoint to the person signed in now. Once per session, never prompts; a subscription made with a rotated key is replaced,
|
|
24
|
+
* one the browser's permission no longer backs is dropped.
|
|
25
|
+
*/
|
|
26
|
+
export declare function syncThisBrowser(http: HttpClient, browser: PushBrowser, signal?: AbortSignal): Promise<void>;
|
|
27
|
+
/** This browser's push subscription, for the settings panel. */
|
|
28
|
+
export declare function useThisDevice(http?: HttpClient, browser?: PushBrowser): ThisDevice;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Preferences } from "../../modules/notification/entities";
|
|
2
|
+
import type { Channel } from "./state";
|
|
3
|
+
/**
|
|
4
|
+
* "Notify me about": a group per category the application declared, with a switch per channel that is available (e-mail, push),
|
|
5
|
+
* saved as it is switched (one on its way ignores another touch). A setting someone else enforces is a row that reads, with
|
|
6
|
+
* who decides. In-app is not a switch: the line under the title says it is always on. Without e-mail and push there is a sentence and no switches.
|
|
7
|
+
*/
|
|
8
|
+
type __VLS_Props = {
|
|
9
|
+
preferences: Preferences;
|
|
10
|
+
};
|
|
11
|
+
declare const _default: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
12
|
+
change: (category: string, channel: Channel, enabled: boolean) => any;
|
|
13
|
+
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
14
|
+
onChange?: ((category: string, channel: Channel, enabled: boolean) => any) | undefined;
|
|
15
|
+
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
16
|
+
export default _default;
|
|
@@ -3,7 +3,9 @@ import type { QuietHoursInput } from "../../modules/notification/schemas";
|
|
|
3
3
|
/**
|
|
4
4
|
* Quiet hours: e-mail made inside them waits until they end (the in-app notification never does). On or off, from and to
|
|
5
5
|
* on the clock of the application's time zone, and one line in words. A window may run over midnight; the same time twice is
|
|
6
|
-
* refused on the field.
|
|
6
|
+
* refused on the field. Edited in place and saved as it changes, like the channel switches (the switch at once, a time when it is
|
|
7
|
+
* committed and valid; a change made while a save is on its way follows it): a refusal or a failure puts the last saved values back
|
|
8
|
+
* and is said under the group. Not a sheet: it is one switch and two times, and what they do to the summary is worth seeing as you edit.
|
|
7
9
|
*/
|
|
8
10
|
type __VLS_Props = {
|
|
9
11
|
quietHours: QuietHours;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Preferences } from "../../modules/notification/entities";
|
|
2
|
+
import type { Settings } from "./settings";
|
|
3
|
+
import type { Channel } from "./state";
|
|
4
|
+
/**
|
|
5
|
+
* The sections of the settings page in their order: the built-in ones (channels 0, quiet hours 10, this device 20, devices 30)
|
|
6
|
+
* and the application's own sections (each under its own title, like the built-in ones). Push is created only here, once the preferences say the server has it.
|
|
7
|
+
*/
|
|
8
|
+
type __VLS_Props = {
|
|
9
|
+
preferences: Preferences;
|
|
10
|
+
settings: Settings;
|
|
11
|
+
};
|
|
12
|
+
declare const _default: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
13
|
+
change: (category: string, channel: Channel, enabled: boolean) => any;
|
|
14
|
+
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
15
|
+
onChange?: ((category: string, channel: Channel, enabled: boolean) => any) | undefined;
|
|
16
|
+
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
17
|
+
export default _default;
|
|
@@ -3,14 +3,15 @@ import type { HttpClient } from "../../client";
|
|
|
3
3
|
import type { Preferences, QuietHours } from "../../modules/notification/entities";
|
|
4
4
|
import type { QuietHoursInput } from "../../modules/notification/schemas";
|
|
5
5
|
import { type Load } from "../../state";
|
|
6
|
+
import { type Channel } from "./state";
|
|
6
7
|
/** One person's notification settings: what the page shows and the two ways to change it. */
|
|
7
8
|
export interface Settings {
|
|
8
9
|
/** The load of the preferences, as `AsyncSection` takes it. */
|
|
9
10
|
readonly loaded: Load<Preferences>;
|
|
10
|
-
/** The categories whose
|
|
11
|
+
/** The categories whose switches are on their way to the server (a category at a time): each is locked meanwhile, so a change never overtakes another. */
|
|
11
12
|
readonly saving: Readonly<Ref<readonly string[]>>;
|
|
12
|
-
/** Switches
|
|
13
|
-
|
|
13
|
+
/** Switches one channel (e-mail or push) of one category at once and saves it; a refusal (an enforced setting) or a failure puts the switch back and rejects. */
|
|
14
|
+
setChannel(category: string, channel: Channel, enabled: boolean): Promise<void>;
|
|
14
15
|
/** Saves the quiet hours; the server's answer is what is shown after. A refusal rejects and changes nothing. */
|
|
15
16
|
saveQuietHours(input: QuietHoursInput): Promise<QuietHours>;
|
|
16
17
|
}
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import type { CategorySettings, Preferences, QuietHours } from "../../modules/notification/entities";
|
|
2
2
|
/** The preferences with one category's setting replaced (what the server answered, or what is shown before it does). */
|
|
3
3
|
export declare function withCategory(preferences: Preferences, settings: CategorySettings): Preferences;
|
|
4
|
-
/**
|
|
5
|
-
export
|
|
4
|
+
/** The channels a category can be switched on: e-mail and web push. In-app is always on. */
|
|
5
|
+
export type Channel = "email" | "push";
|
|
6
|
+
/** One category's channel switched here, before the server answers: it is the person's own choice now. */
|
|
7
|
+
export declare function withChannel(preferences: Preferences, category: string, channel: Channel, enabled: boolean): Preferences;
|
|
6
8
|
/** Minutes after midnight as the `"HH:mm"` a time field edits. */
|
|
7
9
|
export declare function timeOfMinutes(minutes: number): string;
|
|
8
10
|
/** A time field's `"HH:mm"` as minutes after midnight; null when it is empty. */
|