forked from Kieran/snort
fix: notification avatar overflow
refactor: sort notification group avatar by WoT score
This commit is contained in:
parent
f10ad6dd53
commit
22863a289d
@ -21,8 +21,8 @@ export function userSearch(search: string) {
|
|||||||
const combinedResults = fuseResults
|
const combinedResults = fuseResults
|
||||||
.map(result => {
|
.map(result => {
|
||||||
const fuseScore = result.score === undefined ? 1 : result.score;
|
const fuseScore = result.score === undefined ? 1 : result.score;
|
||||||
const followDistance =
|
|
||||||
socialGraphInstance.getFollowDistance(result.item.pubkey) / followDistanceNormalizationFactor;
|
const followDistance = wotScore(result.item.pubkey) / followDistanceNormalizationFactor;
|
||||||
|
|
||||||
const startsWithSearchString = [result.item.name, result.item.display_name, result.item.nip05].some(
|
const startsWithSearchString = [result.item.name, result.item.display_name, result.item.nip05].some(
|
||||||
field => field && field.toLowerCase?.().startsWith(searchString.toLowerCase()),
|
field => field && field.toLowerCase?.().startsWith(searchString.toLowerCase()),
|
||||||
@ -49,4 +49,12 @@ export function userSearch(search: string) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
return combinedResults.map(r => r.item);
|
return combinedResults.map(r => r.item);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function wotScore(pubkey: string) {
|
||||||
|
return socialGraphInstance.getFollowDistance(pubkey);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function sortByWoT(pubkeys: Array<string>) {
|
||||||
|
return pubkeys.sort((a, b) => wotScore(a) > wotScore(b) ? 1 : -1);
|
||||||
}
|
}
|
@ -8,11 +8,12 @@ import { useNavigate } from "react-router-dom";
|
|||||||
|
|
||||||
import Icon from "@/Components/Icons/Icon";
|
import Icon from "@/Components/Icons/Icon";
|
||||||
import ProfileImage from "@/Components/User/ProfileImage";
|
import ProfileImage from "@/Components/User/ProfileImage";
|
||||||
|
import { sortByWoT } from "@/Hooks/useProfileSearch";
|
||||||
import { dedupe, getDisplayName } from "@/Utils";
|
import { dedupe, getDisplayName } from "@/Utils";
|
||||||
import { formatShort } from "@/Utils/Number";
|
import { formatShort } from "@/Utils/Number";
|
||||||
|
|
||||||
import { notificationContext } from "./notificationContext";
|
import { getNotificationContext } from "./getNotificationContext";
|
||||||
import { NotificationContext } from "./Notifications";
|
import { NotificationContext } from "./notificationContext";
|
||||||
|
|
||||||
export function NotificationGroup({
|
export function NotificationGroup({
|
||||||
evs,
|
evs,
|
||||||
@ -40,7 +41,7 @@ export function NotificationGroup({
|
|||||||
);
|
);
|
||||||
const firstPubkey = pubkeys[0];
|
const firstPubkey = pubkeys[0];
|
||||||
const firstPubkeyProfile = useUserProfile(inView ? (firstPubkey === "anon" ? "" : firstPubkey) : "");
|
const firstPubkeyProfile = useUserProfile(inView ? (firstPubkey === "anon" ? "" : firstPubkey) : "");
|
||||||
const context = notificationContext(evs[0]);
|
const context = getNotificationContext(evs[0]);
|
||||||
const totalZaps = zaps.reduce((acc, v) => acc + v.amount, 0);
|
const totalZaps = zaps.reduce((acc, v) => acc + v.amount, 0);
|
||||||
|
|
||||||
const iconName = () => {
|
const iconName = () => {
|
||||||
@ -113,9 +114,9 @@ export function NotificationGroup({
|
|||||||
<div>{kind === EventKind.ZapReceipt && formatShort(totalZaps)}</div>
|
<div>{kind === EventKind.ZapReceipt && formatShort(totalZaps)}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col w-max g12">
|
<div className="flex flex-col w-max g12">
|
||||||
<div className="flex">
|
<div className="flex w-max overflow-hidden">
|
||||||
{pubkeys
|
{sortByWoT(pubkeys
|
||||||
.filter(a => a !== "anon")
|
.filter(a => a !== "anon"))
|
||||||
.slice(0, 12)
|
.slice(0, 12)
|
||||||
.map(v => (
|
.map(v => (
|
||||||
<ProfileImage
|
<ProfileImage
|
||||||
|
@ -1,22 +1,18 @@
|
|||||||
import "./Notifications.css";
|
import "./Notifications.css";
|
||||||
|
|
||||||
import { unwrap } from "@snort/shared";
|
import { unwrap } from "@snort/shared";
|
||||||
import { EventKind, NostrEvent, NostrLink, NostrPrefix, TaggedNostrEvent } from "@snort/system";
|
import { NostrEvent, NostrLink, TaggedNostrEvent } from "@snort/system";
|
||||||
import { useEventFeed } from "@snort/system-react";
|
|
||||||
import { lazy, Suspense, useEffect, useMemo } from "react";
|
import { lazy, Suspense, useEffect, useMemo } from "react";
|
||||||
|
|
||||||
import { ShowMoreInView } from "@/Components/Event/ShowMore";
|
import { ShowMoreInView } from "@/Components/Event/ShowMore";
|
||||||
import { LiveEvent } from "@/Components/LiveStream/LiveEvent";
|
|
||||||
import PageSpinner from "@/Components/PageSpinner";
|
import PageSpinner from "@/Components/PageSpinner";
|
||||||
import Text from "@/Components/Text/Text";
|
|
||||||
import ProfilePreview from "@/Components/User/ProfilePreview";
|
|
||||||
import { useNotificationsView } from "@/Feed/WorkerRelayView";
|
import { useNotificationsView } from "@/Feed/WorkerRelayView";
|
||||||
import useLogin from "@/Hooks/useLogin";
|
import useLogin from "@/Hooks/useLogin";
|
||||||
import useModeration from "@/Hooks/useModeration";
|
import useModeration from "@/Hooks/useModeration";
|
||||||
import { orderDescending } from "@/Utils";
|
import { orderDescending } from "@/Utils";
|
||||||
import { markNotificationsRead } from "@/Utils/Login";
|
import { markNotificationsRead } from "@/Utils/Login";
|
||||||
|
|
||||||
import { notificationContext } from "./notificationContext";
|
import { getNotificationContext } from "./getNotificationContext";
|
||||||
import { NotificationGroup } from "./NotificationGroup";
|
import { NotificationGroup } from "./NotificationGroup";
|
||||||
const NotificationGraph = lazy(() => import("@/Pages/Notifications/NotificationChart"));
|
const NotificationGraph = lazy(() => import("@/Pages/Notifications/NotificationChart"));
|
||||||
|
|
||||||
@ -44,9 +40,8 @@ export default function NotificationsPage({ onClick }: { onClick?: (link: NostrL
|
|||||||
|
|
||||||
const timeGrouped = useMemo(() => {
|
const timeGrouped = useMemo(() => {
|
||||||
return myNotifications.reduce((acc, v) => {
|
return myNotifications.reduce((acc, v) => {
|
||||||
const key = `${timeKey(v)}:${notificationContext(v as TaggedNostrEvent)?.encode(CONFIG.eventLinkPrefix)}:${
|
const key = `${timeKey(v)}:${getNotificationContext(v as TaggedNostrEvent)?.encode(CONFIG.eventLinkPrefix)}:${v.kind
|
||||||
v.kind
|
}`;
|
||||||
}`;
|
|
||||||
if (acc.has(key)) {
|
if (acc.has(key)) {
|
||||||
unwrap(acc.get(key)).push(v as TaggedNostrEvent);
|
unwrap(acc.get(key)).push(v as TaggedNostrEvent);
|
||||||
} else {
|
} else {
|
||||||
@ -67,31 +62,8 @@ export default function NotificationsPage({ onClick }: { onClick?: (link: NostrL
|
|||||||
{login.publicKey &&
|
{login.publicKey &&
|
||||||
[...timeGrouped.entries()].map(([k, g]) => <NotificationGroup key={k} evs={g} onClick={onClick} />)}
|
[...timeGrouped.entries()].map(([k, g]) => <NotificationGroup key={k} evs={g} onClick={onClick} />)}
|
||||||
|
|
||||||
<ShowMoreInView onClick={() => {}} />
|
<ShowMoreInView onClick={() => { }} />
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function NotificationContext({ link, onClick }: { link: NostrLink; onClick: () => void }) {
|
|
||||||
const ev = useEventFeed(link);
|
|
||||||
if (link.type === NostrPrefix.PublicKey) {
|
|
||||||
return <ProfilePreview pubkey={link.id} actions={<></>} />;
|
|
||||||
}
|
|
||||||
if (!ev) return;
|
|
||||||
if (ev.kind === EventKind.LiveEvent) {
|
|
||||||
return <LiveEvent ev={ev} />;
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<Text
|
|
||||||
id={ev.id}
|
|
||||||
content={ev.content}
|
|
||||||
tags={ev.tags}
|
|
||||||
creator={ev.pubkey}
|
|
||||||
truncate={120}
|
|
||||||
disableLinkPreview={true}
|
|
||||||
className="content"
|
|
||||||
onClick={onClick}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
@ -0,0 +1,35 @@
|
|||||||
|
import { unwrap } from "@snort/shared";
|
||||||
|
import { EventExt, EventKind, NostrLink, TaggedNostrEvent } from "@snort/system";
|
||||||
|
|
||||||
|
export function getNotificationContext(ev: TaggedNostrEvent) {
|
||||||
|
switch (ev.kind) {
|
||||||
|
case EventKind.ZapReceipt: {
|
||||||
|
const aTag = ev.tags.find(a => a[0] === "a");
|
||||||
|
if (aTag) {
|
||||||
|
return NostrLink.fromTag(aTag);
|
||||||
|
}
|
||||||
|
const eTag = ev.tags.find(a => a[0] === "e");
|
||||||
|
if (eTag) {
|
||||||
|
return NostrLink.fromTag(eTag);
|
||||||
|
}
|
||||||
|
const pTag = ev.tags.find(a => a[0] === "p");
|
||||||
|
if (pTag) {
|
||||||
|
return NostrLink.fromTag(pTag);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case EventKind.Repost:
|
||||||
|
case EventKind.Reaction: {
|
||||||
|
const thread = EventExt.extractThread(ev);
|
||||||
|
const tag = unwrap(thread?.replyTo ?? thread?.root ?? { value: ev.id, key: "e" });
|
||||||
|
if (tag.key === "e" || tag.key === "a") {
|
||||||
|
return NostrLink.fromThreadTag(tag);
|
||||||
|
} else {
|
||||||
|
throw new Error("Unknown thread context");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case EventKind.TextNote: {
|
||||||
|
return NostrLink.fromEvent(ev);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,35 +1,29 @@
|
|||||||
import { unwrap } from "@snort/shared";
|
import { EventKind, NostrLink, NostrPrefix } from "@snort/system";
|
||||||
import { EventExt, EventKind, NostrLink, TaggedNostrEvent } from "@snort/system";
|
import { useEventFeed } from "@snort/system-react";
|
||||||
|
|
||||||
export function notificationContext(ev: TaggedNostrEvent) {
|
import { LiveEvent } from "@/Components/LiveStream/LiveEvent";
|
||||||
switch (ev.kind) {
|
import Text from "@/Components/Text/Text";
|
||||||
case EventKind.ZapReceipt: {
|
import ProfilePreview from "@/Components/User/ProfilePreview";
|
||||||
const aTag = ev.tags.find(a => a[0] === "a");
|
|
||||||
if (aTag) {
|
export function NotificationContext({ link, onClick }: { link: NostrLink; onClick: () => void }) {
|
||||||
return NostrLink.fromTag(aTag);
|
const ev = useEventFeed(link);
|
||||||
}
|
if (link.type === NostrPrefix.PublicKey) {
|
||||||
const eTag = ev.tags.find(a => a[0] === "e");
|
return <ProfilePreview pubkey={link.id} actions={<></>} />;
|
||||||
if (eTag) {
|
|
||||||
return NostrLink.fromTag(eTag);
|
|
||||||
}
|
|
||||||
const pTag = ev.tags.find(a => a[0] === "p");
|
|
||||||
if (pTag) {
|
|
||||||
return NostrLink.fromTag(pTag);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case EventKind.Repost:
|
|
||||||
case EventKind.Reaction: {
|
|
||||||
const thread = EventExt.extractThread(ev);
|
|
||||||
const tag = unwrap(thread?.replyTo ?? thread?.root ?? { value: ev.id, key: "e" });
|
|
||||||
if (tag.key === "e" || tag.key === "a") {
|
|
||||||
return NostrLink.fromThreadTag(tag);
|
|
||||||
} else {
|
|
||||||
throw new Error("Unknown thread context");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case EventKind.TextNote: {
|
|
||||||
return NostrLink.fromEvent(ev);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if (!ev) return;
|
||||||
|
if (ev.kind === EventKind.LiveEvent) {
|
||||||
|
return <LiveEvent ev={ev} />;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<Text
|
||||||
|
id={ev.id}
|
||||||
|
content={ev.content}
|
||||||
|
tags={ev.tags}
|
||||||
|
creator={ev.pubkey}
|
||||||
|
truncate={120}
|
||||||
|
disableLinkPreview={true}
|
||||||
|
className="content"
|
||||||
|
onClick={onClick}
|
||||||
|
/>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user