Rework user subscription liquid tag to avoid errors (#16460)
* move script to packs, strip ids * make sure userdata is available before determining ui * stop working around old html, fix duplicate ID errors * skip login modal * remove DEV hard coding * replace system spec with cypress spec * update base_data to include apple auth info, add to cypress tests * add initial version of DUS to resave HTML * remove data update script
This commit is contained in:
parent
24eb7d70ab
commit
3982b389f1
15 changed files with 515 additions and 545 deletions
|
|
@ -72,6 +72,15 @@ function sendFetch(switchStatement, body) {
|
|||
},
|
||||
body,
|
||||
});
|
||||
case 'user_subscriptions':
|
||||
return fetchCallback({
|
||||
url: '/user_subscriptions',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body,
|
||||
});
|
||||
default:
|
||||
console.log('A wrong switchStatement was used.'); // eslint-disable-line no-console
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -57,7 +57,8 @@ class AsyncInfoController < ApplicationController
|
|||
config_body_class: @user.config_body_class,
|
||||
feed_style: feed_style_preference,
|
||||
created_at: @user.created_at,
|
||||
admin: @user.any_admin?
|
||||
admin: @user.any_admin?,
|
||||
apple_auth: @user.email.end_with?("@privaterelay.appleid.com")
|
||||
}
|
||||
end.to_json
|
||||
end
|
||||
|
|
|
|||
303
app/javascript/liquidTags/userSubscriptionLiquidTag.js
Normal file
303
app/javascript/liquidTags/userSubscriptionLiquidTag.js
Normal file
|
|
@ -0,0 +1,303 @@
|
|||
import { getUserDataAndCsrfToken } from '@utilities/getUserDataAndCsrfToken';
|
||||
/* global userData */
|
||||
|
||||
function toggleSubscribeActionUI({
|
||||
tagContainer,
|
||||
showSubscribeAction = false,
|
||||
appleAuth = false,
|
||||
}) {
|
||||
const signedInUI = tagContainer.getElementsByClassName(
|
||||
'ltag__user-subscription-tag__signed-in',
|
||||
)[0];
|
||||
|
||||
const appleAuthUI = tagContainer.getElementsByClassName(
|
||||
'ltag__user-subscription-tag__apple-auth',
|
||||
)[0];
|
||||
|
||||
if (!showSubscribeAction) {
|
||||
appleAuthUI.classList.add('hidden');
|
||||
signedInUI.classList.add('hidden');
|
||||
return;
|
||||
}
|
||||
|
||||
// In this case we don't have an email we can use, and need the user to update their settings first
|
||||
if (appleAuth) {
|
||||
signedInUI.classList.add('hidden');
|
||||
appleAuthUI.classList.remove('hidden');
|
||||
return;
|
||||
}
|
||||
|
||||
signedInUI.classList.remove('hidden');
|
||||
}
|
||||
|
||||
function toggleSignedOutUI(tagContainer, showSignedOutUI = false) {
|
||||
const signedOutUI = tagContainer.getElementsByClassName(
|
||||
'ltag__user-subscription-tag__signed-out',
|
||||
)[0];
|
||||
if (showSignedOutUI) {
|
||||
signedOutUI.classList.remove('hidden');
|
||||
} else {
|
||||
signedOutUI.classList.add('hidden');
|
||||
}
|
||||
}
|
||||
|
||||
function updateProfileImagesUI(tagContainer, signedIn = false) {
|
||||
const profileImagesContainer = tagContainer.getElementsByClassName(
|
||||
'ltag__user-subscription-tag__profile-images',
|
||||
)[0];
|
||||
|
||||
if (signedIn) {
|
||||
profileImagesContainer.classList.add('signed-in');
|
||||
profileImagesContainer.classList.remove('signed-out');
|
||||
|
||||
tagContainer
|
||||
.getElementsByClassName(
|
||||
'ltag__user-subscription-tag__subscriber-profile-image',
|
||||
)[0]
|
||||
.classList.remove('hidden');
|
||||
} else {
|
||||
profileImagesContainer.classList.remove('signed-in');
|
||||
profileImagesContainer.classList.add('signed-out');
|
||||
|
||||
tagContainer
|
||||
.getElementsByClassName(
|
||||
'ltag__user-subscription-tag__subscriber-profile-image',
|
||||
)[0]
|
||||
.classList.add('hidden');
|
||||
}
|
||||
}
|
||||
|
||||
function initSignedOutState(tagContainer) {
|
||||
toggleSubscribeActionUI({ tagContainer, showSubscribeAction: false });
|
||||
toggleSignedOutUI(tagContainer, true);
|
||||
updateProfileImagesUI(tagContainer, false);
|
||||
}
|
||||
|
||||
function initSignedInState(tagContainer, appleAuth = false) {
|
||||
toggleSubscribeActionUI({
|
||||
tagContainer,
|
||||
showSubscribeAction: true,
|
||||
appleAuth,
|
||||
});
|
||||
toggleSignedOutUI(tagContainer, false);
|
||||
updateProfileImagesUI(tagContainer, true);
|
||||
|
||||
tagContainer
|
||||
.querySelector('.ltag__user-subscription-tag__signed-in .crayons-btn')
|
||||
.addEventListener('click', () => {
|
||||
window.Forem.showModal({
|
||||
title: 'Confirm subscribe',
|
||||
contentSelector:
|
||||
'.user-subscription-confirmation-modal .crayons-modal__box__body',
|
||||
overlay: true,
|
||||
onOpen: () => {
|
||||
// Attach listeners for cancel button and subscribe button
|
||||
document
|
||||
.querySelector(
|
||||
'#window-modal .ltag__user-subscription-tag____cancel-btn',
|
||||
)
|
||||
.addEventListener('click', window.Forem.closeModal);
|
||||
|
||||
document
|
||||
.querySelector(
|
||||
'#window-modal .ltag__user-subscription-tag__confirmation-btn',
|
||||
)
|
||||
.addEventListener('click', confirmSubscribe);
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function confirmSubscribe() {
|
||||
window.Forem.closeModal();
|
||||
clearNotices();
|
||||
|
||||
submitSubscription().then((response) => {
|
||||
if (response.success) {
|
||||
const allSubscriptionLiquidTags = document.getElementsByClassName(
|
||||
'ltag__user-subscription-tag',
|
||||
);
|
||||
|
||||
showSubscribedNotices();
|
||||
|
||||
for (const tagContainer of allSubscriptionLiquidTags) {
|
||||
// We no longer want to show the submit button since user is now subscribed
|
||||
toggleSubscribeActionUI({ tagContainer, showSubscribeAction: false });
|
||||
}
|
||||
} else {
|
||||
updateNotices({ variant: 'danger', content: response.error });
|
||||
// Allow user to retry
|
||||
toggleSubmitButtonsState({ disabled: false, textContent: 'Subscribe' });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function toggleSubmitButtonsState({ disabled, textContent }) {
|
||||
// Since all user sub tags on the same article page perform the same action, all submit buttons are kept in sync
|
||||
const allUserSubLiquidTagSubmits = document.querySelectorAll(
|
||||
'.ltag__user-subscription-tag__signed-in .crayons-btn',
|
||||
);
|
||||
|
||||
for (const submit of allUserSubLiquidTagSubmits) {
|
||||
submit.disabled = disabled;
|
||||
submit.textContent = textContent;
|
||||
}
|
||||
}
|
||||
|
||||
function updateNotices({ variant, content }) {
|
||||
const allNotices = document.getElementsByClassName(
|
||||
'ltag__user-subscription-tag__response-message',
|
||||
);
|
||||
|
||||
for (const notice of allNotices) {
|
||||
notice.classList.remove('hidden');
|
||||
notice.classList.add(`crayons-notice--${variant}`);
|
||||
notice.textContent = content;
|
||||
}
|
||||
|
||||
// When a notice is shown, we hide the generic signed-in instructions
|
||||
toggleSignedInInstructionsUI(false);
|
||||
}
|
||||
|
||||
function clearNotices() {
|
||||
// Since all user sub tags on the same article page perform the same action, all notices are kept in sync
|
||||
const allNotices = document.getElementsByClassName(
|
||||
'ltag__user-subscription-tag__response-message',
|
||||
);
|
||||
|
||||
for (const notice of allNotices) {
|
||||
notice.classList.add('hidden');
|
||||
}
|
||||
|
||||
// Re-show the signed in instructions
|
||||
toggleSignedInInstructionsUI(true);
|
||||
}
|
||||
|
||||
function toggleSignedInInstructionsUI(isVisible) {
|
||||
const signedInInstructions = document.querySelectorAll(
|
||||
'.ltag__user-subscription-tag__signed-in .ltag__user-subscription-tag__logged-in-text',
|
||||
);
|
||||
|
||||
for (const instructions of signedInInstructions) {
|
||||
if (isVisible) {
|
||||
instructions.classList.remove('hidden');
|
||||
} else {
|
||||
instructions.classList.add('hidden');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function submitSubscription() {
|
||||
toggleSubmitButtonsState({ disabled: true, textContent: 'Submitting' });
|
||||
|
||||
const articleId =
|
||||
document.getElementById('article-body')?.dataset?.articleId ?? null;
|
||||
|
||||
const subscriber = userData();
|
||||
const body = JSON.stringify({
|
||||
user_subscription: {
|
||||
source_type: 'Article',
|
||||
source_id: articleId,
|
||||
subscriber_email: subscriber.email,
|
||||
},
|
||||
});
|
||||
|
||||
return getCsrfToken()
|
||||
.then(sendFetch('user_subscriptions', body))
|
||||
.then((res) => res.json());
|
||||
}
|
||||
|
||||
function fetchSubscriptionStatus() {
|
||||
const { articleId } = document.getElementById('article-body').dataset;
|
||||
|
||||
const params = new URLSearchParams({
|
||||
source_type: 'Article',
|
||||
source_id: articleId,
|
||||
}).toString();
|
||||
|
||||
const headers = {
|
||||
Accept: 'application/json',
|
||||
'X-CSRF-Token': window.csrfToken,
|
||||
'Content-Type': 'application/json',
|
||||
};
|
||||
|
||||
return fetch(`/user_subscriptions/subscribed?${params}`, {
|
||||
method: 'GET',
|
||||
headers,
|
||||
credentials: 'same-origin',
|
||||
}).then((response) => {
|
||||
if (response.ok) {
|
||||
return response.json();
|
||||
}
|
||||
console.error(
|
||||
`Base data error: ${response.status} - ${response.statusText}`,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
function showSubscribedNotices() {
|
||||
const { authorUsername } = document.getElementsByClassName(
|
||||
'ltag__user-subscription-tag',
|
||||
)[0].dataset;
|
||||
|
||||
updateNotices({
|
||||
variant: 'success',
|
||||
content: `You are now subscribed and may receive emails from ${authorUsername}`,
|
||||
});
|
||||
}
|
||||
|
||||
function populateSubscriberProfileImage({
|
||||
profile_image_90: profileImageSrc,
|
||||
username,
|
||||
}) {
|
||||
document
|
||||
.querySelectorAll('.ltag__user-subscription-tag__subscriber-profile-image')
|
||||
.forEach((profileImage) => {
|
||||
profileImage.src = profileImageSrc;
|
||||
profileImage.alt = username;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the functionality of any user_subscription liquid tags in an article.
|
||||
*
|
||||
* An article may have more than one user_subscription liquid tag, but all instances complete the same action,
|
||||
* and are therefore kept in-step throughout any user interaction or update (i.e. the UI and behavior of all user_subscription
|
||||
* liquid tags in an article will always be the same).
|
||||
*/
|
||||
export async function initializeUserSubscriptionLiquidTagContent() {
|
||||
const allUserSubLiquidTags = document.getElementsByClassName(
|
||||
'ltag__user-subscription-tag__container',
|
||||
);
|
||||
|
||||
const { userStatus } = document.querySelector('body').dataset;
|
||||
if (userStatus === 'logged-out') {
|
||||
for (const liquidTag of allUserSubLiquidTags) {
|
||||
initSignedOutState(liquidTag);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const { currentUser: user } = await getUserDataAndCsrfToken();
|
||||
const { apple_auth: isSubscriberAuthedWithApple } = user;
|
||||
|
||||
// Setup the initial signed-in state without waiting on subscription status fetch
|
||||
for (const liquidTag of allUserSubLiquidTags) {
|
||||
initSignedInState(liquidTag, isSubscriberAuthedWithApple);
|
||||
}
|
||||
|
||||
// Check if we need to refresh UI due to existing subscription
|
||||
populateSubscriberProfileImage(user);
|
||||
fetchSubscriptionStatus().then(({ is_subscribed: isSubscribed }) => {
|
||||
if (isSubscribed) {
|
||||
showSubscribedNotices();
|
||||
}
|
||||
for (const liquidTag of allUserSubLiquidTags) {
|
||||
toggleSubscribeActionUI({
|
||||
tagContainer: liquidTag,
|
||||
showSubscribeAction: !isSubscribed,
|
||||
appleAuth: isSubscriberAuthedWithApple,
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
@ -3,6 +3,7 @@ import { Snackbar, addSnackbarItem } from '../Snackbar';
|
|||
import { addFullScreenModeControl } from '../utilities/codeFullscreenModeSwitcher';
|
||||
import { initializeDropdown } from '../utilities/dropdownUtils';
|
||||
import { embedGists } from '../utilities/gist';
|
||||
import { initializeUserSubscriptionLiquidTagContent } from '../liquidTags/userSubscriptionLiquidTag';
|
||||
|
||||
/* global Runtime */
|
||||
|
||||
|
|
@ -130,3 +131,5 @@ getCsrfToken().then(async () => {
|
|||
|
||||
const targetNode = document.querySelector('#comments');
|
||||
targetNode && embedGists(targetNode);
|
||||
|
||||
initializeUserSubscriptionLiquidTagContent();
|
||||
|
|
|
|||
|
|
@ -11,332 +11,6 @@ class UserSubscriptionTag < LiquidTagBase
|
|||
:super_admin,
|
||||
].freeze
|
||||
|
||||
def self.script
|
||||
<<~JAVASCRIPT.freeze
|
||||
var subscribeBtn = document.getElementById('subscribe-btn');
|
||||
|
||||
function isUserSignedIn() {
|
||||
return document.head.querySelector('meta[name="user-signed-in"][content="true"]') !== null;
|
||||
}
|
||||
|
||||
// Hiding/showing elements
|
||||
// If clearSubscribeButton is false, we will not clear out the subscription-signed-in area,
|
||||
// which will allow users to re-submit their subscription if they see any error messages.
|
||||
// ***************************************
|
||||
function clearSubscriptionArea({ clearSubscribeButton = true } = {}) {
|
||||
if (!clearSubscribeButton) {
|
||||
// Allow users to try submitting again if they see an error.
|
||||
hideSubscriptionSignedIn();
|
||||
}
|
||||
|
||||
const subscriptionSignedOut = document.getElementById('subscription-signed-out');
|
||||
if (subscriptionSignedOut) {
|
||||
subscriptionSignedOut.classList.add("hidden");
|
||||
}
|
||||
|
||||
hideResponseMessage();
|
||||
|
||||
const subscriberAppleAuth = document.getElementById('subscriber-apple-auth');
|
||||
if (subscriberAppleAuth) {
|
||||
subscriberAppleAuth.classList.add("hidden");
|
||||
}
|
||||
|
||||
hideConfirmationModal();
|
||||
}
|
||||
|
||||
// Hides the response message (which displays success/error messages) if it exists.
|
||||
// ***************************************
|
||||
function hideResponseMessage() {
|
||||
const responseMessage = document.getElementById('response-message');
|
||||
if (responseMessage) {
|
||||
responseMessage.classList.add("hidden");
|
||||
}
|
||||
}
|
||||
|
||||
function showSignedIn() {
|
||||
clearSubscriptionArea();
|
||||
const subscriptionSignedIn = document.getElementById('subscription-signed-in');
|
||||
if (subscriptionSignedIn) {
|
||||
subscriptionSignedIn.classList.remove("hidden");
|
||||
}
|
||||
|
||||
const profileImages = document.getElementById('profile-images');
|
||||
if (profileImages) {
|
||||
profileImages.classList.remove("signed-out");
|
||||
profileImages.classList.add("signed-in");
|
||||
}
|
||||
}
|
||||
|
||||
function showSignedOut() {
|
||||
clearSubscriptionArea();
|
||||
|
||||
const subscriptionSignedOut = document.getElementById('subscription-signed-out');
|
||||
if (subscriptionSignedOut) {
|
||||
subscriptionSignedOut.classList.remove("hidden");
|
||||
}
|
||||
|
||||
const profileImages = document.getElementById('profile-images');
|
||||
if (profileImages) {
|
||||
profileImages.classList.remove("signed-in");
|
||||
profileImages.classList.add("signed-out");
|
||||
}
|
||||
|
||||
const subscriberProfileImage = document.getElementsByClassName('ltag__user-subscription-tag__subscriber-profile-image')[0];
|
||||
if (subscriberProfileImage) {
|
||||
subscriberProfileImage.classList.add("hidden");
|
||||
}
|
||||
}
|
||||
|
||||
function showResponseMessage(noticeType, msg) {
|
||||
clearSubscriptionArea(clearSubscribeButton = false);
|
||||
|
||||
const responseMessage = document.getElementById('response-message');
|
||||
if (responseMessage) {
|
||||
responseMessage.classList.remove("hidden");
|
||||
responseMessage.classList.add(`crayons-notice--${noticeType}`);
|
||||
responseMessage.textContent = msg;
|
||||
|
||||
if (noticeType === 'danger') {
|
||||
// Allow users to try resubscribing if they see an error message.
|
||||
subscribeBtn.textContent = "#{I18n.t('liquid_tags.user_subscription_tag.submit')}";
|
||||
subscribeBtn.disabled = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function showAppleAuthMessage() {
|
||||
clearSubscriptionArea();
|
||||
const subscriber = userData();
|
||||
if (subscriber) {
|
||||
updateProfileImages('.ltag__user-subscription-tag__subscriber-profile-image', subscriber);
|
||||
}
|
||||
|
||||
const subscriberAppleAuth = document.getElementById('subscriber-apple-auth');
|
||||
if (subscriberAppleAuth) {
|
||||
subscriberAppleAuth.classList.remove("hidden");
|
||||
}
|
||||
}
|
||||
|
||||
function showSubscribed() {
|
||||
hideSubscriptionSignedIn();
|
||||
updateSubscriberData();
|
||||
const authorUsername = document.getElementById('user-subscription-tag')?.dataset.authorUsername;
|
||||
const alreadySubscribedMsg = `#{I18n.t('liquid_tags.user_subscription_tag.subscribed')}`;
|
||||
showResponseMessage('success', alreadySubscribedMsg);
|
||||
}
|
||||
|
||||
function showConfirmationModal() {
|
||||
const confirmationModal = document.getElementById('user-subscription-confirmation-modal');
|
||||
if (confirmationModal) {
|
||||
confirmationModal.classList.remove("hidden");
|
||||
}
|
||||
}
|
||||
|
||||
function hideConfirmationModal() {
|
||||
const confirmationModal = document.getElementById('user-subscription-confirmation-modal');
|
||||
if (confirmationModal) {
|
||||
confirmationModal.classList.add("hidden");
|
||||
}
|
||||
}
|
||||
|
||||
// Updating DOM elements
|
||||
// ***************************************
|
||||
function updateSubscriberData() {
|
||||
const subscriber = userData();
|
||||
if (subscriber.email) {
|
||||
updateElementsTextContent('.ltag__user-subscription-tag__subscriber-email', subscriber.email);
|
||||
}
|
||||
|
||||
updateProfileImages('.ltag__user-subscription-tag__subscriber-profile-image', subscriber);
|
||||
}
|
||||
|
||||
function updateElementsTextContent(identifier, value) {
|
||||
const elements = document.querySelectorAll(identifier);
|
||||
|
||||
elements.forEach(function(element) {
|
||||
element.textContent = value;
|
||||
});
|
||||
}
|
||||
|
||||
function updateProfileImages(identifier, subscriber) {
|
||||
const profileImages = document.querySelectorAll(`img${identifier}`);
|
||||
|
||||
profileImages.forEach(function(profileImage) {
|
||||
profileImage.src = subscriber.profile_image_90;
|
||||
profileImage.alt = `${subscriber.username} profile image`;
|
||||
});
|
||||
}
|
||||
|
||||
function hideSubscriptionSignedIn() {
|
||||
const subscriptionSignedIn = document.getElementById('subscription-signed-in');
|
||||
if (subscriptionSignedIn) {
|
||||
subscriptionSignedIn.classList.add("hidden");
|
||||
}
|
||||
}
|
||||
|
||||
// Adding event listeners for 'click'
|
||||
// ***************************************
|
||||
function addSignInClickHandler() {
|
||||
const signInBtn = document.getElementById('sign-in-btn');
|
||||
if (signInBtn) {
|
||||
signInBtn.addEventListener('click', function(e) {
|
||||
if (typeof showLoginModal !== 'undefined') {
|
||||
showLoginModal();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function addConfirmationModalClickHandlers() {
|
||||
if (subscribeBtn) {
|
||||
subscribeBtn.addEventListener('click', function(e) {
|
||||
showConfirmationModal();
|
||||
});
|
||||
}
|
||||
|
||||
const cancelBtn = document.getElementById('cancel-btn');
|
||||
if (cancelBtn) {
|
||||
cancelBtn.addEventListener('click', function(e) {
|
||||
hideConfirmationModal();
|
||||
});
|
||||
}
|
||||
|
||||
const closeConfirmationModal = document.getElementById('close-confirmation-modal');
|
||||
if (closeConfirmationModal) {
|
||||
closeConfirmationModal.addEventListener('click', function(e) {
|
||||
hideConfirmationModal();
|
||||
});
|
||||
}
|
||||
|
||||
const confirmationModal = document.getElementById('confirmation-btn')
|
||||
if (confirmationModal) {
|
||||
confirmationModal.addEventListener('click', function(e) {
|
||||
handleSubscription();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// API calls
|
||||
// ***************************************
|
||||
function submitSubscription() {
|
||||
// Hide any error messages previously rendered.
|
||||
hideResponseMessage();
|
||||
|
||||
subscribeBtn.textContent = "#{I18n.t('liquid_tags.user_subscription_tag.submitting')}";
|
||||
subscribeBtn.disabled = true;
|
||||
|
||||
const headers = {
|
||||
Accept: 'application/json',
|
||||
'X-CSRF-Token': window.csrfToken,
|
||||
'Content-Type': 'application/json',
|
||||
}
|
||||
|
||||
const articleBody = document.getElementById('article-body');
|
||||
const articleId = (articleBody ? articleBody.dataset.articleId : null);
|
||||
|
||||
const subscriber = userData();
|
||||
const body = JSON.stringify(
|
||||
{
|
||||
user_subscription: {
|
||||
source_type: 'Article',
|
||||
source_id: articleId,
|
||||
subscriber_email: subscriber.email
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
return fetch('/user_subscriptions', {
|
||||
method: 'POST',
|
||||
headers: headers,
|
||||
credentials: 'same-origin',
|
||||
body: body,
|
||||
}).then(function(response) {
|
||||
return response.json();
|
||||
});
|
||||
}
|
||||
|
||||
function fetchIsSubscribed() {
|
||||
const articleBody = document.getElementById('article-body');
|
||||
const articleId = (articleBody ? articleBody.dataset.articleId : null);
|
||||
|
||||
const params = new URLSearchParams({
|
||||
source_type: 'Article',
|
||||
source_id: articleId
|
||||
}).toString();
|
||||
|
||||
const headers = {
|
||||
Accept: 'application/json',
|
||||
'X-CSRF-Token': window.csrfToken,
|
||||
'Content-Type': 'application/json',
|
||||
}
|
||||
|
||||
return fetch(`/user_subscriptions/subscribed?${params}`, {
|
||||
method: 'GET',
|
||||
headers: headers,
|
||||
credentials: 'same-origin',
|
||||
}).then(function(response) {
|
||||
if (response.ok) {
|
||||
return response.json();
|
||||
} else {
|
||||
console.error(`Base data error: ${response.status} - ${response.statusText}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Handle API responses
|
||||
// ***************************************
|
||||
function handleSubscription() {
|
||||
hideConfirmationModal(); // Close the modal once the user has confirmed.
|
||||
|
||||
submitSubscription().then(function(response) {
|
||||
if (response.success) {
|
||||
const userSubscriptionTag = document.getElementById('user-subscription-tag');
|
||||
const authorUsername = (userSubscriptionTag ? userSubscriptionTag.dataset.authorUsername : null);
|
||||
// TODO: [yheuhtozr] currently no way of i18n via Ruby
|
||||
const successMsg = `You are now subscribed and may receive emails from ${authorUsername}`;
|
||||
showResponseMessage('success', successMsg);
|
||||
hideSubscriptionSignedIn();
|
||||
} else {
|
||||
showResponseMessage('danger', response.error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function checkIfSubscribed() {
|
||||
fetchIsSubscribed().then(function(response) {
|
||||
const subscriber = userData();
|
||||
const isSubscriberAuthedWithApple = (subscriber.email ? subscriber.email.endsWith('@privaterelay.appleid.com') : false);
|
||||
|
||||
if (response.is_subscribed) {
|
||||
showSubscribed();
|
||||
} else if (isSubscriberAuthedWithApple) {
|
||||
showAppleAuthMessage();
|
||||
} else {
|
||||
updateSubscriberData();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// We load this JS on every Article. This is to only run it on Articles
|
||||
// where the UserSubscription liquid tag is present
|
||||
if (document.getElementById('user-subscription-tag')) {
|
||||
// The markup defaults to signed out UX
|
||||
if (isUserSignedIn()) {
|
||||
showSignedIn();
|
||||
addConfirmationModalClickHandlers();
|
||||
|
||||
// We need access to some DOM elements (i.e. csrf token, article id, userData, etc.)
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
checkIfSubscribed();
|
||||
});
|
||||
} else {
|
||||
showSignedOut();
|
||||
addSignInClickHandler();
|
||||
}
|
||||
}
|
||||
JAVASCRIPT
|
||||
end
|
||||
|
||||
def initialize(_tag_name, cta_text, parse_context)
|
||||
super
|
||||
@cta_text = cta_text.strip
|
||||
|
|
@ -350,7 +24,8 @@ class UserSubscriptionTag < LiquidTagBase
|
|||
locals: {
|
||||
cta_text: @cta_text,
|
||||
author_profile_image: @user&.profile_image_90,
|
||||
author_username: @user&.username
|
||||
author_username: @user&.username,
|
||||
community_name: Settings::Community.community_name
|
||||
},
|
||||
)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -227,6 +227,5 @@
|
|||
<%== PollTag.script %>
|
||||
<%== RunkitTag.script %>
|
||||
<%== TweetTag.script %>
|
||||
<%== UserSubscriptionTag.script %>
|
||||
</script>
|
||||
<% end %>
|
||||
|
|
|
|||
|
|
@ -1,81 +1,66 @@
|
|||
<div class="ltag__user-subscription-tag" id="user-subscription-tag" data-author-username=<%= author_username %>>
|
||||
<div class="ltag__user-subscription-tag" data-author-username=<%= author_username %>>
|
||||
<div class="ltag__user-subscription-tag__container">
|
||||
|
||||
<div class="ltag__user-subscription-tag__content w-100" id="content">
|
||||
<div class="ltag__user-subscription-tag__content w-100">
|
||||
|
||||
<div class="ltag__user-subscription-tag__profile-images signed-out" id="profile-images">
|
||||
<div class="ltag__user-subscription-tag__profile-images signed-out">
|
||||
|
||||
<span class="crayons-avatar crayons-avatar--xl ltag__user-subscription-tag__author-profile-image">
|
||||
<img class="crayons-avatar__image ltag__user-subscription-tag__author-profile-image" src=<%= author_profile_image %> <%= "#{author_username} profile image" %>>
|
||||
<span class="crayons-avatar crayons-avatar--xl ltag__user-subscription-tag__author-profile-image m-auto">
|
||||
<img class="crayons-avatar__image ltag__user-subscription-tag__author-profile-image m-0" src=<%= author_profile_image %> <%= author_username %> <%= author_username %>>
|
||||
</span>
|
||||
|
||||
<span class="crayons-avatar crayons-avatar--xl ltag__user-subscription-tag__subscriber-profile-image">
|
||||
<img class="crayons-avatar__image ltag__user-subscription-tag__subscriber-profile-image">
|
||||
<span class="crayons-avatar crayons-avatar--xl ltag__user-subscription-tag__subscriber-profile-image m-auto">
|
||||
<img class="crayons-avatar__image ltag__user-subscription-tag__subscriber-profile-image m-0" alt="">
|
||||
</span>
|
||||
|
||||
</div>
|
||||
|
||||
<h1 class="ltag__user-subscription-tag__cta-text fs-xl mt-0 mb-4 align-center" id="user-subscription-cta-text">
|
||||
<h2 class="ltag__user-subscription-tag__cta-text fs-xl mt-0 mb-4 align-center">
|
||||
<%= cta_text %>
|
||||
</h1>
|
||||
</h2>
|
||||
|
||||
<div class="ltag__user-subscription-tag__subscription-area align-center" id="subscription-area">
|
||||
<div class="ltag__user-subscription-tag__signed-out" id="subscription-signed-out">
|
||||
<div class="fs-base mb-2" id="logged-out-text">
|
||||
<%= t("views.liquids.user_subscription.logged_out") %>
|
||||
<div class="ltag__user-subscription-tag__subscription-area align-center">
|
||||
<div class="ltag__user-subscription-tag__signed-out">
|
||||
<div class="fs-base mb-2">
|
||||
<%= t("views.liquids.user_subscription.logged_out", community: community_name) %>
|
||||
</div>
|
||||
<button class="crayons-btn" id="sign-in-btn">
|
||||
<a href="/enter" class="c-cta c-cta--default">
|
||||
<%= t("views.liquids.user_subscription.sign_in") %>
|
||||
</button>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="ltag__user-subscription-tag__signed-in hidden" id="subscription-signed-in">
|
||||
<button class="crayons-btn mb-4" id="subscribe-btn">
|
||||
<div class="ltag__user-subscription-tag__signed-in hidden">
|
||||
<button class="crayons-btn mb-4">
|
||||
<%= t("views.liquids.user_subscription.subscribe.button") %>
|
||||
</button>
|
||||
<div class="fs-s mb-3" id="logged-in-text">
|
||||
<%= t("views.liquids.user_subscription.subscribe.desc_html", email: tag.span(class: "ltag__user-subscription-tag__subscriber-email"), update: link_to(t("views.liquids.user_subscription.subscribe.update"), "/settings")) %>
|
||||
<div class="ltag__user-subscription-tag__logged-in-text fs-s mb-3">
|
||||
<%= t("views.liquids.user_subscription.subscribe.desc_html", community: community_name, update: link_to(t("views.liquids.user_subscription.subscribe.update"), "/settings")) %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ltag__user-subscription-tag__apple-auth fs-s hidden" id="subscriber-apple-auth">
|
||||
<button disabled class="crayons-btn" id="apple-subscribe-btn"><%= t("views.liquids.user_subscription.subscribe.apple.button") %></button>
|
||||
<div class="fs-s" id="apple-auth-message">
|
||||
<%= t("views.liquids.user_subscription.subscribe.apple.desc_html", update: link_to(t("views.liquids.user_subscription.subscribe.apple.update"), "/settings")) %>
|
||||
<div class="ltag__user-subscription-tag__apple-auth fs-s hidden">
|
||||
<button disabled class="crayons-btn"><%= t("views.liquids.user_subscription.subscribe.apple.button") %></button>
|
||||
<div class="fs-s">
|
||||
<%= t("views.liquids.user_subscription.subscribe.apple.desc_html", community: community_name, update: link_to(t("views.liquids.user_subscription.subscribe.apple.update"), "/settings")) %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ltag__user-subscription-tag__response-message crayons-notice fs-base w-100 hidden" aria-live="polite" id="response-message"></div>
|
||||
<div class="ltag__user-subscription-tag__response-message crayons-notice fs-base w-100 hidden" aria-live="polite"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="user-subscription-confirmation-modal" class="hidden">
|
||||
<div class="crayons-modal crayons-modal--small">
|
||||
<div class="crayons-modal__box">
|
||||
<header class="crayons-modal__box__header">
|
||||
<h2 class="fs-l fw-bold mt-0 mb-0">
|
||||
<%= t("views.liquids.user_subscription.confirm.subtitle") %>
|
||||
</h2>
|
||||
<button type="button" class="crayons-btn crayons-btn--icon-rounded crayons-btn--ghost" id="close-confirmation-modal">
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" class="crayons-icon" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M12 10.586l4.95-4.95 1.414 1.414-4.95 4.95 4.95 4.95-1.414 1.414-4.95-4.95-4.95 4.95-1.414-1.414 4.95-4.95-4.95-4.95L7.05 5.636l4.95 4.95z" />
|
||||
</svg>
|
||||
</button>
|
||||
</header>
|
||||
<div class="crayons-modal__box__body">
|
||||
<p id="confirmation-text" class="fs-base mb-4 mt-0">
|
||||
<%= t("views.liquids.user_subscription.confirm.text_html", email: tag.span(class: "ltag__user-subscription-tag__subscriber-email"), username: tag.span(author_username, class: "ltag__user-subscription-tag__author-username fw-medium")) %>
|
||||
<p>
|
||||
<div class="ltag__user-subscription-tag__confirmation-buttons">
|
||||
<button class="crayons-btn mr-1" id="confirmation-btn">
|
||||
<%= t("views.liquids.user_subscription.confirm.button_text") %>
|
||||
</button>
|
||||
<button class="crayons-btn crayons-btn--secondary" id="cancel-btn">
|
||||
<%= t("views.liquids.user_subscription.confirm.cancel_text") %>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="user-subscription-confirmation-modal hidden">
|
||||
<div class="crayons-modal__box__body">
|
||||
<p class="fs-base mb-4 mt-0">
|
||||
<%= t("views.liquids.user_subscription.confirm.text_html", community: community_name, username: tag.span(author_username, class: "ltag__user-subscription-tag__author-username fw-medium")) %>
|
||||
<p>
|
||||
<div class="ltag__user-subscription-tag__confirmation-buttons">
|
||||
<button class="ltag__user-subscription-tag__confirmation-btn crayons-btn mr-1">
|
||||
<%= t("views.liquids.user_subscription.confirm.button_text") %>
|
||||
</button>
|
||||
<button class="ltag__user-subscription-tag____cancel-btn crayons-btn crayons-btn--secondary">
|
||||
<%= t("views.liquids.user_subscription.confirm.cancel_text") %>
|
||||
</button>
|
||||
</div>
|
||||
<div class="crayons-modal__overlay"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -28,16 +28,16 @@ en:
|
|||
subtitle: Confirm subscribe
|
||||
button_text: Confirm subscription
|
||||
cancel_text: Cancel
|
||||
text_html: You'll share your email address%{email}, username, name, and DEV profile URL with %{username}. Once you do this, you cannot undo this.
|
||||
logged_out: You must first sign in to DEV.
|
||||
text_html: "You'll share your email address, username, name, and %{community} profile URL with %{username}. Once you do this, you cannot undo this."
|
||||
logged_out: "You must first sign in to %{community}."
|
||||
sign_in: Sign In
|
||||
subscribe:
|
||||
apple:
|
||||
button: Subscribe
|
||||
desc_html: Hey, there! It looks like when you created your DEV account you signed up with Apple using a private relay email address. If you'd like to subscribe, please %{update} first to a different email address.
|
||||
desc_html: "Hey, there! It looks like when you created your %{community} account you signed up with Apple using a private relay email address. If you'd like to subscribe, please %{update} first to a different email address."
|
||||
update: update your email address in Settings
|
||||
button: Subscribe
|
||||
desc_html: You'll subscribe with %{email} the email address associated with your DEV account. To use a different email address, you can %{update}.
|
||||
desc_html: "You'll subscribe with the email address associated with your %{community} account. To use a different email address, you can %{update}."
|
||||
update: update your email address in Settings
|
||||
wikipedia:
|
||||
logo: Wikipedia Logo
|
||||
|
|
|
|||
|
|
@ -28,16 +28,16 @@ fr:
|
|||
subtitle: Confirm subscribe
|
||||
button_text: Confirm subscription
|
||||
cancel_text: Cancel
|
||||
text_html: You'll share your email address%{email}, username, name, and DEV profile URL with %{username}. Once you do this, you cannot undo this.
|
||||
logged_out: You must first sign in to DEV.
|
||||
text_html: "You'll share your email address, username, name, and %{community} profile URL with %{username}. Once you do this, you cannot undo this."
|
||||
logged_out: "You must first sign in to %{community}."
|
||||
sign_in: Sign In
|
||||
subscribe:
|
||||
apple:
|
||||
button: Subscribe
|
||||
desc_html: Hey, there! It looks like when you created your DEV account you signed up with Apple using a private relay email address. If you'd like to subscribe, please %{update} first to a different email address.
|
||||
desc_html: "Hey, there! It looks like when you created your %{community} account you signed up with Apple using a private relay email address. If you'd like to subscribe, please %{update} first to a different email address."
|
||||
update: update your email address in Settings
|
||||
button: Subscribe
|
||||
desc_html: You'll subscribe with %{email} the email address associated with your DEV account. To use a different email address, you can %{update}.
|
||||
desc_html: "You'll subscribe with the email address associated with your %{community} account. To use a different email address, you can %{update}."
|
||||
update: update your email address in Settings
|
||||
wikipedia:
|
||||
logo: Wikipedia Logo
|
||||
|
|
|
|||
5
cypress/fixtures/users/appleAuthAdminUser.json
Normal file
5
cypress/fixtures/users/appleAuthAdminUser.json
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"username": "apple_auth_admin_user",
|
||||
"email": "apple-auth-admin-user@privaterelay.appleid.com",
|
||||
"password": "password"
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
describe('User subscription liquid tag - apple auth email', () => {
|
||||
beforeEach(() => {
|
||||
cy.testSetup();
|
||||
|
||||
cy.fixture('users/appleAuthAdminUser.json').as('user');
|
||||
|
||||
cy.get('@user').then((user) => {
|
||||
cy.loginUser(user).then(() => {
|
||||
cy.createArticle({
|
||||
title: 'User subscription liquid tag article',
|
||||
tags: ['beginner', 'ruby', 'go'],
|
||||
content: `{% user_subscription CTA text for first tag %}\nSome text\n{% user_subscription CTA text for second tag %}`,
|
||||
published: true,
|
||||
}).then((response) => {
|
||||
cy.visitAndWaitForUserSideEffects(response.body.current_state_path);
|
||||
// Wait for page to load
|
||||
cy.findByRole('heading', {
|
||||
name: 'User subscription liquid tag article',
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('informs user they must change email address to subscribe', () => {
|
||||
// Check both liquid tags are shown
|
||||
cy.findByRole('heading', { name: 'CTA text for first tag' });
|
||||
cy.findByRole('heading', { name: 'CTA text for second tag' });
|
||||
|
||||
// User should not be able to subscribe
|
||||
cy.findAllByRole('button', { name: 'Subscribe' })
|
||||
.last()
|
||||
.should('have.attr', 'disabled');
|
||||
|
||||
cy.findAllByRole('link', {
|
||||
name: 'update your email address in Settings',
|
||||
}).should('have.length', 2);
|
||||
|
||||
cy.findAllByText(
|
||||
/you signed up with Apple using a private relay email/,
|
||||
).should('have.length', 2);
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
describe('User subscription liquid tag - subscribable email', () => {
|
||||
beforeEach(() => {
|
||||
cy.testSetup();
|
||||
|
||||
cy.fixture('users/adminUser.json').as('user');
|
||||
|
||||
cy.get('@user').then((user) => {
|
||||
cy.loginUser(user).then(() => {
|
||||
cy.createArticle({
|
||||
title: 'User subscription liquid tag article',
|
||||
tags: ['beginner', 'ruby', 'go'],
|
||||
content: `{% user_subscription CTA text for first tag %}\nSome text\n{% user_subscription CTA text for second tag %}`,
|
||||
published: true,
|
||||
}).then((response) => {
|
||||
cy.visitAndWaitForUserSideEffects(response.body.current_state_path);
|
||||
// Wait for page to load
|
||||
cy.findByRole('heading', {
|
||||
name: 'User subscription liquid tag article',
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('shows the signed in UI and subscribes', () => {
|
||||
cy.findByRole('heading', { name: 'CTA text for first tag' });
|
||||
cy.findByRole('heading', { name: 'CTA text for second tag' });
|
||||
cy.findAllByRole('button', { name: 'Subscribe' }).should('have.length', 2);
|
||||
cy.findAllByRole('link', {
|
||||
name: 'update your email address in Settings',
|
||||
}).should('have.length', 2);
|
||||
|
||||
cy.findAllByRole('button', { name: 'Subscribe' }).last().click();
|
||||
cy.getModal()
|
||||
.findByRole('button', { name: 'Confirm subscription' })
|
||||
.click();
|
||||
|
||||
cy.findAllByText(
|
||||
'You are now subscribed and may receive emails from admin_mcadmin',
|
||||
).should('have.length', 2);
|
||||
cy.findByRole('button', { name: 'Subscribe' }).should('not.exist');
|
||||
});
|
||||
|
||||
it('cancels subscription action', () => {
|
||||
cy.findAllByRole('button', { name: 'Subscribe' }).last().click();
|
||||
cy.getModal().findByRole('button', { name: 'Cancel' }).click();
|
||||
|
||||
cy.findAllByRole('button', { name: 'Subscribe' }).should('have.length', 2);
|
||||
cy.findByText(
|
||||
'You are now subscribed and may receive emails from admin_mcadmin',
|
||||
).should('not.exist');
|
||||
});
|
||||
|
||||
it('shows any error message and allows user to retry', () => {
|
||||
cy.intercept('POST', '/user_subscriptions', {
|
||||
error: 'This is an error message',
|
||||
});
|
||||
|
||||
cy.findAllByRole('button', { name: 'Subscribe' }).last().click();
|
||||
cy.getModal()
|
||||
.findByRole('button', { name: 'Confirm subscription' })
|
||||
.click();
|
||||
|
||||
cy.findAllByText('This is an error message').should('have.length', 2);
|
||||
});
|
||||
|
||||
it('shows the signed out UI', () => {
|
||||
cy.url().then(() => {
|
||||
cy.signOutUser();
|
||||
cy.findAllByRole('link', {
|
||||
name: 'User subscription liquid tag article',
|
||||
})
|
||||
.first()
|
||||
.click({ force: true });
|
||||
|
||||
cy.findAllByRole('link', { name: 'Sign In' }).should('have.length', 2);
|
||||
cy.findAllByText('You must first sign in to DEV(local).').should(
|
||||
'have.length',
|
||||
2,
|
||||
);
|
||||
cy.findByRole('button', { name: 'Subscribe' }).should('not.exist');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -213,6 +213,26 @@ end
|
|||
|
||||
##############################################################################
|
||||
|
||||
seeder.create_if_doesnt_exist(User, "email", "apple-auth-admin-user@privaterelay.appleid.com") do
|
||||
user = User.create!(
|
||||
name: "Apple Auth Admin User",
|
||||
email: "apple-auth-admin-user@privaterelay.appleid.com",
|
||||
username: "apple_auth_admin_user",
|
||||
profile_image: File.open(Rails.root.join("app/assets/images/#{rand(1..40)}.png")),
|
||||
confirmed_at: Time.current,
|
||||
registered_at: Time.current,
|
||||
password: "password",
|
||||
password_confirmation: "password",
|
||||
saw_onboarding: true,
|
||||
checked_code_of_conduct: true,
|
||||
checked_terms_and_conditions: true,
|
||||
)
|
||||
|
||||
user.add_role(:super_admin)
|
||||
end
|
||||
|
||||
##############################################################################
|
||||
|
||||
seeder.create_if_doesnt_exist(User, "email", "notifications-user@forem.local") do
|
||||
user = User.create!(
|
||||
name: "Notifications User \\:/",
|
||||
|
|
|
|||
|
|
@ -1,157 +0,0 @@
|
|||
require "rails_helper"
|
||||
|
||||
RSpec.describe UserSubscriptionTag, type: :system, js: true do
|
||||
let(:subscriber) { create(:user) }
|
||||
let(:author) { create(:user) }
|
||||
let(:article_with_user_subscription_tag) do
|
||||
create(:article, :with_user_subscription_tag_role_user, with_user_subscription_tag: true)
|
||||
end
|
||||
|
||||
before do
|
||||
Liquid::Template.register_tag("user_subscription", described_class)
|
||||
end
|
||||
|
||||
context "when viewing the default view" do
|
||||
before do
|
||||
# Stub roles because adding them normally can cause flaky specs
|
||||
allow(author).to receive(:has_role?).and_call_original
|
||||
allow(author).to receive(:has_role?).with(:restricted_liquid_tag,
|
||||
LiquidTags::UserSubscriptionTag).and_return(true)
|
||||
end
|
||||
|
||||
it "displays signed out view by default" do
|
||||
sign_in author
|
||||
visit "/new" # Preview behaves differently - we don't load liquid tag scripts
|
||||
fill_in "article_body_markdown", with: "{% user_subscription Some sweet CTA text %}"
|
||||
page.execute_script("window.scrollTo(0, -100000)")
|
||||
find("button", text: /\APreview\z/).click
|
||||
|
||||
expect(page).to have_css("#subscription-signed-in", visible: :hidden)
|
||||
expect(page).to have_css("#subscriber-apple-auth", visible: :hidden)
|
||||
expect(page).to have_css("#response-message", visible: :hidden)
|
||||
expect(page).to have_css("#subscription-signed-out", visible: :visible)
|
||||
css_class = "img.ltag__user-subscription-tag__author-profile-image[src='#{author.profile_image_90}']"
|
||||
expect(page).to have_css(css_class)
|
||||
end
|
||||
end
|
||||
|
||||
context "when signed in" do
|
||||
before do
|
||||
sign_in subscriber
|
||||
visit article_with_user_subscription_tag.path
|
||||
end
|
||||
|
||||
it "shows the signed in UX" do
|
||||
expect(page).to have_css("#subscription-signed-out", visible: :hidden)
|
||||
expect(page).to have_css("#subscriber-apple-auth", visible: :hidden)
|
||||
expect(page).to have_css("#response-message", visible: :hidden)
|
||||
expect(page).to have_css("#subscription-signed-in", visible: :visible)
|
||||
css_class = "img.ltag__user-subscription-tag__subscriber-profile-image[src='#{subscriber.profile_image_90}']"
|
||||
expect(page).to have_css(css_class)
|
||||
end
|
||||
|
||||
it "asks the user to confirm their subscription" do
|
||||
expect(page).to have_css("#user-subscription-confirmation-modal", visible: :hidden)
|
||||
click_button("Subscribe", id: "subscribe-btn")
|
||||
expect(page).to have_css("#user-subscription-confirmation-modal", visible: :visible)
|
||||
end
|
||||
|
||||
it "displays a success message when a subscription is created" do
|
||||
expect(page).to have_css("#subscription-signed-out", visible: :hidden)
|
||||
expect(page).to have_css("#subscriber-apple-auth", visible: :hidden)
|
||||
expect(page).to have_css("#response-message", visible: :hidden)
|
||||
expect(page).to have_css("#subscription-signed-in", visible: :visible)
|
||||
click_button("Subscribe", id: "subscribe-btn")
|
||||
click_button("Confirm subscription", id: "confirmation-btn")
|
||||
expect(page).to have_css("#subscription-signed-in", visible: :hidden)
|
||||
expect(page).to have_css("#response-message.crayons-notice--success", visible: :visible)
|
||||
|
||||
within "#response-message" do
|
||||
expect(page).to have_text("You are now subscribed")
|
||||
end
|
||||
end
|
||||
|
||||
# rubocop:disable RSpec/ExampleLength
|
||||
it "displays errors when there's an error creating a subscription" do
|
||||
# Create a subscription so it causes an error by already being subscribed
|
||||
create(
|
||||
:user_subscription,
|
||||
subscriber_id: subscriber.id, subscriber_email: subscriber.email,
|
||||
author_id: article_with_user_subscription_tag.user_id,
|
||||
user_subscription_sourceable: article_with_user_subscription_tag
|
||||
)
|
||||
|
||||
expect(page).to have_css("#subscription-signed-out", visible: :hidden)
|
||||
expect(page).to have_css("#subscriber-apple-auth", visible: :hidden)
|
||||
expect(page).to have_css("#response-message", visible: :hidden)
|
||||
expect(page).to have_css("#subscription-signed-in", visible: :visible)
|
||||
click_button("Subscribe", id: "subscribe-btn")
|
||||
click_button("Confirm subscription", id: "confirmation-btn")
|
||||
|
||||
# User should be able see the error and should be able to resubscribe.
|
||||
expect(page).to have_css("#subscription-signed-in", visible: :visible)
|
||||
expect(page).to have_css("#response-message.crayons-notice--danger", visible: :visible)
|
||||
|
||||
within "#response-message" do
|
||||
expect(page).to have_text("Subscriber has already been taken")
|
||||
end
|
||||
end
|
||||
# rubocop:enable RSpec/ExampleLength
|
||||
|
||||
it "tells the user they're already subscribed by default if they're already subscribed" do
|
||||
create(
|
||||
:user_subscription,
|
||||
subscriber_id: subscriber.id, subscriber_email: subscriber.email,
|
||||
author_id: article_with_user_subscription_tag.user_id,
|
||||
user_subscription_sourceable: article_with_user_subscription_tag
|
||||
)
|
||||
|
||||
visit article_with_user_subscription_tag.path
|
||||
|
||||
expect(page).to have_css("#subscription-signed-out", visible: :hidden)
|
||||
expect(page).to have_css("#subscription-signed-in", visible: :hidden)
|
||||
expect(page).to have_css("#subscriber-apple-auth", visible: :hidden)
|
||||
expect(page).to have_css("#response-message.crayons-notice--success", visible: :visible)
|
||||
|
||||
within "#response-message" do
|
||||
expect(page).to have_text("You are already subscribed.")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "when signed out" do
|
||||
before { visit article_with_user_subscription_tag.path }
|
||||
|
||||
it "prompts a user to sign in when they're signed out", type: :system, js: true do
|
||||
expect(page).to have_css("#subscription-signed-in", visible: :hidden)
|
||||
expect(page).to have_css("#response-message", visible: :hidden)
|
||||
expect(page).to have_css("#subscriber-apple-auth", visible: :hidden)
|
||||
expect(page).to have_css("#subscription-signed-out", visible: :visible)
|
||||
end
|
||||
|
||||
it "allows a user to sign in" do
|
||||
expect(page).not_to have_text("Log in to continue")
|
||||
click_button("Sign In", id: "sign-in-btn")
|
||||
expect(page).to have_text("Log in to continue")
|
||||
end
|
||||
end
|
||||
|
||||
# TODO: [@forem/delightful]: re-enable this once email confirmation
|
||||
# is re-enabled and confirm it isn't flaky.
|
||||
xcontext "when a user has an Apple private relay email address" do
|
||||
it "prompts the user to update their email address" do
|
||||
allow(subscriber).to receive(:email).and_return("test@privaterelay.appleid.com")
|
||||
sign_in subscriber
|
||||
visit article_with_user_subscription_tag.path
|
||||
|
||||
expect(page).to have_css("#subscription-signed-out", visible: :hidden)
|
||||
expect(page).to have_css("#subscription-signed-in", visible: :visible)
|
||||
expect(page).to have_css("#response-message", visible: :hidden)
|
||||
expect(page).to have_css("#subscriber-apple-auth", visible: :hidden)
|
||||
|
||||
within "#subscriber-apple-auth" do
|
||||
expect(page).to have_button("Subscribe", disabled: true, visible: :visible)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
Loading…
Add table
Reference in a new issue