@eifi1/ui-kit 0.46.0 → 0.47.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/README.md +12 -11
- package/dist/admin/user-roster.d.ts +6 -2
- package/dist/admin/user-roster.js +7 -3
- package/dist/admin/user-roster.js.map +1 -1
- package/dist/components/data-table-row-actions.d.ts +22 -5
- package/dist/components/data-table-row-actions.js +14 -5
- package/dist/components/data-table-row-actions.js.map +1 -1
- package/dist/components/data-table.d.ts +16 -1
- package/dist/components/data-table.js +20 -1
- package/dist/components/data-table.js.map +1 -1
- package/dist/components/row-actions.d.ts +37 -5
- package/dist/components/row-actions.js +22 -5
- package/dist/components/row-actions.js.map +1 -1
- package/dist/components/tabs.d.ts +24 -16
- package/dist/components/tabs.js +37 -31
- package/dist/components/tabs.js.map +1 -1
- package/dist/components/translation-review.js +3 -1
- package/dist/components/translation-review.js.map +1 -1
- package/dist/components/ui-labels.js +1 -1
- package/dist/components/ui-labels.js.map +1 -1
- package/dist/data-table.d.ts +1 -0
- package/dist/data-table.js +2 -0
- package/dist/data-table.js.map +1 -1
- package/dist/eslint/index.js +1 -1
- package/dist/feedback/feedback-attachment-download.d.ts +19 -0
- package/dist/feedback/feedback-attachment-download.js +59 -0
- package/dist/feedback/feedback-attachment-download.js.map +1 -0
- package/dist/feedback/feedback-comment-thread.d.ts +17 -6
- package/dist/feedback/feedback-comment-thread.js +21 -2
- package/dist/feedback/feedback-comment-thread.js.map +1 -1
- package/dist/feedback/feedback-entry-file.d.ts +61 -0
- package/dist/feedback/feedback-entry-file.js +76 -0
- package/dist/feedback/feedback-entry-file.js.map +1 -0
- package/dist/feedback/feedback-public-list.d.ts +18 -5
- package/dist/feedback/feedback-public-list.js +8 -0
- package/dist/feedback/feedback-public-list.js.map +1 -1
- package/dist/feedback/feedback-public-types.d.ts +7 -2
- package/dist/feedback/feedback-replies.d.ts +26 -9
- package/dist/feedback/feedback-replies.js +20 -10
- package/dist/feedback/feedback-replies.js.map +1 -1
- package/dist/feedback/feedback-row-detail-labels.js +0 -1
- package/dist/feedback/feedback-row-detail-labels.js.map +1 -1
- package/dist/feedback/feedback-row-detail.d.ts +23 -10
- package/dist/feedback/feedback-row-detail.js +10 -54
- package/dist/feedback/feedback-row-detail.js.map +1 -1
- package/dist/feedback.d.ts +1 -0
- package/dist/feedback.js +1 -0
- package/dist/feedback.js.map +1 -1
- package/dist/i18n/german.js +1 -6
- package/dist/i18n/german.js.map +1 -1
- package/dist/i18n/locales/en.js +1 -6
- package/dist/i18n/locales/en.js.map +2 -2
- package/dist/i18n/locales/es.js +1 -6
- package/dist/i18n/locales/es.js.map +1 -1
- package/dist/i18n/locales/fr.js +1 -6
- package/dist/i18n/locales/fr.js.map +1 -1
- package/dist/i18n/locales/hu.js +1 -6
- package/dist/i18n/locales/hu.js.map +1 -1
- package/dist/i18n/locales/it.js +1 -6
- package/dist/i18n/locales/it.js.map +1 -1
- package/dist/i18n/locales/zh.js +1 -6
- package/dist/i18n/locales/zh.js.map +1 -1
- package/dist/i18n/review.js +0 -1
- package/dist/i18n/review.js.map +1 -1
- package/dist/index.d.ts +5 -7
- package/dist/index.js +14 -5
- package/dist/index.js.map +1 -1
- package/dist/notifications/device-zone-report.d.ts +7 -0
- package/dist/notifications/device-zone-report.js +8 -1
- package/dist/notifications/device-zone-report.js.map +1 -1
- package/dist/notifications/feedback-notification-types.d.ts +14 -1
- package/dist/notifications/feedback-notification-types.js +5 -1
- package/dist/notifications/feedback-notification-types.js.map +1 -1
- package/dist/notifications/notification-bell.d.ts +1 -1
- package/dist/notifications/notification-bell.js.map +1 -1
- package/dist/notifications/notification-inbox.d.ts +12 -3
- package/dist/notifications/notification-inbox.js +2 -2
- package/dist/notifications/notification-inbox.js.map +1 -1
- package/dist/notifications/notification-types.d.ts +0 -3
- package/dist/notifications/notifications-labels.d.ts +2 -13
- package/dist/notifications/notifications-labels.js +0 -4
- package/dist/notifications/notifications-labels.js.map +1 -1
- package/dist/notifications/quiet-hours-setting.d.ts +3 -8
- package/dist/notifications/quiet-hours-setting.js +8 -3
- package/dist/notifications/quiet-hours-setting.js.map +1 -1
- package/dist/offline/local-first.d.ts +9 -0
- package/dist/offline/local-first.js +14 -0
- package/dist/offline/local-first.js.map +1 -1
- package/dist/offline.d.ts +1 -1
- package/dist/offline.js +2 -1
- package/dist/offline.js.map +1 -1
- package/dist/sync/db.d.ts +17 -0
- package/dist/sync/db.js +10 -1
- package/dist/sync/db.js.map +1 -1
- package/dist/sync/engine.d.ts +11 -9
- package/dist/sync/engine.js.map +1 -1
- package/dist/sync/hooks.d.ts +5 -0
- package/dist/sync/hooks.js.map +1 -1
- package/dist/sync.d.ts +2 -2
- package/dist/sync.js +5 -2
- package/dist/sync.js.map +1 -1
- package/dist/theme/theme-store.d.ts +4 -4
- package/dist/theme/theme-store.js +1 -0
- package/dist/theme/theme-store.js.map +1 -1
- package/package.json +2 -2
- package/src/admin/user-roster.tsx +12 -3
- package/src/components/data-table-row-actions.tsx +33 -9
- package/src/components/data-table.tsx +46 -3
- package/src/components/row-actions.tsx +73 -10
- package/src/components/tabs.tsx +66 -44
- package/src/components/translation-review.tsx +3 -1
- package/src/components/ui-labels.ts +1 -1
- package/src/data-table.ts +3 -0
- package/src/feedback/feedback-attachment-download.tsx +77 -0
- package/src/feedback/feedback-comment-thread.tsx +42 -7
- package/src/feedback/feedback-entry-file.tsx +150 -0
- package/src/feedback/feedback-public-list.tsx +25 -4
- package/src/feedback/feedback-public-types.ts +7 -2
- package/src/feedback/feedback-replies.tsx +46 -19
- package/src/feedback/feedback-row-detail-labels.ts +0 -1
- package/src/feedback/feedback-row-detail.tsx +35 -86
- package/src/feedback.ts +1 -0
- package/src/i18n/german.ts +1 -6
- package/src/i18n/locales/es.ts +1 -6
- package/src/i18n/locales/fr.ts +1 -6
- package/src/i18n/locales/hu.ts +1 -6
- package/src/i18n/locales/it.ts +1 -6
- package/src/i18n/locales/zh.ts +1 -6
- package/src/i18n/review.ts +0 -1
- package/src/index.ts +19 -5
- package/src/notifications/device-zone-report.ts +14 -1
- package/src/notifications/feedback-notification-types.ts +19 -1
- package/src/notifications/notification-bell.tsx +1 -1
- package/src/notifications/notification-inbox.tsx +15 -5
- package/src/notifications/notification-types.ts +0 -3
- package/src/notifications/notifications-labels.ts +2 -17
- package/src/notifications/quiet-hours-setting.tsx +16 -11
- package/src/offline/local-first.ts +24 -0
- package/src/offline.ts +1 -1
- package/src/sync/db.ts +22 -0
- package/src/sync/engine.ts +11 -9
- package/src/sync/hooks.ts +5 -0
- package/src/sync.ts +2 -2
- package/src/theme/theme-store.ts +13 -5
- package/dist/notifications/time-zone-select.d.ts +0 -53
- package/dist/notifications/time-zone-select.js +0 -76
- package/dist/notifications/time-zone-select.js.map +0 -1
- package/src/notifications/time-zone-select.tsx +0 -129
package/src/i18n/german.ts
CHANGED
|
@@ -202,7 +202,6 @@ export function germanLabels(numberLocale: string): UiKitLabels {
|
|
|
202
202
|
outcomeUpdate: "Aktualisieren",
|
|
203
203
|
outcomePlaceholder: "Was wurde umgesetzt, entschieden oder warum nicht.",
|
|
204
204
|
rework: "Nacharbeit",
|
|
205
|
-
openPage: "Seite öffnen",
|
|
206
205
|
reworkTitle: "Zur Nacharbeit senden",
|
|
207
206
|
reworkSend: "Nacharbeit senden",
|
|
208
207
|
reworkPlaceholder: "Was muss noch angepasst werden? Neue Anforderungen oder Richtungswechsel.",
|
|
@@ -541,7 +540,7 @@ export function germanLabels(numberLocale: string): UiKitLabels {
|
|
|
541
540
|
add: "Tab hinzufügen",
|
|
542
541
|
remove: (tab) => `${tab} entfernen`,
|
|
543
542
|
more: "Weitere Tabs",
|
|
544
|
-
|
|
543
|
+
previous: "Vorherige Tabs",
|
|
545
544
|
},
|
|
546
545
|
appShell: {
|
|
547
546
|
collapse: "Seitenleiste einklappen",
|
|
@@ -1184,7 +1183,6 @@ export function germanLabels(numberLocale: string): UiKitLabels {
|
|
|
1184
1183
|
notifications: {
|
|
1185
1184
|
typesTitle: "Benachrichtigungen zu",
|
|
1186
1185
|
typesDescription: "Wählen Sie, worüber Sie benachrichtigt werden, und wie.",
|
|
1187
|
-
byEmail: "Per E-Mail",
|
|
1188
1186
|
channelApp: "In der App",
|
|
1189
1187
|
channelPush: "Push",
|
|
1190
1188
|
channelEmail: "E-Mail",
|
|
@@ -1207,9 +1205,6 @@ export function germanLabels(numberLocale: string): UiKitLabels {
|
|
|
1207
1205
|
quietFrom: "Von",
|
|
1208
1206
|
quietUntil: "Bis",
|
|
1209
1207
|
quietIncomplete: "Legen Sie eine Start- und eine Endzeit fest. Ruhezeiten gelten erst, wenn beide gesetzt sind.",
|
|
1210
|
-
timeZone: "Zeitzone",
|
|
1211
|
-
deviceZone: (zone) => `Dieses Gerät ist in der Zeitzone ${zone}.`,
|
|
1212
|
-
useDeviceZone: "Übernehmen",
|
|
1213
1208
|
regions: {
|
|
1214
1209
|
Africa: "Afrika",
|
|
1215
1210
|
America: "Amerika",
|
package/src/i18n/locales/es.ts
CHANGED
|
@@ -193,7 +193,6 @@ export function uiKitLabelsEs(numberLocale = "es-ES"): UiKitLabels {
|
|
|
193
193
|
outcomeUpdate: "Actualizar",
|
|
194
194
|
outcomePlaceholder: "Qué se ha hecho o decidido, o por qué no se abordará.",
|
|
195
195
|
rework: "Corrección",
|
|
196
|
-
openPage: "Abrir página",
|
|
197
196
|
reworkTitle: "Devolver para corrección",
|
|
198
197
|
reworkSend: "Solicitar corrección",
|
|
199
198
|
reworkPlaceholder: "¿Qué falta por pulir? Nuevos requisitos o cambios de rumbo.",
|
|
@@ -528,7 +527,7 @@ export function uiKitLabelsEs(numberLocale = "es-ES"): UiKitLabels {
|
|
|
528
527
|
add: "Añadir pestaña",
|
|
529
528
|
remove: (tab) => `Quitar ${tab}`,
|
|
530
529
|
more: "Más pestañas",
|
|
531
|
-
|
|
530
|
+
previous: "Pestañas anteriores",
|
|
532
531
|
},
|
|
533
532
|
appShell: {
|
|
534
533
|
collapse: "Contraer barra lateral",
|
|
@@ -1158,7 +1157,6 @@ export function uiKitLabelsEs(numberLocale = "es-ES"): UiKitLabels {
|
|
|
1158
1157
|
notifications: {
|
|
1159
1158
|
typesTitle: "Avisarme de",
|
|
1160
1159
|
typesDescription: "Elija de qué se entera y cómo.",
|
|
1161
|
-
byEmail: "Por correo electrónico",
|
|
1162
1160
|
channelApp: "En la app",
|
|
1163
1161
|
channelPush: "Push",
|
|
1164
1162
|
channelEmail: "Correo",
|
|
@@ -1182,9 +1180,6 @@ export function uiKitLabelsEs(numberLocale = "es-ES"): UiKitLabels {
|
|
|
1182
1180
|
quietUntil: "Hasta",
|
|
1183
1181
|
quietIncomplete:
|
|
1184
1182
|
"Indique una hora de inicio y una de fin. Las horas de silencio se aplican cuando ambas están definidas.",
|
|
1185
|
-
timeZone: "Zona horaria",
|
|
1186
|
-
deviceZone: (zone) => `Este dispositivo está en ${zone}.`,
|
|
1187
|
-
useDeviceZone: "Usarla",
|
|
1188
1183
|
regions: {
|
|
1189
1184
|
Africa: "África",
|
|
1190
1185
|
America: "América",
|
package/src/i18n/locales/fr.ts
CHANGED
|
@@ -191,7 +191,6 @@ export function uiKitLabelsFr(numberLocale = "fr-FR"): UiKitLabels {
|
|
|
191
191
|
outcomeUpdate: "Mettre à jour",
|
|
192
192
|
outcomePlaceholder: "Ce qui a été fait ou décidé, ou pourquoi ce point ne sera pas traité.",
|
|
193
193
|
rework: "Reprise",
|
|
194
|
-
openPage: "Ouvrir la page",
|
|
195
194
|
reworkTitle: "Renvoyer pour reprise",
|
|
196
195
|
reworkSend: "Demander la reprise",
|
|
197
196
|
reworkPlaceholder:
|
|
@@ -536,7 +535,7 @@ export function uiKitLabelsFr(numberLocale = "fr-FR"): UiKitLabels {
|
|
|
536
535
|
add: "Ajouter un onglet",
|
|
537
536
|
remove: (tab) => `Supprimer ${tab}`,
|
|
538
537
|
more: "Autres onglets",
|
|
539
|
-
|
|
538
|
+
previous: "Onglets précédents",
|
|
540
539
|
},
|
|
541
540
|
appShell: {
|
|
542
541
|
collapse: "Réduire la barre latérale",
|
|
@@ -1186,7 +1185,6 @@ export function uiKitLabelsFr(numberLocale = "fr-FR"): UiKitLabels {
|
|
|
1186
1185
|
notifications: {
|
|
1187
1186
|
typesTitle: "Me notifier pour",
|
|
1188
1187
|
typesDescription: "Choisissez ce dont vous êtes averti, et comment.",
|
|
1189
|
-
byEmail: "Par e-mail",
|
|
1190
1188
|
channelApp: "Dans l’app",
|
|
1191
1189
|
channelPush: "Push",
|
|
1192
1190
|
channelEmail: "E-mail",
|
|
@@ -1210,9 +1208,6 @@ export function uiKitLabelsFr(numberLocale = "fr-FR"): UiKitLabels {
|
|
|
1210
1208
|
quietUntil: "Jusqu’à",
|
|
1211
1209
|
quietIncomplete:
|
|
1212
1210
|
"Définissez une heure de début et une heure de fin. Les heures calmes s’appliquent une fois les deux renseignées.",
|
|
1213
|
-
timeZone: "Fuseau horaire",
|
|
1214
|
-
deviceZone: (zone) => `Cet appareil est dans le fuseau ${zone}.`,
|
|
1215
|
-
useDeviceZone: "L’utiliser",
|
|
1216
1211
|
regions: {
|
|
1217
1212
|
Africa: "Afrique",
|
|
1218
1213
|
America: "Amériques",
|
package/src/i18n/locales/hu.ts
CHANGED
|
@@ -190,7 +190,6 @@ export function uiKitLabelsHu(numberLocale = "hu-HU"): UiKitLabels {
|
|
|
190
190
|
outcomeUpdate: "Frissítés",
|
|
191
191
|
outcomePlaceholder: "Mi készült el, mi a döntés, vagy miért nem valósul meg.",
|
|
192
192
|
rework: "Átdolgozás",
|
|
193
|
-
openPage: "Oldal megnyitása",
|
|
194
193
|
reworkTitle: "Visszaküldés átdolgozásra",
|
|
195
194
|
reworkSend: "Átdolgozás kérése",
|
|
196
195
|
reworkPlaceholder: "Min kell még finomítani? Új feltételek vagy irányváltás.",
|
|
@@ -526,7 +525,7 @@ export function uiKitLabelsHu(numberLocale = "hu-HU"): UiKitLabels {
|
|
|
526
525
|
add: "Lap hozzáadása",
|
|
527
526
|
remove: (tab) => `${tab} eltávolítása`,
|
|
528
527
|
more: "További lapok",
|
|
529
|
-
|
|
528
|
+
previous: "Előző lapok",
|
|
530
529
|
},
|
|
531
530
|
appShell: {
|
|
532
531
|
collapse: "Oldalsáv összecsukása",
|
|
@@ -1169,7 +1168,6 @@ export function uiKitLabelsHu(numberLocale = "hu-HU"): UiKitLabels {
|
|
|
1169
1168
|
notifications: {
|
|
1170
1169
|
typesTitle: "Értesítést kérek ezekről",
|
|
1171
1170
|
typesDescription: "Válassza ki, miről és hogyan kapjon értesítést.",
|
|
1172
|
-
byEmail: "E-mailben",
|
|
1173
1171
|
channelApp: "Az appban",
|
|
1174
1172
|
channelPush: "Push",
|
|
1175
1173
|
channelEmail: "E-mail",
|
|
@@ -1193,9 +1191,6 @@ export function uiKitLabelsHu(numberLocale = "hu-HU"): UiKitLabels {
|
|
|
1193
1191
|
quietUntil: "Eddig",
|
|
1194
1192
|
quietIncomplete:
|
|
1195
1193
|
"Adjon meg kezdő és záró időpontot is. A csendes órák csak akkor lépnek életbe, ha mindkettő be van állítva.",
|
|
1196
|
-
timeZone: "Időzóna",
|
|
1197
|
-
deviceZone: (zone) => `Ez az eszköz ebben az időzónában van: ${zone}.`,
|
|
1198
|
-
useDeviceZone: "Ezt használom",
|
|
1199
1194
|
regions: {
|
|
1200
1195
|
Africa: "Afrika",
|
|
1201
1196
|
America: "Amerika",
|
package/src/i18n/locales/it.ts
CHANGED
|
@@ -193,7 +193,6 @@ export function uiKitLabelsIt(numberLocale = "it-IT"): UiKitLabels {
|
|
|
193
193
|
outcomeUpdate: "Aggiorna",
|
|
194
194
|
outcomePlaceholder: "Cosa è stato fatto o deciso, oppure perché non verrà affrontato.",
|
|
195
195
|
rework: "Rielaborazione",
|
|
196
|
-
openPage: "Apri pagina",
|
|
197
196
|
reworkTitle: "Rimanda in rielaborazione",
|
|
198
197
|
reworkSend: "Richiedi rielaborazione",
|
|
199
198
|
reworkPlaceholder: "Cosa va ancora perfezionato? Eventuali nuovi vincoli o cambi di direzione.",
|
|
@@ -531,7 +530,7 @@ export function uiKitLabelsIt(numberLocale = "it-IT"): UiKitLabels {
|
|
|
531
530
|
add: "Aggiungi scheda",
|
|
532
531
|
remove: (tab) => `Rimuovi ${tab}`,
|
|
533
532
|
more: "Altre schede",
|
|
534
|
-
|
|
533
|
+
previous: "Schede precedenti",
|
|
535
534
|
},
|
|
536
535
|
appShell: {
|
|
537
536
|
collapse: "Comprimi barra laterale",
|
|
@@ -1170,7 +1169,6 @@ export function uiKitLabelsIt(numberLocale = "it-IT"): UiKitLabels {
|
|
|
1170
1169
|
notifications: {
|
|
1171
1170
|
typesTitle: "Avvisami per",
|
|
1172
1171
|
typesDescription: "Scelga di cosa essere avvisato, e come.",
|
|
1173
|
-
byEmail: "Per e-mail",
|
|
1174
1172
|
channelApp: "Nell’app",
|
|
1175
1173
|
channelPush: "Push",
|
|
1176
1174
|
channelEmail: "E-mail",
|
|
@@ -1194,9 +1192,6 @@ export function uiKitLabelsIt(numberLocale = "it-IT"): UiKitLabels {
|
|
|
1194
1192
|
quietUntil: "Alle",
|
|
1195
1193
|
quietIncomplete:
|
|
1196
1194
|
"Imposti sia l’ora di inizio sia quella di fine. Le ore di silenzio valgono solo quando sono impostate entrambe.",
|
|
1197
|
-
timeZone: "Fuso orario",
|
|
1198
|
-
deviceZone: (zone) => `Questo dispositivo è nel fuso ${zone}.`,
|
|
1199
|
-
useDeviceZone: "Usa questo",
|
|
1200
1195
|
regions: {
|
|
1201
1196
|
Africa: "Africa",
|
|
1202
1197
|
America: "Americhe",
|
package/src/i18n/locales/zh.ts
CHANGED
|
@@ -183,7 +183,6 @@ export function uiKitLabelsZh(numberLocale = "zh-CN"): UiKitLabels {
|
|
|
183
183
|
outcomeUpdate: "更新",
|
|
184
184
|
outcomePlaceholder: "做了什么、作出了什么决定,或为什么不予处理。",
|
|
185
185
|
rework: "返工",
|
|
186
|
-
openPage: "打开页面",
|
|
187
186
|
reworkTitle: "退回返工",
|
|
188
187
|
reworkSend: "提交返工",
|
|
189
188
|
reworkPlaceholder: "还有哪些需要完善?如有新的限制或方向调整,请一并说明。",
|
|
@@ -514,7 +513,7 @@ export function uiKitLabelsZh(numberLocale = "zh-CN"): UiKitLabels {
|
|
|
514
513
|
// Quoted, so the spacing rule holds whether the tab's name is Han or Latin.
|
|
515
514
|
remove: (tab) => `移除“${tab}”`,
|
|
516
515
|
more: "更多标签页",
|
|
517
|
-
|
|
516
|
+
previous: "之前的标签页",
|
|
518
517
|
},
|
|
519
518
|
appShell: {
|
|
520
519
|
collapse: "收起侧边栏",
|
|
@@ -1129,7 +1128,6 @@ export function uiKitLabelsZh(numberLocale = "zh-CN"): UiKitLabels {
|
|
|
1129
1128
|
notifications: {
|
|
1130
1129
|
typesTitle: "通知我以下内容",
|
|
1131
1130
|
typesDescription: "选择要接收哪些通知,以及接收方式。",
|
|
1132
|
-
byEmail: "通过电子邮件",
|
|
1133
1131
|
channelApp: "应用内",
|
|
1134
1132
|
channelPush: "推送",
|
|
1135
1133
|
channelEmail: "电子邮件",
|
|
@@ -1152,9 +1150,6 @@ export function uiKitLabelsZh(numberLocale = "zh-CN"): UiKitLabels {
|
|
|
1152
1150
|
quietFrom: "从",
|
|
1153
1151
|
quietUntil: "到",
|
|
1154
1152
|
quietIncomplete: "请同时设置开始和结束时间。两者都设置后,免打扰时段才会生效。",
|
|
1155
|
-
timeZone: "时区",
|
|
1156
|
-
deviceZone: (zone) => `此设备位于 ${zone} 时区。`,
|
|
1157
|
-
useDeviceZone: "使用此时区",
|
|
1158
1153
|
regions: {
|
|
1159
1154
|
Africa: "非洲",
|
|
1160
1155
|
America: "美洲",
|
package/src/i18n/review.ts
CHANGED
|
@@ -333,7 +333,6 @@ export const KIT_LABEL_SAMPLES: Readonly<Record<string, readonly (readonly unkno
|
|
|
333
333
|
"notifications.quietDescription": [[false], [true], [false, true], [true, true]],
|
|
334
334
|
"notifications.quietZone": [["{{zone}}"]],
|
|
335
335
|
"notifications.bellUnread": one,
|
|
336
|
-
"notifications.deviceZone": [["{{zone}}"]],
|
|
337
336
|
"notifications.iosInstall": [["{{app}}"]],
|
|
338
337
|
"notifications.testSent": one,
|
|
339
338
|
"notifications.explainerBody": [["{{app}}"]],
|
package/src/index.ts
CHANGED
|
@@ -567,7 +567,15 @@ export {
|
|
|
567
567
|
export type { DateFormatPreference, DateFormatOptions, ResolvedDateFormat } from "./lib/date-format";
|
|
568
568
|
export { DateFormatSetting } from "./components/date-format-setting";
|
|
569
569
|
export type { DateFormatSettingProps } from "./components/date-format-setting";
|
|
570
|
-
export {
|
|
570
|
+
export {
|
|
571
|
+
RowActions,
|
|
572
|
+
rowActionsColumn,
|
|
573
|
+
DEFAULT_ROW_ACTIONS_LABELS,
|
|
574
|
+
// 0.47.0 (keksdose N17): a hand-built table's actions heading and cell.
|
|
575
|
+
RowActionsHead,
|
|
576
|
+
ROW_ACTIONS_CELL_CLASS,
|
|
577
|
+
ROW_ACTIONS_HEAD_CLASS,
|
|
578
|
+
} from "./components/row-actions";
|
|
571
579
|
export type {
|
|
572
580
|
RowAction,
|
|
573
581
|
RowActionList,
|
|
@@ -576,6 +584,7 @@ export type {
|
|
|
576
584
|
RowActionsCollapse,
|
|
577
585
|
RowActionsSize,
|
|
578
586
|
RowActionsColumnOptions,
|
|
587
|
+
RowActionsHeadProps,
|
|
579
588
|
RowActionTone,
|
|
580
589
|
} from "./components/row-actions";
|
|
581
590
|
// 0.22.0: the inputs round (kastlan's, keksdose's and Kurvenschmiede's audits).
|
|
@@ -971,15 +980,18 @@ export { NotificationTypesSetting } from "./notifications/notification-types-set
|
|
|
971
980
|
export type { NotificationTypesSettingProps } from "./notifications/notification-types-setting";
|
|
972
981
|
export { QuietHoursSetting } from "./notifications/quiet-hours-setting";
|
|
973
982
|
export type { QuietHoursSettingProps } from "./notifications/quiet-hours-setting";
|
|
974
|
-
export { TimeZoneSelect, timeZoneOptions, deviceTimeZone } from "./notifications/time-zone-select";
|
|
975
|
-
export type { TimeZoneSelectProps } from "./notifications/time-zone-select";
|
|
976
983
|
// 0.46 (docs/notifications-harmonization.md §10.1, §10.3): the inbox list, the bell and
|
|
977
984
|
// the device-zone report (the account's zone is automatic only, amended 2026-10-20).
|
|
978
985
|
export { NotificationInbox, INBOX_BADGE_INTERVAL } from "./notifications/notification-inbox";
|
|
979
|
-
export type {
|
|
986
|
+
export type {
|
|
987
|
+
NotificationInboxAction,
|
|
988
|
+
NotificationInboxItem,
|
|
989
|
+
NotificationInboxProps,
|
|
990
|
+
} from "./notifications/notification-inbox";
|
|
980
991
|
export { NotificationBell } from "./notifications/notification-bell";
|
|
981
992
|
export type { NotificationBellProps } from "./notifications/notification-bell";
|
|
982
|
-
|
|
993
|
+
// `deviceTimeZone` lives beside the report since 0.46.1 (keksdose N6; sign-up needs it).
|
|
994
|
+
export { useDeviceZoneReport, deviceTimeZone } from "./notifications/device-zone-report";
|
|
983
995
|
export type { DeviceZoneReportOptions } from "./notifications/device-zone-report";
|
|
984
996
|
export {
|
|
985
997
|
usePushDevice,
|
|
@@ -1016,8 +1028,10 @@ export type { PushDevicesSettingProps } from "./notifications/push-devices-setti
|
|
|
1016
1028
|
export { UnsubscribePanel, unsubscribeTokenType } from "./notifications/unsubscribe-panel";
|
|
1017
1029
|
export type { UnsubscribePanelProps } from "./notifications/unsubscribe-panel";
|
|
1018
1030
|
// 0.38.0 (docs/feedback-harmonization.md §9.11): the two feedback types' setting words.
|
|
1031
|
+
// 0.46.1 (keksdose N9): `useFeedbackNotificationTypesLabels`, the words resolved, for `adminGroup`.
|
|
1019
1032
|
export {
|
|
1020
1033
|
useFeedbackNotificationTypes,
|
|
1034
|
+
useFeedbackNotificationTypesLabels,
|
|
1021
1035
|
DEFAULT_FEEDBACK_NOTIFICATION_TYPES_LABELS,
|
|
1022
1036
|
} from "./notifications/feedback-notification-types";
|
|
1023
1037
|
export type {
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { useEffect } from "react";
|
|
2
2
|
|
|
3
3
|
import { settle } from "../account/account-parts";
|
|
4
|
-
import { deviceTimeZone } from "./time-zone-select";
|
|
5
4
|
|
|
6
5
|
export interface DeviceZoneReportOptions {
|
|
7
6
|
/** The account's `device_timezone` from `/auth/me`; `undefined` while it loads (no
|
|
@@ -17,6 +16,20 @@ export interface DeviceZoneReportOptions {
|
|
|
17
16
|
account?: string | number | null;
|
|
18
17
|
}
|
|
19
18
|
|
|
19
|
+
/**
|
|
20
|
+
* This device's IANA zone ("Europe/Zurich"), or `"UTC"` where the runtime names none —
|
|
21
|
+
* the zone `useDeviceZoneReport` reports, which becomes the account's (§10.3, automatic
|
|
22
|
+
* only), and the one sign-up sends (`RegisterRequest.device_timezone`). Here since 0.46.1
|
|
23
|
+
* (keksdose N6).
|
|
24
|
+
*/
|
|
25
|
+
export function deviceTimeZone(): string {
|
|
26
|
+
try {
|
|
27
|
+
return Intl.DateTimeFormat().resolvedOptions().timeZone || "UTC";
|
|
28
|
+
} catch {
|
|
29
|
+
return "UTC";
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
20
33
|
/** The (account, stored, device) triples already reported in this start of the app. */
|
|
21
34
|
const reportedThisStart = new Set<string>();
|
|
22
35
|
|
|
@@ -30,7 +30,8 @@ export type FeedbackNotificationTypeId = "feedback_answered" | "feedback_comment
|
|
|
30
30
|
* - **"New feedback and reworks"** (`feedback_received`, 0.46, feedback §11): to the
|
|
31
31
|
* admins, when someone files a report or sends one back for rework. (A crash is mailed
|
|
32
32
|
* only inside the bundle where an app runs the queue, so the text names no crash.)
|
|
33
|
-
* `labels.adminGroup` ("Admin") is a heading an app may put over it
|
|
33
|
+
* `labels.adminGroup` ("Admin") is a heading an app may put over it — read it with
|
|
34
|
+
* {@link useFeedbackNotificationTypesLabels} (0.46.1) for the app's own `groups`.
|
|
34
35
|
*
|
|
35
36
|
* Every entry is always returned: the card draws a type only when the server answered
|
|
36
37
|
* it (notifications §2.1), so kastlan, which does not declare `feedback_comment` while its
|
|
@@ -51,3 +52,20 @@ export function useFeedbackNotificationTypes(
|
|
|
51
52
|
[words],
|
|
52
53
|
);
|
|
53
54
|
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* The `feedbackNotificationTypes` words, resolved — English, then the provider, then
|
|
58
|
+
* `labels` (0.46.1, keksdose N9): what an app reads to head its own `groups` with
|
|
59
|
+
* `adminGroup` ("Admin") in the provider's locale, as {@link useFeedbackNotificationTypes}
|
|
60
|
+
* labels the entries.
|
|
61
|
+
*
|
|
62
|
+
* ```tsx
|
|
63
|
+
* const { adminGroup } = useFeedbackNotificationTypesLabels();
|
|
64
|
+
* groups={[...appGroups, { id: "admin", label: adminGroup, types: ["feedback_received"] }]}
|
|
65
|
+
* ```
|
|
66
|
+
*/
|
|
67
|
+
export function useFeedbackNotificationTypesLabels(
|
|
68
|
+
labels?: LabelOverride<FeedbackNotificationTypesLabels>,
|
|
69
|
+
): FeedbackNotificationTypesLabels {
|
|
70
|
+
return useKitLabels("feedbackNotificationTypes", DEFAULT_FEEDBACK_NOTIFICATION_TYPES_LABELS, labels);
|
|
71
|
+
}
|
|
@@ -20,7 +20,7 @@ export interface NotificationBellProps<Id extends string | number = string | num
|
|
|
20
20
|
/** Every unread row of the account (`unread_count`): the badge. */
|
|
21
21
|
unreadCount: number;
|
|
22
22
|
/** Open a row: the app marks it read and routes to `item.url` (kastlan switches the
|
|
23
|
-
* acting company first). The panel closes. */
|
|
23
|
+
* acting company first, to `item.company_id`). The panel closes. */
|
|
24
24
|
onOpen: (item: NotificationInboxItem<Id>) => void;
|
|
25
25
|
/** The app's inbox page or card, for "All notifications". */
|
|
26
26
|
href: string;
|
|
@@ -38,8 +38,15 @@ export interface NotificationInboxItem<Id extends string | number = string | num
|
|
|
38
38
|
/** 0.46 (server-kit 0.16 `InboxItem.company`): the company the row is about, named on
|
|
39
39
|
* the row — kastlan's inbox holds every company's rows. */
|
|
40
40
|
company?: string | null;
|
|
41
|
+
/** 0.46.1 (server-kit 0.16.1 `InboxItem.company_id`): the same company by its id, so
|
|
42
|
+
* `onOpen` switches to it exactly (two companies may share a name). */
|
|
43
|
+
company_id?: number | string | null;
|
|
41
44
|
}
|
|
42
45
|
|
|
46
|
+
/** The inbox request that failed, as `describeError` gets it (0.46.1): one row marked read,
|
|
47
|
+
* "Mark all as read", or "Clear all". */
|
|
48
|
+
export type NotificationInboxAction = "read" | "readAll" | "clear";
|
|
49
|
+
|
|
43
50
|
export interface NotificationInboxProps<Id extends string | number = string | number>
|
|
44
51
|
extends Omit<ComponentPropsWithRef<"div">, "children" | "title"> {
|
|
45
52
|
/** The rows, newest first; `undefined` while the first page loads. */
|
|
@@ -53,7 +60,7 @@ export interface NotificationInboxProps<Id extends string | number = string | nu
|
|
|
53
60
|
onClear: () => Promise<unknown> | void;
|
|
54
61
|
/**
|
|
55
62
|
* Open a row: the kit marks it read first, then calls this, and the app routes to
|
|
56
|
-
* `item.url` (kastlan switches the acting company first). Every row is a button then.
|
|
63
|
+
* `item.url` (kastlan switches the acting company, `item.company_id`, first). Every row is a button then.
|
|
57
64
|
* Left out, a row with a `url` is a real link — an in-app path through the provider's
|
|
58
65
|
* router link, an absolute URL in a new tab — that marks itself read on a click or a
|
|
59
66
|
* middle click (keksdose #451), and a row without one marks itself read.
|
|
@@ -72,8 +79,11 @@ export interface NotificationInboxProps<Id extends string | number = string | nu
|
|
|
72
79
|
formatDate?: (iso: string) => string;
|
|
73
80
|
/** The heading. Default `labels.inboxTitle` ("Inbox"). */
|
|
74
81
|
title?: ReactNode;
|
|
75
|
-
/** The app's own words for a failed request, or `undefined` for the kit's `failed`.
|
|
76
|
-
|
|
82
|
+
/** The app's own words for a failed request, or `undefined` for the kit's `failed`.
|
|
83
|
+
* `action` (0.46.1) says which request failed: `"read"` (one row), `"readAll"` ("Mark
|
|
84
|
+
* all as read") or `"clear"` ("Clear all") — so a failed mark-read is not worded as a
|
|
85
|
+
* failed clear. */
|
|
86
|
+
describeError?: (error: unknown, action: NotificationInboxAction) => ReactNode | undefined;
|
|
77
87
|
labels?: LabelOverride<NotificationsLabels>;
|
|
78
88
|
}
|
|
79
89
|
|
|
@@ -141,7 +151,7 @@ export function NotificationInbox<Id extends string | number = string | number>(
|
|
|
141
151
|
(error: unknown) => {
|
|
142
152
|
if (!mounted.current) return;
|
|
143
153
|
setBusy(null);
|
|
144
|
-
setFailure(describeFailure(describeError?.(error), labels.failed));
|
|
154
|
+
setFailure(describeFailure(describeError?.(error, which === "read" ? "readAll" : "clear"), labels.failed));
|
|
145
155
|
},
|
|
146
156
|
);
|
|
147
157
|
};
|
|
@@ -150,7 +160,7 @@ export function NotificationInbox<Id extends string | number = string | number>(
|
|
|
150
160
|
const markOne = (item: NotificationInboxItem<Id>) => {
|
|
151
161
|
if (item.read_at !== null) return;
|
|
152
162
|
settle(() => onMarkRead([item.id])).catch((error: unknown) => {
|
|
153
|
-
if (mounted.current) setFailure(describeFailure(describeError?.(error), labels.failed));
|
|
163
|
+
if (mounted.current) setFailure(describeFailure(describeError?.(error, "read"), labels.failed));
|
|
154
164
|
});
|
|
155
165
|
};
|
|
156
166
|
|
|
@@ -68,9 +68,6 @@ export interface NotificationSettingsPatch<Id extends string = string> {
|
|
|
68
68
|
digest_cadence?: DigestCadence;
|
|
69
69
|
quiet_hours_start?: string | null;
|
|
70
70
|
quiet_hours_end?: string | null;
|
|
71
|
-
/** @deprecated 0.46 — the zone is the account's, automatic only (§10.3), and server-kit 0.16 refuses it here;
|
|
72
|
-
* removed in 0.47. No kit card sends it. */
|
|
73
|
-
timezone?: string;
|
|
74
71
|
}
|
|
75
72
|
|
|
76
73
|
/** One registered push device, as `GET /notifications/devices` lists it (§3.5), oldest
|
|
@@ -21,7 +21,7 @@ export interface NotificationRegionLabels {
|
|
|
21
21
|
/**
|
|
22
22
|
* Every string the notification parts render — the `notifications` namespace of
|
|
23
23
|
* `<UiKitProvider labels>`, overridable per instance through `labels`: the types card,
|
|
24
|
-
* quiet hours
|
|
24
|
+
* quiet hours, this device's push, the device list, and the
|
|
25
25
|
* unsubscribe page. Cancel is `common.cancel`.
|
|
26
26
|
*/
|
|
27
27
|
export interface NotificationsLabels {
|
|
@@ -30,9 +30,6 @@ export interface NotificationsLabels {
|
|
|
30
30
|
typesTitle: string;
|
|
31
31
|
/** The line under it. */
|
|
32
32
|
typesDescription: string;
|
|
33
|
-
/** @deprecated 0.46 — the card's channels are columns now (`channelEmail`); removed in 0.47. Unused,
|
|
34
|
-
* kept so an app's override still type-checks. */
|
|
35
|
-
byEmail: string;
|
|
36
33
|
/** 0.46 (§10.2): the channel columns' headings, and each switch's name. */
|
|
37
34
|
channelApp: string;
|
|
38
35
|
channelPush: string;
|
|
@@ -64,15 +61,7 @@ export interface NotificationsLabels {
|
|
|
64
61
|
quietUntil: string;
|
|
65
62
|
/** One time set, the other not: quiet hours fail open until both are. */
|
|
66
63
|
quietIncomplete: string;
|
|
67
|
-
/** The zone
|
|
68
|
-
timeZone: string;
|
|
69
|
-
/** @deprecated 0.46 — the zone is automatic (§10.3), the card offers no device zone; removed in 0.47.
|
|
70
|
-
* Unused, kept so an app's override still type-checks. */
|
|
71
|
-
deviceZone: (zone: string) => string;
|
|
72
|
-
/** @deprecated 0.46 — the zone is automatic (§10.3), nothing to take; removed in 0.47. Unused, kept
|
|
73
|
-
* so an app's override still type-checks. */
|
|
74
|
-
useDeviceZone: string;
|
|
75
|
-
/** The zone's region headings, in the picker and in a zone's name ("Europe / Zurich"). */
|
|
64
|
+
/** The zone's region headings, in a zone's name ("Europe / Zurich"). */
|
|
76
65
|
regions: NotificationRegionLabels;
|
|
77
66
|
|
|
78
67
|
/* ── This device ────────────────────────────────────────────────────── */
|
|
@@ -185,7 +174,6 @@ export interface NotificationsLabels {
|
|
|
185
174
|
export const DEFAULT_NOTIFICATIONS_LABELS: NotificationsLabels = {
|
|
186
175
|
typesTitle: "Notify me about",
|
|
187
176
|
typesDescription: "Choose what you hear about, and how.",
|
|
188
|
-
byEmail: "By e-mail",
|
|
189
177
|
channelApp: "In the app",
|
|
190
178
|
channelPush: "Push",
|
|
191
179
|
channelEmail: "E-mail",
|
|
@@ -209,9 +197,6 @@ export const DEFAULT_NOTIFICATIONS_LABELS: NotificationsLabels = {
|
|
|
209
197
|
quietFrom: "From",
|
|
210
198
|
quietUntil: "Until",
|
|
211
199
|
quietIncomplete: "Set both a start and an end time. Quiet hours take effect once both are set.",
|
|
212
|
-
timeZone: "Time zone",
|
|
213
|
-
deviceZone: (zone) => `This device is in ${zone}.`,
|
|
214
|
-
useDeviceZone: "Use it",
|
|
215
200
|
regions: {
|
|
216
201
|
Africa: "Africa",
|
|
217
202
|
America: "Americas",
|
|
@@ -10,22 +10,29 @@ import { CARD_DESCRIPTION_CLASS } from "../settings/settings-card-classes";
|
|
|
10
10
|
import { SettingsCardTitle } from "../settings/settings-heading";
|
|
11
11
|
import { settle, useMounted } from "../account/account-parts";
|
|
12
12
|
import { describeFailure, FailureLine, useNotificationsLabels, useWriteOverlay } from "./notification-parts";
|
|
13
|
-
import type { NotificationsLabels } from "./notifications-labels";
|
|
13
|
+
import type { NotificationRegionLabels, NotificationsLabels } from "./notifications-labels";
|
|
14
14
|
import type { NotificationSettingsPatch } from "./notification-types";
|
|
15
|
-
import { zoneName } from "./time-zone-select";
|
|
16
15
|
|
|
17
16
|
/** The zone the server applies while no device ever reported one (§10.3): every app's. */
|
|
18
17
|
const DEFAULT_FALLBACK_ZONE = "Europe/Zurich";
|
|
19
18
|
|
|
19
|
+
/** A zone for a sentence: "Europe / Zurich", the region in the kit's words (`labels.regions`);
|
|
20
|
+
* a region the labels do not know keeps its IANA name, a zone with none ("UTC") is itself. */
|
|
21
|
+
function zoneName(zone: string, regions: NotificationRegionLabels): string {
|
|
22
|
+
const slash = zone.indexOf("/");
|
|
23
|
+
if (slash < 0) return zone;
|
|
24
|
+
const region = zone.slice(0, slash);
|
|
25
|
+
const named = Object.prototype.hasOwnProperty.call(regions, region)
|
|
26
|
+
? regions[region as keyof NotificationRegionLabels]
|
|
27
|
+
: region;
|
|
28
|
+
return `${named} / ${zone.slice(slash + 1).replace(/_/g, " ").replace(/\//g, " / ")}`;
|
|
29
|
+
}
|
|
30
|
+
|
|
20
31
|
export interface QuietHoursSettingProps extends Omit<ComponentPropsWithRef<"div">, "children" | "onChange"> {
|
|
21
32
|
/** The `GET`'s `quiet_hours_start`, `"HH:MM"`, or `null`. */
|
|
22
33
|
start: string | null;
|
|
23
34
|
/** The `GET`'s `quiet_hours_end`. */
|
|
24
35
|
end: string | null;
|
|
25
|
-
/** The zone the 0.36 card showed, an IANA name, or `null`.
|
|
26
|
-
* @deprecated 0.46 — pass `zone` (§10.3: the zone is the account's, automatic only); removed in 0.47.
|
|
27
|
-
* Shown read-only while `zone` is left out; the card no longer offers a picker. */
|
|
28
|
-
timezone?: string | null;
|
|
29
36
|
/**
|
|
30
37
|
* 0.46 (§10.3, amended 2026-10-20: automatic only): the account's zone — `/auth/me`'s
|
|
31
38
|
* `device_timezone`, the zone of the device last used; `null` while no device has
|
|
@@ -43,7 +50,7 @@ export interface QuietHoursSettingProps extends Omit<ComponentPropsWithRef<"div"
|
|
|
43
50
|
* rejects.
|
|
44
51
|
*/
|
|
45
52
|
onChange: (patch: NotificationSettingsPatch) => Promise<unknown> | void;
|
|
46
|
-
/** The zone named while
|
|
53
|
+
/** The zone named while `zone` names none: the one the server
|
|
47
54
|
* applies. Default `"Europe/Zurich"`, every app's. */
|
|
48
55
|
fallbackZone?: string;
|
|
49
56
|
/** The app keeps an inbox, and the description says a held push still reaches it. */
|
|
@@ -69,15 +76,13 @@ type QuietField = "quiet_hours_start" | "quiet_hours_end";
|
|
|
69
76
|
*
|
|
70
77
|
* - **Two times**, each saved as it is entered; a cleared time sends `null`. While only
|
|
71
78
|
* one is set the card says so: the window fails open until both are.
|
|
72
|
-
* - **The zone** shows while a time is set: `zone`, else
|
|
73
|
-
* `fallbackZone`.
|
|
79
|
+
* - **The zone** shows while a time is set: `zone`, else `fallbackZone`.
|
|
74
80
|
*
|
|
75
81
|
* Under a write lock the fields are `commit` controls exempt from billing.
|
|
76
82
|
*/
|
|
77
83
|
export function QuietHoursSetting({
|
|
78
84
|
start,
|
|
79
85
|
end,
|
|
80
|
-
timezone = null,
|
|
81
86
|
zone,
|
|
82
87
|
held = false,
|
|
83
88
|
onChange,
|
|
@@ -99,7 +104,7 @@ export function QuietHoursSetting({
|
|
|
99
104
|
overlay.has(field) ? (overlay.get(field) ?? null) : server;
|
|
100
105
|
const startShown = shown("quiet_hours_start", start);
|
|
101
106
|
const endShown = shown("quiet_hours_end", end);
|
|
102
|
-
const accountZone =
|
|
107
|
+
const accountZone = zone ?? fallbackZone;
|
|
103
108
|
|
|
104
109
|
const save = (patch: NotificationSettingsPatch) => {
|
|
105
110
|
const fields = Object.keys(patch) as QuietField[];
|
|
@@ -23,6 +23,30 @@ export function stampAfter(previous: unknown): string {
|
|
|
23
23
|
return new Date(Number.isFinite(prior) && prior >= now ? prior + 1 : now).toISOString();
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
/**
|
|
27
|
+
* A name-based UUID, version 5 (RFC 4122 §4.3: SHA-1 of the namespace's 16 bytes and the
|
|
28
|
+
* name's UTF-8), the same as Python's `uuid.uuid5(uuid.UUID(namespace), name)` — for an id
|
|
29
|
+
* the device and the server derive alike (§2: the per-owner vote id
|
|
30
|
+
* `uuid5(namespace, "<item>:<invitation>")`). Lower case, hyphenated. `namespace` is a UUID,
|
|
31
|
+
* hyphenated or not, in either case; anything else is a `TypeError`. Uses `crypto.subtle`
|
|
32
|
+
* (a secure context: https or localhost).
|
|
33
|
+
*/
|
|
34
|
+
export async function uuid5(namespace: string, name: string): Promise<string> {
|
|
35
|
+
const hex = /^[0-9a-f]{8}-?[0-9a-f]{4}-?[0-9a-f]{4}-?[0-9a-f]{4}-?[0-9a-f]{12}$/i.test(namespace)
|
|
36
|
+
? namespace.replaceAll("-", "")
|
|
37
|
+
: null;
|
|
38
|
+
if (!hex) throw new TypeError(`uuid5: the namespace must be a UUID, not ${JSON.stringify(namespace)}`);
|
|
39
|
+
const nameBytes = new TextEncoder().encode(name);
|
|
40
|
+
const input = new Uint8Array(16 + nameBytes.length);
|
|
41
|
+
for (let i = 0; i < 16; i += 1) input[i] = Number.parseInt(hex.slice(i * 2, i * 2 + 2), 16);
|
|
42
|
+
input.set(nameBytes, 16);
|
|
43
|
+
const bytes = new Uint8Array(await crypto.subtle.digest("SHA-1", input)).slice(0, 16);
|
|
44
|
+
bytes[6] = (bytes[6] & 0x0f) | 0x50; // version 5
|
|
45
|
+
bytes[8] = (bytes[8] & 0x3f) | 0x80; // the RFC 4122 variant
|
|
46
|
+
const out = Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
47
|
+
return `${out.slice(0, 8)}-${out.slice(8, 12)}-${out.slice(12, 16)}-${out.slice(16, 20)}-${out.slice(20)}`;
|
|
48
|
+
}
|
|
49
|
+
|
|
26
50
|
/**
|
|
27
51
|
* The connection, not the server, failed: the request had no answer (an axios-like error
|
|
28
52
|
* without `response`, or `fetch`'s `TypeError`), or the session refresh marked a 401 as
|
package/src/offline.ts
CHANGED
|
@@ -29,4 +29,4 @@ export {
|
|
|
29
29
|
} from "./offline/shared-computer";
|
|
30
30
|
export type { SharedComputerChoiceProps, SharedComputerLabels, SharedComputerStorage } from "./offline/shared-computer";
|
|
31
31
|
// 0.46 (§2.3): the leaf utilities, shared with `@eifi1/ui-kit/sync` from one module that imports no peer.
|
|
32
|
-
export { isOfflineError, readLocalFirst, stampAfter, writeLocalFirst } from "./offline/local-first";
|
|
32
|
+
export { isOfflineError, readLocalFirst, stampAfter, uuid5, writeLocalFirst } from "./offline/local-first";
|
package/src/sync/db.ts
CHANGED
|
@@ -79,6 +79,28 @@ export const SYNC_DB_STORES = {
|
|
|
79
79
|
meta: "key",
|
|
80
80
|
} as const;
|
|
81
81
|
|
|
82
|
+
/**
|
|
83
|
+
* The `rows` schema with an `[entity+pk]` index (0.46.1), opt-in: the D16 pages start from a
|
|
84
|
+
* server id (`/meters/7`) and {@link rowByPk} finds its copy row by this index. Not in
|
|
85
|
+
* {@link SYNC_DB_STORES}, so no database on a device changes until its app names it in a
|
|
86
|
+
* version of its own — kastlan, whose devices are on v2: `upgrades: [..., { version: 3,
|
|
87
|
+
* stores: SYNC_DB_PK_INDEX }]`. A database new to the devices may take it at once:
|
|
88
|
+
* `extraStores: { ...SYNC_DB_PK_INDEX }`.
|
|
89
|
+
*/
|
|
90
|
+
export const SYNC_DB_PK_INDEX = {
|
|
91
|
+
rows: "[entity+id], entity, [entity+pk]",
|
|
92
|
+
} as const;
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* The copy row of the server's id `pk` in `entity` (0.46.1): by the `[entity+pk]` index when
|
|
96
|
+
* the database has it ({@link SYNC_DB_PK_INDEX}), else by a scan of the entity's rows.
|
|
97
|
+
* `undefined` when the copy holds no such row.
|
|
98
|
+
*/
|
|
99
|
+
export async function rowByPk(db: SyncDb, entity: string, pk: number): Promise<SyncedRow | undefined> {
|
|
100
|
+
if (db.rows.schema.idxByName["[entity+pk]"]) return db.rows.where("[entity+pk]").equals([entity, pk]).first();
|
|
101
|
+
return db.rows.where("entity").equals(entity).filter((row) => row.pk === pk).first();
|
|
102
|
+
}
|
|
103
|
+
|
|
82
104
|
/**
|
|
83
105
|
* Open (lazily, on first use) the sync database `name`: the engine's tables plus the app's
|
|
84
106
|
* `extraStores`, at version 1 or the last of `upgrades`. One database per account (kastlan:
|