mirror of
https://github.com/hzrd149/nostrudel.git
synced 2025-09-21 14:09:17 +02:00
add basic reports tab to users view
This commit is contained in:
@@ -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
|
||||
|
@@ -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 /> },
|
||||
],
|
||||
},
|
||||
{
|
||||
|
@@ -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];
|
||||
|
@@ -14,6 +14,7 @@ const tabs = [
|
||||
{ label: "Followers", path: "followers" },
|
||||
{ label: "Following", path: "following" },
|
||||
{ label: "Relays", path: "relays" },
|
||||
{ label: "Reports", path: "reports" },
|
||||
];
|
||||
|
||||
const UserView = () => {
|
||||
|
70
src/views/user/reports.tsx
Normal file
70
src/views/user/reports.tsx
Normal 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>
|
||||
);
|
||||
}
|
Reference in New Issue
Block a user