account delete link

This commit is contained in:
adueck 2024-04-02 19:10:12 +03:00
parent 1a2a36090f
commit d5f2aa492a
2 changed files with 279 additions and 206 deletions

View File

@ -12,9 +12,8 @@
</script> </script>
</head> </head>
<body> <body>
<div class="container" style="max-width: 400px;"> <div class="container" style="max-width: 800px;">
<h2 class="mt-4 mb-4 text-center">How to Delete your LingDocs Account</h2> <h2 class="mt-4 mb-4 text-center">How to Delete your LingDocs Account</h2>
<h4>Profile <i class="fas fa-user ml-2"></i></h4>
<p>To delete your account for the LingDocs Pashto Dictionary and LingDocs Pashto Grammar:</p> <p>To delete your account for the LingDocs Pashto Dictionary and LingDocs Pashto Grammar:</p>
<ul> <ul>
<li>Go to <a href="https://account.lingdocs.com/user">https://account.lingdocs.com/user</a></li> <li>Go to <a href="https://account.lingdocs.com/user">https://account.lingdocs.com/user</a></li>

View File

@ -1,226 +1,300 @@
import { import { useState, useEffect } from "react";
useState,
useEffect,
} from "react";
import { Modal, Button } from "react-bootstrap"; import { Modal, Button } from "react-bootstrap";
import { import {
upgradeAccount, upgradeAccount,
signOut, signOut,
publishDictionary, publishDictionary,
} from "../lib/backend-calls"; } from "../lib/backend-calls";
import LoadingElipses from "../components/LoadingElipses"; import LoadingElipses from "../components/LoadingElipses";
import { Helmet } from "react-helmet"; import { Helmet } from "react-helmet";
import * as AT from "../types/account-types"; import * as AT from "../types/account-types";
import UpgradePrices from "../components/UpgradePrices"; import UpgradePrices from "../components/UpgradePrices";
const providers: ("google" | "twitter" | "github")[] = ["google", "twitter", "github"]; const providers: ("google" | "twitter" | "github")[] = [
"google",
"twitter",
"github",
];
const capitalize = (s: string): string => { const capitalize = (s: string): string => {
// if (!s) return ""; // if (!s) return "";
return s.charAt(0).toUpperCase() + s.slice(1); return s.charAt(0).toUpperCase() + s.slice(1);
} };
let popupRef: Window | null = null; let popupRef: Window | null = null;
const Account = ({ user, loadUser }: { user: AT.LingdocsUser | undefined, loadUser: () => void }) => { const Account = ({
const [showingUpgradePrompt, setShowingUpgradePrompt] = useState<boolean>(false); user,
const [upgradePassword, setUpgradePassword] = useState<string>(""); loadUser,
const [upgradeError, setUpgradeError] = useState<string>(""); }: {
const [waiting, setWaiting] = useState<boolean>(false); user: AT.LingdocsUser | undefined;
const [publishingStatus, setPublishingStatus] = useState<undefined | "publishing" | any>(undefined); loadUser: () => void;
useEffect(() => { }) => {
setShowingUpgradePrompt(false); const [showingUpgradePrompt, setShowingUpgradePrompt] =
setUpgradeError(""); useState<boolean>(false);
const [upgradePassword, setUpgradePassword] = useState<string>("");
const [upgradeError, setUpgradeError] = useState<string>("");
const [waiting, setWaiting] = useState<boolean>(false);
const [publishingStatus, setPublishingStatus] = useState<
undefined | "publishing" | any
>(undefined);
useEffect(() => {
setShowingUpgradePrompt(false);
setUpgradeError("");
setWaiting(false);
window.addEventListener("message", handleIncomingMessage);
return () => {
window.removeEventListener("message", handleIncomingMessage);
};
// eslint-disable-next-line
}, []);
// TODO put the account url in an imported constant
function handleIncomingMessage(event: MessageEvent<any>) {
if (
event.origin === "https://account.lingdocs.com" &&
event.data === "signed in" &&
popupRef
) {
loadUser();
popupRef.close();
}
}
async function handleSignOut() {
await signOut();
loadUser();
}
function closeUpgrade() {
setShowingUpgradePrompt(false);
setUpgradePassword("");
setUpgradeError("");
}
async function handleUpgrade() {
setUpgradeError("");
setWaiting(true);
upgradeAccount(upgradePassword)
.then((res) => {
setWaiting(false); setWaiting(false);
window.addEventListener("message", handleIncomingMessage); if (res.ok) {
return () => { loadUser();
window.removeEventListener("message", handleIncomingMessage); closeUpgrade();
}; } else {
// eslint-disable-next-line setUpgradeError("Incorrect password");
}, []);
// TODO put the account url in an imported constant
function handleIncomingMessage(event: MessageEvent<any>) {
if (
event.origin === "https://account.lingdocs.com"
&& (event.data === "signed in")
&& popupRef
) {
loadUser();
popupRef.close();
} }
} })
async function handleSignOut() { .catch((err) => {
await signOut(); setWaiting(false);
loadUser(); setUpgradeError(err.message);
} });
function closeUpgrade() { }
setShowingUpgradePrompt(false); function handleOpenSignup() {
setUpgradePassword(""); popupRef = window.open(
setUpgradeError(""); "https://account.lingdocs.com",
} "account",
async function handleUpgrade() { "height=800,width=500,top=50,left=400"
setUpgradeError(""); );
setWaiting(true); }
upgradeAccount(upgradePassword).then((res) => { function handlePublish() {
setWaiting(false); setPublishingStatus("publishing");
if (res.ok) { publishDictionary()
loadUser(); .then((response) => {
closeUpgrade(); setPublishingStatus(response);
} else { })
setUpgradeError("Incorrect password"); .catch((err) => {
} console.error(err);
}).catch((err) => { setPublishingStatus("Offline or connection error");
setWaiting(false); });
setUpgradeError(err.message); }
}); if (!user) {
}
function handleOpenSignup() {
popupRef = window.open("https://account.lingdocs.com", "account", "height=800,width=500,top=50,left=400");
}
function handlePublish() {
setPublishingStatus("publishing");
publishDictionary().then((response) => {
setPublishingStatus(response);
}).catch((err) => {
console.error(err);
setPublishingStatus("Offline or connection error");
});
}
if (!user) {
return <div className="text-center mt-3">
<Helmet>
<link rel="canonical" href="https://dictionary.lingdocs.com/account" />
<meta name="description" content="Sign in to the LingDocs Pashto Dictionary" />
<title>Sign In - LingDocs Pashto Dictionary</title>
</Helmet>
<h2 className="my-4">Sign in to LingDocs</h2>
<p className="lead mb-4">When you sign in or make a LingDocs account you can:</p>
<div className="mb-3"><i className="fas fa-pen mr-2" /> contribute by suggesting corrections and new words</div>
<div className="mb-3"><i className="fas fa-star mr-2" /> upgrade your account and start collecting a personal <strong>wordlist</strong></div>
<button className="btn btn-lg btn-primary my-4" onClick={handleOpenSignup}><i className="fas fa-sign-in-alt mr-2" /> Sign In</button>
</div>
}
return ( return (
<div style={{ marginBottom: "100px", maxWidth: "40rem" }}> <div className="text-center mt-3">
<Helmet> <Helmet>
<link rel="canonical" href="https://dictionary.lingdocs.com/account" /> <link
<meta name="description" content="Account for the LingDocs Pashto Dictionary" /> rel="canonical"
<title>Account - LingDocs Pashto Dictionary</title> href="https://dictionary.lingdocs.com/account"
</Helmet> />
<h2 className="mb-4">Account</h2> <meta
{user.level === "editor" && name="description"
<div className="mb-3"> content="Sign in to the LingDocs Pashto Dictionary"
<h4>Editor Tools</h4> />
{publishingStatus !== "publishing" && <title>Sign In - LingDocs Pashto Dictionary</title>
<button type="button" className="btn btn-secondary" onClick={handlePublish}>Publish Dictionary</button> </Helmet>
} <h2 className="my-4">Sign in to LingDocs</h2>
{publishingStatus && <p className="lead mb-4">
<> When you sign in or make a LingDocs account you can:
<h6 className="mt-3">Publishing response:</h6> </p>
<pre className="pre-scrollable"><code> <div className="mb-3">
{publishingStatus === "publishing" ? <i className="fas fa-pen mr-2" /> contribute by suggesting corrections
"processing..." and new words
: </div>
JSON.stringify(publishingStatus, null, "\t") <div className="mb-3">
} <i className="fas fa-star mr-2" /> upgrade your account and start
</code></pre> collecting a personal <strong>wordlist</strong>
</> </div>
} <button
</div> className="btn btn-lg btn-primary my-4"
} onClick={handleOpenSignup}
<div> >
{/* {user.p && <div className="mb-4 mt-3" style={{ textAlign: "center" }}> <i className="fas fa-sign-in-alt mr-2" /> Sign In
</button>
</div>
);
}
return (
<div style={{ marginBottom: "100px", maxWidth: "40rem" }}>
<Helmet>
<link rel="canonical" href="https://dictionary.lingdocs.com/account" />
<meta
name="description"
content="Account for the LingDocs Pashto Dictionary"
/>
<title>Account - LingDocs Pashto Dictionary</title>
</Helmet>
<h2 className="mb-4">Account</h2>
{user.level === "editor" && (
<div className="mb-3">
<h4>Editor Tools</h4>
{publishingStatus !== "publishing" && (
<button
type="button"
className="btn btn-secondary"
onClick={handlePublish}
>
Publish Dictionary
</button>
)}
{publishingStatus && (
<>
<h6 className="mt-3">Publishing response:</h6>
<pre className="pre-scrollable">
<code>
{publishingStatus === "publishing"
? "processing..."
: JSON.stringify(publishingStatus, null, "\t")}
</code>
</pre>
</>
)}
</div>
)}
<div>
{/* {user.p && <div className="mb-4 mt-3" style={{ textAlign: "center" }}>
<img src={user.photoURL} data-testid="userAvatar" alt="avatar" style={{ borderRadius: "50%", width: "5rem", height: "5rem" }}/> <img src={user.photoURL} data-testid="userAvatar" alt="avatar" style={{ borderRadius: "50%", width: "5rem", height: "5rem" }}/>
</div>} */} </div>} */}
<div className="card mb-4"> <div className="card mb-4">
<ul className="list-group list-group-flush"> <ul className="list-group list-group-flush">
<li className="list-group-item">Name: {user.name}</li> <li className="list-group-item">Name: {user.name}</li>
{user.email && <li className="list-group-item"> {user.email && (
<div className="d-flex justify-content-between align-items-center"> <li className="list-group-item">
<div> <div className="d-flex justify-content-between align-items-center">
<div>Email: {user.email}</div> <div>
</div> <div>Email: {user.email}</div>
</div> </div>
</li>}
<li className="list-group-item">Account Level: {capitalize(user.level)}</li>
<li className="list-group-item">Signs in with:
{(user.password && user.email) && <span>
<i className="fas fa-key ml-2"></i> <span className="small mr-1">Password</span>
</span>}
{providers.map((provider) => (
<span key={provider}>
{user[provider] && <i className={`fab fa-${provider} mx-1`}></i>}
</span>
))}
</li>
</ul>
</div> </div>
</div> </li>
<h4 className="mb-3">Account Admin</h4> )}
<div className="row mb-4"> <li className="list-group-item">
{user.level === "basic" && <div className="col-sm mb-3"> Account Level: {capitalize(user.level)}
<button </li>
type="button" <li className="list-group-item">
className="btn btn-outline-secondary" Signs in with:
onClick={() => setShowingUpgradePrompt(true)} {user.password && user.email && (
data-testid="upgradeButton" <span>
> <i className="fas fa-key ml-2"></i>{" "}
<i className="fa fa-level-up-alt"></i> Upgrade Account <span className="small mr-1">Password</span>
</button> </span>
</div>} )}
<div className="col-sm mb-3"> {providers.map((provider) => (
<a className="btn btn-outline-secondary" href="https://account.lingdocs.com/user"> <span key={provider}>
<i className="fas fa-user mr-2"></i> Edit Account {user[provider] && (
</a> <i className={`fab fa-${provider} mx-1`}></i>
</div> )}
<div className="col-sm mb-3"> </span>
<button className="btn btn-outline-secondary" onClick={handleSignOut}> ))}
<i className="fas fa-sign-out-alt mr-2"></i> Sign Out </li>
</button> </ul>
</div>
</div>
<Modal show={showingUpgradePrompt} onHide={closeUpgrade}>
<Modal.Header closeButton>
<Modal.Title>Upgrade to Student Account</Modal.Title>
</Modal.Header>
<Modal.Body>
<p className="lead">Upgrade to a <strong>student account</strong> to enable the wordlist</p>
<p>Features:</p>
<ul>
<li>Save your wordlist and sync across devices</li>
<li>Save text, audio, or visual context for words</li>
<li>Review words with Anki-style spaced repetition</li>
</ul>
<UpgradePrices source="account" />
</Modal.Body>
<div className="form-group px-3">
<label htmlFor="upgradePasswordForm">Or enter upgrade password:</label>
<input
type="text"
className="form-control"
id="upgradePasswordForm"
data-lpignore="true"
value={upgradePassword}
onChange={(e) => setUpgradePassword(e.target.value)}
/>
</div>
{upgradeError && <div className="mt-3 alert alert-warning mx-3">
<p>
<strong>{upgradeError}</strong>
</p>
</div>}
<Modal.Footer>
{waiting && <LoadingElipses />}
<Button variant="secondary" onClick={closeUpgrade}>
Cancel
</Button>
<Button variant="primary" onClick={handleUpgrade}>
Upgrade with password
</Button>
</Modal.Footer>
</Modal>
</div> </div>
); </div>
<h4 className="mb-3">Account Admin</h4>
<div className="row mb-4">
{user.level === "basic" && (
<div className="col-sm mb-3">
<button
type="button"
className="btn btn-outline-secondary"
onClick={() => setShowingUpgradePrompt(true)}
data-testid="upgradeButton"
>
<i className="fa fa-level-up-alt"></i> Upgrade Account
</button>
</div>
)}
<div className="col-sm mb-3">
<a
className="btn btn-outline-secondary"
href="https://account.lingdocs.com/user"
>
<i className="fas fa-user mr-2"></i> Edit Account
</a>
</div>
<div className="col-sm mb-3">
<button className="btn btn-outline-secondary" onClick={handleSignOut}>
<i className="fas fa-sign-out-alt mr-2"></i> Sign Out
</button>
</div>
</div>
<div>
<a className="small" href="https://account.lingdocs.com/delete-account">
How to delete your account
</a>
</div>
<Modal show={showingUpgradePrompt} onHide={closeUpgrade}>
<Modal.Header closeButton>
<Modal.Title>Upgrade to Student Account</Modal.Title>
</Modal.Header>
<Modal.Body>
<p className="lead">
Upgrade to a <strong>student account</strong> to enable the wordlist
</p>
<p>Features:</p>
<ul>
<li>Save your wordlist and sync across devices</li>
<li>Save text, audio, or visual context for words</li>
<li>Review words with Anki-style spaced repetition</li>
</ul>
<UpgradePrices source="account" />
</Modal.Body>
<div className="form-group px-3">
<label htmlFor="upgradePasswordForm">
Or enter upgrade password:
</label>
<input
type="text"
className="form-control"
id="upgradePasswordForm"
data-lpignore="true"
value={upgradePassword}
onChange={(e) => setUpgradePassword(e.target.value)}
/>
</div>
{upgradeError && (
<div className="mt-3 alert alert-warning mx-3">
<p>
<strong>{upgradeError}</strong>
</p>
</div>
)}
<Modal.Footer>
{waiting && <LoadingElipses />}
<Button variant="secondary" onClick={closeUpgrade}>
Cancel
</Button>
<Button variant="primary" onClick={handleUpgrade}>
Upgrade with password
</Button>
</Modal.Footer>
</Modal>
</div>
);
}; };
export default Account; export default Account;