mirror of
https://github.com/BetterSEQTA/BetterSEQTA-Plus.git
synced 2026-06-13 15:14:40 +00:00
Merge branch 'BetterSEQTA:main' into Popout-TOTM
This commit is contained in:
@@ -107,6 +107,17 @@ class CloudAuthService {
|
||||
return (result[STORAGE_KEYS.accessToken] as string) ?? null;
|
||||
}
|
||||
|
||||
/** Persist an updated user object (e.g. after cloud profile picture sync). */
|
||||
public async setUser(user: CloudUser | null): Promise<void> {
|
||||
(settingsState as any).setKey(STORAGE_KEYS.user, user);
|
||||
await browser.storage.local.set({ [STORAGE_KEYS.user]: user });
|
||||
this._state = {
|
||||
isLoggedIn: this._state.isLoggedIn,
|
||||
user,
|
||||
};
|
||||
this.notify();
|
||||
}
|
||||
|
||||
private async getClientId(): Promise<string> {
|
||||
let clientId = (settingsState as any)[STORAGE_KEYS.clientId] as string | undefined;
|
||||
if (!clientId) {
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
import browser from "webextension-polyfill";
|
||||
import localforage from "localforage";
|
||||
import { cloudAuth } from "@/seqta/utils/CloudAuth";
|
||||
|
||||
const ACCOUNTS_BASE = "https://accounts.betterseqta.org";
|
||||
const PLUGIN_SETTINGS_KEY = "plugin.profile-picture.settings";
|
||||
|
||||
const profileStore = localforage.createInstance({
|
||||
name: "profile-picture-store",
|
||||
storeName: "profilePicture",
|
||||
});
|
||||
|
||||
function cacheBustPfpUrl(url: string): string {
|
||||
const base = url.split("?")[0]!;
|
||||
return `${base}?v=${Date.now()}`;
|
||||
}
|
||||
|
||||
export async function isUseCloudPfpEnabled(): Promise<boolean> {
|
||||
const stored = await browser.storage.local.get(PLUGIN_SETTINGS_KEY);
|
||||
const settings = stored[PLUGIN_SETTINGS_KEY] as { useCloudPfp?: boolean } | undefined;
|
||||
return !!settings?.useCloudPfp;
|
||||
}
|
||||
|
||||
async function parseJsonResponse(r: Response): Promise<Record<string, unknown>> {
|
||||
const text = await r.text();
|
||||
try {
|
||||
return text ? (JSON.parse(text) as Record<string, unknown>) : {};
|
||||
} catch {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
export async function syncLocalProfilePictureToCloud(): Promise<{
|
||||
success: boolean;
|
||||
error?: string;
|
||||
}> {
|
||||
if (!(await isUseCloudPfpEnabled()) || !cloudAuth.state.isLoggedIn) {
|
||||
return { success: true };
|
||||
}
|
||||
|
||||
const token = await cloudAuth.getStoredToken();
|
||||
if (!token) return { success: false, error: "Not logged in" };
|
||||
|
||||
const blob = await profileStore.getItem<Blob>("profile-picture");
|
||||
|
||||
try {
|
||||
if (!blob || !(blob instanceof Blob)) {
|
||||
const res = await fetch(`${ACCOUNTS_BASE}/api/user/pfp/clear`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({}),
|
||||
});
|
||||
const data = await parseJsonResponse(res);
|
||||
if (!res.ok) {
|
||||
return { success: false, error: (data.error as string) ?? `Clear failed (${res.status})` };
|
||||
}
|
||||
const user = cloudAuth.state.user;
|
||||
if (user) {
|
||||
await cloudAuth.setUser({ ...user, pfpUrl: undefined });
|
||||
}
|
||||
return { success: true };
|
||||
}
|
||||
|
||||
if (!blob.type.startsWith("image/")) {
|
||||
return { success: false, error: "Invalid file type" };
|
||||
}
|
||||
if (blob.size > 5 * 1024 * 1024) {
|
||||
return { success: false, error: "File too large (max 5MB)" };
|
||||
}
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append("file", blob, "profile-picture");
|
||||
|
||||
const res = await fetch(`${ACCOUNTS_BASE}/api/user/pfp`, {
|
||||
method: "POST",
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
body: formData,
|
||||
});
|
||||
const data = await parseJsonResponse(res);
|
||||
if (!res.ok) {
|
||||
return { success: false, error: (data.error as string) ?? `Upload failed (${res.status})` };
|
||||
}
|
||||
|
||||
const pfpUrl = data.pfpUrl as string | undefined;
|
||||
const user = cloudAuth.state.user;
|
||||
if (user && pfpUrl) {
|
||||
await cloudAuth.setUser({ ...user, pfpUrl: cacheBustPfpUrl(pfpUrl) });
|
||||
}
|
||||
return { success: true };
|
||||
} catch (err) {
|
||||
return {
|
||||
success: false,
|
||||
error: err instanceof Error ? err.message : "Cloud profile picture sync failed",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/** Notify SEQTA content scripts to refresh the in-page profile image. */
|
||||
export async function notifyProfilePictureChanged(): Promise<void> {
|
||||
const revision = Date.now();
|
||||
await browser.storage.local.set({ profile_picture_revision: revision });
|
||||
}
|
||||
Reference in New Issue
Block a user