From 3606c8669f704e676164af5b21dc20acfe4d20f4 Mon Sep 17 00:00:00 2001 From: Ajay Bura <32841439+ajbura@users.noreply.github.com> Date: Fri, 23 Jun 2023 09:16:25 +0530 Subject: [PATCH 1/8] add two more variant to thumbnail --- src/ui/App.css | 2 ++ src/ui/atoms/thumbnail/Thumbnail.css | 9 +++++++++ src/ui/atoms/thumbnail/Thumbnail.stories.tsx | 6 ++++++ src/ui/atoms/thumbnail/Thumbnail.tsx | 2 +- 4 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/ui/App.css b/src/ui/App.css index 1683f96d4..427363ef0 100644 --- a/src/ui/App.css +++ b/src/ui/App.css @@ -93,6 +93,8 @@ --th-lg: 250px; --th-md: 200px; --th-sm: 150px; + --th-xs: 118px; + --th-xxs: 64px; /* border radius */ --br-lg: 16px; diff --git a/src/ui/atoms/thumbnail/Thumbnail.css b/src/ui/atoms/thumbnail/Thumbnail.css index 8e48e2e9e..3f1f2b708 100644 --- a/src/ui/atoms/thumbnail/Thumbnail.css +++ b/src/ui/atoms/thumbnail/Thumbnail.css @@ -25,6 +25,15 @@ height: var(--th-sm); } +.Thumbnail--xs { + width: var(--th-xs); + height: var(--th-xs); +} + +.Thumbnail--xxs { + width: var(--th-xxs); + height: var(--th-xxs); +} .Thumbnail--outlined::after { content: ""; position: absolute; diff --git a/src/ui/atoms/thumbnail/Thumbnail.stories.tsx b/src/ui/atoms/thumbnail/Thumbnail.stories.tsx index 42f1483be..b9a1da01e 100644 --- a/src/ui/atoms/thumbnail/Thumbnail.stories.tsx +++ b/src/ui/atoms/thumbnail/Thumbnail.stories.tsx @@ -38,6 +38,12 @@ export default function ThumbnailStories() { + + + + + + ); } diff --git a/src/ui/atoms/thumbnail/Thumbnail.tsx b/src/ui/atoms/thumbnail/Thumbnail.tsx index b25b00f04..681c16c92 100644 --- a/src/ui/atoms/thumbnail/Thumbnail.tsx +++ b/src/ui/atoms/thumbnail/Thumbnail.tsx @@ -5,7 +5,7 @@ import "./Thumbnail.css"; interface ThumbnailProps { className?: string; bgColor?: string; - size?: "lg" | "md" | "sm"; + size?: "lg" | "md" | "sm" | "xs" | "xxs"; outlined?: boolean; wide?: boolean; children?: ReactNode; From 13542b237de5829c0c9df18fab1e46c9818e90c5 Mon Sep 17 00:00:00 2001 From: Ajay Bura <32841439+ajbura@users.noreply.github.com> Date: Fri, 23 Jun 2023 09:34:15 +0530 Subject: [PATCH 2/8] refactor pagination dot component --- src/ui/atoms/pagination/PaginationDot.css | 14 +++------ .../pagination/PaginationDot.stories.tsx | 6 ++-- src/ui/atoms/pagination/PaginationDot.tsx | 30 +++++++++---------- .../views/session/world/OnboardingModal.tsx | 9 +++++- 4 files changed, 30 insertions(+), 29 deletions(-) diff --git a/src/ui/atoms/pagination/PaginationDot.css b/src/ui/atoms/pagination/PaginationDot.css index 18a937072..d0aaf00c3 100644 --- a/src/ui/atoms/pagination/PaginationDot.css +++ b/src/ui/atoms/pagination/PaginationDot.css @@ -1,16 +1,10 @@ .PaginationDot { - display: inline-flex; - justify-content: center; - align-items: center; - gap: var(--sp-xxs); -} - -.PaginationDot__item { - width: 8px; - height: 8px; + width: 10px; + height: 10px; border-radius: 50%; background-color: var(--bg-surface-border); + cursor: pointer; } -.PaginationDot__item--active { +.PaginationDot--active { background-color: var(--bg-primary); } diff --git a/src/ui/atoms/pagination/PaginationDot.stories.tsx b/src/ui/atoms/pagination/PaginationDot.stories.tsx index 05359cc41..7fe510535 100644 --- a/src/ui/atoms/pagination/PaginationDot.stories.tsx +++ b/src/ui/atoms/pagination/PaginationDot.stories.tsx @@ -4,8 +4,10 @@ export const title = "PaginationDot"; export default function PaginationDotStories() { return ( -
- +
+ {[0, 1, 2].map((v) => ( + + ))}
); } diff --git a/src/ui/atoms/pagination/PaginationDot.tsx b/src/ui/atoms/pagination/PaginationDot.tsx index 7aa71d6f5..1218ac98f 100644 --- a/src/ui/atoms/pagination/PaginationDot.tsx +++ b/src/ui/atoms/pagination/PaginationDot.tsx @@ -1,22 +1,20 @@ import classNames from "classnames"; +import { ButtonHTMLAttributes, forwardRef } from "react"; import "./PaginationDot.css"; -interface PaginationDotProps { +type PaginationDotProps = ButtonHTMLAttributes & { className?: string; - max: number; - value: number; -} + active?: boolean; +}; -export function PaginationDot({ className, max, value }: PaginationDotProps) { - return ( -
- {Array.from({ length: max }).map((item, index) => ( - - ))} -
- ); -} +export const PaginationDot = forwardRef( + ({ type, className, active, ...props }: PaginationDotProps, ref) => ( + } From cfbfae33ee16ad766eea334a972fda82a850bdcb Mon Sep 17 00:00:00 2001 From: Ajay Bura <32841439+ajbura@users.noreply.github.com> Date: Fri, 23 Jun 2023 13:48:11 +0530 Subject: [PATCH 3/8] add editor action bar component --- src/ui/atoms/thumbnail/Thumbnail.css | 3 +- .../views/session/editor/EditorActionBar.css | 15 +++++++ .../views/session/editor/EditorActionBar.tsx | 39 +++++++++++++++++++ 3 files changed, 56 insertions(+), 1 deletion(-) create mode 100644 src/ui/views/session/editor/EditorActionBar.css create mode 100644 src/ui/views/session/editor/EditorActionBar.tsx diff --git a/src/ui/atoms/thumbnail/Thumbnail.css b/src/ui/atoms/thumbnail/Thumbnail.css index 3f1f2b708..9a2c74ebd 100644 --- a/src/ui/atoms/thumbnail/Thumbnail.css +++ b/src/ui/atoms/thumbnail/Thumbnail.css @@ -5,7 +5,6 @@ background-color: var(--bg-surface-low); border-radius: var(--br-lg); - box-shadow: 0 0 0 1px var(--bg-surface-border); overflow: hidden; position: relative; } @@ -28,11 +27,13 @@ .Thumbnail--xs { width: var(--th-xs); height: var(--th-xs); + border-radius: var(--br-xs); } .Thumbnail--xxs { width: var(--th-xxs); height: var(--th-xxs); + border-radius: var(--br-xxs); } .Thumbnail--outlined::after { content: ""; diff --git a/src/ui/views/session/editor/EditorActionBar.css b/src/ui/views/session/editor/EditorActionBar.css new file mode 100644 index 000000000..f9247f996 --- /dev/null +++ b/src/ui/views/session/editor/EditorActionBar.css @@ -0,0 +1,15 @@ +.EditorActionBar { + padding: var(--sp-xs); + background-color: var(--bg-surface); + box-shadow: var(--bs-surface); + border-radius: var(--br-xs); + overflow: none; +} + +.EditorActionBar__plusBtn { + cursor: pointer; + width: 100%; + height: 100%; + background-color: var(--bg-primary); + position: absolute; +} diff --git a/src/ui/views/session/editor/EditorActionBar.tsx b/src/ui/views/session/editor/EditorActionBar.tsx new file mode 100644 index 000000000..3e739be66 --- /dev/null +++ b/src/ui/views/session/editor/EditorActionBar.tsx @@ -0,0 +1,39 @@ +import { Icon } from "../../../atoms/icon/Icon"; +import { Thumbnail } from "../../../atoms/thumbnail/Thumbnail"; +import AddIC from "../../../../../res/ic/add.svg"; +import "./EditorActionBar.css"; +import { PaginationDot } from "../../../atoms/pagination/PaginationDot"; + +export function EditorActionBar() { + return ( +
+ + + + + + + + + + + + + + + + + + + +
+ + + + +
+
+ ); +} From 80060e1c864ce5a30066b34527bcbcdb61377414 Mon Sep 17 00:00:00 2001 From: Ajay Bura <32841439+ajbura@users.noreply.github.com> Date: Fri, 23 Jun 2023 16:05:08 +0530 Subject: [PATCH 4/8] add editor assets component --- .../views/session/editor/EditorActionBar.css | 3 +- src/ui/views/session/editor/EditorAssets.css | 12 ++++++++ src/ui/views/session/editor/EditorAssets.tsx | 29 +++++++++++++++++++ src/ui/views/session/editor/EditorView.tsx | 10 ++++++- 4 files changed, 52 insertions(+), 2 deletions(-) create mode 100644 src/ui/views/session/editor/EditorAssets.css create mode 100644 src/ui/views/session/editor/EditorAssets.tsx diff --git a/src/ui/views/session/editor/EditorActionBar.css b/src/ui/views/session/editor/EditorActionBar.css index f9247f996..c0856ac9a 100644 --- a/src/ui/views/session/editor/EditorActionBar.css +++ b/src/ui/views/session/editor/EditorActionBar.css @@ -1,9 +1,10 @@ .EditorActionBar { + margin: auto; padding: var(--sp-xs); background-color: var(--bg-surface); box-shadow: var(--bs-surface); border-radius: var(--br-xs); - overflow: none; + overflow: hidden; } .EditorActionBar__plusBtn { diff --git a/src/ui/views/session/editor/EditorAssets.css b/src/ui/views/session/editor/EditorAssets.css new file mode 100644 index 000000000..763096ec3 --- /dev/null +++ b/src/ui/views/session/editor/EditorAssets.css @@ -0,0 +1,12 @@ +.EditorAssets { + background-color: var(--bg-surface); + border-radius: var(--br-xs); + box-shadow: var(--bg-surface); + overflow: hidden; +} + +.EditorAssets__drawer { + max-width: 200px; + width: 100%; + border-right: 1px solid var(--bg-surface-border); +} diff --git a/src/ui/views/session/editor/EditorAssets.tsx b/src/ui/views/session/editor/EditorAssets.tsx new file mode 100644 index 000000000..b118322ae --- /dev/null +++ b/src/ui/views/session/editor/EditorAssets.tsx @@ -0,0 +1,29 @@ +import { Icon } from "../../../atoms/icon/Icon"; +import { EditorHeader } from "../../components/editor-header/EditorHeader"; +import CrossIC from "../../../../../res/ic/cross.svg"; +import WebAssetIC from "../../../../../res/ic/web-asset.svg"; +import { IconButton } from "../../../atoms/button/IconButton"; +import { Text } from "../../../atoms/text/Text"; +import "./EditorAssets.css"; + +interface EditorAssetsProps { + requestClose: () => void; +} + +export function EditorAssets({ requestClose }: EditorAssetsProps) { + return ( +
+ + + + Asset + + + +
+
drawer
+
content
+
+
+ ); +} diff --git a/src/ui/views/session/editor/EditorView.tsx b/src/ui/views/session/editor/EditorView.tsx index ea9c0b591..e6f472a48 100644 --- a/src/ui/views/session/editor/EditorView.tsx +++ b/src/ui/views/session/editor/EditorView.tsx @@ -13,6 +13,8 @@ import { editorAtom, EditorMode, editorModeAtom } from "../../../state/editor"; import { ScriptEditor } from "./ScriptEditor"; import { EditorToolbar } from "./EditorToolbar"; import { useEvent } from "../../../hooks/useEvent"; +import { EditorActionBar } from "./EditorActionBar"; +import { EditorAssets } from "./EditorAssets"; export function EditorView({ room }: { room?: Room }) { const treeViewRef = useRef(null); @@ -76,8 +78,14 @@ export function EditorView({ room }: { room?: Room }) {
-
+
{editorMode === EditorMode.ScriptEditor && room && } + {editorMode === EditorMode.SceneEditor && ( + <> + false} /> + + + )}
{(editorMode === EditorMode.SceneEditor || editorMode === EditorMode.SceneInspector) && (
From 425302943a852945374c6f897471636b39cf0319 Mon Sep 17 00:00:00 2001 From: Ajay Bura <32841439+ajbura@users.noreply.github.com> Date: Wed, 28 Jun 2023 19:22:42 +0530 Subject: [PATCH 5/8] extract auto upload logic in hook --- src/ui/hooks/useAutoUpload.ts | 25 ++++++++++++++++++++++ src/ui/views/components/AutoFileUpload.tsx | 16 +++----------- 2 files changed, 28 insertions(+), 13 deletions(-) create mode 100644 src/ui/hooks/useAutoUpload.ts diff --git a/src/ui/hooks/useAutoUpload.ts b/src/ui/hooks/useAutoUpload.ts new file mode 100644 index 000000000..99be4030c --- /dev/null +++ b/src/ui/hooks/useAutoUpload.ts @@ -0,0 +1,25 @@ +import { IBlobHandle, Session } from "@thirdroom/hydrogen-view-sdk"; +import { useEffect, useState } from "react"; + +import { useThrottle } from "./useThrottle"; +import { useAttachmentUpload } from "./useAttachmentUpload"; + +export const useAutoUpload = (session: Session, blob?: IBlobHandle) => { + const [progress, setProgress] = useState(0); + const throttledSetProgress = useThrottle(setProgress, 16); + const { mxc, error, upload, cancel } = useAttachmentUpload(session.hsApi, throttledSetProgress); + + useEffect(() => { + if (blob) upload(blob); + else { + cancel(); + setProgress(0); + } + }, [blob, upload, cancel]); + + return { + progress, + mxc, + error, + }; +}; diff --git a/src/ui/views/components/AutoFileUpload.tsx b/src/ui/views/components/AutoFileUpload.tsx index 24c13361b..bee4b75cf 100644 --- a/src/ui/views/components/AutoFileUpload.tsx +++ b/src/ui/views/components/AutoFileUpload.tsx @@ -1,11 +1,10 @@ -import { useEffect, useState } from "react"; +import { useEffect } from "react"; import { IBlobHandle } from "@thirdroom/hydrogen-view-sdk"; import { FileUploadCard, FileUploadErrorCard } from "./file-upload-card/FileUploadCard"; -import { useAttachmentUpload } from "../../hooks/useAttachmentUpload"; import { useHydrogen } from "../../hooks/useHydrogen"; import { useFilePicker } from "../../hooks/useFilePicker"; -import { useThrottle } from "../../hooks/useThrottle"; +import { useAutoUpload } from "../../hooks/useAutoUpload"; export interface AutoUploadInfo { mxc?: string; @@ -23,16 +22,7 @@ export function AutoFileUpload({ renderButton, mimeType, onUploadInfo }: AutoFil const { session, platform } = useHydrogen(true); const { fileData, pickFile, dropFile } = useFilePicker(platform, mimeType); - const [progress, setProgress] = useState(0); - const throttledSetProgress = useThrottle(setProgress, 16); - const { mxc, error, upload, cancel } = useAttachmentUpload(session.hsApi, throttledSetProgress); - useEffect(() => { - if (fileData.blob) upload(fileData.blob); - else { - cancel(); - setProgress(0); - } - }, [fileData.blob, upload, cancel]); + const { progress, mxc, error } = useAutoUpload(session, fileData.blob); useEffect(() => { onUploadInfo({ From 3e0ae5c6728ff8ca646252e2e971aeba0cd050b2 Mon Sep 17 00:00:00 2001 From: Ajay Bura <32841439+ajbura@users.noreply.github.com> Date: Wed, 28 Jun 2023 19:23:07 +0530 Subject: [PATCH 6/8] attribution card - WIP --- .../attribution-card/AttributionCard.css | 6 ++++ .../attribution-card/AttributionCard.tsx | 32 +++++++++++++++++++ 2 files changed, 38 insertions(+) create mode 100644 src/ui/views/components/attribution-card/AttributionCard.css create mode 100644 src/ui/views/components/attribution-card/AttributionCard.tsx diff --git a/src/ui/views/components/attribution-card/AttributionCard.css b/src/ui/views/components/attribution-card/AttributionCard.css new file mode 100644 index 000000000..70e526c1a --- /dev/null +++ b/src/ui/views/components/attribution-card/AttributionCard.css @@ -0,0 +1,6 @@ +.AttributionCard { + padding: var(--sp-xs); + background-color: var(--bg-surface); + border: 1px solid var(--bg-surface-border); + border-radius: var(--br-lg); +} diff --git a/src/ui/views/components/attribution-card/AttributionCard.tsx b/src/ui/views/components/attribution-card/AttributionCard.tsx new file mode 100644 index 000000000..c8b32eb5b --- /dev/null +++ b/src/ui/views/components/attribution-card/AttributionCard.tsx @@ -0,0 +1,32 @@ +import { Input } from "../../../atoms/input/Input"; +import { Label } from "../../../atoms/text/Label"; +import { SettingTile } from "../setting-tile/SettingTile"; +import "./AttributionCard.css"; + +export function AttributionCard() { + return ( +
+
+ Title}> + + +
+
+ Author Name}> + + + Author URL}> + + +
+
+ License}> + + + Source URL}> + + +
+
+ ); +} From 26f1668898884b3f142e308a805b6a510b1f7dfc Mon Sep 17 00:00:00 2001 From: Ajay Bura <32841439+ajbura@users.noreply.github.com> Date: Wed, 28 Jun 2023 19:23:34 +0530 Subject: [PATCH 7/8] asset upload modal - WIP --- .../views/session/editor/AssetUploadModal.tsx | 66 +++++++++++++++++++ src/ui/views/session/editor/EditorView.tsx | 2 + 2 files changed, 68 insertions(+) create mode 100644 src/ui/views/session/editor/AssetUploadModal.tsx diff --git a/src/ui/views/session/editor/AssetUploadModal.tsx b/src/ui/views/session/editor/AssetUploadModal.tsx new file mode 100644 index 000000000..d75c9e9ea --- /dev/null +++ b/src/ui/views/session/editor/AssetUploadModal.tsx @@ -0,0 +1,66 @@ +import { Button } from "../../../atoms/button/Button"; +import { Content } from "../../../atoms/content/Content"; +import { Footer } from "../../../atoms/footer/Footer"; +import { Header } from "../../../atoms/header/Header"; +import { HeaderTitle } from "../../../atoms/header/HeaderTitle"; +import { Input } from "../../../atoms/input/Input"; +import { Modal } from "../../../atoms/modal/Modal"; +import { ModalAside } from "../../../atoms/modal/ModalAside"; +import { ModalContent } from "../../../atoms/modal/ModalContent"; +import { Scroll } from "../../../atoms/scroll/Scroll"; +import { Label } from "../../../atoms/text/Label"; +import { Text } from "../../../atoms/text/Text"; +import { AttributionCard } from "../../components/attribution-card/AttributionCard"; +import { ScenePreview } from "../../components/scene-preview/ScenePreview"; +import { SettingTile } from "../../components/setting-tile/SettingTile"; + +export function AssetUploadModal() { + return ( + + + Asset Upload} />} + children={ + +
+
+ Asset Name}> + + + Asset Description}> + + +
+
+ + +
+
+
+ } + bottom={ +
Cancel} right={} /> + } + /> + } + aside={ + + + Uploaded Asset preview will appear here. + + } + /> + + } + /> + + + ); +} diff --git a/src/ui/views/session/editor/EditorView.tsx b/src/ui/views/session/editor/EditorView.tsx index e6f472a48..c98f85a2e 100644 --- a/src/ui/views/session/editor/EditorView.tsx +++ b/src/ui/views/session/editor/EditorView.tsx @@ -15,6 +15,7 @@ import { EditorToolbar } from "./EditorToolbar"; import { useEvent } from "../../../hooks/useEvent"; import { EditorActionBar } from "./EditorActionBar"; import { EditorAssets } from "./EditorAssets"; +import { AssetUploadModal } from "./AssetUploadModal"; export function EditorView({ room }: { room?: Room }) { const treeViewRef = useRef(null); @@ -84,6 +85,7 @@ export function EditorView({ room }: { room?: Room }) { <> false} /> + )}
From d7b5cf62285675dd51c10451f09b3baf97d1fe06 Mon Sep 17 00:00:00 2001 From: Ajay Bura <32841439+ajbura@users.noreply.github.com> Date: Thu, 13 Jul 2023 18:36:35 +0530 Subject: [PATCH 8/8] add asset upload dialog - WIP --- src/ui/utils/common.ts | 69 +++++++++ .../attribution-card/AttributionCard.tsx | 15 +- .../views/session/editor/AssetUploadModal.tsx | 38 ++++- .../views/session/editor/EditorActionBar.css | 2 +- .../views/session/editor/EditorActionBar.tsx | 14 +- src/ui/views/session/editor/EditorAssets.css | 2 +- src/ui/views/session/editor/EditorAssets.tsx | 25 +++- src/ui/views/session/editor/EditorView.tsx | 12 +- src/ui/views/session/editor/assets.ts | 133 ++++++++++++++++++ 9 files changed, 287 insertions(+), 23 deletions(-) create mode 100644 src/ui/views/session/editor/assets.ts diff --git a/src/ui/utils/common.ts b/src/ui/utils/common.ts index 856244817..bbbb7a8c4 100644 --- a/src/ui/utils/common.ts +++ b/src/ui/utils/common.ts @@ -59,6 +59,75 @@ export function loadImageUrl(url: string): Promise { }); } +export function loadImageElement(url: string): Promise { + return new Promise((resolve, reject) => { + const img = document.createElement("img"); + img.onload = () => resolve(img); + img.onerror = (err) => reject(err); + img.src = url; + }); +} + +export function loadVideoElement(url: string): Promise { + return new Promise((resolve, reject) => { + const video = document.createElement("video"); + video.preload = "metadata"; + video.playsInline = true; + video.muted = true; + + video.onloadeddata = () => { + resolve(video); + video.pause(); + }; + video.onerror = (e) => { + reject(e); + }; + + video.src = url; + video.load(); + video.play(); + }); +} + +export function getThumbnailDimensions(width: number, height: number): [number, number] { + const MAX_WIDTH = 800; + const MAX_HEIGHT = 600; + let targetWidth = width; + let targetHeight = height; + if (targetHeight > MAX_HEIGHT) { + targetWidth = Math.floor(targetWidth * (MAX_HEIGHT / targetHeight)); + targetHeight = MAX_HEIGHT; + } + if (targetWidth > MAX_WIDTH) { + targetHeight = Math.floor(targetHeight * (MAX_WIDTH / targetWidth)); + targetWidth = MAX_WIDTH; + } + return [targetWidth, targetHeight]; +} + +export function getThumbnail( + img: HTMLImageElement | SVGImageElement | HTMLVideoElement, + width: number, + height: number, + thumbnailMimeType?: string +): Promise { + return new Promise((resolve) => { + const canvas = document.createElement("canvas"); + canvas.width = width; + canvas.height = height; + const context = canvas.getContext("2d"); + if (!context) { + resolve(undefined); + return; + } + context.drawImage(img, 0, 0, width, height); + + canvas.toBlob((thumbnail) => { + resolve(thumbnail ?? undefined); + }, thumbnailMimeType ?? "image/jpeg"); + }); +} + export function linkifyText(body: string) { const msgParts = []; diff --git a/src/ui/views/components/attribution-card/AttributionCard.tsx b/src/ui/views/components/attribution-card/AttributionCard.tsx index c8b32eb5b..372274c9e 100644 --- a/src/ui/views/components/attribution-card/AttributionCard.tsx +++ b/src/ui/views/components/attribution-card/AttributionCard.tsx @@ -1,11 +1,14 @@ +import { AllHTMLAttributes, forwardRef } from "react"; +import classNames from "classnames"; + import { Input } from "../../../atoms/input/Input"; import { Label } from "../../../atoms/text/Label"; import { SettingTile } from "../setting-tile/SettingTile"; import "./AttributionCard.css"; -export function AttributionCard() { - return ( -
+export const AttributionCard = forwardRef>( + ({ className, ...props }, ref) => ( +
Title}> @@ -27,6 +30,6 @@ export function AttributionCard() {
-
- ); -} + + ) +); diff --git a/src/ui/views/session/editor/AssetUploadModal.tsx b/src/ui/views/session/editor/AssetUploadModal.tsx index d75c9e9ea..2890eec9b 100644 --- a/src/ui/views/session/editor/AssetUploadModal.tsx +++ b/src/ui/views/session/editor/AssetUploadModal.tsx @@ -1,3 +1,5 @@ +import { IBlobHandle } from "@thirdroom/hydrogen-view-sdk"; + import { Button } from "../../../atoms/button/Button"; import { Content } from "../../../atoms/content/Content"; import { Footer } from "../../../atoms/footer/Footer"; @@ -10,11 +12,24 @@ import { ModalContent } from "../../../atoms/modal/ModalContent"; import { Scroll } from "../../../atoms/scroll/Scroll"; import { Label } from "../../../atoms/text/Label"; import { Text } from "../../../atoms/text/Text"; +import { useAutoUpload } from "../../../hooks/useAutoUpload"; +import { useHydrogen } from "../../../hooks/useHydrogen"; import { AttributionCard } from "../../components/attribution-card/AttributionCard"; +import { FileUploadCard, FileUploadErrorCard } from "../../components/file-upload-card/FileUploadCard"; import { ScenePreview } from "../../components/scene-preview/ScenePreview"; import { SettingTile } from "../../components/setting-tile/SettingTile"; +import { Asset, AssetType } from "./assets"; + +interface AssetUploadModalProps { + blob: IBlobHandle; + requestClose: () => void; + onSubmit: (asset: Asset) => void; +} + +export function AssetUploadModal({ blob, requestClose, onSubmit }: AssetUploadModalProps) { + const { session } = useHydrogen(true); + const { progress, error } = useAutoUpload(session, blob); -export function AssetUploadModal() { return ( @@ -25,9 +40,19 @@ export function AssetUploadModal() { children={
+ {error ? ( + + ) : ( + + )}
Asset Name}> - + Asset Description}> @@ -41,7 +66,14 @@ export function AssetUploadModal() { } bottom={ -
Cancel} right={} /> +
+ Cancel + + } + right={} + /> } /> } diff --git a/src/ui/views/session/editor/EditorActionBar.css b/src/ui/views/session/editor/EditorActionBar.css index c0856ac9a..c178e02c2 100644 --- a/src/ui/views/session/editor/EditorActionBar.css +++ b/src/ui/views/session/editor/EditorActionBar.css @@ -1,5 +1,5 @@ .EditorActionBar { - margin: auto; + margin: 0 auto; padding: var(--sp-xs); background-color: var(--bg-surface); box-shadow: var(--bs-surface); diff --git a/src/ui/views/session/editor/EditorActionBar.tsx b/src/ui/views/session/editor/EditorActionBar.tsx index 3e739be66..c1b399308 100644 --- a/src/ui/views/session/editor/EditorActionBar.tsx +++ b/src/ui/views/session/editor/EditorActionBar.tsx @@ -2,18 +2,22 @@ import { Icon } from "../../../atoms/icon/Icon"; import { Thumbnail } from "../../../atoms/thumbnail/Thumbnail"; import AddIC from "../../../../../res/ic/add.svg"; import "./EditorActionBar.css"; -import { PaginationDot } from "../../../atoms/pagination/PaginationDot"; +// import { PaginationDot } from "../../../atoms/pagination/PaginationDot"; -export function EditorActionBar() { +interface EditorActionBarProps { + toggleAssets: () => void; +} + +export function EditorActionBar({ toggleAssets }: EditorActionBarProps) { return (
- - + {/* @@ -33,7 +37,7 @@ export function EditorActionBar() { -
+
*/}
); } diff --git a/src/ui/views/session/editor/EditorAssets.css b/src/ui/views/session/editor/EditorAssets.css index 763096ec3..c4ada2915 100644 --- a/src/ui/views/session/editor/EditorAssets.css +++ b/src/ui/views/session/editor/EditorAssets.css @@ -1,7 +1,7 @@ .EditorAssets { background-color: var(--bg-surface); border-radius: var(--br-xs); - box-shadow: var(--bg-surface); + box-shadow: var(--bs-surface); overflow: hidden; } diff --git a/src/ui/views/session/editor/EditorAssets.tsx b/src/ui/views/session/editor/EditorAssets.tsx index b118322ae..acb32d933 100644 --- a/src/ui/views/session/editor/EditorAssets.tsx +++ b/src/ui/views/session/editor/EditorAssets.tsx @@ -5,14 +5,35 @@ import WebAssetIC from "../../../../../res/ic/web-asset.svg"; import { IconButton } from "../../../atoms/button/IconButton"; import { Text } from "../../../atoms/text/Text"; import "./EditorAssets.css"; +import { AssetUploadModal } from "./AssetUploadModal"; +import { Button } from "../../../atoms/button/Button"; +import { useFilePicker } from "../../../hooks/useFilePicker"; +import { useHydrogen } from "../../../hooks/useHydrogen"; +import { Asset, AssetType } from "./assets"; interface EditorAssetsProps { requestClose: () => void; } export function EditorAssets({ requestClose }: EditorAssetsProps) { + const { platform } = useHydrogen(true); + + const { fileData, pickFile, dropFile } = useFilePicker(platform, "*"); + + const handleAssetUploadClose = () => { + dropFile(); + }; + + const handleAssetSubmit = (asset: Asset) => { + console.log(asset); + // TODO: send stat event + }; + return (
+ {fileData.blob && ( + + )} @@ -22,7 +43,9 @@ export function EditorAssets({ requestClose }: EditorAssetsProps) {
drawer
-
content
+
+ +
); diff --git a/src/ui/views/session/editor/EditorView.tsx b/src/ui/views/session/editor/EditorView.tsx index c98f85a2e..87ef8fc22 100644 --- a/src/ui/views/session/editor/EditorView.tsx +++ b/src/ui/views/session/editor/EditorView.tsx @@ -1,4 +1,4 @@ -import { useEffect, useRef } from "react"; +import { useEffect, useRef, useState } from "react"; import { TreeViewRefApi } from "@thirdroom/manifold-editor-components"; import { useAtomValue } from "jotai"; import { Room } from "@thirdroom/hydrogen-view-sdk"; @@ -15,7 +15,6 @@ import { EditorToolbar } from "./EditorToolbar"; import { useEvent } from "../../../hooks/useEvent"; import { EditorActionBar } from "./EditorActionBar"; import { EditorAssets } from "./EditorAssets"; -import { AssetUploadModal } from "./AssetUploadModal"; export function EditorView({ room }: { room?: Room }) { const treeViewRef = useRef(null); @@ -25,6 +24,8 @@ export function EditorView({ room }: { room?: Room }) { const resource = getLocalResource(mainThread, activeEntity) as unknown as MainThreadResource; const editorMode = useAtomValue(editorModeAtom); + const [assets, setAssets] = useState(false); + useEffect(() => { document.exitPointerLock(); return () => { @@ -79,13 +80,12 @@ export function EditorView({ room }: { room?: Room }) {
-
+
{editorMode === EditorMode.ScriptEditor && room && } {editorMode === EditorMode.SceneEditor && ( <> - false} /> - - + {assets && setAssets(false)} />} + setAssets(!assets)} /> )}
diff --git a/src/ui/views/session/editor/assets.ts b/src/ui/views/session/editor/assets.ts new file mode 100644 index 000000000..59c4f378b --- /dev/null +++ b/src/ui/views/session/editor/assets.ts @@ -0,0 +1,133 @@ +export enum AssetType { + Image = "m.image", + Audio = "m.audio", + Video = "m.video", + Script = "org.matrix.msc4032.world.script", + Model = "org.matrix.msc4032.world.model", + Avatar = "org.matrix.msc4032.world.avatar", + Scene = "org.matrix.msc4032.world.scene", + Object = "org.matrix.msc4032.world.object", + Prefab = "org.matrix.msc4032.world.prefab", +} + +export type ThumbnailInfo = { + w?: number; + h?: number; + mimetype?: string; + size?: number; +}; + +export type ScriptInfo = { + mimetype?: string; + size?: number; +}; + +export type Attribution = { + author_name: string; + author_url?: string; + title?: string; + source_url?: string; + license?: string; +}; + +export type ObjectInfo = { + vertices?: number; + textures?: number; + materials?: number; + animations?: boolean; + audio?: boolean; + boundingBox?: { + min?: number[]; + max?: number[]; + }; +}; + +export type BaseAssetInfo = { + mimetype?: string; + size?: number; +}; + +export type ImageAssetInfo = BaseAssetInfo & { + w?: number; + h?: number; + thumbnail_url?: string; + thumbnail_info?: ThumbnailInfo; +}; + +export type AudioAssetInfo = BaseAssetInfo & { + duration?: number; +}; + +export type VideoAssetInfo = BaseAssetInfo & { + duration?: number; + w?: number; + h?: number; + thumbnail_url?: string; + thumbnail_info?: ThumbnailInfo; +}; + +export type ScriptAssetInfo = BaseAssetInfo; + +export type ModelAssetInfo = BaseAssetInfo & + ObjectInfo & { + thumbnail_url?: string; + thumbnail_info?: ThumbnailInfo; + }; + +export type AvatarAssetInfo = BaseAssetInfo & + ObjectInfo & { + thumbnail_url?: string; + thumbnail_info?: ThumbnailInfo; + script_info?: ScriptInfo; + script_url?: string; + }; + +export type SceneAssetInfo = BaseAssetInfo & + ObjectInfo & { + thumbnail_url?: string; + thumbnail_info?: ThumbnailInfo; + script_info?: ScriptInfo; + script_url?: string; + }; + +export type ObjectAssetInfo = BaseAssetInfo & + ObjectInfo & { + thumbnail_url?: string; + thumbnail_info?: ThumbnailInfo; + script_info?: ScriptInfo; + script_url?: string; + }; + +export type PrefabAssetInfo = { + thumbnail_url?: string; + thumbnail_info?: ThumbnailInfo; +}; + +export type AssetInfo = T extends AssetType.Image + ? ImageAssetInfo + : T extends AssetType.Audio + ? AudioAssetInfo + : T extends AssetType.Video + ? VideoAssetInfo + : T extends AssetType.Script + ? ScriptAssetInfo + : T extends AssetType.Model + ? ModelAssetInfo + : T extends AssetType.Avatar + ? AvatarAssetInfo + : T extends AssetType.Scene + ? SceneAssetInfo + : T extends AssetType.Object + ? ObjectAssetInfo + : T extends AssetType.Prefab + ? PrefabAssetInfo + : never; + +export type Asset = { + title: string; + description?: string; + url: string; + asset_type: T; + attribution?: Attribution[]; + info?: AssetInfo; +};