docbrown/app/javascript/packs/initializers/initializeNotifications.js
Lawrence 0c93bde6c2
Subscribe to Comment Functionality (#19555)
* Move save over and add button labels

* comment notification design and functionality

* controller error handling

* add ternery operator for background class

* minor cleanup

* add proper styling toggles

* tests and mobile spec

* add for all buttons

* add article as a param

* comment is not required on article preview

* update policy spec

* add test for coverage

* add proper button titles

* use the last user comment as comment id

* add better error handling and remove multiple fetch calls in js

* copy change to threads

* update copy, fix multiple fetch calls, add observer for loading more notifications

* update spec

* add rspecs

* more test specs

* fix test specs

* subscribe button text tests

* add export

* optimistically update ui function tests

* button initialize test

* check for comment ancestry

* add feature flag block

* some cleanup

* update logic and specs

* oops

* rubocop issues

* change config names

* change config names

* fix spec

* add one more conditional

* check for comments

* service-object the toggler

* separate routes

* continual refactoring

* refine spec tests

* clean up policy

* Adjust js and view files to match be params

* some service object refactoring

* separate endpoints on client-side

* broken spec

* service specs

* policy spec fixes

* remove action param

* remove action param from spec

* minor updates

* trigger build

* update to post

* Services don't need permitted params

* Notifications continue to be a mess, decorator specs to follow

* Not toggling now

* This doesn't really belong here

* Fix tests after moving

* Streamline front-end

* There could be errors?

* polymorphic_name is a class thing

* ☠️ json_data in notification views

* Use feature_flag helper to get current_user

* Cannot derive current subscription status from notification, only from subscription

* Default is subscribe to all article comments

* Sync up matching unsubscribe, make subscribe idempotent-ish

* Temporarily comment-out button jest tests to see if I can unstick CI

* Stop trying to detect potential subscribe-to-comment

* Stop trying to detect potential subscribe-to-comment

* Minor spec refactoring

* Add specs for article notification decoration

* NotificationDecorator specs are rather long

* Rubocop likes it?

* Minor spec refactoring

* Doesn't need to stay pending

* Add spec for subscription finder

* Expand Subscribe service specs

* YAGNI, but I don't like hard-coded config

* Refactor Unsubscribe service specs

* nobody expects the infinite scroll

* This *might* work, except for feature flag

* Ancestry can be quite deep actually

* Rubocop

* Update subscribe button for subscribe-to-thread

* Jest tests are just tests

* hasAttribute != getAttribute

* Test knows **nothing** about window size

* Tests have been lying about mobileLabel this whole time

* Make payload/endpoint testable

* Is Jest actually happy???

* Still cleaning up jest/eslint

* Temporarily comment-out cypress tests pending feature flag

* Thanks, Rubocop, what would we do without you

* Preserve round-trip for 'top-level' and 'only-author' subscriptions

* Update specs

* Move save button icon

* Update test with better config param

* Try feature flag specific to cypress env

* Tell cypress to wait for (un)subscribing to work

---------

Co-authored-by: Lawrence S <lawrence@forem.com>
Co-authored-by: Joshua Wehner <joshua@forem.com>
2023-07-10 17:11:46 +02:00

207 lines
6.3 KiB
JavaScript

import { sendHapticMessage } from '../../utilities/sendHapticMessage';
import { checkUserLoggedIn } from '../../utilities/checkUserLoggedIn';
import { showModalAfterError } from '../../utilities/showUserAlertModal';
import { initializeSubscribeButton } from '../../packs/subscribeButton';
// eslint-disable-next-line no-redeclare
/* global InstantClick, instantClick */
function markNotificationsAsRead() {
setTimeout(() => {
if (document.getElementById('notifications-container')) {
getCsrfToken().then((csrfToken) => {
const locationAsArray = window.location.pathname.split('/');
// Use regex to ensure only numbers in the original string are converted to integers
const parsedLastParam = parseInt(
locationAsArray[locationAsArray.length - 1].replace(/[^0-9]/g, ''),
10,
);
const options = {
method: 'POST',
headers: { 'X-CSRF-Token': csrfToken },
};
if (Number.isInteger(parsedLastParam)) {
fetch(`/notifications/reads?org_id=${parsedLastParam}`, options);
} else {
fetch('/notifications/reads', options);
}
});
}
}, 450);
}
function fetchNotificationsCount() {
if (
document.getElementById('notifications-container') == null &&
checkUserLoggedIn()
) {
// Prefetch notifications page
if (instantClick) {
InstantClick.removeExpiredKeys('force');
setTimeout(() => {
InstantClick.preload(
document.getElementById('notifications-link').href,
'force',
);
}, 30);
}
}
}
function initReactions() {
setTimeout(() => {
if (document.getElementById('notifications-container')) {
let butts = document.getElementsByClassName('reaction-button');
for (let i = 0; i < butts.length; i++) {
const butt = butts[i];
butt.setAttribute('aria-pressed', butt.classList.contains('reacted'));
butt.onclick = function (event) {
event.preventDefault();
sendHapticMessage('medium');
const thisButt = this;
thisButt.classList.add('reacted');
function successCb(response) {
if (response.result === 'create') {
thisButt.classList.add('reacted');
thisButt.setAttribute('aria-pressed', true);
} else {
thisButt.classList.remove('reacted');
thisButt.setAttribute('aria-pressed', false);
}
}
const formData = new FormData();
formData.append('reactable_type', thisButt.dataset.reactableType);
formData.append('category', thisButt.dataset.category);
formData.append('reactable_id', thisButt.dataset.reactableId);
getCsrfToken()
.then(sendFetch('reaction-creation', formData))
.then((response) => {
if (response.status === 200) {
response.json().then(successCb);
} else {
showModalAfterError({
response,
element: 'reaction',
action_ing: 'updating',
action_past: 'updated',
});
}
});
};
}
butts = document.getElementsByClassName('toggle-reply-form');
for (let i = 0; i < butts.length; i++) {
const butt = butts[i];
butt.onclick = function (event) {
event.preventDefault();
const thisButt = this;
document
.getElementById(`comment-form-for-${thisButt.dataset.reactableId}`)
.classList.remove('hidden');
thisButt.classList.add('hidden');
thisButt.classList.remove('inline-flex');
setTimeout(() => {
document
.getElementById(
`comment-textarea-for-${thisButt.dataset.reactableId}`,
)
.focus();
}, 30);
};
}
}
}, 180);
}
function listenForNotificationsBellClick() {
const notificationsLink = document.getElementById('notifications-link');
if (notificationsLink) {
setTimeout(() => {
notificationsLink.onclick = function () {
document.getElementById('notifications-number').classList.add('hidden');
};
}, 180);
}
}
function initFilter() {
const notificationsFilterSelect = document.getElementById(
'notifications-filter__select',
);
const changeNotifications = (event) => {
window.location.href = event.target.value;
};
if (notificationsFilterSelect) {
notificationsFilterSelect.addEventListener('change', changeNotifications);
}
}
function initPagination() {
// paginators appear after each block of HTML notifications sent by the server
const paginators = document.getElementsByClassName('notifications-paginator');
if (paginators && paginators.length > 0) {
const paginator = paginators[paginators.length - 1];
if (paginator) {
window
.fetch(paginator.dataset.paginationPath, {
method: 'GET',
credentials: 'same-origin',
})
.then((response) => {
if (response.status === 200) {
response.text().then((html) => {
const markup = html.trim();
if (markup) {
const container = document.getElementById('articles-list');
const newNotifications = document.createElement('div');
newNotifications.innerHTML = markup;
paginator.remove();
container.append(newNotifications);
initReactions();
} else {
// no more notifications to load, we hide the load more wrapper
const button = document.getElementById('load-more-button');
if (button) {
button.style.display = 'none';
}
paginator.remove();
}
initializeSubscribeButton();
});
}
});
}
}
}
function initLoadMoreButton() {
const button = document.getElementById('load-more-button');
if (button) {
button.addEventListener('click', initPagination);
}
}
export function initializeNotifications() {
fetchNotificationsCount();
markNotificationsAsRead();
initReactions();
listenForNotificationsBellClick();
initFilter();
initPagination();
initLoadMoreButton();
}