@apptegy/nuxt-navigation 0.1.121 → 0.1.123
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/dist/module.json +1 -1
- package/dist/runtime/components/Nav/Header.vue +16 -5
- package/dist/runtime/components/Nav/Header.vue.d.ts +21 -3
- package/dist/runtime/components/Nav/NotificationItem.vue +147 -81
- package/dist/runtime/components/Nav/NotificationItem.vue.d.ts +2 -2
- package/dist/runtime/components/Nav/NotificationsPanel.vue.d.ts +9 -9
- package/dist/runtime/types/index.d.ts +1 -1
- package/dist/runtime/types/notifications.d.ts +19 -1
- package/dist/runtime/utils/notification-api.d.ts +10 -0
- package/dist/runtime/utils/notification-api.js +44 -1
- package/locale/en.json +1 -0
- package/package.json +15 -16
package/dist/module.json
CHANGED
|
@@ -9,11 +9,13 @@
|
|
|
9
9
|
</div>
|
|
10
10
|
<slot name="org-select" />
|
|
11
11
|
<slot name="user-controls" />
|
|
12
|
-
<div class="
|
|
13
|
-
<
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
12
|
+
<div class="header__end">
|
|
13
|
+
<slot name="searchbar" />
|
|
14
|
+
<div class="user-control-actions">
|
|
15
|
+
<div
|
|
16
|
+
v-if="showNotifications"
|
|
17
|
+
class="header__notifications"
|
|
18
|
+
>
|
|
17
19
|
<Button
|
|
18
20
|
type="button"
|
|
19
21
|
severity="secondary"
|
|
@@ -136,6 +138,7 @@
|
|
|
136
138
|
/>
|
|
137
139
|
</NuxtLink>
|
|
138
140
|
</template>
|
|
141
|
+
</div>
|
|
139
142
|
</div>
|
|
140
143
|
</header>
|
|
141
144
|
</template>
|
|
@@ -313,10 +316,18 @@ header.header {
|
|
|
313
316
|
background-color: var(--primary-white);
|
|
314
317
|
font: var(--font-regular);
|
|
315
318
|
}
|
|
319
|
+
header.header .header__end {
|
|
320
|
+
display: flex;
|
|
321
|
+
align-items: center;
|
|
322
|
+
justify-content: flex-end;
|
|
323
|
+
gap: 24px;
|
|
324
|
+
min-width: 0;
|
|
325
|
+
}
|
|
316
326
|
header.header .user-control-actions {
|
|
317
327
|
display: flex;
|
|
318
328
|
align-items: center;
|
|
319
329
|
gap: 8px;
|
|
330
|
+
flex-shrink: 0;
|
|
320
331
|
}
|
|
321
332
|
header.header .header__notifications {
|
|
322
333
|
position: relative;
|
|
@@ -17,7 +17,7 @@ interface IUserDropdown {
|
|
|
17
17
|
userDropdownOptions: Array<unknown>;
|
|
18
18
|
}
|
|
19
19
|
type __VLS_Props = IHeaderProps & IUserDropdown;
|
|
20
|
-
declare var __VLS_1: {}, __VLS_3: {}, __VLS_5: {},
|
|
20
|
+
declare var __VLS_1: {}, __VLS_3: {}, __VLS_5: {}, __VLS_7: {}, __VLS_17: {};
|
|
21
21
|
type __VLS_Slots = {} & {
|
|
22
22
|
breadcrumbs?: (props: typeof __VLS_1) => any;
|
|
23
23
|
} & {
|
|
@@ -25,9 +25,27 @@ type __VLS_Slots = {} & {
|
|
|
25
25
|
} & {
|
|
26
26
|
'user-controls'?: (props: typeof __VLS_5) => any;
|
|
27
27
|
} & {
|
|
28
|
-
|
|
28
|
+
searchbar?: (props: typeof __VLS_7) => any;
|
|
29
|
+
} & {
|
|
30
|
+
notifications?: (props: typeof __VLS_17) => any;
|
|
29
31
|
};
|
|
30
|
-
declare const __VLS_component: import("vue").DefineComponent<__VLS_Props, void, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin,
|
|
32
|
+
declare const __VLS_component: import("vue").DefineComponent<__VLS_Props, void, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
33
|
+
"selected-option": (...args: any[]) => void;
|
|
34
|
+
"user-logout": (...args: any[]) => void;
|
|
35
|
+
"update:notifications": (...args: any[]) => void;
|
|
36
|
+
"notifications-clear-all": (...args: any[]) => void;
|
|
37
|
+
"notifications-dismiss": (...args: any[]) => void;
|
|
38
|
+
"notifications-select": (...args: any[]) => void;
|
|
39
|
+
"notification-click": (...args: any[]) => void;
|
|
40
|
+
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
41
|
+
"onSelected-option"?: ((...args: any[]) => any) | undefined;
|
|
42
|
+
"onUser-logout"?: ((...args: any[]) => any) | undefined;
|
|
43
|
+
"onUpdate:notifications"?: ((...args: any[]) => any) | undefined;
|
|
44
|
+
"onNotifications-clear-all"?: ((...args: any[]) => any) | undefined;
|
|
45
|
+
"onNotifications-dismiss"?: ((...args: any[]) => any) | undefined;
|
|
46
|
+
"onNotifications-select"?: ((...args: any[]) => any) | undefined;
|
|
47
|
+
"onNotification-click"?: ((...args: any[]) => any) | undefined;
|
|
48
|
+
}>, {
|
|
31
49
|
notifications: INotificationsHeaderModel | null;
|
|
32
50
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
33
51
|
declare const _default: __VLS_WithSlots<typeof __VLS_component, __VLS_Slots>;
|
|
@@ -3,95 +3,125 @@
|
|
|
3
3
|
class="notification-item"
|
|
4
4
|
:class="{ 'notification-item--in-badge': showBadgeActions }"
|
|
5
5
|
>
|
|
6
|
-
<div
|
|
7
|
-
class="notification-item__main"
|
|
8
|
-
role="button"
|
|
9
|
-
tabindex="0"
|
|
10
|
-
@click="handleClick"
|
|
11
|
-
@keydown.enter.prevent="handleClick"
|
|
12
|
-
>
|
|
6
|
+
<div class="notification-item__header">
|
|
13
7
|
<div
|
|
14
|
-
class="notification-
|
|
15
|
-
|
|
8
|
+
class="notification-item__main"
|
|
9
|
+
role="button"
|
|
10
|
+
tabindex="0"
|
|
11
|
+
@click="handleClick"
|
|
12
|
+
@keydown.enter.prevent="handleClick"
|
|
16
13
|
>
|
|
17
|
-
<div
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
14
|
+
<div
|
|
15
|
+
class="notification-item__avatar"
|
|
16
|
+
:style="{ '--type-accent': typeAccent }"
|
|
17
|
+
>
|
|
18
|
+
<div class="notification-item__avatar-ring">
|
|
19
|
+
<NavAvatar
|
|
20
|
+
:name="primaryActor.name"
|
|
21
|
+
:src="primaryActor.avatarUrl"
|
|
22
|
+
class="notification-item__avatar-image"
|
|
23
|
+
/>
|
|
24
|
+
</div>
|
|
25
|
+
<span
|
|
26
|
+
v-if="groupBadgeLabel"
|
|
27
|
+
class="notification-item__group-badge"
|
|
28
|
+
aria-hidden="true"
|
|
29
|
+
>
|
|
30
|
+
{{ groupBadgeLabel }}
|
|
31
|
+
</span>
|
|
32
|
+
<span
|
|
33
|
+
v-else
|
|
34
|
+
class="notification-item__type-badge"
|
|
35
|
+
aria-hidden="true"
|
|
36
|
+
>
|
|
37
|
+
<component :is="typeIcon" />
|
|
38
|
+
</span>
|
|
39
|
+
</div>
|
|
40
|
+
|
|
41
|
+
<div class="notification-item__content">
|
|
42
|
+
<p class="notification-item__message">
|
|
43
|
+
{{ item.actionText }}
|
|
44
|
+
</p>
|
|
45
|
+
<p class="notification-item__meta">
|
|
46
|
+
<span>{{ item.timestampLabel }}</span>
|
|
47
|
+
<template v-if="item.contextLabel">
|
|
48
|
+
<span
|
|
49
|
+
class="notification-item__meta-separator"
|
|
50
|
+
aria-hidden="true"
|
|
51
|
+
>·</span
|
|
52
|
+
>
|
|
53
|
+
<span>{{ item.contextLabel }}</span>
|
|
54
|
+
</template>
|
|
55
|
+
</p>
|
|
56
|
+
<Tag
|
|
57
|
+
v-if="item.stateLabel"
|
|
58
|
+
:value="item.stateLabel"
|
|
59
|
+
severity="secondary"
|
|
60
|
+
class="notification-item__state-chip"
|
|
22
61
|
/>
|
|
23
62
|
</div>
|
|
24
|
-
<span
|
|
25
|
-
v-if="groupBadgeLabel"
|
|
26
|
-
class="notification-item__group-badge"
|
|
27
|
-
aria-hidden="true"
|
|
28
|
-
>
|
|
29
|
-
{{ groupBadgeLabel }}
|
|
30
|
-
</span>
|
|
31
|
-
<span
|
|
32
|
-
class="notification-item__type-badge"
|
|
33
|
-
aria-hidden="true"
|
|
34
|
-
>
|
|
35
|
-
<component :is="typeIcon" />
|
|
36
|
-
</span>
|
|
37
63
|
</div>
|
|
38
64
|
|
|
39
|
-
<div class="notification-
|
|
40
|
-
<
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
<span>{{ item.timestampLabel }}</span>
|
|
45
|
-
<template v-if="item.contextLabel">
|
|
46
|
-
<span
|
|
47
|
-
class="notification-item__meta-separator"
|
|
48
|
-
aria-hidden="true"
|
|
49
|
-
>·</span
|
|
50
|
-
>
|
|
51
|
-
<span>{{ item.contextLabel }}</span>
|
|
52
|
-
</template>
|
|
53
|
-
</p>
|
|
54
|
-
<Tag
|
|
55
|
-
v-if="item.stateLabel"
|
|
56
|
-
:value="item.stateLabel"
|
|
57
|
-
severity="secondary"
|
|
58
|
-
class="notification-item__state-chip"
|
|
65
|
+
<div class="notification-item__actions">
|
|
66
|
+
<span
|
|
67
|
+
v-if="showBadgeActions"
|
|
68
|
+
class="notification-item__unread-dot"
|
|
69
|
+
aria-hidden="true"
|
|
59
70
|
/>
|
|
71
|
+
<Button
|
|
72
|
+
v-if="showBadgeActions"
|
|
73
|
+
type="button"
|
|
74
|
+
severity="secondary"
|
|
75
|
+
text
|
|
76
|
+
rounded
|
|
77
|
+
class="notification-item__dismiss"
|
|
78
|
+
:aria-label="$t('navigation.notificationsPanel.dismiss')"
|
|
79
|
+
@click="handleDismiss"
|
|
80
|
+
>
|
|
81
|
+
<svg
|
|
82
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
83
|
+
width="16"
|
|
84
|
+
height="16"
|
|
85
|
+
viewBox="0 0 24 24"
|
|
86
|
+
fill="none"
|
|
87
|
+
stroke="currentColor"
|
|
88
|
+
stroke-width="2"
|
|
89
|
+
stroke-linecap="round"
|
|
90
|
+
stroke-linejoin="round"
|
|
91
|
+
aria-hidden="true"
|
|
92
|
+
>
|
|
93
|
+
<path d="M18 6 6 18" />
|
|
94
|
+
<path d="m6 6 12 12" />
|
|
95
|
+
</svg>
|
|
96
|
+
</Button>
|
|
60
97
|
</div>
|
|
61
98
|
</div>
|
|
62
99
|
|
|
63
|
-
<div
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
rounded
|
|
75
|
-
class="notification-item__dismiss"
|
|
76
|
-
:aria-label="$t('navigation.notificationsPanel.dismiss')"
|
|
77
|
-
@click="handleDismiss"
|
|
78
|
-
>
|
|
79
|
-
<svg
|
|
80
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
81
|
-
width="16"
|
|
82
|
-
height="16"
|
|
83
|
-
viewBox="0 0 24 24"
|
|
84
|
-
fill="none"
|
|
85
|
-
stroke="currentColor"
|
|
86
|
-
stroke-width="2"
|
|
87
|
-
stroke-linecap="round"
|
|
88
|
-
stroke-linejoin="round"
|
|
100
|
+
<div
|
|
101
|
+
v-if="item.summary"
|
|
102
|
+
class="notification-item__summary"
|
|
103
|
+
role="button"
|
|
104
|
+
tabindex="0"
|
|
105
|
+
@click="handleClick"
|
|
106
|
+
@keydown.enter.prevent="handleClick"
|
|
107
|
+
>
|
|
108
|
+
<template v-if="reactionEmojis.length">
|
|
109
|
+
<span
|
|
110
|
+
class="notification-item__reactions"
|
|
89
111
|
aria-hidden="true"
|
|
90
112
|
>
|
|
91
|
-
<
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
113
|
+
<span
|
|
114
|
+
v-for="(emoji, index) in reactionEmojis"
|
|
115
|
+
:key="`${emoji}-${index}`"
|
|
116
|
+
class="notification-item__reaction"
|
|
117
|
+
>{{ emoji }}</span>
|
|
118
|
+
</span>
|
|
119
|
+
<span class="notification-item__summary-on">
|
|
120
|
+
{{ $t("navigation.notificationsPanel.summaryOn") }}
|
|
121
|
+
</span>
|
|
122
|
+
{{ " " }}
|
|
123
|
+
</template>
|
|
124
|
+
<span class="notification-item__summary-text">"{{ item.summary }}"</span>
|
|
95
125
|
</div>
|
|
96
126
|
</div>
|
|
97
127
|
</template>
|
|
@@ -108,6 +138,7 @@ const emit = defineEmits(["dismiss", "select"]);
|
|
|
108
138
|
const primaryActor = computed(() => props.item.actors[0] || { name: "" });
|
|
109
139
|
const typeAccent = computed(() => getTypeAccent(props.item.type));
|
|
110
140
|
const showBadgeActions = computed(() => isInBadge(props.item));
|
|
141
|
+
const reactionEmojis = computed(() => props.item.reactionEmojis ?? []);
|
|
111
142
|
const groupBadgeLabel = computed(() => {
|
|
112
143
|
const count = props.item.groupCount ?? Math.max(props.item.actors.length - 1, 0);
|
|
113
144
|
return count > 0 ? `+${count}` : "";
|
|
@@ -189,10 +220,8 @@ function handleDismiss(event) {
|
|
|
189
220
|
|
|
190
221
|
<style scoped>
|
|
191
222
|
.notification-item {
|
|
192
|
-
display:
|
|
193
|
-
|
|
194
|
-
gap: 12px;
|
|
195
|
-
align-items: start;
|
|
223
|
+
display: flex;
|
|
224
|
+
flex-direction: column;
|
|
196
225
|
width: 100%;
|
|
197
226
|
padding: 12px;
|
|
198
227
|
border: 1px solid rgb(232, 232, 236);
|
|
@@ -203,6 +232,12 @@ function handleDismiss(event) {
|
|
|
203
232
|
box-shadow: none;
|
|
204
233
|
transition: border-color 0.15s ease, box-shadow 0.15s ease;
|
|
205
234
|
}
|
|
235
|
+
.notification-item__header {
|
|
236
|
+
display: grid;
|
|
237
|
+
grid-template-columns: 1fr auto;
|
|
238
|
+
gap: 12px;
|
|
239
|
+
align-items: start;
|
|
240
|
+
}
|
|
206
241
|
.notification-item__main {
|
|
207
242
|
display: grid;
|
|
208
243
|
grid-template-columns: auto 1fr;
|
|
@@ -308,6 +343,37 @@ function handleDismiss(event) {
|
|
|
308
343
|
.notification-item__state-chip {
|
|
309
344
|
margin-top: 8px;
|
|
310
345
|
}
|
|
346
|
+
.notification-item__summary {
|
|
347
|
+
margin-top: 12px;
|
|
348
|
+
padding-top: 12px;
|
|
349
|
+
border-top: 1px solid rgb(232, 232, 236);
|
|
350
|
+
cursor: pointer;
|
|
351
|
+
font-size: 13px;
|
|
352
|
+
line-height: 18px;
|
|
353
|
+
color: var(--grey-70, #4b5563);
|
|
354
|
+
}
|
|
355
|
+
.notification-item__summary:focus-visible {
|
|
356
|
+
outline: 2px solid var(--purple-60, #662e80);
|
|
357
|
+
outline-offset: 2px;
|
|
358
|
+
border-radius: 4px;
|
|
359
|
+
}
|
|
360
|
+
.notification-item__reactions {
|
|
361
|
+
display: inline;
|
|
362
|
+
white-space: nowrap;
|
|
363
|
+
}
|
|
364
|
+
.notification-item__reaction {
|
|
365
|
+
font-size: 14px;
|
|
366
|
+
line-height: 1;
|
|
367
|
+
}
|
|
368
|
+
.notification-item__summary-on {
|
|
369
|
+
color: var(--grey-50, #747474);
|
|
370
|
+
}
|
|
371
|
+
.notification-item__summary-text {
|
|
372
|
+
font-style: italic;
|
|
373
|
+
color: var(--grey-70, #4b5563);
|
|
374
|
+
white-space: normal;
|
|
375
|
+
overflow-wrap: anywhere;
|
|
376
|
+
}
|
|
311
377
|
.notification-item__actions {
|
|
312
378
|
display: flex;
|
|
313
379
|
align-items: center;
|
|
@@ -3,10 +3,10 @@ type __VLS_Props = {
|
|
|
3
3
|
item: INotificationItem;
|
|
4
4
|
};
|
|
5
5
|
declare const _default: import("vue").DefineComponent<__VLS_Props, void, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
6
|
-
dismiss: (id: string) => any;
|
|
7
6
|
select: (item: INotificationItem) => any;
|
|
7
|
+
dismiss: (id: string) => any;
|
|
8
8
|
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
9
|
-
onDismiss?: ((id: string) => any) | undefined;
|
|
10
9
|
onSelect?: ((item: INotificationItem) => any) | undefined;
|
|
10
|
+
onDismiss?: ((id: string) => any) | undefined;
|
|
11
11
|
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
12
12
|
export default _default;
|
|
@@ -13,6 +13,11 @@ type __VLS_Props = {
|
|
|
13
13
|
mobile?: boolean;
|
|
14
14
|
};
|
|
15
15
|
declare const _default: import("vue").DefineComponent<__VLS_Props, void, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
16
|
+
select: (payload: {
|
|
17
|
+
item: INotificationsPanelData["groups"][number]["notifications"][number];
|
|
18
|
+
tabId: string;
|
|
19
|
+
filterId: string;
|
|
20
|
+
}) => any;
|
|
16
21
|
"update:activeTabId": (id: string) => any;
|
|
17
22
|
"update:activeFilterId": (id: string) => any;
|
|
18
23
|
"update:filterCatalog": (filters: INotificationFilter[]) => any;
|
|
@@ -26,13 +31,13 @@ declare const _default: import("vue").DefineComponent<__VLS_Props, void, {}, {},
|
|
|
26
31
|
tabId: string;
|
|
27
32
|
filterId: string;
|
|
28
33
|
}) => any;
|
|
29
|
-
|
|
34
|
+
close: () => any;
|
|
35
|
+
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
36
|
+
onSelect?: ((payload: {
|
|
30
37
|
item: INotificationsPanelData["groups"][number]["notifications"][number];
|
|
31
38
|
tabId: string;
|
|
32
39
|
filterId: string;
|
|
33
|
-
}) => any;
|
|
34
|
-
close: () => any;
|
|
35
|
-
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
40
|
+
}) => any) | undefined;
|
|
36
41
|
"onUpdate:activeTabId"?: ((id: string) => any) | undefined;
|
|
37
42
|
"onUpdate:activeFilterId"?: ((id: string) => any) | undefined;
|
|
38
43
|
"onUpdate:filterCatalog"?: ((filters: INotificationFilter[]) => any) | undefined;
|
|
@@ -46,11 +51,6 @@ declare const _default: import("vue").DefineComponent<__VLS_Props, void, {}, {},
|
|
|
46
51
|
tabId: string;
|
|
47
52
|
filterId: string;
|
|
48
53
|
}) => any) | undefined;
|
|
49
|
-
onSelect?: ((payload: {
|
|
50
|
-
item: INotificationsPanelData["groups"][number]["notifications"][number];
|
|
51
|
-
tabId: string;
|
|
52
|
-
filterId: string;
|
|
53
|
-
}) => any) | undefined;
|
|
54
54
|
onClose?: (() => any) | undefined;
|
|
55
55
|
}>, {
|
|
56
56
|
loading: boolean;
|
|
@@ -12,4 +12,4 @@ export interface IUser {
|
|
|
12
12
|
client: string;
|
|
13
13
|
admin_email: string;
|
|
14
14
|
}
|
|
15
|
-
export type { NotificationType, NotificationApiState, INotificationApiActor, INotificationApiRecord, INotificationsPagination, INotificationsApiResponse, INotificationActor, INotificationItem, INotificationGroup, INotificationTab, INotificationFilter, INotificationsPanelData, INotificationsHeaderModel, } from './notifications.js';
|
|
15
|
+
export type { NotificationType, NotificationApiState, INotificationApiActor, INotificationApiRoom, INotificationApiRecord, INotificationsPagination, INotificationsApiResponse, INotificationActor, INotificationItem, INotificationGroup, INotificationTab, INotificationFilter, INotificationsPanelData, INotificationsHeaderModel, } from './notifications.js';
|
|
@@ -5,6 +5,11 @@ export interface INotificationApiActor {
|
|
|
5
5
|
first_name: string;
|
|
6
6
|
last_name: string;
|
|
7
7
|
}
|
|
8
|
+
export interface INotificationApiRoom {
|
|
9
|
+
id?: string;
|
|
10
|
+
sis_id?: string;
|
|
11
|
+
name: string;
|
|
12
|
+
}
|
|
8
13
|
export interface INotificationApiRecord {
|
|
9
14
|
id: number | string;
|
|
10
15
|
/**
|
|
@@ -14,7 +19,15 @@ export interface INotificationApiRecord {
|
|
|
14
19
|
kind?: string | null;
|
|
15
20
|
source_record_type: string;
|
|
16
21
|
title: string;
|
|
17
|
-
summary
|
|
22
|
+
/** Post/body preview shown in the card summary row. */
|
|
23
|
+
summary?: string | null;
|
|
24
|
+
/**
|
|
25
|
+
* Reaction tallies for aggregate reaction notifications (e.g. `{ CLAP: 2, LIKE: 1 }`).
|
|
26
|
+
* When present, corresponding emojis are shown beside the summary.
|
|
27
|
+
*/
|
|
28
|
+
reaction_counts?: Record<string, number> | null;
|
|
29
|
+
/** Room associated with the notification; `name` is shown in the meta line. */
|
|
30
|
+
room?: INotificationApiRoom | null;
|
|
18
31
|
actors?: INotificationApiActor[] | null;
|
|
19
32
|
detail_link?: string | null;
|
|
20
33
|
notification_state: NotificationApiState | string;
|
|
@@ -58,7 +71,12 @@ export interface INotificationItem {
|
|
|
58
71
|
* Derived from API `last_activity_at`, falling back to `created_at`.
|
|
59
72
|
*/
|
|
60
73
|
createdAt?: string;
|
|
74
|
+
/** Room name shown after the relative timestamp (e.g. "Staff Lounge"). */
|
|
61
75
|
contextLabel?: string;
|
|
76
|
+
/** Post/body preview shown in the card summary row. */
|
|
77
|
+
summary?: string;
|
|
78
|
+
/** Emoji list derived from API `reaction_counts`, shown before the summary when present. */
|
|
79
|
+
reactionEmojis?: string[];
|
|
62
80
|
/** When false, the notification counts toward the bell badge and appears in the New tab. */
|
|
63
81
|
dismissed?: boolean;
|
|
64
82
|
/** Read receipt — announcement and message types only. */
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import type { INotificationApiActor, INotificationApiRecord, INotificationGroup, INotificationItem, INotificationsApiResponse, INotificationsPanelData, INotificationTab, NotificationType } from '../types/index.js';
|
|
2
2
|
import { type INotificationDateLabels } from './notification-dates.js';
|
|
3
|
+
/**
|
|
4
|
+
* Expand `reaction_counts` into a list of emojis (one entry per count).
|
|
5
|
+
* Unknown reaction keys are skipped.
|
|
6
|
+
*/
|
|
7
|
+
export declare function mapReactionCountsToEmojis(reactionCounts: Record<string, number> | null | undefined): string[];
|
|
3
8
|
export declare const DEFAULT_NOTIFICATION_TABS: INotificationTab[];
|
|
4
9
|
/** Normalize kind values for filter matching and `filter[kind][]` query params. */
|
|
5
10
|
export declare function normalizeNotificationKind(value: string): string;
|
|
@@ -14,6 +19,11 @@ export declare function mapNotificationApiActor(actor: INotificationApiActor): {
|
|
|
14
19
|
name: string;
|
|
15
20
|
avatarUrl: string | undefined;
|
|
16
21
|
};
|
|
22
|
+
export declare const NOTIFICATION_SUMMARY_MAX_LENGTH = 200;
|
|
23
|
+
/**
|
|
24
|
+
* Collapse whitespace/newlines into a single line and truncate for card display.
|
|
25
|
+
*/
|
|
26
|
+
export declare function formatNotificationSummary(summary: string | null | undefined, maxLength?: number): string | undefined;
|
|
17
27
|
export declare function mapNotificationApiRecord(record: INotificationApiRecord, options: {
|
|
18
28
|
locale?: string;
|
|
19
29
|
dateLabels: INotificationDateLabels;
|
|
@@ -15,6 +15,31 @@ const SOURCE_RECORD_TYPE_MAP = {
|
|
|
15
15
|
"school-post": "school-post",
|
|
16
16
|
system: "system"
|
|
17
17
|
};
|
|
18
|
+
const REACTION_EMOJI_MAP = {
|
|
19
|
+
clap: "\u{1F44F}",
|
|
20
|
+
like: "\u{1F44D}",
|
|
21
|
+
heart: "\u2764\uFE0F",
|
|
22
|
+
dislike: "\u{1F44E}",
|
|
23
|
+
laugh: "\u{1F602}",
|
|
24
|
+
wow: "\u{1F62E}"
|
|
25
|
+
};
|
|
26
|
+
export function mapReactionCountsToEmojis(reactionCounts) {
|
|
27
|
+
if (!reactionCounts) {
|
|
28
|
+
return [];
|
|
29
|
+
}
|
|
30
|
+
const emojis = [];
|
|
31
|
+
for (const [key, count] of Object.entries(reactionCounts)) {
|
|
32
|
+
const emoji = REACTION_EMOJI_MAP[key.toLowerCase()];
|
|
33
|
+
const safeCount = Number(count);
|
|
34
|
+
if (!emoji || !Number.isFinite(safeCount) || safeCount < 1) {
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
for (let i = 0; i < Math.floor(safeCount); i += 1) {
|
|
38
|
+
emojis.push(emoji);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return emojis;
|
|
42
|
+
}
|
|
18
43
|
export const DEFAULT_NOTIFICATION_TABS = [
|
|
19
44
|
{ id: "new", label: "New", unreadOnly: true },
|
|
20
45
|
{ id: "all", label: "All" }
|
|
@@ -42,10 +67,26 @@ export function mapNotificationApiActor(actor) {
|
|
|
42
67
|
avatarUrl: actor.avatar_url
|
|
43
68
|
};
|
|
44
69
|
}
|
|
70
|
+
export const NOTIFICATION_SUMMARY_MAX_LENGTH = 200;
|
|
71
|
+
export function formatNotificationSummary(summary, maxLength = NOTIFICATION_SUMMARY_MAX_LENGTH) {
|
|
72
|
+
if (!summary) {
|
|
73
|
+
return void 0;
|
|
74
|
+
}
|
|
75
|
+
const normalized = summary.replace(/\s+/g, " ").trim();
|
|
76
|
+
if (!normalized) {
|
|
77
|
+
return void 0;
|
|
78
|
+
}
|
|
79
|
+
if (normalized.length <= maxLength) {
|
|
80
|
+
return normalized;
|
|
81
|
+
}
|
|
82
|
+
return `${normalized.slice(0, maxLength).trimEnd()}\u2026`;
|
|
83
|
+
}
|
|
45
84
|
export function mapNotificationApiRecord(record, options) {
|
|
46
85
|
const state = record.notification_state;
|
|
47
86
|
const type = mapSourceRecordType(record.source_record_type);
|
|
48
87
|
const activityAt = resolveNotificationActivityAt(record);
|
|
88
|
+
const summary = formatNotificationSummary(record.summary);
|
|
89
|
+
const reactionEmojis = mapReactionCountsToEmojis(record.reaction_counts);
|
|
49
90
|
return {
|
|
50
91
|
id: String(record.id),
|
|
51
92
|
type,
|
|
@@ -61,7 +102,9 @@ export function mapNotificationApiRecord(record, options) {
|
|
|
61
102
|
}
|
|
62
103
|
}),
|
|
63
104
|
createdAt: activityAt,
|
|
64
|
-
contextLabel: record.
|
|
105
|
+
contextLabel: record.room?.name?.trim() || void 0,
|
|
106
|
+
summary,
|
|
107
|
+
reactionEmojis: reactionEmojis.length > 0 ? reactionEmojis : void 0,
|
|
65
108
|
dismissed: state === "dismissed",
|
|
66
109
|
read: state === "read",
|
|
67
110
|
unread: state === "unread",
|
package/locale/en.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@apptegy/nuxt-navigation",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.123",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": {
|
|
@@ -36,20 +36,19 @@
|
|
|
36
36
|
"primevue": "^4.2.5"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
|
-
"@nuxt/devtools": "
|
|
40
|
-
"@nuxt/eslint-config": "
|
|
41
|
-
"@nuxt/module-builder": "
|
|
42
|
-
"@nuxt/schema": "
|
|
43
|
-
"@nuxt/test-utils": "
|
|
44
|
-
"@types/node": "
|
|
45
|
-
"eslint": "
|
|
46
|
-
"nuxt": "
|
|
39
|
+
"@nuxt/devtools": "catalog:",
|
|
40
|
+
"@nuxt/eslint-config": "catalog:",
|
|
41
|
+
"@nuxt/module-builder": "catalog:",
|
|
42
|
+
"@nuxt/schema": "catalog:",
|
|
43
|
+
"@nuxt/test-utils": "catalog:",
|
|
44
|
+
"@types/node": "catalog:",
|
|
45
|
+
"eslint": "catalog:",
|
|
46
|
+
"nuxt": "catalog:",
|
|
47
47
|
"primevue": "^4.5.4",
|
|
48
|
-
"sass": "
|
|
49
|
-
"typescript": "
|
|
50
|
-
"vitest": "
|
|
51
|
-
"vue": "
|
|
52
|
-
"vue-tsc": "
|
|
53
|
-
}
|
|
54
|
-
"gitHead": "82ad905c86fddf3e4037ae98571588d4915209d9"
|
|
48
|
+
"sass": "catalog:",
|
|
49
|
+
"typescript": "catalog:",
|
|
50
|
+
"vitest": "catalog:",
|
|
51
|
+
"vue": "catalog:",
|
|
52
|
+
"vue-tsc": "catalog:"
|
|
53
|
+
}
|
|
55
54
|
}
|