Add Unhead

This commit is contained in:
Alex Gleason 2025-06-07 15:13:00 -05:00
parent b3bc9d45e0
commit d1a4e26f79
No known key found for this signature in database
GPG Key ID: 7211D1F99744FBB7
6 changed files with 79 additions and 21 deletions

34
package-lock.json generated
View File

@ -39,6 +39,7 @@
"@radix-ui/react-toggle-group": "^1.1.0", "@radix-ui/react-toggle-group": "^1.1.0",
"@radix-ui/react-tooltip": "^1.1.4", "@radix-ui/react-tooltip": "^1.1.4",
"@tanstack/react-query": "^5.56.2", "@tanstack/react-query": "^5.56.2",
"@unhead/react": "^2.0.10",
"class-variance-authority": "^0.7.1", "class-variance-authority": "^0.7.1",
"clsx": "^2.1.1", "clsx": "^2.1.1",
"cmdk": "^1.0.0", "cmdk": "^1.0.0",
@ -3625,6 +3626,21 @@
"url": "https://opencollective.com/typescript-eslint" "url": "https://opencollective.com/typescript-eslint"
} }
}, },
"node_modules/@unhead/react": {
"version": "2.0.10",
"resolved": "https://registry.npmjs.org/@unhead/react/-/react-2.0.10.tgz",
"integrity": "sha512-U5tqhUYk4qmyLD8YpKOuYwWmbIGFpB7aacOzcGAhjJ59GH3W/BSFOFHj/6dcoYV5yzr1CZrINGGH7stRVMMLjQ==",
"license": "MIT",
"dependencies": {
"unhead": "2.0.10"
},
"funding": {
"url": "https://github.com/sponsors/harlan-zw"
},
"peerDependencies": {
"react": ">=18"
}
},
"node_modules/@vitejs/plugin-react-swc": { "node_modules/@vitejs/plugin-react-swc": {
"version": "3.9.0", "version": "3.9.0",
"resolved": "https://registry.npmjs.org/@vitejs/plugin-react-swc/-/plugin-react-swc-3.9.0.tgz", "resolved": "https://registry.npmjs.org/@vitejs/plugin-react-swc/-/plugin-react-swc-3.9.0.tgz",
@ -5129,6 +5145,12 @@
"node": ">= 0.4" "node": ">= 0.4"
} }
}, },
"node_modules/hookable": {
"version": "5.5.3",
"resolved": "https://registry.npmjs.org/hookable/-/hookable-5.5.3.tgz",
"integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==",
"license": "MIT"
},
"node_modules/html-encoding-sniffer": { "node_modules/html-encoding-sniffer": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz", "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz",
@ -7197,6 +7219,18 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/unhead": {
"version": "2.0.10",
"resolved": "https://registry.npmjs.org/unhead/-/unhead-2.0.10.tgz",
"integrity": "sha512-GT188rzTCeSKt55tYyQlHHKfUTtZvgubrXiwzGeXg6UjcKO3FsagaMzQp6TVDrpDY++3i7Qt0t3pnCc/ebg5yQ==",
"license": "MIT",
"dependencies": {
"hookable": "^5.5.3"
},
"funding": {
"url": "https://github.com/sponsors/harlan-zw"
}
},
"node_modules/update-browserslist-db": { "node_modules/update-browserslist-db": {
"version": "1.1.3", "version": "1.1.3",
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz",

View File

@ -41,6 +41,7 @@
"@radix-ui/react-toggle-group": "^1.1.0", "@radix-ui/react-toggle-group": "^1.1.0",
"@radix-ui/react-tooltip": "^1.1.4", "@radix-ui/react-tooltip": "^1.1.4",
"@tanstack/react-query": "^5.56.2", "@tanstack/react-query": "^5.56.2",
"@unhead/react": "^2.0.10",
"class-variance-authority": "^0.7.1", "class-variance-authority": "^0.7.1",
"clsx": "^2.1.1", "clsx": "^2.1.1",
"cmdk": "^1.0.0", "cmdk": "^1.0.0",

View File

@ -1,17 +1,20 @@
// NOTE: This file should normally not be modified unless you are adding a new provider. // NOTE: This file should normally not be modified unless you are adding a new provider.
// To add new routes, edit the AppRouter.tsx file. // To add new routes, edit the AppRouter.tsx file.
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { createHead, UnheadProvider } from '@unhead/react/client';
import { Suspense } from 'react'; import { Suspense } from 'react';
import NostrProvider from '@/components/NostrProvider' import NostrProvider from '@/components/NostrProvider';
import { Toaster } from "@/components/ui/toaster"; import { Toaster } from "@/components/ui/toaster";
import { Toaster as Sonner } from "@/components/ui/sonner"; import { Toaster as Sonner } from "@/components/ui/sonner";
import { TooltipProvider } from "@/components/ui/tooltip"; import { TooltipProvider } from "@/components/ui/tooltip";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { NostrLoginProvider } from '@nostrify/react/login'; import { NostrLoginProvider } from '@nostrify/react/login';
import { AppProvider } from '@/components/AppProvider'; import { AppProvider } from '@/components/AppProvider';
import { AppConfig } from '@/contexts/AppContext'; import { AppConfig } from '@/contexts/AppContext';
import AppRouter from './AppRouter'; import AppRouter from './AppRouter';
const head = createHead();
const queryClient = new QueryClient({ const queryClient = new QueryClient({
defaultOptions: { defaultOptions: {
queries: { queries: {
@ -36,21 +39,23 @@ const presetRelays = [
export function App() { export function App() {
return ( return (
<AppProvider storageKey="nostr:app-config" defaultConfig={defaultConfig} presetRelays={presetRelays}> <UnheadProvider head={head}>
<QueryClientProvider client={queryClient}> <AppProvider storageKey="nostr:app-config" defaultConfig={defaultConfig} presetRelays={presetRelays}>
<NostrLoginProvider storageKey='nostr:login'> <QueryClientProvider client={queryClient}>
<NostrProvider> <NostrLoginProvider storageKey='nostr:login'>
<TooltipProvider> <NostrProvider>
<Toaster /> <TooltipProvider>
<Sonner /> <Toaster />
<Suspense> <Sonner />
<AppRouter /> <Suspense>
</Suspense> <AppRouter />
</TooltipProvider> </Suspense>
</NostrProvider> </TooltipProvider>
</NostrLoginProvider> </NostrProvider>
</QueryClientProvider> </NostrLoginProvider>
</AppProvider> </QueryClientProvider>
</AppProvider>
</UnheadProvider>
); );
} }

View File

@ -1,6 +1,13 @@
import { useSeoMeta } from '@unhead/react';
// FIXME: Update this page (the content is just a fallback if you fail to update the page) // FIXME: Update this page (the content is just a fallback if you fail to update the page)
const Index = () => { const Index = () => {
useSeoMeta({
title: 'Welcome to Your Blank App',
description: 'A modern Nostr client application built with React, TailwindCSS, and Nostrify.',
});
return ( return (
<div className="min-h-screen flex items-center justify-center bg-gray-100 dark:bg-gray-900"> <div className="min-h-screen flex items-center justify-center bg-gray-100 dark:bg-gray-900">
<div className="text-center"> <div className="text-center">

View File

@ -1,9 +1,15 @@
import { useSeoMeta } from "@unhead/react";
import { useLocation } from "react-router-dom"; import { useLocation } from "react-router-dom";
import { useEffect } from "react"; import { useEffect } from "react";
const NotFound = () => { const NotFound = () => {
const location = useLocation(); const location = useLocation();
useSeoMeta({
title: "404 - Page Not Found",
description: "The page you are looking for could not be found. Return to the home page to continue browsing.",
});
useEffect(() => { useEffect(() => {
console.error( console.error(
"404 Error: User attempted to access non-existent route:", "404 Error: User attempted to access non-existent route:",

View File

@ -1,5 +1,6 @@
import { BrowserRouter } from 'react-router-dom';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { createHead, UnheadProvider } from '@unhead/react/client';
import { BrowserRouter } from 'react-router-dom';
import { NostrLoginProvider } from '@nostrify/react/login'; import { NostrLoginProvider } from '@nostrify/react/login';
import NostrProvider from '@/components/NostrProvider'; import NostrProvider from '@/components/NostrProvider';
import { AppProvider } from '@/components/AppProvider'; import { AppProvider } from '@/components/AppProvider';
@ -10,6 +11,8 @@ interface TestAppProps {
} }
export function TestApp({ children }: TestAppProps) { export function TestApp({ children }: TestAppProps) {
const head = createHead();
const queryClient = new QueryClient({ const queryClient = new QueryClient({
defaultOptions: { defaultOptions: {
queries: { retry: false }, queries: { retry: false },
@ -23,17 +26,19 @@ export function TestApp({ children }: TestAppProps) {
}; };
return ( return (
<BrowserRouter> <UnheadProvider head={head}>
<AppProvider storageKey='test-app-config' defaultConfig={defaultConfig}> <AppProvider storageKey='test-app-config' defaultConfig={defaultConfig}>
<QueryClientProvider client={queryClient}> <QueryClientProvider client={queryClient}>
<NostrLoginProvider storageKey='test-login'> <NostrLoginProvider storageKey='test-login'>
<NostrProvider> <NostrProvider>
{children} <BrowserRouter>
{children}
</BrowserRouter>
</NostrProvider> </NostrProvider>
</NostrLoginProvider> </NostrLoginProvider>
</QueryClientProvider> </QueryClientProvider>
</AppProvider> </AppProvider>
</BrowserRouter> </UnheadProvider>
); );
} }