* 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>
154 lines
4.3 KiB
JavaScript
154 lines
4.3 KiB
JavaScript
// /* global showModalAfterError*/
|
|
|
|
export function updateSubscribeButtonText(
|
|
button,
|
|
overrideSubscribed,
|
|
window_size,
|
|
) {
|
|
let label = '';
|
|
let mobileLabel = '';
|
|
if (typeof window_size == 'undefined') {
|
|
window_size = window.innerWidth;
|
|
}
|
|
|
|
let noun = 'comments';
|
|
const { subscription_id, subscription_config, comment_id } = button.dataset;
|
|
|
|
let subscriptionIsActive = subscription_id != '';
|
|
if (typeof overrideSubscribed != 'undefined') {
|
|
subscriptionIsActive = overrideSubscribed == 'subscribe';
|
|
}
|
|
|
|
const pressed = subscriptionIsActive;
|
|
const verb = subscriptionIsActive ? 'Subscribed' : 'Subscribe';
|
|
|
|
// comment_id should only be present if there's a subscription, so a button
|
|
// that initially renders as 'Subscribed-to-thread' can be a toggle until refreshed
|
|
if (comment_id && comment_id != '') {
|
|
noun = 'thread';
|
|
}
|
|
|
|
// Find the <span> element within the button
|
|
const spanElement = button.querySelector('span');
|
|
|
|
switch (subscription_config) {
|
|
case 'top_level_comments':
|
|
label = `${verb} to top-level comments`;
|
|
mobileLabel = `Top-level ${noun}`;
|
|
break;
|
|
case 'only_author_comments':
|
|
label = `${verb} to author comments`;
|
|
mobileLabel = `Author ${noun}`;
|
|
break;
|
|
default:
|
|
label = `${verb} to ${noun}`;
|
|
mobileLabel = `${noun}`.charAt(0).toUpperCase() + noun.slice(1);
|
|
}
|
|
|
|
button.setAttribute('aria-label', label);
|
|
spanElement.innerText = window_size <= 760 ? mobileLabel : label;
|
|
button.setAttribute('aria-pressed', pressed);
|
|
}
|
|
|
|
export function optimisticallyUpdateButtonUI(button, modeChange) {
|
|
if (typeof modeChange == 'undefined') {
|
|
modeChange = button.dataset.subscription_id ? 'unsubscribe' : 'subscribe';
|
|
}
|
|
|
|
if (modeChange == 'unsubscribe') {
|
|
button.classList.remove('comment-subscribed');
|
|
updateSubscribeButtonText(button, 'unsubscribe');
|
|
} else {
|
|
button.classList.add('comment-subscribed');
|
|
updateSubscribeButtonText(button, 'subscribe');
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
export function determinePayloadAndEndpoint(button) {
|
|
let payload;
|
|
let endpoint;
|
|
|
|
if (button.dataset.subscription_id != '') {
|
|
payload = {
|
|
subscription_id: button.dataset.subscription_id,
|
|
};
|
|
endpoint = 'comment-unsubscribe';
|
|
} else if (
|
|
button.dataset.subscribed_to &&
|
|
button.dataset.subscribed_to == 'comment'
|
|
) {
|
|
payload = {
|
|
comment_id: button.dataset.comment_id,
|
|
};
|
|
endpoint = 'comment-subscribe';
|
|
} else {
|
|
payload = {
|
|
article_id: button.dataset.article_id,
|
|
subscription_config: button.dataset.subscription_config,
|
|
};
|
|
endpoint = 'comment-subscribe';
|
|
}
|
|
|
|
return {
|
|
payload,
|
|
endpoint,
|
|
};
|
|
}
|
|
|
|
async function handleSubscribeButtonClick({ target }) {
|
|
optimisticallyUpdateButtonUI(target);
|
|
|
|
const { payload, endpoint } = determinePayloadAndEndpoint(target);
|
|
const requestJson = JSON.stringify(payload);
|
|
|
|
getCsrfToken()
|
|
.then(await sendFetch(endpoint, requestJson))
|
|
.then(async (response) => {
|
|
if (response.status === 200) {
|
|
const res = await response.json();
|
|
|
|
if (res.destroyed) {
|
|
const matchingButtons = document.querySelectorAll(
|
|
`button[data-subscription_id='${target.dataset.subscription_id}']`,
|
|
);
|
|
for (let i = 0; i < matchingButtons.length; i++) {
|
|
const button = matchingButtons[i];
|
|
button.dataset.subscription_id = '';
|
|
if (button != target) {
|
|
optimisticallyUpdateButtonUI(button, 'unsubscribe');
|
|
}
|
|
}
|
|
} else if (res.subscription) {
|
|
target.dataset.subscription_id = res.subscription.id;
|
|
} else {
|
|
throw `Problem (un)subscribing: ${JSON.stringify(res)}`;
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
export function initializeSubscribeButton() {
|
|
const buttons = document.querySelectorAll('.subscribe-button');
|
|
|
|
if (buttons.length === 0) {
|
|
return;
|
|
}
|
|
|
|
Array.from(buttons, (button) => {
|
|
if (button.wasInitialized) {
|
|
return;
|
|
}
|
|
|
|
button.removeEventListener('click', handleSubscribeButtonClick); // Remove previous event listener
|
|
button.addEventListener('click', handleSubscribeButtonClick);
|
|
|
|
button.wasInitialized = true;
|
|
updateSubscribeButtonText(button);
|
|
});
|
|
}
|
|
|
|
// Some subscribe buttons are added to the DOM dynamically.
|
|
// They will need to call this — see initializeNotifications > initPagination
|
|
initializeSubscribeButton();
|