strapi-plugin-notifier 1.0.2 → 1.1.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 +36 -0
- package/README.md +130 -20
- package/dist/_chunks/{Index-CiDBgkGf.js → Index-DVk2enQ2.js} +36 -19
- package/dist/_chunks/{Index-DI2t9tlH.mjs → Index-_PTtaZsz.mjs} +20 -3
- package/dist/_chunks/{SettingsPage-DIKfBfvo.mjs → SettingsPage-CuTIslG0.mjs} +95 -6
- package/dist/_chunks/{SettingsPage-BdBxZMqP.js → SettingsPage-DYxNBA3h.js} +100 -11
- package/dist/_chunks/index-BgPnE501.mjs +221 -0
- package/dist/_chunks/index-CKh_iBP2.js +220 -0
- package/dist/admin/index.js +1 -2
- package/dist/admin/index.mjs +1 -2
- package/dist/server/index.js +315 -92
- package/dist/server/index.mjs +315 -92
- package/package.json +9 -3
- package/tests/helpers.ts +145 -0
- package/tests/unit/config.test.ts +64 -0
- package/tests/unit/notification.test.ts +263 -0
- package/tests/unit/notifier.test.ts +348 -0
- package/tests/unit/preference.test.ts +122 -0
- package/vitest.config.ts +9 -0
- package/dist/_chunks/index-7WJGsVEY.js +0 -28282
- package/dist/_chunks/index-CNYabBMJ.mjs +0 -28264
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import { jsxs, jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useState, useEffect, useRef } from "react";
|
|
3
|
+
import { useFetchClient } from "@strapi/admin/strapi-admin";
|
|
4
|
+
let items = [];
|
|
5
|
+
let countSubscribers = [];
|
|
6
|
+
let itemSubscribers = [];
|
|
7
|
+
const getUnreadCount = () => items.filter((n) => !n.read).length;
|
|
8
|
+
const broadcastCount = () => {
|
|
9
|
+
const n = getUnreadCount();
|
|
10
|
+
countSubscribers.forEach((fn) => fn(n));
|
|
11
|
+
};
|
|
12
|
+
const broadcastItems = () => {
|
|
13
|
+
const snapshot = [...items];
|
|
14
|
+
itemSubscribers.forEach((fn) => fn(snapshot));
|
|
15
|
+
broadcastCount();
|
|
16
|
+
};
|
|
17
|
+
const getNotificationCount = () => getUnreadCount();
|
|
18
|
+
const getNotifications = () => [...items];
|
|
19
|
+
const subscribeToCount = (fn) => {
|
|
20
|
+
countSubscribers.push(fn);
|
|
21
|
+
return () => {
|
|
22
|
+
countSubscribers = countSubscribers.filter((s) => s !== fn);
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
const subscribeToNotifications = (fn) => {
|
|
26
|
+
itemSubscribers.push(fn);
|
|
27
|
+
return () => {
|
|
28
|
+
itemSubscribers = itemSubscribers.filter((s) => s !== fn);
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
const setNotifications = (next) => {
|
|
32
|
+
items = next;
|
|
33
|
+
broadcastItems();
|
|
34
|
+
};
|
|
35
|
+
const markAsRead = (id) => {
|
|
36
|
+
items = items.map((n) => n.id === id ? { ...n, read: true } : n);
|
|
37
|
+
broadcastItems();
|
|
38
|
+
};
|
|
39
|
+
const markAllAsRead = () => {
|
|
40
|
+
items = items.map((n) => ({ ...n, read: true }));
|
|
41
|
+
broadcastItems();
|
|
42
|
+
};
|
|
43
|
+
const clearNotification = (id) => {
|
|
44
|
+
items = items.filter((n) => n.id !== id);
|
|
45
|
+
broadcastItems();
|
|
46
|
+
};
|
|
47
|
+
const clearAll = () => {
|
|
48
|
+
items = [];
|
|
49
|
+
broadcastItems();
|
|
50
|
+
};
|
|
51
|
+
const useNotificationCount = () => {
|
|
52
|
+
const [count, setCount] = useState(getNotificationCount);
|
|
53
|
+
useEffect(() => subscribeToCount(setCount), []);
|
|
54
|
+
return count;
|
|
55
|
+
};
|
|
56
|
+
const DEFAULT_CONFIG = {
|
|
57
|
+
pollIntervalMs: 3e4,
|
|
58
|
+
pageSize: 20,
|
|
59
|
+
theme: {
|
|
60
|
+
accent: {
|
|
61
|
+
info: "#4945ff",
|
|
62
|
+
success: "#5cb85c",
|
|
63
|
+
warning: "#f0ad4e",
|
|
64
|
+
error: "#ee5e52"
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
let config = DEFAULT_CONFIG;
|
|
69
|
+
let subscribers = [];
|
|
70
|
+
const getConfig = () => config;
|
|
71
|
+
const setConfig = (next) => {
|
|
72
|
+
config = { ...DEFAULT_CONFIG, ...next, theme: { accent: { ...DEFAULT_CONFIG.theme.accent, ...next?.theme?.accent } } };
|
|
73
|
+
subscribers.forEach((fn) => fn(config));
|
|
74
|
+
};
|
|
75
|
+
const subscribeToConfig = (fn) => {
|
|
76
|
+
subscribers.push(fn);
|
|
77
|
+
return () => {
|
|
78
|
+
subscribers = subscribers.filter((s) => s !== fn);
|
|
79
|
+
};
|
|
80
|
+
};
|
|
81
|
+
const usePluginConfig = () => {
|
|
82
|
+
const [config2, setConfig2] = useState(getConfig);
|
|
83
|
+
useEffect(() => subscribeToConfig(setConfig2), []);
|
|
84
|
+
return config2;
|
|
85
|
+
};
|
|
86
|
+
const toStoreNotification = (n) => ({
|
|
87
|
+
id: String(n.id),
|
|
88
|
+
title: n.title,
|
|
89
|
+
message: n.message ?? "",
|
|
90
|
+
type: n.type ?? "info",
|
|
91
|
+
read: n.read,
|
|
92
|
+
createdAt: n.createdAt,
|
|
93
|
+
url: n.url
|
|
94
|
+
});
|
|
95
|
+
function Bell() {
|
|
96
|
+
const count = useNotificationCount();
|
|
97
|
+
const config2 = usePluginConfig();
|
|
98
|
+
const { get } = useFetchClient();
|
|
99
|
+
const configLoaded = useRef(false);
|
|
100
|
+
useEffect(() => {
|
|
101
|
+
if (configLoaded.current) return;
|
|
102
|
+
configLoaded.current = true;
|
|
103
|
+
get("/notifier/config").then(({ data }) => {
|
|
104
|
+
if (data) setConfig(data);
|
|
105
|
+
}).catch(() => {
|
|
106
|
+
});
|
|
107
|
+
}, [get]);
|
|
108
|
+
useEffect(() => {
|
|
109
|
+
const poll = async () => {
|
|
110
|
+
try {
|
|
111
|
+
const { data } = await get(`/notifier/notifications?page=1&pageSize=${config2.pageSize}`);
|
|
112
|
+
if (Array.isArray(data?.data)) {
|
|
113
|
+
setNotifications(data.data.map(toStoreNotification));
|
|
114
|
+
}
|
|
115
|
+
} catch {
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
poll();
|
|
119
|
+
const id = setInterval(poll, config2.pollIntervalMs);
|
|
120
|
+
return () => clearInterval(id);
|
|
121
|
+
}, [get, config2.pollIntervalMs, config2.pageSize]);
|
|
122
|
+
return /* @__PURE__ */ jsxs("span", { style: { position: "relative", display: "inline-flex" }, children: [
|
|
123
|
+
/* @__PURE__ */ jsxs(
|
|
124
|
+
"svg",
|
|
125
|
+
{
|
|
126
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
127
|
+
viewBox: "0 0 24 24",
|
|
128
|
+
fill: "none",
|
|
129
|
+
stroke: "currentColor",
|
|
130
|
+
strokeWidth: "2",
|
|
131
|
+
strokeLinecap: "round",
|
|
132
|
+
strokeLinejoin: "round",
|
|
133
|
+
width: "1.5em",
|
|
134
|
+
height: "1.5em",
|
|
135
|
+
children: [
|
|
136
|
+
/* @__PURE__ */ jsx("path", { d: "M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9" }),
|
|
137
|
+
/* @__PURE__ */ jsx("path", { d: "M13.73 21a2 2 0 0 1-3.46 0" })
|
|
138
|
+
]
|
|
139
|
+
}
|
|
140
|
+
),
|
|
141
|
+
count > 0 && /* @__PURE__ */ jsx(
|
|
142
|
+
"span",
|
|
143
|
+
{
|
|
144
|
+
"aria-label": `${count} unread notification${count === 1 ? "" : "s"}`,
|
|
145
|
+
style: {
|
|
146
|
+
position: "absolute",
|
|
147
|
+
top: "-5px",
|
|
148
|
+
right: "-6px",
|
|
149
|
+
backgroundColor: "#ee5e52",
|
|
150
|
+
color: "#fff",
|
|
151
|
+
borderRadius: "9px",
|
|
152
|
+
fontSize: "9px",
|
|
153
|
+
fontWeight: 700,
|
|
154
|
+
height: "13px",
|
|
155
|
+
minWidth: "13px",
|
|
156
|
+
display: "flex",
|
|
157
|
+
alignItems: "center",
|
|
158
|
+
justifyContent: "center",
|
|
159
|
+
padding: "0 2px",
|
|
160
|
+
lineHeight: 1,
|
|
161
|
+
pointerEvents: "none"
|
|
162
|
+
},
|
|
163
|
+
children: count > 99 ? "99+" : count
|
|
164
|
+
}
|
|
165
|
+
)
|
|
166
|
+
] });
|
|
167
|
+
}
|
|
168
|
+
const pluginId = "notifier";
|
|
169
|
+
const index = {
|
|
170
|
+
register(app) {
|
|
171
|
+
app.addMenuLink({
|
|
172
|
+
to: `/plugins/${pluginId}`,
|
|
173
|
+
icon: Bell,
|
|
174
|
+
intlLabel: {
|
|
175
|
+
id: `${pluginId}.plugin.name`,
|
|
176
|
+
defaultMessage: "Notifications"
|
|
177
|
+
},
|
|
178
|
+
permissions: [],
|
|
179
|
+
Component: () => import("./Index-_PTtaZsz.mjs")
|
|
180
|
+
});
|
|
181
|
+
app.createSettingSection(
|
|
182
|
+
{
|
|
183
|
+
id: pluginId,
|
|
184
|
+
intlLabel: {
|
|
185
|
+
id: `${pluginId}.settings.section`,
|
|
186
|
+
defaultMessage: "Notifier"
|
|
187
|
+
}
|
|
188
|
+
},
|
|
189
|
+
[
|
|
190
|
+
{
|
|
191
|
+
intlLabel: {
|
|
192
|
+
id: `${pluginId}.settings.page`,
|
|
193
|
+
defaultMessage: "Configuration"
|
|
194
|
+
},
|
|
195
|
+
id: `${pluginId}.settings`,
|
|
196
|
+
to: `/settings/${pluginId}`,
|
|
197
|
+
Component: () => import("./SettingsPage-CuTIslG0.mjs"),
|
|
198
|
+
permissions: [
|
|
199
|
+
{ action: `plugin::${pluginId}.settings.read`, subject: null }
|
|
200
|
+
]
|
|
201
|
+
}
|
|
202
|
+
]
|
|
203
|
+
);
|
|
204
|
+
app.registerPlugin({
|
|
205
|
+
id: pluginId,
|
|
206
|
+
name: "Notifier"
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
};
|
|
210
|
+
export {
|
|
211
|
+
getNotifications as a,
|
|
212
|
+
markAllAsRead as b,
|
|
213
|
+
clearNotification as c,
|
|
214
|
+
clearAll as d,
|
|
215
|
+
subscribeToNotifications as e,
|
|
216
|
+
getConfig as g,
|
|
217
|
+
index as i,
|
|
218
|
+
markAsRead as m,
|
|
219
|
+
setNotifications as s,
|
|
220
|
+
usePluginConfig as u
|
|
221
|
+
};
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
const jsxRuntime = require("react/jsx-runtime");
|
|
3
|
+
const react = require("react");
|
|
4
|
+
const strapiAdmin = require("@strapi/admin/strapi-admin");
|
|
5
|
+
let items = [];
|
|
6
|
+
let countSubscribers = [];
|
|
7
|
+
let itemSubscribers = [];
|
|
8
|
+
const getUnreadCount = () => items.filter((n) => !n.read).length;
|
|
9
|
+
const broadcastCount = () => {
|
|
10
|
+
const n = getUnreadCount();
|
|
11
|
+
countSubscribers.forEach((fn) => fn(n));
|
|
12
|
+
};
|
|
13
|
+
const broadcastItems = () => {
|
|
14
|
+
const snapshot = [...items];
|
|
15
|
+
itemSubscribers.forEach((fn) => fn(snapshot));
|
|
16
|
+
broadcastCount();
|
|
17
|
+
};
|
|
18
|
+
const getNotificationCount = () => getUnreadCount();
|
|
19
|
+
const getNotifications = () => [...items];
|
|
20
|
+
const subscribeToCount = (fn) => {
|
|
21
|
+
countSubscribers.push(fn);
|
|
22
|
+
return () => {
|
|
23
|
+
countSubscribers = countSubscribers.filter((s) => s !== fn);
|
|
24
|
+
};
|
|
25
|
+
};
|
|
26
|
+
const subscribeToNotifications = (fn) => {
|
|
27
|
+
itemSubscribers.push(fn);
|
|
28
|
+
return () => {
|
|
29
|
+
itemSubscribers = itemSubscribers.filter((s) => s !== fn);
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
const setNotifications = (next) => {
|
|
33
|
+
items = next;
|
|
34
|
+
broadcastItems();
|
|
35
|
+
};
|
|
36
|
+
const markAsRead = (id) => {
|
|
37
|
+
items = items.map((n) => n.id === id ? { ...n, read: true } : n);
|
|
38
|
+
broadcastItems();
|
|
39
|
+
};
|
|
40
|
+
const markAllAsRead = () => {
|
|
41
|
+
items = items.map((n) => ({ ...n, read: true }));
|
|
42
|
+
broadcastItems();
|
|
43
|
+
};
|
|
44
|
+
const clearNotification = (id) => {
|
|
45
|
+
items = items.filter((n) => n.id !== id);
|
|
46
|
+
broadcastItems();
|
|
47
|
+
};
|
|
48
|
+
const clearAll = () => {
|
|
49
|
+
items = [];
|
|
50
|
+
broadcastItems();
|
|
51
|
+
};
|
|
52
|
+
const useNotificationCount = () => {
|
|
53
|
+
const [count, setCount] = react.useState(getNotificationCount);
|
|
54
|
+
react.useEffect(() => subscribeToCount(setCount), []);
|
|
55
|
+
return count;
|
|
56
|
+
};
|
|
57
|
+
const DEFAULT_CONFIG = {
|
|
58
|
+
pollIntervalMs: 3e4,
|
|
59
|
+
pageSize: 20,
|
|
60
|
+
theme: {
|
|
61
|
+
accent: {
|
|
62
|
+
info: "#4945ff",
|
|
63
|
+
success: "#5cb85c",
|
|
64
|
+
warning: "#f0ad4e",
|
|
65
|
+
error: "#ee5e52"
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
let config = DEFAULT_CONFIG;
|
|
70
|
+
let subscribers = [];
|
|
71
|
+
const getConfig = () => config;
|
|
72
|
+
const setConfig = (next) => {
|
|
73
|
+
config = { ...DEFAULT_CONFIG, ...next, theme: { accent: { ...DEFAULT_CONFIG.theme.accent, ...next?.theme?.accent } } };
|
|
74
|
+
subscribers.forEach((fn) => fn(config));
|
|
75
|
+
};
|
|
76
|
+
const subscribeToConfig = (fn) => {
|
|
77
|
+
subscribers.push(fn);
|
|
78
|
+
return () => {
|
|
79
|
+
subscribers = subscribers.filter((s) => s !== fn);
|
|
80
|
+
};
|
|
81
|
+
};
|
|
82
|
+
const usePluginConfig = () => {
|
|
83
|
+
const [config2, setConfig2] = react.useState(getConfig);
|
|
84
|
+
react.useEffect(() => subscribeToConfig(setConfig2), []);
|
|
85
|
+
return config2;
|
|
86
|
+
};
|
|
87
|
+
const toStoreNotification = (n) => ({
|
|
88
|
+
id: String(n.id),
|
|
89
|
+
title: n.title,
|
|
90
|
+
message: n.message ?? "",
|
|
91
|
+
type: n.type ?? "info",
|
|
92
|
+
read: n.read,
|
|
93
|
+
createdAt: n.createdAt,
|
|
94
|
+
url: n.url
|
|
95
|
+
});
|
|
96
|
+
function Bell() {
|
|
97
|
+
const count = useNotificationCount();
|
|
98
|
+
const config2 = usePluginConfig();
|
|
99
|
+
const { get } = strapiAdmin.useFetchClient();
|
|
100
|
+
const configLoaded = react.useRef(false);
|
|
101
|
+
react.useEffect(() => {
|
|
102
|
+
if (configLoaded.current) return;
|
|
103
|
+
configLoaded.current = true;
|
|
104
|
+
get("/notifier/config").then(({ data }) => {
|
|
105
|
+
if (data) setConfig(data);
|
|
106
|
+
}).catch(() => {
|
|
107
|
+
});
|
|
108
|
+
}, [get]);
|
|
109
|
+
react.useEffect(() => {
|
|
110
|
+
const poll = async () => {
|
|
111
|
+
try {
|
|
112
|
+
const { data } = await get(`/notifier/notifications?page=1&pageSize=${config2.pageSize}`);
|
|
113
|
+
if (Array.isArray(data?.data)) {
|
|
114
|
+
setNotifications(data.data.map(toStoreNotification));
|
|
115
|
+
}
|
|
116
|
+
} catch {
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
poll();
|
|
120
|
+
const id = setInterval(poll, config2.pollIntervalMs);
|
|
121
|
+
return () => clearInterval(id);
|
|
122
|
+
}, [get, config2.pollIntervalMs, config2.pageSize]);
|
|
123
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("span", { style: { position: "relative", display: "inline-flex" }, children: [
|
|
124
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
125
|
+
"svg",
|
|
126
|
+
{
|
|
127
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
128
|
+
viewBox: "0 0 24 24",
|
|
129
|
+
fill: "none",
|
|
130
|
+
stroke: "currentColor",
|
|
131
|
+
strokeWidth: "2",
|
|
132
|
+
strokeLinecap: "round",
|
|
133
|
+
strokeLinejoin: "round",
|
|
134
|
+
width: "1.5em",
|
|
135
|
+
height: "1.5em",
|
|
136
|
+
children: [
|
|
137
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9" }),
|
|
138
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M13.73 21a2 2 0 0 1-3.46 0" })
|
|
139
|
+
]
|
|
140
|
+
}
|
|
141
|
+
),
|
|
142
|
+
count > 0 && /* @__PURE__ */ jsxRuntime.jsx(
|
|
143
|
+
"span",
|
|
144
|
+
{
|
|
145
|
+
"aria-label": `${count} unread notification${count === 1 ? "" : "s"}`,
|
|
146
|
+
style: {
|
|
147
|
+
position: "absolute",
|
|
148
|
+
top: "-5px",
|
|
149
|
+
right: "-6px",
|
|
150
|
+
backgroundColor: "#ee5e52",
|
|
151
|
+
color: "#fff",
|
|
152
|
+
borderRadius: "9px",
|
|
153
|
+
fontSize: "9px",
|
|
154
|
+
fontWeight: 700,
|
|
155
|
+
height: "13px",
|
|
156
|
+
minWidth: "13px",
|
|
157
|
+
display: "flex",
|
|
158
|
+
alignItems: "center",
|
|
159
|
+
justifyContent: "center",
|
|
160
|
+
padding: "0 2px",
|
|
161
|
+
lineHeight: 1,
|
|
162
|
+
pointerEvents: "none"
|
|
163
|
+
},
|
|
164
|
+
children: count > 99 ? "99+" : count
|
|
165
|
+
}
|
|
166
|
+
)
|
|
167
|
+
] });
|
|
168
|
+
}
|
|
169
|
+
const pluginId = "notifier";
|
|
170
|
+
const index = {
|
|
171
|
+
register(app) {
|
|
172
|
+
app.addMenuLink({
|
|
173
|
+
to: `/plugins/${pluginId}`,
|
|
174
|
+
icon: Bell,
|
|
175
|
+
intlLabel: {
|
|
176
|
+
id: `${pluginId}.plugin.name`,
|
|
177
|
+
defaultMessage: "Notifications"
|
|
178
|
+
},
|
|
179
|
+
permissions: [],
|
|
180
|
+
Component: () => Promise.resolve().then(() => require("./Index-DVk2enQ2.js"))
|
|
181
|
+
});
|
|
182
|
+
app.createSettingSection(
|
|
183
|
+
{
|
|
184
|
+
id: pluginId,
|
|
185
|
+
intlLabel: {
|
|
186
|
+
id: `${pluginId}.settings.section`,
|
|
187
|
+
defaultMessage: "Notifier"
|
|
188
|
+
}
|
|
189
|
+
},
|
|
190
|
+
[
|
|
191
|
+
{
|
|
192
|
+
intlLabel: {
|
|
193
|
+
id: `${pluginId}.settings.page`,
|
|
194
|
+
defaultMessage: "Configuration"
|
|
195
|
+
},
|
|
196
|
+
id: `${pluginId}.settings`,
|
|
197
|
+
to: `/settings/${pluginId}`,
|
|
198
|
+
Component: () => Promise.resolve().then(() => require("./SettingsPage-DYxNBA3h.js")),
|
|
199
|
+
permissions: [
|
|
200
|
+
{ action: `plugin::${pluginId}.settings.read`, subject: null }
|
|
201
|
+
]
|
|
202
|
+
}
|
|
203
|
+
]
|
|
204
|
+
);
|
|
205
|
+
app.registerPlugin({
|
|
206
|
+
id: pluginId,
|
|
207
|
+
name: "Notifier"
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
exports.clearAll = clearAll;
|
|
212
|
+
exports.clearNotification = clearNotification;
|
|
213
|
+
exports.getConfig = getConfig;
|
|
214
|
+
exports.getNotifications = getNotifications;
|
|
215
|
+
exports.index = index;
|
|
216
|
+
exports.markAllAsRead = markAllAsRead;
|
|
217
|
+
exports.markAsRead = markAsRead;
|
|
218
|
+
exports.setNotifications = setNotifications;
|
|
219
|
+
exports.subscribeToNotifications = subscribeToNotifications;
|
|
220
|
+
exports.usePluginConfig = usePluginConfig;
|
package/dist/admin/index.js
CHANGED
package/dist/admin/index.mjs
CHANGED