From 471de10ce66f01de52f7394cf4d45fc3137721f8 Mon Sep 17 00:00:00 2001 From: Victor Date: Thu, 15 Aug 2024 14:57:53 -0700 Subject: [PATCH] initial camera capture input progress for testing --- src/website/package.json | 2 + .../entities/EntityInput/EntityInput.scss | 5 - .../entities/EntityInput/EntityInput.tsx | 3 + .../entities/EntityInput/EntityInputEmpty.tsx | 29 ++- .../modals/CameraCapture/CameraCapture.scss | 59 ++++++ .../modals/CameraCapture/CameraCapture.tsx | 139 ++++++++++++ .../components/modals/CameraCapture/index.tsx | 1 + .../src/components/modals/index.tsx | 7 +- .../providers/ModalProvider/ModalLayer.scss | 15 ++ .../providers/ModalProvider/ModalLayer.tsx | 10 +- .../providers/ModalProvider/ModalProvider.tsx | 1 + .../fakeyou.com/src/hooks/useModalState.tsx | 3 +- .../fakeyou.com/src/scss/site-refresh.scss | 5 + .../view/pages/dev_upload/DevMediaInput.tsx | 197 ++++++++++++------ src/website/yarn.lock | 19 ++ 15 files changed, 415 insertions(+), 80 deletions(-) create mode 100644 src/website/packages/fakeyou.com/src/components/modals/CameraCapture/CameraCapture.scss create mode 100644 src/website/packages/fakeyou.com/src/components/modals/CameraCapture/CameraCapture.tsx create mode 100644 src/website/packages/fakeyou.com/src/components/modals/CameraCapture/index.tsx diff --git a/src/website/package.json b/src/website/package.json index 32ce1863e7..c5f197810d 100644 --- a/src/website/package.json +++ b/src/website/package.json @@ -34,10 +34,12 @@ "@babel/runtime": "^7.25.0", "@react-spring/types": "^9.7.3", "@typeform/embed": "^4.8.1", + "@types/dom-mediacapture-record": "^1.0.19", "framer-motion": "6.5.1", "local-storage": "^2.0.0", "moment": "^2.29.4", "react-easy-crop": "^5.0.7", + "react-webcam": "^7.2.0", "universal-cookie": "^6.1.1", "yarn": "^1.22.19" }, diff --git a/src/website/packages/fakeyou.com/src/components/entities/EntityInput/EntityInput.scss b/src/website/packages/fakeyou.com/src/components/entities/EntityInput/EntityInput.scss index 7f5f6839f0..7c39e7fda4 100644 --- a/src/website/packages/fakeyou.com/src/components/entities/EntityInput/EntityInput.scss +++ b/src/website/packages/fakeyou.com/src/components/entities/EntityInput/EntityInput.scss @@ -1,11 +1,6 @@ @import "scss/colours.scss"; @import "scss/site-refresh.scss"; -@mixin video-tint { - background-color: rgba($secondary-very-dark, 0.5); - backdrop-filter: blur(5px); -} - .fy-slide-frame { position: absolute; height: 100%; diff --git a/src/website/packages/fakeyou.com/src/components/entities/EntityInput/EntityInput.tsx b/src/website/packages/fakeyou.com/src/components/entities/EntityInput/EntityInput.tsx index b87b281029..3b30d7d233 100644 --- a/src/website/packages/fakeyou.com/src/components/entities/EntityInput/EntityInput.tsx +++ b/src/website/packages/fakeyou.com/src/components/entities/EntityInput/EntityInput.tsx @@ -41,6 +41,7 @@ interface EntityInputProps { name?: string; onChange?: any; onPromptUpdate?: (prompt: Prompt | null) => void; + showWebcam?: boolean; type: EntityModeProp; value?: string; } @@ -177,6 +178,7 @@ export default function EntityInput({ name = "", onChange, onPromptUpdate, + showWebcam = false, type, value, ...rest @@ -305,6 +307,7 @@ export default function EntityInput({ onSelect, queryUser, render: EntityInputEmpty, + showWebcam, type, ...sharedProps, ...rest, diff --git a/src/website/packages/fakeyou.com/src/components/entities/EntityInput/EntityInputEmpty.tsx b/src/website/packages/fakeyou.com/src/components/entities/EntityInput/EntityInputEmpty.tsx index f667dac857..6199669653 100644 --- a/src/website/packages/fakeyou.com/src/components/entities/EntityInput/EntityInputEmpty.tsx +++ b/src/website/packages/fakeyou.com/src/components/entities/EntityInput/EntityInputEmpty.tsx @@ -5,9 +5,11 @@ import { useModal, useSession, } from "hooks"; -import MediaBrowser, { +import { + CameraCapture, + MediaBrowser, MediaBrowserProps, -} from "components/modals/MediaBrowser"; +} from "components/modals"; import { FileWrapper } from "components/common"; import { AcceptTypes, @@ -20,6 +22,7 @@ import { } from "components/entities/EntityTypes"; import { FontAwesomeIcon as Icon } from "@fortawesome/react-fontawesome"; import { + faCamera, faDiagramSankey, faFile, faFileArrowUp, @@ -33,6 +36,7 @@ interface EmptySlideProps extends SlideProps { accept: AcceptTypes | AcceptTypes[]; inputProps?: any; queryUser?: string; + showWebcam: boolean; type: EntityModeProp; } @@ -40,6 +44,7 @@ export default function EntityInputEmpty({ accept, inputProps, queryUser, + showWebcam, type, ...rest }: EmptySlideProps) { @@ -71,6 +76,13 @@ export default function EntityInputEmpty({ props, }); + const cameraClick = () => + open({ + component: CameraCapture, + padding: false, + width: "square", + }); + const mediaIcons = () => { if (isSelectedType(MediaFilters.audio, accepted[0])) return faWaveform; if (isSelectedType(MediaFilters.engine_asset, accepted[0])) @@ -130,6 +142,19 @@ export default function EntityInputEmpty({ /> Choose from your {["bookmarks", "media", "weights"][inputMode]} + {showWebcam && ( +
+ + Record with your camera +
+ )} ); diff --git a/src/website/packages/fakeyou.com/src/components/modals/CameraCapture/CameraCapture.scss b/src/website/packages/fakeyou.com/src/components/modals/CameraCapture/CameraCapture.scss new file mode 100644 index 0000000000..adcd21e7ab --- /dev/null +++ b/src/website/packages/fakeyou.com/src/components/modals/CameraCapture/CameraCapture.scss @@ -0,0 +1,59 @@ +@import "scss/colours.scss"; +@import "scss/site-refresh.scss"; + +.fy-camera-capture-modal { + width: 100%; + height: 100%; + + position: relative; + + .fy-camera-capture-display { + height: 100%; + margin: auto; + } + + .fy-camera-capture-controls { + @include video-tint; + position: absolute; + bottom: 1rem; + border-radius: 0.5rem; + padding: 0.5rem 0.5rem 0.25rem; + left: 0; + right: 0; + margin: auto; + width: 50%; + display: flex; + justify-content: center; + } + + .fy-camera-capture-record-toggle { + appearance: none; + border: none; + background: none; + display: inline-flex; + justify-content: center; + flex-direction: column; + align-items: center; + color: #fff; + font-size: 0.75rem; + + // background: green; + + svg { + display: block; + // background: rgba(#00f, 0.25); + height: 2rem; + width: 2rem; + margin-bottom: 0.25rem; + + circle { + fill: none; + stroke: $primary; + stroke-width: 2px; + } + rect { + fill: $primary; + } + } + } +} diff --git a/src/website/packages/fakeyou.com/src/components/modals/CameraCapture/CameraCapture.tsx b/src/website/packages/fakeyou.com/src/components/modals/CameraCapture/CameraCapture.tsx new file mode 100644 index 0000000000..e22dd3d3c3 --- /dev/null +++ b/src/website/packages/fakeyou.com/src/components/modals/CameraCapture/CameraCapture.tsx @@ -0,0 +1,139 @@ +import React, { useCallback, useRef, useState } from "react"; +import Webcam from "react-webcam"; +import { a, useSpring } from "@react-spring/web"; +// import { MediaRecorder, IMediaRecorder } from "extendable-media-recorder"; +import "./CameraCapture.scss"; + +import { ModalUtilities } from "components/common"; + +// interface Props { +// value?: any; +// } + +export default function CameraCapture({ handleClose }: ModalUtilities) { + const webcamRef = useRef(null); + const mediaRecorderRef = useRef(null); + const [capturing, capturingSet] = useState(false); + const [recordedChunks, setRecordedChunks] = useState([]); + + const handleDataAvailable = useCallback( + ({ data }) => { + if (data.size > 0) { + setRecordedChunks(prev => prev.concat(data)); + } + }, + [setRecordedChunks] + ); + + const handleDownload = React.useCallback(() => { + if (recordedChunks.length) { + const blob = new Blob(recordedChunks, { + type: "video/webm", + }); + const url = URL.createObjectURL(blob); + const a = document.createElement("a"); + document.body.appendChild(a); + // a.style = "display: none"; + a.href = url; + a.download = "react-webcam-stream-capture.webm"; + a.click(); + window.URL.revokeObjectURL(url); + setRecordedChunks([]); + } + }, [recordedChunks]); + + const mainToggleClick = () => { + if (!capturing) { + capturingSet(true); + + if (webcamRef.current && webcamRef.current.stream) { + console.log("๐Ÿงข", webcamRef.current.stream); + mediaRecorderRef.current = new MediaRecorder(webcamRef.current.stream, { + mimeType: "video/webm", + }); + + mediaRecorderRef.current.addEventListener( + "dataavailable", + handleDataAvailable + ); + mediaRecorderRef.current.start(); + } + } else { + capturingSet(false); + + if (mediaRecorderRef.current !== null) { + mediaRecorderRef.current.stop(); + } + } + }; + + const mainToggleStyle = useSpring({ + rx: capturing ? 1 : 8, + size: capturing ? 14 : 16, + xy: capturing ? 9 : 8, + }); + + return ( +
+ +
+ + {recordedChunks.length > 0 && ( + + )} +
+ {/* {capturing ? ( + + ) : ( + + )} + {recordedChunks.length > 0 && ( + + )}*/} +
+ ); +} diff --git a/src/website/packages/fakeyou.com/src/components/modals/CameraCapture/index.tsx b/src/website/packages/fakeyou.com/src/components/modals/CameraCapture/index.tsx new file mode 100644 index 0000000000..bf9459aede --- /dev/null +++ b/src/website/packages/fakeyou.com/src/components/modals/CameraCapture/index.tsx @@ -0,0 +1 @@ +export { default } from "./CameraCapture"; diff --git a/src/website/packages/fakeyou.com/src/components/modals/index.tsx b/src/website/packages/fakeyou.com/src/components/modals/index.tsx index dc82713592..e786b275df 100644 --- a/src/website/packages/fakeyou.com/src/components/modals/index.tsx +++ b/src/website/packages/fakeyou.com/src/components/modals/index.tsx @@ -1,4 +1,7 @@ +import CameraCapture from "./CameraCapture"; import InferenceJobsModal from "./InferenceJobsModal"; -import MediaBrowser from "./MediaBrowser"; +import MediaBrowser, { MediaBrowserProps } from "./MediaBrowser"; -export { InferenceJobsModal, MediaBrowser }; \ No newline at end of file +export { CameraCapture, InferenceJobsModal, MediaBrowser }; + +export type { MediaBrowserProps }; diff --git a/src/website/packages/fakeyou.com/src/components/providers/ModalProvider/ModalLayer.scss b/src/website/packages/fakeyou.com/src/components/providers/ModalProvider/ModalLayer.scss index 7109ef0be7..9eaf911443 100644 --- a/src/website/packages/fakeyou.com/src/components/providers/ModalProvider/ModalLayer.scss +++ b/src/website/packages/fakeyou.com/src/components/providers/ModalProvider/ModalLayer.scss @@ -37,6 +37,17 @@ .fy-modal-body-small { width: 100%; height: fit-content; + } + + .fy-modal-body-square { + width: 75vh; + height: 75vh; + } + + .fy-modal-body-wide, + .fy-modal-body-narrow, + .fy-modal-body-small, + .fy-modal-body-square { overflow: hidden; background-color: $panel; border-color: lighten($panel, 8%) !important; @@ -54,4 +65,8 @@ border-radius: 0; } } + + .fy-modal-no-padding { + padding: 0; + } } diff --git a/src/website/packages/fakeyou.com/src/components/providers/ModalProvider/ModalLayer.tsx b/src/website/packages/fakeyou.com/src/components/providers/ModalProvider/ModalLayer.tsx index 9e9148551b..48e1c76ed2 100644 --- a/src/website/packages/fakeyou.com/src/components/providers/ModalProvider/ModalLayer.tsx +++ b/src/website/packages/fakeyou.com/src/components/providers/ModalProvider/ModalLayer.tsx @@ -12,6 +12,7 @@ interface Props { lockTint?: boolean; modalOpen: boolean; onModalCloseEnd: (x: any) => void; + padding?: boolean; width?: ModalWidth; } @@ -23,6 +24,7 @@ export default function ModalLayer({ lockTint, modalOpen, onModalCloseEnd, + padding = true, width = "wide", }: Props) { const mainClassName = "fy-modal-layer"; @@ -57,7 +59,13 @@ export default function ModalLayer({ }, }} > -
+
{Content && ( )} diff --git a/src/website/packages/fakeyou.com/src/components/providers/ModalProvider/ModalProvider.tsx b/src/website/packages/fakeyou.com/src/components/providers/ModalProvider/ModalProvider.tsx index 1740be51a5..dabac072d9 100644 --- a/src/website/packages/fakeyou.com/src/components/providers/ModalProvider/ModalProvider.tsx +++ b/src/website/packages/fakeyou.com/src/components/providers/ModalProvider/ModalProvider.tsx @@ -50,6 +50,7 @@ export default function ModalProvider({ children }: ModalProviderProps) { lockTint: modalState?.lockTint, modalOpen, onModalCloseEnd, + padding: modalState?.padding, width: modalState?.width, }} /> diff --git a/src/website/packages/fakeyou.com/src/hooks/useModalState.tsx b/src/website/packages/fakeyou.com/src/hooks/useModalState.tsx index 27f1036b60..59fec86e91 100644 --- a/src/website/packages/fakeyou.com/src/hooks/useModalState.tsx +++ b/src/website/packages/fakeyou.com/src/hooks/useModalState.tsx @@ -1,11 +1,12 @@ import { useEffect, useState } from "react"; import { isMobile } from "react-device-detect"; -export type ModalWidth = "wide" | "narrow" | "small"; +export type ModalWidth = "wide" | "narrow" | "small" | "square"; export interface ModalConfig { component: React.ElementType; lockTint?: boolean; + padding?: boolean; props?: any; width?: ModalWidth; } diff --git a/src/website/packages/fakeyou.com/src/scss/site-refresh.scss b/src/website/packages/fakeyou.com/src/scss/site-refresh.scss index 719bda5553..75a07d3154 100644 --- a/src/website/packages/fakeyou.com/src/scss/site-refresh.scss +++ b/src/website/packages/fakeyou.com/src/scss/site-refresh.scss @@ -58,6 +58,11 @@ styles with important tags: } } +@mixin video-tint { + background-color: rgba($secondary-very-dark, 0.5); + backdrop-filter: blur(5px); +} + .fakeyou-refresh { //- BS5 classnames -// .object-fit-cover { diff --git a/src/website/packages/fakeyou.com/src/v2/view/pages/dev_upload/DevMediaInput.tsx b/src/website/packages/fakeyou.com/src/v2/view/pages/dev_upload/DevMediaInput.tsx index 4815a724a4..0b9124770c 100644 --- a/src/website/packages/fakeyou.com/src/v2/view/pages/dev_upload/DevMediaInput.tsx +++ b/src/website/packages/fakeyou.com/src/v2/view/pages/dev_upload/DevMediaInput.tsx @@ -1,11 +1,25 @@ import React, { useState } from "react"; import { EntityInput } from "components/entities"; -import { EntityFilterOptions, EntityInputMode, EntityModeProp, ListEntityFilters } from "components/entities/EntityTypes"; -import { useModal, useNotifications, - // useSession +import { + EntityFilterOptions, + EntityInputMode, + EntityModeProp, + ListEntityFilters, +} from "components/entities/EntityTypes"; +import { + useModal, + useNotifications, + // useSession } from "hooks"; import { InferenceJobsModal } from "components/modals"; -import { Button, Checkbox, NumberSlider, SegmentButtons, TempInput, TempSelect } from "components/common"; +import { + Button, + Checkbox, + NumberSlider, + SegmentButtons, + TempInput, + TempSelect, +} from "components/common"; import { onChanger } from "resources"; import "./DevEntityInput.scss"; @@ -14,35 +28,37 @@ interface Props { } interface Yank { - [key: string]: any[] + [key: string]: any[]; } export default function DevMediaInput({ value }: Props) { // const { studioAccessCheck } = useSession(); const { create } = useNotifications(); - const [mediaToken,mediaTokenSet] = useState(); - const [notTitle,notTitleSet] = useState("Notification Title"); - const [notContent,notContentSet] = useState("Notification Content"); - const [autoRemove,autoRemoveSet] = useState(true); - const [btnCount,btnCountSet] = useState(0); + const [mediaToken, mediaTokenSet] = useState(); + const [notTitle, notTitleSet] = useState("Notification Title"); + const [notContent, notContentSet] = useState("Notification Content"); + const [autoRemove, autoRemoveSet] = useState(true); + const [btnCount, btnCountSet] = useState(0); const [mode, modeSet] = useState("media"); - const yadda = Object.keys(EntityInputMode).filter(val => isNaN(Number(val))).reduce((obj,modeType,i) => { - return { - [modeType]: ListEntityFilters(i), - ...obj - }; - },{}); + const yadda = Object.keys(EntityInputMode) + .filter(val => isNaN(Number(val))) + .reduce((obj, modeType, i) => { + return { + [modeType]: ListEntityFilters(i), + ...obj, + }; + }, {}); const onChange = onChanger({ autoRemoveSet, btnCountSet, mediaTokenSet, notContentSet, - notTitleSet + notTitleSet, }); - const [filters,filtersSet] = useState(yadda); - const [owner,ownerSet] = useState(""); + const [filters, filtersSet] = useState(yadda); + const [owner, ownerSet] = useState(""); // const onChange = ({ target }: any) => mediaTokenSet(target.value); const { open } = useModal(); @@ -53,61 +69,104 @@ export default function DevMediaInput({ value }: Props) { console.log("โŒ", mode, inputMode, filterOptions, filters[mode]); - const changeFilter = ({ target }: { target: any }) => filtersSet({ ...filters, [mode]: target.value }); + const changeFilter = ({ target }: { target: any }) => + filtersSet({ ...filters, [mode]: target.value }); const openModal = () => open({ component: InferenceJobsModal }); const testBtn = { label: "abc" }; - const actions = Array.from({length: btnCount},() => testBtn); + const actions = Array.from({ length: btnCount }, () => testBtn); - const createNotification = () => create({ - actions, - autoRemove, - title: notTitle, - content: notContent, - onClick: () => console.log("๐Ÿ‰") - }); + const createNotification = () => + create({ + actions, + autoRemove, + title: notTitle, + content: notContent, + onClick: () => console.log("๐Ÿ‰"), + }); - return (
-

{ [526,187].map((num = 0) => String.fromCodePoint(128000 + num)) }

-
-
-
Make-a-notification
-
- - - -
- -
+
+
+
Select Entities
+
+ modeSet(target.value), + options, + value: mode, + }} + /> + ownerSet(target.value), + placeholder: "User", + }} + /> +
+ + +
-
-
Select Entities
-
- modeSet(target.value), options, value: mode }}/> - ownerSet(target.value), placeholder: "User" }}/> -
- - -
-
); -}; \ No newline at end of file + ); +} diff --git a/src/website/yarn.lock b/src/website/yarn.lock index d0007a9692..36c1199a57 100644 --- a/src/website/yarn.lock +++ b/src/website/yarn.lock @@ -4487,6 +4487,7 @@ __metadata: "@babel/runtime": ^7.25.0 "@react-spring/types": ^9.7.3 "@typeform/embed": ^4.8.1 + "@types/dom-mediacapture-record": ^1.0.19 craco: ^0.0.3 cross-env: ^7.0.3 framer-motion: 6.5.1 @@ -4497,6 +4498,7 @@ __metadata: prettier: ^3.1.0 pretty-quick: ^3.1.3 react-easy-crop: ^5.0.7 + react-webcam: ^7.2.0 universal-cookie: ^6.1.1 yarn: ^1.22.19 languageName: unknown @@ -5100,6 +5102,13 @@ __metadata: languageName: node linkType: hard +"@types/dom-mediacapture-record@npm:^1.0.19": + version: 1.0.19 + resolution: "@types/dom-mediacapture-record@npm:1.0.19" + checksum: 4fb7b0bb7bcc6378fa9f0cfce2e144fcc41b5df3c7842bab07f6b812084bd41c18110ed27043ab28b24e2c251233fe4ae5ab0597e6b8509fef9647b29eed2d40 + languageName: node + linkType: hard + "@types/eslint-scope@npm:^3.7.0": version: 3.7.3 resolution: "@types/eslint-scope@npm:3.7.3" @@ -18207,6 +18216,16 @@ __metadata: languageName: node linkType: hard +"react-webcam@npm:^7.2.0": + version: 7.2.0 + resolution: "react-webcam@npm:7.2.0" + peerDependencies: + react: ">=16.2.0" + react-dom: ">=16.2.0" + checksum: 55aecc9e33ea711525f9b68a7c14c9cf4089777b9edd6d9c93813568d701a101f3da09ffd7df04b64eca8c9783a37830522376049589699c6015555ec8d7ccd8 + languageName: node + linkType: hard + "react@npm:^16.13.1": version: 16.14.0 resolution: "react@npm:16.14.0"