From a90b1ddb5c52f359585f2ce151488eb31f0150f6 Mon Sep 17 00:00:00 2001 From: Kieran Date: Tue, 3 Jan 2023 10:54:18 +0000 Subject: [PATCH] Profile image upload --- src/Const.js | 54 ++++++++++++++++++++++++++++++++++++++ src/element/Note.js | 8 ++---- src/feed/VoidUpload.js | 34 ++++++++++++++++++++++++ src/nostr/Connection.js | 6 ++--- src/nostr/Subscriptions.js | 8 ++++++ src/pages/Layout.js | 2 +- src/pages/Login.js | 3 +-- src/pages/NewUserPage.js | 22 +--------------- src/pages/Notifications.js | 14 +++++----- src/pages/ProfilePage.js | 8 +++++- 10 files changed, 119 insertions(+), 40 deletions(-) create mode 100644 src/Const.js create mode 100644 src/feed/VoidUpload.js diff --git a/src/Const.js b/src/Const.js new file mode 100644 index 00000000..17ac3b6b --- /dev/null +++ b/src/Const.js @@ -0,0 +1,54 @@ + +/** + * Websocket re-connect timeout + */ +export const DefaultConnectTimeout = 1000; + +/** + * List of recommended follows for new users + */ +export const RecommendedFollows = [ + "82341f882b6eabcd2ba7f1ef90aad961cf074af15b9ef44a09f9d2a8fbfbe6a2", // jack + "3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d", // fiatjaf + "020f2d21ae09bf35fcdfb65decf1478b846f5f728ab30c5eaabcd6d081a81c3e", // adam3us + "6e468422dfb74a5738702a8823b9b28168abab8655faacb6853cd0ee15deee93", // gigi + "217e3d8b61c087b10422427e114737a4a4a4b1e15f22301fb4b07e1f33204d7c", // Kieran + "32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245", // jb55 + "e33fe65f1fde44c6dc17eeb38fdad0fceaf1cae8722084332ed1e32496291d42", // wiz + "00000000827ffaa94bfea288c3dfce4422c794fbb96625b6b31e9049f729d700", // cameri + "A341F45FF9758F570A21B000C17D4E53A3A497C8397F26C0E6D61E5ACFFC7A98", // Saylor + "E88A691E98D9987C964521DFF60025F60700378A4879180DCBBB4A5027850411", // NVK + "C4EABAE1BE3CF657BC1855EE05E69DE9F059CB7A059227168B80B89761CBC4E0", // jackmallers + "85080D3BAD70CCDCD7F74C29A44F55BB85CBCD3DD0CBB957DA1D215BDB931204", // preston + "C49D52A573366792B9A6E4851587C28042FB24FA5625C6D67B8C95C8751ACA15", // holdonaut + "83E818DFBECCEA56B0F551576B3FD39A7A50E1D8159343500368FA085CCD964B", // jeffbooth + "3F770D65D3A764A9C5CB503AE123E62EC7598AD035D836E2A810F3877A745B24", // DerekRoss + "472F440F29EF996E92A186B8D320FF180C855903882E59D50DE1B8BD5669301E", // MartyBent + "1577e4599dd10c863498fe3c20bd82aafaf829a595ce83c5cf8ac3463531b09b", // yegorpetrov + "04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9", // ODELL +]; + +/** + * Regex to match email address + */ +export const EmailRegex = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; + +/** + * Generic URL regex + */ +const UrlRegex = /((?:http|ftp|https):\/\/(?:[\w+?\.\w+])+(?:[a-zA-Z0-9\~\!\@\#\$\%\^\&\*\(\)_\-\=\+\\\/\?\.\:\;\'\,]*)?)/i; + +/** + * Extract file extensions regex + */ +const FileExtensionRegex = /\.([\w]+)$/i; + +/** + * Extract note reactions regex + */ +const MentionRegex = /(#\[\d+\])/gi; + +/** + * Simple lightning invoice regex + */ +const InvoiceRegex = /(lnbc\w+)/i; \ No newline at end of file diff --git a/src/element/Note.js b/src/element/Note.js index decb4fa0..8c6a3efc 100644 --- a/src/element/Note.js +++ b/src/element/Note.js @@ -11,11 +11,7 @@ import ProfileImage from "./ProfileImage"; import useEventPublisher from "../feed/EventPublisher"; import { NoteCreator } from "./NoteCreator"; import Invoice from "./Invoice"; - -const UrlRegex = /((?:http|ftp|https):\/\/(?:[\w+?\.\w+])+(?:[a-zA-Z0-9\~\!\@\#\$\%\^\&\*\(\)_\-\=\+\\\/\?\.\:\;\'\,]*)?)/i; -const FileExtensionRegex = /\.([\w]+)$/i; -const MentionRegex = /(#\[\d+\])/gi; -const InvoiceRegex = /(lnbc\w+)/i; +import { UrlRegex, FileExtensionRegex, MentionRegex, InvoiceRegex } from "../Const"; export default function Note(props) { const navigate = useNavigate(); @@ -70,7 +66,7 @@ export default function Note(props) { if (typeof f === "string") { return f.split(InvoiceRegex).map(i => { if (i.toLowerCase().startsWith("lnbc")) { - return + return } else { return i; } diff --git a/src/feed/VoidUpload.js b/src/feed/VoidUpload.js new file mode 100644 index 00000000..d8cc4641 --- /dev/null +++ b/src/feed/VoidUpload.js @@ -0,0 +1,34 @@ +import * as secp from "@noble/secp256k1"; + +/** + * Upload file to void.cat + * https://void.cat/swagger/index.html + * @param {File|Blob} file + * @returns + */ +export default async function VoidUpload(file) { + const buf = await file.arrayBuffer(); + const digest = await crypto.subtle.digest("SHA-256", buf); + + let req = await fetch(`https://void.cat/upload`, { + mode: "cors", + method: "POST", + body: buf, + headers: { + "Content-Type": "application/octet-stream", + "V-Content-Type": file.type, + "V-Filename": file.name, + "V-Full-Digest": secp.utils.bytesToHex(Uint8Array.from(digest)) + } + }); + + if (req.ok) { + let rsp = await req.json(); + if (rsp.ok) { + return rsp; + } else { + throw rsp.errorMessage; + } + } + return null; +} \ No newline at end of file diff --git a/src/nostr/Connection.js b/src/nostr/Connection.js index be85eb2f..27cb9da0 100644 --- a/src/nostr/Connection.js +++ b/src/nostr/Connection.js @@ -1,8 +1,8 @@ -import { Subscriptions } from "./Subscriptions"; -import Event from "./Event"; import * as secp from "@noble/secp256k1"; -const DefaultConnectTimeout = 1000; +import { Subscriptions } from "./Subscriptions"; +import Event from "./Event"; +import { DefaultConnectTimeout } from "../Const"; export default class Connection { constructor(addr, options) { diff --git a/src/nostr/Subscriptions.js b/src/nostr/Subscriptions.js index 06bd0285..8ac07a4d 100644 --- a/src/nostr/Subscriptions.js +++ b/src/nostr/Subscriptions.js @@ -132,4 +132,12 @@ export class Subscriptions { } return ret; } + + /** + * Split subscription by ids + * @param {number} n How many segments to create + */ + Split(n) { + + } } \ No newline at end of file diff --git a/src/pages/Layout.js b/src/pages/Layout.js index 16d88dae..855af8fc 100644 --- a/src/pages/Layout.js +++ b/src/pages/Layout.js @@ -3,13 +3,13 @@ import { useEffect } from "react" import { useDispatch, useSelector } from "react-redux"; import { useNavigate } from "react-router-dom"; import { faBell } from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { System } from ".." import ProfileImage from "../element/ProfileImage"; import { init } from "../state/Login"; import useLoginFeed from "../feed/LoginFeed"; import useUsersCache from "../feed/UsersFeed"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; export default function Layout(props) { const dispatch = useDispatch(); diff --git a/src/pages/Login.js b/src/pages/Login.js index 14318156..806770f3 100644 --- a/src/pages/Login.js +++ b/src/pages/Login.js @@ -5,8 +5,7 @@ import * as secp from '@noble/secp256k1'; import { bech32 } from "bech32"; import { setPrivateKey, setPublicKey } from "../state/Login"; - -const EmailRegex = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; +import { EmailRegex } from "../Const"; export default function LoginPage() { const dispatch = useDispatch(); diff --git a/src/pages/NewUserPage.js b/src/pages/NewUserPage.js index 971751f5..9fd57195 100644 --- a/src/pages/NewUserPage.js +++ b/src/pages/NewUserPage.js @@ -1,26 +1,6 @@ +import { RecommendedFollows } from "../Const"; import ProfilePreview from "../element/ProfilePreview"; -const RecommendedFollows = [ - "82341f882b6eabcd2ba7f1ef90aad961cf074af15b9ef44a09f9d2a8fbfbe6a2", // jack - "3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d", // fiatjaf - "020f2d21ae09bf35fcdfb65decf1478b846f5f728ab30c5eaabcd6d081a81c3e", // adam3us - "6e468422dfb74a5738702a8823b9b28168abab8655faacb6853cd0ee15deee93", // gigi - "217e3d8b61c087b10422427e114737a4a4a4b1e15f22301fb4b07e1f33204d7c", // Kieran - "32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245", // jb55 - "e33fe65f1fde44c6dc17eeb38fdad0fceaf1cae8722084332ed1e32496291d42", // wiz - "00000000827ffaa94bfea288c3dfce4422c794fbb96625b6b31e9049f729d700", // cameri - "A341F45FF9758F570A21B000C17D4E53A3A497C8397F26C0E6D61E5ACFFC7A98", // Saylor - "E88A691E98D9987C964521DFF60025F60700378A4879180DCBBB4A5027850411", // NVK - "C4EABAE1BE3CF657BC1855EE05E69DE9F059CB7A059227168B80B89761CBC4E0", // jackmallers - "85080D3BAD70CCDCD7F74C29A44F55BB85CBCD3DD0CBB957DA1D215BDB931204", // preston - "C49D52A573366792B9A6E4851587C28042FB24FA5625C6D67B8C95C8751ACA15", // holdonaut - "83E818DFBECCEA56B0F551576B3FD39A7A50E1D8159343500368FA085CCD964B", // jeffbooth - "3F770D65D3A764A9C5CB503AE123E62EC7598AD035D836E2A810F3877A745B24", // DerekRoss - "472F440F29EF996E92A186B8D320FF180C855903882E59D50DE1B8BD5669301E", // MartyBent - "1577e4599dd10c863498fe3c20bd82aafaf829a595ce83c5cf8ac3463531b09b", // yegorpetrov - "04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9", // ODELL -]; - export default function NewUserPage(props) { return ( <> diff --git a/src/pages/Notifications.js b/src/pages/Notifications.js index a9974503..adb4221a 100644 --- a/src/pages/Notifications.js +++ b/src/pages/Notifications.js @@ -28,13 +28,15 @@ export default function NotificationsPage() { const subEvents = useMemo(() => { let sub = new Subscriptions(); sub.Id = `reactions:${sub.Id}`; - sub.Kinds.add(EventKind.TextNote); - sub.Ids = new Set(etagged); + sub.Kinds.add(EventKind.Reaction); + sub.ETags = new Set(notifications?.filter(b => b.kind === EventKind.TextNote).map(b => b.id)); - let replyReactions = new Subscriptions(); - replyReactions.Kinds.add(EventKind.Reaction); - replyReactions.ETags = new Set(notifications?.filter(b => b.kind === EventKind.TextNote).map(b => b.id)); - sub.OrSubs.push(replyReactions); + if (etagged.length > 0) { + let reactionsTo = new Subscriptions(); + reactionsTo.Kinds.add(EventKind.TextNote); + reactionsTo.Ids = new Set(etagged); + sub.OrSubs.push(reactionsTo); + } return sub; }, [etagged]); diff --git a/src/pages/ProfilePage.js b/src/pages/ProfilePage.js index 732d8ac6..a87dd556 100644 --- a/src/pages/ProfilePage.js +++ b/src/pages/ProfilePage.js @@ -16,6 +16,7 @@ import QRCodeStyling from "qr-code-styling"; import Modal from "../element/Modal"; import { logout } from "../state/Login"; import FollowButton from "../element/FollowButton"; +import VoidUpload from "../feed/VoidUpload"; export default function ProfilePage() { const dispatch = useDispatch(); @@ -103,7 +104,12 @@ export default function ProfilePage() { async function setNewAvatar() { let file = await openFile(); console.log(file); - + let rsp = await VoidUpload(file); + if (!rsp) { + throw "Upload failed, please try again later"; + } + console.log(rsp); + setPicture(rsp.metadata.url ?? `https://void.cat/d/${rsp.id}`) } function editor() {