diff options
Diffstat (limited to 'apps/web/lib')
| -rw-r--r-- | apps/web/lib/constants.ts | 43 | ||||
| -rw-r--r-- | apps/web/lib/get-metadata.ts | 40 | ||||
| -rw-r--r-- | apps/web/lib/get-theme-button.tsx | 11 | ||||
| -rw-r--r-- | apps/web/lib/handle-errors.ts | 25 | ||||
| -rw-r--r-- | apps/web/lib/searchParams.ts | 34 |
5 files changed, 153 insertions, 0 deletions
diff --git a/apps/web/lib/constants.ts b/apps/web/lib/constants.ts new file mode 100644 index 00000000..7a9485cf --- /dev/null +++ b/apps/web/lib/constants.ts @@ -0,0 +1,43 @@ +export const LIMITS = { + page: 100, + tweet: 1000, + note: 1000, +}; + +export const codeLanguageSubset = [ + "python", + "javascript", + "java", + "go", + "bash", + "c", + "cpp", + "csharp", + "css", + "diff", + "graphql", + "json", + "kotlin", + "less", + "lua", + "makefile", + "markdown", + "objectivec", + "perl", + "php", + "php-template", + "plaintext", + "python-repl", + "r", + "ruby", + "rust", + "scss", + "shell", + "sql", + "swift", + "typescript", + "vbnet", + "wasm", + "xml", + "yaml", +]; diff --git a/apps/web/lib/get-metadata.ts b/apps/web/lib/get-metadata.ts new file mode 100644 index 00000000..4609e49b --- /dev/null +++ b/apps/web/lib/get-metadata.ts @@ -0,0 +1,40 @@ +"use server"; +import * as cheerio from "cheerio"; + +// TODO: THIS SHOULD PROBABLY ALSO FETCH THE OG-IMAGE +export async function getMetaData(url: string) { + const response = await fetch(url); + const html = await response.text(); + + const $ = cheerio.load(html); + + // Extract the base URL + const baseUrl = new URL(url).origin; + + // Extract title + const title = $("title").text().trim(); + + const description = $("meta[name=description]").attr("content") ?? ""; + + const _favicon = + $("link[rel=icon]").attr("href") ?? "https://supermemory.dhr.wtf/web.svg"; + + let favicon = + _favicon.trim().length > 0 + ? _favicon.trim() + : "https://supermemory.dhr.wtf/web.svg"; + if (favicon.startsWith("/")) { + favicon = baseUrl + favicon; + } else if (favicon.startsWith("./")) { + favicon = baseUrl + favicon.slice(1); + } + + // Prepare the metadata object + const metadata = { + title, + description, + image: favicon, + baseUrl, + }; + return metadata; +} diff --git a/apps/web/lib/get-theme-button.tsx b/apps/web/lib/get-theme-button.tsx new file mode 100644 index 00000000..020cc976 --- /dev/null +++ b/apps/web/lib/get-theme-button.tsx @@ -0,0 +1,11 @@ +// Theming that works perfectly with app router (no flicker, jumps etc!) + +import dynamic from "next/dynamic"; + +// Don't SSR the toggle since the value on the server will be different than the client +export const getThemeToggler = () => + dynamic(() => import("@repo/ui/shadcn/theme-toggle"), { + ssr: false, + // Make sure to code a placeholder so the UI doesn't jump when the component loads + loading: () => <div className="w-6 h-6" />, + }); diff --git a/apps/web/lib/handle-errors.ts b/apps/web/lib/handle-errors.ts new file mode 100644 index 00000000..42cae589 --- /dev/null +++ b/apps/web/lib/handle-errors.ts @@ -0,0 +1,25 @@ +import { isRedirectError } from "next/dist/client/components/redirect"; +import { toast } from "sonner"; +import { z } from "zod"; + +export function getErrorMessage(err: unknown) { + const unknownError = "Something went wrong, please try again later."; + + if (err instanceof z.ZodError) { + const errors = err.issues.map((issue) => { + return issue.message; + }); + return errors.join("\n"); + } else if (err instanceof Error) { + return err.message; + } else if (isRedirectError(err)) { + throw err; + } else { + return unknownError; + } +} + +export function showErrorToast(err: unknown) { + const errorMessage = getErrorMessage(err); + return toast.error(errorMessage); +} diff --git a/apps/web/lib/searchParams.ts b/apps/web/lib/searchParams.ts new file mode 100644 index 00000000..6db718c2 --- /dev/null +++ b/apps/web/lib/searchParams.ts @@ -0,0 +1,34 @@ +import { + createSearchParamsCache, + parseAsInteger, + parseAsString, + parseAsBoolean, + parseAsArrayOf, + parseAsJson, +} from "nuqs/server"; +import { z } from "zod"; + +export const homeSearchParamsCache = createSearchParamsCache({ + firstTime: parseAsBoolean.withDefault(false), +}); + +export const chatSearchParamsCache = createSearchParamsCache({ + firstTime: parseAsBoolean.withDefault(false), + q: parseAsString.withDefault(""), + spaces: parseAsArrayOf( + parseAsJson((c) => { + const valid = z + .object({ + id: z.string(), + name: z.string(), + }) + .safeParse(c); + + if (!valid.success) { + return null; + } + + return valid.data; + }), + ).withDefault([]), +}); |