diff --git a/packages/fakeyou.com/src/components/common/Card/Card.scss b/packages/fakeyou.com/src/components/common/Card/Card.scss index fbc93b28eb..44b0947811 100644 --- a/packages/fakeyou.com/src/components/common/Card/Card.scss +++ b/packages/fakeyou.com/src/components/common/Card/Card.scss @@ -87,6 +87,17 @@ &:hover .card-video-play { color: rgba($white, 0.7); } + + &.card-hover-border-red { + &:hover { + border: 1px solid $primary; + } + } + + &.bottom-text-hover { + background-color: lighten($panel-inner, 2%); + border: 1px solid $primary; + } } h6 { @@ -97,3 +108,14 @@ overflow: hidden; } } + +.card-bottom-text { + padding-top: 0.75rem; + cursor: pointer; +} + +.centered-row { + display: flex; + justify-content: center; + flex-wrap: wrap; +} diff --git a/packages/fakeyou.com/src/components/common/Card/Card.tsx b/packages/fakeyou.com/src/components/common/Card/Card.tsx index 80bf4b667e..fdbd2bc034 100644 --- a/packages/fakeyou.com/src/components/common/Card/Card.tsx +++ b/packages/fakeyou.com/src/components/common/Card/Card.tsx @@ -1,4 +1,4 @@ -import React from "react"; +import React, { useState } from "react"; import "./Card.scss"; interface CardProps { @@ -8,6 +8,12 @@ interface CardProps { canHover?: boolean; onMouseEnter?: () => void; onMouseLeave?: () => void; + backgroundImage?: string; + height?: string; + borderWidth?: string; + hoverPrimaryColor?: true; + aspectRatio?: string; + bottomText?: string; } export default function Card({ @@ -17,17 +23,48 @@ export default function Card({ canHover, onMouseEnter, onMouseLeave, + backgroundImage, + height, + borderWidth, + hoverPrimaryColor, + aspectRatio = "auto", + bottomText, }: CardProps) { + const [textHovered, setTextHovered] = useState(false); + return ( -
- {children} -
+ <> +
+ {children} +
+ {bottomText && ( +
setTextHovered(true)} + onMouseLeave={() => setTextHovered(false)} + > + {bottomText} +
+ )} + ); } diff --git a/packages/fakeyou.com/src/components/common/VideoFakeyou/VideoFakeyou.tsx b/packages/fakeyou.com/src/components/common/VideoFakeyou/VideoFakeyou.tsx index 0ff353666f..14a3489875 100644 --- a/packages/fakeyou.com/src/components/common/VideoFakeyou/VideoFakeyou.tsx +++ b/packages/fakeyou.com/src/components/common/VideoFakeyou/VideoFakeyou.tsx @@ -14,6 +14,7 @@ import './styles.scss'; export interface VideoFakeyouProps{ + debug?: boolean; width?: number|string; height?: number|string; hideIfNoMedia?: boolean; @@ -30,6 +31,7 @@ export interface VideoFakeyouProps{ type Ref = HTMLVideoElement; const VideoFakeyou = memo(forwardRef(({ + debug: propsDebug = false, width, height, hideIfNoMedia = false, @@ -44,11 +46,15 @@ const VideoFakeyou = memo(forwardRef(({ ...rest }: VideoFakeyouProps, ref) => { //console.log(`Video Player rerender: ${mediaToken}`); + const debug = false;// || propsDebug; + + if(debug) console.log("VideoFakeyou reRENDER!!"); const [mediaFile, setMediaFile] = useState(null); useEffect(()=>{ setMediaFile(null); },[mediaToken]) + useMedia({ mediaToken: mediaToken, onSuccess: (res: any) => { diff --git a/packages/fakeyou.com/src/components/common/VideoQuickTrim/TrimScrubber.tsx b/packages/fakeyou.com/src/components/common/VideoQuickTrim/TrimScrubber.tsx deleted file mode 100644 index 3b30dad77d..0000000000 --- a/packages/fakeyou.com/src/components/common/VideoQuickTrim/TrimScrubber.tsx +++ /dev/null @@ -1,101 +0,0 @@ -import React, { - useRef, - useState, - PointerEvent -} from 'react'; -import { - faGripDots -} from "@fortawesome/pro-solid-svg-icons"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { QuickTrimData } from './types'; - -export default function TrimScrubber({ - trimStart, - trimDuration, - duration, - width, - boundingWidth, - onChange -}:{ - trimStart:number; - trimDuration: number; - duration: number; - boundingWidth: number; - width:number; - onChange:(val:QuickTrimData)=>void -}){ - const [{ - left, - leftOffset, - pointerStart - }, setState] = useState<{ - left: number, - leftOffset:number - pointerStart:number - }>({ - left: trimStart/duration*boundingWidth, - leftOffset: trimStart/duration*boundingWidth, - pointerStart: -1 - }); - const ref = useRef(null); - - if(ref.current){ - ref.current.style.cursor = pointerStart >= 0 - ? 'grabbing' : 'grab'; - } - - function handleScrubStart(e: PointerEvent){ - e.persist(); - // console.log(`start: ${pointerStart}` ); - setState((curr)=>({ - ...curr, - pointerStart: e.clientX - })); - } - function handleScrubEnd(e: PointerEvent){ - e.persist(); - // console.log(`end` ); - setState((curr)=>({ - ...curr, - pointerStart: -1, - leftOffset: left, - })); - //console.log(`${duration} * ${left} / ${boundingWidth} = ${duration * left / boundingWidth}`) - onChange(({ - trimStartSeconds: duration * left / boundingWidth, - trimEndSeconds: duration * left / boundingWidth + trimDuration, - })) - } - function handleScrubMove(e: PointerEvent){ - e.persist(); - if(pointerStart >= 0 && pointerStart!==null && ref.current){ - // console.log(`${e.clientX} - ${pointerStart} = ${e.clientX-pointerStart}`) - let newLeft = leftOffset + e.clientX - pointerStart; - // console.log(`${newLeft} + ${boundingWidth * widthPercent/100} > ${boundingWidth}`) - if (newLeft + width > boundingWidth) { - newLeft = boundingWidth - width; - }else if(newLeft < 0) { - newLeft = 0; - } - setState((curr)=>({ - ...curr, - left: newLeft - })); - } - } - return( -
- -
- ); -} diff --git a/packages/fakeyou.com/src/components/common/VideoQuickTrim/VideoQuickTrim.tsx b/packages/fakeyou.com/src/components/common/VideoQuickTrim/VideoQuickTrim.tsx index 55503dbbcd..d87185d6d0 100644 --- a/packages/fakeyou.com/src/components/common/VideoQuickTrim/VideoQuickTrim.tsx +++ b/packages/fakeyou.com/src/components/common/VideoQuickTrim/VideoQuickTrim.tsx @@ -1,296 +1,231 @@ import React, { memo, useCallback, - useLayoutEffect, - useState, + useEffect, + useReducer, useRef, } from "react"; import { - faArrowsRepeat, faPlay, faPause, - faVolume, - faVolumeSlash, } from "@fortawesome/pro-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { Button, SelectionBubbles, VideoFakeyou } from "components/common"; + +import { useLocalize } from "hooks"; +import { VideoFakeyou } from "components/common"; import { VideoFakeyouProps } from "../VideoFakeyou/VideoFakeyou"; -import { QuickTrimData, TrimStates, PlaybarStates } from "./types"; -import { formatSecondsToHHMMSSCS } from "./helpers"; -import TrimScrbber from "./TrimScrubber"; +import { + reducer, + initialState, + ACTION_TYPES, + PLAYPUASE_STATES, + STATE_STATUSES, +} from "./reducer"; + +import { QuickTrimData, ONE_MS } from "./utilities"; + +import { ProgressBar } from "./components/ProgressBar"; +import { ControlBar } from "./components/ControlBar"; import './styles.scss' interface VideoQuickTrimProps extends VideoFakeyouProps { + debug?: boolean onSelect: (values: QuickTrimData) => void; + trimStartSeconds: number; + trimEndSeconds: number; } -const trimOptions: { [key: string]: number } = { - "3s": 3, - "5s": 5, - "10s": 10, - "15s": 15, -}; - -const initialTrimState: TrimStates = { - canNotTrim: true, - trimDuration: 0, - trimReset: new Date(), - trimStart: 0, - trimEnd: 0, - maxDuration: 0, -}; - -const initialPlaybarState: PlaybarStates = { - playbarWidth: 0, - timeCursorOffset: 0, -}; - -export default memo(function VideoQuickTrim({ +export const VideoQuickTrim = memo(({ + debug: propsDebug = false, onSelect, + trimStartSeconds: propsTrimStartSeconds, + trimEndSeconds : propsTrimEndSeconds, ...rest -}: VideoQuickTrimProps) { - // console.log("VideoQuickTrim Rerender!!"); +}: VideoQuickTrimProps) => { + const debug = true || propsDebug; + const { t } = useLocalize("VideoPlayerQuickTrim"); const videoRef = useRef(null); - const playbarRef = useRef(null); - - const [{ - playbarWidth, - timeCursorOffset - }, setPlaybarState] = useState(initialPlaybarState); - const [playpause, setPlaypause] = useState<'playing'|'paused'|'ended'>('paused'); - const [isMuted, setIsMuted] = useState(false); - const [isRepeatOn, setIsRepatOn] = useState(true); - const [{ - canNotTrim, - trimReset, - trimStart, - trimEnd, - trimDuration, - maxDuration - }, setTrimState] = useState(initialTrimState); + const [compState, dispatchCompState] = useReducer(reducer, initialState); - + if(compState.status === STATE_STATUSES.LOAD_ORDER_ERROR){ + console.log(`${compState.errorMessage[compState.errorMessage.length -1]}`); + } const videoRefCallback = useCallback(node => { - //console.log("videoRefCallback"); + function setPlaypause(newState: PLAYPUASE_STATES){ + dispatchCompState({ + type: ACTION_TYPES.SET_PLAYPUASE, + payload: { playpause: newState} + }) + } if (node !== null) { // DOM node referenced by ref has changed and exists videoRef.current = node; - node.onloadedmetadata = ()=>{ - if (node.duration >= 3){ - setTrimState((curr)=>({ - ...curr, - canNotTrim: false, - trimDuration: 3, - trimEnd: 3, - maxDuration: node.duration - })); - //TODO: this sbould be set in USEEFFECT - //IFF USEEFFECT starts working again - onSelect({ - trimStartSeconds: 0, - trimEndSeconds: 3, - }) - } + dispatchCompState({ + type: ACTION_TYPES.ON_LOADED_METADATA, + payload:{ videoDuration: node.duration,} + }); }; - + node.ontimeupdate = ()=>{ - if(isRepeatOn && - (node.currentTime >= trimEnd || node.currentTime <= trimStart) + if(compState.trimStartSeconds !== undefined && + compState.trimEndSeconds !== undefined && + compState.playbarWidth !== undefined){ + // reset current time when on repeat + if(compState.isRepeatOn && + (node.currentTime > compState.trimEndSeconds || node.currentTime < compState.trimStartSeconds) ){ - node.currentTime = trimStart; + node.currentTime = compState.trimStartSeconds + ONE_MS; + // if (debug){ + // console.log(`Loop Playing trimStart@${compState.trimStartSeconds}`); + // console.log(`Loop Playing currentTime@${node.currentTime}`); + // } + } + + dispatchCompState({ + type: ACTION_TYPES.MOVE_TIMECURSOR, + payload: { + timeCursorOffset: (node.currentTime / node.duration) * (compState.playbarWidth) + } + }); + }else{ + console.log('ontimeupdate failed'); } - setPlaybarState((curr)=>({ - ...curr, - timeCursorOffset: (node.currentTime / node.duration) * (playbarWidth-8) - })); - }; - node.onplay = ()=>{ setPlaypause("playing");}; - node.onpause = ()=>{setPlaypause("paused");}; - node.onended = ()=>{setPlaypause("ended");}; + node.onplay = ()=>setPlaypause(PLAYPUASE_STATES.PLAYING); + node.onpause = ()=>setPlaypause(PLAYPUASE_STATES.PAUSED); + node.onended = ()=>setPlaypause(PLAYPUASE_STATES.ENDED); + node.onprogress = ()=>{ + console.log(node.buffered); + if(node.buffered.length > 0){ + dispatchCompState({ + type: ACTION_TYPES.SET_VIDEO_LOAD_PROGRESS, + payload: {videoLoadProgress: node.buffered} + }); + } + } } // else{} DOM node referenced by ref has been unmounted - }, [playbarWidth, onSelect, isRepeatOn, trimStart, trimEnd]); //END videoRefCallback + }, [ + compState.trimStartSeconds, + compState.trimEndSeconds, + compState.playbarWidth, + compState.isRepeatOn + ]); //END videoRefCallback\ - function handleWindowResize() { - if (playbarRef.current !== null) { - const newWidth = playbarRef.current.getBoundingClientRect().width; - setPlaybarState(curr => ({ - playbarWidth: newWidth, - timeCursorOffset: - (curr.timeCursorOffset / (curr.playbarWidth - 8)) * (newWidth - 8), - })); - } - } - const playbarRefCallback = useCallback(node => { - if (node !== null) { - playbarRef.current = node; - setPlaybarState(curr => ({ - ...curr, - playbarWidth: node.getBoundingClientRect().width, - })); - } - }, []); - useLayoutEffect(() => { - window.addEventListener("resize", handleWindowResize); - return () => { - window.removeEventListener("resize", handleWindowResize); - }; - }, []); - - const handleChangeTrimDuration = (selected: string) =>{ - if(!canNotTrim && maxDuration > 0 && trimOptions[selected] <= maxDuration){ - let newTrimStart = trimStart; - let newTrimEnd = trimStart+ trimOptions[selected]; - if (newTrimEnd > maxDuration){ - newTrimEnd = maxDuration; - newTrimStart = maxDuration - trimOptions[selected]; + useEffect(()=>{ + if( compState.trimStartSeconds + && compState.trimEndSeconds + && ( + compState.trimStartSeconds !== propsTrimStartSeconds + || compState.trimEndSeconds !== propsTrimEndSeconds + ) + ){ + onSelect({ + trimStartSeconds: compState.trimStartSeconds, + trimEndSeconds: compState.trimEndSeconds, + }); } - setTrimState((curr)=>({ - ...curr, - trimReset: new Date(), - trimDuration: trimOptions[selected], - trimStart: newTrimStart, - trimEnd: newTrimEnd, - })) - onSelect({ - trimStartSeconds: newTrimStart, - trimEndSeconds: newTrimEnd, - }); - if (isRepeatOn - && videoRef.current - && videoRef.current.currentTime > newTrimEnd - ){ - videoRef.current.currentTime = newTrimStart; - } - } - } + }, [onSelect, compState.trimStartSeconds, compState.trimEndSeconds, propsTrimStartSeconds, propsTrimEndSeconds]) - const handlePlaypause = ()=>{ - if (playpause === 'paused' || playpause === 'ended'){ + function videoCanPlay(){ + return (compState.playpause === PLAYPUASE_STATES.PAUSED + || compState.playpause === PLAYPUASE_STATES.ENDED + || compState.playpause === PLAYPUASE_STATES.READY + ); + } + function handlePlaypause(){ + if (videoCanPlay()){ videoRef.current?.play(); - }else{ + }else if (compState.playpause === PLAYPUASE_STATES.PLAYING){ videoRef.current?.pause(); + }else { + console.log('Playpause is triggered while it is NOT_READY'); } }; - - const trimScrubberWidth = videoRef.current && playbarWidth > 0 - ? trimDuration > 0 && trimDuration < videoRef.current.duration - ? (trimDuration / videoRef.current.duration * playbarWidth) - : playbarWidth - : 0; - + function disableRepeatOn(){ + dispatchCompState({ + type: ACTION_TYPES.TOGGLE_REPEAT, + payload: {isRepeatOn: false} + }); + } return (
- -
- {playpause === "paused" && ( - - )} - {playpause === "playing" && ( - - )} -
- {videoRef.current && canNotTrim && ( + {compState.playpause !== PLAYPUASE_STATES.NOT_READY && +
+ {videoCanPlay() && ( + + )} + {compState.playpause === PLAYPUASE_STATES.PLAYING && ( + + )} +
+ } + { compState.canNotTrim === true &&
-

Warning: Sorry Your Video is TOO Short

+

{t('error.videoTooShort')}

- )} -
- {/* END of Video Wrapper */} -
-
- {trimScrubberWidth > 0 && - playbarWidth > 0 && - maxDuration > 0 && - { - //console.log(val); - setTrimState((curr)=>({ - ...curr, - trimStart: val.trimStartSeconds, - trimEnd: val.trimEndSeconds - })) - onSelect({ - trimStartSeconds: val.trimStartSeconds, - trimEndSeconds: val.trimEndSeconds, - }); - }} - /> } -
-
{/* END of Playbar */} - -
-
-
- -
+
{/* END of Video Wrapper */} + { + if(videoRef.current !== null && videoRef.current.currentTime + && compState.playbarWidth && compState.videoDuration){ + const newTime = newPos / compState.playbarWidth * compState.videoDuration; + if(compState.trimStartSeconds + && compState.trimEndSeconds + && (newTime < compState.trimStartSeconds + || newTime > compState.trimEndSeconds + )){ + disableRepeatOn(); + } + videoRef.current.currentTime = newTime; + } + }} + /> +
); }); diff --git a/packages/fakeyou.com/src/components/common/VideoQuickTrim/components/ControlBar.tsx b/packages/fakeyou.com/src/components/common/VideoQuickTrim/components/ControlBar.tsx new file mode 100644 index 0000000000..13180b2a6d --- /dev/null +++ b/packages/fakeyou.com/src/components/common/VideoQuickTrim/components/ControlBar.tsx @@ -0,0 +1,103 @@ +import React, { memo } from 'react' +import { + faArrowsRepeat, + faPlay, + faPause, + faVolume, + faVolumeSlash, +} from "@fortawesome/pro-solid-svg-icons"; + +import { Button, SelectionBubbles } from "components/common"; + +import { + Action, + ACTION_TYPES, + PLAYPUASE_STATES +} from '../reducer'; +import { TRIM_OPTIONS, formatSecondsToHHMMSSCS } from "../utilities"; + +export const ControlBar = memo(({ + debug: propsDebug = false, + readyToMount, + videoCurrentTime, + videoDuration, + isRepeatOn, + isMuted, + playpause, + handlePlaypause, + dispatchCompState +}:{ + debug?:boolean; + readyToMount: boolean; + videoCurrentTime: number | undefined; + videoDuration: number | undefined; + isMuted: boolean, + isRepeatOn: boolean; + playpause: string; + handlePlaypause: ()=>void; + dispatchCompState: (action: Action) => void; +})=>{ + const debug = false || propsDebug; + if (debug) console.log("ControlBar reRENDERED!!"); + + function handleSetTrimDuration(selected: string){ + dispatchCompState({ + type: ACTION_TYPES.SET_TRIM_DURATION, + payload: { + trimDurationString: selected + } + }); + }; + if(readyToMount){ + return( +
+
+
+ +
+ ); + }else{ + console.log('TODO: ControlBar should rendering loading state instead of null'); + return null; + } + +}); \ No newline at end of file diff --git a/packages/fakeyou.com/src/components/common/VideoQuickTrim/components/PlayCursor.tsx b/packages/fakeyou.com/src/components/common/VideoQuickTrim/components/PlayCursor.tsx new file mode 100644 index 0000000000..f1ed16ae45 --- /dev/null +++ b/packages/fakeyou.com/src/components/common/VideoQuickTrim/components/PlayCursor.tsx @@ -0,0 +1,24 @@ +import React from 'react'; +import { withScrubbing, withScrubbingPropsI } from './withScrubbing'; + +interface PlayCursorPropsI extends withScrubbingPropsI{ + onChanged: (posPercent: number)=>void; +} + +export const PlayCursor = ({ + onChanged, + ...rest +}:PlayCursorPropsI)=>{ + const PlayCursorWithScrubbing = withScrubbing(()=>{ + return( +
+ ); + }); + + return( + + ); +}; diff --git a/packages/fakeyou.com/src/components/common/VideoQuickTrim/components/ProgressBar.tsx b/packages/fakeyou.com/src/components/common/VideoQuickTrim/components/ProgressBar.tsx new file mode 100644 index 0000000000..2d96bbb908 --- /dev/null +++ b/packages/fakeyou.com/src/components/common/VideoQuickTrim/components/ProgressBar.tsx @@ -0,0 +1,114 @@ +import React, { + memo, + useCallback, + useLayoutEffect, + useRef +} from 'react'; +import { + Action, + ACTION_TYPES, +} from '../reducer'; +import { TrimScrubber } from './TrimScrubber'; +import { PlayCursor } from './PlayCursor'; + +export const ProgressBar = memo(({ + debug: propsDebug = false, + readyToMount, + timeCursorOffset, + trimStartSeconds, + trimDuration, + playbarWidth, + scrubberWidth, + videoDuration, + videoBuffered, + onPlayCursorChanged, + dispatchCompState +}:{ + debug?: boolean; + readyToMount: boolean; + timeCursorOffset: number; + trimStartSeconds: number; + trimDuration: number; + playbarWidth: number; + scrubberWidth: number; + videoDuration: number; + videoBuffered: TimeRanges | undefined; + onPlayCursorChanged: (newPos: number) => void; + dispatchCompState: (action: Action) => void; +})=>{ + const debug = false || propsDebug; + + const playbarRef = useRef(null); + + const playbarRefCallback = useCallback(node => { + if (node !== null) { + playbarRef.current = node; + dispatchCompState({ + type: ACTION_TYPES.SET_PLAYBAR_LAYOUT, + payload: { + playbarWidth: node.getBoundingClientRect().width, + } + }) + }else{ + // console.log(node); + } + }, [dispatchCompState]); + + const handleWindowResize = useCallback(()=> { + if (playbarRef.current !== null && readyToMount){ + dispatchCompState({ + type: ACTION_TYPES.SET_PLAYBAR_LAYOUT, + payload: { + playbarWidth: playbarRef.current.getBoundingClientRect().width, + } + }); + } + },[dispatchCompState, readyToMount]); + useLayoutEffect(() => { + window.addEventListener("resize", handleWindowResize); + return () => { + window.removeEventListener("resize", handleWindowResize); + }; + }, [handleWindowResize]); + + if(readyToMount){ + // console.log('progress bar rendering'); + return( +
+
+ {videoBuffered !== undefined && + + } + +
+ { + const newTrimStartSeconds = newPos / (playbarWidth) * videoDuration; + dispatchCompState({ + type: ACTION_TYPES.MOVE_TRIM, + payload: { + trimStartSeconds: newTrimStartSeconds, + trimEndSeconds: newTrimStartSeconds + trimDuration + } + }); + }} + /> + + {/* END of Playbar */}
+ ); + }else{ + console.log('TODO: ProgressBar should rendering loading state instead of null'); + return null; + } +}); \ No newline at end of file diff --git a/packages/fakeyou.com/src/components/common/VideoQuickTrim/components/TrimScrubber.tsx b/packages/fakeyou.com/src/components/common/VideoQuickTrim/components/TrimScrubber.tsx new file mode 100644 index 0000000000..0ec0745421 --- /dev/null +++ b/packages/fakeyou.com/src/components/common/VideoQuickTrim/components/TrimScrubber.tsx @@ -0,0 +1,45 @@ +import React, { + memo, +} from 'react'; +import { + faGripDots +} from "@fortawesome/pro-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { withScrubbing, withScrubbingPropsI } from './withScrubbing'; + +interface TrimScrubberPropsI extends withScrubbingPropsI { + trimStartSeconds:number; + trimDuration: number; + videoDuration: number; + onChange:(newPos:number)=>void +} +export const TrimScrubber = memo(({ + debug: propsDebug = false, + trimStartSeconds, + trimDuration, + videoDuration, + onChange, + ...rest +}:TrimScrubberPropsI)=>{ + const debug = false || propsDebug; + + const TrimScrubberWithScrubbing = withScrubbing(() => { + return( +
+ +
+ ); + }); + + return ( + + ); +}); diff --git a/packages/fakeyou.com/src/components/common/VideoQuickTrim/components/withScrubbing.tsx b/packages/fakeyou.com/src/components/common/VideoQuickTrim/components/withScrubbing.tsx new file mode 100644 index 0000000000..972864bf5c --- /dev/null +++ b/packages/fakeyou.com/src/components/common/VideoQuickTrim/components/withScrubbing.tsx @@ -0,0 +1,150 @@ +import React,{ + memo, + useEffect, + useRef, + useState, + useLayoutEffect, +} from 'react'; + +export interface withScrubbingPropsI { + debug?: boolean + boundingWidth: number; + scrubberWidth: number; + scrubPosition?: number; // scrubber location as px + styleOverride?: {[key: string]: string|number }; + onScrubChanged?: (newPos: number)=>void; + //return scrubber location as px +} + +type withSrcubbingStates = { + currLeftOffset: number, + prevLeftOffset: number; + pointerStartPos: number; +} + +export const withScrubbing =

(Component: React.ComponentType

) => memo(({ + debug: propsDebug = false, + boundingWidth, + scrubberWidth, + scrubPosition: propsLeftOffset = 0, + styleOverride = {}, + onScrubChanged, + ...rest +}: withScrubbingPropsI) => { + const debug = false ;//|| propsDebug; + if (debug) console.log(`withSCRUBBING reRENDERING!! `); + + const refEl = useRef(null); + const refListener = useRef(Date.now()); + + const [{ + currLeftOffset, pointerStartPos, + prevLeftOffset + }, setStates] = useState({ + currLeftOffset: propsLeftOffset, + prevLeftOffset: propsLeftOffset, + pointerStartPos: -1 // negative denotes pointer not engaged + }); + + useLayoutEffect(() => { + // if (debug) console.log(`withSCRUBBING useLAYOUTeffect!! `); + function handleScrubStart (e: MouseEvent) { + if(refEl.current){ + if(refEl.current.contains(e.target as Node)){ + setStates((curr)=>({ + ...curr, + pointerStartPos: e.clientX + })); + return true; + } + } + }; + function handleScrubEnd (e: MouseEvent){ + e.preventDefault(); + e.stopPropagation(); + setStates((curr)=>({ + ...curr, + pointerStartPos: -1, + prevLeftOffset: curr.currLeftOffset, + setBySelf: Date.now(), + })); + }; + function handleScrubMove (e: MouseEvent){ + e.preventDefault(); + e.stopPropagation(); + setStates((curr)=>{ + if(curr.pointerStartPos >= 0 && curr.pointerStartPos!==null){ + let newLeftOffset = curr.prevLeftOffset + e.clientX - curr.pointerStartPos; + if (newLeftOffset + scrubberWidth > boundingWidth) { + newLeftOffset = boundingWidth - scrubberWidth; + }else if(newLeftOffset < 0) { + newLeftOffset = 0; + } + if(newLeftOffset !== curr.currLeftOffset){ + return{ + ...curr, + currLeftOffset: newLeftOffset, + } + } + } + return curr; + }); + }; + if(!(window as any)[`listener-id-${refListener}`]){ + (window as any)[`listender-id-${refListener}`] = true; + window.addEventListener("mousedown", handleScrubStart); + window.addEventListener("mouseup", handleScrubEnd); + window.addEventListener("mousemove", handleScrubMove); + return () => { + (window as any)[`listener-id-${refListener}`] = false; + window.removeEventListener("mousedown", handleScrubStart); + window.removeEventListener("mouseup", handleScrubEnd); + window.removeEventListener("mousemove", handleScrubMove); + }; + } + }, [scrubberWidth, boundingWidth]); + + useEffect(()=>{ + // if (debug) console.log(`withSCRUBBING useEFFECT!! `); + if(onScrubChanged && boundingWidth > 0 && prevLeftOffset >= 0 && propsLeftOffset !== prevLeftOffset){ + onScrubChanged(prevLeftOffset); + } + },[propsLeftOffset, prevLeftOffset, boundingWidth, onScrubChanged]); + + useEffect(()=>{ + // this takes a forced reset on leftoffset + setStates((curr)=>{ + if(curr.prevLeftOffset === curr.currLeftOffset + && propsLeftOffset !== curr.prevLeftOffset + && propsLeftOffset !== curr.prevLeftOffset + ){ + return { + ...curr, + currLeftOffset: propsLeftOffset, // in pixels + prevLeftOffset: propsLeftOffset, //in pixels + } + }else{ + return curr + } + }); + }, [propsLeftOffset]) + + return( +

=0 ? 'grabbing': 'grab', + ...styleOverride + }} + > + +
+ ); +}); diff --git a/packages/fakeyou.com/src/components/common/VideoQuickTrim/helpers.tsx b/packages/fakeyou.com/src/components/common/VideoQuickTrim/helpers.tsx deleted file mode 100644 index cbe19a389a..0000000000 --- a/packages/fakeyou.com/src/components/common/VideoQuickTrim/helpers.tsx +++ /dev/null @@ -1,14 +0,0 @@ -export function roundTo2Dec(floaty:number){ - return Math.round(floaty*100)/100; -} -export function fractionToPercentage(fraction:number){ - return roundTo2Dec(fraction * 100); -} -export function formatSecondsToHHMMSSCS(seconds:number){ - //example of the ISO String: 1970-01-01T00:01:40.774Z - const isoString = new Date(seconds * 1000).toISOString(); - if(seconds > 3600) - return isoString.substring(11, 19) + "." + isoString.substring(20, 22) - else - return isoString.substring(14, 19) + "." + isoString.substring(20, 22); -} \ No newline at end of file diff --git a/packages/fakeyou.com/src/components/common/VideoQuickTrim/index.ts b/packages/fakeyou.com/src/components/common/VideoQuickTrim/index.ts index 7e49b01a6f..8f25d05b73 100644 --- a/packages/fakeyou.com/src/components/common/VideoQuickTrim/index.ts +++ b/packages/fakeyou.com/src/components/common/VideoQuickTrim/index.ts @@ -1,5 +1,5 @@ -import VideoQuickTrim from './VideoQuickTrim' -import { QuickTrimData } from './types'; +import {VideoQuickTrim} from './VideoQuickTrim' +import { QuickTrimData } from './utilities'; export type { QuickTrimData }; export default VideoQuickTrim; diff --git a/packages/fakeyou.com/src/components/common/VideoQuickTrim/reducer.tsx b/packages/fakeyou.com/src/components/common/VideoQuickTrim/reducer.tsx new file mode 100644 index 0000000000..18bb54f6a3 --- /dev/null +++ b/packages/fakeyou.com/src/components/common/VideoQuickTrim/reducer.tsx @@ -0,0 +1,223 @@ +import { + MIN_VID_DURATION, + TIME_CURSOR_WIDTH, + TRIM_OPTIONS, + roundToMilliseconds +} from "./utilities"; + +export enum STATE_STATUSES { + INIT = "init", + VIDEO_METADATA_LOADED = "video_metadata_loaded", + LOAD_ORDER_ERROR = "load_order_error", + ACTION_TYPE_ERROR = "no_such_action_type" +}; + +export enum PLAYPUASE_STATES{ + NOT_READY = "not_ready", + READY = "ready", + PLAYING = "playing", + PAUSED = "paused", + ENDED = "ended", +} + +export type State = { + status: string; + errorMessage: string[]; + + playpause: string; + + canNotTrim: boolean | undefined; + trimReset: number; + trimDuration: number | undefined; + trimStartSeconds: number | undefined; + trimEndSeconds: number | undefined; + videoLoadProgress: TimeRanges | undefined; + videoDuration: number | undefined; + + + playbarWidth: number | undefined; + scrubberWidth: number | undefined; + timeCursorOffset: number | undefined; + + isMuted: boolean; + isRepeatOn: boolean; +}; + +export const initialState = { + status: STATE_STATUSES.INIT, + errorMessage : [], + playpause: PLAYPUASE_STATES.NOT_READY, + canNotTrim: undefined, + trimReset: Date.now(), + trimDuration: undefined, + trimStartSeconds: undefined, + trimEndSeconds: undefined, + videoLoadProgress: undefined, + videoDuration: undefined, + playbarWidth: undefined, + scrubberWidth: undefined, + timeCursorOffset: undefined, + isMuted: false, + isRepeatOn: true, +} + +export enum ACTION_TYPES { + RESET = "reset", + TOGGLE_REPEAT = "toggle_repeat", + TOGGLE_MUTE = "toggle_mute", + ON_LOADED_METADATA = "on_loaded_metadata", + MOVE_TIMECURSOR = "move_timecursor", + SET_PLAYPUASE = "set_playpause", + SET_PLAYBAR_LAYOUT = "set_playbar_layout", + SET_TRIM_DURATION = "set_trim_duration", + SET_VIDEO_LOAD_PROGRESS = "set_video_load_progress", + MOVE_TRIM = "move_trim" +} + +export type Action = + | {type: ACTION_TYPES.RESET} + | {type: ACTION_TYPES.TOGGLE_REPEAT, payload?:{isRepeatOn: boolean}} + | {type: ACTION_TYPES.TOGGLE_MUTE} + | {type: ACTION_TYPES.ON_LOADED_METADATA, payload: {videoDuration: number}} + | {type: ACTION_TYPES.MOVE_TIMECURSOR, payload:{ timeCursorOffset: number}} + | {type: ACTION_TYPES.SET_PLAYPUASE, payload:{ playpause: string}} + | {type: ACTION_TYPES.SET_PLAYBAR_LAYOUT, payload:{ playbarWidth: number}} + | {type: ACTION_TYPES.SET_TRIM_DURATION, payload:{ trimDurationString: string}} + | {type: ACTION_TYPES.SET_VIDEO_LOAD_PROGRESS, payload: { + videoLoadProgress: TimeRanges + }} + | {type: ACTION_TYPES.MOVE_TRIM, payload:{ + trimStartSeconds: number, + trimEndSeconds: number, + }} +; + +export function reducer(state: State, action: Action): State { + console.log("VideoQuickTrim State Dispatch"); + console.log(action); + + switch(action.type){ + case ACTION_TYPES.SET_PLAYPUASE:{ + return {...state, ...action.payload}; + } + case ACTION_TYPES.TOGGLE_REPEAT:{ + if(action.payload){ + return {...state, isRepeatOn: action.payload.isRepeatOn} + } + return {...state, isRepeatOn: !state.isRepeatOn} + } + case ACTION_TYPES.TOGGLE_MUTE:{ + return {...state, isMuted: !state.isMuted} + } + case ACTION_TYPES.ON_LOADED_METADATA:{ + if(action.payload.videoDuration >= MIN_VID_DURATION){ + return{ + ...state, + status: STATE_STATUSES.VIDEO_METADATA_LOADED, + videoDuration: action.payload.videoDuration, + canNotTrim: false, + trimDuration: 3, + trimStartSeconds: 0, + trimEndSeconds: 3, + } + }else{ + return { + ...state, + videoDuration: action.payload.videoDuration, + canNotTrim: true + } + } + } + case ACTION_TYPES.SET_VIDEO_LOAD_PROGRESS:{ + return{ + ...state, + videoLoadProgress: action.payload.videoLoadProgress + }; + } + case ACTION_TYPES.SET_PLAYBAR_LAYOUT:{ + const newWidth = action.payload.playbarWidth; + const prevOffset = state.timeCursorOffset || 0; + if(state.trimDuration && state.videoDuration){ + return { + ...state, + playpause: PLAYPUASE_STATES.READY, + playbarWidth: newWidth, + scrubberWidth: newWidth * (state.trimDuration / state.videoDuration), + timeCursorOffset: (newWidth-TIME_CURSOR_WIDTH)*(prevOffset/ state.videoDuration) + }; + }else{ + return{ + ...state, + status: STATE_STATUSES.LOAD_ORDER_ERROR, + errorMessage: [...state.errorMessage, 'Setting playbar layout before video and trim are loaded'] + } + } + } + case ACTION_TYPES.SET_TRIM_DURATION:{ + const selected = action.payload.trimDurationString; + if( state.canNotTrim !== undefined + && state.canNotTrim === false + && state.playbarWidth !==undefined + && state.videoDuration !== undefined + && TRIM_OPTIONS[selected] <= state.videoDuration + && state.trimStartSeconds !== undefined + ){ + let newTrimStart = state.trimStartSeconds; + let newTrimEnd = state.trimStartSeconds + TRIM_OPTIONS[selected]; + if (newTrimEnd > state.videoDuration){ + newTrimEnd = state.videoDuration; + if (state.videoDuration - TRIM_OPTIONS[selected] >=0) { + newTrimStart = roundToMilliseconds(state.videoDuration - TRIM_OPTIONS[selected]); + }else{ + newTrimStart = 0; + } + } + return{ + ...state, + trimDuration: newTrimEnd-newTrimStart, + trimStartSeconds: newTrimStart, + trimEndSeconds: newTrimEnd, + scrubberWidth: (newTrimEnd-newTrimStart)/state.videoDuration * state.playbarWidth, + } + }else{ + return { + ...state, + status: STATE_STATUSES.LOAD_ORDER_ERROR, + errorMessage: [...state.errorMessage, 'Setting trim duration before video and trim are loaded'] + } + } + } + case ACTION_TYPES.MOVE_TRIM:{ + return{ + ...state, + trimStartSeconds: roundToMilliseconds(action.payload.trimStartSeconds), + trimEndSeconds: roundToMilliseconds(action.payload.trimEndSeconds), + } + } + case ACTION_TYPES.MOVE_TIMECURSOR:{ + return { + ...state, + timeCursorOffset: action.payload.timeCursorOffset + } + } + case ACTION_TYPES.RESET:{ + return initialState + } + default:{ + return { + ...state, + status: STATE_STATUSES.ACTION_TYPE_ERROR, + errorMessage: [...state.errorMessage, "Reducer Action switch's default case is reached"] + } + } + } +}; + +/* + const trimScrubberWidth = videoRef.current && playbarWidth > 0 + ? trimDuration > 0 && trimDuration < videoRef.current.duration + ? (trimDuration / videoRef.current.duration * playbarWidth) + : playbarWidth + : 0; +*/ + diff --git a/packages/fakeyou.com/src/components/common/VideoQuickTrim/styles.scss b/packages/fakeyou.com/src/components/common/VideoQuickTrim/styles.scss index d45ce34e48..0dc6db8a49 100644 --- a/packages/fakeyou.com/src/components/common/VideoQuickTrim/styles.scss +++ b/packages/fakeyou.com/src/components/common/VideoQuickTrim/styles.scss @@ -60,11 +60,11 @@ $playbar-height: 2rem; background-color: #e66462; opacity: 70%; } - p { + h1 { position: absolute; - margin-top: 230px; - width: 100%; - top: 0; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); } } .playbar { @@ -80,17 +80,34 @@ $playbar-height: 2rem; left: 0; background-color: $panel-inner-border; border-radius: 0 0 0.5rem 0.5rem; + overflow: hidden; + + .played, .loaded{ + position: absolute; + height: 100%; + top: 0; + left: 0; + opacity: 0.5; + } + .played{ + background-color: $primary-dark; + } + .loaded{ + background-color: $gray-600; + } + } + + .scrubber-wrapper{ + box-shadow: 1px 1px 4px 0px rgba(0,0,0,1); } .trim-scrubber{ - position: absolute; - top: -1rem; height: 3rem; background-color: #e66462; opacity: 0.8; outline: 0.125rem $white solid; border-top: 1rem $white solid; border-radius: 0.25rem 0.25rem 0.125rem 0.125rem; - cursor: grab; + display: flex; justify-content: center; @@ -103,9 +120,12 @@ $playbar-height: 2rem; .playcursor { position: absolute; top: 0; - width: 0.5rem; + width: 0.5rem; //=8px + transform: translate(-3px, 0px); height: $playbar-height; background-color: $white; + border-radius: 1px; + box-shadow: 1px 1px 4px 0px rgba(0,0,0,0.2); } .playpause-external { diff --git a/packages/fakeyou.com/src/components/common/VideoQuickTrim/types.tsx b/packages/fakeyou.com/src/components/common/VideoQuickTrim/types.tsx deleted file mode 100644 index 4e3dd0db0d..0000000000 --- a/packages/fakeyou.com/src/components/common/VideoQuickTrim/types.tsx +++ /dev/null @@ -1,20 +0,0 @@ -//For integration into other components -export type QuickTrimData = { - trimStartSeconds: number; - trimEndSeconds: number; -} - -// INTERNALS -export type TrimStates = { - canNotTrim: boolean; - trimReset: Date; - trimDuration: number; - trimStart: number; - trimEnd: number; - maxDuration: number; -} - -export type PlaybarStates = { - playbarWidth: number; - timeCursorOffset: number; -} diff --git a/packages/fakeyou.com/src/components/common/VideoQuickTrim/utilities.tsx b/packages/fakeyou.com/src/components/common/VideoQuickTrim/utilities.tsx new file mode 100644 index 0000000000..0d8c907333 --- /dev/null +++ b/packages/fakeyou.com/src/components/common/VideoQuickTrim/utilities.tsx @@ -0,0 +1,29 @@ +//For integration into other components +export const TIME_CURSOR_WIDTH = 8; +export const MIN_VID_DURATION = 3; +export const ONE_MS = 0.001; + +export type QuickTrimData = { + trimStartSeconds: number; + trimEndSeconds: number; +} + +export function roundToMilliseconds(floaty:number){ + return Math.round(floaty*1000)/1000; +} + +export function formatSecondsToHHMMSSCS(seconds:number){ + //example of the ISO String: 1970-01-01T00:01:40.774Z + const isoString = new Date(seconds * 1000).toISOString(); + if(seconds > 3600) + return isoString.substring(11, 19) + "." + isoString.substring(20, 22) + else + return isoString.substring(14, 19) + "." + isoString.substring(20, 22); +} + +export const TRIM_OPTIONS:{[key: string]: number } = { + "3s": 3, + "5s": 5, + "10s":10, + "15s":15 +}; \ No newline at end of file diff --git a/packages/fakeyou.com/src/utils/domainConfig.ts b/packages/fakeyou.com/src/utils/domainConfig.ts index a37eade057..feb07676ab 100644 --- a/packages/fakeyou.com/src/utils/domainConfig.ts +++ b/packages/fakeyou.com/src/utils/domainConfig.ts @@ -4,20 +4,20 @@ export enum Website { } export interface DomainConfig { - website: Website, - logo: string, - title: string, - link: string, + website: Website; + logo: string; + title: string; + link: string; } const domainConfigs: Record = { - "storyteller": { + storyteller: { website: Website.StorytellerAi, logo: "/fakeyou/Storyteller-Logo-1.png", title: "Storyteller AI", link: "https://storyteller.ai", }, - "fakeyou": { + fakeyou: { website: Website.FakeYou, logo: "/fakeyou/FakeYou-Logo-2.png", title: "FakeYou", @@ -35,7 +35,7 @@ export const getCurrentDomainConfig = (): DomainConfig => { } if (window.location.hostname.includes("storyteller")) { - return domainConfigs.storyteller; + return domainConfigs.storyteller; } // Default fallback diff --git a/packages/fakeyou.com/src/v2/view/pages/components_library/sandbox.tsx b/packages/fakeyou.com/src/v2/view/pages/components_library/sandbox.tsx index f979b431bb..6b5ea067f6 100644 --- a/packages/fakeyou.com/src/v2/view/pages/components_library/sandbox.tsx +++ b/packages/fakeyou.com/src/v2/view/pages/components_library/sandbox.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useState } from 'react'; import VideoQuickTrim from 'components/common/VideoQuickTrim'; import { NavLink } from 'react-router-dom'; @@ -7,9 +7,13 @@ import {Container, Panel} from 'components/common'; export default function ComponentSandbox( {parentPath}:{parentPath:string} ){ + const [state1, setState1] = useState(undefined); + const [state2, setState2] = useState(undefined); const handleCallback = (val:any)=>{ console.log('onChange is triggered') console.log(val); + setState1(val.trimStartSeconds); + setState2(val.trimEndSeconds); } return ( ← Back @@ -18,18 +22,22 @@ export default function ComponentSandbox(

-
+

{`Start Seconds: ${state1}`}

+

{`Start Seconds: ${state2}`}

+ {/*
-
+
*/} ); } \ No newline at end of file diff --git a/packages/fakeyou.com/src/v2/view/pages/landing/LandingPage.tsx b/packages/fakeyou.com/src/v2/view/pages/landing/LandingPage.tsx index 5f684760d6..eafc84f8f3 100644 --- a/packages/fakeyou.com/src/v2/view/pages/landing/LandingPage.tsx +++ b/packages/fakeyou.com/src/v2/view/pages/landing/LandingPage.tsx @@ -2,14 +2,12 @@ import React from "react"; import { usePrefixedDocumentTitle } from "../../../../common/UsePrefixedDocumentTitle"; import { SessionWrapper } from "@storyteller/components/src/session/SessionWrapper"; import { SessionSubscriptionsWrapper } from "@storyteller/components/src/session/SessionSubscriptionsWrapper"; -import {} from "@fortawesome/pro-solid-svg-icons"; import { PosthogClient } from "@storyteller/components/src/analytics/PosthogClient"; import { Container } from "components/common"; import FakeYouLandingHeader from "./fakeyou/FakeYouLandingHeader"; import Dashboard from "./Dashboard"; import { useDomainConfig } from "context/DomainConfigContext"; -import StorytellerLanding from "./storyteller/StorytellerLanding"; -import LandingVideoReel from "./components/LandingVideoReel"; +import LandingVideoReel from "./components/LandingVideoReel/LandingVideoReel"; import { FrontendInferenceJobType, InferenceJob, @@ -20,6 +18,8 @@ import "./LandingPage.scss"; import VstSectionV2 from "./components/VstSectionV2"; import FakeYouLandingBody from "./fakeyou/FakeYouLandingBody"; import { DomainConfig, Website } from "utils/domainConfig"; +import OnboardingSelection from "./storyteller/OnboardingSelection"; +import TtsDemoSection from "./components/TtsDemoSection/TtsDemoSection"; interface Props { sessionWrapper: SessionWrapper; @@ -39,13 +39,15 @@ function LandingPage(props: Props) { PosthogClient.recordPageview(); - const domain : DomainConfig = useDomainConfig(); + const domain: DomainConfig = useDomainConfig(); const isLoggedIn = props.sessionWrapper.isLoggedIn(); return ( <> - {domain.website === Website.StorytellerAi && !isLoggedIn && } + {domain.website === Website.StorytellerAi && !isLoggedIn && ( + + )} {domain.website === Website.FakeYou ? ( <> @@ -76,15 +78,23 @@ function LandingPage(props: Props) { <> {/* STORYTELLER,AI */} {!isLoggedIn ? ( - + <> + + + + + + ) : ( )} diff --git a/packages/fakeyou.com/src/v2/view/pages/landing/components/LandingVideoReel/LandingVideoReel.scss b/packages/fakeyou.com/src/v2/view/pages/landing/components/LandingVideoReel/LandingVideoReel.scss new file mode 100644 index 0000000000..dcabb0bdc4 --- /dev/null +++ b/packages/fakeyou.com/src/v2/view/pages/landing/components/LandingVideoReel/LandingVideoReel.scss @@ -0,0 +1,72 @@ +@import "scss/colours.scss"; + +.storyteller-landing-reel { + display: flex; + height: 74vh; + justify-content: center; + position: relative; + + video { + position: absolute; + width: 100%; + height: 100%; + object-fit: cover; + } + + video::-webkit-media-controls { + display: none; + } + + video::-webkit-media-controls-start-playback-button { + display: none; + } + + .reel-overlay { + z-index: 0; + position: absolute; + width: 100%; + height: 100%; + background-color: rgba(20, 20, 27, 0.6); + + &:after { + content: ""; + position: absolute; + left: 0; + right: 0; + bottom: 0; + height: 20%; + background: linear-gradient(transparent, $bg); + z-index: 1; + } + } + + .storyteller-landing-heading { + font-size: 52px; + } + + .storyteller-landing-sub-text { + max-width: 900px; + font-size: 18px; + } + + @media screen and (max-width: 992px) { + .storyteller-landing-heading { + font-size: 44px; + } + + .storyteller-landing-sub-text { + max-width: 100%; + font-size: 16px; + } + } + + @media screen and (max-width: 768px) { + .storyteller-landing-heading { + font-size: 32px; + } + + .storyteller-landing-sub-text { + font-size: 16px; + } + } +} diff --git a/packages/fakeyou.com/src/v2/view/pages/landing/components/LandingVideoReel.tsx b/packages/fakeyou.com/src/v2/view/pages/landing/components/LandingVideoReel/LandingVideoReel.tsx similarity index 61% rename from packages/fakeyou.com/src/v2/view/pages/landing/components/LandingVideoReel.tsx rename to packages/fakeyou.com/src/v2/view/pages/landing/components/LandingVideoReel/LandingVideoReel.tsx index a9da927bf2..e526023967 100644 --- a/packages/fakeyou.com/src/v2/view/pages/landing/components/LandingVideoReel.tsx +++ b/packages/fakeyou.com/src/v2/view/pages/landing/components/LandingVideoReel/LandingVideoReel.tsx @@ -1,5 +1,7 @@ +import { faArrowRight } from "@fortawesome/pro-solid-svg-icons"; import { Button } from "components/common"; import React, { useEffect } from "react"; +import "./LandingVideoReel.scss"; interface LandingVideoReelProps {} @@ -42,27 +44,30 @@ export default function LandingVideoReel(props: LandingVideoReelProps) { }, [isOnLandingPage]); return ( -
-
); diff --git a/packages/fakeyou.com/src/v2/view/pages/landing/components/LandingVideoReel/index.ts b/packages/fakeyou.com/src/v2/view/pages/landing/components/LandingVideoReel/index.ts new file mode 100644 index 0000000000..d29a8fcd89 --- /dev/null +++ b/packages/fakeyou.com/src/v2/view/pages/landing/components/LandingVideoReel/index.ts @@ -0,0 +1 @@ +export { default } from "./LandingVideoReel"; diff --git a/packages/fakeyou.com/src/v2/view/pages/landing/components/TtsDemoSection/TtsDemoSection.tsx b/packages/fakeyou.com/src/v2/view/pages/landing/components/TtsDemoSection/TtsDemoSection.tsx new file mode 100644 index 0000000000..c0e663ae69 --- /dev/null +++ b/packages/fakeyou.com/src/v2/view/pages/landing/components/TtsDemoSection/TtsDemoSection.tsx @@ -0,0 +1,60 @@ +import { faArrowRight } from "@fortawesome/pro-solid-svg-icons"; +import { Button, Panel } from "components/common"; +import React from "react"; +import LandingDemo from "../../fakeyou/LandingDemo/FakeYouLandingDemo"; +import { SessionWrapper } from "@storyteller/components/src/session/SessionWrapper"; +import { SessionSubscriptionsWrapper } from "@storyteller/components/src/session/SessionSubscriptionsWrapper"; +import { + FrontendInferenceJobType, + InferenceJob, +} from "@storyteller/components/src/jobs/InferenceJob"; +import { TtsInferenceJob } from "@storyteller/components/src/jobs/TtsInferenceJobs"; + +interface TtsDemoSectionProps { + sessionWrapper: SessionWrapper; + sessionSubscriptionsWrapper: SessionSubscriptionsWrapper; + inferenceJobs: Array; + ttsInferenceJobs: Array; + enqueueInferenceJob: ( + jobToken: string, + frontendInferenceJobType: FrontendInferenceJobType + ) => void; + inferenceJobsByCategory: Map>; + enqueueTtsJob: (jobToken: string) => void; +} + +export default function TtsDemoSection(props: TtsDemoSectionProps) { + return ( + +
+
+

Generate Text to Speech

+

+ Create audio of any character saying anything you want. +

+
+
+
+
+ +
+
+
+ ); +} diff --git a/packages/fakeyou.com/src/v2/view/pages/landing/components/VstSectionV2/VstSectionV2.scss b/packages/fakeyou.com/src/v2/view/pages/landing/components/VstSectionV2/VstSectionV2.scss index 744650702c..35d01947ef 100644 --- a/packages/fakeyou.com/src/v2/view/pages/landing/components/VstSectionV2/VstSectionV2.scss +++ b/packages/fakeyou.com/src/v2/view/pages/landing/components/VstSectionV2/VstSectionV2.scss @@ -3,6 +3,14 @@ .vst-section { padding-top: 5rem; padding-bottom: 5rem; + + video::-webkit-media-controls { + display: none; + } + + video::-webkit-media-controls-start-playback-button { + display: none; + } } .vst-carousel-panel { @@ -166,4 +174,9 @@ font-size: 11px; } } + + .vst-section { + padding-top: 3rem; + padding-bottom: 3rem; + } } diff --git a/packages/fakeyou.com/src/v2/view/pages/landing/components/VstSectionV2/VstSectionV2.tsx b/packages/fakeyou.com/src/v2/view/pages/landing/components/VstSectionV2/VstSectionV2.tsx index 708da7a754..a6f7bfa2fa 100644 --- a/packages/fakeyou.com/src/v2/view/pages/landing/components/VstSectionV2/VstSectionV2.tsx +++ b/packages/fakeyou.com/src/v2/view/pages/landing/components/VstSectionV2/VstSectionV2.tsx @@ -155,6 +155,8 @@ export default function VstSectionV2(props: VstSectionV2Props) { autoPlay loop muted + playsInline + controls={false} key={`${index}-${activeIndex}`} onPlay={handleVideoPlay} onPause={handleVideoPause} @@ -184,7 +186,7 @@ export default function VstSectionV2(props: VstSectionV2Props) { {videoSet.map((video, index) => (
-
diff --git a/packages/fakeyou.com/src/v2/view/pages/landing/fakeyou/LandingDemo/FakeYouLandingDemo.tsx b/packages/fakeyou.com/src/v2/view/pages/landing/fakeyou/LandingDemo/FakeYouLandingDemo.tsx index 3d8091a42a..23a52d143b 100644 --- a/packages/fakeyou.com/src/v2/view/pages/landing/fakeyou/LandingDemo/FakeYouLandingDemo.tsx +++ b/packages/fakeyou.com/src/v2/view/pages/landing/fakeyou/LandingDemo/FakeYouLandingDemo.tsx @@ -35,6 +35,8 @@ interface TtsInferencePanelProps { ) => void; inferenceJobsByCategory: Map>; enqueueTtsJob: (jobToken: string) => void; + showHanashi?: boolean; + autoFocusTextBox?: boolean; } export default function LandingDemo({ @@ -44,6 +46,8 @@ export default function LandingDemo({ enqueueInferenceJob, enqueueTtsJob, inferenceJobsByCategory, + showHanashi = true, + autoFocusTextBox = true, }: TtsInferencePanelProps) { const [textBuffer, setTextBuffer] = useState(""); const [maybeTtsError, setMaybeTtsError] = useState< @@ -221,18 +225,21 @@ export default function LandingDemo({ return (
- Hanashi Demo setIsHanashiHovered(true)} - onMouseLeave={() => setIsHanashiHovered(false)} - className="hanashi-demo-image" - draggable="false" - /> + {showHanashi && ( + Hanashi Demo setIsHanashiHovered(true)} + onMouseLeave={() => setIsHanashiHovered(false)} + className="hanashi-demo-image" + draggable="false" + /> + )} +
@@ -265,7 +272,7 @@ export default function LandingDemo({ onChange={handleChangeText} rows={4} resize={false} - autoFocus={isMobile ? false : true} + autoFocus={isMobile ? false : autoFocusTextBox ? true : false} />
diff --git a/packages/fakeyou.com/src/v2/view/pages/landing/storyteller/OnboardingSelection/OnboardingSelection.tsx b/packages/fakeyou.com/src/v2/view/pages/landing/storyteller/OnboardingSelection/OnboardingSelection.tsx new file mode 100644 index 0000000000..7620cc1ded --- /dev/null +++ b/packages/fakeyou.com/src/v2/view/pages/landing/storyteller/OnboardingSelection/OnboardingSelection.tsx @@ -0,0 +1,67 @@ +import { Panel } from "components/common"; +import Card from "components/common/Card"; +import React from "react"; +import { useHistory } from "react-router-dom"; + +interface OnboardingSelectionProps {} + +export default function OnboardingSelection(props: OnboardingSelectionProps) { + const history = useHistory(); + + const cards = [ + { + text: "Scene 1", + backgroundImage: "", + onClick: () => { + history.push("/studio-intro/"); + }, + }, + { + text: "Scene 2", + backgroundImage: "", + onClick: () => { + history.push("/studio-intro/"); + }, + }, + { + text: "Scene 3", + backgroundImage: "", + onClick: () => { + history.push("/studio-intro/"); + }, + }, + { + text: "Scene 4", + backgroundImage: "", + onClick: () => { + history.push("/studio-intro/"); + }, + }, + ]; + + return ( + +
+

Explore scene examples

+

+ Try generating a movie by clicking a scene below +

+
+ {cards.map((card, index) => ( +
+ +
+ ))} +
+
+
+ ); +} diff --git a/packages/fakeyou.com/src/v2/view/pages/landing/storyteller/OnboardingSelection/index.ts b/packages/fakeyou.com/src/v2/view/pages/landing/storyteller/OnboardingSelection/index.ts new file mode 100644 index 0000000000..bcd45e84f4 --- /dev/null +++ b/packages/fakeyou.com/src/v2/view/pages/landing/storyteller/OnboardingSelection/index.ts @@ -0,0 +1 @@ +export { default } from "./OnboardingSelection"; diff --git a/packages/fakeyou.com/src/v2/view/pages/landing/storyteller/StorytellerLanding.tsx b/packages/fakeyou.com/src/v2/view/pages/landing/storyteller/StorytellerLanding.tsx index f388851c2c..dc57614981 100644 --- a/packages/fakeyou.com/src/v2/view/pages/landing/storyteller/StorytellerLanding.tsx +++ b/packages/fakeyou.com/src/v2/view/pages/landing/storyteller/StorytellerLanding.tsx @@ -31,220 +31,5 @@ export default function StorytellerLanding({ }: StorytellerLandingProps) { // const { t } = useLocalize("LandingPage"); - return ( - <> - {/* - - - {/* - -
-
-
-
- -
- background dots -
-
-
-

- Enabling Anyone to Create Quality Movies with AI -

-

- We are combining generative AI and User Generated Content to - radically democratize both audio and video production. -

-
-
-
-
-
-
*/} - - {/*
- - -
-
-

AI Audio Generation

-

{t("ctaText")}

-
- hanashi before - red chevrons - hanashi after -
- hanashi before - grey chevrons - red chevrons - hanashi after -
-
-
-
- -
-

- - Text to Speech -

- - Try more voices{" "} - - -
- -
- -
-
-
-
-
-
- -
-

- - Voice to Voice -

- - Try more voices{" "} - - -
-
-