automatic updating notifications

This commit is contained in:
byte[] 2019-12-02 09:55:48 -05:00
parent 7be7ca8303
commit e39a8eeccc
8 changed files with 43 additions and 30 deletions

View file

@ -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 };

View file

@ -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 };

View file

@ -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

View file

@ -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)

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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]