diff --git a/web/public/static/langs/bg.json b/web/public/static/langs/bg.json
index 8df20531..a4ecbf69 100644
--- a/web/public/static/langs/bg.json
+++ b/web/public/static/langs/bg.json
@@ -56,7 +56,7 @@
"notifications_attachment_copy_url_button": "Копиране на адреса",
"notifications_attachment_open_button": "Отваряне на прикачения файл",
"notifications_attachment_link_expires": "препратката изтича на {{date}}",
- "notifications_click_open_title": "Към {{url}}",
+ "notifications_actions_open_url_title": "Към {{url}}",
"notifications_click_copy_url_button": "Копиране на препратка",
"notifications_click_open_button": "Отваряне",
"notifications_click_copy_url_title": "Копира препратката в междинната памет",
diff --git a/web/public/static/langs/de.json b/web/public/static/langs/de.json
index 3beca7f4..2be4707f 100644
--- a/web/public/static/langs/de.json
+++ b/web/public/static/langs/de.json
@@ -48,7 +48,7 @@
"notifications_attachment_open_button": "Anhang öffnen",
"notifications_attachment_link_expired": "Download-Link ist abgelaufen",
"notifications_click_copy_url_button": "Link kopieren",
- "notifications_click_open_title": "Gehe zu {{url}}",
+ "notifications_actions_open_url_title": "Gehe zu {{url}}",
"publish_dialog_other_features": "Andere Optionen:",
"notifications_none_for_topic_description": "Um Benachrichtigungen an dieses Thema zu senden, PUTe/POSTe an die Themen-URL.",
"notifications_no_subscriptions_title": "Anscheinend hast Du noch keine Themen abonniert.",
diff --git a/web/public/static/langs/en.json b/web/public/static/langs/en.json
index a27ff867..d668ce2c 100644
--- a/web/public/static/langs/en.json
+++ b/web/public/static/langs/en.json
@@ -26,8 +26,10 @@
"notifications_attachment_link_expired": "download link expired",
"notifications_click_copy_url_title": "Copy link URL to clipboard",
"notifications_click_copy_url_button": "Copy link",
- "notifications_click_open_title": "Go to {{url}}",
"notifications_click_open_button": "Open link",
+ "notifications_actions_open_url_title": "Go to {{url}}",
+ "notifications_actions_not_supported": "Action not supported in web app",
+ "notifications_actions_http_request_title": "Send HTTP {{method}} to {{url}}",
"notifications_none_for_topic_title": "You haven't received any notifications for this topic yet.",
"notifications_none_for_topic_description": "To send notifications to this topic, simply PUT or POST to the topic URL.",
"notifications_none_for_any_title": "You haven't received any notifications.",
diff --git a/web/public/static/langs/es.json b/web/public/static/langs/es.json
index c4baa83f..1d8c62ca 100644
--- a/web/public/static/langs/es.json
+++ b/web/public/static/langs/es.json
@@ -26,7 +26,7 @@
"notifications_attachment_link_expired": "el enlace de descarga ha expirado",
"notifications_click_copy_url_title": "Copiar la URL del enlace en el portapapeles",
"notifications_click_copy_url_button": "Copiar enlace",
- "notifications_click_open_title": "Ir a {{url}}",
+ "notifications_actions_open_url_title": "Ir a {{url}}",
"notifications_click_open_button": "Abrir enlace",
"notifications_none_for_topic_title": "Aún no has recibido ninguna notificación en este tópico.",
"notifications_none_for_topic_description": "Para enviar notificaciones a este tópico, simplemente realice un PUT o POST a la URL del tópico.",
diff --git a/web/public/static/langs/fr.json b/web/public/static/langs/fr.json
index b83cf869..bb559af2 100644
--- a/web/public/static/langs/fr.json
+++ b/web/public/static/langs/fr.json
@@ -24,7 +24,7 @@
"notifications_click_copy_url_button": "Copier le lien",
"notifications_click_open_button": "Ouvrir le lien",
"notifications_none_for_topic_title": "Vous n'avez pas encore reçu de notifications pour ce sujet.",
- "notifications_click_open_title": "Aller à {{url}}",
+ "notifications_actions_open_url_title": "Aller à {{url}}",
"notifications_example": "Exemple",
"notifications_loading": "Chargement des notifications…",
"publish_dialog_progress_uploading": "Téléversement…",
diff --git a/web/public/static/langs/id.json b/web/public/static/langs/id.json
index 2b4c1192..b9dfde1b 100644
--- a/web/public/static/langs/id.json
+++ b/web/public/static/langs/id.json
@@ -29,7 +29,7 @@
"notifications_attachment_open_button": "Buka lampiran",
"notifications_attachment_link_expires": "tautan kadaluwarsa {{date}}",
"notifications_attachment_link_expired": "tautan unduhan kadaluwarsa",
- "notifications_click_open_title": "Pergi ke {{url}}",
+ "notifications_actions_open_url_title": "Pergi ke {{url}}",
"notifications_click_open_button": "Buka tautan",
"publish_dialog_topic_placeholder": "Nama topik, mis. pemberitahuan_andi",
"nav_button_publish_message": "Publikasikan notifikasi",
diff --git a/web/public/static/langs/ja.json b/web/public/static/langs/ja.json
index e456a655..e9d51cbc 100644
--- a/web/public/static/langs/ja.json
+++ b/web/public/static/langs/ja.json
@@ -40,7 +40,7 @@
"notifications_attachment_copy_url_button": "URLをコピー",
"notifications_attachment_open_title": "{{url}} に移動",
"notifications_attachment_link_expired": "ダウンロードリンクは失効しました",
- "notifications_click_open_title": "{{url}} に移動",
+ "notifications_actions_open_url_title": "{{url}} に移動",
"notifications_attachment_copy_url_title": "添付URLをクリップボードにコピー",
"notifications_attachment_open_button": "添付ファイルを開く",
"notifications_click_copy_url_title": "リンクURLをクリップボードにコピー",
diff --git a/web/public/static/langs/nb_NO.json b/web/public/static/langs/nb_NO.json
index 84526e64..1dab51b3 100644
--- a/web/public/static/langs/nb_NO.json
+++ b/web/public/static/langs/nb_NO.json
@@ -18,7 +18,7 @@
"notifications_attachment_open_title": "Gå til {{url}}",
"notifications_attachment_link_expires": "lenken utløper {{date}}",
"notifications_click_copy_url_title": "Kopier lenke-nettadresse til utklippstavlen",
- "notifications_click_open_title": "Gå til {{url}}",
+ "notifications_actions_open_url_title": "Gå til {{url}}",
"notifications_tags": "Etiketter",
"notifications_attachment_link_expired": "nedlastingslenken har utløpt",
"notifications_none_for_any_title": "Du har ikke mottatt noen merknader.",
diff --git a/web/public/static/langs/ru.json b/web/public/static/langs/ru.json
index 5f4c4e69..ed97047a 100644
--- a/web/public/static/langs/ru.json
+++ b/web/public/static/langs/ru.json
@@ -59,7 +59,7 @@
"notifications_none_for_any_title": "Вы ещё не получали никаких уведомлений.",
"alert_grant_title": "Уведомления отключены",
"notifications_attachment_copy_url_title": "Скопировать URL-адрес вложения",
- "notifications_click_open_title": "Перейти на {{url}}",
+ "notifications_actions_open_url_title": "Перейти на {{url}}",
"notifications_tags": "Тэги",
"notifications_attachment_link_expires": "срок действия ссылки истекает {{date}}",
"notifications_click_copy_url_title": "Скопировать URL-адрес ссылки",
diff --git a/web/public/static/langs/tr.json b/web/public/static/langs/tr.json
index 24250059..254213ef 100644
--- a/web/public/static/langs/tr.json
+++ b/web/public/static/langs/tr.json
@@ -71,7 +71,7 @@
"notifications_none_for_any_title": "Herhangi bir bildirim almadınız.",
"notifications_attachment_link_expired": "indirme bağlantısının süresi doldu",
"notifications_click_copy_url_button": "Bağlantıyı kopyala",
- "notifications_click_open_title": "{{url}} adresine git",
+ "notifications_actions_open_url_title": "{{url}} adresine git",
"notifications_click_open_button": "Bağlantıyı aç",
"notifications_no_subscriptions_description": "Bir konu oluşturmak veya bir konuya abone olmak için \"{{linktext}}\" bağlantısına tıklayın. Bundan sonra PUT veya POST yoluyla mesaj gönderebilirsiniz ve buradan bildirimler alırsınız.",
"notifications_example": "Örnek",
diff --git a/web/src/app/SubscriptionManager.js b/web/src/app/SubscriptionManager.js
index b1e44498..b485383d 100644
--- a/web/src/app/SubscriptionManager.js
+++ b/web/src/app/SubscriptionManager.js
@@ -92,6 +92,19 @@ class SubscriptionManager {
});
}
+ async updateNotification(notification) {
+ const exists = await db.notifications.get(notification.id);
+ if (!exists) {
+ return false;
+ }
+ try {
+ await db.notifications.put({ ...notification });
+ } catch (e) {
+ console.error(`[SubscriptionManager] Error updating notification`, e);
+ }
+ return true;
+ }
+
async deleteNotification(notificationId) {
await db.notifications.delete(notificationId);
}
diff --git a/web/src/app/utils.js b/web/src/app/utils.js
index adba2e9f..aaf89111 100644
--- a/web/src/app/utils.js
+++ b/web/src/app/utils.js
@@ -105,6 +105,18 @@ export const encodeBase64Url = (s) => {
return Base64.encodeURI(s);
}
+export const maybeAppendActionErrors = (message, notification) => {
+ const actionErrors = (notification.actions ?? [])
+ .map(action => action.error)
+ .filter(action => !!action)
+ .join("\n")
+ if (actionErrors.length === 0) {
+ return message;
+ } else {
+ return `${message}\n\n${actionErrors}`;
+ }
+}
+
export const shuffle = (arr) => {
let j, x;
for (let index = arr.length - 1; index > 0; index--) {
diff --git a/web/src/components/Notifications.js b/web/src/components/Notifications.js
index a412e29e..3a952190 100644
--- a/web/src/components/Notifications.js
+++ b/web/src/components/Notifications.js
@@ -19,7 +19,7 @@ import {
formatBytes,
formatMessage,
formatShortDateTime,
- formatTitle,
+ formatTitle, maybeAppendActionErrors,
openUrl,
shortUrl,
topicShortUrl,
@@ -138,9 +138,10 @@ const NotificationItem = (props) => {
props.onShowSnack();
};
const expired = attachment && attachment.expires && attachment.expires < Date.now()/1000;
- const showAttachmentActions = attachment && !expired;
- const showClickAction = notification.click;
- const showActions = showAttachmentActions || showClickAction;
+ const hasAttachmentActions = attachment && !expired;
+ const hasClickAction = notification.click;
+ const hasUserActions = notification.actions && notification.actions.length > 0;
+ const showActions = hasAttachmentActions || hasClickAction || hasUserActions;
return (
@@ -161,13 +162,15 @@ const NotificationItem = (props) => {
}
{notification.title && {formatTitle(notification)}}
- {autolink(formatMessage(notification))}
+
+ {autolink(maybeAppendActionErrors(formatMessage(notification), notification))}
+
{attachment && }
{tags && {t("notifications_tags")}: {tags}}
{showActions &&
- {showAttachmentActions && <>
+ {hasAttachmentActions && <>
@@ -175,14 +178,15 @@ const NotificationItem = (props) => {
>}
- {showClickAction && <>
+ {hasClickAction && <>
-
+
>}
+ {hasUserActions && }
}
);
@@ -329,6 +333,83 @@ const Image = (props) => {
);
}
+const UserActions = (props) => {
+ return (
+ <>{props.notification.actions.map(action =>
+ )}>
+ );
+};
+
+const UserAction = (props) => {
+ const { t } = useTranslation();
+ const notification = props.notification;
+ const action = props.action;
+ if (action.action === "broadcast") {
+ return (
+
+
+
+ );
+ } else if (action.action === "view") {
+ return (
+
+
+
+ );
+ } else if (action.action === "http") {
+ const method = action.method ?? "POST";
+ const label = action.label + (ACTION_LABEL_SUFFIX[action.progress ?? 0] ?? "");
+ return (
+
+
+
+ );
+ }
+ return null; // Others
+};
+
+const performHttpAction = async (notification, action) => {
+ console.log(`[Notifications] Performing HTTP user action`, action);
+ try {
+ updateActionStatus(notification, action, ACTION_PROGRESS_ONGOING, null);
+ const response = await fetch(action.url, {
+ method: action.method ?? "POST",
+ headers: action.headers ?? {},
+ body: action.body ?? ""
+ });
+ console.log(`[Notifications] HTTP user action response`, response);
+ const success = response.status >= 200 && response.status <= 299;
+ if (success) {
+ updateActionStatus(notification, action, ACTION_PROGRESS_SUCCESS, null);
+ } else {
+ updateActionStatus(notification, action, ACTION_PROGRESS_FAILED, `${action.label}: Unexpected response HTTP ${response.status}`);
+ }
+ } catch (e) {
+ console.log(`[Notifications] HTTP action failed`, e);
+ updateActionStatus(notification, action, ACTION_PROGRESS_FAILED, `${action.label}: ${e} Check developer console for details.`);
+ }
+};
+
+const updateActionStatus = (notification, action, progress, error) => {
+ notification.actions = notification.actions.map(a => {
+ if (a.id !== action.id) {
+ return a;
+ }
+ return { ...a, progress: progress, error: error };
+ });
+ subscriptionManager.updateNotification(notification);
+}
+
+const ACTION_PROGRESS_ONGOING = 1;
+const ACTION_PROGRESS_SUCCESS = 2;
+const ACTION_PROGRESS_FAILED = 3;
+
+const ACTION_LABEL_SUFFIX = {
+ [ACTION_PROGRESS_ONGOING]: " …",
+ [ACTION_PROGRESS_SUCCESS]: " ✔",
+ [ACTION_PROGRESS_FAILED]: " ❌"
+};
+
const NoNotifications = (props) => {
const { t } = useTranslation();
const shortUrl = topicShortUrl(props.subscription.baseUrl, props.subscription.topic);