From 843c34a287ca3199d94aad2c796ffed450ad3778 Mon Sep 17 00:00:00 2001 From: Maaz Khokhar Date: Sun, 28 Jun 2026 16:53:04 -0500 Subject: [PATCH] adding works, viewing works, but viewing specific blogs are not implemented yet. --- app/write/[id]/page.tsx | 3 ++ app/write/page.tsx | 26 ++++++++++++- components/blog-card.tsx | 84 ++++++++++++++++++++++++++++++++++++++++ src-tauri/src/db/mod.rs | 17 ++++++++ src-tauri/src/lib.rs | 40 ++++++++++++++++++- types.d.ts | 7 ++++ 6 files changed, 175 insertions(+), 2 deletions(-) create mode 100644 app/write/[id]/page.tsx create mode 100644 components/blog-card.tsx diff --git a/app/write/[id]/page.tsx b/app/write/[id]/page.tsx new file mode 100644 index 0000000..e9d8d6b --- /dev/null +++ b/app/write/[id]/page.tsx @@ -0,0 +1,3 @@ +export default function ViewBlogPage() { + // This will take a blog id as a parameter and fetch the blog from the database using the get_blog_by_id command. +} diff --git a/app/write/page.tsx b/app/write/page.tsx index 40d7563..b889f31 100644 --- a/app/write/page.tsx +++ b/app/write/page.tsx @@ -1,7 +1,31 @@ +"use client"; + +import { invoke } from "@tauri-apps/api/core"; +import { Blog } from "@/types"; +import { useEffect, useState } from "react"; +import BlogCard from "@/components/blog-card"; + export default function ViewBlogPage() { + const [blogs, setBlogs] = useState([]); + + useEffect(() => { + const fetchBlogs = async () => { + return await invoke("get_blogs"); + }; + + fetchBlogs().then((blogs) => { + console.log("Blogs:", blogs); + setBlogs(blogs as Blog[]); + }); + }, []); + return (
-

You can view blogs here...

+
+ {blogs.map((blog) => ( + + ))} +
); } diff --git a/components/blog-card.tsx b/components/blog-card.tsx new file mode 100644 index 0000000..2656cf1 --- /dev/null +++ b/components/blog-card.tsx @@ -0,0 +1,84 @@ +"use client"; + +import { Blog, HomelabApp } from "@/types"; +import { useEffect, useState } from "react"; +import { invoke } from "@tauri-apps/api/core"; +import { useRouter } from "next/navigation"; + +interface BlogCardProps { + blog: Blog & { linked_app_ids?: string[] }; +} + +export default function BlogCard({ blog }: BlogCardProps) { + const [referencedApps, setReferencedApps] = useState([]); + const appIds = blog.linked_app_ids; + + const router = useRouter(); + + useEffect(() => { + if (appIds && appIds.length > 0) { + const fetchReferences = async () => { + try { + const apps = await invoke("get_apps_by_id", { + ids: appIds, + }); + setReferencedApps(apps); + } catch (err) { + console.error("Failed to fetch referenced apps:", err); + } + }; + fetchReferences(); + } + }, [appIds]); + + return ( +
router.push(`/write/${blog.id}`)} + > + {/* Decorative background glow that triggers on hover */} +
+ +
+ {/* Category / Meta indicator */} + + Homelab Log + + + {/* Title */} +

+ {blog.title} +

+ + {/* Truncated Tiptap HTML Content */} +
+
+ + {/* Footer / References */} +
+ {!appIds || appIds.length === 0 ? ( + + No linked applications + + ) : ( + referencedApps.map((app) => ( + + {/* App Avatar Placeholder (Uses first letter of app name) */} + + {app.name.charAt(0)} + + {app.name} + + )) + )} +
+
+ ); +} diff --git a/src-tauri/src/db/mod.rs b/src-tauri/src/db/mod.rs index 20d7f43..5671e94 100644 --- a/src-tauri/src/db/mod.rs +++ b/src-tauri/src/db/mod.rs @@ -137,3 +137,20 @@ pub fn save_blog( create_blog_db(conn, title, content, linked_app_ids) } } + +pub fn get_all_blogs(conn: &mut SqliteConnection) -> Vec { + use crate::db::schema::blog; + + blog::table + .load::(conn) + .expect("Error loading apps") +} + +pub fn get_apps_by_ids(conn: &mut SqliteConnection, ids: &[String]) -> Vec { + use crate::db::schema::app; + + app::table + .filter(app::id.eq_any(ids)) + .load::(conn) + .expect("Error loading apps by ids") +} diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index ba7f775..2d581ef 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -74,6 +74,36 @@ async fn ping(address: &str) -> Result { } } +#[derive(Debug, serde::Serialize, serde::Deserialize)] +struct TempBlog { + id: String, + title: String, + content: String, + linked_app_ids: Option>, +} + +#[tauri::command] +fn get_blogs() -> Vec { + // I'll need to decode the linked_app_ids from a json encoded string to a Vec. + let mut conn = establish_connection(); + let encoded_blogs = db::get_all_blogs(&mut conn); + let mut decoded_blogs: Vec = Vec::new(); + encoded_blogs.iter().for_each(|blog| { + let linked_app_ids: Option> = match &blog.reference { + Some(ids) => serde_json::from_str(ids).ok(), + None => None, + }; + decoded_blogs.push(TempBlog { + id: blog.id.clone(), + title: blog.title.clone(), + content: blog.content.clone(), + linked_app_ids, + }); + }); + + decoded_blogs +} + #[tauri::command] fn save_blog( id: Option<&str>, @@ -85,6 +115,12 @@ fn save_blog( db::save_blog(&mut conn, id, title, content, linked_app_ids.as_deref()) } +#[tauri::command] +fn get_apps_by_id(ids: Vec) -> Vec { + let mut conn = establish_connection(); + db::get_apps_by_ids(&mut conn, &ids) +} + #[cfg_attr(mobile, tauri::mobile_entry_point)] pub fn run() { tauri::Builder::default() @@ -94,7 +130,9 @@ pub fn run() { get_app, delete_apps, ping, - save_blog + get_blogs, + save_blog, + get_apps_by_id ]) .run(tauri::generate_context!()) .expect("error while running tauri application"); diff --git a/types.d.ts b/types.d.ts index 6bc6fa7..ded567e 100644 --- a/types.d.ts +++ b/types.d.ts @@ -6,6 +6,13 @@ export interface HomelabApp { ssh_address: string | null; } +export interface Blog { + id: string; + title: string; + content: string; + reference: String[]; // list of app ids +} + // Defines the custom attributes Tiptap will store in the DOM/JSON schema export interface AppMentionAttributes { id: string;