Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/master'
Browse files Browse the repository at this point in the history
  • Loading branch information
liamwhite committed Jun 23, 2024
2 parents efb8b1a + dd9091e commit cc5c21c
Show file tree
Hide file tree
Showing 5 changed files with 59 additions and 47 deletions.
55 changes: 32 additions & 23 deletions assets/js/misc.js → assets/js/misc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,36 +3,43 @@
*/

import store from './utils/store';
import { $, $$ } from './utils/dom';
import { $, $$, hideEl, showEl } from './utils/dom';
import { assertNotNull, assertType } from './utils/assert';
import '../types/ujs';

let touchMoved = false;

function formResult({target, detail}) {

const elements = {
function formResult({target, detail}: FetchcompleteEvent) {
const elements: Record<string, string> = {
'#description-form': '.image-description',
'#uploader-form': '.image_uploader'
};

function showResult(resultEl, formEl, response) {
function showResult(formEl: HTMLFormElement, resultEl: HTMLElement, response: string) {
resultEl.innerHTML = response;
resultEl.classList.remove('hidden');
formEl.classList.add('hidden');
formEl.querySelector('input[type="submit"],button').disabled = false;
}
hideEl(formEl);
showEl(resultEl);

for (const element in elements) {
if (target.matches(element)) detail.text().then(text => showResult($(elements[element]), target, text));
$$<HTMLInputElement | HTMLButtonElement>('input[type="submit"],button', formEl).forEach(button => {
button.disabled = false;
});
}

}
for (const [ formSelector, resultSelector ] of Object.entries(elements)) {
if (target.matches(formSelector)) {
const form = assertType(target, HTMLFormElement);
const result = assertNotNull($<HTMLElement>(resultSelector));

function revealSpoiler(event) {
detail.text().then(text => showResult(form, result, text));
}
}
}

const { target } = event;
function revealSpoiler(event: MouseEvent | TouchEvent) {
const target = assertNotNull(event.target) as HTMLElement;
const spoiler = target.closest('.spoiler');
let imgspoiler = target.closest('.spoiler .imgspoiler, .spoiler-revealed .imgspoiler');
const showContainer = target.closest('.image-show-container');
let imgspoiler = target.closest('.spoiler .imgspoiler, .spoiler-revealed .imgspoiler');

// Prevent reveal if touchend came after touchmove event
if (touchMoved) {
Expand All @@ -42,7 +49,8 @@ function revealSpoiler(event) {

if (spoiler) {
if (showContainer) {
const imageShow = showContainer.querySelector('.image-show');
const imageShow = assertNotNull(showContainer.querySelector('.image-show'));

if (!imageShow.classList.contains('hidden') && imageShow.classList.contains('spoiler-pending')) {
imageShow.classList.remove('spoiler-pending');
return;
Expand All @@ -62,25 +70,26 @@ function revealSpoiler(event) {
if (imgspoiler) {
imgspoiler.classList.remove('imgspoiler');
imgspoiler.classList.add('imgspoiler-revealed');

if (event.type === 'touchend' && !event.defaultPrevented) {
event.preventDefault();
}
}

}

function setupEvents() {
const extrameta = $('#extrameta');
export function setupEvents() {
const extrameta = $<HTMLElement>('#extrameta');

if (extrameta && store.get('hide_uploader')) {
hideEl(extrameta);
}

if (store.get('hide_uploader') && extrameta) extrameta.classList.add('hidden');
if (store.get('hide_score')) {
$$('.upvotes,.score,.downvotes').forEach(s => s.classList.add('hidden'));
$$<HTMLElement>('.upvotes,.score,.downvotes').forEach(s => hideEl(s));
}

document.addEventListener('fetchcomplete', formResult);
document.addEventListener('click', revealSpoiler);
document.addEventListener('touchend', revealSpoiler);
document.addEventListener('touchmove', () => touchMoved = true);
}

export { setupEvents };
40 changes: 19 additions & 21 deletions assets/js/notifications.js → assets/js/notifications.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,17 @@
import { fetchJson, handleError } from './utils/requests';
import { $ } from './utils/dom';
import { delegate } from './utils/events';
import { assertNotNull, assertNotUndefined } from './utils/assert';
import store from './utils/store';

const NOTIFICATION_INTERVAL = 600000,
NOTIFICATION_EXPIRES = 300000;

function makeRequest(verb) {
return fetchJson(verb, '/notifications/unread').then(handleError);
}

function bindSubscriptionLinks() {
delegate(document, 'fetchcomplete', {
'.js-subscription-link': event => {
const target = event.target.closest('.js-subscription-target');
const target = assertNotNull(event.target.closest('.js-subscription-target'));

event.detail.text().then(text => {
target.outerHTML = text;
});
Expand All @@ -30,42 +28,42 @@ function getNewNotifications() {
return;
}

makeRequest('GET').then(response => response.json()).then(({ notifications }) => {
updateNotificationTicker(notifications);
storeNotificationCount(notifications);
fetchJson('GET', '/notifications/unread')
.then(handleError)
.then(response => response.json())
.then(({ notifications }) => {
updateNotificationTicker(notifications);
storeNotificationCount(notifications);

setTimeout(getNewNotifications, NOTIFICATION_INTERVAL);
});
setTimeout(getNewNotifications, NOTIFICATION_INTERVAL);
});
}

function updateNotificationTicker(notificationCount) {
const ticker = $('.js-notification-ticker');
function updateNotificationTicker(notificationCount: string | null) {
const ticker = assertNotNull($<HTMLSpanElement>('.js-notification-ticker'));
const parsedNotificationCount = Number(notificationCount);

ticker.dataset.notificationCount = parsedNotificationCount;
ticker.textContent = parsedNotificationCount;
ticker.dataset.notificationCount = parsedNotificationCount.toString();
ticker.textContent = parsedNotificationCount.toString();
}


function storeNotificationCount(notificationCount) {
function storeNotificationCount(notificationCount: string) {
// The current number of notifications are stored along with the time when the data expires
store.setWithExpireTime('notificationCount', notificationCount, NOTIFICATION_EXPIRES);
}


function setupNotifications() {
export function setupNotifications() {
if (!window.booru.userIsSignedIn) return;

// Fetch notifications from the server at a regular interval
setTimeout(getNewNotifications, NOTIFICATION_INTERVAL);

// Update the current number of notifications based on the latest page load
storeNotificationCount($('.js-notification-ticker').dataset.notificationCount);
const ticker = assertNotNull($<HTMLSpanElement>('.js-notification-ticker'));
storeNotificationCount(assertNotUndefined(ticker.dataset.notificationCount));

// Update ticker when the stored value changes - this will occur in all open tabs
store.watch('notificationCount', updateNotificationTicker);

bindSubscriptionLinks();
}

export { setupNotifications };
5 changes: 4 additions & 1 deletion assets/js/utils/events.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
// DOM events

import '../../types/ujs';

export interface PhilomenaAvailableEventsMap {
dragstart: DragEvent,
dragover: DragEvent,
Expand All @@ -9,7 +11,8 @@ export interface PhilomenaAvailableEventsMap {
drop: DragEvent,
click: MouseEvent,
submit: Event,
reset: Event
reset: Event,
fetchcomplete: FetchcompleteEvent
}

export interface PhilomenaEventElement {
Expand Down
4 changes: 2 additions & 2 deletions assets/js/utils/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,9 @@ export default {
},

// Watch changes to a specified key - returns value on change
watch(key: string, callback: (value: unknown) => void) {
watch<Value = unknown>(key: string, callback: (value: Value | null) => void) {
const handler = (event: StorageEvent) => {
if (event.key === key) callback(this.get(key));
if (event.key === key) callback(this.get<Value>(key));
};
window.addEventListener('storage', handler);
return () => window.removeEventListener('storage', handler);
Expand Down
2 changes: 2 additions & 0 deletions lib/philomena_web/controllers/api/json/image_controller.ex
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ defmodule PhilomenaWeb.Api.Json.ImageController do

case Images.create_image(attributes, image_params) do
{:ok, %{image: image}} ->
image = Repo.preload(image, tags: :aliases)

PhilomenaWeb.Endpoint.broadcast!(
"firehose",
"image:create",
Expand Down

0 comments on commit cc5c21c

Please sign in to comment.