feat: add profile language preferences and refine toast, dropdown and history UI

This commit is contained in:
Mateusz Gruszczyński
2026-09-04 23:48:09 +02:00
parent f036240d5d
commit bf13587a71
42 changed files with 3971 additions and 357 deletions
+36 -8
View File
@@ -10,6 +10,7 @@
import { api } from "@rustpad/api";
import * as sessionStore from "@rustpad/session";
import { askConfirm, askInput, showMessage } from "@rustpad/modal";
import { toast } from "@rustpad/toast";
const { getAuthToken, setAuthSession, setNickname } = sessionStore;
const clearAuthSession = sessionStore.clearAuthSession || (() => {
@@ -26,6 +27,21 @@ const clearResourceAccessState = sessionStore.clearResourceAccessState || (() =>
}
});
function authErrorTitle(mode) {
if (mode === "register") return "Registration failed";
if (mode === "reset") return "Password reset failed";
return "Sign-in failed";
}
function notifyAuthError(error, mode) {
toast.danger(error?.message || "Please try again.", { title: authErrorTitle(mode) });
}
if (typeof window !== "undefined") window.addEventListener("rustpad:session-expired", () => {
toast.warning("Your session expired. Sign in again to continue with account-only actions.", { title: "Session expired", duration: 7000 });
});
function configureCredentialFields({ emailLabel, email, password, loginMode, externalAuth }) {
const directoryLogin = loginMode && externalAuth;
emailLabel.textContent = directoryLogin ? "E-mail / LDAP or AD Username" : "E-mail";
@@ -129,6 +145,7 @@ export function bindIdentityDialog({ dialog, onIdentity, initialMode = "login" }
message.classList.remove("error");
message.classList.add("success");
message.textContent = result.message;
toast.success(result.message, { title: "Reset link sent", duration: 6500 });
return;
}
@@ -141,16 +158,19 @@ export function bindIdentityDialog({ dialog, onIdentity, initialMode = "login" }
message.classList.add("success");
message.textContent = session.message;
password.value = "";
toast.success(session.message, { title: "Check your inbox", duration: 7000 });
return;
}
setAuthSession(session);
await setAuthSession(session);
await onIdentity(session.nickname, session);
toast.success(`Signed in as ${session.nickname}.`, { title: "Signed in" });
dialog.close();
} catch (error) {
message.classList.remove("success");
message.classList.add("error");
message.textContent = error.message;
if (/confirm the account/i.test(error.message) && email.value.trim()) {
notifyAuthError(error, mode);
if (/confirm the account/i.test(error.serverMessage || error.message) && email.value.trim()) {
const resend = document.createElement("button");
resend.type = "button";
resend.className = "text-button resend-confirmation";
@@ -162,8 +182,10 @@ export function bindIdentityDialog({ dialog, onIdentity, initialMode = "login" }
message.classList.remove("error");
message.classList.add("success");
message.textContent = result.message;
toast.success(result.message, { title: "Confirmation e-mail sent", duration: 6500 });
} catch (resendError) {
message.textContent = resendError.message;
toast.danger(resendError.message, { title: "Could not resend confirmation" });
resend.disabled = false;
}
});
@@ -258,10 +280,12 @@ function bindLegacyIdentityDialog({ dialog, onIdentity }) {
message.classList.remove("error");
message.classList.add("success");
message.textContent = result.message;
toast.success(result.message, { title: "Reset link sent", duration: 6500 });
} catch (error) {
message.classList.remove("success");
message.classList.add("error");
message.textContent = error.message;
notifyAuthError(error, "reset");
}
});
@@ -270,7 +294,7 @@ function bindLegacyIdentityDialog({ dialog, onIdentity }) {
updateActions();
collapse();
nickname.value = "";
message.textContent = "Logged out.";
toast.info("You have been signed out.", { title: "Signed out" });
});
form.addEventListener("submit", async (event) => {
@@ -289,10 +313,12 @@ function bindLegacyIdentityDialog({ dialog, onIdentity }) {
message.classList.add("success");
message.textContent = session.message;
password.value = "";
toast.success(session.message, { title: "Check your inbox", duration: 7000 });
return;
}
setAuthSession(session);
await setAuthSession(session);
await onIdentity(session.nickname, session);
toast.success(`Signed in as ${session.nickname}.`, { title: "Signed in" });
dialog.close();
return;
}
@@ -300,12 +326,14 @@ function bindLegacyIdentityDialog({ dialog, onIdentity }) {
const result = await api("/api/auth/identity", { method: "POST", body: JSON.stringify({ nickname: name }) });
setNickname(result.nickname);
await onIdentity(result.nickname, null);
toast.info(`Continuing as ${result.nickname}.`, { title: "Guest session" });
dialog.close();
} catch (error) {
message.classList.remove("success");
message.classList.add("error");
message.textContent = error.message;
if (/registered|session|account/i.test(error.message)) showAuth("login");
notifyAuthError(error, authRequested ? mode : "login");
if (/registered|session|account/i.test(error.serverMessage || error.message)) showAuth("login");
}
});
@@ -318,10 +346,10 @@ export async function validateCurrentSession() {
const expectedSession = Boolean(getAuthToken());
try {
const session = await api("/api/auth/me");
setAuthSession(session);
await setAuthSession(session);
return session;
} catch {
if (expectedSession) clearAuthSession();
if (expectedSession) await clearAuthSession();
return null;
}
}
@@ -331,7 +359,7 @@ export async function logoutCurrentSession() {
await api("/api/auth/logout", { method: "POST" });
} catch { }
clearResourceAccessState();
clearAuthSession();
await clearAuthSession();
}