Skip to content

Commit

Permalink
space add
Browse files Browse the repository at this point in the history
  • Loading branch information
yxshv committed Apr 13, 2024
1 parent 5b34007 commit 0d4e910
Show file tree
Hide file tree
Showing 4 changed files with 144 additions and 26 deletions.
82 changes: 80 additions & 2 deletions apps/web/src/actions/db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ import {
storedContent,
StoredSpace,
users,
space
space,
} from "@/server/db/schema";
import { SearchResult } from "@/contexts/MemoryContext";
import { like, eq, and, sql } from "drizzle-orm";
import { like, eq, and, sql, exists, asc, notExists } from "drizzle-orm";
import { union } from "drizzle-orm/sqlite-core"

// @todo: (future) pagination not yet needed
Expand Down Expand Up @@ -137,3 +137,81 @@ export async function addMemory(
);
return _content;
}

export async function addSpace(name: string, memories: number[]) {

const user = await getUser();

if (!user) {
return null
}

const [addedSpace] = await db
.insert(space)
.values({
name: name,
user: user.id
}).returning();

const addedMemories = memories.length > 0 ? await db.insert(contentToSpace)
.values(memories.map(m => ({
contentId: m,
spaceId: addedSpace.id
}))).returning() : []

return {
space: addedSpace,
addedMemories
}
}


export async function fetchContentForSpace(
spaceId: number,
range?: {
offset: number;
limit: number;
},
) {

const query = db
.select()
.from(storedContent)
.where(
exists(
db.select().from(contentToSpace).where(and(eq(contentToSpace.spaceId, spaceId), eq(contentToSpace.contentId, storedContent.id))),
),
).orderBy(asc(storedContent.title))

return range ? await query.limit(range.limit).offset(range.offset) : await query.all()
}

export async function fetchFreeMemories(
range?: {
offset: number;
limit: number;
}
) {

const user = await getUser()

if (!user) {
return []
}

const query = db
.select()
.from(storedContent)
.where(
and(
notExists(
db.select().from(contentToSpace).where(eq(contentToSpace.contentId, storedContent.id)),
),
eq(storedContent.user, user.id),
)

).orderBy(asc(storedContent.title))

return range ? await query.limit(range.limit).offset(range.offset) : await query.all()

}
32 changes: 26 additions & 6 deletions apps/web/src/components/Sidebar/AddMemoryDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@ import { Markdown } from "tiptap-markdown";
import { useEffect, useRef, useState } from "react";
import { FilterMemories, FilterSpaces } from "./FilterCombobox";
import { useMemory } from "@/contexts/MemoryContext";
import { Command, Plus, X } from "lucide-react";
import { Loader, Plus, X } from "lucide-react";
import { StoredContent } from "@/server/db/schema";
import { cleanUrl } from "@/lib/utils";
import { motion } from "framer-motion"

export function AddMemoryPage() {
const { addMemory } = useMemory();
Expand Down Expand Up @@ -157,8 +158,11 @@ export function NoteAddPage({ closeDialog }: { closeDialog: () => void }) {

export function SpaceAddPage({ closeDialog }: { closeDialog: () => void }) {

const { addSpace } = useMemory()

const inputRef = useRef<HTMLInputElement>(null);
const [name, setName] = useState("");

const [loading, setLoading] = useState(false);

const [selected, setSelected] = useState<StoredContent[]>([]);
Expand Down Expand Up @@ -198,6 +202,7 @@ export function SpaceAddPage({ closeDialog }: { closeDialog: () => void }) {
type="url"
data-modal-autofocus
value={name}
disabled={loading}
onChange={e => setName(e.target.value)}
className="bg-rgray-4 mt-2 w-full focus-visible:data-[error=true]:ring-red-500/10 data-[error=true]:placeholder:text-red-400 placeholder:transition placeholder:duration-500"
/>
Expand All @@ -219,7 +224,8 @@ export function SpaceAddPage({ closeDialog }: { closeDialog: () => void }) {
<FilterMemories
selected={selected}
setSelected={setSelected}
className="mr-auto bg-white/5 hover:bg-rgray-4 focus-visible:bg-rgray-4"
disabled={loading}
className="mr-auto bg-white/5 hover:bg-rgray-4 focus-visible:bg-rgray-4 disabled:opacity-70 disabled:cursor-not-allowed"
>
<Plus className="w-5 h-5" />
Memory
Expand All @@ -228,14 +234,28 @@ export function SpaceAddPage({ closeDialog }: { closeDialog: () => void }) {
type={undefined}
onClick={() => {
if (check()) {

setLoading(true)
addSpace(name, selected.map(s => s.id)).then(() => closeDialog())
}
}}
className="bg-rgray-4 hover:bg-rgray-5 focus-visible:bg-rgray-5 focus-visible:ring-rgray-7 rounded-md px-4 py-2 ring-transparent transition focus-visible:outline-none focus-visible:ring-2"
disabled={loading}
className="relative disabled:opacity-70 disabled:cursor-not-allowed bg-rgray-4 hover:bg-rgray-5 focus-visible:bg-rgray-5 focus-visible:ring-rgray-7 rounded-md px-4 py-2 ring-transparent transition focus-visible:outline-none focus-visible:ring-2"
>
Add
<motion.div
initial={{ x: '-50%', y: '-100%' }}
animate={loading && { y: '-50%', x: '-50%', opacity: 1 }}
className="opacity-0 absolute top-1/2 left-1/2 translate-y-[-100%] -translate-x-1/2"
>
<Loader className="w-5 h-5 animate-spin text-rgray-11" />
</motion.div>
<motion.div
initial={{ y: '0%' }}
animate={loading && { opacity: 0, y: '30%' }}
>
Add
</motion.div>
</button>
<DialogClose className="hover:bg-rgray-4 focus-visible:bg-rgray-4 focus-visible:ring-rgray-7 rounded-md px-3 py-2 ring-transparent transition focus-visible:outline-none focus-visible:ring-2">
<DialogClose disabled={loading} className="disabled:opacity-70 disabled:cursor-not-allowed hover:bg-rgray-4 focus-visible:bg-rgray-4 focus-visible:ring-rgray-7 rounded-md px-3 py-2 ring-transparent transition focus-visible:outline-none focus-visible:ring-2">
Cancel
</DialogClose>
</DialogFooter>
Expand Down
27 changes: 18 additions & 9 deletions apps/web/src/components/Sidebar/MemoriesBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -196,13 +196,16 @@ export function MemoryItem({
title,
image
}: StoredContent) {

const name = title ? title.length > 10 ? title.slice(0, 10) + "..." : title : '<no title>';

return (
<div

className="hover:bg-rgray-2 has-[[data-state='true']]:bg-rgray-2 has-[[data-space-text]:focus-visible]:bg-rgray-2 has-[[data-space-text]:focus-visible]:ring-rgray-7 [&:has-[[data-space-text]:focus-visible]>[data-more-button]]:opacity-100 relative flex select-none flex-col-reverse items-center justify-center rounded-md p-2 pb-4 text-center font-normal ring-transparent transition has-[[data-space-text]:focus-visible]:outline-none has-[[data-space-text]:focus-visible]:ring-2 md:has-[[data-state='true']]:bg-transparent [&:hover>[data-more-button]]:opacity-100"
>
<button data-space-text className="focus-visible:outline-none">
{title}
{name}
</button>

<div className="w-24 h-24 flex justify-center items-center">
Expand Down Expand Up @@ -240,6 +243,8 @@ export function SpaceItem({
return cachedMemories.filter(m => m.space === id)
}, [cachedMemories])

const _name = name.length > 10 ? name.slice(0, 10) + "..." : name

return (
<motion.div
ref={itemRef}
Expand All @@ -248,7 +253,7 @@ export function SpaceItem({
className="hover:bg-rgray-2 has-[[data-state='true']]:bg-rgray-2 has-[[data-space-text]:focus-visible]:bg-rgray-2 has-[[data-space-text]:focus-visible]:ring-rgray-7 [&:has-[[data-space-text]:focus-visible]>[data-more-button]]:opacity-100 relative flex select-none flex-col-reverse items-center justify-center rounded-md p-2 pb-4 text-center font-normal ring-transparent transition has-[[data-space-text]:focus-visible]:outline-none has-[[data-space-text]:focus-visible]:ring-2 md:has-[[data-state='true']]:bg-transparent [&:hover>[data-more-button]]:opacity-100"
>
<button data-space-text className="focus-visible:outline-none">
{name}
{_name}
</button>
<SpaceMoreButton
isOpen={moreDropdownOpen}
Expand Down Expand Up @@ -341,19 +346,23 @@ export function SpaceItem({
id={id.toString()}
images={spaceMemories.map((c) => c.image).reverse() as string[]}
/>
) : spaceMemories.length === 1 ? (
<MemoryWithImage
) : spaceMemories.length > 1 ? (
<MemoryWithImages2
className="h-24 w-24"
id={id.toString()}
image={spaceMemories[0].image!}
images={spaceMemories.map((c) => c.image).reverse() as string[]}
/>
) : (
<MemoryWithImages2
) : spaceMemories.length === 1 ? (
<MemoryWithImage
className="h-24 w-24"
id={id.toString()}
images={spaceMemories.map((c) => c.image).reverse() as string[]}
image={spaceMemories[0].image!}
/>
)}
) : (
<div className="bg-rgray-4 opacity-30 rounded-full w-24 h-24 scale-50 shadow-">

</div>
)}
</motion.div>
);
}
Expand Down
29 changes: 20 additions & 9 deletions apps/web/src/contexts/MemoryContext.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
"use client";
import React, { useCallback } from "react";
import { CollectedSpaces } from "../../types/memory";
import { ChachedSpaceContent, StoredContent, storedContent, StoredSpace } from "@/server/db/schema";
import { addMemory, searchMemoriesAndSpaces } from "@/actions/db";
import { addMemory, searchMemoriesAndSpaces, addSpace, fetchContentForSpace } from "@/actions/db";
import { User } from "next-auth";

export type SearchResult = {
Expand All @@ -16,7 +15,7 @@ export const MemoryContext = React.createContext<{
spaces: StoredSpace[];
deleteSpace: (id: number) => Promise<void>;
freeMemories: StoredContent[];
addSpace: (space: StoredSpace) => Promise<void>;
addSpace: typeof addSpace;
addMemory: (
memory: typeof storedContent.$inferInsert,
spaces?: number[],
Expand All @@ -27,7 +26,7 @@ export const MemoryContext = React.createContext<{
spaces: [],
freeMemories: [],
addMemory: async () => {},
addSpace: async () => {},
addSpace: (async () => {}) as unknown as (typeof addSpace),
deleteSpace: async () => {},
cachedMemories: [],
search: async () => []
Expand All @@ -49,10 +48,6 @@ export const MemoryProvider: React.FC<
const [cachedMemories, setCachedMemories] = React.useState<ChachedSpaceContent[]>(
initialCachedMemories
);

const addSpace = async (space: StoredSpace) => {
setSpaces((prev) => [...prev, space]);
}

const deleteSpace = async (id: number) => {
setSpaces((prev) => prev.filter((s) => s.id !== id));
Expand All @@ -68,13 +63,29 @@ export const MemoryProvider: React.FC<
) => {
const content = await addMemory(memory, spaces);
}

const _addSpace: typeof addSpace = async (...params) => {
const { space: addedSpace, addedMemories } = (await addSpace(...params))!;

setSpaces(prev => [...prev, addedSpace])
const cachedMemories = (await fetchContentForSpace(addedSpace.id, {
offset: 0,
limit: 3
})).map(m => ({ ...m, space: addedSpace.id }))

setCachedMemories(prev => [...prev, ...cachedMemories])

return {
space: addedSpace, addedMemories
}
}

return (
<MemoryContext.Provider
value={{
search: searchMemoriesAndSpaces,
spaces,
addSpace,
addSpace: _addSpace,
deleteSpace,
freeMemories,
cachedMemories,
Expand Down

0 comments on commit 0d4e910

Please sign in to comment.