forked from Kieran/snort
commit
c9cb30cb51
@ -6,10 +6,9 @@ import "./NoteCreator.css";
|
|||||||
|
|
||||||
import useEventPublisher from "Feed/EventPublisher";
|
import useEventPublisher from "Feed/EventPublisher";
|
||||||
import { openFile } from "Util";
|
import { openFile } from "Util";
|
||||||
import VoidUpload from "Feed/VoidUpload";
|
|
||||||
import { FileExtensionRegex, VoidCatHost } from "Const";
|
|
||||||
import Textarea from "Element/Textarea";
|
import Textarea from "Element/Textarea";
|
||||||
import Event, { default as NEvent } from "Nostr/Event";
|
import { default as NEvent } from "Nostr/Event";
|
||||||
|
import useFileUpload from "Feed/FileUpload";
|
||||||
|
|
||||||
export interface NoteCreatorProps {
|
export interface NoteCreatorProps {
|
||||||
replyTo?: NEvent,
|
replyTo?: NEvent,
|
||||||
@ -23,6 +22,7 @@ export function NoteCreator(props: NoteCreatorProps) {
|
|||||||
const [note, setNote] = useState<string>();
|
const [note, setNote] = useState<string>();
|
||||||
const [error, setError] = useState<string>();
|
const [error, setError] = useState<string>();
|
||||||
const [active, setActive] = useState<boolean>(false);
|
const [active, setActive] = useState<boolean>(false);
|
||||||
|
const uploader = useFileUpload();
|
||||||
|
|
||||||
async function sendNote() {
|
async function sendNote() {
|
||||||
if (note) {
|
if (note) {
|
||||||
@ -41,16 +41,11 @@ export function NoteCreator(props: NoteCreatorProps) {
|
|||||||
try {
|
try {
|
||||||
let file = await openFile();
|
let file = await openFile();
|
||||||
if (file) {
|
if (file) {
|
||||||
let rx = await VoidUpload(file, file.name);
|
let rx = await uploader.upload(file, file.name);
|
||||||
if (rx?.ok && rx?.file) {
|
if (rx.url) {
|
||||||
let ext = file.name.match(FileExtensionRegex);
|
setNote(n => `${n}\n${rx.url}`);
|
||||||
|
} else if (rx?.error) {
|
||||||
// extension tricks note parser to embed the content
|
setError(rx.error);
|
||||||
let url = rx.file.meta?.url ?? `${VoidCatHost}/d/${rx.file.id}${ext ? `.${ext[1]}` : ""}`;
|
|
||||||
|
|
||||||
setNote(n => `${n}\n${url}`);
|
|
||||||
} else if (rx?.errorMessage) {
|
|
||||||
setError(rx.errorMessage);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
|
30
src/Feed/FileUpload.ts
Normal file
30
src/Feed/FileUpload.ts
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import { useSelector } from "react-redux";
|
||||||
|
import { RootState } from "State/Store";
|
||||||
|
import NostrBuildUpload from "./NostrBuildUpload";
|
||||||
|
import VoidUpload from "./VoidUpload";
|
||||||
|
|
||||||
|
export interface UploadResult {
|
||||||
|
url?: string,
|
||||||
|
error?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Uploader {
|
||||||
|
upload: (f: File | Blob, filename: string) => Promise<UploadResult>
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function useFileUpload(): Uploader {
|
||||||
|
const fileUploader = useSelector((s: RootState) => s.login.preferences.fileUploader);
|
||||||
|
|
||||||
|
switch (fileUploader) {
|
||||||
|
case "nostr.build": {
|
||||||
|
return {
|
||||||
|
upload: NostrBuildUpload
|
||||||
|
} as Uploader;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
return {
|
||||||
|
upload: VoidUpload
|
||||||
|
} as Uploader;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
24
src/Feed/NostrBuildUpload.ts
Normal file
24
src/Feed/NostrBuildUpload.ts
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import { UploadResult } from "./FileUpload";
|
||||||
|
|
||||||
|
export default async function NostrBuildUpload(file: File | Blob): Promise<UploadResult> {
|
||||||
|
let fd = new FormData();
|
||||||
|
fd.append("fileToUpload", file);
|
||||||
|
fd.append("submit", "Upload Image");
|
||||||
|
|
||||||
|
let rsp = await fetch("https://nostr.build/api/upload/snort.php", {
|
||||||
|
body: fd,
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"accept": "application/json"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if(rsp.ok) {
|
||||||
|
let data = await rsp.json();
|
||||||
|
return {
|
||||||
|
url: new URL(data).toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
error: "Upload failed"
|
||||||
|
}
|
||||||
|
}
|
@ -1,11 +1,12 @@
|
|||||||
import * as secp from "@noble/secp256k1";
|
import * as secp from "@noble/secp256k1";
|
||||||
import { VoidCatHost } from "Const";
|
import { FileExtensionRegex, VoidCatHost } from "Const";
|
||||||
|
import { UploadResult } from "./FileUpload";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Upload file to void.cat
|
* Upload file to void.cat
|
||||||
* https://void.cat/swagger/index.html
|
* https://void.cat/swagger/index.html
|
||||||
*/
|
*/
|
||||||
export default async function VoidUpload(file: File | Blob, filename: string) {
|
export default async function VoidUpload(file: File | Blob, filename: string): Promise<UploadResult> {
|
||||||
const buf = await file.arrayBuffer();
|
const buf = await file.arrayBuffer();
|
||||||
const digest = await crypto.subtle.digest("SHA-256", buf);
|
const digest = await crypto.subtle.digest("SHA-256", buf);
|
||||||
|
|
||||||
@ -25,9 +26,20 @@ export default async function VoidUpload(file: File | Blob, filename: string) {
|
|||||||
|
|
||||||
if (req.ok) {
|
if (req.ok) {
|
||||||
let rsp: VoidUploadResponse = await req.json();
|
let rsp: VoidUploadResponse = await req.json();
|
||||||
return rsp;
|
if (rsp.ok) {
|
||||||
|
let ext = filename.match(FileExtensionRegex);
|
||||||
|
return {
|
||||||
|
url: rsp.file?.meta?.url ?? `${VoidCatHost}/d/${rsp.file?.id}${ext ? `.${ext[1]}` : ""}`
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
error: rsp.errorMessage
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return {
|
||||||
|
error: "Upload failed"
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export type VoidUploadResponse = {
|
export type VoidUploadResponse = {
|
||||||
|
@ -59,6 +59,18 @@ const PreferencesPage = () => {
|
|||||||
<input type="checkbox" checked={perf.autoShowLatest} onChange={e => dispatch(setPreferences({ ...perf, autoShowLatest: e.target.checked }))} />
|
<input type="checkbox" checked={perf.autoShowLatest} onChange={e => dispatch(setPreferences({ ...perf, autoShowLatest: e.target.checked }))} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="card flex">
|
||||||
|
<div className="flex f-col f-grow">
|
||||||
|
<div>File upload service</div>
|
||||||
|
<small>Pick which upload service you want to upload attachments to</small>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<select value={perf.fileUploader} onChange={e => dispatch(setPreferences({ ...perf, fileUploader: e.target.value} as UserPreferences))}>
|
||||||
|
<option value="void.cat">void.cat (Default)</option>
|
||||||
|
<option value="nostr.build">nostr.build</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div className="card flex">
|
<div className="card flex">
|
||||||
<div className="flex f-col f-grow">
|
<div className="flex f-col f-grow">
|
||||||
<div>Debug Menus</div>
|
<div>Debug Menus</div>
|
||||||
|
@ -9,13 +9,12 @@ import { faShop } from "@fortawesome/free-solid-svg-icons";
|
|||||||
|
|
||||||
import useEventPublisher from "Feed/EventPublisher";
|
import useEventPublisher from "Feed/EventPublisher";
|
||||||
import { useUserProfile } from "Feed/ProfileFeed";
|
import { useUserProfile } from "Feed/ProfileFeed";
|
||||||
import VoidUpload from "Feed/VoidUpload";
|
|
||||||
import { logout } from "State/Login";
|
import { logout } from "State/Login";
|
||||||
import { hexToBech32, openFile } from "Util";
|
import { hexToBech32, openFile } from "Util";
|
||||||
import Copy from "Element/Copy";
|
import Copy from "Element/Copy";
|
||||||
import { RootState } from "State/Store";
|
import { RootState } from "State/Store";
|
||||||
import { HexKey } from "Nostr";
|
import { HexKey } from "Nostr";
|
||||||
import { VoidCatHost } from "Const";
|
import useFileUpload from "Feed/FileUpload";
|
||||||
|
|
||||||
export default function ProfileSettings() {
|
export default function ProfileSettings() {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
@ -24,6 +23,7 @@ export default function ProfileSettings() {
|
|||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const user = useUserProfile(id!);
|
const user = useUserProfile(id!);
|
||||||
const publisher = useEventPublisher();
|
const publisher = useEventPublisher();
|
||||||
|
const uploader = useFileUpload();
|
||||||
|
|
||||||
const [name, setName] = useState<string>();
|
const [name, setName] = useState<string>();
|
||||||
const [displayName, setDisplayName] = useState<string>();
|
const [displayName, setDisplayName] = useState<string>();
|
||||||
@ -77,25 +77,25 @@ export default function ProfileSettings() {
|
|||||||
let file = await openFile();
|
let file = await openFile();
|
||||||
if (file) {
|
if (file) {
|
||||||
console.log(file);
|
console.log(file);
|
||||||
let rsp = await VoidUpload(file, file.name);
|
let rsp = await uploader.upload(file, file.name);
|
||||||
if (!rsp?.ok) {
|
if (!rsp?.error) {
|
||||||
throw "Upload failed, please try again later";
|
throw new Error("Upload failed, please try again later");
|
||||||
}
|
}
|
||||||
return rsp.file;
|
return rsp.url;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function setNewAvatar() {
|
async function setNewAvatar() {
|
||||||
const rsp = await uploadFile();
|
const rsp = await uploadFile();
|
||||||
if (rsp) {
|
if (rsp) {
|
||||||
setPicture(rsp.meta?.url ?? `${VoidCatHost}/d/${rsp.id}`);
|
setPicture(rsp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function setNewBanner() {
|
async function setNewBanner() {
|
||||||
const rsp = await uploadFile();
|
const rsp = await uploadFile();
|
||||||
if (rsp) {
|
if (rsp) {
|
||||||
setBanner(rsp.meta?.url ?? `${VoidCatHost}/d/${rsp.id}`);
|
setBanner(rsp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,7 +40,12 @@ export interface UserPreferences {
|
|||||||
/**
|
/**
|
||||||
* Show debugging menus to help diagnose issues
|
* Show debugging menus to help diagnose issues
|
||||||
*/
|
*/
|
||||||
showDebugMenus: boolean
|
showDebugMenus: boolean,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* File uploading service to upload attachments to
|
||||||
|
*/
|
||||||
|
fileUploader: "void.cat" | "nostr.build"
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface LoginStore {
|
export interface LoginStore {
|
||||||
@ -117,7 +122,8 @@ const InitState = {
|
|||||||
theme: "system",
|
theme: "system",
|
||||||
confirmReposts: false,
|
confirmReposts: false,
|
||||||
showDebugMenus: false,
|
showDebugMenus: false,
|
||||||
autoShowLatest: false
|
autoShowLatest: false,
|
||||||
|
fileUploader: "void.cat"
|
||||||
}
|
}
|
||||||
} as LoginStore;
|
} as LoginStore;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user