diff --git a/.changeset/purple-carrots-worry.md b/.changeset/purple-carrots-worry.md
new file mode 100644
index 000000000..e7152b34b
--- /dev/null
+++ b/.changeset/purple-carrots-worry.md
@@ -0,0 +1,5 @@
+---
+"nostrudel": minor
+---
+
+Remove NIP-72 communities
diff --git a/src/app.tsx b/src/app.tsx
index 2a211b7e7..6f3721ef3 100644
--- a/src/app.tsx
+++ b/src/app.tsx
@@ -70,12 +70,6 @@ const BadgesView = lazy(() => import("./views/badges"));
const BadgesBrowseView = lazy(() => import("./views/badges/browse"));
const BadgeDetailsView = lazy(() => import("./views/badges/badge-details"));
-const CommunitiesHomeView = lazy(() => import("./views/communities"));
-const CommunityFindByNameView = lazy(() => import("./views/community/find-by-name"));
-const CommunityView = lazy(() => import("./views/community/index"));
-const CommunityPendingView = lazy(() => import("./views/community/views/pending"));
-const CommunityNewestView = lazy(() => import("./views/community/views/newest"));
-
import RelaysView from "./views/relays";
import RelayView from "./views/relays/relay";
import BrowseRelaySetsView from "./views/relays/browse-sets";
@@ -458,10 +452,6 @@ const router = createHashRouter([
{ path: "", element: },
],
},
- {
- path: "communities",
- children: [{ path: "", element: }],
- },
{
path: "articles",
children: [
@@ -469,21 +459,6 @@ const router = createHashRouter([
{ path: ":naddr", element: },
],
},
- {
- path: "c/:community",
- children: [
- { path: "", element: },
- {
- path: ":pubkey",
- element: ,
- children: [
- { path: "", element: },
- { path: "newest", element: },
- { path: "pending", element: },
- ],
- },
- ],
- },
{
path: "torrents",
children: [
diff --git a/src/components/common-event/event-verification-icon.tsx b/src/components/common-event/event-verification-icon.tsx
index 8de85a6df..ce8433ea5 100644
--- a/src/components/common-event/event-verification-icon.tsx
+++ b/src/components/common-event/event-verification-icon.tsx
@@ -3,7 +3,7 @@ import { verifyEvent } from "nostr-tools";
import { NostrEvent } from "../../types/nostr-event";
import { CheckIcon, VerificationFailed } from "../icons";
-import useAppSettings from "../../hooks/use-app-settings";
+import useAppSettings from "../../hooks/use-user-app-settings";
function EventVerificationIcon({ event }: { event: NostrEvent }) {
const { showSignatureVerification } = useAppSettings();
diff --git a/src/components/content/components/expandable-embed.tsx b/src/components/content/components/expandable-embed.tsx
index 227b6062f..5ba71f3bf 100644
--- a/src/components/content/components/expandable-embed.tsx
+++ b/src/components/content/components/expandable-embed.tsx
@@ -2,7 +2,7 @@ import { PropsWithChildren, ReactNode } from "react";
import EmbedActions from "./embed-actions";
import { Link, useDisclosure } from "@chakra-ui/react";
-import useAppSettings from "../../../hooks/use-app-settings";
+import useAppSettings from "../../../hooks/use-user-app-settings";
export default function ExpandableEmbed({
children,
diff --git a/src/components/content/links/image.tsx b/src/components/content/links/image.tsx
index 1126c7d0d..a7a5e9f74 100644
--- a/src/components/content/links/image.tsx
+++ b/src/components/content/links/image.tsx
@@ -28,7 +28,7 @@ import {
import { useRegisterSlide } from "../../lightbox-provider";
import { isImageURL } from "../../../helpers/url";
import { NostrEvent } from "../../../types/nostr-event";
-import useAppSettings from "../../../hooks/use-app-settings";
+import useAppSettings from "../../../hooks/use-user-app-settings";
import useElementTrustBlur from "../../../hooks/use-element-trust-blur";
import { buildImageProxyURL } from "../../../helpers/image";
import ExpandableEmbed from "../components/expandable-embed";
diff --git a/src/components/content/links/music.tsx b/src/components/content/links/music.tsx
index 75e102677..9051af464 100644
--- a/src/components/content/links/music.tsx
+++ b/src/components/content/links/music.tsx
@@ -3,7 +3,7 @@ import { Box, useColorMode } from "@chakra-ui/react";
import { EmbedEventPointer } from "../../embed-event";
import { STEMSTR_RELAY } from "../../../helpers/nostr/stemstr";
import ExpandableEmbed from "../components/expandable-embed";
-import useAppSettings from "../../../hooks/use-app-settings";
+import useAppSettings from "../../../hooks/use-user-app-settings";
const setZIndex: CSSProperties = { zIndex: 1, position: "relative" };
diff --git a/src/components/content/links/reddit.tsx b/src/components/content/links/reddit.tsx
index 4d542fe2f..3d0627ad8 100644
--- a/src/components/content/links/reddit.tsx
+++ b/src/components/content/links/reddit.tsx
@@ -1,5 +1,5 @@
import { replaceDomain } from "../../../helpers/url";
-import useAppSettings from "../../../hooks/use-app-settings";
+import useAppSettings from "../../../hooks/use-user-app-settings";
import { renderGenericUrl } from "./common";
// copied from https://github.com/SimonBrazell/privacy-redirect/blob/master/src/assets/javascripts/helpers/reddit.js
diff --git a/src/components/content/links/twitter.tsx b/src/components/content/links/twitter.tsx
index d2caeae8f..d2946da7d 100644
--- a/src/components/content/links/twitter.tsx
+++ b/src/components/content/links/twitter.tsx
@@ -2,7 +2,7 @@ import { Link } from "applesauce-content/nast";
import { renderOpenGraphUrl } from "./common";
import { replaceDomain } from "../../../helpers/url";
-import useAppSettings from "../../../hooks/use-app-settings";
+import useAppSettings from "../../../hooks/use-user-app-settings";
// copied from https://github.com/SimonBrazell/privacy-redirect/blob/master/src/assets/javascripts/helpers/twitter.js
export const TWITTER_DOMAINS = ["x.com", "twitter.com", "www.twitter.com", "mobile.twitter.com", "pbs.twimg.com"];
diff --git a/src/components/content/links/video.tsx b/src/components/content/links/video.tsx
index 4451f8b0f..931964edb 100644
--- a/src/components/content/links/video.tsx
+++ b/src/components/content/links/video.tsx
@@ -2,7 +2,7 @@ import { lazy, VideoHTMLAttributes } from "react";
import styled from "@emotion/styled";
import { isStreamURL, isVideoURL } from "../../../helpers/url";
-import useAppSettings from "../../../hooks/use-app-settings";
+import useAppSettings from "../../../hooks/use-user-app-settings";
import useElementTrustBlur from "../../../hooks/use-element-trust-blur";
import ExpandableEmbed from "../components/expandable-embed";
const LiveVideoPlayer = lazy(() => import("../../live-video-player"));
diff --git a/src/components/content/links/youtube.tsx b/src/components/content/links/youtube.tsx
index 0133c57cb..dfdf635b3 100644
--- a/src/components/content/links/youtube.tsx
+++ b/src/components/content/links/youtube.tsx
@@ -1,6 +1,6 @@
import { AspectRatio } from "@chakra-ui/react";
import ExpandableEmbed from "../components/expandable-embed";
-import useAppSettings from "../../../hooks/use-app-settings";
+import useAppSettings from "../../../hooks/use-user-app-settings";
// copied from https://github.com/SimonBrazell/privacy-redirect/blob/master/src/assets/javascripts/helpers/youtube.js
export const YOUTUBE_DOMAINS = [
diff --git a/src/components/embed-event/event-types/embedded-community.tsx b/src/components/embed-event/event-types/embedded-community.tsx
index 45cdc25e2..dc7b35ec1 100644
--- a/src/components/embed-event/event-types/embedded-community.tsx
+++ b/src/components/embed-event/event-types/embedded-community.tsx
@@ -1,17 +1,21 @@
-import { Link as RouterLink } from "react-router-dom";
+import { useContext } from "react";
import { Card, CardFooter, CardHeader, CardProps, Heading, LinkBox, LinkOverlay, Text } from "@chakra-ui/react";
-import { nip19 } from "nostr-tools";
+import { getTagValue } from "applesauce-core/helpers";
import UserAvatarLink from "../../user/user-avatar-link";
import UserLink from "../../user/user-link";
import { NostrEvent } from "../../../types/nostr-event";
-import { getCommunityImage, getCommunityName } from "../../../helpers/nostr/communities";
+import { AppHandlerContext } from "../../../providers/route/app-handler-provider";
+import useShareableEventAddress from "../../../hooks/use-shareable-event-address";
export default function EmbeddedCommunity({
community,
...props
}: Omit & { community: NostrEvent }) {
- const name = getCommunityName(community);
+ const name = getTagValue(community, "name") || getTagValue(community, "d");
+ const image = getTagValue(community, "image");
+ const naddr = useShareableEventAddress(community);
+ const { openAddress } = useContext(AppHandlerContext);
return (
-
- {name}
-
+ naddr && openAddress(naddr)}>{name}
diff --git a/src/components/embed-event/event-types/embedded-note.tsx b/src/components/embed-event/event-types/embedded-note.tsx
index 738fbab2c..63ea2a82e 100644
--- a/src/components/embed-event/event-types/embedded-note.tsx
+++ b/src/components/embed-event/event-types/embedded-note.tsx
@@ -16,7 +16,7 @@ import HoverLinkOverlay from "../../hover-link-overlay";
import singleEventService from "../../../services/single-event";
import { getSharableEventAddress } from "../../../services/relay-hints";
import localSettings from "../../../services/local-settings";
-import useAppSettings from "../../../hooks/use-app-settings";
+import useAppSettings from "../../../hooks/use-user-app-settings";
export default function EmbeddedNote({ event, ...props }: Omit & { event: NostrEvent }) {
const { showSignatureVerification } = useAppSettings();
diff --git a/src/components/embed-event/event-types/embedded-torrent-comment.tsx b/src/components/embed-event/event-types/embedded-torrent-comment.tsx
index 4efc0d243..40f7a24db 100644
--- a/src/components/embed-event/event-types/embedded-torrent-comment.tsx
+++ b/src/components/embed-event/event-types/embedded-torrent-comment.tsx
@@ -15,7 +15,7 @@ import { getTorrentTitle } from "../../../helpers/nostr/torrents";
import { useNavigateInDrawer } from "../../../providers/drawer-sub-view-provider";
import { MouseEventHandler, useCallback } from "react";
import { nip19 } from "nostr-tools";
-import useAppSettings from "../../../hooks/use-app-settings";
+import useAppSettings from "../../../hooks/use-user-app-settings";
export default function EmbeddedTorrentComment({
comment,
diff --git a/src/components/event-zap-modal/input-step.tsx b/src/components/event-zap-modal/input-step.tsx
index db6ed1ed7..16ed1ee55 100644
--- a/src/components/event-zap-modal/input-step.tsx
+++ b/src/components/event-zap-modal/input-step.tsx
@@ -7,7 +7,7 @@ import { humanReadableSats } from "../../helpers/lightning";
import { LightningIcon } from "../icons";
import useUserLNURLMetadata from "../../hooks/use-user-lnurl-metadata";
import { EmbedEvent, EmbedProps } from "../embed-event";
-import useAppSettings from "../../hooks/use-app-settings";
+import useAppSettings from "../../hooks/use-user-app-settings";
import CustomZapAmountOptions from "./zap-options";
import UserAvatar from "../user/user-avatar";
import UserLink from "../user/user-link";
diff --git a/src/components/event-zap-modal/pay-step.tsx b/src/components/event-zap-modal/pay-step.tsx
index 096cbf494..eae6a5b44 100644
--- a/src/components/event-zap-modal/pay-step.tsx
+++ b/src/components/event-zap-modal/pay-step.tsx
@@ -17,7 +17,7 @@ import UserLink from "../user/user-link";
import { ChevronDownIcon, ChevronUpIcon, CheckIcon, ErrorIcon, LightningIcon } from "../icons";
import { InvoiceModalContent } from "../invoice-modal";
import { PropsWithChildren, useEffect, useState } from "react";
-import useAppSettings from "../../hooks/use-app-settings";
+import useAppSettings from "../../hooks/use-user-app-settings";
function UserCard({ children, pubkey }: PropsWithChildren & { pubkey: string }) {
return (
diff --git a/src/components/event-zap-modal/zap-options.tsx b/src/components/event-zap-modal/zap-options.tsx
index 38c9cb5c9..1973158fb 100644
--- a/src/components/event-zap-modal/zap-options.tsx
+++ b/src/components/event-zap-modal/zap-options.tsx
@@ -1,6 +1,6 @@
import { Button, Flex } from "@chakra-ui/react";
-import useAppSettings from "../../hooks/use-app-settings";
+import useAppSettings from "../../hooks/use-user-app-settings";
import { LightningIcon } from "../icons";
export default function CustomZapAmountOptions({ onSelect }: { onSelect: (value: number) => void }) {
diff --git a/src/components/icons.tsx b/src/components/icons.tsx
index 762565cb8..d32018bd7 100644
--- a/src/components/icons.tsx
+++ b/src/components/icons.tsx
@@ -223,12 +223,6 @@ export const AppearanceIcon = Colors;
export const DatabaseIcon = Database01;
export const PerformanceIcon = Speedometer03;
-export const CommunityIcon = createIcon({
- displayName: "CommunityIcon",
- d: "M9.55 11.5C8.30736 11.5 7.3 10.4926 7.3 9.25C7.3 8.00736 8.30736 7 9.55 7C10.7926 7 11.8 8.00736 11.8 9.25C11.8 10.4926 10.7926 11.5 9.55 11.5ZM10 19.748V16.4C10 15.9116 10.1442 15.4627 10.4041 15.0624C10.1087 15.0213 9.80681 15 9.5 15C7.93201 15 6.49369 15.5552 5.37091 16.4797C6.44909 18.0721 8.08593 19.2553 10 19.748ZM4.45286 14.66C5.86432 13.6168 7.61013 13 9.5 13C10.5435 13 11.5431 13.188 12.4667 13.5321C13.3447 13.1888 14.3924 13 15.5 13C17.1597 13 18.6849 13.4239 19.706 14.1563C19.8976 13.4703 20 12.7471 20 12C20 7.58172 16.4183 4 12 4C7.58172 4 4 7.58172 4 12C4 12.9325 4.15956 13.8278 4.45286 14.66ZM18.8794 16.0859C18.4862 15.5526 17.1708 15 15.5 15C13.4939 15 12 15.7967 12 16.4V20C14.9255 20 17.4843 18.4296 18.8794 16.0859ZM12 22C6.47715 22 2 17.5228 2 12C2 6.47715 6.47715 2 12 2C17.5228 2 22 6.47715 22 12C22 17.5228 17.5228 22 12 22ZM15.5 12.5C14.3954 12.5 13.5 11.6046 13.5 10.5C13.5 9.39543 14.3954 8.5 15.5 8.5C16.6046 8.5 17.5 9.39543 17.5 10.5C17.5 11.6046 16.6046 12.5 15.5 12.5Z",
- defaultProps,
-});
-
/** @deprecated */
export const GhostIcon = createIcon({
displayName: "GhostIcon",
@@ -255,4 +249,4 @@ export const WikiIcon = BookOpen01;
export const ArticleIcon = Edit04;
export const VideoIcon = Film02;
-export const MediaIcon = Camera01
+export const MediaIcon = Camera01;
diff --git a/src/components/layout/nav-items.tsx b/src/components/layout/nav-items.tsx
index c5410f53c..2f07ba3aa 100644
--- a/src/components/layout/nav-items.tsx
+++ b/src/components/layout/nav-items.tsx
@@ -49,9 +49,7 @@ export default function NavItems() {
else if (location.pathname.startsWith("/relays")) active = "relays";
else if (location.pathname.startsWith("/r/")) active = "relays";
else if (location.pathname.startsWith("/lists")) active = "lists";
- else if (location.pathname.startsWith("/communities")) active = "communities";
else if (location.pathname.startsWith("/channels")) active = "channels";
- else if (location.pathname.startsWith("/c/")) active = "communities";
else if (location.pathname.startsWith("/goals")) active = "goals";
else if (location.pathname.startsWith("/badges")) active = "badges";
else if (location.pathname.startsWith("/emojis")) active = "emojis";
@@ -83,8 +81,8 @@ export default function NavItems() {
if (apps.length < 3 && !apps.some((a) => a.id === "streams")) {
apps.push(internal.find((app) => app.id === "streams")!);
}
- if (apps.length < 3 && !apps.some((a) => a.id === "communities")) {
- apps.push(internal.find((app) => app.id === "communities")!);
+ if (apps.length < 3 && !apps.some((a) => a.id === "articles")) {
+ apps.push(internal.find((app) => app.id === "articles")!);
}
if (apps.length < 3 && !apps.some((a) => a.id === "channels")) {
apps.push(internal.find((app) => app.id === "channels")!);
diff --git a/src/components/media-post/media-post-card.tsx b/src/components/media-post/media-post-card.tsx
index 8fe7cb0d5..1360d2973 100644
--- a/src/components/media-post/media-post-card.tsx
+++ b/src/components/media-post/media-post-card.tsx
@@ -9,7 +9,7 @@ import DebugEventButton from "../debug-modal/debug-event-button";
import { TrustProvider } from "../../providers/local/trust-provider";
import EventReactionButtons from "../event-reactions/event-reactions";
import AddReactionButton from "../note/timeline-note/components/add-reaction-button";
-import RepostButton from "../note/timeline-note/components/repost-button";
+import ShareButton from "../note/timeline-note/components/share-button";
import QuoteEventButton from "../note/quote-event-button";
import MediaPostSlides from "./media-slides";
import MediaPostContents from "./media-post-content";
@@ -55,7 +55,7 @@ export default function MediaPost({ post }: { post: NostrEvent }) {
-
+
diff --git a/src/components/note/timeline-note/components/repost-modal.tsx b/src/components/note/timeline-note/components/repost-modal.tsx
deleted file mode 100644
index d34a0b862..000000000
--- a/src/components/note/timeline-note/components/repost-modal.tsx
+++ /dev/null
@@ -1,126 +0,0 @@
-import { useState } from "react";
-import {
- Button,
- Modal,
- ModalBody,
- ModalCloseButton,
- ModalContent,
- ModalFooter,
- ModalHeader,
- ModalOverlay,
- ModalProps,
- SimpleGrid,
- useDisclosure,
-} from "@chakra-ui/react";
-import { EventTemplate, NostrEvent, kinds } from "nostr-tools";
-import dayjs from "dayjs";
-import type { AddressPointer } from "nostr-tools/nip19";
-
-import { ChevronDownIcon, ChevronUpIcon, ExternalLinkIcon } from "../../../icons";
-import { getAddressPointerRelayHints, getEventRelayHint } from "../../../../services/relay-hints";
-import { usePublishEvent } from "../../../../providers/global/publish-provider";
-import useCurrentAccount from "../../../../hooks/use-current-account";
-import useUserCommunitiesList from "../../../../hooks/use-user-communities-list";
-import { createCoordinate } from "../../../../classes/batch-kind-pubkey-loader";
-import { EmbedEvent } from "../../../embed-event";
-
-function buildRepost(event: NostrEvent): EventTemplate {
- const hint = getEventRelayHint(event);
- const tags: NostrEvent["tags"] = [];
- tags.push(["e", event.id, hint ?? ""]);
- tags.push(["k", String(event.kind)]);
-
- return {
- kind: event.kind === kinds.ShortTextNote ? kinds.Repost : kinds.GenericRepost,
- tags,
- content: JSON.stringify(event),
- created_at: dayjs().unix(),
- };
-}
-
-export default function RepostModal({
- event,
- isOpen,
- onClose,
- ...props
-}: Omit & { event: NostrEvent }) {
- const account = useCurrentAccount();
- const publish = usePublishEvent();
- const showCommunities = useDisclosure();
- const { pointers } = useUserCommunitiesList(account?.pubkey);
-
- const [loading, setLoading] = useState(false);
- const repost = async (communityPointer?: AddressPointer) => {
- setLoading(true);
- const draft = buildRepost(event);
- if (communityPointer) {
- draft.tags.push([
- "a",
- createCoordinate(communityPointer.kind, communityPointer.pubkey, communityPointer.identifier),
- getAddressPointerRelayHints(communityPointer)[0],
- ]);
- }
- await publish("Repost", draft);
- onClose();
- setLoading(false);
- };
-
- return (
-
-
-
-
- Repost Note
-
-
-
-
- {showCommunities.isOpen && (
-
- {pointers.map((pointer) => (
- }
- isLoading={loading}
- onClick={() => repost(pointer)}
- >
- {pointer.identifier}
-
- ))}
-
- )}
-
-
-
- : }
- py="2"
- onClick={showCommunities.onToggle}
- >
- Repost to community
-
-
- {!showCommunities.isOpen && (
-
- )}
-
-
-
- );
-}
diff --git a/src/components/note/timeline-note/components/repost-button.tsx b/src/components/note/timeline-note/components/share-button.tsx
similarity index 62%
rename from src/components/note/timeline-note/components/repost-button.tsx
rename to src/components/note/timeline-note/components/share-button.tsx
index 090ce08cb..f5ec738dd 100644
--- a/src/components/note/timeline-note/components/repost-button.tsx
+++ b/src/components/note/timeline-note/components/share-button.tsx
@@ -5,27 +5,27 @@ import { NostrEvent } from "../../../../types/nostr-event";
import { RepostIcon } from "../../../icons";
import useEventCount from "../../../../hooks/use-event-count";
import useCurrentAccount from "../../../../hooks/use-current-account";
-import RepostModal from "./repost-modal";
+import ShareModal from "./share-modal";
-export default function RepostButton({ event }: { event: NostrEvent }) {
+export default function ShareButton({ event }: { event: NostrEvent }) {
const { isOpen, onClose, onOpen } = useDisclosure();
const account = useCurrentAccount();
- const hasReposted = useEventCount(
+ const hasShared = useEventCount(
account ? { "#e": [event.id], kinds: [kinds.Repost, kinds.GenericRepost], authors: [account.pubkey] } : undefined,
);
- const repostCount = useEventCount({ "#e": [event.id], kinds: [kinds.Repost, kinds.GenericRepost] });
+ const ShareCount = useEventCount({ "#e": [event.id], kinds: [kinds.Repost, kinds.GenericRepost] });
return (
<>
- {repostCount !== undefined && repostCount > 0 ? (
+ {ShareCount !== undefined && ShareCount > 0 ? (
}
onClick={onOpen}
title="Repost Note"
- colorScheme={hasReposted ? "primary" : undefined}
+ colorScheme={hasShared ? "primary" : undefined}
>
- {repostCount}
+ {ShareCount}
) : (
)}
- {isOpen && }
+ {isOpen && }
>
);
}
diff --git a/src/components/note/timeline-note/components/share-modal.tsx b/src/components/note/timeline-note/components/share-modal.tsx
new file mode 100644
index 000000000..08cbd7eac
--- /dev/null
+++ b/src/components/note/timeline-note/components/share-modal.tsx
@@ -0,0 +1,68 @@
+import { useState } from "react";
+import {
+ Button,
+ Modal,
+ ModalBody,
+ ModalCloseButton,
+ ModalContent,
+ ModalFooter,
+ ModalHeader,
+ ModalOverlay,
+ ModalProps,
+} from "@chakra-ui/react";
+import { NostrEvent } from "nostr-tools";
+import { useEventFactory } from "applesauce-react/hooks";
+
+import { usePublishEvent } from "../../../../providers/global/publish-provider";
+import { EmbedEvent } from "../../../embed-event";
+
+export default function ShareModal({
+ event,
+ isOpen,
+ onClose,
+ ...props
+}: Omit & { event: NostrEvent }) {
+ const publish = usePublishEvent();
+ const factory = useEventFactory();
+
+ const [loading, setLoading] = useState(false);
+ const share = async () => {
+ setLoading(true);
+ const draft = await factory.share(event);
+
+ await publish("Share", draft);
+ onClose();
+ setLoading(false);
+ };
+
+ return (
+
+
+
+
+ Share Note
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/src/components/note/timeline-note/index.tsx b/src/components/note/timeline-note/index.tsx
index 3abd7680e..142f69582 100644
--- a/src/components/note/timeline-note/index.tsx
+++ b/src/components/note/timeline-note/index.tsx
@@ -23,7 +23,7 @@ import UserLink from "../../user/user-link";
import NoteZapButton from "../note-zap-button";
import { ExpandProvider } from "../../../providers/local/expanded";
import EventVerificationIcon from "../../common-event/event-verification-icon";
-import RepostButton from "./components/repost-button";
+import ShareButton from "./components/share-button";
import QuoteEventButton from "../quote-event-button";
import { ReplyIcon } from "../../icons";
import NoteContentWithWarning from "./note-content-with-warning";
@@ -47,7 +47,7 @@ import useEventIntersectionRef from "../../../hooks/use-event-intersection-ref";
import { getSharableEventAddress } from "../../../services/relay-hints";
import localSettings from "../../../services/local-settings";
import NotePublishedUsing from "../note-published-using";
-import useAppSettings from "../../../hooks/use-app-settings";
+import useAppSettings from "../../../hooks/use-user-app-settings";
export type TimelineNoteProps = Omit & {
event: NostrEvent;
@@ -130,7 +130,7 @@ export function TimelineNote({
{showReplyButton && (
} aria-label="Reply" title="Reply" onClick={replyForm.onOpen} />
)}
-
+
diff --git a/src/components/note/timeline-note/note-community-metadata.tsx b/src/components/note/timeline-note/note-community-metadata.tsx
index 64a845bca..eba1edfcd 100644
--- a/src/components/note/timeline-note/note-community-metadata.tsx
+++ b/src/components/note/timeline-note/note-community-metadata.tsx
@@ -1,22 +1,24 @@
-import { useMemo } from "react";
-import { Link as RouterLink } from "react-router-dom";
+import { useContext, useMemo } from "react";
import { Link, Text, TextProps } from "@chakra-ui/react";
-import { NostrEvent } from "nostr-tools";
+import { nip19, NostrEvent } from "nostr-tools";
import { getEventCommunityPointer } from "../../../helpers/nostr/communities";
+import { AppHandlerContext } from "../../../providers/route/app-handler-provider";
+/** @deprecated remove when communities are no longer supported */
export default function NoteCommunityMetadata({
event,
...props
}: Omit & { event: NostrEvent }) {
const communityPointer = useMemo(() => getEventCommunityPointer(event), [event]);
+ const { openAddress } = useContext(AppHandlerContext);
if (!communityPointer) return null;
return (
Posted in{" "}
-
+ openAddress(nip19.naddrEncode(communityPointer))} color="blue.500">
{communityPointer.identifier}
{" "}
community
diff --git a/src/components/note/timeline-note/note-content-with-warning.tsx b/src/components/note/timeline-note/note-content-with-warning.tsx
index eea83b7c8..6612b5037 100644
--- a/src/components/note/timeline-note/note-content-with-warning.tsx
+++ b/src/components/note/timeline-note/note-content-with-warning.tsx
@@ -4,7 +4,7 @@ import { getContentWarning } from "applesauce-core/helpers";
import { TextNoteContents } from "./text-note-contents";
import { useExpand } from "../../../providers/local/expanded";
import ContentWarning from "../../content-warning";
-import useAppSettings from "../../../hooks/use-app-settings";
+import useAppSettings from "../../../hooks/use-user-app-settings";
export default function NoteContentWithWarning({ event }: { event: NostrEvent }) {
const expand = useExpand();
diff --git a/src/components/post-modal/community-select.tsx b/src/components/post-modal/community-select.tsx
deleted file mode 100644
index 6019643ec..000000000
--- a/src/components/post-modal/community-select.tsx
+++ /dev/null
@@ -1,30 +0,0 @@
-import { forwardRef } from "react";
-import { Select, SelectProps } from "@chakra-ui/react";
-
-import useUserCommunitiesList from "../../hooks/use-user-communities-list";
-import useCurrentAccount from "../../hooks/use-current-account";
-import { getCommunityName } from "../../helpers/nostr/communities";
-import { AddressPointer } from "nostr-tools/nip19";
-import useReplaceableEvent from "../../hooks/use-replaceable-event";
-import { getEventCoordinate } from "../../helpers/nostr/event";
-
-function CommunityOption({ pointer }: { pointer: AddressPointer }) {
- const community = useReplaceableEvent(pointer);
- if (!community) return;
-
- return ;
-}
-
-const CommunitySelect = forwardRef>(({ ...props }, ref) => {
- const account = useCurrentAccount();
- const { pointers } = useUserCommunitiesList(account?.pubkey);
-
- return (
-
- );
-});
-export default CommunitySelect;
diff --git a/src/components/post-modal/index.tsx b/src/components/post-modal/index.tsx
index 6e1b2f71f..b16a6adcf 100644
--- a/src/components/post-modal/index.tsx
+++ b/src/components/post-modal/index.tsx
@@ -38,13 +38,12 @@ import { PublishDetails } from "../../views/task-manager/publish-log/publish-det
import { TrustProvider } from "../../providers/local/trust-provider";
import MagicTextArea, { RefType } from "../magic-textarea";
import { useContextEmojis } from "../../providers/global/emoji-provider";
-import CommunitySelect from "./community-select";
import ZapSplitCreator from "../../views/new/note/zap-split-creator";
import useCurrentAccount from "../../hooks/use-current-account";
import useCacheForm from "../../hooks/use-cache-form";
import useTextAreaUploadFile, { useTextAreaInsertTextWithForm } from "../../hooks/use-textarea-upload-file";
import MinePOW from "../pow/mine-pow";
-import useAppSettings from "../../hooks/use-app-settings";
+import useAppSettings from "../../hooks/use-user-app-settings";
import { ErrorBoundary } from "../error-boundary";
import { useFinalizeDraft, usePublishEvent } from "../../providers/global/publish-provider";
import { TextNoteContents } from "../note/timeline-note/text-note-contents";
@@ -54,11 +53,9 @@ import InsertGifButton from "../gif/insert-gif-button";
import InsertImageButton from "../../views/new/note/insert-image-button";
type FormValues = {
- subject: string;
content: string;
nsfw: boolean;
nsfwReason: string;
- community: string;
split: Omit[];
difficulty: number;
};
@@ -66,8 +63,6 @@ type FormValues = {
export type PostModalProps = {
cacheFormKey?: string | null;
initContent?: string;
- initCommunity?: string;
- requireSubject?: boolean;
};
export default function PostModal({
@@ -75,8 +70,6 @@ export default function PostModal({
onClose,
cacheFormKey = "new-note",
initContent = "",
- initCommunity = "",
- requireSubject,
}: Omit & PostModalProps) {
const publish = usePublishEvent();
const finalizeDraft = useFinalizeDraft();
@@ -93,11 +86,9 @@ export default function PostModal({
const [draft, setDraft] = useState();
const { getValues, setValue, watch, register, handleSubmit, formState, reset } = useForm({
defaultValues: {
- subject: "",
content: initContent,
nsfw: false,
nsfwReason: "",
- community: initCommunity,
split: [] as Omit[],
difficulty: noteDifficulty || 0,
},
@@ -123,10 +114,6 @@ export default function PostModal({
splits: values.split,
});
- // TODO: remove when NIP-72 communities are removed
- if (values.community) draft.tags.push(["a", values.community]);
- if (values.subject) draft.tags.push(["subject", values.subject]);
-
const unsigned = await finalizeDraft(draft);
setDraft(unsigned);
@@ -188,7 +175,6 @@ export default function PostModal({
return (
<>
- {requireSubject && }
-
- Post to community
-
-
NSFW
{getValues().nsfw && (
diff --git a/src/components/timeline-page/generic-note-timeline/repost-event.tsx b/src/components/timeline-page/generic-note-timeline/share-event.tsx
similarity index 74%
rename from src/components/timeline-page/generic-note-timeline/repost-event.tsx
rename to src/components/timeline-page/generic-note-timeline/share-event.tsx
index e38edb0d8..0064d9a82 100644
--- a/src/components/timeline-page/generic-note-timeline/repost-event.tsx
+++ b/src/components/timeline-page/generic-note-timeline/share-event.tsx
@@ -1,7 +1,6 @@
import { memo } from "react";
-import { Flex, Heading, Link, Text } from "@chakra-ui/react";
+import { Flex, Heading, Text } from "@chakra-ui/react";
import { kinds, nip18 } from "nostr-tools";
-import { Link as RouterLink } from "react-router-dom";
import { NostrEvent } from "../../../types/nostr-event";
import TimelineNote from "../../note/timeline-note";
@@ -13,12 +12,11 @@ import useSingleEvent from "../../../hooks/use-single-event";
import { EmbedEvent } from "../../embed-event";
import useUserMuteFilter from "../../../hooks/use-user-mute-filter";
import { parseHardcodedNoteContent } from "../../../helpers/nostr/event";
-import { getEventCommunityPointer } from "../../../helpers/nostr/communities";
import LoadingNostrLink from "../../loading-nostr-link";
import NoteMenu from "../../note/note-menu";
import useEventIntersectionRef from "../../../hooks/use-event-intersection-ref";
-function RepostEvent({ event }: { event: NostrEvent }) {
+function ShareEvent({ event }: { event: NostrEvent }) {
const muteFilter = useUserMuteFilter();
const hardCodedNote = parseHardcodedNoteContent(event);
@@ -26,8 +24,6 @@ function RepostEvent({ event }: { event: NostrEvent }) {
const loadedNote = useSingleEvent(pointer?.id, pointer?.relays);
const note = hardCodedNote || loadedNote;
- const communityCoordinate = getEventCommunityPointer(event);
-
const ref = useEventIntersectionRef(event);
if ((note && muteFilter(note)) || !pointer) return null;
@@ -41,18 +37,7 @@ function RepostEvent({ event }: { event: NostrEvent }) {
-
- {communityCoordinate ? `Shared to` : `Shared`}
-
- {communityCoordinate && (
-
- {communityCoordinate.identifier}
-
- )}
+ Shared
{!note ? (
@@ -68,4 +53,4 @@ function RepostEvent({ event }: { event: NostrEvent }) {
);
}
-export default memo(RepostEvent);
+export default memo(ShareEvent);
diff --git a/src/components/timeline-page/generic-note-timeline/timeline-item.tsx b/src/components/timeline-page/generic-note-timeline/timeline-item.tsx
index 126c67c96..94a3f839b 100644
--- a/src/components/timeline-page/generic-note-timeline/timeline-item.tsx
+++ b/src/components/timeline-page/generic-note-timeline/timeline-item.tsx
@@ -4,7 +4,7 @@ import { Box } from "@chakra-ui/react";
import { ErrorBoundary } from "../../error-boundary";
import ReplyNote from "./reply-note";
-import RepostEvent from "./repost-event";
+import ShareEvent from "./share-event";
import StreamNote from "./stream-note";
import RelayRecommendation from "./relay-recommendation";
import BadgeAwardCard from "../../../views/badges/components/badge-award-card";
@@ -29,7 +29,7 @@ function TimelineItem({ event, visible, minHeight }: { event: NostrEvent; visibl
break;
case kinds.Repost:
case kinds.GenericRepost:
- content = ;
+ content = ;
break;
case kinds.LiveEvent:
content = ;
diff --git a/src/components/user/user-avatar.tsx b/src/components/user/user-avatar.tsx
index 45b2df52a..d1eac5cb5 100644
--- a/src/components/user/user-avatar.tsx
+++ b/src/components/user/user-avatar.tsx
@@ -7,7 +7,7 @@ import { ProfileContent } from "applesauce-core/helpers";
import { getIdenticon } from "../../helpers/identicon";
import { safeUrl } from "../../helpers/parse";
import { getDisplayName } from "../../helpers/nostr/profile";
-import useAppSettings from "../../hooks/use-app-settings";
+import useAppSettings from "../../hooks/use-user-app-settings";
import useCurrentAccount from "../../hooks/use-current-account";
import { buildImageProxyURL } from "../../helpers/image";
import UserDnsIdentityIcon from "./user-dns-identity-icon";
diff --git a/src/components/user/user-link.tsx b/src/components/user/user-link.tsx
index a9577790b..fd00fab49 100644
--- a/src/components/user/user-link.tsx
+++ b/src/components/user/user-link.tsx
@@ -4,7 +4,7 @@ import { nip19 } from "nostr-tools";
import { getDisplayName } from "../../helpers/nostr/profile";
import useUserProfile from "../../hooks/use-user-profile";
-import useAppSettings from "../../hooks/use-app-settings";
+import useAppSettings from "../../hooks/use-user-app-settings";
import useCurrentAccount from "../../hooks/use-current-account";
export type UserLinkProps = LinkProps & {
diff --git a/src/components/user/user-name.tsx b/src/components/user/user-name.tsx
index 0f9f216ae..3d998b3e3 100644
--- a/src/components/user/user-name.tsx
+++ b/src/components/user/user-name.tsx
@@ -3,7 +3,7 @@ import { Text, TextProps } from "@chakra-ui/react";
import { getDisplayName } from "../../helpers/nostr/profile";
import useUserProfile from "../../hooks/use-user-profile";
-import useAppSettings from "../../hooks/use-app-settings";
+import useAppSettings from "../../hooks/use-user-app-settings";
function UserName({ pubkey, ...props }: Omit & { pubkey: string }) {
const metadata = useUserProfile(pubkey);
diff --git a/src/helpers/app-settings.ts b/src/helpers/app-settings.ts
index 709bac206..d07f2d916 100644
--- a/src/helpers/app-settings.ts
+++ b/src/helpers/app-settings.ts
@@ -1,6 +1,7 @@
import { ColorModeWithSystem } from "@chakra-ui/react";
+import { kinds } from "nostr-tools";
-export const APP_SETTINGS_KIND = 30078;
+export const APP_SETTINGS_KIND = kinds.Application;
export const APP_SETTING_IDENTIFIER = "nostrudel-settings";
export type AppSettingsV0 = {
diff --git a/src/helpers/nostr/communities.ts b/src/helpers/nostr/communities.ts
index 8f8e4cbf0..1a315cc72 100644
--- a/src/helpers/nostr/communities.ts
+++ b/src/helpers/nostr/communities.ts
@@ -39,49 +39,6 @@ export function getCommunityRanking(community: NostrEvent) {
return community.tags.find((t) => t[0] === "rank_mode")?.[1];
}
-export function getPostSubject(event: NostrEvent) {
- const subject = event.tags.find((t) => t[0] === "subject")?.[1];
- if (subject) return subject;
- const firstLine = event.content.match(/^[^\n\t]+/)?.[0];
- if (!firstLine) return;
- if (!getMatchNostrLink().test(firstLine) && !getMatchLink().test(firstLine)) return firstLine;
-}
-
-export function getApprovedEmbeddedNote(approval: NostrEvent) {
- if (!approval.content) return null;
- try {
- const json = JSON.parse(approval.content);
- validateEvent(json);
- return (json as NostrEvent) ?? null;
- } catch (e) {}
- return null;
-}
-
-export function validateCommunity(community: NostrEvent) {
- try {
- getCommunityName(community);
- return true;
- } catch (e) {
- return false;
- }
-}
-
-export function buildApprovalMap(events: Iterable, mods: string[]) {
- const approvals = new Map();
- for (const event of events) {
- if (event.kind === kinds.CommunityPostApproval && mods.includes(event.pubkey)) {
- for (const tag of event.tags) {
- if (isETag(tag)) {
- const arr = approvals.get(tag[1]);
- if (!arr) approvals.set(tag[1], [event]);
- else arr.push(event);
- }
- }
- }
- }
- return approvals;
-}
-
export function getEventCommunityPointer(event: NostrEvent) {
const communityTag = event.tags.filter(isATag).find((t) => t[1].startsWith(kinds.CommunityDefinition + ":"));
return communityTag ? parseCoordinate(communityTag[1], true) : null;
diff --git a/src/helpers/nostr/lists.ts b/src/helpers/nostr/lists.ts
index c187954cb..f06bcedcf 100644
--- a/src/helpers/nostr/lists.ts
+++ b/src/helpers/nostr/lists.ts
@@ -1,8 +1,8 @@
import dayjs from "dayjs";
-import { EventTemplate, NostrEvent, kinds, nip19 } from "nostr-tools";
+import { EventTemplate, NostrEvent, kinds } from "nostr-tools";
import { getPointerFromTag } from "applesauce-core/helpers";
-import { PTag, isATag, isDTag, isPTag, isRTag } from "../../types/nostr-event";
+import { PTag, isDTag, isPTag, isRTag } from "../../types/nostr-event";
import { getEventCoordinate, replaceOrAddSimpleTag } from "./event";
import { getRelayVariations, safeRelayUrls } from "../relay";
import { isAddressPointerInList, isEventPointerInList, isProfilePointerInList } from "applesauce-lists/helpers";
diff --git a/src/hooks/use-count-community-members.ts b/src/hooks/use-count-community-members.ts
deleted file mode 100644
index 20dc14144..000000000
--- a/src/hooks/use-count-community-members.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-import { kinds } from "nostr-tools";
-
-import { getEventCoordinate } from "../helpers/nostr/event";
-import { NostrEvent } from "../types/nostr-event";
-import useEventCount from "./use-event-count";
-
-export default function useCountCommunityMembers(community: NostrEvent) {
- return useEventCount({ "#a": [getEventCoordinate(community)], kinds: [kinds.CommunitiesList] });
-}
diff --git a/src/hooks/use-mute-word-filter.ts b/src/hooks/use-mute-word-filter.ts
index b283973ea..fc01e2066 100644
--- a/src/hooks/use-mute-word-filter.ts
+++ b/src/hooks/use-mute-word-filter.ts
@@ -1,7 +1,7 @@
import { useCallback, useMemo } from "react";
import { NostrEvent } from "../types/nostr-event";
-import useAppSettings from "./use-app-settings";
+import useAppSettings from "./use-user-app-settings";
export default function useWordMuteFilter() {
const { mutedWords } = useAppSettings();
diff --git a/src/hooks/use-open-graph-data.ts b/src/hooks/use-open-graph-data.ts
index 7e85b74ef..acc0ebdf1 100644
--- a/src/hooks/use-open-graph-data.ts
+++ b/src/hooks/use-open-graph-data.ts
@@ -1,7 +1,7 @@
import { useAsync } from "react-use";
import { fetchWithProxy } from "../helpers/request";
import type { OgObjectInteral } from "../lib/open-graph-scraper/types";
-import useAppSettings from "./use-app-settings";
+import useAppSettings from "./use-user-app-settings";
const pageExtensions = [".html", ".php", "htm"];
diff --git a/src/hooks/use-set-color-mode.ts b/src/hooks/use-set-color-mode.ts
index 4499b516f..be68df72c 100644
--- a/src/hooks/use-set-color-mode.ts
+++ b/src/hooks/use-set-color-mode.ts
@@ -1,7 +1,7 @@
import { useColorMode } from "@chakra-ui/react";
import { useSearchParams } from "react-router-dom";
import { useEffect } from "react";
-import useAppSettings from "./use-app-settings";
+import useAppSettings from "./use-user-app-settings";
export default function useSetColorMode() {
const { setColorMode } = useColorMode();
diff --git a/src/hooks/use-textarea-upload-file.ts b/src/hooks/use-textarea-upload-file.ts
index 52ff53306..ed3d9aeb4 100644
--- a/src/hooks/use-textarea-upload-file.ts
+++ b/src/hooks/use-textarea-upload-file.ts
@@ -5,7 +5,7 @@ import { nostrBuildUploadImage } from "../helpers/media-upload/nostr-build";
import { RefType } from "../components/magic-textarea";
import { useSigningContext } from "../providers/global/signing-provider";
import { UseFormGetValues, UseFormSetValue } from "react-hook-form";
-import useAppSettings from "./use-app-settings";
+import useAppSettings from "./use-user-app-settings";
import useUsersMediaServers from "./use-user-media-servers";
import { simpleMultiServerUpload } from "../helpers/media-upload/blossom";
import useCurrentAccount from "./use-current-account";
diff --git a/src/hooks/use-app-settings.ts b/src/hooks/use-user-app-settings.ts
similarity index 83%
rename from src/hooks/use-app-settings.ts
rename to src/hooks/use-user-app-settings.ts
index 6e34a129c..9848c7eaf 100644
--- a/src/hooks/use-app-settings.ts
+++ b/src/hooks/use-user-app-settings.ts
@@ -19,12 +19,19 @@ function buildAppSettingsEvent(settings: Partial): EventTemplate {
};
}
+export function useUserAppSettings(pubkey: string) {
+ useReplaceableEvent({ kind: APP_SETTINGS_KIND, pubkey, identifier: APP_SETTING_IDENTIFIER });
+ return useStoreQuery(AppSettingsQuery, [pubkey]);
+}
+
export default function useAppSettings() {
const account = useCurrentAccount();
const publish = usePublishEvent();
// load synced settings
- useReplaceableEvent(account?.pubkey && { kind: APP_SETTINGS_KIND, pubkey: account.pubkey });
+ useReplaceableEvent(
+ account?.pubkey && { kind: APP_SETTINGS_KIND, pubkey: account.pubkey, identifier: APP_SETTING_IDENTIFIER },
+ );
const localSettings = account?.localSettings;
const syncedSettings = useStoreQuery(AppSettingsQuery, account && [account.pubkey]);
diff --git a/src/hooks/use-user-communities-list.ts b/src/hooks/use-user-communities-list.ts
deleted file mode 100644
index 1314332de..000000000
--- a/src/hooks/use-user-communities-list.ts
+++ /dev/null
@@ -1,41 +0,0 @@
-import { kinds } from "nostr-tools";
-import { getAddressPointersFromList } from "applesauce-lists/helpers";
-
-import { SUBSCRIBED_COMMUNITIES_LIST_IDENTIFIER } from "../helpers/nostr/communities";
-import { RequestOptions } from "../services/replaceable-events";
-import useCurrentAccount from "./use-current-account";
-import useReplaceableEvent from "./use-replaceable-event";
-
-export default function useUserCommunitiesList(pubkey?: string, relays?: Iterable, opts?: RequestOptions) {
- const account = useCurrentAccount();
- const key = pubkey ?? account?.pubkey;
-
- // TODO: remove at some future date when apps have transitioned to using k:10004 for communities
- // https://github.com/nostr-protocol/nips/pull/880
- /** @deprecated */
- const oldList = useReplaceableEvent(
- key
- ? {
- kind: kinds.Genericlists,
- identifier: SUBSCRIBED_COMMUNITIES_LIST_IDENTIFIER,
- pubkey: key,
- }
- : undefined,
- [],
- opts,
- );
- const list = useReplaceableEvent(key ? { kind: kinds.CommunitiesList, pubkey: key } : undefined, relays, opts);
-
- let useList = list || oldList;
-
- // if both exist, use the newest one
- if (list && oldList) {
- useList = list.created_at > oldList.created_at ? list : oldList;
- }
-
- const pointers = useList
- ? getAddressPointersFromList(useList).filter((cord) => cord.kind === kinds.CommunityDefinition)
- : [];
-
- return { list: useList, pointers };
-}
diff --git a/src/providers/global/index.tsx b/src/providers/global/index.tsx
index bc6431033..638e2fabf 100644
--- a/src/providers/global/index.tsx
+++ b/src/providers/global/index.tsx
@@ -4,7 +4,7 @@ import { QueryStoreProvider } from "applesauce-react/providers";
import { SigningProvider } from "./signing-provider";
import buildTheme from "../../theme";
-import useAppSettings from "../../hooks/use-app-settings";
+import useAppSettings from "../../hooks/use-user-app-settings";
import NotificationsProvider from "./notifications-provider";
import { UserEmojiProvider } from "./emoji-provider";
import BreakpointProvider from "./breakpoint-provider";
diff --git a/src/providers/route/invoice-modal-provider.tsx b/src/providers/route/invoice-modal-provider.tsx
index ca913c179..cebb110f2 100644
--- a/src/providers/route/invoice-modal-provider.tsx
+++ b/src/providers/route/invoice-modal-provider.tsx
@@ -1,7 +1,7 @@
import React, { useCallback, useContext, useState } from "react";
import InvoiceModal from "../../components/invoice-modal";
import createDefer, { Deferred } from "../../classes/deferred";
-import useAppSettings from "../../hooks/use-app-settings";
+import useAppSettings from "../../hooks/use-user-app-settings";
export type InvoiceModalContext = {
requestPay: (invoice: string) => Promise;
diff --git a/src/views/communities/components/community-card.tsx b/src/views/communities/components/community-card.tsx
deleted file mode 100644
index fcdd1447d..000000000
--- a/src/views/communities/components/community-card.tsx
+++ /dev/null
@@ -1,86 +0,0 @@
-import { memo } from "react";
-import { nip19 } from "nostr-tools";
-import { Link as RouterLink } from "react-router-dom";
-import {
- Card,
- CardFooter,
- CardHeader,
- CardProps,
- Heading,
- LinkBox,
- LinkOverlay,
- Tag,
- TagLabel,
- TagLeftIcon,
- Text,
-} from "@chakra-ui/react";
-
-import { NostrEvent } from "../../../types/nostr-event";
-import { getCommunityImage, getCommunityName } from "../../../helpers/nostr/communities";
-import UserAvatarLink from "../../../components/user/user-avatar-link";
-import UserLink from "../../../components/user/user-link";
-import useCountCommunityMembers from "../../../hooks/use-count-community-members";
-import { humanReadableSats } from "../../../helpers/lightning";
-import User01 from "../../../components/icons/user-01";
-import useReplaceableEvent from "../../../hooks/use-replaceable-event";
-import { AddressPointer } from "nostr-tools/nip19";
-import useEventIntersectionRef from "../../../hooks/use-event-intersection-ref";
-
-function CommunityCard({ community, ...props }: Omit & { community: NostrEvent }) {
- const ref = useEventIntersectionRef(community);
-
- const name = getCommunityName(community);
- const countMembers = useCountCommunityMembers(community);
-
- // NOTE: disabled because nostr.band has a rate limit
- // const notesInLastMonth = useCountCommunityPosts(community);
-
- return (
-
-
-
-
- {name}
-
-
-
- {/*
-
- */}
-
-
- by
-
- {countMembers !== undefined && countMembers > 0 && (
-
-
- {humanReadableSats(countMembers)}
-
- )}
-
- {/* {notesInLastMonth !== undefined && {notesInLastMonth} Posts in the past month} */}
-
-
- );
-}
-
-export function PointerCommunityCard({ pointer, ...props }: Omit & { pointer: AddressPointer }) {
- const community = useReplaceableEvent(pointer);
- if (!community) return Loading {pointer.identifier};
- return ;
-}
-
-export default memo(CommunityCard);
diff --git a/src/views/communities/components/community-create-modal.tsx b/src/views/communities/components/community-create-modal.tsx
deleted file mode 100644
index 4706e0d76..000000000
--- a/src/views/communities/components/community-create-modal.tsx
+++ /dev/null
@@ -1,350 +0,0 @@
-import { useCallback, useState } from "react";
-import {
- Box,
- Button,
- Flex,
- FormControl,
- FormErrorMessage,
- FormHelperText,
- FormLabel,
- IconButton,
- IconButtonProps,
- Input,
- Link,
- Modal,
- ModalBody,
- ModalCloseButton,
- ModalContent,
- ModalFooter,
- ModalHeader,
- ModalOverlay,
- ModalProps,
- Text,
- Textarea,
- useToast,
-} from "@chakra-ui/react";
-import { SubmitHandler, useForm } from "react-hook-form";
-
-import useCurrentAccount from "../../../hooks/use-current-account";
-import UserAvatar from "../../../components/user/user-avatar";
-import UserLink from "../../../components/user/user-link";
-import { TrashIcon } from "../../../components/icons";
-import Upload01 from "../../../components/icons/upload-01";
-import { nostrBuildUploadImage } from "../../../helpers/media-upload/nostr-build";
-import { useSigningContext } from "../../../providers/global/signing-provider";
-import { RelayUrlInput } from "../../../components/relay-url-input";
-import { RelayFavicon } from "../../../components/relay-favicon";
-import UserAutocomplete from "../../../components/user-autocomplete";
-import { normalizeToHexPubkey } from "../../../helpers/nip19";
-import { safeUrl } from "../../../helpers/parse";
-import { safeRelayUrl } from "../../../helpers/relay";
-
-function RemoveButton({ ...props }: IconButtonProps) {
- return } size="sm" colorScheme="red" variant="ghost" ml="auto" {...props} />;
-}
-
-export type FormValues = {
- name: string;
- banner: string;
- description: string;
- rules: string;
- mods: string[];
- relays: string[];
- links: ([string] | [string, string])[];
- // ranking: string;
-};
-
-export default function CommunityCreateModal({
- isOpen,
- onClose,
- onSubmit,
- defaultValues,
- isUpdate,
- ...props
-}: Omit & {
- onSubmit: SubmitHandler;
- defaultValues?: FormValues;
- isUpdate?: boolean;
-}) {
- const toast = useToast();
- const account = useCurrentAccount();
- const { requestSignature } = useSigningContext();
-
- const {
- register,
- formState: { errors, isSubmitting },
- handleSubmit,
- watch,
- getValues,
- setValue,
- } = useForm({
- mode: "all",
- defaultValues: defaultValues || {
- name: "",
- banner: "",
- description: "",
- rules: "",
- mods: account ? [account.pubkey] : [],
- relays: [],
- links: [],
- // ranking: "votes",
- },
- });
-
- watch("mods");
- // watch("ranking");
- watch("banner");
- watch("links");
- watch("relays");
-
- const [uploading, setUploading] = useState(false);
- const uploadFile = useCallback(
- async (file: File) => {
- try {
- if (!(file.type.includes("image") || file.type.includes("video") || file.type.includes("audio")))
- throw new Error("Unsupported file type");
-
- setUploading(true);
-
- const response = await nostrBuildUploadImage(file, requestSignature);
- const imageUrl = response.url;
- setValue("banner", imageUrl, { shouldDirty: true, shouldValidate: true });
- } catch (e) {
- if (e instanceof Error) toast({ description: e.message, status: "error" });
- }
- setUploading(false);
- },
- [setValue, getValues, requestSignature, toast],
- );
-
- const [modInput, setModInput] = useState("");
- const addMod = () => {
- if (!modInput) return;
- const pubkey = normalizeToHexPubkey(modInput);
- if (pubkey) {
- setValue("mods", getValues("mods").concat(pubkey));
- }
- setModInput("");
- };
- const removeMod = (pubkey: string) => {
- setValue(
- "mods",
- getValues("mods").filter((p) => p !== pubkey),
- );
- };
-
- const [relayInput, setRelayInput] = useState("");
- const addRelay = () => {
- if (!relayInput) return;
- const url = safeRelayUrl(relayInput);
- if (url) {
- setValue("relays", getValues("relays").concat(url));
- }
- setRelayInput("");
- };
- const removeRelay = (url: string) => {
- setValue(
- "relays",
- getValues("relays").filter((r) => r !== url),
- );
- };
-
- const [linkInput, setLinkInput] = useState("");
- const [linkName, setLinkName] = useState("");
- const addLink = () => {
- if (!linkInput) return;
- const url = safeUrl(linkInput);
- if (url) {
- setValue("links", [...getValues("links"), linkName ? [url, linkName] : [url]]);
- }
- setLinkInput("");
- setLinkName("");
- };
- const removeLink = (url: string) => {
- setValue(
- "links",
- getValues("links").filter(([r]) => r !== url),
- );
- };
-
- return (
-
-
-
- {isUpdate ? "Update Community" : "Create Community"}
-
-
- {!isUpdate && (
-
- Community Name
- {
- if (/\p{Z}/iu.test(v)) return "Must not have spaces";
- return true;
- },
- })}
- isReadOnly={isUpdate}
- autoComplete="off"
- placeholder="more-cat-pictures"
- />
- The name of your community (no-spaces)
- {errors.name?.message && {errors.name?.message}}
-
- )}
-
-
- Description
-
- Short description about your community
- {errors.description?.message && {errors.description?.message}}
-
-
-
- Banner
- {getValues().banner && (
-
- )}
-
-
- {
- const img = e.target.files?.[0];
- if (img) uploadFile(img);
- }}
- />
- }
- aria-label="Upload Image"
- cursor="pointer"
- tabIndex={0}
- isLoading={uploading}
- />
-
- {errors.banner?.message && {errors.banner?.message}}
-
-
-
- Rules and Guidelines
-
- Rules and posting guidelines
- {errors.rules?.message && {errors.rules?.message}}
-
-
-
- Moderators
-
- {getValues().mods.map((pubkey) => (
-
-
-
- removeMod(pubkey)}
- />
-
- ))}
-
-
- setModInput(e.target.value)} />
-
-
-
-
- {/*
- Default Raking
- setValue("ranking", e, { shouldDirty: true, shouldTouch: true })}
- >
-
- Votes
- Zaps
-
-
- The default way posts are ranked when viewing the community
- {errors.ranking?.message && {errors.ranking?.message}}
- */}
-
-
- Relays
- A Short list of recommended relays for the community
-
- {getValues().relays.map((url) => (
-
-
-
- {url}
-
- removeRelay(url)} />
-
- ))}
-
-
- setRelayInput(e.target.value)} />
-
-
-
-
-
- Links
- A few helpful resources for the community
-
- {getValues().links.map(([link, name]) => (
-
- {name || link}
- removeLink(link)} />
-
- ))}
-
-
- setLinkInput(e.target.value)}
- />
- setLinkName(e.target.value)} />
-
-
-
-
-
-
-
-
-
-
-
- );
-}
diff --git a/src/views/communities/components/community-description.tsx b/src/views/communities/components/community-description.tsx
deleted file mode 100644
index cac64e7d3..000000000
--- a/src/views/communities/components/community-description.tsx
+++ /dev/null
@@ -1,40 +0,0 @@
-import { useState } from "react";
-import { Box, BoxProps, Button } from "@chakra-ui/react";
-import { useRenderedContent } from "applesauce-react/hooks";
-
-import { NostrEvent } from "../../../types/nostr-event";
-import { getCommunityDescription } from "../../../helpers/nostr/communities";
-import { components } from "../../../components/content";
-import { renderGenericUrl } from "../../../components/content/links";
-
-const linkRenderers = [renderGenericUrl];
-
-const CommunityDescriptionSymbol = Symbol.for("community-description-content");
-
-export default function CommunityDescription({
- community,
- maxLength,
- showExpand,
- ...props
-}: Omit & { community: NostrEvent; maxLength?: number; showExpand?: boolean }) {
- const description = getCommunityDescription(community);
- const [showAll, setShowAll] = useState(false);
- const content = useRenderedContent(description, components, {
- maxLength: showAll ? undefined : maxLength,
- linkRenderers,
- cacheKey: CommunityDescriptionSymbol,
- });
-
- return (
- <>
-
- {content}
-
- {maxLength !== undefined && showExpand && !showAll && (description?.length ?? 0) > maxLength && (
-
- )}
- >
- );
-}
diff --git a/src/views/communities/components/community-join-button.tsx b/src/views/communities/components/community-join-button.tsx
deleted file mode 100644
index e9b80ff00..000000000
--- a/src/views/communities/components/community-join-button.tsx
+++ /dev/null
@@ -1,51 +0,0 @@
-import { useCallback } from "react";
-import dayjs from "dayjs";
-import { Button, ButtonProps } from "@chakra-ui/react";
-import { kinds } from "nostr-tools";
-
-import { DraftNostrEvent, NostrEvent, isDTag } from "../../../types/nostr-event";
-import useUserCommunitiesList from "../../../hooks/use-user-communities-list";
-import useCurrentAccount from "../../../hooks/use-current-account";
-import { getCommunityName } from "../../../helpers/nostr/communities";
-import { listAddCoordinate, listRemoveCoordinate } from "../../../helpers/nostr/lists";
-import { getEventCoordinate } from "../../../helpers/nostr/event";
-import { usePublishEvent } from "../../../providers/global/publish-provider";
-
-export default function CommunityJoinButton({
- community,
- ...props
-}: Omit & { community: NostrEvent }) {
- const publish = usePublishEvent();
- const account = useCurrentAccount();
- const { list, pointers } = useUserCommunitiesList(account?.pubkey);
-
- const isSubscribed = pointers.find(
- (cord) => cord.identifier === getCommunityName(community) && cord.pubkey === community.pubkey,
- );
-
- const handleClick = useCallback(async () => {
- const favList = {
- kind: kinds.CommunitiesList,
- content: list?.content ?? "",
- created_at: dayjs().unix(),
- tags: list?.tags.filter((t) => !isDTag(t)) ?? [],
- };
-
- let draft: DraftNostrEvent;
- if (isSubscribed) draft = listRemoveCoordinate(favList, getEventCoordinate(community));
- else draft = listAddCoordinate(favList, getEventCoordinate(community));
-
- await publish(isSubscribed ? "Unsubscribe" : "Subscribe", draft);
- }, [isSubscribed, list, community, publish]);
-
- return (
-
- );
-}
diff --git a/src/views/communities/components/community-mod-list.tsx b/src/views/communities/components/community-mod-list.tsx
deleted file mode 100644
index 2c011db94..000000000
--- a/src/views/communities/components/community-mod-list.tsx
+++ /dev/null
@@ -1,20 +0,0 @@
-import { AvatarGroup, AvatarGroupProps } from "@chakra-ui/react";
-
-import UserAvatarLink from "../../../components/user/user-avatar-link";
-import { NostrEvent } from "../../../types/nostr-event";
-import { getCommunityMods } from "../../../helpers/nostr/communities";
-
-export default function CommunityModList({
- community,
- ...props
-}: Omit & { community: NostrEvent }) {
- const mods = getCommunityMods(community);
-
- return (
-
- {mods.map((pubkey) => (
-
- ))}
-
- );
-}
diff --git a/src/views/communities/hooks/use-count-community-post.ts b/src/views/communities/hooks/use-count-community-post.ts
deleted file mode 100644
index bd225cad4..000000000
--- a/src/views/communities/hooks/use-count-community-post.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-import dayjs from "dayjs";
-import { kinds } from "nostr-tools";
-
-import { NostrEvent } from "../../../types/nostr-event";
-import useEventCount from "../../../hooks/use-event-count";
-import { getEventCoordinate } from "../../../helpers/nostr/event";
-
-export default function useCountCommunityPosts(
- community: NostrEvent,
- since: number = dayjs().subtract(1, "month").unix(),
-) {
- return useEventCount({ "#a": [getEventCoordinate(community)], kinds: [kinds.ShortTextNote], since });
-}
diff --git a/src/views/communities/index.tsx b/src/views/communities/index.tsx
deleted file mode 100644
index 4d7795f61..000000000
--- a/src/views/communities/index.tsx
+++ /dev/null
@@ -1,196 +0,0 @@
-import { useMemo } from "react";
-import {
- Button,
- ButtonGroup,
- Center,
- Drawer,
- DrawerBody,
- DrawerCloseButton,
- DrawerContent,
- DrawerHeader,
- DrawerOverlay,
- Flex,
- Heading,
- Link,
- Switch,
- Text,
- useDisclosure,
-} from "@chakra-ui/react";
-import { kinds } from "nostr-tools";
-import { Link as RouterLink, useNavigate } from "react-router-dom";
-import { Navigate } from "react-router-dom";
-import dayjs from "dayjs";
-
-import VerticalPageLayout from "../../components/vertical-page-layout";
-import { ErrorBoundary } from "../../components/error-boundary";
-import useUserCommunitiesList from "../../hooks/use-user-communities-list";
-import useCurrentAccount from "../../hooks/use-current-account";
-import CommunityCard from "./components/community-card";
-import CommunityCreateModal, { FormValues } from "./components/community-create-modal";
-import { DraftNostrEvent } from "../../types/nostr-event";
-import { buildApprovalMap, getCommunityMods, getCommunityName } from "../../helpers/nostr/communities";
-import { getImageSize } from "../../helpers/image";
-import { useReadRelays } from "../../hooks/use-client-relays";
-import useTimelineLoader from "../../hooks/use-timeline-loader";
-import useUserMuteFilter from "../../hooks/use-user-mute-filter";
-import { useTimelineCurserIntersectionCallback } from "../../hooks/use-timeline-cursor-intersection-callback";
-import useReplaceableEvents from "../../hooks/use-replaceable-events";
-import { getEventCoordinate, sortByDate } from "../../helpers/nostr/event";
-import IntersectionObserverProvider from "../../providers/local/intersection-observer";
-import ApprovedEvent from "../community/components/community-approved-post";
-import TimelineActionAndStatus from "../../components/timeline/timeline-action-and-status";
-import { usePublishEvent } from "../../providers/global/publish-provider";
-import { createCoordinate } from "../../classes/batch-kind-pubkey-loader";
-
-function CommunitiesHomePage() {
- const publish = usePublishEvent();
- const navigate = useNavigate();
- const account = useCurrentAccount()!;
- const createModal = useDisclosure();
-
- const readRelays = useReadRelays();
- const { pointers: communityCoordinates } = useUserCommunitiesList(account.pubkey, readRelays, {
- alwaysRequest: true,
- });
- const communities = useReplaceableEvents(communityCoordinates, readRelays).sort(sortByDate);
-
- const createCommunity = async (values: FormValues) => {
- const draft: DraftNostrEvent = {
- kind: kinds.CommunityDefinition,
- created_at: dayjs().unix(),
- content: "",
- tags: [["d", values.name]],
- };
-
- if (values.description) draft.tags.push(["description", values.description]);
- if (values.banner) {
- try {
- const size = await getImageSize(values.banner);
- draft.tags.push(["image", values.banner, `${size.width}x${size.height}`]);
- } catch (e) {
- draft.tags.push(["image", values.banner]);
- }
- }
- for (const pubkey of values.mods) draft.tags.push(["p", pubkey, "", "moderator"]);
- for (const url of values.relays) draft.tags.push(["relay", url]);
- for (const [url, name] of values.links) draft.tags.push(name ? ["r", url, name] : ["r", url]);
- // if (values.ranking) draft.tags.push(["rank_mode", values.ranking]);
-
- const pub = await publish("Create Community", draft, values.relays, false);
-
- if (pub) navigate(`/c/${getCommunityName(pub.event)}/${pub.event.pubkey}`);
- };
-
- const { loader, timeline: events } = useTimelineLoader(
- `all-communities-timeline`,
- readRelays,
- communityCoordinates.length > 0
- ? {
- kinds: [kinds.ShortTextNote, kinds.Repost, kinds.GenericRepost, kinds.CommunityPostApproval],
- "#a": communityCoordinates.map((p) => createCoordinate(p.kind, p.pubkey, p.identifier)),
- }
- : undefined,
- );
-
- const showUnapproved = useDisclosure();
- const muteFilter = useUserMuteFilter();
- const mods = useMemo(() => {
- const set = new Set();
- for (const community of communities) {
- for (const pubkey of getCommunityMods(community)) {
- set.add(pubkey);
- }
- }
- return Array.from(set);
- }, [communities]);
-
- const approvalMap = buildApprovalMap(events, mods);
-
- const approved = events
- .filter((e) => e.kind !== kinds.CommunityPostApproval && (showUnapproved.isOpen ? true : approvalMap.has(e.id)))
- .map((event) => ({ event, approvals: approvalMap.get(event.id) }))
- .filter((e) => !muteFilter(e.event));
-
- const callback = useTimelineCurserIntersectionCallback(loader);
-
- const communityDrawer = useDisclosure();
-
- return (
- <>
-
-
-
-
-
-
-
-
-
- {communities.length > 0 ? (
-
-
-
- Latest Posts
-
- Show Unapproved
-
-
-
- {approved.map(({ event, approvals }) => (
-
- ))}
-
-
-
-
- Joined Communities
- {communities.map((community) => (
-
-
-
- ))}
-
-
- ) : (
-
- No communities :(
-
- go find a cool one to join.{" "}
-
- Explore
-
-
-
- )}
-
- {createModal.isOpen && (
-
- )}
-
-
-
-
-
- Joined Communities
-
-
- {communities.map((community) => (
-
-
-
- ))}
-
-
-
- >
- );
-}
-
-export default function CommunitiesHomeView() {
- const account = useCurrentAccount();
- return account ? : ;
-}
diff --git a/src/views/community/community-home.tsx b/src/views/community/community-home.tsx
deleted file mode 100644
index b8b070026..000000000
--- a/src/views/community/community-home.tsx
+++ /dev/null
@@ -1,166 +0,0 @@
-import { useContext } from "react";
-import { Button, ButtonGroup, Divider, Flex, Heading, Text, useDisclosure } from "@chakra-ui/react";
-import { Outlet, Link as RouterLink, useLocation } from "react-router-dom";
-import { useObservable } from "applesauce-react/hooks";
-import { kinds, nip19 } from "nostr-tools";
-
-import {
- getCommunityRelays,
- getCommunityImage,
- getCommunityName,
- getCommunityMods,
- buildApprovalMap,
-} from "../../helpers/nostr/communities";
-import { NostrEvent } from "../../types/nostr-event";
-import VerticalPageLayout from "../../components/vertical-page-layout";
-import UserAvatarLink from "../../components/user/user-avatar-link";
-import UserLink from "../../components/user/user-link";
-import { AdditionalRelayProvider } from "../../providers/local/additional-relay-context";
-
-import TrendUp01 from "../../components/icons/trend-up-01";
-import Clock from "../../components/icons/clock";
-import Hourglass03 from "../../components/icons/hourglass-03";
-import VerticalCommunityDetails from "./components/vertical-community-details";
-import { useBreakpointValue } from "../../providers/global/breakpoint-provider";
-import HorizontalCommunityDetails from "./components/horizonal-community-details";
-import { useReadRelays } from "../../hooks/use-client-relays";
-import useTimelineLoader from "../../hooks/use-timeline-loader";
-import { getEventCoordinate, getEventUID } from "../../helpers/nostr/event";
-import { WritingIcon } from "../../components/icons";
-import { PostModalContext } from "../../providers/route/post-modal-provider";
-import CommunityEditModal from "./components/community-edit-modal";
-import TimelineLoader from "../../classes/timeline-loader";
-import useClientSideMuteFilter from "../../hooks/use-client-side-mute-filter";
-
-function getCommunityPath(community: NostrEvent) {
- return `/c/${encodeURIComponent(getCommunityName(community))}/${nip19.npubEncode(community.pubkey)}`;
-}
-
-export type RouterContext = { community: NostrEvent; timeline: TimelineLoader };
-
-export default function CommunityHomePage({ community }: { community: NostrEvent }) {
- const muteFilter = useClientSideMuteFilter();
- const image = getCommunityImage(community);
- const location = useLocation();
- const { openModal } = useContext(PostModalContext);
- const editModal = useDisclosure();
- const communityCoordinate = getEventCoordinate(community);
-
- const verticalLayout = useBreakpointValue({ base: true, xl: false });
-
- const communityRelays = getCommunityRelays(community);
- const readRelays = useReadRelays(communityRelays);
- const { loader } = useTimelineLoader(`${getEventUID(community)}-timeline`, readRelays, {
- kinds: [kinds.ShortTextNote, kinds.Repost, kinds.GenericRepost, kinds.CommunityPostApproval],
- "#a": [communityCoordinate],
- });
-
- // get pending notes
- const events = useObservable(loader.timeline) ?? [];
- const mods = getCommunityMods(community);
- const approvals = buildApprovalMap(events, mods);
- const pending = events.filter(
- (e) => e.kind !== kinds.CommunityPostApproval && !approvals.has(e.id) && !muteFilter(e),
- );
-
- let active = "newest";
- if (location.pathname.endsWith("/newest")) active = "newest";
- if (location.pathname.endsWith("/pending")) active = "pending";
- if (location.pathname.endsWith("/trending")) active = "trending";
-
- return (
- <>
-
-
-
- {getCommunityName(community)}
-
-
- by
-
-
-
-
- {verticalLayout && (
-
- )}
-
-
-
-
- }
- onClick={() =>
- openModal({
- cacheFormKey: communityCoordinate + "-new-post",
- initCommunity: communityCoordinate,
- requireSubject: true,
- })
- }
- >
- New Post
-
-
- }
- as={RouterLink}
- to={getCommunityPath(community) + "/trending"}
- colorScheme={active === "trending" ? "primary" : "gray"}
- replace
- >
- Trending
-
- }
- as={RouterLink}
- to={getCommunityPath(community) + "/newest"}
- colorScheme={active === "newest" ? "primary" : "gray"}
- replace
- >
- New
-
- }
- as={RouterLink}
- to={getCommunityPath(community) + "/pending"}
- colorScheme={active == "pending" ? "primary" : "gray"}
- replace
- >
- Pending ({pending.length})
-
-
-
-
-
-
- {!verticalLayout && (
-
- )}
-
-
-
- {editModal.isOpen && (
-
- )}
- >
- );
-}
diff --git a/src/views/community/components/community-approved-post.tsx b/src/views/community/components/community-approved-post.tsx
deleted file mode 100644
index 1c19173b8..000000000
--- a/src/views/community/components/community-approved-post.tsx
+++ /dev/null
@@ -1,21 +0,0 @@
-import { memo } from "react";
-import { Card, Flex } from "@chakra-ui/react";
-
-import EventVoteButtons from "../../../components/reactions/event-vote-buttions";
-import CommunityPost from "./community-post";
-import { NostrEvent } from "../../../types/nostr-event";
-
-const ApprovedEvent = memo(
- ({ event, approvals, showCommunity }: { event: NostrEvent; approvals: NostrEvent[]; showCommunity?: boolean }) => {
- return (
-
-
-
-
-
-
- );
- },
-);
-
-export default ApprovedEvent;
diff --git a/src/views/community/components/community-edit-modal.tsx b/src/views/community/components/community-edit-modal.tsx
deleted file mode 100644
index eff481f0f..000000000
--- a/src/views/community/components/community-edit-modal.tsx
+++ /dev/null
@@ -1,79 +0,0 @@
-import { useMemo } from "react";
-import { ModalProps } from "@chakra-ui/react";
-import { kinds } from "nostr-tools";
-import dayjs from "dayjs";
-
-import { DraftNostrEvent, NostrEvent } from "../../../types/nostr-event";
-import {
- getCommunityDescription,
- getCommunityImage,
- getCommunityLinks,
- getCommunityMods,
- getCommunityName,
- getCommunityRelays,
- getCommunityRules,
-} from "../../../helpers/nostr/communities";
-import CommunityCreateModal, { FormValues } from "../../communities/components/community-create-modal";
-import { getImageSize } from "../../../helpers/image";
-import { usePublishEvent } from "../../../providers/global/publish-provider";
-
-export default function CommunityEditModal({
- isOpen,
- onClose,
- community,
- ...props
-}: Omit & { community: NostrEvent }) {
- const publish = usePublishEvent();
-
- const defaultValues = useMemo(
- () => ({
- name: getCommunityName(community),
- description: getCommunityDescription(community) || "",
- banner: getCommunityImage(community) || "",
- rules: getCommunityRules(community) || "",
- mods: getCommunityMods(community) || [],
- relays: getCommunityRelays(community) || [],
- links: getCommunityLinks(community) || [],
- // ranking: getCommunityRanking(community) || "votes",
- }),
- [community],
- );
-
- const updateCommunity = async (values: FormValues) => {
- const draft: DraftNostrEvent = {
- kind: kinds.CommunityDefinition,
- created_at: dayjs().unix(),
- content: "",
- tags: [["d", getCommunityName(community)]],
- };
-
- if (values.description) draft.tags.push(["description", values.description]);
- if (values.banner) {
- try {
- const size = await getImageSize(values.banner);
- draft.tags.push(["image", values.banner, `${size.width}x${size.height}`]);
- } catch (e) {
- draft.tags.push(["image", values.banner]);
- }
- }
- for (const pubkey of values.mods) draft.tags.push(["p", pubkey, "", "moderator"]);
- for (const url of values.relays) draft.tags.push(["relay", url]);
- for (const [url, name] of values.links) draft.tags.push(name ? ["r", url, name] : ["r", url]);
-
- // if (values.ranking) draft.tags.push(["rank_mode", values.ranking]);
-
- const pub = await publish("Update Community", draft, values.relays);
- if (pub) onClose();
- };
-
- return (
-
- );
-}
diff --git a/src/views/community/components/community-members-modal.tsx b/src/views/community/components/community-members-modal.tsx
deleted file mode 100644
index 99dbbca06..000000000
--- a/src/views/community/components/community-members-modal.tsx
+++ /dev/null
@@ -1,84 +0,0 @@
-import {
- Button,
- Flex,
- Modal,
- ModalBody,
- ModalCloseButton,
- ModalContent,
- ModalFooter,
- ModalHeader,
- ModalOverlay,
- ModalProps,
- SimpleGrid,
-} from "@chakra-ui/react";
-import { kinds } from "nostr-tools";
-
-import { NostrEvent } from "../../../types/nostr-event";
-import useTimelineLoader from "../../../hooks/use-timeline-loader";
-import { useReadRelays } from "../../../hooks/use-client-relays";
-import { getCommunityRelays } from "../../../helpers/nostr/communities";
-import { getEventCoordinate } from "../../../helpers/nostr/event";
-import IntersectionObserverProvider from "../../../providers/local/intersection-observer";
-import { useTimelineCurserIntersectionCallback } from "../../../hooks/use-timeline-cursor-intersection-callback";
-import TimelineActionAndStatus from "../../../components/timeline/timeline-action-and-status";
-import UserLink from "../../../components/user/user-link";
-import UserDnsIdentity from "../../../components/user/user-dns-identity";
-import UserAvatarLink from "../../../components/user/user-avatar-link";
-
-function UserCard({ pubkey }: { pubkey: string }) {
- return (
-
-
-
-
-
-
-
- );
-}
-
-export default function CommunityMembersModal({
- community,
- onClose,
- ...props
-}: Omit & { community: NostrEvent }) {
- const communityCoordinate = getEventCoordinate(community);
- const readRelays = useReadRelays(getCommunityRelays(community));
- const { loader, timeline: lists } = useTimelineLoader(`${communityCoordinate}-members`, readRelays, [
- { "#a": [communityCoordinate], kinds: [kinds.CommunitiesList] },
- ]);
- const callback = useTimelineCurserIntersectionCallback(loader);
-
- const listsByPubkey: Record = {};
- if (lists) {
- for (const list of lists) {
- if (!listsByPubkey[list.pubkey] || listsByPubkey[list.pubkey].created_at < list.created_at) {
- listsByPubkey[list.pubkey] = list;
- }
- }
- }
-
- return (
-
-
-
-
-
- Community Members
-
-
-
-
- {lists?.map((list) => )}
-
-
-
-
-
-
-
-
-
-
- );
-}
diff --git a/src/views/community/components/community-menu.tsx b/src/views/community/components/community-menu.tsx
deleted file mode 100644
index 16b8dbda6..000000000
--- a/src/views/community/components/community-menu.tsx
+++ /dev/null
@@ -1,32 +0,0 @@
-import { MenuItem } from "@chakra-ui/react";
-
-import { DotsMenuButton, MenuIconButtonProps } from "../../../components/dots-menu-button";
-import { NostrEvent } from "../../../types/nostr-event";
-import useCurrentAccount from "../../../hooks/use-current-account";
-import PencilLine from "../../../components/icons/pencil-line";
-import OpenInAppMenuItem from "../../../components/common-menu-items/open-in-app";
-import CopyEmbedCodeMenuItem from "../../../components/common-menu-items/copy-embed-code";
-import DebugEventMenuItem from "../../../components/debug-modal/debug-event-menu-item";
-
-export default function CommunityMenu({
- community,
- onEditClick,
- ...props
-}: Omit & { community: NostrEvent; onEditClick?: () => void }) {
- const account = useCurrentAccount();
-
- return (
- <>
-
-
-
- {account?.pubkey === community.pubkey && onEditClick && (
- }>
- Edit Community
-
- )}
-
-
- >
- );
-}
diff --git a/src/views/community/components/community-post-menu.tsx b/src/views/community/components/community-post-menu.tsx
deleted file mode 100644
index 1b9cd7835..000000000
--- a/src/views/community/components/community-post-menu.tsx
+++ /dev/null
@@ -1,39 +0,0 @@
-import { MenuItem, useToast } from "@chakra-ui/react";
-import { nip19 } from "nostr-tools";
-
-import { DotsMenuButton, MenuIconButtonProps } from "../../../components/dots-menu-button";
-import { NostrEvent } from "../../../types/nostr-event";
-import { CopyToClipboardIcon } from "../../../components/icons";
-import ShareLinkMenuItem from "../../../components/common-menu-items/share-link";
-import OpenInAppMenuItem from "../../../components/common-menu-items/open-in-app";
-import DeleteEventMenuItem from "../../../components/common-menu-items/delete-event";
-import DebugEventMenuItem from "../../../components/debug-modal/debug-event-menu-item";
-
-export default function CommunityPostMenu({
- event,
- approvals,
- ...props
-}: Omit & { event: NostrEvent; approvals: NostrEvent[] }) {
- const toast = useToast();
-
- return (
- <>
-
-
-
-
-
-
-
- >
- );
-}
diff --git a/src/views/community/components/community-post.tsx b/src/views/community/components/community-post.tsx
deleted file mode 100644
index e5e95670d..000000000
--- a/src/views/community/components/community-post.tsx
+++ /dev/null
@@ -1,184 +0,0 @@
-import { MouseEventHandler, useCallback } from "react";
-import {
- AvatarGroup,
- Card,
- CardBody,
- CardFooter,
- CardHeader,
- CardProps,
- Flex,
- Heading,
- Link,
- LinkBox,
- Text,
-} from "@chakra-ui/react";
-import { Link as RouterLink } from "react-router-dom";
-import dayjs from "dayjs";
-import { kinds } from "nostr-tools";
-
-import { NostrEvent, isETag } from "../../../types/nostr-event";
-import { getEventCommunityPointer, getPostSubject } from "../../../helpers/nostr/communities";
-import { useNavigateInDrawer } from "../../../providers/drawer-sub-view-provider";
-import HoverLinkOverlay from "../../../components/hover-link-overlay";
-import { CompactNoteContent } from "../../../components/compact-note-content";
-import { parseHardcodedNoteContent } from "../../../helpers/nostr/event";
-import UserLink from "../../../components/user/user-link";
-import UserAvatarLink from "../../../components/user/user-avatar-link";
-import useUserMuteFilter from "../../../hooks/use-user-mute-filter";
-import { useReadRelays } from "../../../hooks/use-client-relays";
-import useSingleEvent from "../../../hooks/use-single-event";
-import CommunityPostMenu from "./community-post-menu";
-import useEventIntersectionRef from "../../../hooks/use-event-intersection-ref";
-import useShareableEventAddress from "../../../hooks/use-shareable-event-address";
-
-export function ApprovalIcon({ approval }: { approval: NostrEvent }) {
- const ref = useEventIntersectionRef(approval);
-
- return ;
-}
-
-export type CommunityPostPropTypes = {
- event: NostrEvent;
- approvals: NostrEvent[];
- showCommunity?: boolean;
-};
-
-function PostSubject({ event }: { event: NostrEvent }) {
- const subject = getPostSubject(event);
- const address = useShareableEventAddress(event);
-
- const navigate = useNavigateInDrawer();
- const to = `/n/${address}`;
- const handleClick = useCallback(
- (e) => {
- e.preventDefault();
- navigate(to);
- },
- [navigate, to],
- );
-
- return subject ? (
-
-
-
- {getPostSubject(event)}
-
-
-
- ) : (
-
- );
-}
-function Approvals({ approvals }: { approvals: NostrEvent[] }) {
- return (
- <>
- Approved by
-
- {approvals.map((approval) => (
-
- ))}
-
- >
- );
-}
-
-export function CommunityTextPost({
- event,
- approvals,
- showCommunity,
- ...props
-}: Omit & CommunityPostPropTypes) {
- const ref = useEventIntersectionRef(event);
-
- const communityPointer = getEventCommunityPointer(event);
-
- return (
-
-
-
-
-
-
-
- Posted {dayjs.unix(event.created_at).fromNow()} by
-
- {showCommunity && communityPointer && (
-
- to{" "}
-
- {communityPointer.identifier}
-
-
- )}
-
- {approvals.length > 0 && }
-
-
-
-
- );
-}
-
-export function CommunityRepostPost({
- event,
- approvals,
- showCommunity,
- ...props
-}: Omit & CommunityPostPropTypes) {
- const encodedRepost = parseHardcodedNoteContent(event);
-
- const [_, eventId, relay] = event.tags.find(isETag) ?? [];
- const readRelays = useReadRelays(relay ? [relay] : []);
-
- const loadedRepost = useSingleEvent(eventId, readRelays);
- const repost = encodedRepost || loadedRepost;
-
- const ref = useEventIntersectionRef(repost);
-
- const muteFilter = useUserMuteFilter();
- if (repost && muteFilter(repost)) return;
-
- const communityPointer = getEventCommunityPointer(event);
-
- return (
-
- {repost && (
- <>
-
-
-
-
- >
- )}
-
-
- Shared {dayjs.unix(event.created_at).fromNow()} by
-
- {showCommunity && communityPointer && (
-
- to{" "}
-
- {communityPointer.identifier}
-
-
- )}
-
- {approvals.length > 0 && }
-
-
-
-
- );
-}
-
-export default function CommunityPost({ event, ...props }: Omit & CommunityPostPropTypes) {
- switch (event.kind) {
- case kinds.ShortTextNote:
- return ;
- case kinds.Repost:
- return ;
- case kinds.GenericRepost:
- return ;
- }
- return null;
-}
diff --git a/src/views/community/components/horizonal-community-details.tsx b/src/views/community/components/horizonal-community-details.tsx
deleted file mode 100644
index 214bf365c..000000000
--- a/src/views/community/components/horizonal-community-details.tsx
+++ /dev/null
@@ -1,140 +0,0 @@
-import {
- Box,
- Button,
- ButtonGroup,
- Card,
- CardBody,
- CardProps,
- Flex,
- Heading,
- Link,
- SimpleGrid,
- Text,
- useDisclosure,
-} from "@chakra-ui/react";
-import { Link as RouterLink } from "react-router-dom";
-
-import {
- getCommunityDescription,
- getCommunityLinks,
- getCommunityMods,
- getCommunityRelays,
- getCommunityRules,
-} from "../../../helpers/nostr/communities";
-import CommunityDescription from "../../communities/components/community-description";
-import UserAvatarLink from "../../../components/user/user-avatar-link";
-import UserLink from "../../../components/user/user-link";
-import { NostrEvent } from "../../../types/nostr-event";
-import CommunityJoinButton from "../../communities/components/community-join-button";
-import CommunityMenu from "./community-menu";
-import useCountCommunityMembers from "../../../hooks/use-count-community-members";
-import { humanReadableSats } from "../../../helpers/lightning";
-import CommunityMembersModal from "./community-members-modal";
-import { RelayFavicon } from "../../../components/relay-favicon";
-
-export default function HorizontalCommunityDetails({
- community,
- onEditClick,
- ...props
-}: Omit & { community: NostrEvent; onEditClick?: () => void }) {
- const membersModal = useDisclosure();
- const communityRelays = getCommunityRelays(community);
- const mods = getCommunityMods(community);
- const description = getCommunityDescription(community);
- const rules = getCommunityRules(community);
- const links = getCommunityLinks(community);
-
- const more = useDisclosure();
- const countMembers = useCountCommunityMembers(community);
-
- return (
- <>
-
-
-
-
-
-
- {description && (
- <>
-
- Description
-
-
- >
- )}
-
- {more.isOpen ? (
-
-
-
- Mods
-
-
- {mods.map((pubkey) => (
-
-
-
-
- ))}
-
-
-
-
- Members
-
- {countMembers ? humanReadableSats(countMembers) : "unknown"}
-
- {rules && (
-
-
- Rules
-
- {rules}
-
- )}
- {communityRelays.length > 0 && (
-
-
- Relays
-
-
- {communityRelays.map((url) => (
-
-
-
- {url}
-
-
- ))}
-
-
- )}
- {links.length > 0 && (
-
-
- Links
-
-
- {links.map(([url, name]) => (
-
- {name || url}
-
- ))}
-
-
- )}
-
- ) : (
-
- )}
-
-
- {membersModal.isOpen && (
-
- )}
- >
- );
-}
diff --git a/src/views/community/components/vertical-community-details.tsx b/src/views/community/components/vertical-community-details.tsx
deleted file mode 100644
index 723818b28..000000000
--- a/src/views/community/components/vertical-community-details.tsx
+++ /dev/null
@@ -1,115 +0,0 @@
-import { Box, ButtonGroup, Card, CardProps, Flex, Heading, Link, Text, useDisclosure } from "@chakra-ui/react";
-import { Link as RouterLink } from "react-router-dom";
-
-import {
- getCommunityDescription,
- getCommunityLinks,
- getCommunityMods,
- getCommunityRelays,
- getCommunityRules,
-} from "../../../helpers/nostr/communities";
-import CommunityDescription from "../../communities/components/community-description";
-import UserAvatarLink from "../../../components/user/user-avatar-link";
-import UserLink from "../../../components/user/user-link";
-import { NostrEvent } from "../../../types/nostr-event";
-import CommunityJoinButton from "../../communities/components/community-join-button";
-import CommunityMenu from "./community-menu";
-import useCountCommunityMembers from "../../../hooks/use-count-community-members";
-import CommunityMembersModal from "./community-members-modal";
-import { humanReadableSats } from "../../../helpers/lightning";
-import { RelayFavicon } from "../../../components/relay-favicon";
-
-export default function VerticalCommunityDetails({
- community,
- onEditClick,
- ...props
-}: Omit & { community: NostrEvent; onEditClick?: () => void }) {
- const membersModal = useDisclosure();
- const communityRelays = getCommunityRelays(community);
- const mods = getCommunityMods(community);
- const description = getCommunityDescription(community);
- const rules = getCommunityRules(community);
- const links = getCommunityLinks(community);
-
- const countMembers = useCountCommunityMembers(community);
-
- return (
- <>
-
- {description && (
-
-
- About
-
-
-
- )}
-
-
-
-
-
-
- Mods
-
-
- {mods.map((pubkey) => (
-
-
-
-
- ))}
-
-
-
-
- Members
-
- {countMembers ? humanReadableSats(countMembers) : "unknown"}
-
- {rules && (
-
-
- Rules
-
- {rules}
-
- )}
- {communityRelays.length > 0 && (
-
-
- Relays
-
-
- {communityRelays.map((url) => (
-
-
-
- {url}
-
-
- ))}
-
-
- )}
- {links.length > 0 && (
-
-
- Links
-
-
- {links.map(([url, name]) => (
-
- {name || url}
-
- ))}
-
-
- )}
-
- {membersModal.isOpen && (
-
- )}
- >
- );
-}
diff --git a/src/views/community/find-by-name.tsx b/src/views/community/find-by-name.tsx
deleted file mode 100644
index 5a9f8f599..000000000
--- a/src/views/community/find-by-name.tsx
+++ /dev/null
@@ -1,47 +0,0 @@
-import { useCallback } from "react";
-import { Navigate, useParams } from "react-router-dom";
-import { Heading, SimpleGrid } from "@chakra-ui/react";
-import { kinds } from "nostr-tools";
-
-import { useReadRelays } from "../../hooks/use-client-relays";
-import { validateCommunity } from "../../helpers/nostr/communities";
-import useTimelineLoader from "../../hooks/use-timeline-loader";
-import { NostrEvent } from "../../types/nostr-event";
-import { useTimelineCurserIntersectionCallback } from "../../hooks/use-timeline-cursor-intersection-callback";
-import IntersectionObserverProvider from "../../providers/local/intersection-observer";
-import VerticalPageLayout from "../../components/vertical-page-layout";
-import CommunityCard from "../communities/components/community-card";
-import { getEventUID } from "../../helpers/nostr/event";
-import { safeDecode } from "../../helpers/nip19";
-
-export default function CommunityFindByNameView() {
- const { community } = useParams() as { community: string };
-
- // if community name is a naddr, redirect
- const decoded = safeDecode(community);
- if (decoded?.type === "naddr" && decoded.data.kind === kinds.CommunityDefinition) {
- return ;
- }
-
- const readRelays = useReadRelays();
- const eventFilter = useCallback((event: NostrEvent) => {
- return validateCommunity(event);
- }, []);
- const { loader, timeline: communities } = useTimelineLoader(
- `${community}-find-communities`,
- readRelays,
- community ? { kinds: [kinds.CommunityDefinition], "#d": [community] } : undefined,
- );
- const callback = useTimelineCurserIntersectionCallback(loader);
-
- return (
-
-
- Select Community
-
- {communities?.map((event) => )}
-
-
-
- );
-}
diff --git a/src/views/community/index.tsx b/src/views/community/index.tsx
deleted file mode 100644
index fdd2c3cf7..000000000
--- a/src/views/community/index.tsx
+++ /dev/null
@@ -1,30 +0,0 @@
-import { useParams } from "react-router-dom";
-import { Spinner } from "@chakra-ui/react";
-import { kinds } from "nostr-tools";
-
-import useReplaceableEvent from "../../hooks/use-replaceable-event";
-import CommunityHomePage from "./community-home";
-import { getPubkeyFromDecodeResult, isHexKey, safeDecode } from "../../helpers/nip19";
-
-function useCommunityPointer() {
- const { community, pubkey } = useParams();
-
- const decoded = community ? safeDecode(community) : undefined;
- if (decoded) {
- if (decoded.type === "naddr" && decoded.data.kind === kinds.CommunityDefinition) return decoded.data;
- } else if (community && pubkey) {
- const hexPubkey = isHexKey(pubkey) ? pubkey : getPubkeyFromDecodeResult(safeDecode(pubkey));
- if (!hexPubkey) return;
-
- return { kind: kinds.CommunityDefinition, pubkey: hexPubkey, identifier: community };
- }
-}
-
-export default function CommunityView() {
- const pointer = useCommunityPointer();
- const community = useReplaceableEvent(pointer, undefined, { alwaysRequest: true });
-
- if (!community) return ;
-
- return ;
-}
diff --git a/src/views/community/views/newest.tsx b/src/views/community/views/newest.tsx
deleted file mode 100644
index 594452119..000000000
--- a/src/views/community/views/newest.tsx
+++ /dev/null
@@ -1,38 +0,0 @@
-import { useOutletContext } from "react-router-dom";
-import { useObservable } from "applesauce-react/hooks";
-import { kinds } from "nostr-tools";
-
-import { buildApprovalMap, getCommunityMods } from "../../../helpers/nostr/communities";
-import { useTimelineCurserIntersectionCallback } from "../../../hooks/use-timeline-cursor-intersection-callback";
-import IntersectionObserverProvider from "../../../providers/local/intersection-observer";
-import TimelineActionAndStatus from "../../../components/timeline/timeline-action-and-status";
-import useUserMuteFilter from "../../../hooks/use-user-mute-filter";
-import ApprovedEvent from "../components/community-approved-post";
-import { RouterContext } from "../community-home";
-
-export default function CommunityNewestView() {
- const { community, timeline } = useOutletContext();
- const muteFilter = useUserMuteFilter();
- const mods = getCommunityMods(community);
-
- const events = useObservable(timeline.timeline) ?? [];
- const approvalMap = buildApprovalMap(events, mods);
-
- const approved = events
- .filter((e) => e.kind !== kinds.CommunityPostApproval && approvalMap.has(e.id))
- .map((event) => ({ event, approvals: approvalMap.get(event.id) }))
- .filter((e) => !muteFilter(e.event));
-
- const callback = useTimelineCurserIntersectionCallback(timeline);
-
- return (
- <>
-
- {approved.map(({ event, approvals }) => (
-
- ))}
-
-
- >
- );
-}
diff --git a/src/views/community/views/pending.tsx b/src/views/community/views/pending.tsx
deleted file mode 100644
index 7fbe291d2..000000000
--- a/src/views/community/views/pending.tsx
+++ /dev/null
@@ -1,101 +0,0 @@
-import { useCallback, useState } from "react";
-import { Button, Flex } from "@chakra-ui/react";
-import { useOutletContext } from "react-router-dom";
-import { useObservable } from "applesauce-react/hooks";
-import { kinds } from "nostr-tools";
-import dayjs from "dayjs";
-
-import { DraftNostrEvent, NostrEvent } from "../../../types/nostr-event";
-import { getEventCoordinate, getEventUID } from "../../../helpers/nostr/event";
-import { buildApprovalMap, getCommunityMods, getCommunityRelays } from "../../../helpers/nostr/communities";
-import IntersectionObserverProvider from "../../../providers/local/intersection-observer";
-import { useTimelineCurserIntersectionCallback } from "../../../hooks/use-timeline-cursor-intersection-callback";
-import TimelineActionAndStatus from "../../../components/timeline/timeline-action-and-status";
-import { CheckIcon } from "../../../components/icons";
-import useCurrentAccount from "../../../hooks/use-current-account";
-import CommunityPost from "../components/community-post";
-import { RouterContext } from "../community-home";
-import useUserMuteFilter from "../../../hooks/use-user-mute-filter";
-import { usePublishEvent } from "../../../providers/global/publish-provider";
-import useEventIntersectionRef from "../../../hooks/use-event-intersection-ref";
-
-type PendingProps = {
- event: NostrEvent;
- approvals: NostrEvent[];
- community: NostrEvent;
-};
-
-function ModPendingPost({ event, community, approvals }: PendingProps) {
- const publish = usePublishEvent();
-
- const ref = useEventIntersectionRef(event);
-
- const communityRelays = getCommunityRelays(community);
- const [loading, setLoading] = useState(false);
- const approve = useCallback(async () => {
- setLoading(true);
- const relay = communityRelays[0];
- const draft: DraftNostrEvent = {
- kind: kinds.CommunityPostApproval,
- content: JSON.stringify(event),
- created_at: dayjs().unix(),
- tags: [
- relay ? ["a", getEventCoordinate(community), relay] : ["a", getEventCoordinate(community)],
- ["e", event.id],
- ["p", event.pubkey],
- ["k", String(event.kind)],
- ],
- };
-
- await publish("Approve", draft);
- setLoading(false);
- }, [event, publish, setLoading, community]);
-
- return (
-
-
-
- }
- size="sm"
- ml="auto"
- onClick={approve}
- isLoading={loading}
- >
- Approve
-
-
-
- );
-}
-
-export default function CommunityPendingView() {
- const account = useCurrentAccount();
- const muteFilter = useUserMuteFilter();
- const { community, timeline } = useOutletContext();
-
- const events = useObservable(timeline.timeline) ?? [];
-
- const mods = getCommunityMods(community);
- const approvals = buildApprovalMap(events, mods);
- const pending = events.filter(
- (e) => e.kind !== kinds.CommunityPostApproval && !approvals.has(e.id) && !muteFilter(e),
- );
-
- const callback = useTimelineCurserIntersectionCallback(timeline);
-
- const isMod = !!account && mods.includes(account?.pubkey);
- const PostComponent = isMod ? ModPendingPost : CommunityPost;
-
- return (
- <>
-
- {pending.map((event) => (
-
- ))}
-
-
- >
- );
-}
diff --git a/src/views/dms/chat.tsx b/src/views/dms/chat.tsx
index 2382680c8..3d34f8619 100644
--- a/src/views/dms/chat.tsx
+++ b/src/views/dms/chat.tsx
@@ -21,7 +21,7 @@ import DirectMessageBlock from "./components/direct-message-block";
import useParamsProfilePointer from "../../hooks/use-params-pubkey-pointer";
import useUserMailboxes from "../../hooks/use-user-mailboxes";
import RelaySet from "../../classes/relay-set";
-import useAppSettings from "../../hooks/use-app-settings";
+import useAppSettings from "../../hooks/use-user-app-settings";
import { truncateId } from "../../helpers/string";
import useRouterMarker from "../../hooks/use-router-marker";
import { BackIconButton } from "../../components/router/back-button";
diff --git a/src/views/dms/components/decrypt-placeholder.tsx b/src/views/dms/components/decrypt-placeholder.tsx
index f93f44242..917d55629 100644
--- a/src/views/dms/components/decrypt-placeholder.tsx
+++ b/src/views/dms/components/decrypt-placeholder.tsx
@@ -4,7 +4,7 @@ import { NostrEvent } from "nostr-tools";
import { UnlockIcon } from "../../../components/icons";
import DebugEventButton from "../../../components/debug-modal/debug-event-button";
-import useAppSettings from "../../../hooks/use-app-settings";
+import useAppSettings from "../../../hooks/use-user-app-settings";
import { useKind4Decrypt } from "../../../hooks/use-kind4-decryption";
export default function DecryptPlaceholder({
diff --git a/src/views/files/index.tsx b/src/views/files/index.tsx
index 0b4556916..1e0e1282f 100644
--- a/src/views/files/index.tsx
+++ b/src/views/files/index.tsx
@@ -5,7 +5,7 @@ import useTimelineLoader from "../../hooks/use-timeline-loader";
import { NostrEvent } from "../../types/nostr-event";
import { FILE_KIND, IMAGE_TYPES, VIDEO_TYPES, getFileUrl, parseImageFile } from "../../helpers/nostr/files";
import { ErrorBoundary } from "../../components/error-boundary";
-import useAppSettings from "../../hooks/use-app-settings";
+import useAppSettings from "../../hooks/use-user-app-settings";
import { TrustProvider, useTrustContext } from "../../providers/local/trust-provider";
import BlurredImage from "../../components/blured-image";
import PeopleListProvider, { usePeopleListContext } from "../../providers/local/people-list-provider";
diff --git a/src/views/link/index.tsx b/src/views/link/index.tsx
index b4df9f682..79747d9b2 100644
--- a/src/views/link/index.tsx
+++ b/src/views/link/index.tsx
@@ -53,7 +53,6 @@ function RenderRedirect({ event, link }: { event?: NostrEvent; link: string }) {
if (k === kinds.Followsets) return ;
if (k === kinds.Bookmarksets) return ;
if (k === kinds.BadgeDefinition) return ;
- if (k === kinds.CommunityDefinition) return ;
if (k === FLARE_VIDEO_KIND) return ;
if (k === kinds.ChannelCreation) return ;
if (k === kinds.ShortTextNote) return ;
diff --git a/src/views/lists/components/list-card.tsx b/src/views/lists/components/list-card.tsx
index 5d3ca14ec..2cfb62d6f 100644
--- a/src/views/lists/components/list-card.tsx
+++ b/src/views/lists/components/list-card.tsx
@@ -29,7 +29,7 @@ import { NostrEvent } from "../../../types/nostr-event";
import useReplaceableEvent from "../../../hooks/use-replaceable-event";
import ListFavoriteButton from "./list-favorite-button";
import ListMenu from "./list-menu";
-import { CommunityIcon, NotesIcon } from "../../../components/icons";
+import { NotesIcon } from "../../../components/icons";
import User01 from "../../../components/icons/user-01";
import HoverLinkOverlay from "../../../components/hover-link-overlay";
import NoteZapButton from "../../../components/note/note-zap-button";
@@ -40,11 +40,10 @@ import { createCoordinate } from "../../../classes/batch-kind-pubkey-loader";
import useEventIntersectionRef from "../../../hooks/use-event-intersection-ref";
import { getSharableEventAddress } from "../../../services/relay-hints";
-export function ListCardContent({ list, ...props }: Omit & { list: NostrEvent }) {
+export function ListCardContent({ list }: { list: NostrEvent }) {
const people = getPubkeysFromList(list);
const notes = getEventPointersFromList(list);
const coordinates = getAddressPointersFromList(list);
- const communities = coordinates.filter((cord) => cord.kind === kinds.CommunityDefinition);
const articles = coordinates.filter((cord) => cord.kind === kinds.LongFormArticle);
const references = getReferencesFromList(list);
@@ -70,11 +69,6 @@ export function ListCardContent({ list, ...props }: Omit
{articles.length}
)}
- {communities.length > 0 && (
-
- {communities.length}
-
- )}
);
}
diff --git a/src/views/media/media-post.tsx b/src/views/media/media-post.tsx
index aae2d02cd..c232d7515 100644
--- a/src/views/media/media-post.tsx
+++ b/src/views/media/media-post.tsx
@@ -11,7 +11,7 @@ import MediaPostSlides from "../../components/media-post/media-slides";
import MediaPostContents from "../../components/media-post/media-post-content";
import { TrustProvider } from "../../providers/local/trust-provider";
import DebugEventButton from "../../components/debug-modal/debug-event-button";
-import RepostButton from "../../components/note/timeline-note/components/repost-button";
+import ShareButton from "../../components/note/timeline-note/components/share-button";
import QuoteEventButton from "../../components/note/quote-event-button";
import { useBreakpointValue } from "../../providers/global/breakpoint-provider";
import EventZapIconButton from "../../components/zap/event-zap-icon-button";
@@ -30,7 +30,7 @@ function Header({ post }: { post: NostrEvent }) {
-
+
diff --git a/src/views/new/note/short-text-form.tsx b/src/views/new/note/short-text-form.tsx
index 391af07d0..a0e670605 100644
--- a/src/views/new/note/short-text-form.tsx
+++ b/src/views/new/note/short-text-form.tsx
@@ -29,7 +29,7 @@ import { Emoji } from "applesauce-core/helpers";
import { useFinalizeDraft, usePublishEvent } from "../../../providers/global/publish-provider";
import useCurrentAccount from "../../../hooks/use-current-account";
-import useAppSettings from "../../../hooks/use-app-settings";
+import useAppSettings from "../../../hooks/use-user-app-settings";
import localSettings from "../../../services/local-settings";
import useLocalStorageDisclosure from "../../../hooks/use-localstorage-disclosure";
import PublishAction from "../../../classes/nostr-publish-action";
@@ -46,13 +46,11 @@ import { ChevronDownIcon, ChevronUpIcon } from "../../../components/icons";
import ZapSplitCreator, { Split } from "./zap-split-creator";
import MinePOW from "../../../components/pow/mine-pow";
import { PublishDetails } from "../../task-manager/publish-log/publish-details";
-import CommunitySelect from "../../../components/post-modal/community-select";
type FormValues = {
content: string;
nsfw: boolean;
nsfwReason: string;
- community: string;
split: Split[];
difficulty: number;
};
@@ -60,13 +58,11 @@ type FormValues = {
export type ShortTextNoteFormProps = {
cacheFormKey?: string | null;
initContent?: string;
- initCommunity?: string;
};
export default function ShortTextNoteForm({
cacheFormKey = "new-note",
initContent = "",
- initCommunity = "",
}: Omit & ShortTextNoteFormProps) {
const publish = usePublishEvent();
const finalizeDraft = useFinalizeDraft();
@@ -86,7 +82,6 @@ export default function ShortTextNoteForm({
content: initContent,
nsfw: false,
nsfwReason: "",
- community: initCommunity,
split: [] as Split[],
difficulty: noteDifficulty || 0,
},
@@ -112,9 +107,6 @@ export default function ShortTextNoteForm({
splits: values.split,
});
- // TODO: remove when NIP-72 communities are removed
- if (values.community) draft.tags.push(["a", values.community]);
-
const unsigned = await finalizeDraft(draft);
setDraft(unsigned);
@@ -232,10 +224,6 @@ export default function ShortTextNoteForm({
{showAdvanced && (
-
- Post to community
-
-
NSFW
{getValues().nsfw && (
diff --git a/src/views/other-stuff/apps.ts b/src/views/other-stuff/apps.ts
index 2c578ddc5..2c9922207 100644
--- a/src/views/other-stuff/apps.ts
+++ b/src/views/other-stuff/apps.ts
@@ -3,7 +3,6 @@ import {
BadgeIcon,
BookmarkIcon,
ChannelsIcon,
- CommunityIcon,
DirectMessagesIcon,
EmojiPacksIcon,
GoalIcon,
@@ -40,13 +39,6 @@ export const internalApps: App[] = [
id: "media",
to: "/media",
},
- {
- title: "Communities",
- description: "Create and manage communities",
- icon: CommunityIcon,
- id: "communities",
- to: "/communities",
- },
{ title: "Wiki", description: "Browse wiki pages", icon: WikiIcon, id: "wiki", to: "/wiki" },
{
title: "Channels",
@@ -88,13 +80,6 @@ export const internalTools: App[] = [
id: "unknown",
to: "/tools/unknown",
},
- {
- title: "Satellite CDN",
- description: "Scalable media hosting for the nostr ecosystem",
- image: "https://satellite.earth/image.png",
- id: "satellite-cdn",
- to: "/tools/satellite-cdn",
- },
{ title: "Map", description: "Explore events with geohashes", icon: MapIcon, id: "map", to: "/map" },
{
title: "Stream Moderation",
@@ -189,6 +174,14 @@ export const externalTools: App[] = [
image: "https://nostr-delete.vercel.app/favicon.png",
isExternal: true,
},
+ {
+ title: "Satellite CDN",
+ description: "Scalable media hosting for the nostr ecosystem",
+ image: "https://satellite.earth/image.png",
+ id: "satellite-cdn",
+ to: "https://satellite.earth/cdn",
+ isExternal: true,
+ },
{
id: "w3.do",
title: "URL Shortener",
diff --git a/src/views/relays/components/supported-nips.tsx b/src/views/relays/components/supported-nips.tsx
index cd0488654..cd887b224 100644
--- a/src/views/relays/components/supported-nips.tsx
+++ b/src/views/relays/components/supported-nips.tsx
@@ -3,27 +3,24 @@ import { Flex, FlexProps, Tag, Tooltip } from "@chakra-ui/react";
// copied from github
export const NIP_NAMES: Record = {
"01": "Basic protocol",
- "02": "Contact List and Petnames",
+ "02": "Follow List",
"03": "OpenTimestamps Attestations for Events",
"04": "Encrypted Direct Message",
"05": "Mapping Nostr keys to DNS-based internet identifiers",
"06": "Basic key derivation from mnemonic seed phrase",
"07": "window.nostr capability for web browsers",
"08": "Handling Mentions",
- "09": "Event Deletion",
- "10": "Conventions for clients' use of e and p tags in text events",
+ "09": "Event Deletion Request",
+ "10": "Conventions for clients' use of `e` and `p` tags in text events",
"11": "Relay Information Document",
- "12": "Generic Tag Queries",
"13": "Proof of Work",
- "14": "Subject tag in Text events",
- "15": "Nostr Marketplace",
- "16": "Event Treatment",
+ "14": "Subject tag in text events",
+ "15": "Nostr Marketplace (for resilient marketplaces)",
"17": "Private Direct Messages",
"18": "Reposts",
"19": "bech32-encoded entities",
- "20": "Command Results",
"21": "nostr: URI scheme",
- "22": "Generic Comments",
+ "22": "Comment",
"23": "Long-form Content",
"24": "Extra metadata fields and tags",
"25": "Reactions",
@@ -34,10 +31,10 @@ export const NIP_NAMES: Record = {
"30": "Custom Emoji",
"31": "Dealing with Unknown Events",
"32": "Labeling",
- "33": "Parameterized Replaceable Events",
"34": "git stuff",
"35": "Torrents",
- "36": "Sensitive Content / Content Warning",
+ "36": "Sensitive Content",
+ "37": "Draft Events",
"38": "User Statuses",
"39": "External Identities in Profiles",
"40": "Expiration Timestamp",
@@ -58,8 +55,12 @@ export const NIP_NAMES: Record = {
"57": "Lightning Zaps",
"58": "Badges",
"59": "Gift Wrap",
- "64": "Chess",
+ "60": "Cashu Wallet",
+ "61": "Nutzaps",
+ "64": "Chess (PGN)",
"65": "Relay List Metadata",
+ "68": "Picture-first feeds",
+ "69": "Peer-to-peer Order events",
"70": "Protected Events",
"71": "Video Events",
"72": "Moderated Communities",
@@ -67,6 +68,7 @@ export const NIP_NAMES: Record = {
"75": "Zap Goals",
"78": "Application-specific data",
"84": "Highlights",
+ "86": "Relay Management API",
"89": "Recommended Application Handlers",
"90": "Data Vending Machines",
"92": "Media Attachments",
@@ -74,6 +76,8 @@ export const NIP_NAMES: Record = {
"96": "HTTP File Storage Integration",
"98": "HTTP Auth",
"99": "Classified Listings",
+ "7D": "Threads",
+ C7: "Chats",
};
function NipTag({ nip, name }: { nip: number; name?: boolean }) {
diff --git a/src/views/relays/media-servers/index.tsx b/src/views/relays/media-servers/index.tsx
index e7cbf4dd8..ba63aae8b 100644
--- a/src/views/relays/media-servers/index.tsx
+++ b/src/views/relays/media-servers/index.tsx
@@ -32,7 +32,7 @@ import BackButton from "../../../components/router/back-button";
import useUsersMediaServers from "../../../hooks/use-user-media-servers";
import DebugEventButton from "../../../components/debug-modal/debug-event-button";
import { cloneEvent } from "../../../helpers/nostr/event";
-import useAppSettings from "../../../hooks/use-app-settings";
+import useAppSettings from "../../../hooks/use-user-app-settings";
import useAsyncErrorHandler from "../../../hooks/use-async-error-handler";
import { isServerTag } from "../../../helpers/nostr/blossom";
import { USER_BLOSSOM_SERVER_LIST_KIND, areServersEqual } from "blossom-client-sdk";
diff --git a/src/views/settings/use-settings-form.ts b/src/views/settings/use-settings-form.ts
index f9879444f..3235d7b5d 100644
--- a/src/views/settings/use-settings-form.ts
+++ b/src/views/settings/use-settings-form.ts
@@ -1,5 +1,5 @@
import { useToast } from "@chakra-ui/react";
-import useAppSettings from "../../hooks/use-app-settings";
+import useAppSettings from "../../hooks/use-user-app-settings";
import { useForm } from "react-hook-form";
export default function useSettingsForm() {
diff --git a/src/views/streams/components/stream-share-button.tsx b/src/views/streams/components/stream-share-button.tsx
index 57bce6b26..0f8a18749 100644
--- a/src/views/streams/components/stream-share-button.tsx
+++ b/src/views/streams/components/stream-share-button.tsx
@@ -13,7 +13,7 @@ export type StreamShareButtonProps = Omit &
export default function StreamShareButton({
stream,
"aria-label": ariaLabel,
- title = "Quote repost",
+ title = "Quote share",
...props
}: StreamShareButtonProps) {
const { openModal } = useContext(PostModalContext);
diff --git a/src/views/thread/components/thread-post.tsx b/src/views/thread/components/thread-post.tsx
index 27f706235..6290c9a56 100644
--- a/src/views/thread/components/thread-post.tsx
+++ b/src/views/thread/components/thread-post.tsx
@@ -15,10 +15,10 @@ import Expand01 from "../../../components/icons/expand-01";
import Minus from "../../../components/icons/minus";
import { useBreakpointValue } from "../../../providers/global/breakpoint-provider";
import UserDnsIdentity from "../../../components/user/user-dns-identity";
-import useAppSettings from "../../../hooks/use-app-settings";
+import useAppSettings from "../../../hooks/use-user-app-settings";
import useThreadColorLevelProps from "../../../hooks/use-thread-color-level-props";
import POWIcon from "../../../components/pow/pow-icon";
-import RepostButton from "../../../components/note/timeline-note/components/repost-button";
+import ShareButton from "../../../components/note/timeline-note/components/share-button";
import QuoteEventButton from "../../../components/note/quote-event-button";
import NoteZapButton from "../../../components/note/note-zap-button";
import NoteProxyLink from "../../../components/note/timeline-note/components/note-proxy-link";
@@ -114,7 +114,7 @@ function ThreadPost({ post, initShowReplies, focusId, level = -1 }: ThreadItemPr
} />
-
+
diff --git a/src/views/torrents/components/torrents-comments.tsx b/src/views/torrents/components/torrents-comments.tsx
index 7b4dfcb06..a1c9cf0ee 100644
--- a/src/views/torrents/components/torrents-comments.tsx
+++ b/src/views/torrents/components/torrents-comments.tsx
@@ -20,7 +20,7 @@ import useThreadTimelineLoader from "../../../hooks/use-thread-timeline-loader";
import { countReplies, repliesByDate } from "../../../helpers/thread";
import { useTimelineCurserIntersectionCallback } from "../../../hooks/use-timeline-cursor-intersection-callback";
import IntersectionObserverProvider from "../../../providers/local/intersection-observer";
-import useAppSettings from "../../../hooks/use-app-settings";
+import useAppSettings from "../../../hooks/use-user-app-settings";
import useClientSideMuteFilter from "../../../hooks/use-client-side-mute-filter";
import UserAvatarLink from "../../../components/user/user-avatar-link";
import UserLink from "../../../components/user/user-link";
diff --git a/src/views/user/about/index.tsx b/src/views/user/about/index.tsx
index df86fa6f2..57b242fb3 100644
--- a/src/views/user/about/index.tsx
+++ b/src/views/user/about/index.tsx
@@ -42,7 +42,6 @@ import UserZapButton from "../components/user-zap-button";
import { UserProfileMenu } from "../components/user-profile-menu";
import { useSharableProfileId } from "../../../hooks/use-shareable-profile-id";
import UserProfileBadges from "./user-profile-badges";
-import UserJoinedCommunities from "./user-joined-communities";
import UserPinnedEvents from "./user-pinned-events";
import UserStatsAccordion from "./user-stats-accordion";
import UserJoinedChanneled from "./user-joined-channels";
@@ -51,6 +50,7 @@ import UserName from "../../../components/user/user-name";
import { useUserDNSIdentity } from "../../../hooks/use-user-dns-identity";
import UserAboutContent from "../../../components/user/user-about";
import UserRecentEvents from "./user-recent-events";
+import useAppSettings, { useUserAppSettings } from "../../../hooks/use-user-app-settings";
function DNSIdentityWarning({ pubkey }: { pubkey: string }) {
const metadata = useUserProfile(pubkey);
@@ -98,6 +98,7 @@ export default function UserAboutTab() {
const npub = nip19.npubEncode(pubkey);
const nprofile = useSharableProfileId(pubkey);
const pubkeyColor = "#" + pubkey.slice(0, 6);
+ const settings = useUserAppSettings(pubkey);
const parsedNip05 = metadata?.nip05 ? parseAddress(metadata.nip05) : undefined;
const nip05URL = parsedNip05
@@ -215,6 +216,13 @@ export default function UserAboutTab() {
)}
+
+ {settings?.primaryColor && (
+
+
+ noStrudel theme color
+
+ )}
@@ -254,7 +262,6 @@ export default function UserAboutTab() {
Nostree page
-
diff --git a/src/views/user/about/user-joined-communities.tsx b/src/views/user/about/user-joined-communities.tsx
deleted file mode 100644
index d46e5bae0..000000000
--- a/src/views/user/about/user-joined-communities.tsx
+++ /dev/null
@@ -1,36 +0,0 @@
-import { Button, Flex, Heading, SimpleGrid, useDisclosure } from "@chakra-ui/react";
-
-import { useAdditionalRelayContext } from "../../../providers/local/additional-relay-context";
-import useUserCommunitiesList from "../../../hooks/use-user-communities-list";
-import { PointerCommunityCard } from "../../communities/components/community-card";
-import { ErrorBoundary } from "../../../components/error-boundary";
-import { useBreakpointValue } from "../../../providers/global/breakpoint-provider";
-
-export default function UserJoinedCommunities({ pubkey }: { pubkey: string }) {
- const contextRelays = useAdditionalRelayContext();
- const { pointers: communities } = useUserCommunitiesList(pubkey, contextRelays, { alwaysRequest: true });
- const columns = useBreakpointValue({ base: 1, lg: 2, xl: 3 }) ?? 1;
- const showAllCommunities = useDisclosure();
-
- if (communities.length === 0) return null;
-
- return (
-
-
- Joined Communities ({communities.length})
-
-
- {(showAllCommunities.isOpen ? communities : communities.slice(0, columns * 2)).map((pointer) => (
-
-
-
- ))}
-
- {!showAllCommunities.isOpen && communities.length > columns * 2 && (
-
- )}
-
- );
-}
diff --git a/src/views/user/about/user-recent-events.tsx b/src/views/user/about/user-recent-events.tsx
index 71ec2a4ad..363a7cd32 100644
--- a/src/views/user/about/user-recent-events.tsx
+++ b/src/views/user/about/user-recent-events.tsx
@@ -6,7 +6,6 @@ import {
ArticleIcon,
BookmarkIcon,
ChannelsIcon,
- CommunityIcon,
DirectMessagesIcon,
EmojiPacksIcon,
ListsIcon,
@@ -110,8 +109,6 @@ const KnownKinds: KnownKind[] = [
{ kind: kinds.Handlerinformation, name: "Application" },
{ kind: kinds.Handlerrecommendation, name: "App recommendation" },
- { kind: kinds.CommunityDefinition, icon: CommunityIcon, name: "Communities" },
-
{ kind: kinds.BadgeAward, name: "Badge Award" },
{ kind: kinds.LiveChatMessage, icon: MessageSquare02, name: "Stream Chat" },
diff --git a/src/views/wiki/components/markdown-editor.tsx b/src/views/wiki/components/markdown-editor.tsx
index 4cd649786..cd82b2d42 100644
--- a/src/views/wiki/components/markdown-editor.tsx
+++ b/src/views/wiki/components/markdown-editor.tsx
@@ -8,7 +8,7 @@ import EasyMDE from "easymde";
import "easymde/dist/easymde.min.css";
import useUsersMediaServers from "../../../hooks/use-user-media-servers";
-import useAppSettings from "../../../hooks/use-app-settings";
+import useAppSettings from "../../../hooks/use-user-app-settings";
import useCurrentAccount from "../../../hooks/use-current-account";
import { CharkaMarkdown } from "../../../components/markdown/markdown";