Skip to content

Commit

Permalink
Handle side bar updates
Browse files Browse the repository at this point in the history
  • Loading branch information
arjunkomath committed Jan 28, 2025
1 parent 9e48099 commit 0c7f4c7
Show file tree
Hide file tree
Showing 5 changed files with 79 additions and 39 deletions.
20 changes: 13 additions & 7 deletions app/(dashboard)/[tenant]/projects/[projectId]/documents/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import { blob, comment, document, documentFolder } from "@/drizzle/schema";
import { generateObjectDiffMessage, logActivity } from "@/lib/activity";
import { deleteFile } from "@/lib/blobStore";
import { broadcastEvent } from "@/lib/utils/cable-server";
import { database } from "@/lib/utils/useDatabase";
import { deleteFilesInMarkdown } from "@/lib/utils/useMarkdown";
import { getOwner } from "@/lib/utils/useOwner";
Expand Down Expand Up @@ -124,7 +125,7 @@ export async function updateDocument(payload: FormData) {
}

export async function createDocumentFolder(payload: FormData) {
const { userId, orgSlug } = await getOwner();
const { userId, orgSlug, ownerId } = await getOwner();
const name = payload.get("name") as string;
const description = payload.get("description") as string;
const projectId = payload.get("projectId") as string;
Expand All @@ -135,8 +136,7 @@ export async function createDocumentFolder(payload: FormData) {
});

const db = await database();
await db
.insert(documentFolder)
db.insert(documentFolder)
.values({
...data,
projectId: +projectId,
Expand All @@ -153,13 +153,15 @@ export async function createDocumentFolder(payload: FormData) {
projectId: +projectId,
});

await broadcastEvent("update_sidebar", ownerId);

revalidatePath(`/${orgSlug}/projects/${projectId}`);
revalidatePath(`/${orgSlug}/projects/${projectId}/documents`);
redirect(`/${orgSlug}/projects/${projectId}/documents`);
}

export async function updateDocumentFolder(payload: FormData) {
const { orgSlug } = await getOwner();
const { orgSlug, ownerId } = await getOwner();
const name = payload.get("name") as string;
const description = payload.get("description") as string;
const id = payload.get("id") as string;
Expand All @@ -175,7 +177,7 @@ export async function updateDocumentFolder(payload: FormData) {
.findFirst({ where: eq(documentFolder.id, +id) })
.execute();

const folderDetails = await db
const folderDetails = db
.update(documentFolder)
.set({
...data,
Expand All @@ -195,13 +197,15 @@ export async function updateDocumentFolder(payload: FormData) {
projectId: +projectId,
});

await broadcastEvent("update_sidebar", ownerId);

revalidatePath(`/${orgSlug}/projects/${projectId}`);
revalidatePath(`/${orgSlug}/projects/${projectId}/documents/folders/${id}`);
redirect(`/${orgSlug}/projects/${projectId}/documents/folders/${id}`);
}

export async function deleteDocumentFolder(payload: FormData) {
const { orgSlug } = await getOwner();
const { orgSlug, ownerId } = await getOwner();
const id = payload.get("id") as string;
const projectId = payload.get("projectId") as string;
const currentPath = payload.get("currentPath") as string;
Expand All @@ -226,6 +230,8 @@ export async function deleteDocumentFolder(payload: FormData) {
projectId: +projectId,
});

await broadcastEvent("update_sidebar", ownerId);

revalidatePath(currentPath);
redirect(`/${orgSlug}/projects/${projectId}/documents`);
}
Expand Down Expand Up @@ -295,7 +301,7 @@ export async function deleteBlob(
await deleteFile(file.key);

const db = await database();
const blobDetails = await db
const blobDetails = db
.delete(blob)
.where(eq(blob.id, file.id))
.returning()
Expand Down
32 changes: 18 additions & 14 deletions app/(dashboard)/[tenant]/projects/[projectId]/tasklists/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import { task, taskList } from "@/drizzle/schema";
import { generateObjectDiffMessage, logActivity } from "@/lib/activity";
import { broadcastEvent } from "@/lib/utils/cable-server";
import { database } from "@/lib/utils/useDatabase";
import { getOwner } from "@/lib/utils/useOwner";
import { and, desc, eq } from "drizzle-orm";
Expand Down Expand Up @@ -42,7 +43,7 @@ const taskSchema = z.object({
});

export async function createTaskList(payload: FormData) {
const { userId, orgSlug } = await getOwner();
const { userId, orgSlug, ownerId } = await getOwner();
const name = payload.get("name") as string;
const description = payload.get("description") as string;
const dueDate = payload.get("dueDate") as string;
Expand Down Expand Up @@ -74,12 +75,14 @@ export async function createTaskList(payload: FormData) {
projectId: +projectId,
});

await broadcastEvent("update_sidebar", ownerId);

revalidatePath(`/${orgSlug}/projects/${projectId}/tasklists`);
redirect(`/${orgSlug}/projects/${projectId}/tasklists`);
}

export async function updateTaskList(payload: FormData) {
const { orgSlug } = await getOwner();
const { orgSlug, ownerId } = await getOwner();
const id = payload.get("id") as string;
const name = payload.get("name") as string;
const description = payload.get("description") as string;
Expand All @@ -100,8 +103,7 @@ export async function updateTaskList(payload: FormData) {
})
.execute();

await db
.update(taskList)
db.update(taskList)
.set({
...data,
updatedAt: new Date(),
Expand All @@ -120,6 +122,8 @@ export async function updateTaskList(payload: FormData) {
projectId: +projectId,
});

await broadcastEvent("update_sidebar", ownerId);

revalidatePath(`/${orgSlug}/projects/${projectId}/tasklists`);
redirect(`/${orgSlug}/projects/${projectId}/tasklists`);
}
Expand All @@ -136,7 +140,7 @@ export async function partialUpdateTaskList(
})
.execute();

const updated = await db
const updated = db
.update(taskList)
.set({
...data,
Expand Down Expand Up @@ -164,7 +168,7 @@ export async function deleteTaskList(payload: FormData) {
const id = payload.get("id") as string;
const projectId = payload.get("projectId") as string;

const { orgSlug } = await getOwner();
const { orgSlug, ownerId } = await getOwner();
const db = await database();
const taskListDetails = db
.delete(taskList)
Expand All @@ -179,6 +183,7 @@ export async function deleteTaskList(payload: FormData) {
projectId: +projectId,
});

await broadcastEvent("update_sidebar", ownerId);
revalidatePath(`/${orgSlug}/projects/${projectId}/tasklists`);
}

Expand Down Expand Up @@ -217,8 +222,7 @@ export async function createTask({
? lastPosition?.position + POSITION_INCREMENT
: 1;

await db
.insert(task)
db.insert(task)
.values({
...data,
position,
Expand Down Expand Up @@ -335,7 +339,7 @@ export async function repositionTask(
}

export async function forkTaskList(taskListId: number, projectId: number) {
const { orgSlug } = await getOwner();
const { orgSlug, ownerId } = await getOwner();
const db = await database();

const taskListDetails = await db.query.taskList
Expand All @@ -347,16 +351,15 @@ export async function forkTaskList(taskListId: number, projectId: number) {
throw new Error("Task list not found");
}

await db
.update(taskList)
db.update(taskList)
.set({
status: "archived",
updatedAt: new Date(),
})
.where(eq(taskList.id, +taskListId))
.run();

const newTaskList = await db
const newTaskList = db
.insert(taskList)
.values({
name: taskListDetails.name,
Expand All @@ -371,8 +374,7 @@ export async function forkTaskList(taskListId: number, projectId: number) {
.returning()
.get();

await db
.update(task)
db.update(task)
.set({
taskListId: newTaskList.id,
updatedAt: new Date(),
Expand All @@ -387,6 +389,8 @@ export async function forkTaskList(taskListId: number, projectId: number) {
projectId: +projectId,
});

await broadcastEvent("update_sidebar", ownerId);

revalidatePath(`/${orgSlug}/projects/${projectId}/tasklists`);
}

Expand Down
7 changes: 6 additions & 1 deletion app/(dashboard)/[tenant]/settings/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import { logtoConfig } from "@/app/logto";
import { notification, user } from "@/drizzle/schema";
import { updateUser } from "@/lib/ops/auth";
import { getStreamFor, getToken } from "@/lib/utils/cable-server";
import { getStreamFor } from "@/lib/utils/cable-server";
import { database } from "@/lib/utils/useDatabase";
import { getOwner } from "@/lib/utils/useOwner";
import { signOut } from "@logto/next/server-actions";
Expand Down Expand Up @@ -63,6 +63,11 @@ export async function getNotificationsStream() {
return getStreamFor("notifications", userId);
}

export async function getSidebarStream() {
const { ownerId } = await getOwner();
return getStreamFor("update_sidebar", ownerId);
}

export async function logout() {
await signOut(logtoConfig);
}
47 changes: 36 additions & 11 deletions components/nav-main.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"use client";

import { getSidebarStream } from "@/app/(dashboard)/[tenant]/settings/actions";
import {
Collapsible,
CollapsibleContent,
Expand All @@ -18,20 +19,22 @@ import {
} from "@/components/ui/sidebar";
import type { ProjectWithData } from "@/drizzle/types";
import { cn } from "@/lib/utils";
import { useCable } from "@/lib/utils/cable-client";
import { getProjectById } from "@/lib/utils/useProjects";
import type { Channel } from "@anycable/web";
import {
CalendarCheck,
CalendarHeartIcon,
ChevronRight,
File,
GaugeIcon,
ListChecksIcon,
type LucideIcon,
SettingsIcon,
} from "lucide-react";
import { CalendarHeartIcon } from "lucide-react";
import Link from "next/link";
import { useParams, usePathname } from "next/navigation";
import { useEffect, useMemo, useState } from "react";
import { useCallback, useEffect, useMemo, useState } from "react";
import { Notifications } from "./core/notifications";

type MainNavItem = {
Expand All @@ -48,23 +51,45 @@ type MainNavItem = {

export function NavMain() {
const { setOpenMobile } = useSidebar();
const cable = useCable();
const { tenant, projectId } = useParams();
const pathname = usePathname();

const [projectData, setProjectData] = useState<ProjectWithData | null>(null);

const updateProjectData = useCallback(() => {
getProjectById(String(projectId), true)
.then((data) => {
setProjectData(data);
})
.catch((error) => {
setProjectData(null);
console.error(error);
});
}, [projectId]);

useEffect(() => {
if (projectId) {
getProjectById(String(projectId), true)
.then((data) => {
setProjectData(data);
})
.catch((error) => {
setProjectData(null);
console.error(error);
});
updateProjectData();
}
}, [projectId]);
}, [updateProjectData, projectId]);

useEffect(() => {
if (!cable) return;

let channel: Channel | undefined;

getSidebarStream().then((stream) => {
channel = cable.streamFromSigned(stream);
channel.on("message", (_) => {
updateProjectData();
});
});

return () => {
channel?.disconnect();
};
}, [cable, updateProjectData]);

const navItems: MainNavItem[] = useMemo(() => {
const items: MainNavItem[] = [
Expand Down
12 changes: 6 additions & 6 deletions lib/utils/cable-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ const jwtTTL = "1h";
const broadcastURL = process.env.ANYCABLE_BROADCAST_URL!;
const broadcastKey = process.env.ANYCABLE_BROADCAST_KEY!;

export type Event = "notifications";
export type Event = "notifications" | "update_sidebar";

export async function getToken(userId: string) {
if (!secret) {
Expand All @@ -19,22 +19,22 @@ export async function getToken(userId: string) {
return token;
}

export async function getStreamFor(room: Event, userId: string) {
export async function getStreamFor(room: Event, actor: string) {
if (!secret) {
throw new Error("ANYCABLE_STREAMS_SECRET is not set");
}

const sign = signer(secret);
const signedStreamName = sign(`${room}/${userId}`);
const signedStreamName = sign(`${room}/${actor}`);

return signedStreamName;
}

export async function broadcastEvent(
room: Event,
userId: string,
message: Record<string, string | number>,
actor: string,
message: Record<string, string | number> | null = null,
) {
const broadcastTo = broadcaster(broadcastURL, broadcastKey);
await broadcastTo(`${room}/${userId}`, message);
await broadcastTo(`${room}/${actor}`, message);
}

0 comments on commit 0c7f4c7

Please sign in to comment.