tangled
alpha
login
or
join now
whey.party
/
red-dwarf
82
fork
atom
an independent Bluesky client using Constellation, PDS Queries, and other services
reddwarf.app
frontend
spa
bluesky
reddwarf
microcosm
client
app
82
fork
atom
overview
issues
25
pulls
pipelines
lightbox is now a route
rimar1337
5 months ago
efb287ec
07f352c4
+256
-143
4 changed files
expand all
collapse all
unified
split
src
components
UniversalPostRenderer.tsx
routeTree.gen.ts
routes
profile.$did
post.$rkey.image.$i.tsx
post.$rkey.tsx
+40
-125
src/components/UniversalPostRenderer.tsx
···
3
3
import { useAtom } from "jotai";
4
4
import * as React from "react";
5
5
import { type SVGProps } from "react";
6
6
-
import { createPortal } from "react-dom";
7
6
8
8
-
import { ProfilePostComponent } from "~/routes/profile.$did/post.$rkey";
9
7
import { likedPostsAtom } from "~/utils/atoms";
10
8
import { useHydratedEmbed } from "~/utils/useHydrated";
11
9
import {
···
35
33
ref?: React.Ref<HTMLDivElement>;
36
34
dataIndexPropPass?: number;
37
35
nopics?: boolean;
36
36
+
lightboxCallback?: (d:LightboxProps) => void;
38
37
}
39
38
40
39
// export async function cachedGetRecord({
···
143
142
ref,
144
143
dataIndexPropPass,
145
144
nopics,
145
145
+
lightboxCallback,
146
146
}: UniversalPostRendererATURILoaderProps) {
147
147
// /*mass comment*/ console.log("atUri", atUri);
148
148
//const { get, set } = usePersistentStore();
···
421
421
ref={ref}
422
422
dataIndexPropPass={dataIndexPropPass}
423
423
nopics={nopics}
424
424
+
lightboxCallback={lightboxCallback}
424
425
/>
425
426
);
426
427
}
···
449
450
ref,
450
451
dataIndexPropPass,
451
452
nopics,
453
453
+
lightboxCallback,
452
454
}: {
453
455
postRecord: any;
454
456
profileRecord: any;
···
467
469
ref?: React.Ref<HTMLDivElement>;
468
470
dataIndexPropPass?: number;
469
471
nopics?: boolean;
472
472
+
lightboxCallback?: (d:LightboxProps) => void;
470
473
}) {
471
474
// /*mass comment*/ console.log(`received aturi: ${aturi} of post content: ${postRecord}`);
472
475
const navigate = useNavigate();
···
665
668
ref={ref}
666
669
dataIndexPropPass={dataIndexPropPass}
667
670
nopics={nopics}
671
671
+
lightboxCallback={lightboxCallback}
668
672
/>
669
673
</>
670
674
);
···
983
987
984
988
import defaultpfp from "~/../public/favicon.png";
985
989
import { useAuth } from "~/providers/UnifiedAuthProvider";
990
990
+
import type { LightboxProps } from "~/routes/profile.$did/post.$rkey.image.$i";
986
991
// import type { OutputSchema } from "@atproto/api/dist/client/types/app/bsky/feed/getFeed";
987
992
// import type {
988
993
// ViewRecord,
···
1110
1115
ref,
1111
1116
dataIndexPropPass,
1112
1117
nopics,
1118
1118
+
lightboxCallback
1113
1119
}: {
1114
1120
post: PostView;
1115
1121
// optional for now because i havent ported every use to this yet
···
1133
1139
ref?: React.Ref<HTMLDivElement>;
1134
1140
dataIndexPropPass?: number;
1135
1141
nopics?: boolean;
1142
1142
+
lightboxCallback?: (d:LightboxProps) => void;
1136
1143
}) {
1137
1144
const parsed = new AtUri(post.uri);
1138
1145
const navigate = useNavigate();
···
1514
1521
navigate={navigate}
1515
1522
postid={{ did: post.author.did, rkey: parsed.rkey }}
1516
1523
nopics={nopics}
1524
1524
+
lightboxCallback={lightboxCallback}
1517
1525
/>
1518
1526
) : null}
1519
1527
{post.embed && depth > 0 && (
···
1720
1728
navigate,
1721
1729
postid,
1722
1730
nopics,
1731
1731
+
lightboxCallback
1723
1732
}: {
1724
1733
embed?: Embed;
1725
1734
moderation?: ModerationDecision;
···
1730
1739
navigate: (_: any) => void;
1731
1740
postid?: { did: string; rkey: string };
1732
1741
nopics?: boolean;
1742
1742
+
lightboxCallback?: (d:LightboxProps) => void;
1733
1743
}) {
1734
1734
-
const [lightboxIndex, setLightboxIndex] = useState<number | null>(null);
1744
1744
+
//const [lightboxIndex, setLightboxIndex] = useState<number | null>(null);
1745
1745
+
function setLightboxIndex(number:number) {
1746
1746
+
navigate({
1747
1747
+
to: "/profile/$did/post/$rkey/image/$i",
1748
1748
+
params: {
1749
1749
+
did: postid?.did,
1750
1750
+
rkey: postid?.rkey,
1751
1751
+
i: number.toString(),
1752
1752
+
},
1753
1753
+
});
1754
1754
+
}
1735
1755
if (
1736
1756
AppBskyEmbedRecordWithMedia.isView(embed) &&
1737
1757
AppBskyEmbedRecord.isViewRecord(embed.record.record) &&
···
1767
1787
navigate={navigate}
1768
1788
postid={postid}
1769
1789
nopics={nopics}
1790
1790
+
lightboxCallback={lightboxCallback}
1770
1791
/>
1771
1792
{/* padding empty div of 8px height */}
1772
1793
<div style={{ height: 12 }} />
···
1934
1955
1935
1956
// image embed
1936
1957
// =
1937
1937
-
if (AppBskyEmbedImages.isView(embed) && !nopics) {
1958
1958
+
if (AppBskyEmbedImages.isView(embed)) {
1938
1959
const { images } = embed;
1939
1960
1940
1961
const lightboxImages = images.map((img) => ({
1941
1962
src: img.fullsize,
1942
1963
alt: img.alt,
1943
1964
}));
1965
1965
+
console.log("rendering images")
1966
1966
+
if (lightboxCallback) {
1967
1967
+
lightboxCallback({images: lightboxImages})
1968
1968
+
console.log("rendering images")
1969
1969
+
};
1970
1970
+
1971
1971
+
if (nopics) return;
1944
1972
1945
1973
if (images.length > 0) {
1946
1974
// const items = embed.images.map(img => ({
···
1972
2000
}}
1973
2001
className="border border-gray-200 dark:border-gray-800 was7 bg-gray-200 dark:bg-gray-900"
1974
2002
>
1975
1975
-
{lightboxIndex !== null && (
2003
2003
+
{/* {lightboxIndex !== null && (
1976
2004
<Lightbox
1977
2005
images={lightboxImages}
1978
2006
index={lightboxIndex}
···
1980
2008
onNavigate={(newIndex) => setLightboxIndex(newIndex)}
1981
2009
post={postid}
1982
2010
/>
1983
1983
-
)}
2011
2011
+
)} */}
1984
2012
<img
1985
2013
src={image.fullsize}
1986
2014
alt={image.alt}
···
2013
2041
}}
2014
2042
className="border border-gray-200 dark:border-gray-800 was7"
2015
2043
>
2016
2016
-
{lightboxIndex !== null && (
2044
2044
+
{/* {lightboxIndex !== null && (
2017
2045
<Lightbox
2018
2046
images={lightboxImages}
2019
2047
index={lightboxIndex}
···
2021
2049
onNavigate={(newIndex) => setLightboxIndex(newIndex)}
2022
2050
post={postid}
2023
2051
/>
2024
2024
-
)}
2052
2052
+
)} */}
2025
2053
{images.map((img, i) => (
2026
2054
<div
2027
2055
key={i}
···
2063
2091
}}
2064
2092
className="border border-gray-200 dark:border-gray-800 was7"
2065
2093
>
2066
2066
-
{lightboxIndex !== null && (
2094
2094
+
{/* {lightboxIndex !== null && (
2067
2095
<Lightbox
2068
2096
images={lightboxImages}
2069
2097
index={lightboxIndex}
···
2071
2099
onNavigate={(newIndex) => setLightboxIndex(newIndex)}
2072
2100
post={postid}
2073
2101
/>
2074
2074
-
)}
2102
2102
+
)} */}
2075
2103
{/* Left: 1:1 */}
2076
2104
<div
2077
2105
style={{ flex: 1, aspectRatio: "1 / 1", position: "relative" }}
···
2148
2176
}}
2149
2177
className="border border-gray-200 dark:border-gray-800 was7"
2150
2178
>
2151
2151
-
{lightboxIndex !== null && (
2179
2179
+
{/* {lightboxIndex !== null && (
2152
2180
<Lightbox
2153
2181
images={lightboxImages}
2154
2182
index={lightboxIndex}
···
2156
2184
onNavigate={(newIndex) => setLightboxIndex(newIndex)}
2157
2185
post={postid}
2158
2186
/>
2159
2159
-
)}
2187
2187
+
)} */}
2160
2188
{images.map((img, i) => (
2161
2189
<div
2162
2190
key={i}
···
2245
2273
}
2246
2274
2247
2275
return <div />;
2248
2248
-
}
2249
2249
-
2250
2250
-
type LightboxProps = {
2251
2251
-
images: { src: string; alt?: string }[];
2252
2252
-
index: number;
2253
2253
-
onClose: () => void;
2254
2254
-
onNavigate?: (newIndex: number) => void;
2255
2255
-
post?: { did: string; rkey: string };
2256
2256
-
};
2257
2257
-
export function Lightbox({
2258
2258
-
images,
2259
2259
-
index,
2260
2260
-
onClose,
2261
2261
-
onNavigate,
2262
2262
-
post,
2263
2263
-
}: LightboxProps) {
2264
2264
-
const image = images[index];
2265
2265
-
2266
2266
-
useEffect(() => {
2267
2267
-
function handleKey(e: KeyboardEvent) {
2268
2268
-
if (e.key === "Escape") onClose();
2269
2269
-
if (e.key === "ArrowRight" && onNavigate)
2270
2270
-
onNavigate((index + 1) % images.length);
2271
2271
-
if (e.key === "ArrowLeft" && onNavigate)
2272
2272
-
onNavigate((index - 1 + images.length) % images.length);
2273
2273
-
}
2274
2274
-
window.addEventListener("keydown", handleKey);
2275
2275
-
return () => window.removeEventListener("keydown", handleKey);
2276
2276
-
}, [index, images.length, onClose, onNavigate]);
2277
2277
-
2278
2278
-
return createPortal(
2279
2279
-
<>
2280
2280
-
{post && (
2281
2281
-
<div
2282
2282
-
onClick={(e) => {
2283
2283
-
e.stopPropagation();
2284
2284
-
e.nativeEvent.stopImmediatePropagation();
2285
2285
-
}}
2286
2286
-
className="lightbox-sidebar overscroll-none disablegutter border-l dark:border-gray-800 was7 border-gray-300 fixed z-50 flex top-0 right-0 flex-col max-w-[350px] min-w-[350px] max-h-screen overflow-y-scroll dark:bg-gray-950 bg-white"
2287
2287
-
>
2288
2288
-
<ProfilePostComponent
2289
2289
-
did={post.did}
2290
2290
-
rkey={post.rkey}
2291
2291
-
nopics={onClose}
2292
2292
-
/>
2293
2293
-
</div>
2294
2294
-
)}
2295
2295
-
<div
2296
2296
-
className="lightbox fixed inset-0 z-50 flex items-center justify-center bg-black/80 w-screen lg:w-[calc(100vw-350px)] lg:max-w-[calc(100vw-350px)]"
2297
2297
-
onClick={(e) => {
2298
2298
-
e.stopPropagation();
2299
2299
-
onClose();
2300
2300
-
}}
2301
2301
-
>
2302
2302
-
<img
2303
2303
-
src={image.src}
2304
2304
-
alt={image.alt}
2305
2305
-
className="max-h-[90%] max-w-[90%] object-contain rounded-lg shadow-lg"
2306
2306
-
onClick={(e) => e.stopPropagation()}
2307
2307
-
/>
2308
2308
-
2309
2309
-
{images.length > 1 && (
2310
2310
-
<>
2311
2311
-
<button
2312
2312
-
onClick={(e) => {
2313
2313
-
e.stopPropagation();
2314
2314
-
onNavigate?.((index - 1 + images.length) % images.length);
2315
2315
-
}}
2316
2316
-
className="absolute left-4 top-1/2 -translate-y-1/2 text-white text-4xl h-8 w-8 rounded-full bg-gray-900 flex items-center justify-center"
2317
2317
-
>
2318
2318
-
<svg
2319
2319
-
xmlns="http://www.w3.org/2000/svg"
2320
2320
-
width={28}
2321
2321
-
height={28}
2322
2322
-
viewBox="0 0 24 24"
2323
2323
-
>
2324
2324
-
<g fill="none" fillRule="evenodd">
2325
2325
-
<path d="M24 0v24H0V0zM12.593 23.258l-.011.002l-.071.035l-.02.004l-.014-.004l-.071-.035q-.016-.005-.024.005l-.004.01l-.017.428l.005.02l.01.013l.104.074l.015.004l.012-.004l.104-.074l.012-.016l.004-.017l-.017-.427q-.004-.016-.017-.018m.265-.113l-.013.002l-.185.093l-.01.01l-.003.011l.018.43l.005.012l.008.007l.201.093q.019.005.029-.008l.004-.014l-.034-.614q-.005-.019-.02-.022m-.715.002a.02.02 0 0 0-.027.006l-.006.014l-.034.614q.001.018.017.024l.015-.002l.201-.093l.01-.008l.004-.011l.017-.43l-.003-.012l-.01-.01z"></path>
2326
2326
-
<path
2327
2327
-
fill="currentColor"
2328
2328
-
d="M8.293 12.707a1 1 0 0 1 0-1.414l5.657-5.657a1 1 0 1 1 1.414 1.414L10.414 12l4.95 4.95a1 1 0 0 1-1.414 1.414z"
2329
2329
-
></path>
2330
2330
-
</g>
2331
2331
-
</svg>
2332
2332
-
</button>
2333
2333
-
<button
2334
2334
-
onClick={(e) => {
2335
2335
-
e.stopPropagation();
2336
2336
-
onNavigate?.((index + 1) % images.length);
2337
2337
-
}}
2338
2338
-
className="absolute right-4 top-1/2 -translate-y-1/2 text-white text-4xl h-8 w-8 rounded-full bg-gray-900 flex items-center justify-center"
2339
2339
-
>
2340
2340
-
<svg
2341
2341
-
xmlns="http://www.w3.org/2000/svg"
2342
2342
-
width={28}
2343
2343
-
height={28}
2344
2344
-
viewBox="0 0 24 24"
2345
2345
-
>
2346
2346
-
<g fill="none" fillRule="evenodd">
2347
2347
-
<path d="M24 0v24H0V0zM12.593 23.258l-.011.002l-.071.035l-.02.004l-.014-.004l-.071-.035q-.016-.005-.024.005l-.004.01l-.017.428l.005.02l.01.013l.104.074l.015.004l.012-.004l.104-.074l.012-.016l.004-.017l-.017-.427q-.004-.016-.017-.018m.265-.113l-.013.002l-.185.093l-.01.01l-.003.011l.018.43l.005.012l.008.007l.201.093q.019.005.029-.008l.004-.014l-.034-.614q-.005-.019-.02-.022m-.715.002a.02.02 0 0 0-.027.006l-.006.014l-.034.614q.001.018.017.024l.015-.002l.201-.093l.01-.008l.004-.011l.017-.43l-.003-.012l-.01-.01z"></path>
2348
2348
-
<path
2349
2349
-
fill="currentColor"
2350
2350
-
d="M15.707 11.293a1 1 0 0 1 0 1.414l-5.657 5.657a1 1 0 1 1-1.414-1.414l4.95-4.95l-4.95-4.95a1 1 0 0 1 1.414-1.414z"
2351
2351
-
></path>
2352
2352
-
</g>
2353
2353
-
</svg>
2354
2354
-
</button>
2355
2355
-
</>
2356
2356
-
)}
2357
2357
-
</div>
2358
2358
-
</>,
2359
2359
-
document.body
2360
2360
-
);
2361
2276
}
2362
2277
2363
2278
function getDomain(url: string) {
+36
-5
src/routeTree.gen.ts
···
21
21
import { Route as PathlessLayoutNestedLayoutRouteBRouteImport } from './routes/_pathlessLayout/_nested-layout/route-b'
22
22
import { Route as PathlessLayoutNestedLayoutRouteARouteImport } from './routes/_pathlessLayout/_nested-layout/route-a'
23
23
import { Route as ProfileDidPostRkeyRouteImport } from './routes/profile.$did/post.$rkey'
24
24
+
import { Route as ProfileDidPostRkeyImageIRouteImport } from './routes/profile.$did/post.$rkey.image.$i'
24
25
25
26
const SettingsRoute = SettingsRouteImport.update({
26
27
id: '/settings',
···
83
84
path: '/profile/$did/post/$rkey',
84
85
getParentRoute: () => rootRouteImport,
85
86
} as any)
87
87
+
const ProfileDidPostRkeyImageIRoute =
88
88
+
ProfileDidPostRkeyImageIRouteImport.update({
89
89
+
id: '/image/$i',
90
90
+
path: '/image/$i',
91
91
+
getParentRoute: () => ProfileDidPostRkeyRoute,
92
92
+
} as any)
86
93
87
94
export interface FileRoutesByFullPath {
88
95
'/': typeof IndexRoute
···
94
101
'/route-a': typeof PathlessLayoutNestedLayoutRouteARoute
95
102
'/route-b': typeof PathlessLayoutNestedLayoutRouteBRoute
96
103
'/profile/$did': typeof ProfileDidIndexRoute
97
97
-
'/profile/$did/post/$rkey': typeof ProfileDidPostRkeyRoute
104
104
+
'/profile/$did/post/$rkey': typeof ProfileDidPostRkeyRouteWithChildren
105
105
+
'/profile/$did/post/$rkey/image/$i': typeof ProfileDidPostRkeyImageIRoute
98
106
}
99
107
export interface FileRoutesByTo {
100
108
'/': typeof IndexRoute
···
106
114
'/route-a': typeof PathlessLayoutNestedLayoutRouteARoute
107
115
'/route-b': typeof PathlessLayoutNestedLayoutRouteBRoute
108
116
'/profile/$did': typeof ProfileDidIndexRoute
109
109
-
'/profile/$did/post/$rkey': typeof ProfileDidPostRkeyRoute
117
117
+
'/profile/$did/post/$rkey': typeof ProfileDidPostRkeyRouteWithChildren
118
118
+
'/profile/$did/post/$rkey/image/$i': typeof ProfileDidPostRkeyImageIRoute
110
119
}
111
120
export interface FileRoutesById {
112
121
__root__: typeof rootRouteImport
···
121
130
'/_pathlessLayout/_nested-layout/route-a': typeof PathlessLayoutNestedLayoutRouteARoute
122
131
'/_pathlessLayout/_nested-layout/route-b': typeof PathlessLayoutNestedLayoutRouteBRoute
123
132
'/profile/$did/': typeof ProfileDidIndexRoute
124
124
-
'/profile/$did/post/$rkey': typeof ProfileDidPostRkeyRoute
133
133
+
'/profile/$did/post/$rkey': typeof ProfileDidPostRkeyRouteWithChildren
134
134
+
'/profile/$did/post/$rkey/image/$i': typeof ProfileDidPostRkeyImageIRoute
125
135
}
126
136
export interface FileRouteTypes {
127
137
fileRoutesByFullPath: FileRoutesByFullPath
···
136
146
| '/route-b'
137
147
| '/profile/$did'
138
148
| '/profile/$did/post/$rkey'
149
149
+
| '/profile/$did/post/$rkey/image/$i'
139
150
fileRoutesByTo: FileRoutesByTo
140
151
to:
141
152
| '/'
···
148
159
| '/route-b'
149
160
| '/profile/$did'
150
161
| '/profile/$did/post/$rkey'
162
162
+
| '/profile/$did/post/$rkey/image/$i'
151
163
id:
152
164
| '__root__'
153
165
| '/'
···
162
174
| '/_pathlessLayout/_nested-layout/route-b'
163
175
| '/profile/$did/'
164
176
| '/profile/$did/post/$rkey'
177
177
+
| '/profile/$did/post/$rkey/image/$i'
165
178
fileRoutesById: FileRoutesById
166
179
}
167
180
export interface RootRouteChildren {
···
173
186
SettingsRoute: typeof SettingsRoute
174
187
CallbackIndexRoute: typeof CallbackIndexRoute
175
188
ProfileDidIndexRoute: typeof ProfileDidIndexRoute
176
176
-
ProfileDidPostRkeyRoute: typeof ProfileDidPostRkeyRoute
189
189
+
ProfileDidPostRkeyRoute: typeof ProfileDidPostRkeyRouteWithChildren
177
190
}
178
191
179
192
declare module '@tanstack/react-router' {
···
262
275
preLoaderRoute: typeof ProfileDidPostRkeyRouteImport
263
276
parentRoute: typeof rootRouteImport
264
277
}
278
278
+
'/profile/$did/post/$rkey/image/$i': {
279
279
+
id: '/profile/$did/post/$rkey/image/$i'
280
280
+
path: '/image/$i'
281
281
+
fullPath: '/profile/$did/post/$rkey/image/$i'
282
282
+
preLoaderRoute: typeof ProfileDidPostRkeyImageIRouteImport
283
283
+
parentRoute: typeof ProfileDidPostRkeyRoute
284
284
+
}
265
285
}
266
286
}
267
287
···
295
315
PathlessLayoutRouteChildren,
296
316
)
297
317
318
318
+
interface ProfileDidPostRkeyRouteChildren {
319
319
+
ProfileDidPostRkeyImageIRoute: typeof ProfileDidPostRkeyImageIRoute
320
320
+
}
321
321
+
322
322
+
const ProfileDidPostRkeyRouteChildren: ProfileDidPostRkeyRouteChildren = {
323
323
+
ProfileDidPostRkeyImageIRoute: ProfileDidPostRkeyImageIRoute,
324
324
+
}
325
325
+
326
326
+
const ProfileDidPostRkeyRouteWithChildren =
327
327
+
ProfileDidPostRkeyRoute._addFileChildren(ProfileDidPostRkeyRouteChildren)
328
328
+
298
329
const rootRouteChildren: RootRouteChildren = {
299
330
IndexRoute: IndexRoute,
300
331
PathlessLayoutRoute: PathlessLayoutRouteWithChildren,
···
304
335
SettingsRoute: SettingsRoute,
305
336
CallbackIndexRoute: CallbackIndexRoute,
306
337
ProfileDidIndexRoute: ProfileDidIndexRoute,
307
307
-
ProfileDidPostRkeyRoute: ProfileDidPostRkeyRoute,
338
338
+
ProfileDidPostRkeyRoute: ProfileDidPostRkeyRouteWithChildren,
308
339
}
309
340
export const routeTree = rootRouteImport
310
341
._addFileChildren(rootRouteChildren)
+165
src/routes/profile.$did/post.$rkey.image.$i.tsx
···
1
1
+
import {
2
2
+
createFileRoute,
3
3
+
useNavigate,
4
4
+
type UseNavigateResult,
5
5
+
} from "@tanstack/react-router";
6
6
+
import { useEffect, useState } from "react";
7
7
+
import { createPortal } from "react-dom";
8
8
+
9
9
+
import { ProfilePostComponent } from "./post.$rkey";
10
10
+
11
11
+
export const Route = createFileRoute("/profile/$did/post/$rkey/image/$i")({
12
12
+
component: Lightbox,
13
13
+
});
14
14
+
15
15
+
export type LightboxProps = {
16
16
+
images: { src: string; alt?: string }[];
17
17
+
};
18
18
+
19
19
+
function nextprev({
20
20
+
index,
21
21
+
images,
22
22
+
navigate,
23
23
+
did,
24
24
+
rkey,
25
25
+
prev,
26
26
+
}: {
27
27
+
index?: number;
28
28
+
images?: LightboxProps["images"];
29
29
+
navigate: UseNavigateResult<string>;
30
30
+
did: string;
31
31
+
rkey: string;
32
32
+
prev?: boolean;
33
33
+
}) {
34
34
+
const len = images?.length ?? 0;
35
35
+
if (len === 0) return;
36
36
+
37
37
+
const nextIndex = ((index ?? 0) + (prev ? -1 : 1) + len) % len;
38
38
+
39
39
+
navigate({
40
40
+
to: "/profile/$did/post/$rkey/image/$i",
41
41
+
params: {
42
42
+
did,
43
43
+
rkey,
44
44
+
i: nextIndex.toString(),
45
45
+
},
46
46
+
replace: true,
47
47
+
});
48
48
+
}
49
49
+
50
50
+
export function Lightbox() {
51
51
+
console.log("hey the $i route is loaded w!!!");
52
52
+
const { did, rkey, i } = Route.useParams();
53
53
+
const [images, setImages] = useState<LightboxProps["images"] | undefined>(
54
54
+
undefined
55
55
+
);
56
56
+
const index = Number(i);
57
57
+
const navigate = useNavigate();
58
58
+
const post = true;
59
59
+
const image = images?.[index] ?? undefined;
60
60
+
61
61
+
function lightboxCallback(d: LightboxProps) {
62
62
+
console.log("callback actually called!");
63
63
+
setImages(d.images);
64
64
+
}
65
65
+
66
66
+
useEffect(() => {
67
67
+
function handleKey(e: KeyboardEvent) {
68
68
+
if (e.key === "Escape") window.history.back();
69
69
+
if (e.key === "ArrowRight")
70
70
+
nextprev({ index, images, navigate, did, rkey });
71
71
+
//onNavigate((index + 1) % images.length);
72
72
+
if (e.key === "ArrowLeft")
73
73
+
nextprev({ index, images, navigate, did, rkey, prev: true });
74
74
+
//onNavigate((index - 1 + images.length) % images.length);
75
75
+
}
76
76
+
window.addEventListener("keydown", handleKey);
77
77
+
return () => window.removeEventListener("keydown", handleKey);
78
78
+
}, [index, navigate, did, rkey, images]);
79
79
+
80
80
+
return createPortal(
81
81
+
<>
82
82
+
{post && (
83
83
+
<div
84
84
+
onClick={(e) => {
85
85
+
e.stopPropagation();
86
86
+
e.nativeEvent.stopImmediatePropagation();
87
87
+
}}
88
88
+
className="lightbox-sidebar hidden lg:flex overscroll-none disablegutter border-l dark:border-gray-800 was7 border-gray-300 fixed z-50 flex top-0 right-0 flex-col max-w-[350px] min-w-[350px] max-h-screen overflow-y-scroll dark:bg-gray-950 bg-white"
89
89
+
>
90
90
+
<ProfilePostComponent
91
91
+
key={`/profile/${did}/post/${rkey}`}
92
92
+
did={did}
93
93
+
rkey={rkey}
94
94
+
nopics
95
95
+
lightboxCallback={lightboxCallback}
96
96
+
/>
97
97
+
</div>
98
98
+
)}
99
99
+
<div
100
100
+
className="lightbox fixed inset-0 z-50 flex items-center justify-center bg-black/80 w-screen lg:w-[calc(100vw-350px)] lg:max-w-[calc(100vw-350px)]"
101
101
+
onClick={(e) => {
102
102
+
e.stopPropagation();
103
103
+
window.history.back();
104
104
+
}}
105
105
+
>
106
106
+
<img
107
107
+
src={image?.src}
108
108
+
alt={image?.alt}
109
109
+
className="max-h-[90%] max-w-[90%] object-contain rounded-lg shadow-lg"
110
110
+
onClick={(e) => e.stopPropagation()}
111
111
+
/>
112
112
+
113
113
+
{(images?.length ?? 0) > 1 && (
114
114
+
<>
115
115
+
<button
116
116
+
onClick={(e) => {
117
117
+
e.stopPropagation();
118
118
+
nextprev({ index, images, navigate, did, rkey, prev: true });
119
119
+
}}
120
120
+
className="absolute left-4 top-1/2 -translate-y-1/2 text-white text-4xl h-8 w-8 rounded-full bg-gray-900 flex items-center justify-center"
121
121
+
>
122
122
+
<svg
123
123
+
xmlns="http://www.w3.org/2000/svg"
124
124
+
width={28}
125
125
+
height={28}
126
126
+
viewBox="0 0 24 24"
127
127
+
>
128
128
+
<g fill="none" fillRule="evenodd">
129
129
+
<path d="M24 0v24H0V0zM12.593 23.258l-.011.002l-.071.035l-.02.004l-.014-.004l-.071-.035q-.016-.005-.024.005l-.004.01l-.017.428l.005.02l.01.013l.104.074l.015.004l.012-.004l.104-.074l.012-.016l.004-.017l-.017-.427q-.004-.016-.017-.018m.265-.113l-.013.002l-.185.093l-.01.01l-.003.011l.018.43l.005.012l.008.007l.201.093q.019.005.029-.008l.004-.014l-.034-.614q-.005-.019-.02-.022m-.715.002a.02.02 0 0 0-.027.006l-.006.014l-.034.614q.001.018.017.024l.015-.002l.201-.093l.01-.008l.004-.011l.017-.43l-.003-.012l-.01-.01z"></path>
130
130
+
<path
131
131
+
fill="currentColor"
132
132
+
d="M8.293 12.707a1 1 0 0 1 0-1.414l5.657-5.657a1 1 0 1 1 1.414 1.414L10.414 12l4.95 4.95a1 1 0 0 1-1.414 1.414z"
133
133
+
></path>
134
134
+
</g>
135
135
+
</svg>
136
136
+
</button>
137
137
+
<button
138
138
+
onClick={(e) => {
139
139
+
e.stopPropagation();
140
140
+
nextprev({ index, images, navigate, did, rkey });
141
141
+
}}
142
142
+
className="absolute right-4 top-1/2 -translate-y-1/2 text-white text-4xl h-8 w-8 rounded-full bg-gray-900 flex items-center justify-center"
143
143
+
>
144
144
+
<svg
145
145
+
xmlns="http://www.w3.org/2000/svg"
146
146
+
width={28}
147
147
+
height={28}
148
148
+
viewBox="0 0 24 24"
149
149
+
>
150
150
+
<g fill="none" fillRule="evenodd">
151
151
+
<path d="M24 0v24H0V0zM12.593 23.258l-.011.002l-.071.035l-.02.004l-.014-.004l-.071-.035q-.016-.005-.024.005l-.004.01l-.017.428l.005.02l.01.013l.104.074l.015.004l.012-.004l.104-.074l.012-.016l.004-.017l-.017-.427q-.004-.016-.017-.018m.265-.113l-.013.002l-.185.093l-.01.01l-.003.011l.018.43l.005.012l.008.007l.201.093q.019.005.029-.008l.004-.014l-.034-.614q-.005-.019-.02-.022m-.715.002a.02.02 0 0 0-.027.006l-.006.014l-.034.614q.001.018.017.024l.015-.002l.201-.093l.01-.008l.004-.011l.017-.43l-.003-.012l-.01-.01z"></path>
152
152
+
<path
153
153
+
fill="currentColor"
154
154
+
d="M15.707 11.293a1 1 0 0 1 0 1.414l-5.657 5.657a1 1 0 1 1-1.414-1.414l4.95-4.95l-4.95-4.95a1 1 0 0 1 1.414-1.414z"
155
155
+
></path>
156
156
+
</g>
157
157
+
</svg>
158
158
+
</button>
159
159
+
</>
160
160
+
)}
161
161
+
</div>
162
162
+
</>,
163
163
+
document.body
164
164
+
);
165
165
+
}
+15
-13
src/routes/profile.$did/post.$rkey.tsx
···
1
1
import { useQueryClient } from "@tanstack/react-query";
2
2
-
import { createFileRoute } from "@tanstack/react-router";
2
2
+
import { createFileRoute, Outlet } from "@tanstack/react-router";
3
3
import React, { useLayoutEffect } from "react";
4
4
5
5
import { Header } from "~/components/Header";
···
11
11
useQueryIdentity,
12
12
useQueryPost,
13
13
} from "~/utils/useQuery";
14
14
+
15
15
+
import type { LightboxProps } from "./post.$rkey.image.$i";
14
16
15
17
//const HANDLE_DID_CACHE_TIMEOUT = 60 * 60 * 1000; // 1 hour
16
18
···
37
39
did,
38
40
rkey,
39
41
nopics,
42
42
+
lightboxCallback
40
43
}: {
41
44
did: string;
42
45
rkey: string;
43
43
-
nopics?: () => void;
46
46
+
nopics?: boolean;
47
47
+
lightboxCallback?: (d:LightboxProps) => void;
44
48
}) {
45
49
//const { get, set } = usePersistentStore();
46
50
const queryClient = useQueryClient();
···
297
301
298
302
return (
299
303
<>
304
304
+
<Outlet />
300
305
<Header
301
306
title={`Post`}
302
302
-
backButtonCallback={
303
303
-
nopics
304
304
-
? nopics
305
305
-
: () => {
306
306
-
if (window.history.length > 1) {
307
307
-
window.history.back();
308
308
-
} else {
309
309
-
window.location.assign("/");
310
310
-
}
311
311
-
}
312
312
-
}
307
307
+
backButtonCallback={() => {
308
308
+
if (window.history.length > 1) {
309
309
+
window.history.back();
310
310
+
} else {
311
311
+
window.location.assign("/");
312
312
+
}
313
313
+
}}
313
314
/>
314
315
315
316
{parentsLoading && (
···
342
343
detailed={true}
343
344
topReplyLine={parentsLoading || parents.length > 0}
344
345
nopics={!!nopics}
346
346
+
lightboxCallback={lightboxCallback}
345
347
/>
346
348
</div>
347
349
<div