add basic reports tab to users view

This commit is contained in:
hzrd149
2023-03-11 09:53:45 -06:00
parent 917023e576
commit fcd7526874
5 changed files with 91 additions and 3 deletions

View File

@@ -77,7 +77,6 @@ I would recomend you use a browser extension like [Alby](https://getalby.com/) o
- Rebuild relays view to show relay info and settings NIP-11
- filter list of followers by users the user has blocked/reported (stops bots/spammers from showing up at followers)
- Add mentions in notes (https://css-tricks.com/so-you-want-to-build-an-mention-autocomplete-feature/)
- add `client` tag to published events
- Save note drafts and let users manage them
- make app a valid web share target https://developer.chrome.com/articles/web-share-target/
- handle image share

View File

@@ -31,6 +31,7 @@ const UserZapsTab = React.lazy(() => import("./views/user/zaps"));
const DirectMessagesView = React.lazy(() => import("./views/dm"));
const DirectMessageChatView = React.lazy(() => import("./views/dm/chat"));
const NostrLinkView = React.lazy(() => import("./views/link"));
const UserReportsTab = React.lazy(() => import("./views/user/reports"));
const RequireCurrentAccount = ({ children }: { children: JSX.Element }) => {
let location = useLocation();
@@ -96,6 +97,7 @@ const router = createBrowserRouter([
{ path: "followers", element: <UserFollowersTab /> },
{ path: "following", element: <UserFollowingTab /> },
{ path: "relays", element: <UserRelaysTab /> },
{ path: "reports", element: <UserReportsTab /> },
],
},
{

View File

@@ -1,6 +1,6 @@
import moment from "moment";
import { getEventRelays } from "../services/event-relays";
import { DraftNostrEvent, isETag, isPTag, NostrEvent, RTag } from "../types/nostr-event";
import { DraftNostrEvent, isETag, isPTag, NostrEvent, RTag, Tag } from "../types/nostr-event";
import { RelayConfig, RelayMode } from "../classes/relay";
import accountService from "../services/account";
import { Kind } from "nostr-tools";
@@ -17,6 +17,22 @@ export function truncatedId(id: string, keep = 6) {
return id.substring(0, keep) + "..." + id.substring(id.length - keep);
}
export function getContentTagRefs(content: string) {
return Array.from(content.matchAll(/#\[(\d+)\]/gi)).map((m) => parseInt(m[1]));
}
export function filterTagsByContentRefs(content: string, tags: Tag[], referenced = true) {
const contentTagRefs = getContentTagRefs(content);
const newTags: Tag[] = [];
for (let i = 0; i < tags.length; i++) {
if (contentTagRefs.includes(i) === referenced) {
newTags.push(tags[i]);
}
}
return newTags;
}
export type EventReferences = ReturnType<typeof getReferences>;
export function getReferences(event: NostrEvent | DraftNostrEvent) {
const eTags = event.tags.filter(isETag);
@@ -24,7 +40,7 @@ export function getReferences(event: NostrEvent | DraftNostrEvent) {
const events = eTags.map((t) => t[1]);
const pubkeys = pTags.map((t) => t[1]);
const contentTagRefs = Array.from(event.content.matchAll(/#\[(\d+)\]/gi)).map((m) => parseInt(m[1]));
const contentTagRefs = getContentTagRefs(event.content);
let replyId = eTags.find((t) => t[3] === "reply")?.[1];
let rootId = eTags.find((t) => t[3] === "root")?.[1];

View File

@@ -14,6 +14,7 @@ const tabs = [
{ label: "Followers", path: "followers" },
{ label: "Following", path: "following" },
{ label: "Relays", path: "relays" },
{ label: "Reports", path: "reports" },
];
const UserView = () => {

View File

@@ -0,0 +1,70 @@
import { Box, Button, Flex, Spinner, Text } from "@chakra-ui/react";
import moment from "moment";
import { useOutletContext } from "react-router-dom";
import { RelayMode } from "../../classes/relay";
import { NoteLink } from "../../components/note-link";
import { UserLink } from "../../components/user-link";
import { filterTagsByContentRefs, truncatedId } from "../../helpers/nostr-event";
import { useReadRelayUrls } from "../../hooks/use-client-relays";
import useFallbackUserRelays from "../../hooks/use-fallback-user-relays";
import { useTimelineLoader } from "../../hooks/use-timeline-loader";
import relayScoreboardService from "../../services/relay-scoreboard";
import { isETag, isPTag, NostrEvent } from "../../types/nostr-event";
function ReportEvent({ report }: { report: NostrEvent }) {
const reportedEvent = report.tags.filter(isETag)[0]?.[1];
const reportedPubkey = filterTagsByContentRefs(report.content, report.tags, false).filter(isPTag)[0]?.[1];
if (!reportedEvent && !reportedPubkey) return null;
const reason = report.tags.find((t) => t[0] === "report")?.[1];
return (
<Flex gap="2">
{reportedEvent ? (
<>
<NoteLink noteId={reportedEvent} />
{reportedPubkey && (
<>
<Text>From</Text>
<UserLink pubkey={reportedPubkey} color="blue.500" />
</>
)}
</>
) : (
<UserLink pubkey={reportedPubkey} color="blue.500" />
)}
<Text>{reason}</Text>
</Flex>
);
}
export default function UserReportsTab() {
const { pubkey } = useOutletContext() as { pubkey: string };
// get user relays
const userRelays = useFallbackUserRelays(pubkey)
.filter((r) => r.mode & RelayMode.WRITE)
.map((r) => r.url);
// merge the users relays with client relays
const readRelays = useReadRelayUrls();
// find the top 4
const relays = relayScoreboardService.getRankedRelays(userRelays.length === 0 ? readRelays : userRelays).slice(0, 4);
const {
events: reports,
loading,
loadMore,
} = useTimelineLoader(
`${truncatedId(pubkey)}-reports`,
relays,
{ authors: [pubkey], kinds: [1984] },
{ pageSize: moment.duration(1, "week").asSeconds() }
);
return (
<Flex direction="column" gap="2" pr="2" pl="2">
{reports.map((report) => (
<ReportEvent key={report.id} report={report} />
))}
{loading ? <Spinner ml="auto" mr="auto" mt="8" mb="8" /> : <Button onClick={() => loadMore()}>Load More</Button>}
</Flex>
);
}