From 04572a8e5081b1e4871e273cde9dbaaa44c52fe0 Mon Sep 17 00:00:00 2001 From: MohamedBassem Date: Wed, 13 Mar 2024 21:43:44 +0000 Subject: structure: Create apps dir and copy tooling dir from t3-turbo repo --- apps/web/lib/bookmarkUtils.tsx | 22 ++++++++++ apps/web/lib/hooks/bookmark-search.ts | 73 ++++++++++++++++++++++++++++++++++ apps/web/lib/providers.tsx | 75 +++++++++++++++++++++++++++++++++++ apps/web/lib/trpc.tsx | 5 +++ apps/web/lib/utils.ts | 6 +++ 5 files changed, 181 insertions(+) create mode 100644 apps/web/lib/bookmarkUtils.tsx create mode 100644 apps/web/lib/hooks/bookmark-search.ts create mode 100644 apps/web/lib/providers.tsx create mode 100644 apps/web/lib/trpc.tsx create mode 100644 apps/web/lib/utils.ts (limited to 'apps/web/lib') diff --git a/apps/web/lib/bookmarkUtils.tsx b/apps/web/lib/bookmarkUtils.tsx new file mode 100644 index 00000000..a2828c29 --- /dev/null +++ b/apps/web/lib/bookmarkUtils.tsx @@ -0,0 +1,22 @@ +import { ZBookmark } from "@hoarder/trpc/types/bookmarks"; + +const MAX_LOADING_MSEC = 30 * 1000; + +export function isBookmarkStillCrawling(bookmark: ZBookmark) { + return ( + bookmark.content.type == "link" && + !bookmark.content.crawledAt && + Date.now().valueOf() - bookmark.createdAt.valueOf() < MAX_LOADING_MSEC + ); +} + +export function isBookmarkStillTagging(bookmark: ZBookmark) { + return ( + bookmark.taggingStatus == "pending" && + Date.now().valueOf() - bookmark.createdAt.valueOf() < MAX_LOADING_MSEC + ); +} + +export function isBookmarkStillLoading(bookmark: ZBookmark) { + return isBookmarkStillTagging(bookmark) || isBookmarkStillCrawling(bookmark); +} diff --git a/apps/web/lib/hooks/bookmark-search.ts b/apps/web/lib/hooks/bookmark-search.ts new file mode 100644 index 00000000..738e1bd8 --- /dev/null +++ b/apps/web/lib/hooks/bookmark-search.ts @@ -0,0 +1,73 @@ +import { useEffect, useState } from "react"; +import { api } from "@/lib/trpc"; +import { useRouter, useSearchParams } from "next/navigation"; +import { keepPreviousData } from "@tanstack/react-query"; + +function useSearchQuery() { + const searchParams = useSearchParams(); + const searchQuery = searchParams.get("q") || ""; + return { searchQuery }; +} + +export function useDoBookmarkSearch() { + const router = useRouter(); + const { searchQuery } = useSearchQuery(); + const [timeoutId, setTimeoutId] = useState(); + + useEffect(() => { + return () => { + if (!timeoutId) { + return; + } + clearTimeout(timeoutId); + }; + }, [timeoutId]); + + const doSearch = (val: string) => { + setTimeoutId(undefined); + router.replace(`/dashboard/search?q=${val}`); + }; + + const debounceSearch = (val: string) => { + if (timeoutId) { + clearTimeout(timeoutId); + } + const id = setTimeout(() => { + doSearch(val); + }, 200); + setTimeoutId(id); + }; + + return { + doSearch, + debounceSearch, + searchQuery, + }; +} + +export function useBookmarkSearch() { + const { searchQuery } = useSearchQuery(); + + const { data, isPending, isPlaceholderData, error } = + api.bookmarks.searchBookmarks.useQuery( + { + text: searchQuery, + }, + { + placeholderData: keepPreviousData, + gcTime: 0, + }, + ); + + if (error) { + throw error; + } + + return { + searchQuery, + error, + data, + isPending, + isPlaceholderData, + }; +} diff --git a/apps/web/lib/providers.tsx b/apps/web/lib/providers.tsx new file mode 100644 index 00000000..5c4649b5 --- /dev/null +++ b/apps/web/lib/providers.tsx @@ -0,0 +1,75 @@ +"use client"; + +import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; +import React, { useState } from "react"; +import { api } from "./trpc"; +import { loggerLink } from "@trpc/client"; +import { httpBatchLink } from "@trpc/client"; +import superjson from "superjson"; +import { SessionProvider } from "next-auth/react"; +import { Session } from "next-auth"; + +function makeQueryClient() { + return new QueryClient({ + defaultOptions: { + queries: { + // With SSR, we usually want to set some default staleTime + // above 0 to avoid refetching immediately on the client + staleTime: 60 * 1000, + }, + }, + }); +} + +let browserQueryClient: QueryClient | undefined = undefined; + +function getQueryClient() { + if (typeof window === "undefined") { + // Server: always make a new query client + return makeQueryClient(); + } else { + // Browser: make a new query client if we don't already have one + // This is very important so we don't re-make a new client if React + // supsends during the initial render. This may not be needed if we + // have a suspense boundary BELOW the creation of the query client + if (!browserQueryClient) browserQueryClient = makeQueryClient(); + return browserQueryClient; + } +} + +export default function Providers({ + children, + session, +}: { + children: React.ReactNode; + session: Session | null; +}) { + const queryClient = getQueryClient(); + + const [trpcClient] = useState(() => + api.createClient({ + links: [ + loggerLink({ + enabled: (op) => + process.env.NODE_ENV === "development" || + (op.direction === "down" && op.result instanceof Error), + }), + httpBatchLink({ + // TODO: Change this to be a full URL exposed as a client side setting + url: `/api/trpc`, + transformer: superjson, + }), + ], + }), + ); + + return ( + + + + {children} + + + + ); +} diff --git a/apps/web/lib/trpc.tsx b/apps/web/lib/trpc.tsx new file mode 100644 index 00000000..79a2a9fe --- /dev/null +++ b/apps/web/lib/trpc.tsx @@ -0,0 +1,5 @@ +"use client"; +import type { AppRouter } from "@hoarder/trpc/routers/_app"; +import { createTRPCReact } from "@trpc/react-query"; + +export const api = createTRPCReact(); diff --git a/apps/web/lib/utils.ts b/apps/web/lib/utils.ts new file mode 100644 index 00000000..365058ce --- /dev/null +++ b/apps/web/lib/utils.ts @@ -0,0 +1,6 @@ +import { type ClassValue, clsx } from "clsx"; +import { twMerge } from "tailwind-merge"; + +export function cn(...inputs: ClassValue[]) { + return twMerge(clsx(inputs)); +} -- cgit v1.2.3-70-g09d2