diff --git a/apps/extension/src/SideBar.tsx b/apps/extension/src/SideBar.tsx index 9704511b..385c0f22 100644 --- a/apps/extension/src/SideBar.tsx +++ b/apps/extension/src/SideBar.tsx @@ -79,7 +79,7 @@ function SideBar({ jwt }: { jwt: string }) { const fetchSpaces = async () => { setLoading(true); chrome.runtime.sendMessage({ type: "fetchSpaces" }, (resp) => { - console.log(resp); + console.log("response", resp); setSpaces(resp); setLoading(false); }); diff --git a/apps/extension/src/background.ts b/apps/extension/src/background.ts index ec71810b..d2f8759e 100644 --- a/apps/extension/src/background.ts +++ b/apps/extension/src/background.ts @@ -52,12 +52,13 @@ chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { const spaces = request.spaces( // eslint-disable-next-line no-unexpected-multiline async () => { - chrome.storage.local.get(["jwt"], async ({ jwt }) => { + chrome.storage.local.get(["jwt"], ({ jwt }) => { if (!jwt) { console.error("No JWT found"); return; } - await fetch(`${backendUrl}/api/spaces`, { + fetch(`${backendUrl}/api/store`, { + method: "POST", headers: { Authorization: `Bearer ${jwt}`, }, @@ -67,29 +68,26 @@ chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { }, )(); } else if (request.type === "fetchSpaces") { - const run = () => - chrome.storage.local.get(["jwt"], async ({ jwt }) => { - if (!jwt) { - console.error("No JWT found"); - return; - } - const resp = await fetch(`${backendUrl}/api/spaces`, { - headers: { - Authorization: `Bearer ${jwt}`, - }, - }); - - const data: { - message: "OK" | string; - data: Space[] | undefined; - } = await resp.json(); - - if (data.message === "OK" && data.data) { - sendResponse(data.data); - } + chrome.storage.local.get(["jwt"], async ({ jwt }) => { + if (!jwt) { + console.error("No JWT found"); + return; + } + const resp = await fetch(`${backendUrl}/api/spaces`, { + headers: { + Authorization: `Bearer ${jwt}`, + }, }); - run(); + const data: { + message: "OK" | string; + data: Space[] | undefined; + } = await resp.json(); + + if (data.message === "OK" && data.data) { + sendResponse(data.data); + } + }); return true; } else if (request.type === "queryApi") { diff --git a/apps/web/public/note.svg b/apps/web/public/note.svg new file mode 100644 index 00000000..ffb4c795 --- /dev/null +++ b/apps/web/public/note.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/apps/web/public/web.svg b/apps/web/public/web.svg index dffbed97..c59f016d 100644 --- a/apps/web/public/web.svg +++ b/apps/web/public/web.svg @@ -1,3 +1,3 @@ - \ No newline at end of file + diff --git a/apps/web/src/actions/db.ts b/apps/web/src/actions/db.ts index e380d562..cd54c1bd 100644 --- a/apps/web/src/actions/db.ts +++ b/apps/web/src/actions/db.ts @@ -41,7 +41,8 @@ export async function searchMemoriesAndSpaces( eq(storedContent.user, user.id), like(storedContent.title, `%${query}%`), ), - ); + ) + .orderBy(asc(storedContent.savedAt)); const searchSpacesQuery = db .select({ @@ -50,10 +51,13 @@ export async function searchMemoriesAndSpaces( memory: sql`NULL`, }) .from(space) - .where(and(eq(space.user, user.id), like(space.name, `%${query}%`))); + .where(and(eq(space.user, user.id), like(space.name, `%${query}%`))) + .orderBy(asc(space.name)); let queries = []; + console.log("adding"); + [undefined, true].includes(opts?.filter?.memories) && queries.push(searchMemoriesQuery); [undefined, true].includes(opts?.filter?.spaces) && @@ -69,6 +73,8 @@ export async function searchMemoriesAndSpaces( const data = await Promise.all(queries); + console.log("resp", data); + return data.reduce((acc, i) => [...acc, ...i]) as SearchResult[]; } catch { return []; @@ -183,7 +189,7 @@ export async function fetchContentForSpace( ), ), ) - .orderBy(asc(storedContent.title)); + .orderBy(asc(storedContent.savedAt)); return range ? await query.limit(range.limit).offset(range.offset) @@ -214,7 +220,7 @@ export async function fetchFreeMemories(range?: { eq(storedContent.user, user.id), ), ) - .orderBy(asc(storedContent.title)); + .orderBy(asc(storedContent.savedAt)); return range ? await query.limit(range.limit).offset(range.offset) @@ -257,3 +263,37 @@ export async function addMemory( addedToSpaces, }; } + +export async function deleteSpace(id: number) { + const user = await getUser(); + + if (!user) { + return null; + } + + await db.delete(contentToSpace).where(eq(contentToSpace.spaceId, id)); + + const [deleted] = await db + .delete(space) + .where(and(eq(space.user, user.id), eq(space.id, id))) + .returning(); + + return deleted; +} + +export async function deleteMemory(id: number) { + const user = await getUser(); + + if (!user) { + return null; + } + + await db.delete(contentToSpace).where(eq(contentToSpace.contentId, id)); + + const [deleted] = await db + .delete(storedContent) + .where(and(eq(storedContent.user, user.id), eq(storedContent.id, id))) + .returning(); + + return deleted; +} diff --git a/apps/web/src/app/page.tsx b/apps/web/src/app/page.tsx index 3112e71e..1cc21adf 100644 --- a/apps/web/src/app/page.tsx +++ b/apps/web/src/app/page.tsx @@ -18,8 +18,6 @@ import { } from "../../types/memory"; import { MemoryProvider } from "@/contexts/MemoryContext"; import Content from "./content"; -import { searchMemoriesAndSpaces } from "@/actions/db"; -import { getMetaData } from "@/server/helpers"; export const runtime = "edge"; diff --git a/apps/web/src/components/Sidebar/AddMemoryDialog.tsx b/apps/web/src/components/Sidebar/AddMemoryDialog.tsx index 3043c972..31628ab7 100644 --- a/apps/web/src/components/Sidebar/AddMemoryDialog.tsx +++ b/apps/web/src/components/Sidebar/AddMemoryDialog.tsx @@ -324,12 +324,18 @@ export function MemorySelectedItem({ id, title, url, + type, image, onRemove, }: StoredContent & { onRemove: () => void }) { return (
- + {title} - {cleanUrl(url)} + + {type === "note" ? "Note" : cleanUrl(url)} +
); } diff --git a/apps/web/src/components/Sidebar/FilterCombobox.tsx b/apps/web/src/components/Sidebar/FilterCombobox.tsx index 80319ab1..c2c68135 100644 --- a/apps/web/src/components/Sidebar/FilterCombobox.tsx +++ b/apps/web/src/components/Sidebar/FilterCombobox.tsx @@ -180,7 +180,6 @@ export function FilterMemories({ const [isSearching, setIsSearching] = React.useState(false); const results = React.useMemo(() => { - console.log("use memo"); return searchResults.map((r) => r.memory); }, [searchResults]); @@ -266,7 +265,11 @@ export function FilterMemories({ >
{m.title} diff --git a/apps/web/src/components/Sidebar/MemoriesBar.tsx b/apps/web/src/components/Sidebar/MemoriesBar.tsx index 0c468475..052098db 100644 --- a/apps/web/src/components/Sidebar/MemoriesBar.tsx +++ b/apps/web/src/components/Sidebar/MemoriesBar.tsx @@ -46,7 +46,6 @@ import { ExpandedSpace } from "./ExpandedSpace"; import { StoredContent, StoredSpace } from "@/server/db/schema"; import Image from "next/image"; import { useDebounce } from "@/hooks/useDebounce"; -import { searchMemoriesAndSpaces } from "@/actions/db"; export function MemoriesBar() { const [parent, enableAnimations] = useAutoAnimate(); @@ -169,7 +168,7 @@ export function MemoriesBar() { <> {spaces.map((space) => ( {}} + onDelete={() => deleteSpace(space.id)} key={space.id} //onClick={() => setExpandedSpace(space.id)} {...space} @@ -255,7 +254,6 @@ export function SpaceItem({ }, [cachedMemories]); const _name = name.length > 10 ? name.slice(0, 10) + "..." : name; - return ( c.image).reverse() as string[]} + images={ + spaceMemories + .map((c) => (c.type === "note" ? "/note.svg" : c.image)) + .reverse() as string[] + } /> ) : spaceMemories.length > 1 ? ( c.image).reverse() as string[]} + images={ + spaceMemories + .map((c) => (c.type === "note" ? "/note.svg" : c.image)) + .reverse() as string[] + } /> ) : spaceMemories.length === 1 ? ( ) : (
diff --git a/apps/web/src/contexts/MemoryContext.tsx b/apps/web/src/contexts/MemoryContext.tsx index 881ba45e..548365f8 100644 --- a/apps/web/src/contexts/MemoryContext.tsx +++ b/apps/web/src/contexts/MemoryContext.tsx @@ -11,6 +11,8 @@ import { searchMemoriesAndSpaces, addSpace, fetchContentForSpace, + deleteSpace, + deleteMemory, } from "@/actions/db"; import { User } from "next-auth"; @@ -23,20 +25,22 @@ export type SearchResult = { // temperory (will change) export const MemoryContext = React.createContext<{ spaces: StoredSpace[]; - deleteSpace: (id: number) => Promise; freeMemories: StoredContent[]; addSpace: typeof addSpace; addMemory: typeof addMemory; cachedMemories: ChachedSpaceContent[]; search: typeof searchMemoriesAndSpaces; + deleteSpace: typeof deleteSpace; + deleteMemory: typeof deleteMemory; }>({ spaces: [], freeMemories: [], addMemory: (() => {}) as unknown as typeof addMemory, addSpace: (async () => {}) as unknown as typeof addSpace, - deleteSpace: async () => {}, cachedMemories: [], search: async () => [], + deleteMemory: (() => {}) as unknown as typeof deleteMemory, + deleteSpace: (() => {}) as unknown as typeof deleteSpace, }); export const MemoryProvider: React.FC< @@ -61,8 +65,22 @@ export const MemoryProvider: React.FC< ChachedSpaceContent[] >(initialCachedMemories); - const deleteSpace = async (id: number) => { - setSpaces((prev) => prev.filter((s) => s.id !== id)); + const _deleteSpace: typeof deleteSpace = async (...params) => { + const deleted = (await deleteSpace(...params))!; + + setSpaces((prev) => prev.filter((i) => i.id !== deleted.id)); + setCachedMemories((prev) => prev.filter((i) => i.space !== deleted.id)); + + return deleted; + }; + + const _deleteMemory: typeof deleteMemory = async (...params) => { + const deleted = (await deleteMemory(...params))!; + + setCachedMemories((prev) => prev.filter((i) => i.id !== deleted.id)); + setFreeMemories((prev) => prev.filter((i) => i.id !== deleted.id)); + + return deleted; }; // const fetchMemories = useCallback(async (query: string) => { @@ -115,9 +133,10 @@ export const MemoryProvider: React.FC< search: searchMemoriesAndSpaces, spaces, addSpace: _addSpace, - deleteSpace, + deleteSpace: _deleteSpace, freeMemories, cachedMemories, + deleteMemory: _deleteMemory, addMemory: _addMemory, }} > diff --git a/apps/web/src/server/db/schema.ts b/apps/web/src/server/db/schema.ts index 4ca4332f..cd2756f1 100644 --- a/apps/web/src/server/db/schema.ts +++ b/apps/web/src/server/db/schema.ts @@ -34,7 +34,7 @@ export const accounts = createTable( id: integer("id").notNull().primaryKey({ autoIncrement: true }), userId: text("userId", { length: 255 }) .notNull() - .references(() => users.id), + .references(() => users.id, { onDelete: "cascade" }), type: text("type", { length: 255 }).notNull(), provider: text("provider", { length: 255 }).notNull(), providerAccountId: text("providerAccountId", { length: 255 }).notNull(), @@ -60,7 +60,7 @@ export const sessions = createTable( sessionToken: text("sessionToken", { length: 255 }).notNull(), userId: text("userId", { length: 255 }) .notNull() - .references(() => users.id), + .references(() => users.id, { onDelete: "cascade" }), expires: int("expires", { mode: "timestamp" }).notNull(), }, (session) => ({ @@ -94,7 +94,9 @@ export const storedContent = createTable( "page", ), image: text("image", { length: 255 }), - user: text("user", { length: 255 }).references(() => users.id), + user: text("user", { length: 255 }).references(() => users.id, { + onDelete: "cascade", + }), }, (sc) => ({ urlIdx: index("storedContent_url_idx").on(sc.url), @@ -109,10 +111,10 @@ export const contentToSpace = createTable( { contentId: integer("contentId") .notNull() - .references(() => storedContent.id), + .references(() => storedContent.id, { onDelete: "cascade" }), spaceId: integer("spaceId") .notNull() - .references(() => space.id), + .references(() => space.id, { onDelete: "cascade" }), }, (cts) => ({ compoundKey: primaryKey({ columns: [cts.contentId, cts.spaceId] }), @@ -124,7 +126,9 @@ export const space = createTable( { id: integer("id").notNull().primaryKey({ autoIncrement: true }), name: text("name").notNull().unique().default("none"), - user: text("user", { length: 255 }).references(() => users.id), + user: text("user", { length: 255 }).references(() => users.id, { + onDelete: "cascade", + }), }, (space) => ({ nameIdx: index("spaces_name_idx").on(space.name),