Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 0 additions & 45 deletions app/api/account-emails/route.ts

This file was deleted.

44 changes: 17 additions & 27 deletions components/Files/FileInfoDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,46 +11,37 @@ import { extractAccountIds } from "@/utils/extractAccountIds";
import FileInfoDialogHeader from "./FileInfoDialogHeader";
import FileInfoDialogContent from "./FileInfoDialogContent";
import FileInfoDialogMetadata from "./FileInfoDialogMetadata";
import { useQuery } from "@tanstack/react-query";
import { Tables } from "@/types/database.types";
import { useUserProvider } from "@/providers/UserProvder";

type AccountEmail = Tables<"account_emails">;
import { useAccountEmails } from "@/hooks/useAccountEmails";

type FileInfoDialogProps = {
file: FileRow | null;
open: boolean;
onOpenChange: (open: boolean) => void;
};

export default function FileInfoDialog({ file, open, onOpenChange }: FileInfoDialogProps) {
export default function FileInfoDialog({
file,
open,
onOpenChange,
}: FileInfoDialogProps) {
const { userData } = useUserProvider();
const { content } = useFileContent(
file?.file_name || "",
file?.storage_key || "",
userData?.account_id || ""
file?.file_name || "",
file?.storage_key || "",
userData?.account_id || "",
);

// Extract account IDs and check if file is editable
const { ownerAccountId, artistAccountId } = file
? extractAccountIds(file.storage_key)
const { ownerAccountId, artistAccountId } = file
? extractAccountIds(file.storage_key)
: { ownerAccountId: "", artistAccountId: "" };
const canEdit = file ? isTextFile(file.file_name) : false;

// Fetch owner email
const { data: emails } = useQuery<AccountEmail[]>({
queryKey: ["file-owner-email", ownerAccountId, artistAccountId],
queryFn: async () => {
if (!ownerAccountId || !artistAccountId || !userData) return [];
const params = new URLSearchParams();
params.append("accountIds", ownerAccountId);
params.append("currentAccountId", userData.id);
params.append("artistAccountId", artistAccountId);
const response = await fetch(`/api/account-emails?${params}`);
if (!response.ok) return [];
return response.json();
},
enabled: !!ownerAccountId && !!artistAccountId && !!userData && open,
const { data: emails } = useAccountEmails({
accountIds: ownerAccountId ? [ownerAccountId] : [],
enabled: open,
});

const ownerEmail = emails?.[0]?.email || undefined;
Expand Down Expand Up @@ -92,7 +83,7 @@ export default function FileInfoDialog({ file, open, onOpenChange }: FileInfoDia
}
baseHandleEditToggle(editing);
};

return (
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent className="w-[96vw] sm:w-[92vw] max-w-5xl h-[90vh] p-0 gap-0 pt-6 flex flex-col">
Expand All @@ -107,7 +98,7 @@ export default function FileInfoDialog({ file, open, onOpenChange }: FileInfoDia
/>

<div className="flex flex-col sm:flex-row flex-1 overflow-hidden">
<FileInfoDialogContent
<FileInfoDialogContent
isEditing={isEditing}
fileName={file.file_name}
storageKey={file.storage_key}
Expand All @@ -121,4 +112,3 @@ export default function FileInfoDialog({ file, open, onOpenChange }: FileInfoDia
</Dialog>
);
}

40 changes: 13 additions & 27 deletions components/TasksPage/TasksList.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,10 @@
import { Tables } from "@/types/database.types";
import { Task } from "@/lib/tasks/getTasks";
import TaskCard from "@/components/VercelChat/tools/tasks/TaskCard";
import TaskSkeleton from "./TaskSkeleton";
import TaskDetailsDialog from "@/components/VercelChat/dialogs/tasks/TaskDetailsDialog";
import { useArtistProvider } from "@/providers/ArtistProvider";
import { useUserProvider } from "@/providers/UserProvder";
import { useMemo } from "react";
import { useQuery } from "@tanstack/react-query";

type AccountEmail = Tables<"account_emails">;
import { useAccountEmails } from "@/hooks/useAccountEmails";

interface TasksListProps {
tasks: Task[];
Expand All @@ -18,36 +14,22 @@ interface TasksListProps {

const TasksList: React.FC<TasksListProps> = ({ tasks, isLoading, isError }) => {
const { userData } = useUserProvider();
const { selectedArtist } = useArtistProvider();

// Extract unique account IDs from tasks
const accountIds = useMemo(
() => [...new Set(tasks.map(task => task.account_id))],
[tasks]
() => [...new Set(tasks.map((task) => task.account_id))],
[tasks],
);

// Batch fetch emails for all task owners
const { data: accountEmails = [] } = useQuery<AccountEmail[]>({
queryKey: ["task-owner-emails", accountIds],
queryFn: async () => {
if (accountIds.length === 0 || !userData) return [];
const params = new URLSearchParams();
accountIds.forEach(id => params.append("accountIds", id));
params.append("currentAccountId", userData.id);
if (selectedArtist) {
params.append("artistAccountId", selectedArtist.account_id);
}
const response = await fetch(`/api/account-emails?${params}`);
if (!response.ok) throw new Error("Failed to fetch emails");
return response.json();
},
enabled: accountIds.length > 0 && !!userData,
const { data: accountEmails = [] } = useAccountEmails({
accountIds,
});

// Create lookup map for O(1) email access
const emailByAccountId = useMemo(() => {
const map = new Map<string, string>();
accountEmails.forEach(ae => {
accountEmails.forEach((ae) => {
if (ae.account_id && ae.email) {
map.set(ae.account_id, ae.email);
}
Expand All @@ -56,7 +38,11 @@ const TasksList: React.FC<TasksListProps> = ({ tasks, isLoading, isError }) => {
}, [accountEmails]);

if (isError) {
return <div className="text-sm text-red-600 dark:text-red-400">Failed to load tasks</div>;
return (
<div className="text-sm text-red-600 dark:text-red-400">
Failed to load tasks
</div>
);
}

if (isLoading || !userData) {
Expand Down Expand Up @@ -88,8 +74,8 @@ const TasksList: React.FC<TasksListProps> = ({ tasks, isLoading, isError }) => {
index !== tasks.length - 1 ? "border-b border-border " : ""
}
>
<TaskCard
task={task}
<TaskCard
task={task}
ownerEmail={emailByAccountId.get(task.account_id)}
/>
</div>
Expand Down
43 changes: 43 additions & 0 deletions hooks/useAccountEmails.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
"use client";

import { useQuery } from "@tanstack/react-query";
import { usePrivy } from "@privy-io/react-auth";
import {
fetchAccountEmails,
type AccountEmail,
} from "@/lib/accounts/fetchAccountEmails";

interface UseAccountEmailsParams {
accountIds: string[];
enabled?: boolean;
}

/**
* Fetches account email rows for one or more account IDs the authenticated user can access.
*/
export function useAccountEmails({
accountIds,
enabled = true,
}: UseAccountEmailsParams) {
const { getAccessToken, authenticated } = usePrivy();

return useQuery<AccountEmail[]>({
queryKey: ["account-emails", accountIds],
queryFn: async () => {
if (accountIds.length === 0) {
return [];
}

const accessToken = await getAccessToken();
if (!accessToken) {
throw new Error("Please sign in to view account emails");
}

return fetchAccountEmails({
accessToken,
accountIds,
});
},
enabled: enabled && accountIds.length > 0 && authenticated,
});
}
40 changes: 40 additions & 0 deletions lib/accounts/fetchAccountEmails.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { getClientApiBaseUrl } from "@/lib/api/getClientApiBaseUrl";
import { Tables } from "@/types/database.types";

export type AccountEmail = Tables<"account_emails">;

interface FetchAccountEmailsParams {
accessToken: string;
accountIds: string[];
}

/**
* Fetches account email rows for the provided account IDs.
*/
export async function fetchAccountEmails({
accessToken,
accountIds,
}: FetchAccountEmailsParams): Promise<AccountEmail[]> {
if (accountIds.length === 0) {
return [];
}

const url = new URL(`${getClientApiBaseUrl()}/api/accounts/emails`);
accountIds.forEach((accountId) => {
url.searchParams.append("account_id", accountId);
});

const response = await fetch(url.toString(), {
method: "GET",
headers: {
Authorization: `Bearer ${accessToken}`,
},
});

if (!response.ok) {
const error = await response.json().catch(() => null);
throw new Error(error?.error || "Failed to fetch account emails");
}

return response.json();
}
Loading