From 2c7ff3d0ba8347ee46418c402af7b11e4e0d2eca Mon Sep 17 00:00:00 2001 From: bdbch Date: Thu, 23 Apr 2026 16:12:56 +0200 Subject: [PATCH] Add reset seeded data command and update seeds Add a Tauri command reset_seeded_data that clears articles, feeds, folders and app_state, reseeds sidebar data and returns seeded counts. Expose a JS API, global window hook and a sidebar store action to invoke it and reload the sidebar. Update default seeded folders and feeds (Technology, News, AI and several tech/news feeds). --- src-tauri/src/db.rs | 150 +++++++++++++++++++++++++++++++------ src-tauri/src/lib.rs | 13 +++- src/global.d.ts | 7 ++ src/lib/sidebarApi.ts | 4 + src/main.tsx | 3 + src/stores/sidebarStore.ts | 15 ++++ 6 files changed, 166 insertions(+), 26 deletions(-) create mode 100644 src/global.d.ts diff --git a/src-tauri/src/db.rs b/src-tauri/src/db.rs index 4a7925e..d006006 100644 --- a/src-tauri/src/db.rs +++ b/src-tauri/src/db.rs @@ -134,6 +134,13 @@ pub struct DeleteFeedResult { pub feed_id: String, } +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct ResetSeededDataResult { + pub folders_count: usize, + pub feeds_count: usize, +} + #[derive(Debug, Clone)] struct FeedFetchTarget { id: String, @@ -471,6 +478,47 @@ pub fn delete_feed(db_path: &PathBuf, feed_id: &str) -> Result Result { + let mut connection = Connection::open(db_path) + .map_err(|error| format!("failed to open database: {error}"))?; + let transaction = connection + .transaction() + .map_err(|error| format!("failed to start reset transaction: {error}"))?; + + transaction + .execute("DELETE FROM articles", []) + .map_err(|error| format!("failed to clear articles: {error}"))?; + transaction + .execute("DELETE FROM feeds", []) + .map_err(|error| format!("failed to clear feeds: {error}"))?; + transaction + .execute("DELETE FROM folders", []) + .map_err(|error| format!("failed to clear folders: {error}"))?; + transaction + .execute("DELETE FROM app_state", []) + .map_err(|error| format!("failed to clear app state: {error}"))?; + + seed_sidebar_data(&transaction)?; + + let folders_count = transaction + .query_row("SELECT COUNT(*) FROM folders", [], |row| row.get::<_, i64>(0)) + .map_err(|error| format!("failed to count seeded folders: {error}"))? + as usize; + let feeds_count = transaction + .query_row("SELECT COUNT(*) FROM feeds", [], |row| row.get::<_, i64>(0)) + .map_err(|error| format!("failed to count seeded feeds: {error}"))? + as usize; + + transaction + .commit() + .map_err(|error| format!("failed to commit reset transaction: {error}"))?; + + Ok(ResetSeededDataResult { + folders_count, + feeds_count, + }) +} + pub async fn refetch_feed( db_path: &PathBuf, sync_state: Arc, @@ -1183,55 +1231,109 @@ fn seed_sidebar_data(connection: &Connection) -> Result<(), String> { connection .execute( "INSERT INTO folders (id, name, parent_folder_id, sort_order, created_at, updated_at) VALUES (?1, ?2, ?3, ?4, ?5, ?6)", - params!["folder-tech", "Tech", Option::::None, 0, now, now], + params!["folder-technology", "Technology", Option::::None, 0, now, now], + ) + .map_err(|error| format!("failed to seed folder Technology: {error}"))?; + connection + .execute( + "INSERT INTO folders (id, name, parent_folder_id, sort_order, created_at, updated_at) VALUES (?1, ?2, ?3, ?4, ?5, ?6)", + params!["folder-news", "News", Option::::None, 1, now, now], ) - .map_err(|error| format!("failed to seed folder Tech: {error}"))?; + .map_err(|error| format!("failed to seed folder News: {error}"))?; connection .execute( "INSERT INTO folders (id, name, parent_folder_id, sort_order, created_at, updated_at) VALUES (?1, ?2, ?3, ?4, ?5, ?6)", - params!["folder-design", "Design", Option::::None, 1, now, now], + params!["folder-technology-ai", "AI", Some("folder-technology"), 0, now, now], ) - .map_err(|error| format!("failed to seed folder Design: {error}"))?; + .map_err(|error| format!("failed to seed folder AI: {error}"))?; connection .execute( "INSERT INTO folders (id, name, parent_folder_id, sort_order, created_at, updated_at) VALUES (?1, ?2, ?3, ?4, ?5, ?6)", - params!["folder-devtools", "Dev Tools", Some("folder-tech"), 0, now, now], + params!["folder-technology-news", "News", Some("folder-technology"), 1, now, now], ) - .map_err(|error| format!("failed to seed folder Dev Tools: {error}"))?; + .map_err(|error| format!("failed to seed folder Technology/News: {error}"))?; let feeds = [ ( - "feed-ars", - "Ars Technica", - "https://feeds.arstechnica.com/arstechnica/index", - Some("https://arstechnica.com"), - Some("folder-tech"), + "feed-openai", + "OpenAI", + "https://openai.com/news/rss.xml", + Some("https://openai.com/news"), + Some("folder-technology-ai"), 0, ), ( - "feed-tauri", - "Tauri Blog", - "https://tauri.app/blog/rss.xml", - Some("https://tauri.app"), - Some("folder-devtools"), - 0, + "feed-google-ai", + "Google AI", + "https://blog.google/innovation-and-ai/technology/ai/rss/", + Some("https://blog.google/innovation-and-ai/technology/ai/"), + Some("folder-technology-ai"), + 1, ), ( - "feed-verge", + "feed-the-verge", "The Verge", "https://www.theverge.com/rss/index.xml", Some("https://www.theverge.com"), - Option::<&str>::None, + Some("folder-technology-news"), 0, ), ( - "feed-figma", - "Figma", - "https://www.figma.com/blog/feed.xml", - Some("https://www.figma.com/blog"), - Some("folder-design"), + "feed-wired", + "WIRED", + "https://www.wired.com/feed/rss", + Some("https://www.wired.com"), + Some("folder-technology-news"), + 1, + ), + ( + "feed-techcrunch", + "TechCrunch", + "https://techcrunch.com/feed/", + Some("https://techcrunch.com"), + Some("folder-technology-news"), + 2, + ), + ( + "feed-bbc-news", + "BBC News", + "http://feeds.bbci.co.uk/news/rss.xml", + Some("https://www.bbc.com/news"), + Some("folder-news"), 0, ), + ( + "feed-new-york-times", + "The New York Times", + "https://rss.nytimes.com/services/xml/rss/nyt/HomePage.xml", + Some("https://www.nytimes.com"), + Some("folder-news"), + 1, + ), + ( + "feed-cnn-news", + "CNN News", + "http://rss.cnn.com/rss/edition_world.rss", + Some("https://www.cnn.com/world"), + Some("folder-news"), + 2, + ), + ( + "feed-ndtv", + "NDTV", + "https://feeds.feedburner.com/ndtvnews-world-news", + Some("https://www.ndtv.com/world-news"), + Some("folder-news"), + 3, + ), + ( + "feed-the-guardian", + "The Guardian", + "https://www.theguardian.com/world/rss", + Some("https://www.theguardian.com/world"), + Some("folder-news"), + 4, + ), ]; for (id, title, url, site_url, folder_id, sort_order) in feeds { diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 97e2d6c..49eb51f 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -1,6 +1,7 @@ mod db; use db::{ + reset_seeded_data as reset_app_seeded_data, get_sidebar_data as load_sidebar_data, init_database, list_articles as load_articles, load_sidebar_expansion_state as read_sidebar_expansion_state, @@ -9,7 +10,7 @@ use db::{ save_sidebar_expansion_state as persist_sidebar_expansion_state, save_sidebar_structure as persist_sidebar_structure, start_background_feed_sync, AppState, FeedMoveInput, FolderMoveInput, ListArticlesInput, RefetchFeedResult, SidebarData, - ArticlePage, DeleteFeedResult, FeedSyncState, + ArticlePage, DeleteFeedResult, FeedSyncState, ResetSeededDataResult, }; use std::collections::HashMap; use std::sync::Arc; @@ -68,6 +69,13 @@ fn delete_feed( remove_feed(&state.db_path, &feed_id) } +#[tauri::command] +fn reset_seeded_data( + state: tauri::State<'_, AppState>, +) -> Result { + reset_app_seeded_data(&state.db_path) +} + #[cfg_attr(mobile, tauri::mobile_entry_point)] pub fn run() { tauri::Builder::default() @@ -94,7 +102,8 @@ pub fn run() { save_sidebar_expansion_state, list_articles, refetch_feed, - delete_feed + delete_feed, + reset_seeded_data ]) .run(tauri::generate_context!()) .expect("error while running tauri application"); diff --git a/src/global.d.ts b/src/global.d.ts new file mode 100644 index 0000000..6967499 --- /dev/null +++ b/src/global.d.ts @@ -0,0 +1,7 @@ +export {}; + +declare global { + interface Window { + resetReleaderData: () => Promise<{ foldersCount: number; feedsCount: number }>; + } +} diff --git a/src/lib/sidebarApi.ts b/src/lib/sidebarApi.ts index ea5a216..a092599 100644 --- a/src/lib/sidebarApi.ts +++ b/src/lib/sidebarApi.ts @@ -26,3 +26,7 @@ export async function saveSidebarStructure({ export async function deleteFeed(feedId: string) { return invoke<{ feedId: string }>("delete_feed", { feedId }); } + +export async function resetSeededData() { + return invoke<{ foldersCount: number; feedsCount: number }>("reset_seeded_data"); +} diff --git a/src/main.tsx b/src/main.tsx index 2be325e..bcf8804 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -1,6 +1,9 @@ import React from "react"; import ReactDOM from "react-dom/client"; import App from "./App"; +import { useSidebarStore } from "@/stores/sidebarStore"; + +window.resetReleaderData = () => useSidebarStore.getState().resetToSeededData(); ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render( diff --git a/src/stores/sidebarStore.ts b/src/stores/sidebarStore.ts index fd21e49..66f2b32 100644 --- a/src/stores/sidebarStore.ts +++ b/src/stores/sidebarStore.ts @@ -2,6 +2,7 @@ import { create } from "zustand"; import { deleteFeed, getSidebarData, + resetSeededData, saveSidebarStructure, } from "@/lib/sidebarApi"; import { @@ -24,6 +25,7 @@ type SidebarState = { setSidebarStructure: (folders: FolderRecord[], feeds: FeedRecord[]) => void; persistSidebarStructure: () => Promise; removeFeed: (feedId: string) => Promise; + resetToSeededData: () => Promise<{ foldersCount: number; feedsCount: number }>; }; export const useSidebarStore = create((set, get) => ({ @@ -105,6 +107,19 @@ export const useSidebarStore = create((set, get) => ({ set({ hasLoaded: false }); await get().loadSidebarData(); }, + resetToSeededData: async () => { + const result = await resetSeededData(); + set({ + folders: [], + feeds: [], + expandedFolderIds: {}, + error: null, + hasLoaded: false, + isLoading: false, + }); + await get().loadSidebarData(); + return result; + }, })); function getDescendantFolderIds( -- 2.51.2