This commit is contained in:
parent
df16384f07
commit
b07961802c
@ -1,4 +1,4 @@
|
|||||||
import { EventKind, NostrLink } from "@snort/system";
|
import { EventKind, NostrLink, TaggedNostrEvent } from "@snort/system";
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import React, { useCallback, useEffect, useState } from "react";
|
import React, { useCallback, useEffect, useState } from "react";
|
||||||
import { useInView } from "react-intersection-observer";
|
import { useInView } from "react-intersection-observer";
|
||||||
@ -8,8 +8,10 @@ import { LRUCache } from "typescript-lru-cache";
|
|||||||
|
|
||||||
import { Relay } from "@/Cache";
|
import { Relay } from "@/Cache";
|
||||||
import NoteHeader from "@/Components/Event/Note/NoteHeader";
|
import NoteHeader from "@/Components/Event/Note/NoteHeader";
|
||||||
|
import NoteQuote from "@/Components/Event/Note/NoteQuote";
|
||||||
import { NoteText } from "@/Components/Event/Note/NoteText";
|
import { NoteText } from "@/Components/Event/Note/NoteText";
|
||||||
import { TranslationInfo } from "@/Components/Event/Note/TranslationInfo";
|
import { TranslationInfo } from "@/Components/Event/Note/TranslationInfo";
|
||||||
|
import Username from "@/Components/User/Username";
|
||||||
import useModeration from "@/Hooks/useModeration";
|
import useModeration from "@/Hooks/useModeration";
|
||||||
import { findTag } from "@/Utils";
|
import { findTag } from "@/Utils";
|
||||||
import { chainKey } from "@/Utils/Thread/ChainKey";
|
import { chainKey } from "@/Utils/Thread/ChainKey";
|
||||||
@ -146,7 +148,25 @@ function useGoToEvent(props, options) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleNonTextNote(ev) {
|
function Reaction({ ev }: { ev: TaggedNostrEvent }) {
|
||||||
|
const reactedToTag = ev.tags.find((tag: string[]) => tag[0] === "e");
|
||||||
|
const link = NostrLink.fromTag(reactedToTag);
|
||||||
|
if (!reactedToTag) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<div className="note card">
|
||||||
|
<div className="text-gray-medium font-bold">
|
||||||
|
<Username pubkey={ev.pubkey} onLinkVisit={() => {}} />
|
||||||
|
<span> </span>
|
||||||
|
<FormattedMessage defaultMessage="liked" id="TvKqBp" />
|
||||||
|
</div>
|
||||||
|
<NoteQuote link={link} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleNonTextNote(ev: TaggedNostrEvent) {
|
||||||
const alt = findTag(ev, "alt");
|
const alt = findTag(ev, "alt");
|
||||||
if (alt) {
|
if (alt) {
|
||||||
return (
|
return (
|
||||||
@ -154,6 +174,8 @@ function handleNonTextNote(ev) {
|
|||||||
<Text id={ev.id} content={alt} tags={[]} creator={ev.pubkey} />
|
<Text id={ev.id} content={alt} tags={[]} creator={ev.pubkey} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
} else if (ev.kind === EventKind.Reaction) {
|
||||||
|
return <Reaction ev={ev} />;
|
||||||
} else {
|
} else {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
@ -20,6 +20,7 @@ export interface TimelineSubject {
|
|||||||
items: string[];
|
items: string[];
|
||||||
relay?: Array<string>;
|
relay?: Array<string>;
|
||||||
extra?: (rb: RequestBuilder) => void;
|
extra?: (rb: RequestBuilder) => void;
|
||||||
|
kinds?: EventKind[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export type TimelineFeed = ReturnType<typeof useTimelineFeed>;
|
export type TimelineFeed = ReturnType<typeof useTimelineFeed>;
|
||||||
@ -37,14 +38,14 @@ export default function useTimelineFeed(subject: TimelineSubject, options: Timel
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const kinds =
|
||||||
|
subject.kinds ??
|
||||||
|
(subject.type === "profile_keyword"
|
||||||
|
? [EventKind.SetMetadata]
|
||||||
|
: [EventKind.TextNote, EventKind.Repost, EventKind.Polls]);
|
||||||
|
|
||||||
const b = new RequestBuilder(`timeline:${subject.type}:${subject.discriminator}`);
|
const b = new RequestBuilder(`timeline:${subject.type}:${subject.discriminator}`);
|
||||||
const f = b
|
const f = b.withFilter().kinds(kinds);
|
||||||
.withFilter()
|
|
||||||
.kinds(
|
|
||||||
subject.type === "profile_keyword"
|
|
||||||
? [EventKind.SetMetadata]
|
|
||||||
: [EventKind.TextNote, EventKind.Repost, EventKind.Polls],
|
|
||||||
);
|
|
||||||
|
|
||||||
if (subject.relay) {
|
if (subject.relay) {
|
||||||
subject.relay.forEach(r => f.relay(r));
|
subject.relay.forEach(r => f.relay(r));
|
||||||
@ -75,7 +76,7 @@ export default function useTimelineFeed(subject: TimelineSubject, options: Timel
|
|||||||
}
|
}
|
||||||
subject.extra?.(b);
|
subject.extra?.(b);
|
||||||
return b;
|
return b;
|
||||||
}, [subject.type, subject.items, subject.discriminator, subject.extra]);
|
}, [subject]);
|
||||||
|
|
||||||
const sub = useMemo(() => {
|
const sub = useMemo(() => {
|
||||||
const rb = createBuilder();
|
const rb = createBuilder();
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
|
import debug from "debug";
|
||||||
import { FormattedMessage } from "react-intl";
|
import { FormattedMessage } from "react-intl";
|
||||||
import { useRouteError } from "react-router-dom";
|
import { useRouteError } from "react-router-dom";
|
||||||
|
|
||||||
import AsyncButton from "@/Components/Button/AsyncButton";
|
import AsyncButton from "@/Components/Button/AsyncButton";
|
||||||
import { db } from "@/Db";
|
import { db } from "@/Db";
|
||||||
import debug from "debug";
|
|
||||||
|
|
||||||
const log = debug("ErrorPage");
|
const log = debug("ErrorPage");
|
||||||
|
|
||||||
|
@ -24,6 +24,7 @@ import {
|
|||||||
FollowersTab,
|
FollowersTab,
|
||||||
FollowsTab,
|
FollowsTab,
|
||||||
ProfileNotesTab,
|
ProfileNotesTab,
|
||||||
|
ReactionsTab,
|
||||||
RelaysTab,
|
RelaysTab,
|
||||||
ZapsProfileTab,
|
ZapsProfileTab,
|
||||||
} from "@/Pages/Profile/ProfileTabComponents";
|
} from "@/Pages/Profile/ProfileTabComponents";
|
||||||
@ -139,6 +140,9 @@ export default function ProfilePage({ id: propId, state }: ProfilePageProps) {
|
|||||||
case ProfileTabType.BOOKMARKS: {
|
case ProfileTabType.BOOKMARKS: {
|
||||||
return <BookMarksTab id={id} />;
|
return <BookMarksTab id={id} />;
|
||||||
}
|
}
|
||||||
|
case ProfileTabType.REACTIONS: {
|
||||||
|
return <ReactionsTab id={id} />;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -175,9 +179,12 @@ export default function ProfilePage({ id: propId, state }: ProfilePageProps) {
|
|||||||
</div>
|
</div>
|
||||||
<div className="main-content">
|
<div className="main-content">
|
||||||
<div className="tabs p" ref={horizontalScroll}>
|
<div className="tabs p" ref={horizontalScroll}>
|
||||||
{[ProfileTabSelectors.Notes, ProfileTabSelectors.Followers, ProfileTabSelectors.Follows].map(
|
{[
|
||||||
renderTabSelector,
|
ProfileTabSelectors.Notes,
|
||||||
)}
|
ProfileTabSelectors.Reactions,
|
||||||
|
ProfileTabSelectors.Followers,
|
||||||
|
ProfileTabSelectors.Follows,
|
||||||
|
].map(renderTabSelector)}
|
||||||
{optionalTabs.map(renderTabSelector)}
|
{optionalTabs.map(renderTabSelector)}
|
||||||
{isMe && blocked.length > 0 && renderTabSelector(ProfileTabSelectors.Blocked)}
|
{isMe && blocked.length > 0 && renderTabSelector(ProfileTabSelectors.Blocked)}
|
||||||
</div>
|
</div>
|
||||||
|
@ -52,6 +52,22 @@ export function BookMarksTab({ id }: { id: HexKey }) {
|
|||||||
return <Bookmarks pubkey={id} bookmarks={bookmarks} />;
|
return <Bookmarks pubkey={id} bookmarks={bookmarks} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function ReactionsTab({ id }: { id: HexKey }) {
|
||||||
|
const subject = useMemo(
|
||||||
|
() =>
|
||||||
|
({
|
||||||
|
type: "pubkey",
|
||||||
|
items: [id],
|
||||||
|
discriminator: `reactions:${id.slice(0, 12)}`,
|
||||||
|
kinds: [EventKind.Reaction],
|
||||||
|
}) as TimelineSubject,
|
||||||
|
[id],
|
||||||
|
);
|
||||||
|
return (
|
||||||
|
<Timeline subject={subject} postsOnly={false} method={"LIMIT_UNTIL"} ignoreModeration={true} window={60 * 60 * 6} />
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export function ProfileNotesTab({ id, relays, isMe }: { id: HexKey; relays?: Array<string>; isMe: boolean }) {
|
export function ProfileNotesTab({ id, relays, isMe }: { id: HexKey; relays?: Array<string>; isMe: boolean }) {
|
||||||
const pinned = usePinList(id);
|
const pinned = usePinList(id);
|
||||||
const options = useMemo(() => ({ showTime: false, showPinned: true, canUnpin: isMe }), [isMe]);
|
const options = useMemo(() => ({ showTime: false, showPinned: true, canUnpin: isMe }), [isMe]);
|
||||||
|
@ -17,7 +17,7 @@ const ProfileTabSelectors = {
|
|||||||
Reactions: {
|
Reactions: {
|
||||||
text: (
|
text: (
|
||||||
<>
|
<>
|
||||||
<Icon name="reaction" size={16} />
|
<Icon name="heart-solid" size={16} />
|
||||||
<FormattedMessage defaultMessage="Reactions" id="XgWvGA" />
|
<FormattedMessage defaultMessage="Reactions" id="XgWvGA" />
|
||||||
</>
|
</>
|
||||||
),
|
),
|
||||||
|
@ -924,6 +924,9 @@
|
|||||||
"Tpy00S": {
|
"Tpy00S": {
|
||||||
"defaultMessage": "People"
|
"defaultMessage": "People"
|
||||||
},
|
},
|
||||||
|
"TvKqBp": {
|
||||||
|
"defaultMessage": "liked"
|
||||||
|
},
|
||||||
"TwyMau": {
|
"TwyMau": {
|
||||||
"defaultMessage": "Account"
|
"defaultMessage": "Account"
|
||||||
},
|
},
|
||||||
|
@ -304,6 +304,7 @@
|
|||||||
"TdtZQ5": "Crypto",
|
"TdtZQ5": "Crypto",
|
||||||
"TpgeGw": "Hex Salt..",
|
"TpgeGw": "Hex Salt..",
|
||||||
"Tpy00S": "People",
|
"Tpy00S": "People",
|
||||||
|
"TvKqBp": "liked",
|
||||||
"TwyMau": "Account",
|
"TwyMau": "Account",
|
||||||
"U1aPPi": "Stop listening",
|
"U1aPPi": "Stop listening",
|
||||||
"UDYlxu": "Pending Subscriptions",
|
"UDYlxu": "Pending Subscriptions",
|
||||||
|
Loading…
Reference in New Issue
Block a user