Emojis, formatting, clear all
parent
202c4ac4b3
commit
48523a2269
|
@ -18,7 +18,7 @@ fi
|
||||||
if [[ "$1" == *.js ]]; then
|
if [[ "$1" == *.js ]]; then
|
||||||
echo -n "// This file is generated by scripts/emoji-convert.sh to reduce the size
|
echo -n "// This file is generated by scripts/emoji-convert.sh to reduce the size
|
||||||
// Original data source: https://github.com/github/gemoji/blob/master/db/emoji.json
|
// Original data source: https://github.com/github/gemoji/blob/master/db/emoji.json
|
||||||
const rawEmojis = " > "$1"
|
export const rawEmojis = " > "$1"
|
||||||
cat "$SCRIPTDIR/emoji.json" | jq -rc 'map({emoji: .emoji,aliases: .aliases})' >> "$1"
|
cat "$SCRIPTDIR/emoji.json" | jq -rc 'map({emoji: .emoji,aliases: .aliases})' >> "$1"
|
||||||
elif [[ "$1" == *.md ]]; then
|
elif [[ "$1" == *.md ]]; then
|
||||||
echo "# Emoji reference
|
echo "# Emoji reference
|
||||||
|
|
|
@ -1,29 +1,40 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="UTF-8">
|
||||||
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico" />
|
|
||||||
<meta name="viewport" content="initial-scale=1, width=device-width" />
|
|
||||||
<meta name="theme-color" content="#000000" />
|
|
||||||
<!--
|
|
||||||
manifest.json provides metadata used when your web app is installed on a
|
|
||||||
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
|
||||||
-->
|
|
||||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json">
|
|
||||||
<!--
|
|
||||||
Notice the use of %PUBLIC_URL% in the tags above.
|
|
||||||
It will be replaced with the URL of the `public` folder during the build.
|
|
||||||
Only files inside the `public` folder can be referenced from the HTML.
|
|
||||||
|
|
||||||
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
|
<title>ntfy.sh | Send push notifications to your phone via PUT/POST</title>
|
||||||
work correctly both with client-side routing and a non-root public URL.
|
|
||||||
Learn how to configure a non-root public URL by running `npm run build`.
|
<!-- Mobile view -->
|
||||||
-->
|
<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no">
|
||||||
<title>My page</title>
|
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap" />
|
<meta name="HandheldFriendly" content="true">
|
||||||
</head>
|
|
||||||
<body>
|
<!-- Mobile browsers, background color -->
|
||||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
<meta name="theme-color" content="#317f6f">
|
||||||
<div id="root"></div>
|
<meta name="msapplication-navbutton-color" content="#317f6f">
|
||||||
</body>
|
<meta name="apple-mobile-web-app-status-bar-style" content="#317f6f">
|
||||||
|
|
||||||
|
<!-- Favicon, see favicon.io -->
|
||||||
|
<link rel="icon" type="image/png" href="%PUBLIC_URL%/static/img/favicon.png">
|
||||||
|
|
||||||
|
<!-- Previews in Google, Slack, WhatsApp, etc. -->
|
||||||
|
<meta property="og:type" content="website" />
|
||||||
|
<meta property="og:locale" content="en_US" />
|
||||||
|
<meta property="og:site_name" content="ntfy.sh" />
|
||||||
|
<meta property="og:title" content="ntfy.sh | Send push notifications to your phone or desktop via PUT/POST" />
|
||||||
|
<meta property="og:description" content="ntfy is a simple HTTP-based pub-sub notification service. It allows you to send desktop notifications via scripts from any computer, entirely without signup or cost. Made with ❤ by Philipp C. Heckel, Apache License 2.0, source at https://heckel.io/ntfy." />
|
||||||
|
<meta property="og:image" content="/static/img/ntfy.png" />
|
||||||
|
<meta property="og:url" content="https://ntfy.sh" />
|
||||||
|
|
||||||
|
<!-- FIXME Never index topic page -->
|
||||||
|
<!-- <meta name="robots" content="noindex, nofollow" /> -->
|
||||||
|
|
||||||
|
<!-- FIXME Roboto -->
|
||||||
|
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap" />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||||
|
<div id="root"></div>
|
||||||
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 4.6 KiB |
Binary file not shown.
After Width: | Height: | Size: 3.5 KiB |
|
@ -0,0 +1,47 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
height="24px"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
width="24px"
|
||||||
|
fill="#000000"
|
||||||
|
version="1.1"
|
||||||
|
id="svg1428"
|
||||||
|
sodipodi:docname="priority_1_24dp.svg"
|
||||||
|
inkscape:version="1.1.1 (3bf5ae0, 2021-09-20)"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg">
|
||||||
|
<defs
|
||||||
|
id="defs1432" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="namedview1430"
|
||||||
|
pagecolor="#505050"
|
||||||
|
bordercolor="#eeeeee"
|
||||||
|
borderopacity="1"
|
||||||
|
inkscape:pageshadow="0"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pagecheckerboard="0"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="20.517358"
|
||||||
|
inkscape:cx="22.834324"
|
||||||
|
inkscape:cy="15.742768"
|
||||||
|
inkscape:window-width="1863"
|
||||||
|
inkscape:window-height="1025"
|
||||||
|
inkscape:window-x="57"
|
||||||
|
inkscape:window-y="27"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg1428" />
|
||||||
|
<path
|
||||||
|
style="color:#000000;fill:#999999;fill-opacity:1;stroke-width:0.0919748;stroke-linecap:round;stroke-linejoin:round;-inkscape-stroke:none"
|
||||||
|
d="m 12.195014,20.828316 a 1.2747098,1.2747098 0 0 0 0.661605,-0.185206 l 6.646593,-4.037178 a 1.2745823,1.2745823 0 0 0 0.427537,-1.751107 1.2745823,1.2745823 0 0 0 -1.750928,-0.427718 l -5.984807,3.635327 -5.9848086,-3.635327 a 1.2745823,1.2745823 0 0 0 -1.750927,0.427718 1.2745823,1.2745823 0 0 0 0.427536,1.751107 l 6.6464146,4.037178 a 1.2747098,1.2747098 0 0 0 0.661785,0.185206 z"
|
||||||
|
id="rect3554" />
|
||||||
|
<path
|
||||||
|
style="color:#000000;fill:#b3b3b3;fill-opacity:1;stroke:none;stroke-width:0.0919748;stroke-linecap:round;stroke-linejoin:round;-inkscape-stroke:none"
|
||||||
|
d="m 12.195014,15.694014 a 1.2747098,1.2747098 0 0 0 0.661605,-0.185206 l 6.646593,-4.037176 A 1.2745823,1.2745823 0 0 0 19.930749,9.7205243 1.2745823,1.2745823 0 0 0 18.179821,9.2928073 L 12.195014,12.928134 6.2102054,9.2928073 a 1.2745823,1.2745823 0 0 0 -1.750927,0.427717 1.2745823,1.2745823 0 0 0 0.427536,1.7511077 l 6.6464146,4.037176 a 1.2747098,1.2747098 0 0 0 0.661785,0.185206 z"
|
||||||
|
id="path9314" />
|
||||||
|
<path
|
||||||
|
style="color:#000000;fill:#cccccc;fill-opacity:1;stroke:none;stroke-width:0.0919748;stroke-linecap:round;stroke-linejoin:round;-inkscape-stroke:none"
|
||||||
|
d="m 12.116784,10.426777 a 1.2747098,1.2747098 0 0 0 0.661606,-0.185205 l 6.646593,-4.0371767 a 1.2745823,1.2745823 0 0 0 0.427537,-1.751108 1.2745823,1.2745823 0 0 0 -1.750928,-0.427718 l -5.984808,3.635327 -5.9848066,-3.635327 a 1.2745823,1.2745823 0 0 0 -1.750928,0.427718 1.2745823,1.2745823 0 0 0 0.427537,1.751108 L 11.455,10.241572 a 1.2747098,1.2747098 0 0 0 0.661784,0.185205 z"
|
||||||
|
id="path9316" />
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.7 KiB |
|
@ -0,0 +1,43 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
height="24px"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
width="24px"
|
||||||
|
fill="#000000"
|
||||||
|
version="1.1"
|
||||||
|
id="svg1428"
|
||||||
|
sodipodi:docname="priority_2_24dp.svg"
|
||||||
|
inkscape:version="1.1.1 (3bf5ae0, 2021-09-20)"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg">
|
||||||
|
<defs
|
||||||
|
id="defs1432" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="namedview1430"
|
||||||
|
pagecolor="#505050"
|
||||||
|
bordercolor="#eeeeee"
|
||||||
|
borderopacity="1"
|
||||||
|
inkscape:pageshadow="0"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pagecheckerboard="0"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="20.517358"
|
||||||
|
inkscape:cx="22.834324"
|
||||||
|
inkscape:cy="15.742768"
|
||||||
|
inkscape:window-width="1863"
|
||||||
|
inkscape:window-height="1025"
|
||||||
|
inkscape:window-x="57"
|
||||||
|
inkscape:window-y="27"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg1428" />
|
||||||
|
<path
|
||||||
|
style="color:#000000;fill:#999999;fill-opacity:1;stroke-width:0.0919748;stroke-linecap:round;stroke-linejoin:round;-inkscape-stroke:none"
|
||||||
|
d="m 12.172712,17.774352 a 1.2747098,1.2747098 0 0 0 0.661605,-0.185206 l 6.646593,-4.037178 a 1.2745823,1.2745823 0 0 0 0.427537,-1.751107 1.2745823,1.2745823 0 0 0 -1.750928,-0.427718 L 12.172712,15.00847 6.1879033,11.373143 a 1.2745823,1.2745823 0 0 0 -1.750927,0.427718 1.2745823,1.2745823 0 0 0 0.427536,1.751107 l 6.6464147,4.037178 a 1.2747098,1.2747098 0 0 0 0.661785,0.185206 z"
|
||||||
|
id="rect3554" />
|
||||||
|
<path
|
||||||
|
style="color:#000000;fill:#b3b3b3;fill-opacity:1;stroke:none;stroke-width:0.0919748;stroke-linecap:round;stroke-linejoin:round;-inkscape-stroke:none"
|
||||||
|
d="m 12.172712,12.64005 a 1.2747098,1.2747098 0 0 0 0.661605,-0.185206 L 19.48091,8.4176679 A 1.2745823,1.2745823 0 0 0 19.908447,6.6665602 1.2745823,1.2745823 0 0 0 18.157519,6.2388432 L 12.172712,9.8741699 6.1879033,6.2388432 a 1.2745823,1.2745823 0 0 0 -1.750927,0.427717 1.2745823,1.2745823 0 0 0 0.427536,1.7511077 l 6.6464147,4.0371761 a 1.2747098,1.2747098 0 0 0 0.661785,0.185206 z"
|
||||||
|
id="path9314" />
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.1 KiB |
|
@ -0,0 +1,43 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
height="24px"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
width="24px"
|
||||||
|
fill="#000000"
|
||||||
|
version="1.1"
|
||||||
|
id="svg1428"
|
||||||
|
sodipodi:docname="priority_4_24dp.svg"
|
||||||
|
inkscape:version="1.1.1 (3bf5ae0, 2021-09-20)"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg">
|
||||||
|
<defs
|
||||||
|
id="defs1432" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="namedview1430"
|
||||||
|
pagecolor="#505050"
|
||||||
|
bordercolor="#eeeeee"
|
||||||
|
borderopacity="1"
|
||||||
|
inkscape:pageshadow="0"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pagecheckerboard="0"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="20.517358"
|
||||||
|
inkscape:cx="22.834324"
|
||||||
|
inkscape:cy="15.742768"
|
||||||
|
inkscape:window-width="1863"
|
||||||
|
inkscape:window-height="1025"
|
||||||
|
inkscape:window-x="57"
|
||||||
|
inkscape:window-y="27"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg1428" />
|
||||||
|
<path
|
||||||
|
style="color:#000000;fill:#c60000;fill-opacity:1;stroke:none;stroke-width:0.0919748;stroke-linecap:round;stroke-linejoin:round;-inkscape-stroke:none"
|
||||||
|
d="M 12.116784,6.5394415 A 1.2747098,1.2747098 0 0 0 11.455179,6.724648 l -6.6465926,4.037176 a 1.2745823,1.2745823 0 0 0 -0.427537,1.751108 1.2745823,1.2745823 0 0 0 1.7509281,0.427717 l 5.9848065,-3.635327 5.984809,3.635327 A 1.2745823,1.2745823 0 0 0 19.85252,12.512932 1.2745823,1.2745823 0 0 0 19.424984,10.761824 L 12.778569,6.724648 A 1.2747098,1.2747098 0 0 0 12.116784,6.5394415 Z"
|
||||||
|
id="path9314" />
|
||||||
|
<path
|
||||||
|
style="color:#000000;fill:#de0000;fill-opacity:1;stroke:none;stroke-width:0.0919748;stroke-linecap:round;stroke-linejoin:round;-inkscape-stroke:none"
|
||||||
|
d="m 12.195014,11.806679 a 1.2747098,1.2747098 0 0 0 -0.661606,0.185205 l -6.6465924,4.037177 a 1.2745823,1.2745823 0 0 0 -0.427537,1.751108 1.2745823,1.2745823 0 0 0 1.750928,0.427718 l 5.9848074,-3.635327 5.984807,3.635327 a 1.2745823,1.2745823 0 0 0 1.750928,-0.427718 1.2745823,1.2745823 0 0 0 -0.427537,-1.751108 l -6.646414,-4.037177 a 1.2747098,1.2747098 0 0 0 -0.661784,-0.185205 z"
|
||||||
|
id="path9316" />
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.1 KiB |
|
@ -0,0 +1,47 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
height="24px"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
width="24px"
|
||||||
|
fill="#000000"
|
||||||
|
version="1.1"
|
||||||
|
id="svg1428"
|
||||||
|
sodipodi:docname="priority_5_24dp.svg"
|
||||||
|
inkscape:version="1.1.1 (3bf5ae0, 2021-09-20)"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg">
|
||||||
|
<defs
|
||||||
|
id="defs1432" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="namedview1430"
|
||||||
|
pagecolor="#505050"
|
||||||
|
bordercolor="#eeeeee"
|
||||||
|
borderopacity="1"
|
||||||
|
inkscape:pageshadow="0"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pagecheckerboard="0"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="20.517358"
|
||||||
|
inkscape:cx="22.834323"
|
||||||
|
inkscape:cy="15.742767"
|
||||||
|
inkscape:window-width="1863"
|
||||||
|
inkscape:window-height="1025"
|
||||||
|
inkscape:window-x="57"
|
||||||
|
inkscape:window-y="27"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg1428" />
|
||||||
|
<path
|
||||||
|
style="color:#000000;fill:#aa0000;fill-opacity:1;stroke-width:0.0919748;stroke-linecap:round;stroke-linejoin:round;-inkscape-stroke:none"
|
||||||
|
d="M 12.116784,3.40514 A 1.2747098,1.2747098 0 0 0 11.455179,3.5903463 L 4.8085864,7.6275238 A 1.2745823,1.2745823 0 0 0 4.3810494,9.3786313 1.2745823,1.2745823 0 0 0 6.1319775,9.8063489 L 12.116784,6.1710217 18.101593,9.8063489 A 1.2745823,1.2745823 0 0 0 19.85252,9.3786313 1.2745823,1.2745823 0 0 0 19.424984,7.6275238 L 12.778569,3.5903463 A 1.2747098,1.2747098 0 0 0 12.116784,3.40514 Z"
|
||||||
|
id="rect3554" />
|
||||||
|
<path
|
||||||
|
style="color:#000000;fill:#c60000;fill-opacity:1;stroke:none;stroke-width:0.0919748;stroke-linecap:round;stroke-linejoin:round;-inkscape-stroke:none"
|
||||||
|
d="M 12.116784,8.5394415 A 1.2747098,1.2747098 0 0 0 11.455179,8.724648 l -6.6465926,4.037176 a 1.2745823,1.2745823 0 0 0 -0.427537,1.751108 1.2745823,1.2745823 0 0 0 1.7509281,0.427717 l 5.9848065,-3.635327 5.984809,3.635327 A 1.2745823,1.2745823 0 0 0 19.85252,14.512932 1.2745823,1.2745823 0 0 0 19.424984,12.761824 L 12.778569,8.724648 A 1.2747098,1.2747098 0 0 0 12.116784,8.5394415 Z"
|
||||||
|
id="path9314" />
|
||||||
|
<path
|
||||||
|
style="color:#000000;fill:#de0000;fill-opacity:1;stroke:none;stroke-width:0.0919748;stroke-linecap:round;stroke-linejoin:round;-inkscape-stroke:none"
|
||||||
|
d="m 12.195014,13.806679 a 1.2747098,1.2747098 0 0 0 -0.661606,0.185205 l -6.6465924,4.037177 a 1.2745823,1.2745823 0 0 0 -0.427537,1.751108 1.2745823,1.2745823 0 0 0 1.750928,0.427718 l 5.9848074,-3.635327 5.984807,3.635327 a 1.2745823,1.2745823 0 0 0 1.750928,-0.427718 1.2745823,1.2745823 0 0 0 -0.427537,-1.751108 l -6.646414,-4.037177 a 1.2747098,1.2747098 0 0 0 -0.661784,-0.185205 z"
|
||||||
|
id="path9316" />
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.7 KiB |
|
@ -1,14 +1,18 @@
|
||||||
import {topicUrl, shortTopicUrl, topicUrlWs} from './utils';
|
import {shortTopicUrl, topicUrl} from './utils';
|
||||||
|
|
||||||
export default class Subscription {
|
export default class Subscription {
|
||||||
constructor(baseUrl, topic) {
|
constructor(baseUrl, topic) {
|
||||||
this.id = topicUrl(baseUrl, topic);
|
this.id = topicUrl(baseUrl, topic);
|
||||||
this.baseUrl = baseUrl;
|
this.baseUrl = baseUrl;
|
||||||
this.topic = topic;
|
this.topic = topic;
|
||||||
this.notifications = new Map();
|
this.notifications = new Map(); // notification ID -> notification object
|
||||||
|
this.deleted = new Set(); // notification IDs
|
||||||
}
|
}
|
||||||
|
|
||||||
addNotification(notification) {
|
addNotification(notification) {
|
||||||
|
if (this.notifications.has(notification.id) || this.deleted.has(notification.id)) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
this.notifications.set(notification.id, notification);
|
this.notifications.set(notification.id, notification);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
@ -18,6 +22,21 @@ export default class Subscription {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
deleteNotification(notificationId) {
|
||||||
|
this.notifications.delete(notificationId);
|
||||||
|
this.deleted.add(notificationId);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteAllNotifications() {
|
||||||
|
console.log(this.notifications);
|
||||||
|
for (const [id] of this.notifications) {
|
||||||
|
console.log(`delete ${id}`);
|
||||||
|
this.deleteNotification(id);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
getNotifications() {
|
getNotifications() {
|
||||||
return Array.from(this.notifications.values());
|
return Array.from(this.notifications.values());
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -1,3 +1,5 @@
|
||||||
|
import { rawEmojis} from "./emojis";
|
||||||
|
|
||||||
export const topicUrl = (baseUrl, topic) => `${baseUrl}/${topic}`;
|
export const topicUrl = (baseUrl, topic) => `${baseUrl}/${topic}`;
|
||||||
export const topicUrlWs = (baseUrl, topic) => `${topicUrl(baseUrl, topic)}/ws`
|
export const topicUrlWs = (baseUrl, topic) => `${topicUrl(baseUrl, topic)}/ws`
|
||||||
.replaceAll("https://", "wss://")
|
.replaceAll("https://", "wss://")
|
||||||
|
@ -8,6 +10,46 @@ export const topicUrlJsonPoll = (baseUrl, topic) => `${topicUrlJson(baseUrl, top
|
||||||
export const shortUrl = (url) => url.replaceAll(/https?:\/\//g, "");
|
export const shortUrl = (url) => url.replaceAll(/https?:\/\//g, "");
|
||||||
export const shortTopicUrl = (baseUrl, topic) => shortUrl(topicUrl(baseUrl, topic));
|
export const shortTopicUrl = (baseUrl, topic) => shortUrl(topicUrl(baseUrl, topic));
|
||||||
|
|
||||||
|
// Format emojis (see emoji.js)
|
||||||
|
const emojis = {};
|
||||||
|
rawEmojis.forEach(emoji => {
|
||||||
|
emoji.aliases.forEach(alias => {
|
||||||
|
emojis[alias] = emoji.emoji;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const toEmojis = (tags) => {
|
||||||
|
if (!tags) return [];
|
||||||
|
else return tags.filter(tag => tag in emojis).map(tag => emojis[tag]);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const formatTitle = (m) => {
|
||||||
|
const emojiList = toEmojis(m.tags);
|
||||||
|
if (emojiList.length > 0) {
|
||||||
|
return `${emojiList.join(" ")} ${m.title}`;
|
||||||
|
} else {
|
||||||
|
return m.title;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const formatMessage = (m) => {
|
||||||
|
if (m.title) {
|
||||||
|
return m.message;
|
||||||
|
} else {
|
||||||
|
const emojiList = toEmojis(m.tags);
|
||||||
|
if (emojiList.length > 0) {
|
||||||
|
return `${emojiList.join(" ")} ${m.message}`;
|
||||||
|
} else {
|
||||||
|
return m.message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const unmatchedTags = (tags) => {
|
||||||
|
if (!tags) return [];
|
||||||
|
else return tags.filter(tag => !(tag in emojis));
|
||||||
|
}
|
||||||
|
|
||||||
// From: https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch
|
// From: https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch
|
||||||
export async function* fetchLinesIterator(fileURL) {
|
export async function* fetchLinesIterator(fileURL) {
|
||||||
const utf8Decoder = new TextDecoder('utf-8');
|
const utf8Decoder = new TextDecoder('utf-8');
|
||||||
|
|
|
@ -130,6 +130,13 @@ const App = () => {
|
||||||
console.log(`[App] Cancel clicked`);
|
console.log(`[App] Cancel clicked`);
|
||||||
setSubscribeDialogOpen(false);
|
setSubscribeDialogOpen(false);
|
||||||
};
|
};
|
||||||
|
const handleClearAll = (subscriptionId) => {
|
||||||
|
console.log(`[App] Deleting all notifications from ${subscriptionId}`);
|
||||||
|
setSubscriptions(prev => {
|
||||||
|
const newSubscription = prev.get(subscriptionId).deleteAllNotifications();
|
||||||
|
return prev.update(newSubscription).clone();
|
||||||
|
});
|
||||||
|
};
|
||||||
const handleUnsubscribe = (subscriptionId) => {
|
const handleUnsubscribe = (subscriptionId) => {
|
||||||
console.log(`[App] Unsubscribing from ${subscriptionId}`);
|
console.log(`[App] Unsubscribing from ${subscriptionId}`);
|
||||||
setSubscriptions(prev => {
|
setSubscriptions(prev => {
|
||||||
|
@ -179,6 +186,7 @@ const App = () => {
|
||||||
</Typography>
|
</Typography>
|
||||||
{selectedSubscription !== null && <DetailSettingsIcon
|
{selectedSubscription !== null && <DetailSettingsIcon
|
||||||
subscription={selectedSubscription}
|
subscription={selectedSubscription}
|
||||||
|
onClearAll={handleClearAll}
|
||||||
onUnsubscribe={handleUnsubscribe}
|
onUnsubscribe={handleUnsubscribe}
|
||||||
/>}
|
/>}
|
||||||
</Toolbar>
|
</Toolbar>
|
||||||
|
|
|
@ -26,6 +26,11 @@ const DetailSettingsIcon = (props) => {
|
||||||
setOpen(false);
|
setOpen(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleClearAll = (event) => {
|
||||||
|
handleClose(event);
|
||||||
|
props.onClearAll(props.subscription.id);
|
||||||
|
};
|
||||||
|
|
||||||
const handleUnsubscribe = (event) => {
|
const handleUnsubscribe = (event) => {
|
||||||
handleClose(event);
|
handleClose(event);
|
||||||
props.onUnsubscribe(props.subscription.id);
|
props.onUnsubscribe(props.subscription.id);
|
||||||
|
@ -97,6 +102,7 @@ const DetailSettingsIcon = (props) => {
|
||||||
onKeyDown={handleListKeyDown}
|
onKeyDown={handleListKeyDown}
|
||||||
>
|
>
|
||||||
<MenuItem onClick={handleSendTestMessage}>Send test notification</MenuItem>
|
<MenuItem onClick={handleSendTestMessage}>Send test notification</MenuItem>
|
||||||
|
<MenuItem onClick={handleClearAll}>Clear all notifications</MenuItem>
|
||||||
<MenuItem onClick={handleUnsubscribe}>Unsubscribe</MenuItem>
|
<MenuItem onClick={handleUnsubscribe}>Unsubscribe</MenuItem>
|
||||||
</MenuList>
|
</MenuList>
|
||||||
</ClickAwayListener>
|
</ClickAwayListener>
|
||||||
|
|
|
@ -3,12 +3,13 @@ import {CardContent, Stack} from "@mui/material";
|
||||||
import Card from "@mui/material/Card";
|
import Card from "@mui/material/Card";
|
||||||
import Typography from "@mui/material/Typography";
|
import Typography from "@mui/material/Typography";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
|
import {formatTitle, formatMessage, unmatchedTags} from "../app/utils";
|
||||||
|
|
||||||
const NotificationList = (props) => {
|
const NotificationList = (props) => {
|
||||||
const sortedNotifications = props.notifications.sort((a, b) => a.time < b.time);
|
const sortedNotifications = props.notifications.sort((a, b) => a.time < b.time);
|
||||||
return (
|
return (
|
||||||
<Container maxWidth="lg" sx={{ marginTop: 3 }}>
|
<Container maxWidth="lg" sx={{ marginTop: 3, marginBottom: 3 }}>
|
||||||
<Stack container spacing={3}>
|
<Stack spacing={3}>
|
||||||
{sortedNotifications.map(notification =>
|
{sortedNotifications.map(notification =>
|
||||||
<NotificationItem key={notification.id} notification={notification}/>)}
|
<NotificationItem key={notification.id} notification={notification}/>)}
|
||||||
</Stack>
|
</Stack>
|
||||||
|
@ -20,13 +21,22 @@ const NotificationItem = (props) => {
|
||||||
const notification = props.notification;
|
const notification = props.notification;
|
||||||
const date = new Intl.DateTimeFormat('default', {dateStyle: 'short', timeStyle: 'short'})
|
const date = new Intl.DateTimeFormat('default', {dateStyle: 'short', timeStyle: 'short'})
|
||||||
.format(new Date(notification.time * 1000));
|
.format(new Date(notification.time * 1000));
|
||||||
const tags = (notification.tags && notification.tags.length > 0) ? notification.tags.join(', ') : null;
|
const otherTags = unmatchedTags(notification.tags);
|
||||||
|
const tags = (otherTags.length > 0) ? otherTags.join(', ') : null;
|
||||||
return (
|
return (
|
||||||
<Card sx={{ minWidth: 275 }}>
|
<Card sx={{ minWidth: 275 }}>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<Typography sx={{ fontSize: 14 }} color="text.secondary">{date}</Typography>
|
<Typography sx={{ fontSize: 14 }} color="text.secondary">
|
||||||
{notification.title && <Typography variant="h5" component="div">{notification.title}</Typography>}
|
{date}
|
||||||
<Typography variant="body1" sx={{ whiteSpace: 'pre-line' }}>{notification.message}</Typography>
|
{[1,2,4,5].includes(notification.priority) &&
|
||||||
|
<img
|
||||||
|
src={`static/img/priority-${notification.priority}.svg`}
|
||||||
|
alt={`Priority ${notification.priority}`}
|
||||||
|
style={{ verticalAlign: 'bottom' }}
|
||||||
|
/>}
|
||||||
|
</Typography>
|
||||||
|
{notification.title && <Typography variant="h5" component="div">{formatTitle(notification)}</Typography>}
|
||||||
|
<Typography variant="body1" sx={{ whiteSpace: 'pre-line' }}>{formatMessage(notification)}</Typography>
|
||||||
{tags && <Typography sx={{ fontSize: 14 }} color="text.secondary">Tags: {tags}</Typography>}
|
{tags && <Typography sx={{ fontSize: 14 }} color="text.secondary">Tags: {tags}</Typography>}
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
Loading…
Reference in New Issue