* 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>
207 lines
6.3 KiB
JavaScript
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();
|
|
}
|