Merge branch 'main' into new-ui

This commit is contained in:
Alejandro Gomez 2023-01-29 23:15:22 +01:00
commit 6a1430722d
No known key found for this signature in database
GPG Key ID: 4DF39E566658C817
9 changed files with 112 additions and 28 deletions

View File

@ -24,7 +24,9 @@ export const ProfileCacheExpire = (1_000 * 60 * 5);
* Default bootstrap relays
*/
export const DefaultRelays = new Map<string, RelaySettings>([
["wss://relay.snort.social", { read: true, write: true }]
["wss://relay.snort.social", { read: true, write: true }],
["wss://eden.nostr.land", { read: true, write: true }],
["wss://nostr-pub.semisol.dev", { read: true, write: true }]
]);
/**
@ -114,4 +116,4 @@ export const SoundCloudRegex = /soundcloud\.com\/(?!live)([a-zA-Z0-9]+)\/([a-zA-
* Mixcloud regex
*/
export const MixCloudRegex = /mixcloud\.com\/(?!live)([a-zA-Z0-9]+)\/([a-zA-Z0-9-]+)/
export const MixCloudRegex = /mixcloud\.com\/(?!live)([a-zA-Z0-9]+)\/([a-zA-Z0-9-]+)/

View File

@ -7,11 +7,10 @@ import "./NoteCreator.css";
import Plus from "Icons/Plus";
import useEventPublisher from "Feed/EventPublisher";
import { openFile } from "Util";
import VoidUpload from "Feed/VoidUpload";
import { FileExtensionRegex, VoidCatHost } from "Const";
import Textarea from "Element/Textarea";
import Modal from "Element/Modal";
import { default as NEvent } from "Nostr/Event";
import useFileUpload from "Feed/FileUpload";
export interface NoteCreatorProps {
show: boolean
@ -27,6 +26,7 @@ export function NoteCreator(props: NoteCreatorProps) {
const [note, setNote] = useState<string>();
const [error, setError] = useState<string>();
const [active, setActive] = useState<boolean>(false);
const uploader = useFileUpload();
async function sendNote() {
if (note) {
@ -46,16 +46,11 @@ export function NoteCreator(props: NoteCreatorProps) {
try {
let file = await openFile();
if (file) {
let rx = await VoidUpload(file, file.name);
if (rx?.ok && rx?.file) {
let ext = file.name.match(FileExtensionRegex);
// extension tricks note parser to embed the content
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);
let rx = await uploader.upload(file, file.name);
if (rx.url) {
setNote(n => `${n}\n${rx.url}`);
} else if (rx?.error) {
setError(rx.error);
}
}
} catch (error: any) {

View File

@ -12,6 +12,8 @@ export interface NoteTimeProps {
export default function NoteTime(props: NoteTimeProps) {
const [time, setTime] = useState<string>();
const { from, fallback } = props;
const absoluteTime = new Intl.DateTimeFormat(undefined, { dateStyle: 'medium', timeStyle: 'long'}).format(from);
const isoDate = new Date(from).toISOString();
function calcTime() {
let fromDate = new Date(from);
@ -46,5 +48,5 @@ export default function NoteTime(props: NoteTimeProps) {
return () => clearInterval(t);
}, [from]);
return <>{time}</>
return <time dateTime={isoDate} title={absoluteTime}>{time}</time>
}

30
src/Feed/FileUpload.ts Normal file
View 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;
}
}
}

View 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"
}
}

View File

@ -1,11 +1,12 @@
import * as secp from "@noble/secp256k1";
import { VoidCatHost } from "Const";
import { FileExtensionRegex, VoidCatHost } from "Const";
import { UploadResult } from "./FileUpload";
/**
* Upload file to void.cat
* 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 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) {
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 = {

View File

@ -59,6 +59,18 @@ const PreferencesPage = () => {
<input type="checkbox" checked={perf.autoShowLatest} onChange={e => dispatch(setPreferences({ ...perf, autoShowLatest: e.target.checked }))} />
</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="flex f-col f-grow">
<div>Debug Menus</div>

View File

@ -15,7 +15,7 @@ import { hexToBech32, openFile } from "Util";
import Copy from "Element/Copy";
import { RootState } from "State/Store";
import { HexKey } from "Nostr";
import { VoidCatHost } from "Const";
import useFileUpload from "Feed/FileUpload";
export default function ProfileSettings() {
const navigate = useNavigate();
@ -23,6 +23,7 @@ export default function ProfileSettings() {
const privKey = useSelector<RootState, HexKey | undefined>(s => s.login.privateKey);
const user = useUserProfile(id!);
const publisher = useEventPublisher();
const uploader = useFileUpload();
const [name, setName] = useState<string>();
const [displayName, setDisplayName] = useState<string>();
@ -76,25 +77,25 @@ export default function ProfileSettings() {
let file = await openFile();
if (file) {
console.log(file);
let rsp = await VoidUpload(file, file.name);
if (!rsp?.ok) {
throw "Upload failed, please try again later";
let rsp = await uploader.upload(file, file.name);
if (!rsp?.error) {
throw new Error("Upload failed, please try again later");
}
return rsp.file;
return rsp.url;
}
}
async function setNewAvatar() {
const rsp = await uploadFile();
if (rsp) {
setPicture(rsp.meta?.url ?? `${VoidCatHost}/d/${rsp.id}`);
setPicture(rsp);
}
}
async function setNewBanner() {
const rsp = await uploadFile();
if (rsp) {
setBanner(rsp.meta?.url ?? `${VoidCatHost}/d/${rsp.id}`);
setBanner(rsp);
}
}

View File

@ -48,7 +48,12 @@ export interface UserPreferences {
/**
* 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 {
@ -149,7 +154,8 @@ const InitState = {
theme: "system",
confirmReposts: false,
showDebugMenus: false,
autoShowLatest: false
autoShowLatest: false,
fileUploader: "void.cat"
}
} as LoginStore;