+8
-6
app/at/(trail)/[handle]/trail/[rkey]/StopEmbed.tsx
+8
-6
app/at/(trail)/[handle]/trail/[rkey]/StopEmbed.tsx
···
1
1
"use client";
2
2
3
-
import { use } from "react";
3
+
import { use, createContext } from "react";
4
4
import type { ExternalEmbed } from "@/data/queries";
5
5
import { LinkPreview } from "./LinkPreview";
6
6
import { BlueskyPostEmbed } from "./embeds/BlueskyPostEmbed";
···
16
16
17
17
type EmbedPromise = Promise<BlueskyPost | LinkMetadata | React.ReactElement | null>;
18
18
export type EmbedCache = Map<string, EmbedPromise>;
19
+
export const EmbedCacheContext = createContext<[EmbedCache | null, (c: EmbedCache) => void]>([
20
+
null,
21
+
() => {},
22
+
]);
19
23
20
24
type Props = {
21
25
external: ExternalEmbed;
22
-
cache: EmbedCache;
23
26
onDelete?: () => void;
24
27
};
25
28
···
50
53
return promise;
51
54
}
52
55
53
-
export function StopEmbed({ external, cache, onDelete }: Props) {
54
-
if (typeof window === "undefined") {
55
-
throw new Error("Client only");
56
-
}
56
+
export function StopEmbed({ external, onDelete }: Props) {
57
+
const [cache] = use(EmbedCacheContext);
58
+
if (!cache) throw new Error("StopEmbed must be used within EmbedCacheContext.Provider");
57
59
58
60
const uri = external.uri;
59
61
const isTrail = isTrailUri(uri);
+3
-4
app/at/(trail)/[handle]/trail/[rkey]/TrailStopCard.tsx
+3
-4
app/at/(trail)/[handle]/trail/[rkey]/TrailStopCard.tsx
···
1
1
"use client";
2
2
3
-
import { Suspense, useState, useCallback } from "react";
3
+
import { Suspense, use, useCallback } from "react";
4
4
import { useEditMode } from "./EditModeContext";
5
5
import type { TrailStop } from "@/data/queries";
6
-
import { StopEmbed, type EmbedCache } from "./StopEmbed";
6
+
import { EmbedCacheContext, StopEmbed } from "./StopEmbed";
7
7
import { extractLink } from "./utils/linkExtraction";
8
8
import { getLinkMetadata, blueskyUrlToAtUri } from "./utils/embed-resolver";
9
9
import "./TrailStopCard.css";
···
30
30
const isEditing = !!editContext;
31
31
const updateStop = editContext?.updateStop;
32
32
const error = editContext?.inlineErrors[stop.tid];
33
-
const [embedCache, setEmbedCache] = useState<EmbedCache>(() => new Map());
33
+
const [, setEmbedCache] = use(EmbedCacheContext);
34
34
35
35
const textareaRef = useCallback((el: HTMLTextAreaElement | null) => {
36
36
if (el) {
···
175
175
<Suspense fallback={<EmbedLoading />}>
176
176
<StopEmbed
177
177
external={stop.external}
178
-
cache={embedCache}
179
178
onDelete={isEditing ? handleRemoveLink : undefined}
180
179
/>
181
180
</Suspense>
+15
-4
app/at/(trail)/[handle]/trail/[rkey]/TrailWalk.tsx
+15
-4
app/at/(trail)/[handle]/trail/[rkey]/TrailWalk.tsx
···
1
-
import { useState, useLayoutEffect, useRef, Suspense, Activity } from "react";
1
+
import { useState, useLayoutEffect, useRef, Suspense, Activity, type ReactNode } from "react";
2
2
import { useRouter } from "next/navigation";
3
3
import type { TrailDetailData } from "@/data/queries";
4
4
import { BackButton } from "@/app/BackButton";
···
10
10
import { TrailWalkersOverlay } from "./TrailWalkersOverlay";
11
11
import { visitStop, completeTrail, abandonWalk, deleteCompletion } from "@/data/actions";
12
12
import { useAuthAction } from "@/auth/useAuthAction";
13
+
import type { EmbedCache } from "./StopEmbed";
13
14
import "./TrailWalk.css";
15
+
16
+
import { EmbedCacheContext } from "./StopEmbed";
17
+
18
+
function RevealedStop({ revealed, children }: { revealed: boolean; children: ReactNode }) {
19
+
const cacheAndSetCache = useState<EmbedCache>(() => new Map());
20
+
return (
21
+
<EmbedCacheContext value={cacheAndSetCache}>
22
+
<Activity mode={revealed ? "visible" : "hidden"}>{children}</Activity>
23
+
</EmbedCacheContext>
24
+
);
25
+
}
14
26
15
27
type Props = {
16
28
trail: TrailDetailData;
···
234
246
const isVisited =
235
247
!isCompleted && index > currentStopIndex && index <= furthestStopIndex;
236
248
const isUnreached = index > furthestStopIndex;
237
-
const isHidden = !isCompleted && !isEditMode && isUnreached;
238
249
239
250
const isClickable =
240
251
isEditMode ||
···
244
255
const isReorderActive = isEditMode && isCurrent && !isHoveringStopContent;
245
256
246
257
return (
247
-
<Activity key={stop.tid} mode={isHidden ? "hidden" : "visible"}>
258
+
<RevealedStop key={stop.tid} revealed={isCompleted || isEditMode || !isUnreached}>
248
259
<div
249
260
onPointerEnter={() => {
250
261
setIsHoveringStopContent(true);
···
266
277
onContinue={handleContinue}
267
278
/>
268
279
</div>
269
-
</Activity>
280
+
</RevealedStop>
270
281
);
271
282
})}
272
283
</div>