mirror of
https://github.com/philomena-dev/philomena.git
synced 2024-11-23 20:18:00 +01:00
automatic updating notifications
This commit is contained in:
parent
7be7ca8303
commit
e39a8eeccc
8 changed files with 43 additions and 30 deletions
|
@ -8,7 +8,6 @@ import { $ } from './utils/dom';
|
||||||
import { fetchHtml, handleError } from './utils/requests';
|
import { fetchHtml, handleError } from './utils/requests';
|
||||||
import { showBlock } from './utils/image';
|
import { showBlock } from './utils/image';
|
||||||
import { addTag } from './tagsinput';
|
import { addTag } from './tagsinput';
|
||||||
import { markRead } from './notifications';
|
|
||||||
|
|
||||||
// Event types and any qualifying conditions - return true to not run action
|
// Event types and any qualifying conditions - return true to not run action
|
||||||
const types = {
|
const types = {
|
||||||
|
@ -30,7 +29,8 @@ const actions = {
|
||||||
|
|
||||||
disable(data) { selectorCb(data.base, data.value, el => el.disabled = true); },
|
disable(data) { selectorCb(data.base, data.value, el => el.disabled = true); },
|
||||||
|
|
||||||
copy(data) { document.querySelector(data.value).select(); document.execCommand('copy'); },
|
copy(data) { document.querySelector(data.value).select();
|
||||||
|
document.execCommand('copy'); },
|
||||||
|
|
||||||
inputvalue(data) { document.querySelector(data.value).value = data.el.dataset.setValue; },
|
inputvalue(data) { document.querySelector(data.value).value = data.el.dataset.setValue; },
|
||||||
|
|
||||||
|
@ -46,8 +46,8 @@ const actions = {
|
||||||
|
|
||||||
tab(data) {
|
tab(data) {
|
||||||
const block = data.el.parentNode.parentNode,
|
const block = data.el.parentNode.parentNode,
|
||||||
newTab = $(`.block__tab[data-tab="${data.value}"]`),
|
newTab = $(`.block__tab[data-tab="${data.value}"]`),
|
||||||
loadTab = data.el.dataset.loadTab;
|
loadTab = data.el.dataset.loadTab;
|
||||||
|
|
||||||
// Switch tab
|
// Switch tab
|
||||||
const selectedTab = block.querySelector('.selected');
|
const selectedTab = block.querySelector('.selected');
|
||||||
|
@ -74,8 +74,6 @@ const actions = {
|
||||||
|
|
||||||
unfilter(data) { showBlock(data.el.closest('.image-show-container')); },
|
unfilter(data) { showBlock(data.el.closest('.image-show-container')); },
|
||||||
|
|
||||||
markRead,
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Use this function to apply a callback to elements matching the selectors
|
// Use this function to apply a callback to elements matching the selectors
|
||||||
|
@ -87,9 +85,9 @@ function matchAttributes(event) {
|
||||||
if (!types[event.type](event)) {
|
if (!types[event.type](event)) {
|
||||||
for (const action in actions) {
|
for (const action in actions) {
|
||||||
|
|
||||||
const attr = `data-${event.type}-${action.toLowerCase()}`,
|
const attr = `data-${event.type}-${action.toLowerCase()}`,
|
||||||
el = event.target && event.target.closest(`[${attr}]`),
|
el = event.target && event.target.closest(`[${attr}]`),
|
||||||
value = el && el.getAttribute(attr);
|
value = el && el.getAttribute(attr);
|
||||||
|
|
||||||
if (el) {
|
if (el) {
|
||||||
// Return true if you don't want to preventDefault
|
// Return true if you don't want to preventDefault
|
||||||
|
@ -104,4 +102,4 @@ function registerEvents() {
|
||||||
for (const type in types) document.addEventListener(type, matchAttributes);
|
for (const type in types) document.addEventListener(type, matchAttributes);
|
||||||
}
|
}
|
||||||
|
|
||||||
export { registerEvents };
|
export { registerEvents };
|
|
@ -3,18 +3,17 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { fetchJson, handleError } from './utils/requests';
|
import { fetchJson, handleError } from './utils/requests';
|
||||||
import { $, $$, hideEl, toggleEl } from './utils/dom';
|
import { $ } from './utils/dom';
|
||||||
import { delegate } from './utils/events';
|
import { delegate } from './utils/events';
|
||||||
import store from './utils/store';
|
import store from './utils/store';
|
||||||
|
|
||||||
const NOTIFICATION_INTERVAL = 600000,
|
const NOTIFICATION_INTERVAL = 600000,
|
||||||
NOTIFICATION_EXPIRES = 300000;
|
NOTIFICATION_EXPIRES = 300000;
|
||||||
|
|
||||||
function makeRequest(verb, action, body) {
|
function makeRequest(verb) {
|
||||||
return fetchJson(verb, `${window.booru.apiEndpoint}notifications/${action}.json`, body).then(handleError);
|
return fetchJson(verb, '/notifications/unread').then(handleError);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function bindSubscriptionLinks() {
|
function bindSubscriptionLinks() {
|
||||||
delegate(document, 'fetchcomplete', {
|
delegate(document, 'fetchcomplete', {
|
||||||
'.js-subscription-link': event => {
|
'.js-subscription-link': event => {
|
||||||
|
@ -26,28 +25,17 @@ function bindSubscriptionLinks() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function markRead(data) {
|
|
||||||
const notificationId = data.value;
|
|
||||||
const notification = $(`.js-notification-id-${notificationId}`);
|
|
||||||
|
|
||||||
makeRequest('PUT', 'mark_read', { id: notificationId })
|
|
||||||
.then(() => hideEl(notification))
|
|
||||||
.catch(() => data.el.textContent = 'Error!');
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function getNewNotifications() {
|
function getNewNotifications() {
|
||||||
if (document.hidden || !store.hasExpired('notificationCount')) {
|
if (document.hidden || !store.hasExpired('notificationCount')) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
makeRequest('GET', 'unread').then(response => response.json()).then(({ data }) => {
|
makeRequest('GET').then(response => response.json()).then(({ notifications }) => {
|
||||||
updateNotificationTicker(data.length);
|
updateNotificationTicker(notifications);
|
||||||
storeNotificationCount(data.length);
|
storeNotificationCount(notifications);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function updateNotificationTicker(notificationCount) {
|
function updateNotificationTicker(notificationCount) {
|
||||||
const ticker = $('.js-notification-ticker');
|
const ticker = $('.js-notification-ticker');
|
||||||
const parsedNotificationCount = Number(notificationCount);
|
const parsedNotificationCount = Number(notificationCount);
|
||||||
|
@ -78,4 +66,4 @@ function setupNotifications() {
|
||||||
bindSubscriptionLinks();
|
bindSubscriptionLinks();
|
||||||
}
|
}
|
||||||
|
|
||||||
export { setupNotifications, markRead };
|
export { setupNotifications };
|
|
@ -1,6 +1,7 @@
|
||||||
defmodule PhilomenaWeb.ConversationController do
|
defmodule PhilomenaWeb.ConversationController do
|
||||||
use PhilomenaWeb, :controller
|
use PhilomenaWeb, :controller
|
||||||
|
|
||||||
|
alias PhilomenaWeb.NotificationCountPlug
|
||||||
alias Philomena.{Conversations, Conversations.Conversation, Conversations.Message}
|
alias Philomena.{Conversations, Conversations.Conversation, Conversations.Message}
|
||||||
alias Philomena.Textile.Renderer
|
alias Philomena.Textile.Renderer
|
||||||
alias Philomena.Repo
|
alias Philomena.Repo
|
||||||
|
@ -47,6 +48,9 @@ defmodule PhilomenaWeb.ConversationController do
|
||||||
conversation
|
conversation
|
||||||
|> Conversations.mark_conversation_read(user)
|
|> Conversations.mark_conversation_read(user)
|
||||||
|
|
||||||
|
# Update the conversation ticker in the header
|
||||||
|
conn = NotificationCountPlug.call(conn)
|
||||||
|
|
||||||
render(conn, "show.html", conversation: conversation, messages: messages, changeset: changeset)
|
render(conn, "show.html", conversation: conversation, messages: messages, changeset: changeset)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -33,6 +33,9 @@ defmodule PhilomenaWeb.ImageController do
|
||||||
|
|
||||||
Images.clear_notification(image, user)
|
Images.clear_notification(image, user)
|
||||||
|
|
||||||
|
# Update the notification ticker in the header
|
||||||
|
conn = NotificationCountPlug.call(conn)
|
||||||
|
|
||||||
comments =
|
comments =
|
||||||
Comment
|
Comment
|
||||||
|> where(image_id: ^image.id)
|
|> where(image_id: ^image.id)
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
defmodule PhilomenaWeb.Notification.UnreadController do
|
||||||
|
use PhilomenaWeb, :controller
|
||||||
|
|
||||||
|
def index(conn, _params) do
|
||||||
|
json(conn, %{
|
||||||
|
notifications: conn.assigns.notification_count,
|
||||||
|
conversations: conn.assigns.conversation_count
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end
|
|
@ -27,6 +27,9 @@ defmodule PhilomenaWeb.TopicController do
|
||||||
Topics.clear_notification(topic, user)
|
Topics.clear_notification(topic, user)
|
||||||
Forums.clear_notification(forum, user)
|
Forums.clear_notification(forum, user)
|
||||||
|
|
||||||
|
# Update the notification ticker in the header
|
||||||
|
conn = NotificationCountPlug.call(conn)
|
||||||
|
|
||||||
conn = conn |> assign(:topic, topic)
|
conn = conn |> assign(:topic, topic)
|
||||||
%{page_number: page} = conn.assigns.pagination
|
%{page_number: page} = conn.assigns.pagination
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,10 @@ defmodule PhilomenaWeb.NotificationCountPlug do
|
||||||
@spec init(any()) :: any()
|
@spec init(any()) :: any()
|
||||||
def init(opts), do: opts
|
def init(opts), do: opts
|
||||||
|
|
||||||
|
@doc false
|
||||||
|
@spec call(Plug.Conn.t()) :: Plug.Conn.t()
|
||||||
|
def call(conn), do: call(conn, nil)
|
||||||
|
|
||||||
@doc false
|
@doc false
|
||||||
@spec call(Plug.Conn.t(), any()) :: Plug.Conn.t()
|
@spec call(Plug.Conn.t(), any()) :: Plug.Conn.t()
|
||||||
def call(conn, _opts) do
|
def call(conn, _opts) do
|
||||||
|
|
|
@ -75,6 +75,9 @@ defmodule PhilomenaWeb.Router do
|
||||||
scope "/", PhilomenaWeb do
|
scope "/", PhilomenaWeb do
|
||||||
pipe_through [:browser, :ensure_totp, :protected]
|
pipe_through [:browser, :ensure_totp, :protected]
|
||||||
|
|
||||||
|
scope "/notifications", Notification, as: :notification do
|
||||||
|
resources "/unread", UnreadController, only: [:index]
|
||||||
|
end
|
||||||
resources "/notifications", NotificationController, only: [:index, :delete]
|
resources "/notifications", NotificationController, only: [:index, :delete]
|
||||||
resources "/conversations", ConversationController, only: [:index, :show, :new, :create] do
|
resources "/conversations", ConversationController, only: [:index, :show, :new, :create] do
|
||||||
resources "/messages", Conversation.MessageController, only: [:create]
|
resources "/messages", Conversation.MessageController, only: [:create]
|
||||||
|
|
Loading…
Reference in a new issue