unsubscribe/change email confirmation fixes
This commit is contained in:
parent
eab17b3340
commit
7b665325f7
|
|
@ -8,6 +8,7 @@ describe('Change Email Confirm Page', () => {
|
||||||
email: 'sam@example.com'
|
email: 'sam@example.com'
|
||||||
});
|
});
|
||||||
cy.stubAuthenticate({ ...currentUser });
|
cy.stubAuthenticate({ ...currentUser });
|
||||||
|
cy.intercept('POST', /\S+\/update_email/, { statusCode: 200, body: { ok: true } });
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should display the confirm page when visiting the confirm URL', () => {
|
it('should display the confirm page when visiting the confirm URL', () => {
|
||||||
|
|
@ -15,7 +16,7 @@ describe('Change Email Confirm Page', () => {
|
||||||
const token = 'dummy-confirm-token';
|
const token = 'dummy-confirm-token';
|
||||||
|
|
||||||
// Visit the confirm URL
|
// Visit the confirm URL
|
||||||
cy.visit(`/public/confirm-email-change?token=${token}`);
|
cy.visit(`/confirm-email-change?token=${token}`);
|
||||||
|
|
||||||
// Assert that the JKChangeEmailConfirm page is rendered
|
// Assert that the JKChangeEmailConfirm page is rendered
|
||||||
// Adjust selectors/texts as per your actual component
|
// Adjust selectors/texts as per your actual component
|
||||||
|
|
|
||||||
|
|
@ -1,24 +1,31 @@
|
||||||
/// <reference types="cypress" />
|
/// <reference types="cypress" />
|
||||||
|
|
||||||
|
import makeFakeUser from '../../factories/user';
|
||||||
|
|
||||||
describe('Unsubscribe from email link', () => {
|
describe('Unsubscribe from email link', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
// cy.intercept('POST', /\S+\/unsubscribe_user_match\/\S+/, { statusCode: 200, body: { ok: true } });
|
// cy.intercept('POST', /\S+\/unsubscribe_user_match\/\S+/, { statusCode: 200, body: { ok: true } });
|
||||||
|
const currentUser = makeFakeUser({
|
||||||
|
email: 'sam@example.com'
|
||||||
|
});
|
||||||
|
cy.stubAuthenticate({ ...currentUser });
|
||||||
cy.intercept('POST', /\S+\/unsubscribe\/\S+/, { statusCode: 200, body: { ok: true } });
|
cy.intercept('POST', /\S+\/unsubscribe\/\S+/, { statusCode: 200, body: { ok: true } });
|
||||||
})
|
})
|
||||||
|
|
||||||
it("redirect to home page if tok is not provided", () => {
|
it("redirect to home page if tok is not provided", () => {
|
||||||
cy.visit('/public/unsubscribe');
|
cy.visit('/unsubscribe');
|
||||||
cy.location('pathname').should('eq', '/errors/404');
|
cy.location('pathname').should('eq', '/errors/404');
|
||||||
});
|
});
|
||||||
|
|
||||||
it("show unsubscribed message", () => {
|
it("show unsubscribed message", () => {
|
||||||
cy.visit('/public/unsubscribe/123');
|
cy.visit('/unsubscribe/123');
|
||||||
// cy.location('search')
|
// cy.location('search')
|
||||||
// .should('equal', '?tok=123')
|
// .should('equal', '?tok=123')
|
||||||
// .then((s) => new URLSearchParams(s))
|
// .then((s) => new URLSearchParams(s))
|
||||||
// .invoke('get', 'tok')
|
// .invoke('get', 'tok')
|
||||||
// .should('equal', '123')
|
// .should('equal', '123')
|
||||||
cy.contains("Unsubscribe from JamKazam emails")
|
cy.contains("You have successfully unsubscribed from JamKazam emails.").should('be.visible');
|
||||||
|
cy.contains("Loading...").should('not.exist');
|
||||||
});
|
});
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
@ -55,6 +55,9 @@ import JKMyJamTracks from '../jamtracks/JKMyJamTracks';
|
||||||
import JKJamTrackShow from '../jamtracks/JKJamTrackShow';
|
import JKJamTrackShow from '../jamtracks/JKJamTrackShow';
|
||||||
import JKPayPalConfirmation from '../shopping-cart/JKPayPalConfirmation';
|
import JKPayPalConfirmation from '../shopping-cart/JKPayPalConfirmation';
|
||||||
|
|
||||||
|
import JKUnsubscribe from '../public/JKUnsubscribe';
|
||||||
|
import JKConfirmEmailChange from '../public/JKConfirmEmailChange';
|
||||||
|
|
||||||
|
|
||||||
//import loadable from '@loadable/component';
|
//import loadable from '@loadable/component';
|
||||||
//const DashboardRoutes = loadable(() => import('../../layouts/JKDashboardRoutes'));
|
//const DashboardRoutes = loadable(() => import('../../layouts/JKDashboardRoutes'));
|
||||||
|
|
@ -319,6 +322,8 @@ function JKDashboardMain() {
|
||||||
<PrivateRoute path="/checkout/success" component={JKCheckoutSuccess} />
|
<PrivateRoute path="/checkout/success" component={JKCheckoutSuccess} />
|
||||||
<PrivateRoute path="/checkout" component={JKCheckout} />
|
<PrivateRoute path="/checkout" component={JKCheckout} />
|
||||||
<PrivateRoute path="/applaunch" component={JKAppLaunch} />
|
<PrivateRoute path="/applaunch" component={JKAppLaunch} />
|
||||||
|
<PrivateRoute path="/unsubscribe/:tok" exact component={JKUnsubscribe} />
|
||||||
|
<PrivateRoute path="/confirm-email-change" exact component={JKConfirmEmailChange} />
|
||||||
{/*Redirect*/}
|
{/*Redirect*/}
|
||||||
<Redirect to="/errors/404" />
|
<Redirect to="/errors/404" />
|
||||||
</Switch>
|
</Switch>
|
||||||
|
|
|
||||||
|
|
@ -3,13 +3,17 @@ import { useLocation } from "react-router-dom";
|
||||||
import { Card, CardBody, CardText, CardTitle } from 'reactstrap';
|
import { Card, CardBody, CardText, CardTitle } from 'reactstrap';
|
||||||
import { useState, useEffect } from 'react';
|
import { useState, useEffect } from 'react';
|
||||||
import { updateEmail } from '../../helpers/rest';
|
import { updateEmail } from '../../helpers/rest';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
const JKConfirmEmailChange = () => {
|
const JKConfirmEmailChange = () => {
|
||||||
|
const { t } = useTranslation("account");
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const params = new URLSearchParams(location.search);
|
const params = new URLSearchParams(location.search);
|
||||||
const token = params.get('token');
|
const token = params.get('token');
|
||||||
|
|
||||||
const [success, setSuccess] = useState(false);
|
const [success, setSuccess] = useState(false);
|
||||||
|
const [error, setError] = useState(null);
|
||||||
|
const [loading, setLoading] = useState(true);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (token) {
|
if (token) {
|
||||||
|
|
@ -23,6 +27,9 @@ const JKConfirmEmailChange = () => {
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
setSuccess(false);
|
setSuccess(false);
|
||||||
|
setError(t('identity.changed_email_confirmation.error'));
|
||||||
|
}).finally(() => {
|
||||||
|
setLoading(false);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [token]);
|
}, [token]);
|
||||||
|
|
@ -31,15 +38,13 @@ const JKConfirmEmailChange = () => {
|
||||||
<Card style={{ width: '25rem', margin: '2rem auto' }}>
|
<Card style={{ width: '25rem', margin: '2rem auto' }}>
|
||||||
<CardBody>
|
<CardBody>
|
||||||
<CardTitle className="mb-2">
|
<CardTitle className="mb-2">
|
||||||
Change Email Confirmation
|
{t('identity.changed_email_confirmation.title')}
|
||||||
</CardTitle>
|
</CardTitle>
|
||||||
<CardText>
|
<>
|
||||||
{
|
{loading && <div className="text-muted"><span className="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>{t('identity.changed_email_confirmation.loading')}</div>}
|
||||||
success?
|
{success && <div className="text-success">{t('identity.changed_email_confirmation.success')}</div>}
|
||||||
'Your email has been successfully updated.' :
|
{error && <div className="text-danger">{error}</div>}
|
||||||
'Loading...'
|
</>
|
||||||
}
|
|
||||||
</CardText>
|
|
||||||
</CardBody>
|
</CardBody>
|
||||||
</Card>
|
</Card>
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,14 @@
|
||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { Card, CardBody, CardText, CardTitle } from 'reactstrap';
|
import { Card, CardBody, CardText, CardTitle } from 'reactstrap';
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useBrowserQuery } from '../../context/BrowserQuery';
|
import { useBrowserQuery } from '../../context/BrowserQuery';
|
||||||
import { useHistory, useParams } from "react-router-dom";
|
import { useHistory, useParams } from "react-router-dom";
|
||||||
|
|
||||||
|
|
||||||
const unsubscribeFromNewUsersWeeklyEmail = (token) => {
|
const unsubscribeFromNewUsersWeeklyEmail = (token) => {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const baseUrl = process.env.REACT_APP_CLIENT_BASE_URL
|
const baseUrl = process.env.REACT_APP_CLIENT_BASE_URL
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
|
|
@ -25,32 +27,42 @@ const unsubscribeFromNewUsersWeeklyEmail = (token) => {
|
||||||
const unsubscribe = (token) => {
|
const unsubscribe = (token) => {
|
||||||
const baseUrl = process.env.REACT_APP_CLIENT_BASE_URL
|
const baseUrl = process.env.REACT_APP_CLIENT_BASE_URL
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
fetch(`${baseUrl}/unsubscribe/${token}`, { method: 'POST' })
|
fetch(`${baseUrl}/unsubscribe/${token}`, { method: 'POST', headers: { 'Content-Type': 'application/json', accept: 'application/json' } })
|
||||||
.then(response => {
|
.then(response => {
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
resolve(response)
|
resolve(response)
|
||||||
} else {
|
} else {
|
||||||
reject(response)
|
reject(response)
|
||||||
}
|
}
|
||||||
})
|
}).catch(error => {
|
||||||
|
reject(error);
|
||||||
|
});
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function JKUnsubscribe() {
|
function JKUnsubscribe() {
|
||||||
const {t} = useTranslation()
|
const { t } = useTranslation("unsubscribe");
|
||||||
const queryObj = useBrowserQuery();
|
|
||||||
const history = useHistory()
|
const history = useHistory()
|
||||||
|
const [loading, setLoading] = useState(true)
|
||||||
const [success, setSuccess] = useState(false)
|
const [success, setSuccess] = useState(false)
|
||||||
|
const [error, setError] = useState(null)
|
||||||
const { tok } = useParams();
|
const { tok } = useParams();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (tok) {
|
if (tok) {
|
||||||
unsubscribe(tok)
|
unsubscribe(tok)
|
||||||
.then((resp) => {
|
.then((resp) => {
|
||||||
if (resp.ok) {
|
if (resp.ok) {
|
||||||
setSuccess(true)
|
setSuccess(true)
|
||||||
|
} else {
|
||||||
|
setSuccess(false)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(error => console.error(error))
|
.catch(error => {
|
||||||
|
setError(error)
|
||||||
|
}).finally(() => {
|
||||||
|
setLoading(false)
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
history.push('/')
|
history.push('/')
|
||||||
}
|
}
|
||||||
|
|
@ -58,17 +70,15 @@ function JKUnsubscribe() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
||||||
|
|
||||||
<Card style={{ width: '25rem', margin: '2rem auto' }}>
|
<Card style={{ width: '25rem', margin: '2rem auto' }}>
|
||||||
<CardBody>
|
<CardBody>
|
||||||
<CardTitle className="mb-2">Unsubscribe from JamKazam emails</CardTitle>
|
<CardTitle className="mb-2">{t('page_title')}</CardTitle>
|
||||||
<CardText>
|
<>
|
||||||
{
|
{loading && <div className="text-muted"><span className="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>{t('loading')}</div>}
|
||||||
success?
|
{success && <div className="text-success">{t('success')}</div>}
|
||||||
'You have been unsubscribed from all JamKazam emails. You will no longer receive any emails from JamKazam.' :
|
{error && <div className="text-danger">{t('error')}</div>}
|
||||||
'Unsubscribing...'
|
</>
|
||||||
}
|
|
||||||
</CardText>
|
|
||||||
</CardBody>
|
</CardBody>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,12 @@
|
||||||
"confirmation_email_sent": "A confirmation email has been sent to your email address. Please click the link in the email to confirm your email address."
|
"confirmation_email_sent": "A confirmation email has been sent to your email address. Please click the link in the email to confirm your email address."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"changed_email_confirmation": {
|
||||||
|
"title": "Change Email Confirmation",
|
||||||
|
"loadding": "Loading...",
|
||||||
|
"success": "Your email has been successfully changed.",
|
||||||
|
"error": "An error occurred while confirming your email change. Please try again later."
|
||||||
|
},
|
||||||
"password_form": {
|
"password_form": {
|
||||||
"title": "Password",
|
"title": "Password",
|
||||||
"help_text": "To update the password associated with your account, enter your current password (for security reasons) and the new password, and click the \"Save Password\" button.",
|
"help_text": "To update the password associated with your account, enter your current password (for security reasons) and the new password, and click the \"Save Password\" button.",
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,6 @@
|
||||||
{
|
{
|
||||||
"page_title": "Unsubscribe"
|
"page_title": "Unsubscribe from JamKazam emails",
|
||||||
|
"success": "You have successfully unsubscribed from JamKazam emails.",
|
||||||
|
"error": "An error occurred while unsubscribing. Please try again later.",
|
||||||
|
"loading": "Loading..."
|
||||||
}
|
}
|
||||||
|
|
@ -23,8 +23,8 @@ const JKPublicRoutes = ({ match: { url } }) => (
|
||||||
<Route path={`${url}/knowledge-base`} component={JKKnowledgeBase} />
|
<Route path={`${url}/knowledge-base`} component={JKKnowledgeBase} />
|
||||||
<Route path={`${url}/help-desk`} component={JKHelpDesk} />
|
<Route path={`${url}/help-desk`} component={JKHelpDesk} />
|
||||||
<Route path={`${url}/forum`} component={JKForum} />
|
<Route path={`${url}/forum`} component={JKForum} />
|
||||||
<Route path={`${url}/unsubscribe/:tok`} exact component={JKUnsubscribe} />
|
{/* <Route path={`${url}/unsubscribe/:tok`} exact component={JKUnsubscribe} />
|
||||||
<Route path={`${url}/confirm-email-change`} exact component={JKConfirmEmailChange} />
|
<Route path={`${url}/confirm-email-change`} exact component={JKConfirmEmailChange} /> */}
|
||||||
<Route path={`${url}/downloads`} exact component={JKDownloads} />
|
<Route path={`${url}/downloads`} exact component={JKDownloads} />
|
||||||
<Route path={`${url}/downloads-legacy`} exact component={JKDownloadsLegacy} />
|
<Route path={`${url}/downloads-legacy`} exact component={JKDownloadsLegacy} />
|
||||||
<Route path={`${url}/obs-plugin-download`} exact component={JKObsDownloads} />
|
<Route path={`${url}/obs-plugin-download`} exact component={JKObsDownloads} />
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@
|
||||||
<p>
|
<p>
|
||||||
<%= I18n.t "mailer_layout.footer.paragraph1" -%> <a href="https://www.jamkazam.com" target="_blank">JamKazam</a>. <br /> <%= I18n.t "mailer_layout.footer.you_can" -%> <a
|
<%= I18n.t "mailer_layout.footer.paragraph1" -%> <a href="https://www.jamkazam.com" target="_blank">JamKazam</a>. <br /> <%= I18n.t "mailer_layout.footer.you_can" -%> <a
|
||||||
|
|
||||||
href="https://www.jamkazam.com/unsubscribe/<%= @user.unsubscribe_token %>"
|
href="<%= ApplicationHelper.spa_base_uri %>/unsubscribe/<%= @user.unsubscribe_token %>"
|
||||||
style="
|
style="
|
||||||
color: #2c7be5;
|
color: #2c7be5;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
|
|
|
||||||
|
|
@ -619,7 +619,7 @@ class ApiUsersController < ApiController
|
||||||
end
|
end
|
||||||
|
|
||||||
def begin_update_email_alt
|
def begin_update_email_alt
|
||||||
confirm_email_link = ApplicationHelper.spa_base_uri + '/public/confirm-email-change' + "?token="
|
confirm_email_link = ApplicationHelper.spa_base_uri + '/confirm-email-change' + "?token="
|
||||||
do_bigin_complete_email(confirm_email_link)
|
do_bigin_complete_email(confirm_email_link)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -439,22 +439,21 @@ JS
|
||||||
end
|
end
|
||||||
|
|
||||||
def unsubscribe
|
def unsubscribe
|
||||||
unless @user = User.read_access_token(params[:user_token])
|
unless params[:user_token].present? && (@user = User.read_access_token(params[:user_token]))
|
||||||
redirect_to '/'
|
|
||||||
end if params[:user_token].present?
|
|
||||||
|
|
||||||
#if request.get?
|
|
||||||
|
|
||||||
#elsif request.post?
|
|
||||||
@user.subscribe_email = false
|
|
||||||
@user.save!
|
|
||||||
#end
|
|
||||||
|
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.html { render text: 'You have been unsubscribed.' }
|
format.html { redirect_to '/', alert: 'Invalid or expired token.' }
|
||||||
format.json { render json: { status: 'success', message: 'You have been unsubscribed.' } }
|
format.json { render json: { status: 'error', message: 'Invalid or expired token.' }, status: :unprocessable_entity }
|
||||||
|
end
|
||||||
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@user.subscribe_email = false
|
||||||
|
@user.save!
|
||||||
|
|
||||||
|
respond_to do |format|
|
||||||
|
format.html { render plain: 'You have been unsubscribed.' }
|
||||||
|
format.json { render json: { status: 'success', message: 'You have been unsubscribed.' } }
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def unsubscribe_user_match
|
def unsubscribe_user_match
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue