From a8c7a8f60bf28fa91f4523a0388669bc0e001a93 Mon Sep 17 00:00:00 2001 From: kenneth Date: Tue, 28 Oct 2025 20:26:12 +0000 Subject: [PATCH] feat: basic recent file browsing --- .../components/ui/middle-truncated-text.tsx | 13 +++++++++++ .../src/dashboard/dashboard-sidebar.tsx | 10 ++++---- apps/drive-web/src/files/file-grid.tsx | 19 +++++++++++++++ apps/drive-web/src/routeTree.gen.ts | 23 +++++++++++++++++++ .../_authenticated/_sidebar-layout/recent.tsx | 22 ++++++++++++++++++ .../src/routes/_authenticated/index.tsx | 2 +- packages/convex/filesystem.ts | 9 ++++++++ packages/convex/model/filesystem.ts | 16 +++++++++++++ packages/convex/schema.ts | 2 +- 9 files changed, 109 insertions(+), 7 deletions(-) create mode 100644 apps/drive-web/src/components/ui/middle-truncated-text.tsx create mode 100644 apps/drive-web/src/files/file-grid.tsx create mode 100644 apps/drive-web/src/routes/_authenticated/_sidebar-layout/recent.tsx diff --git a/apps/drive-web/src/components/ui/middle-truncated-text.tsx b/apps/drive-web/src/components/ui/middle-truncated-text.tsx new file mode 100644 index 0000000..240dcf6 --- /dev/null +++ b/apps/drive-web/src/components/ui/middle-truncated-text.tsx @@ -0,0 +1,13 @@ +function MiddleTruncatedText({ children }: { children: string }) { + const LAST_PART_LENGTH = 3 + const lastPart = children.slice(children.length - LAST_PART_LENGTH) + const firstPart = children.slice(0, children.length - LAST_PART_LENGTH) + return ( +

+ {firstPart} + {lastPart} +

+ ) +} + +export { MiddleTruncatedText } diff --git a/apps/drive-web/src/dashboard/dashboard-sidebar.tsx b/apps/drive-web/src/dashboard/dashboard-sidebar.tsx index 351310b..0a86991 100644 --- a/apps/drive-web/src/dashboard/dashboard-sidebar.tsx +++ b/apps/drive-web/src/dashboard/dashboard-sidebar.tsx @@ -3,8 +3,8 @@ import { Link, useLocation } from "@tanstack/react-router" import { useQuery as useConvexQuery } from "convex/react" import { useAtomValue } from "jotai" import { + ClockIcon, FilesIcon, - HomeIcon, LogOutIcon, SettingsIcon, TrashIcon, @@ -66,10 +66,10 @@ function MainSidebarMenu() { return ( - - - - Home + + + + Recent diff --git a/apps/drive-web/src/files/file-grid.tsx b/apps/drive-web/src/files/file-grid.tsx new file mode 100644 index 0000000..13a9666 --- /dev/null +++ b/apps/drive-web/src/files/file-grid.tsx @@ -0,0 +1,19 @@ +import type { Doc } from "@fileone/convex/dataModel" +import { TextFileIcon } from "../components/icons/text-file-icon" +import { MiddleTruncatedText } from "../components/ui/middle-truncated-text" + +export function FileGrid({ files }: { files: Doc<"files">[] }) { + return ( +
+ {files.map((file) => ( +
+ + {file.name} +
+ ))} +
+ ) +} diff --git a/apps/drive-web/src/routeTree.gen.ts b/apps/drive-web/src/routeTree.gen.ts index 2664b6c..6e2075f 100644 --- a/apps/drive-web/src/routeTree.gen.ts +++ b/apps/drive-web/src/routeTree.gen.ts @@ -15,6 +15,7 @@ import { Route as AuthenticatedRouteImport } from './routes/_authenticated' import { Route as AuthenticatedIndexRouteImport } from './routes/_authenticated/index' import { Route as LoginCallbackRouteImport } from './routes/login_.callback' import { Route as AuthenticatedSidebarLayoutRouteImport } from './routes/_authenticated/_sidebar-layout' +import { Route as AuthenticatedSidebarLayoutRecentRouteImport } from './routes/_authenticated/_sidebar-layout/recent' import { Route as AuthenticatedSidebarLayoutHomeRouteImport } from './routes/_authenticated/_sidebar-layout/home' import { Route as AuthenticatedSidebarLayoutDirectoriesDirectoryIdRouteImport } from './routes/_authenticated/_sidebar-layout/directories.$directoryId' import { Route as AuthenticatedSidebarLayoutTrashDirectoriesDirectoryIdRouteImport } from './routes/_authenticated/_sidebar-layout/trash.directories.$directoryId' @@ -48,6 +49,12 @@ const AuthenticatedSidebarLayoutRoute = id: '/_sidebar-layout', getParentRoute: () => AuthenticatedRoute, } as any) +const AuthenticatedSidebarLayoutRecentRoute = + AuthenticatedSidebarLayoutRecentRouteImport.update({ + id: '/recent', + path: '/recent', + getParentRoute: () => AuthenticatedSidebarLayoutRoute, + } as any) const AuthenticatedSidebarLayoutHomeRoute = AuthenticatedSidebarLayoutHomeRouteImport.update({ id: '/home', @@ -73,6 +80,7 @@ export interface FileRoutesByFullPath { '/login/callback': typeof LoginCallbackRoute '/': typeof AuthenticatedIndexRoute '/home': typeof AuthenticatedSidebarLayoutHomeRoute + '/recent': typeof AuthenticatedSidebarLayoutRecentRoute '/directories/$directoryId': typeof AuthenticatedSidebarLayoutDirectoriesDirectoryIdRoute '/trash/directories/$directoryId': typeof AuthenticatedSidebarLayoutTrashDirectoriesDirectoryIdRoute } @@ -82,6 +90,7 @@ export interface FileRoutesByTo { '/login/callback': typeof LoginCallbackRoute '/': typeof AuthenticatedIndexRoute '/home': typeof AuthenticatedSidebarLayoutHomeRoute + '/recent': typeof AuthenticatedSidebarLayoutRecentRoute '/directories/$directoryId': typeof AuthenticatedSidebarLayoutDirectoriesDirectoryIdRoute '/trash/directories/$directoryId': typeof AuthenticatedSidebarLayoutTrashDirectoriesDirectoryIdRoute } @@ -94,6 +103,7 @@ export interface FileRoutesById { '/login_/callback': typeof LoginCallbackRoute '/_authenticated/': typeof AuthenticatedIndexRoute '/_authenticated/_sidebar-layout/home': typeof AuthenticatedSidebarLayoutHomeRoute + '/_authenticated/_sidebar-layout/recent': typeof AuthenticatedSidebarLayoutRecentRoute '/_authenticated/_sidebar-layout/directories/$directoryId': typeof AuthenticatedSidebarLayoutDirectoriesDirectoryIdRoute '/_authenticated/_sidebar-layout/trash/directories/$directoryId': typeof AuthenticatedSidebarLayoutTrashDirectoriesDirectoryIdRoute } @@ -105,6 +115,7 @@ export interface FileRouteTypes { | '/login/callback' | '/' | '/home' + | '/recent' | '/directories/$directoryId' | '/trash/directories/$directoryId' fileRoutesByTo: FileRoutesByTo @@ -114,6 +125,7 @@ export interface FileRouteTypes { | '/login/callback' | '/' | '/home' + | '/recent' | '/directories/$directoryId' | '/trash/directories/$directoryId' id: @@ -125,6 +137,7 @@ export interface FileRouteTypes { | '/login_/callback' | '/_authenticated/' | '/_authenticated/_sidebar-layout/home' + | '/_authenticated/_sidebar-layout/recent' | '/_authenticated/_sidebar-layout/directories/$directoryId' | '/_authenticated/_sidebar-layout/trash/directories/$directoryId' fileRoutesById: FileRoutesById @@ -180,6 +193,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof AuthenticatedSidebarLayoutRouteImport parentRoute: typeof AuthenticatedRoute } + '/_authenticated/_sidebar-layout/recent': { + id: '/_authenticated/_sidebar-layout/recent' + path: '/recent' + fullPath: '/recent' + preLoaderRoute: typeof AuthenticatedSidebarLayoutRecentRouteImport + parentRoute: typeof AuthenticatedSidebarLayoutRoute + } '/_authenticated/_sidebar-layout/home': { id: '/_authenticated/_sidebar-layout/home' path: '/home' @@ -206,6 +226,7 @@ declare module '@tanstack/react-router' { interface AuthenticatedSidebarLayoutRouteChildren { AuthenticatedSidebarLayoutHomeRoute: typeof AuthenticatedSidebarLayoutHomeRoute + AuthenticatedSidebarLayoutRecentRoute: typeof AuthenticatedSidebarLayoutRecentRoute AuthenticatedSidebarLayoutDirectoriesDirectoryIdRoute: typeof AuthenticatedSidebarLayoutDirectoriesDirectoryIdRoute AuthenticatedSidebarLayoutTrashDirectoriesDirectoryIdRoute: typeof AuthenticatedSidebarLayoutTrashDirectoriesDirectoryIdRoute } @@ -213,6 +234,8 @@ interface AuthenticatedSidebarLayoutRouteChildren { const AuthenticatedSidebarLayoutRouteChildren: AuthenticatedSidebarLayoutRouteChildren = { AuthenticatedSidebarLayoutHomeRoute: AuthenticatedSidebarLayoutHomeRoute, + AuthenticatedSidebarLayoutRecentRoute: + AuthenticatedSidebarLayoutRecentRoute, AuthenticatedSidebarLayoutDirectoriesDirectoryIdRoute: AuthenticatedSidebarLayoutDirectoriesDirectoryIdRoute, AuthenticatedSidebarLayoutTrashDirectoriesDirectoryIdRoute: diff --git a/apps/drive-web/src/routes/_authenticated/_sidebar-layout/recent.tsx b/apps/drive-web/src/routes/_authenticated/_sidebar-layout/recent.tsx new file mode 100644 index 0000000..012ab84 --- /dev/null +++ b/apps/drive-web/src/routes/_authenticated/_sidebar-layout/recent.tsx @@ -0,0 +1,22 @@ +import { api } from "@fileone/convex/api" +import { createFileRoute } from "@tanstack/react-router" +import { useQuery as useConvexQuery } from "convex/react" +import { FileGrid } from "@/files/file-grid" + +export const Route = createFileRoute("/_authenticated/_sidebar-layout/recent")({ + component: RouteComponent, +}) + +function RouteComponent() { + const recentFiles = useConvexQuery(api.filesystem.fetchRecentFiles, { + limit: 100, + }) + + console.log("recentFiles", recentFiles) + + return ( +
+ +
+ ) +} diff --git a/apps/drive-web/src/routes/_authenticated/index.tsx b/apps/drive-web/src/routes/_authenticated/index.tsx index 96d34c6..47994f6 100644 --- a/apps/drive-web/src/routes/_authenticated/index.tsx +++ b/apps/drive-web/src/routes/_authenticated/index.tsx @@ -5,5 +5,5 @@ export const Route = createFileRoute("/_authenticated/")({ }) function RouteComponent() { - return + return } diff --git a/packages/convex/filesystem.ts b/packages/convex/filesystem.ts index 9420b04..d0db1f0 100644 --- a/packages/convex/filesystem.ts +++ b/packages/convex/filesystem.ts @@ -188,3 +188,12 @@ export const openFile = authenticatedMutation({ return await FileSystem.openFile(ctx, { fileId }) }, }) + +export const fetchRecentFiles = authenticatedQuery({ + args: { + limit: v.number(), + }, + handler: async (ctx, { limit }) => { + return await FileSystem.fetchRecentFiles(ctx, { limit }) + }, +}) diff --git a/packages/convex/model/filesystem.ts b/packages/convex/model/filesystem.ts index dfddfd5..81fd1cf 100644 --- a/packages/convex/model/filesystem.ts +++ b/packages/convex/model/filesystem.ts @@ -265,3 +265,19 @@ export async function openFile( shareToken: newFileShare.shareToken, } } + +export async function fetchRecentFiles( + ctx: AuthenticatedQueryCtx, + { limit }: { limit: number }, +) { + return await ctx.db + .query("files") + .withIndex("byLastAccessedAt", (q) => + q + .eq("userId", ctx.user._id) + .eq("deletedAt", undefined) + .gte("lastAccessedAt", 0), + ) + .order("desc") + .take(limit) +} diff --git a/packages/convex/schema.ts b/packages/convex/schema.ts index a626c2f..237733b 100644 --- a/packages/convex/schema.ts +++ b/packages/convex/schema.ts @@ -17,7 +17,7 @@ const schema = defineSchema({ .index("byDirectoryId", ["userId", "directoryId", "deletedAt"]) .index("byUserId", ["userId", "deletedAt"]) .index("byDeletedAt", ["deletedAt"]) - .index("byLastAccessedAt", ["userId", "lastAccessedAt"]) + .index("byLastAccessedAt", ["userId", "deletedAt", "lastAccessedAt"]) .index("uniqueFileInDirectory", [ "userId", "directoryId",