BookmarkLinkPreview.tsx•5.29 kB
import { useState } from "react";
import { Pressable, View } from "react-native";
import ImageView from "react-native-image-viewing";
import WebView from "react-native-webview";
import { WebViewSourceUri } from "react-native-webview/lib/WebViewTypes";
import { Text } from "@/components/ui/Text";
import { useAssetUrl } from "@/lib/hooks";
import { api } from "@/lib/trpc";
import { useColorScheme } from "@/lib/useColorScheme";
import { BookmarkTypes, ZBookmark } from "@karakeep/shared/types/bookmarks";
import FullPageError from "../FullPageError";
import FullPageSpinner from "../ui/FullPageSpinner";
import BookmarkAssetImage from "./BookmarkAssetImage";
export function BookmarkLinkBrowserPreview({
bookmark,
}: {
bookmark: ZBookmark;
}) {
if (bookmark.content.type !== BookmarkTypes.LINK) {
throw new Error("Wrong content type rendered");
}
return (
<WebView
startInLoadingState={true}
mediaPlaybackRequiresUserAction={true}
source={{ uri: bookmark.content.url }}
/>
);
}
export function BookmarkLinkReaderPreview({
bookmark,
}: {
bookmark: ZBookmark;
}) {
const { isDarkColorScheme: isDark } = useColorScheme();
const {
data: bookmarkWithContent,
error,
isLoading,
refetch,
} = api.bookmarks.getBookmark.useQuery({
bookmarkId: bookmark.id,
includeContent: true,
});
if (isLoading) {
return <FullPageSpinner />;
}
if (error) {
return <FullPageError error={error.message} onRetry={refetch} />;
}
if (bookmarkWithContent?.content.type !== BookmarkTypes.LINK) {
throw new Error("Wrong content type rendered");
}
return (
<View className="flex-1 bg-background">
<WebView
originWhitelist={["*"]}
source={{
html: `
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;
line-height: 1.6;
color: ${isDark ? "#e5e7eb" : "#374151"};
margin: 0;
padding: 16px;
background: ${isDark ? "#000000" : "#ffffff"};
}
p { margin: 0 0 1em 0; }
h1, h2, h3, h4, h5, h6 { margin: 1.5em 0 0.5em 0; line-height: 1.2; }
img { max-width: 100%; height: auto; border-radius: 8px; }
a { color: #3b82f6; text-decoration: none; }
a:hover { text-decoration: underline; }
blockquote {
border-left: 4px solid ${isDark ? "#374151" : "#e5e7eb"};
margin: 1em 0;
padding-left: 1em;
color: ${isDark ? "#9ca3af" : "#6b7280"};
}
pre {
background: ${isDark ? "#1f2937" : "#f3f4f6"};
padding: 1em;
border-radius: 6px;
overflow-x: auto;
}
</style>
</head>
<body>
${bookmarkWithContent.content.htmlContent}
</body>
</html>
`,
}}
style={{
flex: 1,
backgroundColor: isDark ? "#000000" : "#ffffff",
}}
showsVerticalScrollIndicator={false}
showsHorizontalScrollIndicator={false}
decelerationRate={0.998}
/>
</View>
);
}
export function BookmarkLinkArchivePreview({
bookmark,
}: {
bookmark: ZBookmark;
}) {
const asset =
bookmark.assets.find((r) => r.assetType == "precrawledArchive") ??
bookmark.assets.find((r) => r.assetType == "fullPageArchive");
const assetSource = useAssetUrl(asset?.id ?? "");
if (!asset) {
return (
<View className="flex-1 bg-background">
<Text>Asset has no offline archive</Text>
</View>
);
}
const webViewUri: WebViewSourceUri = {
uri: assetSource.uri!,
headers: assetSource.headers,
};
return (
<WebView
startInLoadingState={true}
mediaPlaybackRequiresUserAction={true}
source={webViewUri}
decelerationRate={0.998}
/>
);
}
export function BookmarkLinkScreenshotPreview({
bookmark,
}: {
bookmark: ZBookmark;
}) {
const asset = bookmark.assets.find((r) => r.assetType == "screenshot");
const assetSource = useAssetUrl(asset?.id ?? "");
const [imageZoom, setImageZoom] = useState(false);
if (!asset) {
return (
<View className="flex-1 bg-background">
<Text>Asset has no screenshot</Text>
</View>
);
}
return (
<View className="flex flex-1 gap-2">
<ImageView
visible={imageZoom}
imageIndex={0}
onRequestClose={() => setImageZoom(false)}
doubleTapToZoomEnabled={true}
images={[assetSource]}
/>
<Pressable onPress={() => setImageZoom(true)}>
<BookmarkAssetImage
assetId={asset.id}
className="h-full w-full object-contain"
/>
</Pressable>
</View>
);
}