Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 27 additions & 24 deletions src/src/app/(root)/layout.tsx.jinja
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import type { Schemas } from "./schemas";
import type { Keys } from "./types";

import { ThemeScript } from "../../common/theme/components/theme-script";
import { HistoryProvider } from "../../isomorphic/generic/components/history-provider";
{%- if identity %}
import { IdentityProvider } from "../../isomorphic/identity/components/identity-provider";
{%- endif %}
Expand Down Expand Up @@ -97,35 +98,37 @@ export default async function RootLayout({
</head>
<body>
<StateProvider>
<MetadataProvider>
<QueryProvider>
<HydrationBoundary state={dehydrate(queryClient)}>
<LocalizationProvider locale={locale}>
<ThemeProvider
colors={constants.colors.all}
primaryColor={constants.colors.primary.name}
primaryShade={constants.colors.primary.shade}
>
{%- if identity %}
<IdentityProvider user={identity.user}>
<HistoryProvider>
<MetadataProvider>
<QueryProvider>
<HydrationBoundary state={dehydrate(queryClient)}>
<LocalizationProvider locale={locale}>
<ThemeProvider
colors={constants.colors.all}
primaryColor={constants.colors.primary.name}
primaryShade={constants.colors.primary.shade}
>
{%- if identity %}
<IdentityProvider user={identity.user}>
<Metadata
description={await getDescription()}
title={await getTitle()}
/>
<RootLayoutView>{children}</RootLayoutView>
</IdentityProvider>
{%- else %}
<Metadata
description={await getDescription()}
title={await getTitle()}
/>
<RootLayoutView>{children}</RootLayoutView>
</IdentityProvider>
{%- else %}
<Metadata
description={await getDescription()}
title={await getTitle()}
/>
<RootLayoutView>{children}</RootLayoutView>
{%- endif %}
</ThemeProvider>
</LocalizationProvider>
</HydrationBoundary>
</QueryProvider>
</MetadataProvider>
{%- endif %}
</ThemeProvider>
</LocalizationProvider>
</HydrationBoundary>
</QueryProvider>
</MetadataProvider>
</HistoryProvider>
</StateProvider>
</body>
</html>
Expand Down
4 changes: 2 additions & 2 deletions src/src/common/localization/locales/en.po.jinja
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,12 @@ msgstr "Error"
msgid "Error • {{ appname }}"
msgstr "Error • {{ appname }}"

#: src/app/(root)/layout.tsx:{% if identity %}36{% else %}34{% endif %}
#: src/app/(root)/layout.tsx:{% if identity %}37{% else %}35{% endif %}
msgid "{{ description }}"
msgstr "{{ description }}"

#: src/app/(root)/(main)/(home)/page.tsx:{% if identity and access %}21{% else %}20{% endif %}
#: src/app/(root)/layout.tsx:{% if identity %}42{% else %}40{% endif %}
#: src/app/(root)/layout.tsx:{% if identity %}43{% else %}41{% endif %}
msgid "{{ appname }}"
msgstr "{{ appname }}"
{%- if identity and access %}
Expand Down
4 changes: 2 additions & 2 deletions src/src/common/localization/locales/pl.po.jinja
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,12 @@ msgstr "Błąd"
msgid "Error • {{ appname }}"
msgstr "Błąd • {{ appname }}"

#: src/app/(root)/layout.tsx:{% if identity %}36{% else %}34{% endif %}
#: src/app/(root)/layout.tsx:{% if identity %}37{% else %}35{% endif %}
msgid "{{ description }}"
msgstr "{{ description }}"

#: src/app/(root)/(main)/(home)/page.tsx:{% if identity and access %}21{% else %}20{% endif %}
#: src/app/(root)/layout.tsx:{% if identity %}42{% else %}40{% endif %}
#: src/app/(root)/layout.tsx:{% if identity %}43{% else %}41{% endif %}
msgid "{{ appname }}"
msgstr "{{ appname }}"
{%- if identity and access %}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { HistorySynchronizer } from "./main";
export type { HistorySynchronizerInput } from "./types";
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { isEqual } from "es-toolkit/predicate";
import { usePathname, useSearchParams } from "next/navigation";
import { useEffect } from "react";

import type { HistorySynchronizerInput } from "./types";

import { useGlobalState } from "../../../../../state/hooks/use-global-state";

export function HistorySynchronizer({}: HistorySynchronizerInput) {
const { state } = useGlobalState();

const pathname = usePathname();
const searchParams = useSearchParams();

useEffect(() => {
const latestEntry = state.current.history.entries.at(-1);
const newEntry = {
path: pathname,
query: searchParams.entries().reduce(
(acc, [key, value]) => {
if (acc[key] === undefined) acc[key] = value;
else if (Array.isArray(acc[key])) acc[key].push(value);
else acc[key] = [acc[key], value];

return acc;
},
{} as { [key: string]: string | string[] },
),
};

if (!isEqual(newEntry, latestEntry))
state.current.history.entries.push(newEntry);
}, [pathname, searchParams, state.current.history.entries]);

return null;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export type HistorySynchronizerInput = object;
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { HistoryProvider } from "./main";
export type { HistoryProviderInput } from "./types";
14 changes: 14 additions & 0 deletions src/src/isomorphic/generic/components/history-provider/main.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
"use client";

import type { HistoryProviderInput } from "./types";

import { HistorySynchronizer } from "./components/history-synchronizer";

export function HistoryProvider({ children }: HistoryProviderInput) {
return (
<>
<HistorySynchronizer />
{children}
</>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import type { PropsWithChildren } from "react";

export type HistoryProviderInput = PropsWithChildren<object>;
8 changes: 8 additions & 0 deletions src/src/isomorphic/generic/hooks/use-history/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import "client-only";

export { useHistory } from "./main";
export type {
UseHistoryHistory,
UseHistoryInput,
UseHistoryOutput,
} from "./types";
13 changes: 13 additions & 0 deletions src/src/isomorphic/generic/hooks/use-history/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { useMemo } from "react";

import type { UseHistoryInput, UseHistoryOutput } from "./types";

import { useGlobalState } from "../../../state/hooks/use-global-state";

export function useHistory({}: UseHistoryInput = {}): UseHistoryOutput {
const { state } = useGlobalState();

const history = state.snapshot.history;

return useMemo(() => ({ history: history }), [history]);
}
9 changes: 9 additions & 0 deletions src/src/isomorphic/generic/hooks/use-history/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import type { ReadonlyState } from "../../../state/types";

export type UseHistoryHistory = ReadonlyState["history"];

export type UseHistoryInput = object;

export type UseHistoryOutput = {
history: UseHistoryHistory;
};
8 changes: 5 additions & 3 deletions src/src/isomorphic/metadata/components/metadata/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { useDeepCompareMemo } from "use-deep-compare";

import type { MetadataInput } from "./types";

import { useHistory } from "../../../generic/hooks/use-history";
import { useLocalization } from "../../../localization/hooks/use-localization";
import { serializeViewportAttributes } from "./utils";

Expand All @@ -15,6 +16,7 @@ export function Metadata({
title,
viewportAttributes,
}: MetadataInput) {
const { history } = useHistory();
const { localization } = useLocalization();

const cachedDescription = useDeepCompareMemo(
Expand All @@ -24,23 +26,23 @@ export function Metadata({
typeof description === "string"
? description
: localization.localize(description),
[description, localization.localize],
[description, history.entries.length, localization.localize],
);

const cachedViewport = useDeepCompareMemo(
() =>
viewportAttributes === undefined
? viewportAttributes
: serializeViewportAttributes(viewportAttributes),
[viewportAttributes],
[history.entries.length, viewportAttributes],
);

const cachedTitle = useDeepCompareMemo(
() =>
title === undefined || title === null || typeof title === "string"
? title
: localization.localize(title),
[title, localization.localize],
[history.entries.length, localization.localize, title],
);

return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@ import { proxy } from "valtio";
import type { State } from "../../types";

export function createInitialState() {
return proxy({} satisfies State);
return proxy({ history: { entries: [] } } satisfies State);
}
10 changes: 10 additions & 0 deletions src/src/isomorphic/state/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,23 @@ import "client-only";

import type { ReadonlyDeep } from "type-fest";

export type HistoryEntry = {
path: string;
query: { [key: string]: string | string[] };
};

export type HistoryState = {
entries: HistoryEntry[];
};

export type NowState = {
counter: number;
timer: number;
timestamp: number;
};

export type State = {
history: HistoryState;
now?: NowState;
};

Expand Down
Loading