tangled
alpha
login
or
join now
leaflet.pub
/
leaflet
289
fork
atom
a tool for shared writing and social publishing
289
fork
atom
overview
issues
28
pulls
pipelines
rename has page loaded and switch logic
awarm.space
3 months ago
3ff4755b
558d9b7c
+13
-14
6 changed files
expand all
collapse all
unified
split
app
lish
[did]
[publication]
[rkey]
PublishBskyPostBlock.tsx
components
Blocks
BlueskyPostBlock
index.tsx
DateTimeBlock.tsx
TextBlock
index.tsx
InitialPageLoadProvider.tsx
src
hooks
useLocalizedDate.ts
+2
-2
app/lish/[did]/[publication]/[rkey]/PublishBskyPostBlock.tsx
···
7
7
import { focusBlock } from "src/utils/focusBlock";
8
8
import { AppBskyFeedDefs, AppBskyFeedPost, RichText } from "@atproto/api";
9
9
import { Separator } from "components/Layout";
10
10
-
import { useInitialPageLoad } from "components/InitialPageLoadProvider";
10
10
+
import { useHasPageLoaded } from "components/InitialPageLoadProvider";
11
11
import { BlueskyTiny } from "components/Icons/BlueskyTiny";
12
12
import { CommentTiny } from "components/Icons/CommentTiny";
13
13
import { useLocalizedDate } from "src/hooks/useLocalizedDate";
···
123
123
};
124
124
125
125
const ClientDate = (props: { date?: string }) => {
126
126
-
let pageLoaded = useInitialPageLoad();
126
126
+
let pageLoaded = useHasPageLoaded();
127
127
const formattedDate = useLocalizedDate(
128
128
props.date || new Date().toISOString(),
129
129
{
-1
components/Blocks/BlueskyPostBlock/index.tsx
···
10
10
import { BlueskyPostEmpty } from "./BlueskyEmpty";
11
11
import { BlueskyRichText } from "./BlueskyRichText";
12
12
import { Separator } from "components/Layout";
13
13
-
import { useInitialPageLoad } from "components/InitialPageLoadProvider";
14
13
import { BlueskyTiny } from "components/Icons/BlueskyTiny";
15
14
import { CommentTiny } from "components/Icons/CommentTiny";
16
15
import { useLocalizedDate } from "src/hooks/useLocalizedDate";
+2
-2
components/Blocks/DateTimeBlock.tsx
···
8
8
import { setHours, setMinutes } from "date-fns";
9
9
import { Separator } from "react-aria-components";
10
10
import { Checkbox } from "components/Checkbox";
11
11
-
import { useInitialPageLoad } from "components/InitialPageLoadProvider";
11
11
+
import { useHasPageLoaded } from "components/InitialPageLoadProvider";
12
12
import { useSpring, animated } from "@react-spring/web";
13
13
import { ArrowRightTiny } from "components/Icons/ArrowRightTiny";
14
14
import { BlockCalendarSmall } from "components/Icons/BlockCalendarSmall";
15
15
16
16
export function DateTimeBlock(props: BlockProps) {
17
17
const [isClient, setIsClient] = useState(false);
18
18
-
let initialPageLoad = useInitialPageLoad();
18
18
+
let initialPageLoad = useHasPageLoaded();
19
19
20
20
useEffect(() => {
21
21
setIsClient(true);
+2
-2
components/Blocks/TextBlock/index.tsx
···
4
4
import { isVisible } from "src/utils/isVisible";
5
5
import { EditorState, TextSelection } from "prosemirror-state";
6
6
import { RenderYJSFragment } from "./RenderYJSFragment";
7
7
-
import { useInitialPageLoad } from "components/InitialPageLoadProvider";
7
7
+
import { useHasPageLoaded } from "components/InitialPageLoadProvider";
8
8
import { BlockProps } from "../Block";
9
9
import { focusBlock } from "src/utils/focusBlock";
10
10
import { useUIState } from "src/useUIState";
···
37
37
},
38
38
) {
39
39
let isLocked = useEntity(props.entityID, "block/is-locked");
40
40
-
let initialized = useInitialPageLoad();
40
40
+
let initialized = useHasPageLoaded();
41
41
let first = props.previousBlock === null;
42
42
let permission = useEntitySetContext().permissions.write;
43
43
+2
-2
components/InitialPageLoadProvider.tsx
···
2
2
import { useEffect } from "react";
3
3
import { create } from "zustand";
4
4
5
5
-
export const useInitialPageLoad = create(() => false);
5
5
+
export const useHasPageLoaded = create(() => false);
6
6
export function InitialPageLoad(props: { children: React.ReactNode }) {
7
7
useEffect(() => {
8
8
setTimeout(() => {
9
9
-
useInitialPageLoad.setState(() => true);
9
9
+
useHasPageLoaded.setState(() => true);
10
10
}, 80);
11
11
}, []);
12
12
return <>{props.children}</>;
+5
-5
src/hooks/useLocalizedDate.ts
···
2
2
import { useContext, useMemo } from "react";
3
3
import { DateTime } from "luxon";
4
4
import { RequestHeadersContext } from "components/Providers/RequestHeadersProvider";
5
5
-
import { useInitialPageLoad } from "components/InitialPageLoadProvider";
5
5
+
import { useHasPageLoaded } from "components/InitialPageLoadProvider";
6
6
7
7
/**
8
8
* Hook that formats a date string using Luxon with timezone and locale from request headers.
···
20
20
options?: Intl.DateTimeFormatOptions,
21
21
): string {
22
22
const { timezone, language } = useContext(RequestHeadersContext);
23
23
-
const isInitialPageLoad = useInitialPageLoad();
23
23
+
const hasPageLoaded = useHasPageLoaded();
24
24
25
25
return useMemo(() => {
26
26
// Parse the date string to Luxon DateTime
27
27
let dateTime = DateTime.fromISO(dateString);
28
28
29
29
// On initial page load, use header timezone. After hydration, use system timezone
30
30
-
const effectiveTimezone = isInitialPageLoad
30
30
+
const effectiveTimezone = !hasPageLoaded
31
31
? timezone
32
32
: Intl.DateTimeFormat().resolvedOptions().timeZone;
33
33
···
39
39
// On initial page load, use header locale. After hydration, use system locale
40
40
// Parse locale from accept-language header (take first locale)
41
41
// accept-language format: "en-US,en;q=0.9,es;q=0.8"
42
42
-
const effectiveLocale = isInitialPageLoad
42
42
+
const effectiveLocale = !hasPageLoaded
43
43
? language?.split(",")[0]?.split(";")[0]?.trim() || "en-US"
44
44
: Intl.DateTimeFormat().resolvedOptions().locale;
45
45
···
50
50
// Fallback to en-US if locale is invalid
51
51
return dateTime.toLocaleString(options, { locale: "en-US" });
52
52
}
53
53
-
}, [dateString, options, timezone, language, isInitialPageLoad]);
53
53
+
}, [dateString, options, timezone, language, hasPageLoaded]);
54
54
}