mirror of
https://github.com/hzrd149/nostrudel.git
synced 2025-04-11 21:29:26 +02:00
allow user to reorder media servers
This commit is contained in:
parent
289fff2f29
commit
794193b575
@ -60,8 +60,8 @@ function PublishAction({ pub }: { pub: NostrPublishAction }) {
|
||||
return (
|
||||
<>
|
||||
<Flex gap="2" alignItems="center" cursor="pointer" onClick={details.onOpen}>
|
||||
<Text>{pub.label}</Text>
|
||||
<PublishActionStatusTag ml="auto" pub={pub} />
|
||||
<Text isTruncated>{pub.label}</Text>
|
||||
<PublishActionStatusTag ml="auto" pub={pub} flexShrink={0} />
|
||||
</Flex>
|
||||
{details.isOpen && (
|
||||
<Modal isOpen onClose={details.onClose} size="2xl">
|
||||
|
@ -1,14 +1,5 @@
|
||||
import { NostrEvent } from "nostr-tools";
|
||||
import { safeUrl } from "../parse";
|
||||
import { BlobDescriptor, BlossomClient, Signer } from "blossom-client-sdk";
|
||||
|
||||
export function getServersFromEvent(event: NostrEvent) {
|
||||
return event.tags
|
||||
.filter((t) => t[0] === "r")
|
||||
.map((t) => safeUrl(t[1]))
|
||||
.filter(Boolean) as string[];
|
||||
}
|
||||
|
||||
export async function uploadFileToServers(servers: string[], file: File, signer: Signer) {
|
||||
const results: BlobDescriptor[] = [];
|
||||
|
||||
|
19
src/helpers/nostr/blossom.ts
Normal file
19
src/helpers/nostr/blossom.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import { NostrEvent } from "nostr-tools";
|
||||
import { safeUrl } from "../parse";
|
||||
|
||||
export const USER_MEDIA_SERVERS_KIND = 10063;
|
||||
|
||||
export function isServerTag(tag: string[]) {
|
||||
return (tag[0] === "r" || tag[0] === "server") && tag[1];
|
||||
}
|
||||
|
||||
export function serversEqual(a: string, b: string) {
|
||||
return new URL(a).hostname === new URL(b).hostname;
|
||||
}
|
||||
|
||||
export function getServersFromEvent(event: NostrEvent) {
|
||||
return event.tags
|
||||
.filter(isServerTag)
|
||||
.map((t) => safeUrl(t[1]))
|
||||
.filter(Boolean) as string[];
|
||||
}
|
@ -7,7 +7,7 @@ import { useSigningContext } from "../providers/global/signing-provider";
|
||||
import { UseFormGetValues, UseFormSetValue } from "react-hook-form";
|
||||
import useAppSettings from "./use-app-settings";
|
||||
import useUsersMediaServers from "./use-user-media-servers";
|
||||
import { getServersFromEvent, uploadFileToServers } from "../helpers/media-upload/blossom";
|
||||
import { uploadFileToServers } from "../helpers/media-upload/blossom";
|
||||
import useCurrentAccount from "./use-current-account";
|
||||
|
||||
export function useTextAreaUploadFileWithForm(
|
||||
@ -31,7 +31,7 @@ export default function useTextAreaUploadFile(
|
||||
const toast = useToast();
|
||||
const account = useCurrentAccount();
|
||||
const { mediaUploadService } = useAppSettings();
|
||||
const mediaServers = useUsersMediaServers(account?.pubkey);
|
||||
const { servers: mediaServers } = useUsersMediaServers(account?.pubkey);
|
||||
const { requestSignature } = useSigningContext();
|
||||
|
||||
const insertURL = useCallback(
|
||||
@ -68,8 +68,8 @@ export default function useTextAreaUploadFile(
|
||||
const response = await nostrBuildUploadImage(file, requestSignature);
|
||||
const imageUrl = response.url;
|
||||
insertURL(imageUrl);
|
||||
} else if (mediaUploadService === "blossom" && mediaServers) {
|
||||
const blob = await uploadFileToServers(getServersFromEvent(mediaServers), file, requestSignature);
|
||||
} else if (mediaUploadService === "blossom" && mediaServers.length) {
|
||||
const blob = await uploadFileToServers(mediaServers, file, requestSignature);
|
||||
insertURL(blob.url);
|
||||
}
|
||||
} catch (e) {
|
||||
|
@ -1,10 +1,16 @@
|
||||
import { useMemo } from "react";
|
||||
import { USER_MEDIA_SERVERS_KIND, getServersFromEvent } from "../helpers/nostr/blossom";
|
||||
import replaceableEventsService, { RequestOptions } from "../services/replaceable-events";
|
||||
import { useReadRelays } from "./use-client-relays";
|
||||
import useSubject from "./use-subject";
|
||||
|
||||
export default function useUsersMediaServers(pubkey?: string, additionalRelays?: string[], opts?: RequestOptions) {
|
||||
const readRelays = useReadRelays(additionalRelays);
|
||||
const sub = pubkey ? replaceableEventsService.requestEvent(readRelays, 10063, pubkey, undefined, opts) : undefined;
|
||||
const value = useSubject(sub);
|
||||
return value;
|
||||
const sub = pubkey
|
||||
? replaceableEventsService.requestEvent(readRelays, USER_MEDIA_SERVERS_KIND, pubkey, undefined, opts)
|
||||
: undefined;
|
||||
const event = useSubject(sub);
|
||||
const servers = useMemo(() => (event ? getServersFromEvent(event) : []), [event?.id]);
|
||||
|
||||
return { event, servers };
|
||||
}
|
||||
|
@ -34,29 +34,42 @@ import DebugEventButton from "../../../components/debug-modal/debug-event-button
|
||||
import { cloneEvent } from "../../../helpers/nostr/event";
|
||||
import useAppSettings from "../../../hooks/use-app-settings";
|
||||
import useAsyncErrorHandler from "../../../hooks/use-async-error-handler";
|
||||
import { getServersFromEvent } from "../../../helpers/media-upload/blossom";
|
||||
|
||||
function serversEqual(a: string, b: string) {
|
||||
return new URL(a).hostname === new URL(b).hostname;
|
||||
}
|
||||
import { USER_MEDIA_SERVERS_KIND, isServerTag, serversEqual } from "../../../helpers/nostr/blossom";
|
||||
|
||||
function MediaServersPage() {
|
||||
const toast = useToast();
|
||||
const account = useCurrentAccount()!;
|
||||
const publish = usePublishEvent();
|
||||
const { mediaUploadService, updateSettings } = useAppSettings();
|
||||
const mediaServers = useUsersMediaServers(account.pubkey, undefined, { alwaysRequest: true, ignoreCache: true });
|
||||
|
||||
const servers = mediaServers ? getServersFromEvent(mediaServers) : [];
|
||||
const { event, servers } = useUsersMediaServers(account.pubkey, undefined, {
|
||||
alwaysRequest: true,
|
||||
ignoreCache: true,
|
||||
});
|
||||
|
||||
const addServer = async (server: string) => {
|
||||
const draft = cloneEvent(10063, mediaServers);
|
||||
draft.tags = [...draft.tags, ["r", server]];
|
||||
const draft = cloneEvent(USER_MEDIA_SERVERS_KIND, event);
|
||||
draft.tags = [
|
||||
...draft.tags.filter((t) => !isServerTag(t)),
|
||||
...servers.map((server) => ["server", server]),
|
||||
["server", server],
|
||||
];
|
||||
await publish("Add media server", draft);
|
||||
};
|
||||
const removeServer = async (server: string) => {
|
||||
const draft = cloneEvent(10063, mediaServers);
|
||||
draft.tags = draft.tags.filter((t) => t[0] === "r" && !serversEqual(t[1], server));
|
||||
const draft = cloneEvent(USER_MEDIA_SERVERS_KIND, event);
|
||||
draft.tags = [
|
||||
...draft.tags.filter((t) => !isServerTag(t)),
|
||||
...servers.filter((s) => !serversEqual(s, server)).map((server) => ["server", server]),
|
||||
];
|
||||
await publish("Remove media server", draft);
|
||||
};
|
||||
const makeDefault = async (server: string) => {
|
||||
const draft = cloneEvent(USER_MEDIA_SERVERS_KIND, event);
|
||||
draft.tags = [
|
||||
...draft.tags.filter((t) => !isServerTag(t)),
|
||||
["server", server],
|
||||
...servers.filter((s) => !serversEqual(s, server)).map((server) => ["server", server]),
|
||||
];
|
||||
await publish("Remove media server", draft);
|
||||
};
|
||||
|
||||
@ -67,12 +80,21 @@ function MediaServersPage() {
|
||||
const { register, handleSubmit, reset } = useForm({ defaultValues: { server: "" } });
|
||||
|
||||
const [confirmServer, setConfirmServer] = useState("");
|
||||
const submit = handleSubmit((values) => {
|
||||
if (mediaServers?.tags.some((t) => t[0] === "r" && serversEqual(t[1], values.server)))
|
||||
const submit = handleSubmit(async (values) => {
|
||||
let url = new URL(values.server.startsWith("http") ? values.server : "https://" + values.server).toString();
|
||||
|
||||
if (event?.tags.some((t) => isServerTag(t) && serversEqual(t[1], url)))
|
||||
return toast({ status: "error", description: "Server already in list" });
|
||||
|
||||
setConfirmServer(new URL(values.server).toString());
|
||||
reset();
|
||||
try {
|
||||
// test server
|
||||
const res = await fetch(url);
|
||||
|
||||
setConfirmServer(url);
|
||||
reset();
|
||||
} catch (error) {
|
||||
toast({ status: "error", description: "Cant reach server" });
|
||||
}
|
||||
});
|
||||
|
||||
const [loading, setLoading] = useState(false);
|
||||
@ -88,7 +110,7 @@ function MediaServersPage() {
|
||||
<Flex gap="2" alignItems="center">
|
||||
<BackButton hideFrom="lg" size="sm" />
|
||||
<Heading size="lg">Media Servers</Heading>
|
||||
{mediaServers && <DebugEventButton event={mediaServers} size="sm" ml="auto" />}
|
||||
{event && <DebugEventButton event={event} size="sm" ml="auto" />}
|
||||
</Flex>
|
||||
<Text fontStyle="italic" mt="-2">
|
||||
<Link href="https://github.com/hzrd149/blossom" target="_blank" color="blue.500">
|
||||
@ -136,14 +158,32 @@ function MediaServersPage() {
|
||||
)}
|
||||
|
||||
<Flex direction="column" gap="2">
|
||||
{servers.map((server) => (
|
||||
<Flex gap="2" p="2" alignItems="center" borderWidth="1px" borderRadius="lg" key={server}>
|
||||
{servers.map((server, i) => (
|
||||
<Flex
|
||||
gap="2"
|
||||
p="2"
|
||||
alignItems="center"
|
||||
borderWidth="1px"
|
||||
borderRadius="lg"
|
||||
key={server}
|
||||
borderColor={i === 0 ? "primary.500" : undefined}
|
||||
>
|
||||
<MediaServerFavicon server={server} size="sm" />
|
||||
<Link href={server} target="_blank" color="blue.500" fontSize="lg">
|
||||
{new URL(server).hostname}
|
||||
</Link>
|
||||
|
||||
<CloseButton ml="auto" onClick={() => removeServer(server)} />
|
||||
<Button
|
||||
ml="auto"
|
||||
variant={i === 0 ? "solid" : "outline"}
|
||||
colorScheme={i === 0 ? "primary" : undefined}
|
||||
size="sm"
|
||||
onClick={() => makeDefault(server)}
|
||||
isDisabled={i === 0}
|
||||
>
|
||||
Default
|
||||
</Button>
|
||||
<CloseButton onClick={() => removeServer(server)} />
|
||||
</Flex>
|
||||
))}
|
||||
</Flex>
|
||||
@ -159,16 +199,16 @@ function MediaServersPage() {
|
||||
</Flex>
|
||||
|
||||
{confirmServer && (
|
||||
<Modal isOpen onClose={() => setConfirmServer("")} size="full">
|
||||
<Modal isOpen onClose={() => setConfirmServer("")} size="6xl">
|
||||
<ModalOverlay />
|
||||
<ModalContent>
|
||||
<ModalHeader>Add media server</ModalHeader>
|
||||
<ModalContent h="calc(100vh - var(--chakra-space-32))">
|
||||
<ModalHeader p="4">Add media server</ModalHeader>
|
||||
<ModalCloseButton />
|
||||
<ModalBody display="flex" p="0" flexDirection="column">
|
||||
<ModalBody p="0" display="flex" flexDirection="column">
|
||||
<Box as="iframe" src={confirmServer} w="full" h="full" flex={1} />
|
||||
</ModalBody>
|
||||
|
||||
<ModalFooter>
|
||||
<ModalFooter p="4">
|
||||
<Button variant="ghost" mr={3} onClick={() => setConfirmServer("")}>
|
||||
Cancel
|
||||
</Button>
|
||||
|
Loading…
x
Reference in New Issue
Block a user