Skip to content

Commit 933f3cb

Browse files
committed
feat: Supported jumping to short content details when clicking on the short content embed block.
1 parent ea7932c commit 933f3cb

7 files changed

Lines changed: 101 additions & 24 deletions

File tree

.changeset/slimy-guests-repair.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"xlog": patch
3+
---
4+
5+
Supported jumping to short content details when clicking on the short content embed block.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,7 @@
155155
"npm-run-all": "^4.1.5",
156156
"patch-package": "^6.4.7",
157157
"path-browserify": "0.0.0",
158+
"path-to-regexp": "^6.2.1",
158159
"pinyin-pro": "^3.14.0",
159160
"punycode": "^1.4.1",
160161
"qrcode": "^1.5.1",

src/pages/PostDetails/WebViewRenderer.tsx

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { WebView } from "@/components/WebView";
1212
import { VERSION } from "@/constants";
1313
import { useRootNavigation } from "@/hooks/use-navigation";
1414
import { useThemeStore } from "@/hooks/use-theme-store";
15+
import { getParamsFromShortsURL } from "@/utils/get-params-from-shorts-url";
1516

1617
import { javaScriptBeforeContentLoaded } from "./javascript-before-content";
1718
import { javaScriptContentLoaded } from "./javascript-content";
@@ -47,6 +48,21 @@ export const WebViewRenderer: FC<{
4748
const { height, imageUrlArray, link, title } = JSON.parse(data);
4849

4950
if (link) {
51+
const url = new URL(link);
52+
const params = getParamsFromShortsURL(url);
53+
const isPost = !!params;
54+
55+
if (isPost) {
56+
navigation.push(
57+
"PostDetails",
58+
{
59+
slug: params.slug,
60+
handle: params.handle,
61+
},
62+
);
63+
return;
64+
}
65+
5066
navigation.navigate("Web", {
5167
url: link,
5268
title,

src/pages/PostDetails/index.tsx

Lines changed: 32 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,15 @@ import { useSharedValue, withSpring, withDelay } from "react-native-reanimated";
44
import { useSafeAreaInsets } from "react-native-safe-area-context";
55

66
import type { NativeStackScreenProps } from "@react-navigation/native-stack";
7-
import type { NoteEntity } from "crossbell";
87
import { Stack } from "tamagui";
98

109
import { DelayedRender } from "@/components/DelayRender";
11-
import { ImageGallery } from "@/components/ImageGallery";
1210
import { usePostWebViewLink } from "@/hooks/use-post-link";
1311
import { useScrollVisibilityHandler } from "@/hooks/use-scroll-visibility-handler";
1412
import { useThemeStore } from "@/hooks/use-theme-store";
1513
import type { RootStackParamList } from "@/navigation/types";
14+
import { useGetPage } from "@/queries/page";
15+
import { useGetSite } from "@/queries/site";
1616
import type { ExpandedNote } from "@/types/crossbell";
1717
import { GA } from "@/utils/GA";
1818

@@ -24,8 +24,10 @@ import { Header } from "./Header";
2424
import { Navigator } from "./Navigator";
2525

2626
export interface Props {
27-
characterId: number
28-
note: ExpandedNote
27+
characterId?: number
28+
note?: ExpandedNote
29+
slug?: string
30+
handle?: string
2931
coverImage?: string
3032
placeholderCoverImageIndex?: number
3133
}
@@ -35,7 +37,16 @@ const animationTimeout = 300;
3537
export const PostDetailsPage: FC<NativeStackScreenProps<RootStackParamList, "PostDetails">> = (props) => {
3638
const { route } = props;
3739
const { params } = route;
38-
const { note, characterId } = params;
40+
const { slug, handle, coverImage, placeholderCoverImageIndex } = params;
41+
const site = useGetSite(handle);
42+
const page = useGetPage(site && {
43+
characterId: site.data?.characterId,
44+
slug,
45+
handle,
46+
});
47+
48+
const note = params.note || page.data;
49+
const characterId = params.characterId || site.data?.characterId;
3950
const { isDarkMode } = useThemeStore();
4051
const { bottom } = useSafeAreaInsets();
4152
const bottomBarHeight = bottom + 45;
@@ -44,24 +55,28 @@ export const PostDetailsPage: FC<NativeStackScreenProps<RootStackParamList, "Pos
4455
const bottomSheetModalRef = React.useRef<BottomSheetModalInstance>(null);
4556
const followAnimValue = useSharedValue<number>(0);
4657
const scrollVisibilityHandler = useScrollVisibilityHandler({ scrollThreshold: 30 });
47-
const postUri = usePostWebViewLink({ ...params, noteId: note.noteId });
58+
const postUri = usePostWebViewLink({ ...params, characterId, noteId: note?.noteId });
4859
const onTakeScreenshot = React.useCallback(async (): Promise<string> => contentRef.current.takeScreenshot(), []);
4960

5061
useEffect(() => {
5162
followAnimValue.value = withDelay(1500, withSpring(1));
5263
GA.logEvent("start_reading_post", {
53-
node_id: note.noteId,
64+
node_id: note?.noteId,
5465
character_id: characterId,
5566
});
5667
}, []);
5768

69+
if (!note || !characterId) {
70+
return null;
71+
}
72+
5873
return (
5974
<Stack flex={1} backgroundColor={isDarkMode ? "black" : "white"}>
6075
<Navigator
6176
onTakeScreenshot={onTakeScreenshot}
6277
isExpandedAnimValue={scrollVisibilityHandler.isExpandedAnimValue}
63-
characterId={params.characterId}
64-
note={params.note}
78+
characterId={characterId}
79+
note={note}
6580
postUri={postUri}
6681
headerContainerHeight={headerContainerHeight}
6782
/>
@@ -75,15 +90,15 @@ export const PostDetailsPage: FC<NativeStackScreenProps<RootStackParamList, "Pos
7590
isCapturing={isCapturing}
7691
headerContainerHeight={headerContainerHeight}
7792
postUri={postUri}
78-
note={params.note}
79-
characterId={params.characterId}
80-
placeholderCoverImageIndex={params.placeholderCoverImageIndex}
81-
coverImage={params.coverImage}
93+
note={note}
94+
characterId={characterId}
95+
placeholderCoverImageIndex={placeholderCoverImageIndex}
96+
coverImage={coverImage}
8297
/>
8398
);
8499
}}
85-
characterId={params.characterId}
86-
note={params.note}
100+
characterId={characterId}
101+
note={note}
87102
scrollEventHandler={scrollVisibilityHandler}
88103
bottomBarHeight={bottomBarHeight}
89104
headerContainerHeight={headerContainerHeight}
@@ -94,8 +109,8 @@ export const PostDetailsPage: FC<NativeStackScreenProps<RootStackParamList, "Pos
94109
<DelayedRender timeout={animationTimeout}>
95110
<BottomSheetModal
96111
ref={bottomSheetModalRef}
97-
note={params.note}
98-
characterId={params.characterId}
112+
note={note}
113+
characterId={characterId}
99114
bottomBarHeight={bottomBarHeight}
100115
/>
101116
</DelayedRender>

src/pages/PostDetails/javascript-content.ts

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ export const javaScriptContentLoaded = (
6161
6262
function handleImageClick(event) {
6363
event.preventDefault();
64+
event.stopPropagation();
65+
6466
const isAvatar = img => img.width === 32 && img.height === 32;
6567
6668
if (isAvatar(event.target)) {
@@ -84,12 +86,17 @@ export const javaScriptContentLoaded = (
8486
8587
function handleLinkClick(event) {
8688
event.preventDefault();
87-
window.ReactNativeWebView.postMessage(
88-
JSON.stringify({
89-
link: event.target.href,
90-
title: event.target.innerText
91-
})
92-
);
89+
90+
let target = event.target.closest('a');
91+
if (target) {
92+
window.ReactNativeWebView.postMessage(
93+
JSON.stringify({
94+
link: target.href,
95+
title: target.innerText
96+
})
97+
);
98+
}
99+
93100
return false;
94101
}
95102
@@ -109,7 +116,7 @@ export const javaScriptContentLoaded = (
109116
110117
for (let i = 0; i < links.length; i++) {
111118
if (!observedLinks.has(links[i])) {
112-
links[i].addEventListener('click', handleLinkClick, true);
119+
links[i].addEventListener('click', handleLinkClick);
113120
observedLinks.add(links[i]);
114121
}
115122
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { match } from "path-to-regexp";
2+
3+
export function getParamsFromShortsURL(url: URL) {
4+
let slug = "";
5+
let handle = "";
6+
7+
// https://caspian-3030.xlog.app/AtOAglnMV_N6xslDbfhz4?ct=shorts
8+
let matched = match<{ slug: string }>("/:slug")(url.pathname);
9+
10+
if (matched) {
11+
slug = matched.params.slug;
12+
handle = url.host.split(".")[0];
13+
}
14+
else {
15+
// https://xlog.app/site/caspian-3030/AtOAglnMV_N6xslDbfhz4?ct=shorts
16+
matched = match<{ slug: string; handle: string }>("/site/:handle/:slug")(
17+
url.pathname,
18+
);
19+
20+
if (!matched) return undefined;
21+
22+
slug = matched?.params.slug || "";
23+
// @ts-expect-error
24+
handle = matched?.params.handle || "";
25+
}
26+
27+
return { slug, handle };
28+
}

yarn.lock

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15930,6 +15930,11 @@ [email protected]:
1593015930
resolved "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c"
1593115931
integrity sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==
1593215932

15933+
path-to-regexp@^6.2.1:
15934+
version "6.2.1"
15935+
resolved "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.2.1.tgz#d54934d6798eb9e5ef14e7af7962c945906918e5"
15936+
integrity sha512-JLyh7xT1kizaEvcaXOQwOc2/Yhw6KZOvPf1S8401UyLk86CU79LN3vl7ztXGm/pZ+YjoyAJ4rxmHwbkBXJX+yw==
15937+
1593315938
path-type@^3.0.0:
1593415939
version "3.0.0"
1593515940
resolved "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz"

0 commit comments

Comments
 (0)