mirror of
https://github.com/umami-software/umami.git
synced 2026-02-04 04:37:11 +01:00
Restructure share routes to fix client-side navigation
- Change from [...shareId] catch-all to [slug]/[[...path]] structure - Layout with ShareProvider now persists across sub-route navigation - Add slug to ShareData context (separate from shareId UUID) - Links now use slug instead of UUID for proper routing - Remove unused ShareFooter and ShareHeader files Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
d028bfa1f5
commit
c9e14f3bce
8 changed files with 26 additions and 76 deletions
|
|
@ -7,6 +7,7 @@ import type { WhiteLabel } from '@/lib/types';
|
||||||
|
|
||||||
export interface ShareData {
|
export interface ShareData {
|
||||||
shareId: string;
|
shareId: string;
|
||||||
|
slug: string;
|
||||||
websiteId: string;
|
websiteId: string;
|
||||||
parameters: any;
|
parameters: any;
|
||||||
token: string;
|
token: string;
|
||||||
|
|
@ -31,8 +32,8 @@ const ALL_SECTION_IDS = [
|
||||||
'attribution',
|
'attribution',
|
||||||
];
|
];
|
||||||
|
|
||||||
export function ShareProvider({ shareId, children }: { shareId: string; children: ReactNode }) {
|
export function ShareProvider({ slug, children }: { slug: string; children: ReactNode }) {
|
||||||
const { share, isLoading, isFetching } = useShareTokenQuery(shareId);
|
const { share, isLoading, isFetching } = useShareTokenQuery(slug);
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const pathname = usePathname();
|
const pathname = usePathname();
|
||||||
const path = pathname.split('/')[3];
|
const path = pathname.split('/')[3];
|
||||||
|
|
@ -48,9 +49,9 @@ export function ShareProvider({ shareId, children }: { shareId: string; children
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (shouldRedirect) {
|
if (shouldRedirect) {
|
||||||
router.replace(`/share/${shareId}/${allowedSections[0]}`);
|
router.replace(`/share/${slug}/${allowedSections[0]}`);
|
||||||
}
|
}
|
||||||
}, [shouldRedirect, shareId, allowedSections, router]);
|
}, [shouldRedirect, slug, allowedSections, router]);
|
||||||
|
|
||||||
if (isFetching && isLoading) {
|
if (isFetching && isLoading) {
|
||||||
return <Loading placement="absolute" />;
|
return <Loading placement="absolute" />;
|
||||||
|
|
@ -60,5 +61,5 @@ export function ShareProvider({ shareId, children }: { shareId: string; children
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return <ShareContext.Provider value={share}>{children}</ShareContext.Provider>;
|
return <ShareContext.Provider value={{ ...share, slug }}>{children}</ShareContext.Provider>;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,23 +0,0 @@
|
||||||
import { Row, Text } from '@umami/react-zen';
|
|
||||||
import { CURRENT_VERSION, HOMEPAGE_URL } from '@/lib/constants';
|
|
||||||
import type { WhiteLabel } from '@/lib/types';
|
|
||||||
|
|
||||||
export function ShareFooter({ whiteLabel }: { whiteLabel?: WhiteLabel }) {
|
|
||||||
if (whiteLabel) {
|
|
||||||
return (
|
|
||||||
<Row as="footer" paddingY="6" justifyContent="flex-end">
|
|
||||||
<a href={whiteLabel.url} target="_blank">
|
|
||||||
<Text weight="bold">{whiteLabel.name}</Text>
|
|
||||||
</a>
|
|
||||||
</Row>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Row as="footer" paddingY="6" justifyContent="flex-end">
|
|
||||||
<a href={HOMEPAGE_URL} target="_blank">
|
|
||||||
<Text weight="bold">umami</Text> {`v${CURRENT_VERSION}`}
|
|
||||||
</a>
|
|
||||||
</Row>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,33 +0,0 @@
|
||||||
import { Icon, Row, Text, ThemeButton } from '@umami/react-zen';
|
|
||||||
import { LanguageButton } from '@/components/input/LanguageButton';
|
|
||||||
import { PreferencesButton } from '@/components/input/PreferencesButton';
|
|
||||||
import { Logo } from '@/components/svg';
|
|
||||||
import type { WhiteLabel } from '@/lib/types';
|
|
||||||
|
|
||||||
export function ShareHeader({ whiteLabel }: { whiteLabel?: WhiteLabel }) {
|
|
||||||
const logoUrl = whiteLabel?.url || 'https://umami.is';
|
|
||||||
const logoName = whiteLabel?.name || 'umami';
|
|
||||||
const logoImage = whiteLabel?.image;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Row as="header" justifyContent="space-between" alignItems="center" paddingY="3">
|
|
||||||
<a href={logoUrl} target="_blank" rel="noopener">
|
|
||||||
<Row alignItems="center" gap>
|
|
||||||
{logoImage ? (
|
|
||||||
<img src={logoImage} alt={logoName} style={{ height: 24 }} />
|
|
||||||
) : (
|
|
||||||
<Icon>
|
|
||||||
<Logo />
|
|
||||||
</Icon>
|
|
||||||
)}
|
|
||||||
<Text weight="bold">{logoName}</Text>
|
|
||||||
</Row>
|
|
||||||
</a>
|
|
||||||
<Row alignItems="center" gap>
|
|
||||||
<ThemeButton />
|
|
||||||
<LanguageButton />
|
|
||||||
<PreferencesButton />
|
|
||||||
</Row>
|
|
||||||
</Row>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,13 +0,0 @@
|
||||||
import { ShareProvider } from '@/app/share/ShareProvider';
|
|
||||||
import { SharePage } from './SharePage';
|
|
||||||
|
|
||||||
export default async function ({ params }: { params: Promise<{ shareId: string[] }> }) {
|
|
||||||
const { shareId } = await params;
|
|
||||||
const [slug] = shareId;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ShareProvider shareId={slug}>
|
|
||||||
<SharePage />
|
|
||||||
</ShareProvider>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -10,13 +10,13 @@ export function ShareNav({ onItemClick }: { onItemClick?: () => void }) {
|
||||||
const share = useShare();
|
const share = useShare();
|
||||||
const { formatMessage, labels } = useMessages();
|
const { formatMessage, labels } = useMessages();
|
||||||
const { pathname } = useNavigation();
|
const { pathname } = useNavigation();
|
||||||
const { shareId, parameters, whiteLabel } = share;
|
const { slug, parameters, whiteLabel } = share;
|
||||||
|
|
||||||
const logoUrl = whiteLabel?.url || 'https://umami.is';
|
const logoUrl = whiteLabel?.url || 'https://umami.is';
|
||||||
const logoName = whiteLabel?.name || 'umami';
|
const logoName = whiteLabel?.name || 'umami';
|
||||||
const logoImage = whiteLabel?.image;
|
const logoImage = whiteLabel?.image;
|
||||||
|
|
||||||
const renderPath = (path: string) => `/share/${shareId}${path}`;
|
const renderPath = (path: string) => `/share/${slug}${path}`;
|
||||||
|
|
||||||
const allItems = [
|
const allItems = [
|
||||||
{
|
{
|
||||||
5
src/app/share/[slug]/[[...path]]/page.tsx
Normal file
5
src/app/share/[slug]/[[...path]]/page.tsx
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
import { SharePage } from './SharePage';
|
||||||
|
|
||||||
|
export default function () {
|
||||||
|
return <SharePage />;
|
||||||
|
}
|
||||||
13
src/app/share/[slug]/layout.tsx
Normal file
13
src/app/share/[slug]/layout.tsx
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
import { ShareProvider } from '@/app/share/ShareProvider';
|
||||||
|
|
||||||
|
export default async function ({
|
||||||
|
params,
|
||||||
|
children,
|
||||||
|
}: {
|
||||||
|
params: Promise<{ slug: string }>;
|
||||||
|
children: React.ReactNode;
|
||||||
|
}) {
|
||||||
|
const { slug } = await params;
|
||||||
|
|
||||||
|
return <ShareProvider slug={slug}>{children}</ShareProvider>;
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue