reactions tab
continuous-integration/drone/push Build is running Details

This commit is contained in:
Martti Malmi 2024-02-07 10:36:17 +02:00
parent df16384f07
commit b07961802c
8 changed files with 65 additions and 15 deletions

View File

@ -1,4 +1,4 @@
import { EventKind, NostrLink } from "@snort/system";
import { EventKind, NostrLink, TaggedNostrEvent } from "@snort/system";
import classNames from "classnames";
import React, { useCallback, useEffect, useState } from "react";
import { useInView } from "react-intersection-observer";
@ -8,8 +8,10 @@ import { LRUCache } from "typescript-lru-cache";
import { Relay } from "@/Cache";
import NoteHeader from "@/Components/Event/Note/NoteHeader";
import NoteQuote from "@/Components/Event/Note/NoteQuote";
import { NoteText } from "@/Components/Event/Note/NoteText";
import { TranslationInfo } from "@/Components/Event/Note/TranslationInfo";
import Username from "@/Components/User/Username";
import useModeration from "@/Hooks/useModeration";
import { findTag } from "@/Utils";
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");
if (alt) {
return (
@ -154,6 +174,8 @@ function handleNonTextNote(ev) {
<Text id={ev.id} content={alt} tags={[]} creator={ev.pubkey} />
</div>
);
} else if (ev.kind === EventKind.Reaction) {
return <Reaction ev={ev} />;
} else {
return (
<>

View File

@ -20,6 +20,7 @@ export interface TimelineSubject {
items: string[];
relay?: Array<string>;
extra?: (rb: RequestBuilder) => void;
kinds?: EventKind[];
}
export type TimelineFeed = ReturnType<typeof useTimelineFeed>;
@ -37,14 +38,14 @@ export default function useTimelineFeed(subject: TimelineSubject, options: Timel
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 f = b
.withFilter()
.kinds(
subject.type === "profile_keyword"
? [EventKind.SetMetadata]
: [EventKind.TextNote, EventKind.Repost, EventKind.Polls],
);
const f = b.withFilter().kinds(kinds);
if (subject.relay) {
subject.relay.forEach(r => f.relay(r));
@ -75,7 +76,7 @@ export default function useTimelineFeed(subject: TimelineSubject, options: Timel
}
subject.extra?.(b);
return b;
}, [subject.type, subject.items, subject.discriminator, subject.extra]);
}, [subject]);
const sub = useMemo(() => {
const rb = createBuilder();

View File

@ -1,9 +1,9 @@
import debug from "debug";
import { FormattedMessage } from "react-intl";
import { useRouteError } from "react-router-dom";
import AsyncButton from "@/Components/Button/AsyncButton";
import { db } from "@/Db";
import debug from "debug";
const log = debug("ErrorPage");

View File

@ -24,6 +24,7 @@ import {
FollowersTab,
FollowsTab,
ProfileNotesTab,
ReactionsTab,
RelaysTab,
ZapsProfileTab,
} from "@/Pages/Profile/ProfileTabComponents";
@ -139,6 +140,9 @@ export default function ProfilePage({ id: propId, state }: ProfilePageProps) {
case ProfileTabType.BOOKMARKS: {
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 className="main-content">
<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)}
{isMe && blocked.length > 0 && renderTabSelector(ProfileTabSelectors.Blocked)}
</div>

View File

@ -52,6 +52,22 @@ export function BookMarksTab({ id }: { id: HexKey }) {
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 }) {
const pinned = usePinList(id);
const options = useMemo(() => ({ showTime: false, showPinned: true, canUnpin: isMe }), [isMe]);

View File

@ -17,7 +17,7 @@ const ProfileTabSelectors = {
Reactions: {
text: (
<>
<Icon name="reaction" size={16} />
<Icon name="heart-solid" size={16} />
<FormattedMessage defaultMessage="Reactions" id="XgWvGA" />
</>
),

View File

@ -924,6 +924,9 @@
"Tpy00S": {
"defaultMessage": "People"
},
"TvKqBp": {
"defaultMessage": "liked"
},
"TwyMau": {
"defaultMessage": "Account"
},

View File

@ -304,6 +304,7 @@
"TdtZQ5": "Crypto",
"TpgeGw": "Hex Salt..",
"Tpy00S": "People",
"TvKqBp": "liked",
"TwyMau": "Account",
"U1aPPi": "Stop listening",
"UDYlxu": "Pending Subscriptions",