mirror of
https://github.com/narrrl/pl8list-rewind.git
synced 2026-11-03 16:41:26 +01:00
Listen without a pl8list account
"start listening" on the sign-in page makes a guest: a user with a negative id and a session of its own, so the feed, likes, follows and radio work as for anyone. Signing in with pl8list later moves the guest's plays, likes and follows to the account. Signing out as a guest deletes it; guests whose session ran out are purged hourly. New guests are capped at 10 per IP and hour, and that limiter forgets IPs hourly. The privacy policy covers guests.
This commit is contained in:
1 parent
6e7311bb9d
commit
300674b8ac
11 files changed
+232
-26
No files matched your search
@@ -61,6 +61,8 @@ export interface Stats {
|
||||
export interface User {
|
||||
handle: string;
|
||||
name: string | null;
|
||||
/// Listening without a pl8list account.
|
||||
guest?: boolean;
|
||||
}
|
||||
|
||||
export interface Pl8Status {
|
||||
|
||||
+38
-8
@@ -123,6 +123,7 @@ async function loadMore(): Promise<void> {
|
||||
if (gen !== feedGen) return;
|
||||
if (err instanceof api.HttpError && err.status === 401) {
|
||||
loading = false;
|
||||
signedInUser = null;
|
||||
showLogin();
|
||||
return;
|
||||
}
|
||||
@@ -996,13 +997,29 @@ function showLogin({ resync = false } = {}): void {
|
||||
loginEl.hidden = false;
|
||||
emailForm.hidden = false;
|
||||
codeForm.hidden = true;
|
||||
loginCancel.hidden = !resync;
|
||||
// Someone already listening is here only to sign in with pl8list.
|
||||
guestStart.hidden = !!signedInUser;
|
||||
loginCancel.hidden = !signedInUser;
|
||||
loginError.textContent = resync ? "sign in with pl8list again to sync your library" : "";
|
||||
$("email").focus();
|
||||
if (signedInUser) $("email").focus();
|
||||
}
|
||||
|
||||
loginCancel.addEventListener("click", () => { if (signedInUser) signedIn(signedInUser); });
|
||||
|
||||
const guestStart = $("guest-start");
|
||||
$("guest-btn").addEventListener("click", async (e) => {
|
||||
const btn = e.currentTarget as HTMLButtonElement;
|
||||
btn.disabled = true;
|
||||
loginError.textContent = "";
|
||||
try {
|
||||
const { user } = await api.postJSON<{ user: User }>("/auth/guest");
|
||||
signedIn(user);
|
||||
} catch (err) {
|
||||
loginError.textContent = (err as Error).message;
|
||||
}
|
||||
btn.disabled = false;
|
||||
});
|
||||
|
||||
async function withForm(form: HTMLFormElement, fn: () => Promise<void>): Promise<void> {
|
||||
const buttons = form.querySelectorAll("button");
|
||||
buttons.forEach((b) => { b.disabled = true; });
|
||||
@@ -1062,7 +1079,10 @@ const disconnectBtn = $("disconnect-btn");
|
||||
function showSync(s: Pl8Status | undefined): void {
|
||||
const connected = !!s?.connected;
|
||||
syncStatus.classList.toggle("on", connected);
|
||||
syncStatus.textContent = connected
|
||||
syncStatus.textContent = signedInUser?.guest
|
||||
? "listening as a guest on this device. Sign in with pl8list to bring your follows and likes " +
|
||||
"along; what you did here comes with you."
|
||||
: connected
|
||||
? `connected on this device${s?.synced_at ? `, last sync ${ago(s.synced_at)}` : ""}. ` +
|
||||
"Likes and follows are saved to your pl8list library."
|
||||
: "not connected. Resync signs you in with pl8list once to read your feed and library.";
|
||||
@@ -1075,6 +1095,10 @@ sheets.account.onOpen = async () => {
|
||||
};
|
||||
|
||||
$("sync-btn").addEventListener("click", async () => {
|
||||
if (signedInUser?.guest) {
|
||||
showLogin();
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await api.postJSON("/account/pl8list/sync");
|
||||
toast("syncing with pl8list…");
|
||||
@@ -1126,14 +1150,20 @@ deleteForm.addEventListener("submit", async (e) => {
|
||||
|
||||
function signedIn(user: User, pl8list?: Pl8Status): void {
|
||||
signedInUser = user;
|
||||
if (pl8list) showSync(pl8list);
|
||||
const guest = !!user.guest;
|
||||
showSync(pl8list);
|
||||
loginEl.hidden = true;
|
||||
$("bar").hidden = false;
|
||||
accountBtn.textContent = (user.name || user.handle)[0]?.toUpperCase() ?? "?";
|
||||
accountBtn.style.backgroundColor = accentForName(user.handle);
|
||||
accountBtn.title = `@${user.handle}`;
|
||||
$("account-handle").textContent = `@${user.handle}`;
|
||||
accountBtn.textContent = guest ? "?" : (user.name || user.handle)[0]?.toUpperCase() ?? "?";
|
||||
accountBtn.style.backgroundColor = guest ? "#ffffff30" : accentForName(user.handle);
|
||||
accountBtn.title = guest ? "guest" : `@${user.handle}`;
|
||||
$("account-handle").textContent = guest ? "listening as a guest" : `@${user.handle}`;
|
||||
$("delete-handle").textContent = user.handle;
|
||||
// A guest has one session and nothing on pl8list: signing out forgets them.
|
||||
$("sync-title").textContent = guest ? "pl8list" : "pl8list sync";
|
||||
$("sync-btn").textContent = guest ? "sign in with pl8list" : "resync with pl8list";
|
||||
$("logout-btn").textContent = guest ? "sign out and forget me" : "sign out";
|
||||
for (const id of ["logout-all-btn", "delete-open"]) $(id).hidden = guest;
|
||||
// Typing the login code counts as a user gesture, so playback may start right away.
|
||||
if (navigator.userActivation?.hasBeenActive) start();
|
||||
else if (!started) startEl.hidden = false;
|
||||
|
||||
@@ -1000,6 +1000,11 @@ button.primary:disabled { opacity: 0.35; cursor: default; }
|
||||
}
|
||||
#code { font-family: ui-monospace, monospace; font-size: 1.4rem; letter-spacing: 0.5em; text-align: center; }
|
||||
.login-box button[type=submit] { background: var(--accent); border: none; color: #fff; padding: 0.7em; font-weight: 600; }
|
||||
/* Listening without an account comes first; signing in is the quieter way below. */
|
||||
#guest-start { display: flex; flex-direction: column; gap: 0.6rem; padding-bottom: 0.4rem; border-bottom: 1px solid var(--line); }
|
||||
#guest-btn { width: 100%; font-size: 1.05rem; padding: 0.8em; }
|
||||
#guest-start .or { margin: 0 0 0.5rem; color: var(--muted); font-size: 0.85rem; text-align: center; }
|
||||
#guest-start:not([hidden]) ~ #email-form button[type=submit] { background: none; border: 1px solid var(--line-strong); color: var(--fg); }
|
||||
.login-box button[disabled] { opacity: 0.6; cursor: wait; }
|
||||
.login-links { display: flex; justify-content: space-between; }
|
||||
.login-box label.check { display: flex; gap: 0.6rem; align-items: flex-start; color: var(--fg); cursor: pointer; }
|
||||
|
||||
Reference in new issue
Block a user