mirror of
https://github.com/hzrd149/nostrudel.git
synced 2025-09-25 11:13:30 +02:00
add compare view to wiki
This commit is contained in:
@@ -123,6 +123,7 @@ const WikiHomeView = lazy(() => import("./views/wiki"));
|
|||||||
const WikiPageView = lazy(() => import("./views/wiki/page"));
|
const WikiPageView = lazy(() => import("./views/wiki/page"));
|
||||||
const WikiTopicView = lazy(() => import("./views/wiki/topic"));
|
const WikiTopicView = lazy(() => import("./views/wiki/topic"));
|
||||||
const WikiSearchView = lazy(() => import("./views/wiki/search"));
|
const WikiSearchView = lazy(() => import("./views/wiki/search"));
|
||||||
|
const WikiCompareView = lazy(() => import("./views/wiki/compare"));
|
||||||
|
|
||||||
const overrideReactTextareaAutocompleteStyles = css`
|
const overrideReactTextareaAutocompleteStyles = css`
|
||||||
.rta__autocomplete {
|
.rta__autocomplete {
|
||||||
@@ -318,6 +319,7 @@ const router = createHashRouter([
|
|||||||
{ path: "search", element: <WikiSearchView /> },
|
{ path: "search", element: <WikiSearchView /> },
|
||||||
{ path: "topic/:topic", element: <WikiTopicView /> },
|
{ path: "topic/:topic", element: <WikiTopicView /> },
|
||||||
{ path: "page/:naddr", element: <WikiPageView /> },
|
{ path: "page/:naddr", element: <WikiPageView /> },
|
||||||
|
{ path: "compare/:topic/:a/:b", element: <WikiCompareView /> },
|
||||||
{ path: "", element: <WikiHomeView /> },
|
{ path: "", element: <WikiHomeView /> },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import { ColorModeContext, useColorMode } from "@chakra-ui/react";
|
import { ColorModeContext, useColorMode } from "@chakra-ui/react";
|
||||||
import ReactDiffViewer from "react-diff-viewer-continued";
|
import ReactDiffViewer, { DiffMethod } from "react-diff-viewer-continued";
|
||||||
import computeStyles, { ReactDiffViewerStylesOverride } from "react-diff-viewer-continued/lib/src/styles";
|
import computeStyles, { ReactDiffViewerStylesOverride } from "react-diff-viewer-continued/lib/src/styles";
|
||||||
|
|
||||||
const fixedStyles: ReactDiffViewerStylesOverride = {
|
const fixedStyles: ReactDiffViewerStylesOverride = {
|
||||||
@@ -35,7 +35,17 @@ class FixedReactDiffViewer extends ReactDiffViewer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function DiffViewer({ oldValue, newValue }: { oldValue: string; newValue: string }) {
|
export default function DiffViewer({
|
||||||
|
oldValue,
|
||||||
|
newValue,
|
||||||
|
method = DiffMethod.WORDS,
|
||||||
|
splitView = false,
|
||||||
|
}: {
|
||||||
|
oldValue: string;
|
||||||
|
newValue: string;
|
||||||
|
method?: DiffMethod;
|
||||||
|
splitView?: boolean;
|
||||||
|
}) {
|
||||||
const { colorMode } = useColorMode();
|
const { colorMode } = useColorMode();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -44,9 +54,8 @@ export default function DiffViewer({ oldValue, newValue }: { oldValue: string; n
|
|||||||
newValue={newValue}
|
newValue={newValue}
|
||||||
useDarkTheme={colorMode === "dark"}
|
useDarkTheme={colorMode === "dark"}
|
||||||
hideLineNumbers
|
hideLineNumbers
|
||||||
splitView={false}
|
splitView={splitView}
|
||||||
//@ts-expect-error
|
compareMethod={method}
|
||||||
compareMethod="diffWords"
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@@ -7,4 +7,5 @@ export const SEARCH_RELAYS = safeRelayUrls([
|
|||||||
// TODO: requires NIP-42 auth
|
// TODO: requires NIP-42 auth
|
||||||
// "wss://filter.nostr.wine",
|
// "wss://filter.nostr.wine",
|
||||||
]);
|
]);
|
||||||
|
export const WIKI_RELAYS = safeRelayUrls(["wss://relay.wikifreedia.xyz/"]);
|
||||||
export const COMMON_CONTACT_RELAY = safeRelayUrl("wss://purplepag.es") as string;
|
export const COMMON_CONTACT_RELAY = safeRelayUrl("wss://purplepag.es") as string;
|
||||||
|
@@ -1,9 +1,10 @@
|
|||||||
import { NostrEvent } from "nostr-tools";
|
import { NostrEvent, nip19 } from "nostr-tools";
|
||||||
|
import { parseCoordinate } from "./event";
|
||||||
|
|
||||||
export const WIKI_PAGE_KIND = 30818;
|
export const WIKI_PAGE_KIND = 30818;
|
||||||
|
|
||||||
export function getPageTitle(page: NostrEvent) {
|
export function getPageTitle(page: NostrEvent) {
|
||||||
return page.tags.find((t) => t[0] === "title")?.[1];
|
return page.tags.find((t) => t[0] === "title")?.[1] || page.tags.find((t) => t[0] === "d")?.[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getPageTopic(page: NostrEvent) {
|
export function getPageTopic(page: NostrEvent) {
|
||||||
@@ -16,3 +17,26 @@ export function getPageSummary(page: NostrEvent) {
|
|||||||
const summary = page.tags.find((t) => t[0] === "summary")?.[1];
|
const summary = page.tags.find((t) => t[0] === "summary")?.[1];
|
||||||
return summary || page.content.split("\n")[0];
|
return summary || page.content.split("\n")[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getPageForks(page: NostrEvent) {
|
||||||
|
const addressFork = page.tags.find((t) => t[0] === "a" && t[1] && t[3]);
|
||||||
|
const eventFork = page.tags.find((t) => t[0] === "a" && t[1] && t[3]);
|
||||||
|
|
||||||
|
const address = addressFork ? parseCoordinate(addressFork[1], true) ?? undefined : undefined;
|
||||||
|
const event: nip19.EventPointer | undefined = eventFork ? { id: eventFork[1] } : undefined;
|
||||||
|
|
||||||
|
return { event, address };
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isPageFork(page: NostrEvent) {
|
||||||
|
return page.tags.some((t) => (t[0] === "a" || t[0] === "e") && t[3] === "fork");
|
||||||
|
}
|
||||||
|
|
||||||
|
export function validatePage(page: NostrEvent) {
|
||||||
|
try {
|
||||||
|
getPageTopic(page);
|
||||||
|
return true;
|
||||||
|
} catch (error) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
74
src/views/wiki/compare.tsx
Normal file
74
src/views/wiki/compare.tsx
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
import { NostrEvent } from "nostr-tools";
|
||||||
|
import { Alert, AlertIcon, Box, ButtonGroup, Divider, Flex, Heading, Spinner, Text } from "@chakra-ui/react";
|
||||||
|
import { Navigate, useParams } from "react-router-dom";
|
||||||
|
|
||||||
|
import useReplaceableEvent from "../../hooks/use-replaceable-event";
|
||||||
|
import VerticalPageLayout from "../../components/vertical-page-layout";
|
||||||
|
import { WIKI_PAGE_KIND, getPageTitle } from "../../helpers/nostr/wiki";
|
||||||
|
import Timestamp from "../../components/timestamp";
|
||||||
|
import DebugEventButton from "../../components/debug-modal/debug-event-button";
|
||||||
|
import WikiPageHeader from "./components/wiki-page-header";
|
||||||
|
import DiffViewer from "../../components/diff/diff-viewer";
|
||||||
|
import { useBreakpointValue } from "../../providers/global/breakpoint-provider";
|
||||||
|
import MarkdownContent from "./components/markdown";
|
||||||
|
import { WIKI_RELAYS } from "../../const";
|
||||||
|
import UserName from "../../components/user/user-name";
|
||||||
|
|
||||||
|
function WikiComparePage({ base, diff }: { base: NostrEvent; diff: NostrEvent }) {
|
||||||
|
const vertical = useBreakpointValue({ base: true, lg: false }) ?? false;
|
||||||
|
const identical = base.content.trim() === diff.content.trim();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<VerticalPageLayout>
|
||||||
|
<WikiPageHeader />
|
||||||
|
|
||||||
|
<Flex gap="4" direction={vertical ? "column" : "row"}>
|
||||||
|
<Box flex={1}>
|
||||||
|
<ButtonGroup float="right">
|
||||||
|
<DebugEventButton event={base} />
|
||||||
|
</ButtonGroup>
|
||||||
|
<Heading>
|
||||||
|
<UserName pubkey={base.pubkey} />
|
||||||
|
</Heading>
|
||||||
|
<Text>
|
||||||
|
{getPageTitle(base)} - <Timestamp timestamp={base.created_at} />
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
<Box flex={1}>
|
||||||
|
<ButtonGroup float="right">
|
||||||
|
<DebugEventButton event={diff} />
|
||||||
|
</ButtonGroup>
|
||||||
|
<Heading>
|
||||||
|
<UserName pubkey={diff.pubkey} />
|
||||||
|
</Heading>
|
||||||
|
<Text>
|
||||||
|
{getPageTitle(diff)} - <Timestamp timestamp={diff.created_at} />
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
</Flex>
|
||||||
|
<Divider />
|
||||||
|
{identical ? (
|
||||||
|
<>
|
||||||
|
<Alert status="info">
|
||||||
|
<AlertIcon />
|
||||||
|
Both versions are identical
|
||||||
|
</Alert>
|
||||||
|
<MarkdownContent event={base} />
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<DiffViewer oldValue={base.content} newValue={diff.content} splitView={!vertical} />
|
||||||
|
)}
|
||||||
|
</VerticalPageLayout>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function WikiCompareView() {
|
||||||
|
const { topic, a, b } = useParams();
|
||||||
|
if (!topic || !a || !b) return <Navigate to="/wiki" />;
|
||||||
|
|
||||||
|
const base = useReplaceableEvent({ kind: WIKI_PAGE_KIND, identifier: topic, pubkey: a }, WIKI_RELAYS);
|
||||||
|
const diff = useReplaceableEvent({ kind: WIKI_PAGE_KIND, identifier: topic, pubkey: b }, WIKI_RELAYS);
|
||||||
|
|
||||||
|
if (!base || !diff) return <Spinner />;
|
||||||
|
return <WikiComparePage base={base} diff={diff} />;
|
||||||
|
}
|
17
src/views/wiki/components/wiki-page-header.tsx
Normal file
17
src/views/wiki/components/wiki-page-header.tsx
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import { Flex, Heading, Link } from "@chakra-ui/react";
|
||||||
|
import { Link as RouterLink } from "react-router-dom";
|
||||||
|
|
||||||
|
import WikiSearchForm from "./wiki-search-form";
|
||||||
|
|
||||||
|
export default function WikiPageHeader() {
|
||||||
|
return (
|
||||||
|
<Flex gap="2" wrap="wrap">
|
||||||
|
<Heading mr="4">
|
||||||
|
<Link as={RouterLink} to="/wiki">
|
||||||
|
Wikifreedia
|
||||||
|
</Link>
|
||||||
|
</Heading>
|
||||||
|
<WikiSearchForm w="full" />
|
||||||
|
</Flex>
|
||||||
|
);
|
||||||
|
}
|
@@ -1,27 +1,62 @@
|
|||||||
import { Heading, LinkBox, Text } from "@chakra-ui/react";
|
import { useMemo } from "react";
|
||||||
import { NostrEvent } from "nostr-tools";
|
import { Box, Button, ButtonGroup, Flex, Heading, LinkBox, Text } from "@chakra-ui/react";
|
||||||
|
import { NostrEvent, nip19 } from "nostr-tools";
|
||||||
import { Link as RouterLink } from "react-router-dom";
|
import { Link as RouterLink } from "react-router-dom";
|
||||||
|
|
||||||
import HoverLinkOverlay from "../../../components/hover-link-overlay";
|
import HoverLinkOverlay from "../../../components/hover-link-overlay";
|
||||||
import { getSharableEventAddress } from "../../../helpers/nip19";
|
import { getSharableEventAddress } from "../../../helpers/nip19";
|
||||||
import { getPageSummary, getPageTitle } from "../../../helpers/nostr/wiki";
|
import { getPageForks, getPageSummary, getPageTitle, getPageTopic } from "../../../helpers/nostr/wiki";
|
||||||
import UserLink from "../../../components/user/user-link";
|
import UserLink from "../../../components/user/user-link";
|
||||||
import Timestamp from "../../../components/timestamp";
|
import Timestamp from "../../../components/timestamp";
|
||||||
|
import FileSearch01 from "../../../components/icons/file-search-01";
|
||||||
|
import GitBranch01 from "../../../components/icons/git-branch-01";
|
||||||
|
import UserName from "../../../components/user/user-name";
|
||||||
|
|
||||||
|
export default function WikiPageResult({ page, compare }: { page: NostrEvent; compare?: NostrEvent }) {
|
||||||
|
const topic = getPageTopic(page);
|
||||||
|
|
||||||
|
const { address } = useMemo(() => getPageForks(page), [page]);
|
||||||
|
|
||||||
export default function WikiPageResult({ page }: { page: NostrEvent }) {
|
|
||||||
return (
|
return (
|
||||||
<LinkBox py="2" px="4">
|
<Flex as={LinkBox} py="2" px="4" direction="column">
|
||||||
<Heading size="md">
|
<Flex gap="2" alignItems="center">
|
||||||
<HoverLinkOverlay as={RouterLink} to={`/wiki/page/${getSharableEventAddress(page)}`}>
|
<Box overflow="hidden">
|
||||||
{getPageTitle(page)}
|
<Heading size="md">
|
||||||
</HoverLinkOverlay>
|
<HoverLinkOverlay as={RouterLink} to={`/wiki/page/${getSharableEventAddress(page)}`}>
|
||||||
</Heading>
|
{getPageTitle(page)}
|
||||||
<Text>
|
</HoverLinkOverlay>
|
||||||
by <UserLink pubkey={page.pubkey} fontWeight="bold " /> - <Timestamp timestamp={page.created_at} />
|
</Heading>
|
||||||
</Text>
|
<Text>
|
||||||
<Text color="GrayText" noOfLines={2}>
|
by <UserLink pubkey={page.pubkey} fontWeight="bold " /> - <Timestamp timestamp={page.created_at} />
|
||||||
{getPageSummary(page)}
|
</Text>
|
||||||
</Text>
|
<Text color="GrayText" noOfLines={2}>
|
||||||
</LinkBox>
|
{getPageSummary(page)}
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
</Flex>
|
||||||
|
<ButtonGroup variant="link" mt="auto">
|
||||||
|
{address && (
|
||||||
|
<Button
|
||||||
|
as={RouterLink}
|
||||||
|
to={`/wiki/page/${nip19.naddrEncode(address)}`}
|
||||||
|
p="2"
|
||||||
|
colorScheme="blue"
|
||||||
|
leftIcon={<GitBranch01 />}
|
||||||
|
>
|
||||||
|
<UserName pubkey={address.pubkey} />
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
{compare && (
|
||||||
|
<Button
|
||||||
|
as={RouterLink}
|
||||||
|
to={`/wiki/compare/${topic}/${compare.pubkey}/${page.pubkey}`}
|
||||||
|
leftIcon={<FileSearch01 />}
|
||||||
|
ml="auto"
|
||||||
|
>
|
||||||
|
Compare
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</ButtonGroup>
|
||||||
|
</Flex>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@@ -1,19 +1,22 @@
|
|||||||
import { NostrEvent } from "nostr-tools";
|
import { NostrEvent } from "nostr-tools";
|
||||||
import { WIKI_PAGE_KIND } from "../../../helpers/nostr/wiki";
|
|
||||||
|
import { WIKI_PAGE_KIND, validatePage } from "../../../helpers/nostr/wiki";
|
||||||
import { useReadRelays } from "../../../hooks/use-client-relays";
|
import { useReadRelays } from "../../../hooks/use-client-relays";
|
||||||
import useTimelineLoader from "../../../hooks/use-timeline-loader";
|
import useTimelineLoader from "../../../hooks/use-timeline-loader";
|
||||||
|
import { WIKI_RELAYS } from "../../../const";
|
||||||
|
|
||||||
function noEmptyEvent(event: NostrEvent) {
|
function eventFilter(event: NostrEvent) {
|
||||||
|
if (!validatePage(event)) return false;
|
||||||
return event.content.length > 0;
|
return event.content.length > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function useWikiTopicTimeline(topic: string) {
|
export default function useWikiTopicTimeline(topic: string) {
|
||||||
const relays = useReadRelays(["wss://relay.wikifreedia.xyz/"]);
|
const relays = useReadRelays(WIKI_RELAYS);
|
||||||
|
|
||||||
return useTimelineLoader(
|
return useTimelineLoader(
|
||||||
`wiki-${topic.toLocaleLowerCase()}-pages`,
|
`wiki-${topic.toLocaleLowerCase()}-pages`,
|
||||||
relays,
|
relays,
|
||||||
[{ kinds: [WIKI_PAGE_KIND], "#d": [topic.toLocaleLowerCase()] }],
|
[{ kinds: [WIKI_PAGE_KIND], "#d": [topic.toLocaleLowerCase()] }],
|
||||||
{ eventFilter: noEmptyEvent },
|
{ eventFilter: eventFilter },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@@ -1,20 +1,28 @@
|
|||||||
import { Box, Flex, Heading, SimpleGrid } from "@chakra-ui/react";
|
import { Box, Flex, Heading, SimpleGrid } from "@chakra-ui/react";
|
||||||
import { Link } from "@chakra-ui/react";
|
import { Link } from "@chakra-ui/react";
|
||||||
import { Link as RouterLink } from "react-router-dom";
|
import { Link as RouterLink } from "react-router-dom";
|
||||||
|
import { NostrEvent } from "nostr-tools";
|
||||||
|
|
||||||
import VerticalPageLayout from "../../components/vertical-page-layout";
|
import VerticalPageLayout from "../../components/vertical-page-layout";
|
||||||
import WikiSearchForm from "./components/wiki-search-form";
|
import WikiSearchForm from "./components/wiki-search-form";
|
||||||
import { WIKI_PAGE_KIND } from "../../helpers/nostr/wiki";
|
import { WIKI_PAGE_KIND, validatePage } from "../../helpers/nostr/wiki";
|
||||||
import useTimelineLoader from "../../hooks/use-timeline-loader";
|
import useTimelineLoader from "../../hooks/use-timeline-loader";
|
||||||
import { useReadRelays } from "../../hooks/use-client-relays";
|
import { useReadRelays } from "../../hooks/use-client-relays";
|
||||||
import useSubject from "../../hooks/use-subject";
|
import useSubject from "../../hooks/use-subject";
|
||||||
import { getWebOfTrust } from "../../services/web-of-trust";
|
import { getWebOfTrust } from "../../services/web-of-trust";
|
||||||
import WikiPageResult from "./components/wiki-page-result";
|
import WikiPageResult from "./components/wiki-page-result";
|
||||||
import TimelineActionAndStatus from "../../components/timeline-page/timeline-action-and-status";
|
import TimelineActionAndStatus from "../../components/timeline-page/timeline-action-and-status";
|
||||||
|
import { ErrorBoundary } from "../../components/error-boundary";
|
||||||
|
import { WIKI_RELAYS } from "../../const";
|
||||||
|
|
||||||
|
function eventFilter(event: NostrEvent) {
|
||||||
|
if (!validatePage(event)) return false;
|
||||||
|
return event.content.length > 0;
|
||||||
|
}
|
||||||
|
|
||||||
export default function WikiHomeView() {
|
export default function WikiHomeView() {
|
||||||
const relays = useReadRelays(["wss://relay.wikifreedia.xyz/"]);
|
const relays = useReadRelays(WIKI_RELAYS);
|
||||||
const timeline = useTimelineLoader(`wiki-recent-pages`, relays, [{ kinds: [WIKI_PAGE_KIND] }]);
|
const timeline = useTimelineLoader(`wiki-recent-pages`, relays, [{ kinds: [WIKI_PAGE_KIND] }], { eventFilter });
|
||||||
|
|
||||||
const pages = useSubject(timeline.timeline).filter((p) => p.content.length > 0);
|
const pages = useSubject(timeline.timeline).filter((p) => p.content.length > 0);
|
||||||
const sorted = getWebOfTrust().sortByDistanceAndConnections(pages, (p) => p.pubkey);
|
const sorted = getWebOfTrust().sortByDistanceAndConnections(pages, (p) => p.pubkey);
|
||||||
@@ -35,7 +43,9 @@ export default function WikiHomeView() {
|
|||||||
</Heading>
|
</Heading>
|
||||||
<SimpleGrid spacing="2" columns={{ base: 1, lg: 2, xl: 3 }}>
|
<SimpleGrid spacing="2" columns={{ base: 1, lg: 2, xl: 3 }}>
|
||||||
{sorted.map((page) => (
|
{sorted.map((page) => (
|
||||||
<WikiPageResult key={page.id} page={page} />
|
<ErrorBoundary key={page.id}>
|
||||||
|
<WikiPageResult page={page} />
|
||||||
|
</ErrorBoundary>
|
||||||
))}
|
))}
|
||||||
</SimpleGrid>
|
</SimpleGrid>
|
||||||
<TimelineActionAndStatus timeline={timeline} />
|
<TimelineActionAndStatus timeline={timeline} />
|
||||||
|
@@ -1,12 +1,22 @@
|
|||||||
import { NostrEvent } from "nostr-tools";
|
import { NostrEvent, nip19 } from "nostr-tools";
|
||||||
import { Box, ButtonGroup, Divider, Flex, Heading, Link, Spinner, Text } from "@chakra-ui/react";
|
import {
|
||||||
|
Alert,
|
||||||
|
AlertIcon,
|
||||||
|
Box,
|
||||||
|
Button,
|
||||||
|
ButtonGroup,
|
||||||
|
Divider,
|
||||||
|
Heading,
|
||||||
|
SimpleGrid,
|
||||||
|
Spinner,
|
||||||
|
Text,
|
||||||
|
} from "@chakra-ui/react";
|
||||||
import { Link as RouterLink } from "react-router-dom";
|
import { Link as RouterLink } from "react-router-dom";
|
||||||
|
|
||||||
import useParamsAddressPointer from "../../hooks/use-params-address-pointer";
|
import useParamsAddressPointer from "../../hooks/use-params-address-pointer";
|
||||||
import useReplaceableEvent from "../../hooks/use-replaceable-event";
|
import useReplaceableEvent from "../../hooks/use-replaceable-event";
|
||||||
import VerticalPageLayout from "../../components/vertical-page-layout";
|
import VerticalPageLayout from "../../components/vertical-page-layout";
|
||||||
import { getPageTitle, getPageTopic } from "../../helpers/nostr/wiki";
|
import { getPageForks, getPageTitle, getPageTopic } from "../../helpers/nostr/wiki";
|
||||||
import WikiSearchForm from "./components/wiki-search-form";
|
|
||||||
import MarkdownContent from "./components/markdown";
|
import MarkdownContent from "./components/markdown";
|
||||||
import UserLink from "../../components/user/user-link";
|
import UserLink from "../../components/user/user-link";
|
||||||
import { getWebOfTrust } from "../../services/web-of-trust";
|
import { getWebOfTrust } from "../../services/web-of-trust";
|
||||||
@@ -15,45 +25,94 @@ import useWikiTopicTimeline from "./hooks/use-wiki-topic-timeline";
|
|||||||
import WikiPageResult from "./components/wiki-page-result";
|
import WikiPageResult from "./components/wiki-page-result";
|
||||||
import Timestamp from "../../components/timestamp";
|
import Timestamp from "../../components/timestamp";
|
||||||
import DebugEventButton from "../../components/debug-modal/debug-event-button";
|
import DebugEventButton from "../../components/debug-modal/debug-event-button";
|
||||||
|
import WikiPageHeader from "./components/wiki-page-header";
|
||||||
|
import { WIKI_RELAYS } from "../../const";
|
||||||
|
import GitBranch01 from "../../components/icons/git-branch-01";
|
||||||
|
import { ExternalLinkIcon } from "../../components/icons";
|
||||||
|
import FileSearch01 from "../../components/icons/file-search-01";
|
||||||
|
import NoteZapButton from "../../components/note/note-zap-button";
|
||||||
|
import ZapBubbles from "../../components/note/timeline-note/components/zap-bubbles";
|
||||||
|
import QuoteRepostButton from "../../components/note/quote-repost-button";
|
||||||
|
|
||||||
function WikiPagePage({ page }: { page: NostrEvent }) {
|
function WikiPagePage({ page }: { page: NostrEvent }) {
|
||||||
const topic = getPageTopic(page);
|
const topic = getPageTopic(page);
|
||||||
const timeline = useWikiTopicTimeline(topic);
|
const timeline = useWikiTopicTimeline(topic);
|
||||||
|
|
||||||
const pages = useSubject(timeline.timeline).filter((p) => p.pubkey !== page.pubkey);
|
const pages = useSubject(timeline.timeline).filter((p) => p.pubkey !== page.pubkey);
|
||||||
const sorted = getWebOfTrust().sortByDistanceAndConnections(pages, (p) => p.pubkey);
|
const { address } = getPageForks(page);
|
||||||
|
|
||||||
|
const forks = getWebOfTrust().sortByDistanceAndConnections(
|
||||||
|
pages.filter((p) => getPageForks(p).address?.pubkey === page.pubkey),
|
||||||
|
(p) => p.pubkey,
|
||||||
|
);
|
||||||
|
const other = getWebOfTrust().sortByDistanceAndConnections(
|
||||||
|
pages.filter((p) => !forks.includes(p)),
|
||||||
|
(p) => p.pubkey,
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<VerticalPageLayout>
|
<VerticalPageLayout>
|
||||||
<Flex gap="2" wrap="wrap">
|
<WikiPageHeader />
|
||||||
<Heading mr="4">
|
|
||||||
<Link as={RouterLink} to="/wiki">
|
|
||||||
Wikifreedia
|
|
||||||
</Link>
|
|
||||||
</Heading>
|
|
||||||
<WikiSearchForm w="full" />
|
|
||||||
</Flex>
|
|
||||||
|
|
||||||
<Box>
|
<Box>
|
||||||
<ButtonGroup float="right">
|
<ButtonGroup float="right">
|
||||||
|
<QuoteRepostButton event={page} />
|
||||||
|
<NoteZapButton event={page} showEventPreview={false} />
|
||||||
<DebugEventButton event={page} />
|
<DebugEventButton event={page} />
|
||||||
</ButtonGroup>
|
</ButtonGroup>
|
||||||
<Heading>{getPageTitle(page)}</Heading>
|
<Heading>{getPageTitle(page)}</Heading>
|
||||||
<Text>
|
<Text>
|
||||||
by <UserLink pubkey={page.pubkey} /> - <Timestamp timestamp={page.created_at} />
|
by <UserLink pubkey={page.pubkey} /> - <Timestamp timestamp={page.created_at} />
|
||||||
</Text>
|
</Text>
|
||||||
|
{address && (
|
||||||
|
<Alert status="info" display="flex" flexWrap="wrap">
|
||||||
|
<AlertIcon>
|
||||||
|
<GitBranch01 boxSize={5} />
|
||||||
|
</AlertIcon>
|
||||||
|
<Text>
|
||||||
|
This page was forked from <UserLink pubkey={address.pubkey} fontWeight="bold" /> version
|
||||||
|
</Text>
|
||||||
|
<ButtonGroup variant="link" ml="auto">
|
||||||
|
<Button leftIcon={<ExternalLinkIcon />} as={RouterLink} to={`/wiki/page/${nip19.naddrEncode(address)}`}>
|
||||||
|
Original
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
leftIcon={<FileSearch01 />}
|
||||||
|
as={RouterLink}
|
||||||
|
to={`/wiki/compare/${topic}/${address.pubkey}/${page.pubkey}`}
|
||||||
|
>
|
||||||
|
Compare
|
||||||
|
</Button>
|
||||||
|
</ButtonGroup>
|
||||||
|
</Alert>
|
||||||
|
)}
|
||||||
<Divider my="2" />
|
<Divider my="2" />
|
||||||
<MarkdownContent event={page} />
|
<MarkdownContent event={page} />
|
||||||
|
<ZapBubbles event={page} />
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{sorted.length > 0 && (
|
{forks.length > 0 && (
|
||||||
|
<>
|
||||||
|
<Heading size="lg" mt="4">
|
||||||
|
Forks:
|
||||||
|
</Heading>
|
||||||
|
<SimpleGrid spacing="2" columns={{ base: 1, lg: 2, xl: 3 }}>
|
||||||
|
{forks.map((p) => (
|
||||||
|
<WikiPageResult key={p.id} page={p} compare={page} />
|
||||||
|
))}
|
||||||
|
</SimpleGrid>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{other.length > 0 && (
|
||||||
<>
|
<>
|
||||||
<Heading size="lg" mt="4">
|
<Heading size="lg" mt="4">
|
||||||
Other Versions:
|
Other Versions:
|
||||||
</Heading>
|
</Heading>
|
||||||
{sorted.slice(0, 6).map((page) => (
|
<SimpleGrid spacing="2" columns={{ base: 1, lg: 2, xl: 3 }}>
|
||||||
<WikiPageResult key={page.id} page={page} />
|
{other.map((p) => (
|
||||||
))}
|
<WikiPageResult key={p.id} page={p} compare={page} />
|
||||||
|
))}
|
||||||
|
</SimpleGrid>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</VerticalPageLayout>
|
</VerticalPageLayout>
|
||||||
@@ -62,7 +121,7 @@ function WikiPagePage({ page }: { page: NostrEvent }) {
|
|||||||
|
|
||||||
export default function WikiPageView() {
|
export default function WikiPageView() {
|
||||||
const pointer = useParamsAddressPointer("naddr");
|
const pointer = useParamsAddressPointer("naddr");
|
||||||
const event = useReplaceableEvent(pointer, ["wss://relay.wikifreedia.xyz/"]);
|
const event = useReplaceableEvent(pointer, WIKI_RELAYS);
|
||||||
|
|
||||||
if (!event) return <Spinner />;
|
if (!event) return <Spinner />;
|
||||||
return <WikiPagePage page={event} />;
|
return <WikiPagePage page={event} />;
|
||||||
|
Reference in New Issue
Block a user