replace void.cat with nostr.build

This commit is contained in:
Ren Amamiya 2023-09-05 12:46:00 +07:00
parent 4019623d99
commit 2389ad5fdc
8 changed files with 120 additions and 44 deletions

20
src-tauri/Cargo.lock generated
View File

@ -2777,6 +2777,16 @@ version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
[[package]]
name = "mime_guess"
version = "2.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef"
dependencies = [
"mime",
"unicase",
]
[[package]]
name = "minimal-lexical"
version = "0.2.1"
@ -3828,6 +3838,7 @@ dependencies = [
"js-sys",
"log",
"mime",
"mime_guess",
"native-tls",
"once_cell",
"percent-encoding",
@ -5543,6 +5554,15 @@ dependencies = [
"winapi",
]
[[package]]
name = "unicase"
version = "2.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89"
dependencies = [
"version_check",
]
[[package]]
name = "unicode-bidi"
version = "0.3.13"

View File

@ -35,6 +35,7 @@ tauri = { version = "1.4.0", features = [
"window-center",
"dialog-all",
"macos-private-api",
"http-multipart",
] }
tauri-plugin-sql = { git = "hhttps://github.com/tauri-apps/plugins-workspace", branch = "v1", features = [
"sqlite",

View File

@ -7,8 +7,8 @@ import { convert } from 'html-to-text';
import { useState } from 'react';
import { twMerge } from 'tailwind-merge';
import { MentionPopup } from '@shared/composer';
import { CancelIcon, LoaderIcon, MediaIcon } from '@shared/icons';
import { MediaUploader, MentionPopup } from '@shared/composer';
import { CancelIcon, LoaderIcon } from '@shared/icons';
import { MentionNote } from '@shared/notes';
import { useComposer } from '@stores/composer';
@ -20,7 +20,7 @@ export function Composer() {
const [loading, setLoading] = useState<boolean>(false);
const [reply, clearReply] = useComposer((state) => [state.reply, state.clearReply]);
const { publish, upload } = useNostr();
const { publish } = useNostr();
const expand = useComposer((state) => state.expand);
const editor = useEditor({
@ -46,14 +46,6 @@ export function Composer() {
},
});
const uploadImage = async (file?: string) => {
const image = await upload(file, true);
if (image.url) {
editor.commands.setImage({ src: image.url });
editor.commands.createParagraphNear();
}
};
const submit = async () => {
try {
setLoading(true);
@ -148,14 +140,7 @@ export function Composer() {
</div>
<div className="flex items-center justify-between rounded-b-xl border-t border-white/10 bg-white/5 p-2">
<div className="inline-flex items-center gap-1">
<button
type="button"
onClick={() => uploadImage()}
className="ml-2 inline-flex h-10 w-max items-center justify-center gap-1.5 rounded-lg px-2 text-sm font-medium text-white/80 hover:bg-white/10 hover:backdrop-blur-xl"
>
<MediaIcon className="h-5 w-5 text-white/80" />
Add media
</button>
<MediaUploader editor={editor} />
<MentionPopup editor={editor} />
</div>
<button

View File

@ -3,3 +3,4 @@ export * from './modal';
export * from './composer';
export * from './mention/item';
export * from './mention/popup';
export * from './mediaUploader';

View File

@ -0,0 +1,43 @@
import { message } from '@tauri-apps/api/dialog';
import { Editor } from '@tiptap/react';
import { useState } from 'react';
import { MediaIcon } from '@shared/icons';
import { useNostr } from '@utils/hooks/useNostr';
export function MediaUploader({ editor }: { editor: Editor }) {
const { upload } = useNostr();
const [loading, setLoading] = useState(false);
const uploadImage = async (file?: string) => {
try {
// start loading
setLoading(true);
const image = await upload(file, true);
if (image.url) {
editor.commands.setImage({ src: image.url });
editor.commands.createParagraphNear();
// stop loading
setLoading(false);
}
} catch (e) {
// stop loading
setLoading(false);
await message('Upload failed', { title: 'Lume', type: 'error' });
}
};
return (
<button
type="button"
onClick={() => uploadImage()}
className="ml-2 inline-flex h-10 w-max items-center justify-center gap-1.5 rounded-lg px-2 text-sm font-medium text-white/80 hover:bg-white/10 hover:backdrop-blur-xl"
>
<MediaIcon className="h-5 w-5 text-white/80" />
{loading ? 'Uploading...' : 'Add media'}
</button>
);
}

View File

@ -1,6 +1,8 @@
import { readBinaryFile } from '@tauri-apps/api/fs';
export async function createBlobFromFile(path: string): Promise<Blob> {
export async function createBlobFromFile(path: string): Promise<Uint8Array> {
const file = await readBinaryFile(path);
return new Blob([file]);
const blob = new Blob([file]);
const arr = new Uint8Array(await blob.arrayBuffer());
return arr;
}

View File

@ -1,4 +1,3 @@
import { magnetDecode } from '@ctrl/magnet-link';
import {
NDKEvent,
NDKFilter,
@ -9,7 +8,7 @@ import {
} from '@nostr-dev-kit/ndk';
import { ndkAdapter } from '@nostr-fetch/adapter-ndk';
import { message, open } from '@tauri-apps/api/dialog';
import { VoidApi } from '@void-cat/api';
import { Body, fetch } from '@tauri-apps/api/http';
import { LRUCache } from 'lru-cache';
import { NostrFetcher } from 'nostr-fetch';
import { nip19 } from 'nostr-tools';
@ -22,7 +21,7 @@ import { useStronghold } from '@stores/stronghold';
import { createBlobFromFile } from '@utils/createBlobFromFile';
import { nHoursAgo } from '@utils/date';
import { NDKEventWithReplies } from '@utils/types';
import { NDKEventWithReplies, NostrBuildResponse } from '@utils/types';
export function useNostr() {
const { ndk, relayUrls } = useNDK();
@ -330,9 +329,8 @@ export function useNostr() {
const upload = async (file: null | string, nip94?: boolean) => {
try {
const voidcat = new VoidApi('https://void.cat');
let filepath = file;
if (!file) {
const selected = await open({
multiple: false,
@ -369,32 +367,37 @@ export function useNostr() {
const filename = filepath.split('/').pop();
const filetype = filename.split('.').pop();
const blob = await createBlobFromFile(filepath);
const uploader = voidcat.getUploader(blob);
// upload file
const res = await uploader.upload();
const fileData = await createBlobFromFile(filepath);
const res: NostrBuildResponse = await fetch(
'https://nostr.build/api/v2/upload/files',
{
method: 'POST',
timeout: 30,
headers: { 'Content-Type': 'multipart/form-data' },
body: Body.form({
fileData: {
file: fileData,
mime: `image/${filetype}`,
fileName: filename,
},
}),
}
);
if (res.ok) {
const url =
res.file?.metadata?.url ?? `https://void.cat/d/${res.file?.id}.${filetype}`;
const data = res.data.data[0];
const url = data.url;
if (nip94) {
const tags = [
['url', url],
['x', res.file?.metadata?.digest ?? ''],
['m', res.file?.metadata?.mimeType ?? 'application/octet-stream'],
['size', res.file?.metadata?.size.toString() ?? '0'],
['x', data.sha256 ?? ''],
['m', data.mime ?? 'application/octet-stream'],
['size', data.size.toString() ?? '0'],
['dim', `${data.dimensions.width}x${data.dimensions.height}` ?? '0'],
['blurhash', data.blurhash ?? ''],
];
if (res.file?.metadata?.magnetLink) {
tags.push(['magnet', res.file.metadata.magnetLink]);
const parsedMagnet = magnetDecode(res.file.metadata.magnetLink);
if (parsedMagnet?.infoHash) {
tags.push(['i', parsedMagnet?.infoHash]);
}
}
await publish({ content: '', kind: 1063, tags: tags });
}

21
src/utils/types.d.ts vendored
View File

@ -1,4 +1,5 @@
import { NDKEvent, NDKUserProfile } from '@nostr-dev-kit/ndk';
import { Response } from '@tauri-apps/api/http';
export interface RichContent {
parsed: string;
@ -88,3 +89,23 @@ export interface Opengraph {
export interface NDKEventWithReplies extends NDKEvent {
replies: Array<NDKEvent>;
}
export interface NostrBuildResponse extends Response {
ok: boolean;
data: {
message: string;
status: string;
data: Array<{
blurhash: string;
dimensions: {
width: number;
height: number;
};
mime: string;
name: string;
sha256: string;
size: number;
url: string;
}>;
};
}