mirror of
https://github.com/BetterSEQTA/BetterSEQTA-Plus.git
synced 2026-06-06 03:34:40 +00:00
feat: implement cloud store
This commit is contained in:
+29
-1
@@ -14,8 +14,8 @@ function reloadSeqtaPages() {
|
||||
result.then(open, console.error);
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
browser.runtime.onMessage.addListener(
|
||||
// @ts-ignore - OnMessageListener expects literal true for async, we return boolean
|
||||
(request: any, _: any, sendResponse: (response?: any) => void) => {
|
||||
switch (request.type) {
|
||||
case "reloadTabs":
|
||||
@@ -56,6 +56,34 @@ browser.runtime.onMessage.addListener(
|
||||
fetchNews(request.source ?? "australia", sendResponse);
|
||||
return true;
|
||||
|
||||
case "fetchThemes": {
|
||||
const url = `https://betterseqta.org/api/themes?type=betterseqta&limit=100&nocache=${Date.now()}`;
|
||||
fetch(url, { cache: "no-store" })
|
||||
.then((r) => r.json())
|
||||
.then(sendResponse)
|
||||
.catch((err) => {
|
||||
console.error("[Background] fetchThemes error:", err);
|
||||
sendResponse({ success: false, error: err?.message });
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
case "fetchFromUrl": {
|
||||
const { url } = request;
|
||||
if (!url || typeof url !== "string") {
|
||||
sendResponse({ error: "Missing url" });
|
||||
return false;
|
||||
}
|
||||
fetch(url, { cache: "no-store" })
|
||||
.then((r) => r.json())
|
||||
.then((data) => sendResponse({ data }))
|
||||
.catch((err) => {
|
||||
console.error("[Background] fetchFromUrl error:", err);
|
||||
sendResponse({ error: err?.message });
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
default:
|
||||
console.log("Unknown request type");
|
||||
}
|
||||
|
||||
@@ -48,20 +48,26 @@
|
||||
activeTab = tab;
|
||||
};
|
||||
|
||||
// Fetch themes and initialize app
|
||||
// Fetch themes via background script (avoids CORS when store runs inside SEQTA page)
|
||||
const fetchThemes = async () => {
|
||||
try {
|
||||
const response = await fetch(`https://raw.githubusercontent.com/BetterSEQTA/BetterSEQTA-Themes/main/store/themes.json?nocache=${(new Date()).getTime()}`, { cache: 'no-store' });
|
||||
const data = await response.json();
|
||||
themes = data.themes;
|
||||
const data = (await browser.runtime.sendMessage({ type: 'fetchThemes' })) as {
|
||||
success?: boolean;
|
||||
data?: { themes: Theme[] };
|
||||
error?: string;
|
||||
};
|
||||
if (!data?.success || !data?.data?.themes) {
|
||||
throw new Error(data?.error || 'Failed to fetch themes');
|
||||
}
|
||||
themes = data.data.themes;
|
||||
|
||||
// Shuffle for cover themes
|
||||
const shuffled = [...themes].sort(() => 0.5 - Math.random());
|
||||
coverThemes = shuffled.slice(0, 3);
|
||||
|
||||
loading = false;
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch themes', error);
|
||||
} catch (err) {
|
||||
console.error('Failed to fetch themes', err);
|
||||
setTimeout(fetchThemes, 5000); // Retry after 5 seconds if failure occurs
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
export type Theme = {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
coverImage: string;
|
||||
marqueeImage: string;
|
||||
id: string;
|
||||
marqueeImage?: string;
|
||||
theme_json_url?: string;
|
||||
};
|
||||
|
||||
@@ -16,12 +16,12 @@
|
||||
}
|
||||
},
|
||||
"permissions": ["tabs", "notifications", "storage"],
|
||||
"host_permissions": ["https://newsapi.org/", "*://*/*"],
|
||||
"host_permissions": ["https://newsapi.org/", "https://betterseqta.org/", "*://*/*"],
|
||||
"background": {
|
||||
"service_worker": "background.ts"
|
||||
},
|
||||
"content_security_policy": {
|
||||
"extension_pages": "script-src 'self'; object-src 'self'"
|
||||
"extension_pages": "script-src 'self'; object-src 'self'; connect-src 'self' https://betterseqta.org"
|
||||
},
|
||||
"content_scripts": [
|
||||
{
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import localforage from "localforage";
|
||||
import browser from "webextension-polyfill";
|
||||
import type { CustomTheme, LoadedCustomTheme } from "@/types/CustomThemes";
|
||||
import { settingsState } from "@/seqta/utils/listeners/SettingsState";
|
||||
import debounce from "@/seqta/utils/debounce";
|
||||
@@ -470,23 +471,50 @@ export class ThemeManager {
|
||||
}
|
||||
}
|
||||
|
||||
private readonly THEME_API_BASE = 'https://betterseqta.org/api';
|
||||
|
||||
/**
|
||||
* Fetch JSON from a URL via background script (avoids CORS when running inside SEQTA page)
|
||||
*/
|
||||
private async fetchFromUrl(url: string): Promise<any> {
|
||||
const result = (await browser.runtime.sendMessage({
|
||||
type: 'fetchFromUrl',
|
||||
url,
|
||||
})) as { data?: unknown; error?: string };
|
||||
if (result?.error) throw new Error(result.error);
|
||||
return result?.data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Download and install a theme from the store
|
||||
*/
|
||||
public async downloadTheme(themeContent: {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
coverImage: string;
|
||||
description?: string;
|
||||
coverImage?: string;
|
||||
theme_json_url?: string;
|
||||
}): Promise<void> {
|
||||
console.debug("[ThemeManager] Downloading theme:", themeContent.name);
|
||||
try {
|
||||
if (!themeContent.id) return;
|
||||
|
||||
const response = await fetch(
|
||||
`https://raw.githubusercontent.com/BetterSEQTA/BetterSEQTA-Themes/main/store/themes/${themeContent.id}/theme.json`,
|
||||
);
|
||||
const themeData = (await response.json()) as ThemeContent;
|
||||
let themeJsonUrl: string;
|
||||
|
||||
// Use theme_json_url if provided (from API list), otherwise call download endpoint
|
||||
if (themeContent.theme_json_url) {
|
||||
themeJsonUrl = themeContent.theme_json_url;
|
||||
} else {
|
||||
const downloadData = await this.fetchFromUrl(
|
||||
`${this.THEME_API_BASE}/themes/${themeContent.id}/download`
|
||||
) as { success?: boolean; data?: { theme_json_url: string } };
|
||||
if (!downloadData?.success || !downloadData?.data?.theme_json_url) {
|
||||
throw new Error("Failed to get theme download URL");
|
||||
}
|
||||
themeJsonUrl = downloadData.data.theme_json_url;
|
||||
}
|
||||
|
||||
const themeData = (await this.fetchFromUrl(themeJsonUrl)) as ThemeContent;
|
||||
|
||||
await this.installTheme(themeData);
|
||||
} catch (error) {
|
||||
|
||||
Reference in New Issue
Block a user