From dffe300a5ff84e63f72bc7e11017c6de7655eaa5 Mon Sep 17 00:00:00 2001
From: Ren Amamiya <123083837+reyamir@users.noreply.github.com>
Date: Fri, 2 Jun 2023 16:23:50 +0700
Subject: [PATCH] add create space
---
src/app/space/components/add.tsx | 67 ++++++
src/app/space/components/addFeed.tsx | 179 ++++++++++++++
src/app/space/components/addImage.tsx | 265 +++++++++++++++++++++
src/app/space/components/create.tsx | 216 -----------------
src/app/space/components/imageUploader.tsx | 86 -------
src/app/space/pages/index.page.tsx | 8 +-
src/shared/icons/feed.tsx | 24 ++
src/shared/icons/image.tsx | 21 ++
src/shared/icons/index.tsx | 2 +
9 files changed, 562 insertions(+), 306 deletions(-)
create mode 100644 src/app/space/components/add.tsx
create mode 100644 src/app/space/components/addFeed.tsx
create mode 100644 src/app/space/components/addImage.tsx
delete mode 100644 src/app/space/components/create.tsx
delete mode 100644 src/app/space/components/imageUploader.tsx
create mode 100644 src/shared/icons/feed.tsx
create mode 100644 src/shared/icons/image.tsx
diff --git a/src/app/space/components/add.tsx b/src/app/space/components/add.tsx
new file mode 100644
index 00000000..5d2c4934
--- /dev/null
+++ b/src/app/space/components/add.tsx
@@ -0,0 +1,67 @@
+import { AddFeedBlock } from "@app/space/components/addFeed";
+import { AddImageBlock } from "@app/space/components/addImage";
+import { Menu, Transition } from "@headlessui/react";
+import { FeedIcon, ImageIcon, PlusIcon } from "@shared/icons";
+import { Fragment, useState } from "react";
+
+export function AddBlock() {
+ const [imageModal, setImageModal] = useState(false);
+ const [feedModal, setFeedModal] = useState(false);
+
+ const openAddImageModal = () => {
+ setImageModal(true);
+ };
+
+ const openAddFeedModal = () => {
+ setFeedModal(true);
+ };
+
+ return (
+ <>
+
+
+ {imageModal && }
+ {feedModal && }
+ >
+ );
+}
diff --git a/src/app/space/components/addFeed.tsx b/src/app/space/components/addFeed.tsx
new file mode 100644
index 00000000..2ebefa83
--- /dev/null
+++ b/src/app/space/components/addFeed.tsx
@@ -0,0 +1,179 @@
+import { Dialog, Transition } from "@headlessui/react";
+import { CancelIcon } from "@shared/icons";
+import { useActiveAccount } from "@stores/accounts";
+import { createBlock } from "@utils/storage";
+import { nip19 } from "nostr-tools";
+import { Fragment, useState } from "react";
+import { useForm } from "react-hook-form";
+
+export function AddFeedBlock({ parentState }: { parentState: any }) {
+ const account = useActiveAccount((state: any) => state.account);
+
+ const [loading, setLoading] = useState(false);
+ const [isOpen, setIsOpen] = useState(true);
+
+ const closeModal = () => {
+ // update local state
+ setIsOpen(false);
+ // update parent state
+ parentState(false);
+ };
+
+ const {
+ register,
+ handleSubmit,
+ reset,
+ formState: { isDirty, isValid },
+ } = useForm();
+
+ const onSubmit = (data: any) => {
+ setLoading(true);
+
+ let pubkey = data.content;
+
+ if (pubkey.substring(0, 4) === "npub") {
+ pubkey = nip19.decode(pubkey).data;
+ }
+
+ // insert to database
+ createBlock(account.id, 1, data.title, pubkey);
+
+ setTimeout(() => {
+ setLoading(false);
+ // reset form
+ reset();
+ // close modal
+ closeModal();
+ }, 1000);
+ };
+
+ return (
+
+
+
+ );
+}
diff --git a/src/app/space/components/addImage.tsx b/src/app/space/components/addImage.tsx
new file mode 100644
index 00000000..954cd0ce
--- /dev/null
+++ b/src/app/space/components/addImage.tsx
@@ -0,0 +1,265 @@
+import { Dialog, Transition } from "@headlessui/react";
+import { CancelIcon, ImageIcon } from "@shared/icons";
+import { Image } from "@shared/image";
+import { RelayContext } from "@shared/relayProvider";
+import { useActiveAccount } from "@stores/accounts";
+import { WRITEONLY_RELAYS } from "@stores/constants";
+import { open } from "@tauri-apps/api/dialog";
+import { Body, fetch } from "@tauri-apps/api/http";
+import { createBlobFromFile } from "@utils/createBlobFromFile";
+import { dateToUnix } from "@utils/date";
+import { createBlock } from "@utils/storage";
+import { getEventHash, getSignature } from "nostr-tools";
+import { Fragment, useContext, useEffect, useRef, useState } from "react";
+import { useForm } from "react-hook-form";
+
+export function AddImageBlock({ parentState }: { parentState: any }) {
+ const pool: any = useContext(RelayContext);
+ const account = useActiveAccount((state: any) => state.account);
+
+ const [loading, setLoading] = useState(false);
+ const [isOpen, setIsOpen] = useState(true);
+ const [image, setImage] = useState("");
+
+ const tags = useRef(null);
+
+ const closeModal = () => {
+ // update local state
+ setIsOpen(false);
+ // update parent state
+ parentState(false);
+ };
+
+ const {
+ register,
+ handleSubmit,
+ reset,
+ setValue,
+ formState: { isDirty, isValid },
+ } = useForm();
+
+ const openFileDialog = async () => {
+ const selected: any = await open({
+ multiple: false,
+ filters: [
+ {
+ name: "Image",
+ extensions: ["png", "jpeg", "jpg"],
+ },
+ ],
+ });
+
+ if (Array.isArray(selected)) {
+ // user selected multiple files
+ } else if (selected === null) {
+ // user cancelled the selection
+ } else {
+ const filename = selected.split("/").pop();
+ const file = await createBlobFromFile(selected);
+ const buf = await file.arrayBuffer();
+
+ const res: any = await fetch("https://void.cat/upload?cli=false", {
+ method: "POST",
+ timeout: 5,
+ headers: {
+ accept: "*/*",
+ "Content-Type": "application/octet-stream",
+ "V-Filename": filename,
+ "V-Description": "Upload from https://lume.nu",
+ "V-Strip-Metadata": "true",
+ },
+ body: Body.bytes(buf),
+ });
+
+ if (res.ok) {
+ const imageURL = `https://void.cat/d/${res.data.file.id}.webp`;
+ tags.current = [
+ ["url", imageURL],
+ ["m", res.data.file.metadata.mimeType],
+ ["x", res.data.file.metadata.digest],
+ ["size", res.data.file.metadata.size],
+ ["magnet", res.data.file.metadata.magnetLink],
+ ];
+
+ setImage(imageURL);
+ }
+ }
+ };
+
+ const onSubmit = (data: any) => {
+ setLoading(true);
+
+ const event: any = {
+ content: data.title,
+ created_at: dateToUnix(),
+ kind: 1063,
+ pubkey: account.pubkey,
+ tags: tags.current,
+ };
+
+ console.log(event);
+
+ event.id = getEventHash(event);
+ event.sig = getSignature(event, account.privkey);
+
+ // publish channel
+ pool.publish(event, WRITEONLY_RELAYS);
+
+ // insert to database
+ createBlock(account.id, 0, data.title, data.content);
+
+ setTimeout(() => {
+ setLoading(false);
+ // reset form
+ reset();
+ // close modal
+ closeModal();
+ }, 1000);
+ };
+
+ useEffect(() => {
+ setValue("content", image);
+ }, [setValue, image]);
+
+ return (
+
+
+
+ );
+}
diff --git a/src/app/space/components/create.tsx b/src/app/space/components/create.tsx
deleted file mode 100644
index 8f231781..00000000
--- a/src/app/space/components/create.tsx
+++ /dev/null
@@ -1,216 +0,0 @@
-import { BlockImageUploader } from "./imageUploader";
-import { Dialog, Transition } from "@headlessui/react";
-import { CancelIcon, PlusIcon } from "@shared/icons";
-import { Image } from "@shared/image";
-import { useActiveAccount } from "@stores/accounts";
-import { DEFAULT_AVATAR } from "@stores/constants";
-import { createBlock } from "@utils/storage";
-import { Fragment, useEffect, useState } from "react";
-import { useForm } from "react-hook-form";
-
-export function CreateBlockModal() {
- const account = useActiveAccount((state: any) => state.account);
- const { register, handleSubmit, reset, watch, setValue } = useForm();
-
- const [image, setImage] = useState(DEFAULT_AVATAR);
- const [isOpen, setIsOpen] = useState(false);
- const [loading, setLoading] = useState(false);
-
- const kind = watch("kind");
-
- const closeModal = () => {
- setIsOpen(false);
- };
-
- const openModal = () => {
- setIsOpen(true);
- };
-
- const onSubmit = (data: any) => {
- setLoading(true);
-
- createBlock(account.id, data.kind, data.title, data.content).then(() => {
- // reset form
- reset();
- // close modal
- setIsOpen(false);
- // stop loading
- setLoading(false);
- });
- };
-
- useEffect(() => {
- setValue("content", image);
- }, [setValue, image]);
-
- return (
- <>
-
-
-
-
- >
- );
-}
diff --git a/src/app/space/components/imageUploader.tsx b/src/app/space/components/imageUploader.tsx
deleted file mode 100644
index 28eac283..00000000
--- a/src/app/space/components/imageUploader.tsx
+++ /dev/null
@@ -1,86 +0,0 @@
-import { createBlobFromFile } from "@utils/createBlobFromFile";
-
-import { open } from "@tauri-apps/api/dialog";
-import { Body, fetch } from "@tauri-apps/api/http";
-import { useState } from "react";
-
-export function BlockImageUploader({ valueState }: { valueState: any }) {
- const [loading, setLoading] = useState(false);
-
- const openFileDialog = async () => {
- const selected: any = await open({
- multiple: false,
- filters: [
- {
- name: "Image",
- extensions: ["png", "jpeg", "jpg"],
- },
- ],
- });
- if (Array.isArray(selected)) {
- // user selected multiple files
- } else if (selected === null) {
- // user cancelled the selection
- } else {
- setLoading(true);
-
- const filename = selected.split("/").pop();
- const file = await createBlobFromFile(selected);
- const buf = await file.arrayBuffer();
-
- const res: { data: { file: { id: string } } } = await fetch(
- "https://void.cat/upload?cli=false",
- {
- method: "POST",
- timeout: 5,
- headers: {
- accept: "*/*",
- "Content-Type": "application/octet-stream",
- "V-Filename": filename,
- "V-Description": "Upload from https://lume.nu",
- "V-Strip-Metadata": "true",
- },
- body: Body.bytes(buf),
- },
- );
- const webpImage = `https://void.cat/d/${res.data.file.id}.webp`;
-
- valueState(webpImage);
- setLoading(false);
- }
- };
-
- return (
-
- );
-}
diff --git a/src/app/space/pages/index.page.tsx b/src/app/space/pages/index.page.tsx
index c5b36a28..0408af8f 100644
--- a/src/app/space/pages/index.page.tsx
+++ b/src/app/space/pages/index.page.tsx
@@ -1,7 +1,7 @@
+import { AddBlock } from "@app/space/components/add";
import { FeedBlock } from "@app/space/components/blocks/feed";
import { FollowingBlock } from "@app/space/components/blocks/following";
import { ImageBlock } from "@app/space/components/blocks/image";
-import { CreateBlockModal } from "@app/space/components/create";
import { useActiveAccount } from "@stores/accounts";
import { getBlocks } from "@utils/storage";
import useSWR from "swr";
@@ -16,7 +16,7 @@ export function Page() {
);
return (
-
+
{data
? data.map((block: any) =>
@@ -27,9 +27,9 @@ export function Page() {
),
)
: null}
-
+
diff --git a/src/shared/icons/feed.tsx b/src/shared/icons/feed.tsx
new file mode 100644
index 00000000..728a54d7
--- /dev/null
+++ b/src/shared/icons/feed.tsx
@@ -0,0 +1,24 @@
+import { SVGProps } from "react";
+
+export function FeedIcon(
+ props: JSX.IntrinsicAttributes & SVGProps
,
+) {
+ return (
+
+ );
+}
diff --git a/src/shared/icons/image.tsx b/src/shared/icons/image.tsx
new file mode 100644
index 00000000..35b20b2a
--- /dev/null
+++ b/src/shared/icons/image.tsx
@@ -0,0 +1,21 @@
+import { SVGProps } from "react";
+
+export function ImageIcon(
+ props: JSX.IntrinsicAttributes & SVGProps,
+) {
+ return (
+
+ );
+}
diff --git a/src/shared/icons/index.tsx b/src/shared/icons/index.tsx
index 421a42f2..58f1503f 100644
--- a/src/shared/icons/index.tsx
+++ b/src/shared/icons/index.tsx
@@ -12,8 +12,10 @@ export * from "./edit";
export * from "./enter";
export * from "./eyeOff";
export * from "./eyeOn";
+export * from "./feed";
export * from "./heartbeat";
export * from "./hide";
+export * from "./image";
export * from "./like";
export * from "./lume";
export * from "./media";