mirror of
https://github.com/hzrd149/nostrudel.git
synced 2025-09-19 12:00:32 +02:00
allow user to edit community moderators
This commit is contained in:
36
src/components/npub-autocomplete.tsx
Normal file
36
src/components/npub-autocomplete.tsx
Normal file
@@ -0,0 +1,36 @@
|
||||
import { Input, InputProps } from "@chakra-ui/react";
|
||||
import { forwardRef } from "react";
|
||||
import { useAsync } from "react-use";
|
||||
import { nip19 } from "nostr-tools";
|
||||
|
||||
import { useUserSearchDirectoryContext } from "../providers/user-directory-provider";
|
||||
import userMetadataService from "../services/user-metadata";
|
||||
import { getUserDisplayName } from "../helpers/user-metadata";
|
||||
|
||||
const NpubAutocomplete = forwardRef<HTMLInputElement, InputProps>(({ value, ...props }, ref) => {
|
||||
const getDirectory = useUserSearchDirectoryContext();
|
||||
|
||||
const { value: users } = useAsync(async () => {
|
||||
const dir = await getDirectory();
|
||||
return dir.map(({ pubkey }) => ({ pubkey, metadata: userMetadataService.getSubject(pubkey).value }));
|
||||
}, [getDirectory]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Input placeholder="npub..." list="users" value={value} {...props} ref={ref} />
|
||||
{users && (
|
||||
<datalist id="users">
|
||||
{users
|
||||
.filter((p) => !!p.metadata)
|
||||
.map(({ metadata, pubkey }) => (
|
||||
<option key={pubkey} value={nip19.npubEncode(pubkey)}>
|
||||
{getUserDisplayName(metadata, pubkey)}
|
||||
</option>
|
||||
))}
|
||||
</datalist>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
});
|
||||
|
||||
export default NpubAutocomplete;
|
@@ -2,7 +2,6 @@ import {
|
||||
Flex,
|
||||
Heading,
|
||||
IconButton,
|
||||
Input,
|
||||
NumberDecrementStepper,
|
||||
NumberIncrementStepper,
|
||||
NumberInput,
|
||||
@@ -12,18 +11,14 @@ import {
|
||||
useToast,
|
||||
} from "@chakra-ui/react";
|
||||
import { CloseIcon } from "@chakra-ui/icons";
|
||||
import { nip19 } from "nostr-tools";
|
||||
import { useForm } from "react-hook-form";
|
||||
|
||||
import { EventSplit } from "../../helpers/nostr/zaps";
|
||||
import { AddIcon } from "../icons";
|
||||
import { useUserSearchDirectoryContext } from "../../providers/user-directory-provider";
|
||||
import { useAsync } from "react-use";
|
||||
import { getUserDisplayName } from "../../helpers/user-metadata";
|
||||
import userMetadataService from "../../services/user-metadata";
|
||||
import { normalizeToHex } from "../../helpers/nip19";
|
||||
import UserAvatar from "../user-avatar";
|
||||
import { UserLink } from "../user-link";
|
||||
import NpubAutocomplete from "../npub-autocomplete";
|
||||
|
||||
function getRemainingPercent(split: EventSplit) {
|
||||
return Math.round((1 - split.reduce((v, p) => v + p.percent, 0)) * 100) / 100;
|
||||
@@ -58,12 +53,6 @@ function AddUserForm({
|
||||
});
|
||||
watch("percent");
|
||||
|
||||
const getDirectory = useUserSearchDirectoryContext();
|
||||
const { value: users } = useAsync(async () => {
|
||||
const dir = await getDirectory();
|
||||
return dir.map(({ pubkey }) => ({ pubkey, metadata: userMetadataService.getSubject(pubkey).value }));
|
||||
}, [getDirectory]);
|
||||
|
||||
const submit = handleSubmit((values) => {
|
||||
try {
|
||||
const pubkey = normalizeToHex(values.pubkey);
|
||||
@@ -78,18 +67,7 @@ function AddUserForm({
|
||||
|
||||
return (
|
||||
<Flex as="form" gap="2" onSubmit={submit}>
|
||||
<Input placeholder="npub..." list="users" {...register("pubkey", { required: true, validate: validateNpub })} />
|
||||
{users && (
|
||||
<datalist id="users">
|
||||
{users
|
||||
.filter((p) => !!p.metadata)
|
||||
.map(({ metadata, pubkey }) => (
|
||||
<option key={pubkey} value={nip19.npubEncode(pubkey)}>
|
||||
{getUserDisplayName(metadata, pubkey)}
|
||||
</option>
|
||||
))}
|
||||
</datalist>
|
||||
)}
|
||||
<NpubAutocomplete {...register("pubkey", { required: true, validate: validateNpub })} />
|
||||
<NumberInput
|
||||
step={1}
|
||||
min={1}
|
||||
|
@@ -1,3 +1,4 @@
|
||||
import { useCallback, useState } from "react";
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
@@ -7,9 +8,8 @@ import {
|
||||
FormHelperText,
|
||||
FormLabel,
|
||||
IconButton,
|
||||
Image,
|
||||
IconButtonProps,
|
||||
Input,
|
||||
Link,
|
||||
Modal,
|
||||
ModalBody,
|
||||
ModalCloseButton,
|
||||
@@ -26,18 +26,23 @@ import {
|
||||
useToast,
|
||||
} from "@chakra-ui/react";
|
||||
import { SubmitHandler, useForm } from "react-hook-form";
|
||||
|
||||
import { useCurrentAccount } from "../../../hooks/use-current-account";
|
||||
import UserAvatar from "../../../components/user-avatar";
|
||||
import { UserLink } from "../../../components/user-link";
|
||||
import { TrashIcon, UploadImageIcon } from "../../../components/icons";
|
||||
import { TrashIcon } from "../../../components/icons";
|
||||
import Upload01 from "../../../components/icons/upload-01";
|
||||
import Upload02 from "../../../components/icons/upload-02";
|
||||
import { useCallback, useState } from "react";
|
||||
import { nostrBuildUploadImage } from "../../../helpers/nostr-build";
|
||||
import { useSigningContext } from "../../../providers/signing-provider";
|
||||
import { RelayUrlInput } from "../../../components/relay-url-input";
|
||||
import { normalizeRelayUrl, safeRelayUrl } from "../../../helpers/url";
|
||||
import { safeRelayUrl } from "../../../helpers/url";
|
||||
import { RelayFavicon } from "../../../components/relay-favicon";
|
||||
import NpubAutocomplete from "../../../components/npub-autocomplete";
|
||||
import { normalizeToHex } from "../../../helpers/nip19";
|
||||
|
||||
function RemoveButton({ ...props }: IconButtonProps) {
|
||||
return <IconButton icon={<TrashIcon />} size="sm" colorScheme="red" variant="ghost" ml="auto" {...props} />;
|
||||
}
|
||||
|
||||
export type FormValues = {
|
||||
name: string;
|
||||
@@ -113,6 +118,22 @@ export default function CommunityCreateModal({
|
||||
[setValue, getValues, requestSignature, toast],
|
||||
);
|
||||
|
||||
const [modInput, setModInput] = useState("");
|
||||
const addMod = () => {
|
||||
if (!modInput) return;
|
||||
const pubkey = normalizeToHex(modInput);
|
||||
if (pubkey) {
|
||||
setValue("mods", getValues("mods").concat(pubkey));
|
||||
}
|
||||
setModInput("");
|
||||
};
|
||||
const removeMod = (pubkey: string) => {
|
||||
setValue(
|
||||
"mods",
|
||||
getValues("mods").filter((p) => p !== pubkey),
|
||||
);
|
||||
};
|
||||
|
||||
const [relayInput, setRelayInput] = useState("");
|
||||
const addRelay = () => {
|
||||
if (!relayInput) return;
|
||||
@@ -216,12 +237,25 @@ export default function CommunityCreateModal({
|
||||
|
||||
<FormControl isInvalid={!!errors.mods}>
|
||||
<FormLabel>Moderators</FormLabel>
|
||||
{getValues().mods.map((pubkey) => (
|
||||
<Flex gap="2" alignItems="center" key={pubkey}>
|
||||
<UserAvatar pubkey={pubkey} size="sm" />
|
||||
<UserLink pubkey={pubkey} fontWeight="bold" />
|
||||
</Flex>
|
||||
))}
|
||||
<Flex direction="column" gap="2" pb="2">
|
||||
{getValues().mods.map((pubkey) => (
|
||||
<Flex gap="2" alignItems="center" key={pubkey}>
|
||||
<UserAvatar pubkey={pubkey} size="sm" />
|
||||
<UserLink pubkey={pubkey} fontWeight="bold" />
|
||||
<RemoveButton
|
||||
aria-label={`Remove moderator`}
|
||||
title={`Remove moderator`}
|
||||
onClick={() => removeMod(pubkey)}
|
||||
/>
|
||||
</Flex>
|
||||
))}
|
||||
</Flex>
|
||||
<Flex gap="2">
|
||||
<NpubAutocomplete value={modInput} onChange={(e) => setModInput(e.target.value)} />
|
||||
<Button isDisabled={!modInput} onClick={addMod}>
|
||||
Add
|
||||
</Button>
|
||||
</Flex>
|
||||
</FormControl>
|
||||
|
||||
<FormControl isInvalid={!!errors.mods}>
|
||||
@@ -249,16 +283,7 @@ export default function CommunityCreateModal({
|
||||
<Text fontWeight="bold" isTruncated>
|
||||
{url}
|
||||
</Text>
|
||||
<IconButton
|
||||
icon={<TrashIcon />}
|
||||
aria-label={`Remove ${url}`}
|
||||
title={`Remove ${url}`}
|
||||
onClick={() => removeRelay(url)}
|
||||
size="sm"
|
||||
colorScheme="red"
|
||||
variant="ghost"
|
||||
ml="auto"
|
||||
/>
|
||||
<RemoveButton aria-label={`Remove ${url}`} title={`Remove ${url}`} onClick={() => removeRelay(url)} />
|
||||
</Flex>
|
||||
))}
|
||||
</Flex>
|
||||
|
Reference in New Issue
Block a user