From 30ec05948bb4216c835ba2d3382826ba440297a9 Mon Sep 17 00:00:00 2001 From: MyNameIsBatman Date: Sun, 20 Jun 2021 04:39:47 -0300 Subject: [PATCH] Camera --- .../room/widgets/camera/CameraWidgetView.scss | 12 +- .../room/widgets/camera/CameraWidgetView.tsx | 22 ++- .../camera/context/CameraWidgetContext.tsx | 18 +++ .../context/CameraWidgetContext.types.ts | 15 ++ .../views/capture/CameraWidgetCaptureView.tsx | 65 ++++---- .../capture/CameraWidgetCaptureView.types.ts | 2 +- .../views/editor/CameraWidgetEditorView.tsx | 141 +++++++++++++++--- .../editor/CameraWidgetEditorView.types.ts | 2 +- 8 files changed, 210 insertions(+), 67 deletions(-) create mode 100644 src/views/room/widgets/camera/context/CameraWidgetContext.tsx create mode 100644 src/views/room/widgets/camera/context/CameraWidgetContext.types.ts diff --git a/src/views/room/widgets/camera/CameraWidgetView.scss b/src/views/room/widgets/camera/CameraWidgetView.scss index e24d2e21..337ae3d0 100644 --- a/src/views/room/widgets/camera/CameraWidgetView.scss +++ b/src/views/room/widgets/camera/CameraWidgetView.scss @@ -7,8 +7,8 @@ .camera-frame { position: absolute; - width: 318px; - height: 318px; + width: 320px; + height: 320px; margin-top: 9px; margin-left: 11.4px; @@ -60,7 +60,7 @@ width: 600px; .effects { - max-height: 354px; + max-height: 363px; .effect-thumbnail { border-color: $grid-border-color !important; @@ -72,8 +72,6 @@ } .effect-thumbnail-image { - background: black; - img { width: 56px; height: 56px; @@ -83,8 +81,8 @@ } .picture-preview { - width: 318px; - height: 318px; + width: 320px; + height: 320px; &.zoomed { background-size: 636px; diff --git a/src/views/room/widgets/camera/CameraWidgetView.tsx b/src/views/room/widgets/camera/CameraWidgetView.tsx index 5133fa5e..e20574ad 100644 --- a/src/views/room/widgets/camera/CameraWidgetView.tsx +++ b/src/views/room/widgets/camera/CameraWidgetView.tsx @@ -1,21 +1,24 @@ import { RoomCameraWidgetManagerEvent } from 'nitro-renderer/src/nitro/camera/events/RoomCameraWidgetManagerEvent'; import { FC, useCallback, useEffect, useMemo, useState } from 'react'; +import { IRoomCameraWidgetSelectedEffect } from '../../../../../../nitro-renderer/src/nitro/camera/IRoomCameraWidgetSelectedEffect'; import { GetRoomCameraWidgetManager } from '../../../../api'; import { RoomWidgetCameraEvent } from '../../../../events/room-widgets/camera/RoomWidgetCameraEvent'; import { useCameraEvent } from '../../../../hooks/events/nitro/camera/camera-event'; import { useUiEvent } from '../../../../hooks/events/ui/ui-event'; -import { useRoomContext } from '../../context/RoomContext'; import { CameraWidgetViewProps } from './CameraWidgetView.types'; +import { CameraWidgetContextProvider } from './context/CameraWidgetContext'; import { CameraWidgetCaptureView } from './views/capture/CameraWidgetCaptureView'; import { CameraWidgetEditorView } from './views/editor/CameraWidgetEditorView'; export const CameraWidgetView: FC = props => { - const { eventDispatcher = null, widgetHandler = null } = useRoomContext(); const [ effectsReady, setEffectsReady ] = useState(false); const [ isCaptureVisible, setIsCaptureVisible ] = useState(false); const [ isEditorVisible, setIsEditorVisible ] = useState(false); - const [ chosenPicture, setChosenPicture ] = useState(null); + + const [ cameraRoll, setCameraRoll ] = useState([]); + const [ selectedPictureIndex, setSelectedPictureIndex ] = useState(-1); + const [ selectedEffects, setSelectedEffects ] = useState([]); const onNitroEvent = useCallback((event: RoomWidgetCameraEvent) => { @@ -73,16 +76,21 @@ export const CameraWidgetView: FC = props => setIsCaptureVisible(false); setIsEditorVisible(false); return; - case 'capture_choose_picture': - setChosenPicture(value); + case 'capture_edit': setIsCaptureVisible(false); setIsEditorVisible(true); return; + case 'editor_cancel': + setIsCaptureVisible(true); + setIsEditorVisible(false); + return; } }, []); return ( - ( isCaptureVisible && processAction('close') } onChoosePicture={ (picture) => processAction('capture_choose_picture', picture) } /> ) || - ( isEditorVisible && processAction('close') } picture={ chosenPicture } availableEffects={ availableEffects } /> ) + + { ( isCaptureVisible && processAction('close') } onEditClick={ () => processAction('capture_edit') } /> ) || + ( isEditorVisible && processAction('close') } onCancelClick={ () => processAction('editor_cancel') } availableEffects={ availableEffects } /> ) } + ); } diff --git a/src/views/room/widgets/camera/context/CameraWidgetContext.tsx b/src/views/room/widgets/camera/context/CameraWidgetContext.tsx new file mode 100644 index 00000000..55d7ba6e --- /dev/null +++ b/src/views/room/widgets/camera/context/CameraWidgetContext.tsx @@ -0,0 +1,18 @@ +import { createContext, FC, useContext } from 'react'; +import { CameraWidgetContextProps, ICameraWidgetContext } from './CameraWidgetContext.types'; + +const CameraWidgetContext = createContext({ + cameraRoll: null, + setCameraRoll: null, + selectedPictureIndex: null, + setSelectedPictureIndex: null, + selectedEffects: null, + setSelectedEffects: null +}); + +export const CameraWidgetContextProvider: FC = props => +{ + return { props.children } +} + +export const useCameraWidgetContext = () => useContext(CameraWidgetContext); diff --git a/src/views/room/widgets/camera/context/CameraWidgetContext.types.ts b/src/views/room/widgets/camera/context/CameraWidgetContext.types.ts new file mode 100644 index 00000000..af69cc2b --- /dev/null +++ b/src/views/room/widgets/camera/context/CameraWidgetContext.types.ts @@ -0,0 +1,15 @@ +import { ProviderProps } from 'react'; +import { IRoomCameraWidgetSelectedEffect } from '../../../../../../../nitro-renderer/src/nitro/camera/IRoomCameraWidgetSelectedEffect'; + +export interface ICameraWidgetContext +{ + cameraRoll: HTMLImageElement[], + setCameraRoll: (cameraRoll: HTMLImageElement[]) => void, + selectedPictureIndex: number, + setSelectedPictureIndex: (index: number) => void, + selectedEffects: IRoomCameraWidgetSelectedEffect[], + setSelectedEffects: (selectedEffects: IRoomCameraWidgetSelectedEffect[]) => void +} + +export interface CameraWidgetContextProps extends ProviderProps +{} diff --git a/src/views/room/widgets/camera/views/capture/CameraWidgetCaptureView.tsx b/src/views/room/widgets/camera/views/capture/CameraWidgetCaptureView.tsx index e57f2626..a3433ac7 100644 --- a/src/views/room/widgets/camera/views/capture/CameraWidgetCaptureView.tsx +++ b/src/views/room/widgets/camera/views/capture/CameraWidgetCaptureView.tsx @@ -1,25 +1,27 @@ import classNames from 'classnames'; import { NitroRectangle } from 'nitro-renderer'; -import { FC, useCallback, useRef, useState } from 'react'; +import { FC, useCallback, useRef } from 'react'; import { GetRoomEngine } from '../../../../../../api/nitro/room/GetRoomEngine'; import { GetRoomSession } from '../../../../../../api/nitro/session/GetRoomSession'; import { DraggableWindow } from '../../../../../../hooks/draggable-window/DraggableWindow'; import { LocalizeText } from '../../../../../../utils/LocalizeText'; +import { useCameraWidgetContext } from '../../context/CameraWidgetContext'; import { CameraWidgetCaptureViewProps } from './CameraWidgetCaptureView.types'; export const CameraWidgetCaptureView: FC = props => { - const CAMERA_ROLL_LIMIT: number = 5; + const { onCloseClick = null, onEditClick = null } = props; - const [ picturesTaken, setPicturesTaken ] = useState([]); - const [ selectedPictureIndex, setSelectedPictureIndex ] = useState(-1); - const cameraFrameRef = useRef(); + const CAMERA_ROLL_LIMIT: number = 5; + const cameraFrameRef = useRef(); + + const cameraWidgetContext = useCameraWidgetContext(); const takePicture = useCallback(() => { - if(selectedPictureIndex > -1) + if(cameraWidgetContext.selectedPictureIndex > -1) { - setSelectedPictureIndex(-1); + cameraWidgetContext.setSelectedPictureIndex(-1); return; } @@ -31,20 +33,20 @@ export const CameraWidgetCaptureView: FC = props = const image = GetRoomEngine().createRoomScreenshot(GetRoomSession().roomId, 1, rectangle); - if(picturesTaken.length + 1 === CAMERA_ROLL_LIMIT) + if(cameraWidgetContext.cameraRoll.length + 1 === CAMERA_ROLL_LIMIT) { alert(LocalizeText('camera.full.body')); } - if(picturesTaken.length === CAMERA_ROLL_LIMIT) + let remainingRoll = cameraWidgetContext.cameraRoll; + + if(cameraWidgetContext.cameraRoll.length === CAMERA_ROLL_LIMIT) { - setPicturesTaken(picturesTaken => [ ...picturesTaken.slice(0, CAMERA_ROLL_LIMIT - 1), image ]); + remainingRoll = remainingRoll.slice(0, CAMERA_ROLL_LIMIT - 1); } - else - { - setPicturesTaken(picturesTaken => [ ...picturesTaken, image ]); - } - }, [ picturesTaken, selectedPictureIndex ]); + + cameraWidgetContext.setCameraRoll([ ...remainingRoll, image ]); + }, [ cameraWidgetContext.cameraRoll, cameraWidgetContext.selectedPictureIndex ]); const processAction = useCallback((type: string, value: string | number = null) => { @@ -54,32 +56,37 @@ export const CameraWidgetCaptureView: FC = props = takePicture(); return; case 'preview_picture': - setSelectedPictureIndex(Number(value)); + cameraWidgetContext.setSelectedPictureIndex(Number(value)); return; case 'discard_picture': - setSelectedPictureIndex(-1); - const newPicturesTaken = picturesTaken; - picturesTaken.splice(selectedPictureIndex, 1); - setPicturesTaken(newPicturesTaken); + cameraWidgetContext.setSelectedPictureIndex(-1); + + const clone = Array.from(cameraWidgetContext.cameraRoll); + clone.splice(cameraWidgetContext.selectedPictureIndex, 1); + + cameraWidgetContext.setCameraRoll(clone); return; case 'edit_picture': - props.onChoosePicture(picturesTaken[selectedPictureIndex]); + onEditClick(); + return; + case 'close': + onCloseClick(); return; } - }, [ picturesTaken, selectedPictureIndex ]); + }, [ cameraWidgetContext.selectedPictureIndex, cameraWidgetContext.cameraRoll, onEditClick, onCloseClick ]); return (
-
+
processAction('close') }>
-
-1}) }> - { selectedPictureIndex > -1 &&
- +
-1}) }> + { cameraWidgetContext.selectedPictureIndex > -1 &&
+
@@ -90,10 +97,10 @@ export const CameraWidgetCaptureView: FC = props =
- { picturesTaken.length > 0 &&
- { picturesTaken.map((picture, index) => + { cameraWidgetContext.cameraRoll.length > 0 &&
+ { cameraWidgetContext.cameraRoll.map((picture, index) => { - return processAction('preview_picture', index) } />; + return processAction('preview_picture', index) } />; }) }
}
diff --git a/src/views/room/widgets/camera/views/capture/CameraWidgetCaptureView.types.ts b/src/views/room/widgets/camera/views/capture/CameraWidgetCaptureView.types.ts index 435e6d29..84f6d560 100644 --- a/src/views/room/widgets/camera/views/capture/CameraWidgetCaptureView.types.ts +++ b/src/views/room/widgets/camera/views/capture/CameraWidgetCaptureView.types.ts @@ -1,5 +1,5 @@ export interface CameraWidgetCaptureViewProps { onCloseClick: () => void; - onChoosePicture: (picture: HTMLImageElement) => void; + onEditClick: () => void; } diff --git a/src/views/room/widgets/camera/views/editor/CameraWidgetEditorView.tsx b/src/views/room/widgets/camera/views/editor/CameraWidgetEditorView.tsx index f1d41d12..47ae9b5b 100644 --- a/src/views/room/widgets/camera/views/editor/CameraWidgetEditorView.tsx +++ b/src/views/room/widgets/camera/views/editor/CameraWidgetEditorView.tsx @@ -1,5 +1,4 @@ import classNames from 'classnames'; -import { IRoomCameraWidgetSelectedEffect } from 'nitro-renderer/src/nitro/camera/IRoomCameraWidgetSelectedEffect'; import { RoomCameraWidgetSelectedEffect } from 'nitro-renderer/src/nitro/camera/RoomCameraWidgetSelectedEffect'; import { FC, useCallback, useEffect, useState } from 'react'; import { GetRoomCameraWidgetManager } from '../../../../../../api'; @@ -9,18 +8,22 @@ import { NitroCardView } from '../../../../../../layout/card/NitroCardView'; import { NitroCardTabsView } from '../../../../../../layout/card/tabs/NitroCardTabsView'; import { NitroCardTabsItemView } from '../../../../../../layout/card/tabs/tabs-item/NitroCardTabsItemView'; import { LocalizeText } from '../../../../../../utils/LocalizeText'; +import { useCameraWidgetContext } from '../../context/CameraWidgetContext'; import { CameraWidgetEditorTabs, CameraWidgetEditorViewProps } from './CameraWidgetEditorView.types'; export const CameraWidgetEditorView: FC = props => { - const { picture = null, availableEffects = null, onCloseClick = null } = props; + const { availableEffects = null, onCloseClick = null, onCancelClick = null } = props; + const TABS: string[] = [ CameraWidgetEditorTabs.COLORMATRIX, CameraWidgetEditorTabs.COMPOSITE ]; const MY_LEVEL: number = 0; + + const cameraWidgetContext = useCameraWidgetContext(); - const [ currentTab, setCurrentTab ] = useState(CameraWidgetEditorTabs.COLORMATRIX); - const [ isZoomed, setIsZoomed ] = useState(false); - const [ selectedEffects, setSelectedEffects ] = useState([]); - const [ effectsThumbnails, setEffectThumbnails ] = useState<{name: string, image: HTMLImageElement}[]>([]); + const [ currentTab, setCurrentTab ] = useState(CameraWidgetEditorTabs.COLORMATRIX); + const [ selectedEffectName, setSelectedEffectName ] = useState(null); + const [ effectsThumbnails, setEffectsThumbnails ] = useState<{ name: string, image: HTMLImageElement }[]>([]); + const [ isZoomed, setIsZoomed ] = useState(false); useEffect(() => { @@ -32,11 +35,11 @@ export const CameraWidgetEditorView: FC = props => if(effect.colorMatrix) alpha = 0.5; - thumbnails.push({name: effect.name, image: GetRoomCameraWidgetManager().applyEffects(picture, [ new RoomCameraWidgetSelectedEffect(effect, alpha) ])}); + thumbnails.push({name: effect.name, image: GetRoomCameraWidgetManager().applyEffects(cameraWidgetContext.cameraRoll[cameraWidgetContext.selectedPictureIndex], [ new RoomCameraWidgetSelectedEffect(effect, alpha) ])}); } - setEffectThumbnails(thumbnails); - }, [ picture, availableEffects ]); + setEffectsThumbnails(thumbnails); + }, [ cameraWidgetContext.selectedPictureIndex, availableEffects ]); const getEffectThumbnail = useCallback((effectName: string) => { @@ -59,6 +62,50 @@ export const CameraWidgetEditorView: FC = props => } }, [ currentTab, availableEffects ]); + const getCurrentPicture = useCallback(() => + { + return GetRoomCameraWidgetManager().applyEffects(cameraWidgetContext.cameraRoll[cameraWidgetContext.selectedPictureIndex], cameraWidgetContext.selectedEffects); + }, [ cameraWidgetContext.selectedEffects ]); + + const getEffectRangeConfig = useCallback(() => + { + if(!selectedEffectName) return [0, 0]; + + const selectedEffect = cameraWidgetContext.selectedEffects.find(effect => effect.effect.name === selectedEffectName); + + if(!selectedEffect) return [0, 0]; + + let isColormatrix = selectedEffect.effect.colorMatrix != null; + + let max = 255; + let step = 1; + + if(isColormatrix) + { + max = 1; + step = 0.01; + } + + return [max, step, selectedEffect.alpha]; + }, [ selectedEffectName, cameraWidgetContext.selectedEffects ]); + + const setSelectedEffectAlpha = useCallback((newAlpha: number) => + { + if(!selectedEffectName) return; + + const selectedEffectIndex = cameraWidgetContext.selectedEffects.findIndex(effect => effect.effect.name === selectedEffectName); + + if(selectedEffectIndex === -1) return; + + const clone = Array.from(cameraWidgetContext.selectedEffects); + + const selectedEffect = clone[selectedEffectIndex]; + + clone[selectedEffectIndex] = new RoomCameraWidgetSelectedEffect(selectedEffect.effect, newAlpha); + + cameraWidgetContext.setSelectedEffects(clone); + }, [ selectedEffectName, cameraWidgetContext.selectedEffects ]); + const processAction = useCallback((type: string, value: string | number = null) => { switch(type) @@ -66,14 +113,60 @@ export const CameraWidgetEditorView: FC = props => case 'close': onCloseClick(); return; + case 'cancel': + onCancelClick(); + return; case 'change_tab': setCurrentTab(String(value)); return; + case 'select_effect': + let existingIndex = -1; + + if(cameraWidgetContext.selectedEffects.length > 0) + { + existingIndex = cameraWidgetContext.selectedEffects.findIndex(effect => effect.effect.name === value); + + /*if(existingIndex > -1) + { + const clone = Array.from(cameraWidgetContext.selectedEffects); + clone.splice(existingIndex, 1); + + cameraWidgetContext.setSelectedEffects(clone); + }*/ + } + + if(existingIndex === -1) + { + const effect = availableEffects.find(effect => effect.name === value); + + let alpha = 126; + + if(effect.colorMatrix) alpha = 0.5; + + cameraWidgetContext.setSelectedEffects([...cameraWidgetContext.selectedEffects, new RoomCameraWidgetSelectedEffect(effect, alpha)]); + } + + if(selectedEffectName !== value) + { + setSelectedEffectName(value); + } + else + { + setSelectedEffectName(null); + } + return; + case 'clear_effects': + setSelectedEffectName(null); + cameraWidgetContext.setSelectedEffects([]); + return; + case 'download': + window.open(getCurrentPicture().src, '_blank'); + return; case 'zoom': setIsZoomed(isZoomed => !isZoomed); return; } - }, [ onCloseClick ]); + }, [ onCloseClick, onCancelClick, availableEffects, cameraWidgetContext.selectedEffects, selectedEffectName ]); return ( @@ -92,7 +185,7 @@ export const CameraWidgetEditorView: FC = props => { getEffectList().map(effect => { return ( -
+
processAction('select_effect', effect.name) }>
@@ -108,22 +201,26 @@ export const CameraWidgetEditorView: FC = props =>
-
-
+
+ { selectedEffectName &&
+
{ LocalizeText('camera.effect.name.' + selectedEffectName) + ' - ' + getEffectRangeConfig()[2] }
+ setSelectedEffectAlpha(Number(event.target.value)) } className="form-range w-100" /> +
}
-
- - +
+
+ + + +
+
+ + +
- -
- - -
-
); } diff --git a/src/views/room/widgets/camera/views/editor/CameraWidgetEditorView.types.ts b/src/views/room/widgets/camera/views/editor/CameraWidgetEditorView.types.ts index fae983d8..69c6512a 100644 --- a/src/views/room/widgets/camera/views/editor/CameraWidgetEditorView.types.ts +++ b/src/views/room/widgets/camera/views/editor/CameraWidgetEditorView.types.ts @@ -2,9 +2,9 @@ import { IRoomCameraWidgetEffect } from 'nitro-renderer/src/nitro/camera/IRoomCa export interface CameraWidgetEditorViewProps { - picture: HTMLImageElement; availableEffects: IRoomCameraWidgetEffect[]; onCloseClick: () => void; + onCancelClick: () => void; } export class CameraWidgetEditorTabs