From a3350b8b54afeaa9ebe72ae4faacef471d9ba85a Mon Sep 17 00:00:00 2001 From: Bill Date: Sat, 7 Aug 2021 19:05:31 -0400 Subject: [PATCH] More camera changes --- .../room/widgets/camera/CameraWidgetView.scss | 57 +++- .../room/widgets/camera/CameraWidgetView.tsx | 4 +- .../camera/common/CameraPictureThumbnail.ts | 6 + .../context/CameraWidgetContext.types.ts | 10 +- .../views/editor/CameraWidgetEditorView.tsx | 260 +++++++----------- .../editor/CameraWidgetEditorView.types.ts | 2 + .../CameraWidgetEffectListItemView.tsx | 29 ++ .../CameraWidgetEffectListItemView.types.ts | 11 + .../CameraWidgetEffectListView.tsx | 21 ++ .../CameraWidgetEffectListView.types.ts | 11 + tsconfig.json | 3 +- 11 files changed, 240 insertions(+), 174 deletions(-) create mode 100644 src/views/room/widgets/camera/common/CameraPictureThumbnail.ts create mode 100644 src/views/room/widgets/camera/views/editor/effect-list-item/CameraWidgetEffectListItemView.tsx create mode 100644 src/views/room/widgets/camera/views/editor/effect-list-item/CameraWidgetEffectListItemView.types.ts create mode 100644 src/views/room/widgets/camera/views/editor/effect-list/CameraWidgetEffectListView.tsx create mode 100644 src/views/room/widgets/camera/views/editor/effect-list/CameraWidgetEffectListView.types.ts diff --git a/src/views/room/widgets/camera/CameraWidgetView.scss b/src/views/room/widgets/camera/CameraWidgetView.scss index 52844d70..244d7be6 100644 --- a/src/views/room/widgets/camera/CameraWidgetView.scss +++ b/src/views/room/widgets/camera/CameraWidgetView.scss @@ -87,6 +87,53 @@ .nitro-camera-editor { width: 600px; + .content-area { + min-height: 350px; + height: 350px; + resize: vertical; + + .picture-preview { + width: 320px; + height: 320px; + + .slider { + background: linear-gradient(180deg, transparent, black); + text-shadow: 1px 1px rgba(0, 0, 0, .5); + } + } + } + + .effect-grid { + + .grid-item-container { + height: 60px !important; + max-height: 60px !important; + + .grid-item { + overflow: unset; + + .remove-effect { + position: absolute; + top: -1px; + right: -1px; + padding: 2px; + font-size: 11px; + line-height: 11px; + min-height: unset; + } + + .effect-thumbnail-image { + + img { + width: 40px; + height: 40px; + image-rendering: auto; + } + } + } + } + } + .effects { height: 363px; min-height: 363px; @@ -117,16 +164,6 @@ } } } - - .picture-preview { - width: 320px; - height: 320px; - - .slider { - background: linear-gradient(180deg, transparent, black); - text-shadow: 1px 1px rgba(0, 0, 0, .5); - } - } } .nitro-camera-checkout { diff --git a/src/views/room/widgets/camera/CameraWidgetView.tsx b/src/views/room/widgets/camera/CameraWidgetView.tsx index a8e2ef3e..85b535b9 100644 --- a/src/views/room/widgets/camera/CameraWidgetView.tsx +++ b/src/views/room/widgets/camera/CameraWidgetView.tsx @@ -24,7 +24,7 @@ export const CameraWidgetView: FC<{}> = props => const [ selectedPictureIndex, setSelectedPictureIndex ] = useState(-1); const [ selectedEffects, setSelectedEffects ] = useState([]); const [ isZoomed, setIsZoomed ] = useState(false); - const [ myLevel, setMyLevel ] = useState(10); + const [ myLevel, setMyLevel ] = useState(1); const [ price, setPrice ] = useState<{ credits: number, duckets: number, publishDucketPrice: number }>(null); const onNitroEvent = useCallback((event: RoomWidgetCameraEvent) => @@ -111,7 +111,7 @@ export const CameraWidgetView: FC<{}> = props => return ( { (mode === MODE_CAPTURE) && processAction('close') } onEdit={ () => processAction('edit') } onDelete={ () => processAction('delete') } /> } - { (mode === MODE_EDITOR) && processAction('close') } onCancel={ () => processAction('editor_cancel') } onCheckout={ () => processAction('checkout') } availableEffects={ availableEffects } /> } + { (mode === MODE_EDITOR) && processAction('close') } onCancel={ () => processAction('editor_cancel') } onCheckout={ () => processAction('checkout') } availableEffects={ availableEffects } /> } { (mode === MODE_CHECKOUT) && processAction('close') } onCancelClick={ () => processAction('editor_cancel') } price={ price }> } ); diff --git a/src/views/room/widgets/camera/common/CameraPictureThumbnail.ts b/src/views/room/widgets/camera/common/CameraPictureThumbnail.ts new file mode 100644 index 00000000..d88f9dcd --- /dev/null +++ b/src/views/room/widgets/camera/common/CameraPictureThumbnail.ts @@ -0,0 +1,6 @@ +export class CameraPictureThumbnail +{ + constructor( + public effectName: string, + public thumbnailUrl: string) {} +} diff --git a/src/views/room/widgets/camera/context/CameraWidgetContext.types.ts b/src/views/room/widgets/camera/context/CameraWidgetContext.types.ts index 8c6c4d82..b162cbb7 100644 --- a/src/views/room/widgets/camera/context/CameraWidgetContext.types.ts +++ b/src/views/room/widgets/camera/context/CameraWidgetContext.types.ts @@ -1,17 +1,17 @@ import { IRoomCameraWidgetSelectedEffect } from '@nitrots/nitro-renderer'; -import { ProviderProps } from 'react'; +import { Dispatch, ProviderProps, SetStateAction } from 'react'; import { CameraPicture } from '../common/CameraPicture'; export interface ICameraWidgetContext { cameraRoll: CameraPicture[], - setCameraRoll: (cameraRoll: CameraPicture[]) => void, + setCameraRoll: Dispatch>; selectedPictureIndex: number, - setSelectedPictureIndex: (index: number) => void, + setSelectedPictureIndex: Dispatch>; selectedEffects: IRoomCameraWidgetSelectedEffect[], - setSelectedEffects: (selectedEffects: IRoomCameraWidgetSelectedEffect[]) => void, + setSelectedEffects: Dispatch>; isZoomed: boolean, - setIsZoomed: (isZoomed: boolean) => void + setIsZoomed: Dispatch>; } export interface CameraWidgetContextProps extends ProviderProps diff --git a/src/views/room/widgets/camera/views/editor/CameraWidgetEditorView.tsx b/src/views/room/widgets/camera/views/editor/CameraWidgetEditorView.tsx index cce2c55c..8cb13259 100644 --- a/src/views/room/widgets/camera/views/editor/CameraWidgetEditorView.tsx +++ b/src/views/room/widgets/camera/views/editor/CameraWidgetEditorView.tsx @@ -1,94 +1,85 @@ -import { RoomCameraWidgetSelectedEffect } from '@nitrots/nitro-renderer'; -import classNames from 'classnames'; -import { FC, useCallback, useEffect, useState } from 'react'; +import { IRoomCameraWidgetSelectedEffect, RoomCameraWidgetSelectedEffect } from '@nitrots/nitro-renderer'; +import { FC, useCallback, useEffect, useMemo, useState } from 'react'; import { GetRoomCameraWidgetManager } from '../../../../../../api'; import { NitroCardContentView, NitroCardHeaderView, NitroCardTabsItemView, NitroCardTabsView, NitroCardView } from '../../../../../../layout'; import { LocalizeText } from '../../../../../../utils/LocalizeText'; -import { useCameraWidgetContext } from '../../context/CameraWidgetContext'; +import { CameraPictureThumbnail } from '../../common/CameraPictureThumbnail'; import { CameraWidgetEditorTabs, CameraWidgetEditorViewProps } from './CameraWidgetEditorView.types'; +import { CameraWidgetEffectListView } from './effect-list/CameraWidgetEffectListView'; const TABS: string[] = [ CameraWidgetEditorTabs.COLORMATRIX, CameraWidgetEditorTabs.COMPOSITE ]; export const CameraWidgetEditorView: FC = props => { - const { availableEffects = null, myLevel = 1, onClose = null, onCancel = null, onCheckout = null } = props; + const { picture = null, availableEffects = null, myLevel = 1, onClose = null, onCancel = null, onCheckout = null } = props; const [ currentTab, setCurrentTab ] = useState(TABS[0]); - const [ selectedEffectName, setSelectedEffectName ] = useState(null); - const [ effectsThumbnails, setEffectsThumbnails ] = useState<{ name: string, image: HTMLImageElement }[]>([]); - const { cameraRoll = null, selectedPictureIndex = -1, selectedEffects = null, isZoomed = false, setSelectedEffects = null, setIsZoomed = null } = useCameraWidgetContext(); + const [ selectedEffectName, setSelectedEffectName ] = useState(null); + const [ selectedEffects, setSelectedEffects ] = useState([]); + const [ effectsThumbnails, setEffectsThumbnails ] = useState([]); + const [ isZoomed, setIsZoomed ] = useState(false); - useEffect(() => + const getColorMatrixEffects = useMemo(() => { - const thumbnails = []; + return availableEffects.filter(effect => effect.colorMatrix); + }, [ availableEffects ]); - for(const effect of availableEffects) - { - thumbnails.push({name: effect.name, image: GetRoomCameraWidgetManager().applyEffects(cameraRoll[selectedPictureIndex].texture, [ new RoomCameraWidgetSelectedEffect(effect, 1) ], false)}); - } - - setEffectsThumbnails(thumbnails); - }, [ cameraRoll, selectedPictureIndex, availableEffects ]); - - const getEffectThumbnail = useCallback((effectName: string) => + const getCompositeEffects = useMemo(() => { - const search = effectsThumbnails.find(thumbnail => thumbnail.name === effectName); - - if(search) return search.image.src; - - return null; - }, [ effectsThumbnails ]); + return availableEffects.filter(effect => effect.texture); + }, [ availableEffects ]); const getEffectList = useCallback(() => { if(currentTab === CameraWidgetEditorTabs.COLORMATRIX) { - return availableEffects.filter(effect => effect.colorMatrix); + return getColorMatrixEffects; } - else - { - return availableEffects.filter(effect => effect.texture); - } - }, [ currentTab, availableEffects ]); - const getCurrentPicture = useCallback(() => + return getCompositeEffects; + }, [ currentTab, getColorMatrixEffects, getCompositeEffects ]); + + const getSelectedEffectIndex = useCallback((name: string) => { - return GetRoomCameraWidgetManager().applyEffects(cameraRoll[selectedPictureIndex].texture, selectedEffects, isZoomed); - }, [ cameraRoll, selectedPictureIndex, selectedEffects, isZoomed ]); + if(!name || !name.length || !selectedEffects || !selectedEffects.length) return -1; - const getCurrentEffectAlpha = useCallback(() => - { - if(!selectedEffectName) return 0; - - const selectedEffect = selectedEffects.find(effect => effect.effect.name === selectedEffectName); - - if(!selectedEffect) return 0; - - return selectedEffect.alpha; - }, [ selectedEffectName, selectedEffects ]); - - const getEffectIndex = useCallback((effectName) => - { - return selectedEffects.findIndex(effect => effect.effect.name === effectName); + return selectedEffects.findIndex(effect => (effect.effect.name === name)); }, [ selectedEffects ]) - const setSelectedEffectAlpha = useCallback((newAlpha: number) => + const getCurrentEffectIndex = useMemo(() => { - if(!selectedEffectName) return; + return getSelectedEffectIndex(selectedEffectName) + }, [ selectedEffectName, getSelectedEffectIndex ]) - const selectedEffectIndex = getEffectIndex(selectedEffectName); + const getCurrentEffect = useMemo(() => + { + if(!selectedEffectName) return null; - if(selectedEffectIndex === -1) return; + return (selectedEffects[getCurrentEffectIndex] || null); + }, [ selectedEffectName, getCurrentEffectIndex, selectedEffects ]); - const clone = Array.from(selectedEffects); + const setCurrentEffectAlpha = useCallback((alpha: number) => + { + const index = getCurrentEffectIndex; - const selectedEffect = clone[selectedEffectIndex]; + if(index === -1) return; - clone[selectedEffectIndex] = new RoomCameraWidgetSelectedEffect(selectedEffect.effect, newAlpha); + setSelectedEffects(prevValue => + { + const clone = [ ...prevValue ]; + const currentEffect = clone[index]; - setSelectedEffects(clone); - }, [ selectedEffectName, getEffectIndex, selectedEffects, setSelectedEffects ]); + clone[getCurrentEffectIndex] = new RoomCameraWidgetSelectedEffect(currentEffect.effect, alpha); - const processAction = useCallback((type: string, value: string | number = null) => + return clone; + }); + }, [ getCurrentEffectIndex, setSelectedEffects ]); + + const getCurrentPictureUrl = useMemo(() => + { + return GetRoomCameraWidgetManager().applyEffects(picture.texture, selectedEffects, isZoomed).src; + }, [ picture, selectedEffects, isZoomed ]); + + const processAction = useCallback((type: string, effectName: string = null) => { switch(type) { @@ -102,131 +93,88 @@ export const CameraWidgetEditorView: FC = props => onCheckout(); return; case 'change_tab': - setCurrentTab(String(value)); + setCurrentTab(String(effectName)); return; - case 'select_effect': - { - let existingIndex = -1; + case 'select_effect': { + let existingIndex = getSelectedEffectIndex(effectName); - if(selectedEffects.length > 0) - { - existingIndex = getEffectIndex(value); - } - - let effect = null; + if(existingIndex >= 0) return; + + const effect = availableEffects.find(effect => (effect.name === effectName)); - if(existingIndex === -1) - { - effect = availableEffects.find(effect => effect.name === value); - - if(effect.minLevel > myLevel) return; - - setSelectedEffects([...selectedEffects, new RoomCameraWidgetSelectedEffect(effect, 0.5)]); - } - - if(effect && effect.minLevel > myLevel) return; + if(!effect) return; - if(selectedEffectName !== value) + setSelectedEffects(prevValue => { - setSelectedEffectName(value); - } - else - { - setSelectedEffectName(null); - } - } + return [ ...prevValue, new RoomCameraWidgetSelectedEffect(effect, 1) ]; + }); + + setSelectedEffectName(effect.name); return; - case 'remove_effect': - { - const existingIndex = getEffectIndex(value); + } + case 'remove_effect': { + let existingIndex = getSelectedEffectIndex(effectName); - if(existingIndex > -1) + if(existingIndex === -1) return; + + setSelectedEffects(prevValue => { - const effect = selectedEffects[existingIndex]; + const clone = [ ...prevValue ]; - if(effect.effect.name === selectedEffectName) - { - setSelectedEffectName(null); - } - - const clone = Array.from(selectedEffects); clone.splice(existingIndex, 1); - - setSelectedEffects(clone); - } - } + + return clone; + }); + + if(selectedEffectName === effectName) setSelectedEffectName(null); return; + } case 'clear_effects': setSelectedEffectName(null); setSelectedEffects([]); return; case 'download': - window.open(getCurrentPicture().src, '_blank'); + //window.open(getCurrentPicture().src, '_blank'); return; case 'zoom': setIsZoomed(!isZoomed); return; } - }, [ onClose, onCancel, onCheckout, getCurrentPicture, myLevel, selectedEffectName, getEffectIndex, availableEffects, isZoomed, setIsZoomed, selectedEffects, setSelectedEffects ]); + }, [ isZoomed, availableEffects, getSelectedEffectIndex, selectedEffectName, onCancel, onCheckout, onClose, setIsZoomed, setSelectedEffects ]); + + useEffect(() => + { + const thumbnails: CameraPictureThumbnail[] = []; + + for(const effect of availableEffects) + { + thumbnails.push(new CameraPictureThumbnail(effect.name, GetRoomCameraWidgetManager().applyEffects(picture.texture, [ new RoomCameraWidgetSelectedEffect(effect, 1) ], false).src)); + } + + setEffectsThumbnails(thumbnails); + }, [ picture, availableEffects ]); return ( processAction('close') } /> -
-
- - { TABS.map(tab => - { - return processAction('change_tab', tab) }> - }) } - - -
-
- { getEffectList().map(effect => - { - return ( -
- { getEffectIndex(effect.name) > -1 && } -
processAction('select_effect', effect.name) } className={"effect-thumbnail cursor-pointer position-relative border border-2 rounded d-flex flex-column justify-content-center align-items-center py-1" + classNames({' active': selectedEffectName === effect.name})}> - { effect.minLevel <= myLevel &&
- -
} - { effect.minLevel > myLevel &&
-
-
{ effect.minLevel }
-
} -
-
- ); - }) } -
+ + { TABS.map(tab => + { + return processAction('change_tab', tab) }> + }) } + + +
+
+ +
+
+
+
- +
-
- - -
- { selectedEffectName &&
-
{ LocalizeText('camera.effect.name.' + selectedEffectName) + ' - ' + getCurrentEffectAlpha() }
- 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 6a58547a..c33ba910 100644 --- a/src/views/room/widgets/camera/views/editor/CameraWidgetEditorView.types.ts +++ b/src/views/room/widgets/camera/views/editor/CameraWidgetEditorView.types.ts @@ -1,7 +1,9 @@ import { IRoomCameraWidgetEffect } from '@nitrots/nitro-renderer'; +import { CameraPicture } from '../../common/CameraPicture'; export interface CameraWidgetEditorViewProps { + picture: CameraPicture; availableEffects: IRoomCameraWidgetEffect[]; myLevel: number; onClose: () => void; diff --git a/src/views/room/widgets/camera/views/editor/effect-list-item/CameraWidgetEffectListItemView.tsx b/src/views/room/widgets/camera/views/editor/effect-list-item/CameraWidgetEffectListItemView.tsx new file mode 100644 index 00000000..ae84972f --- /dev/null +++ b/src/views/room/widgets/camera/views/editor/effect-list-item/CameraWidgetEffectListItemView.tsx @@ -0,0 +1,29 @@ +import { FC } from 'react'; +import { NitroCardGridItemView } from '../../../../../../../layout/card/grid/item/NitroCardGridItemView'; +import { LocalizeText } from '../../../../../../../utils'; +import { CameraWidgetEffectListItemViewProps } from './CameraWidgetEffectListItemView.types'; + +export const CameraWidgetEffectListItemView: FC = props => +{ + const { effect = null, thumbnailUrl = null, isActive = false, isLocked = false, selectEffect = null, removeEffect = null } = props; + + return ( + (!isActive && selectEffect()) }> + { isActive && + } + { !isLocked && (thumbnailUrl && thumbnailUrl.length > 0) && +
+ +
} + { isLocked && +
+
+ +
+
{ effect.minLevel }
+
} +
+ ); +} diff --git a/src/views/room/widgets/camera/views/editor/effect-list-item/CameraWidgetEffectListItemView.types.ts b/src/views/room/widgets/camera/views/editor/effect-list-item/CameraWidgetEffectListItemView.types.ts new file mode 100644 index 00000000..19028be3 --- /dev/null +++ b/src/views/room/widgets/camera/views/editor/effect-list-item/CameraWidgetEffectListItemView.types.ts @@ -0,0 +1,11 @@ +import { IRoomCameraWidgetEffect } from '@nitrots/nitro-renderer'; + +export interface CameraWidgetEffectListItemViewProps +{ + effect: IRoomCameraWidgetEffect; + thumbnailUrl: string; + isActive: boolean; + isLocked: boolean; + selectEffect: () => void; + removeEffect: () => void; +} diff --git a/src/views/room/widgets/camera/views/editor/effect-list/CameraWidgetEffectListView.tsx b/src/views/room/widgets/camera/views/editor/effect-list/CameraWidgetEffectListView.tsx new file mode 100644 index 00000000..5f36ce7c --- /dev/null +++ b/src/views/room/widgets/camera/views/editor/effect-list/CameraWidgetEffectListView.tsx @@ -0,0 +1,21 @@ +import { FC } from 'react'; +import { NitroCardGridView } from '../../../../../../../layout/card/grid/NitroCardGridView'; +import { CameraWidgetEffectListItemView } from '../effect-list-item/CameraWidgetEffectListItemView'; +import { CameraWidgetEffectListViewProps } from './CameraWidgetEffectListView.types'; + +export const CameraWidgetEffectListView: FC = props => +{ + const { myLevel = 0, selectedEffects = [], effects = [], thumbnails = [], processAction = null } = props; + + return ( + + { effects && (effects.length > 0) && effects.map((effect, index) => + { + const thumbnailUrl = (thumbnails.find(thumbnail => (thumbnail.effectName === effect.name))); + const isActive = (selectedEffects.findIndex(selectedEffect => (selectedEffect.effect.name === effect.name)) > -1); + + return myLevel) } selectEffect={ () => processAction('select_effect', effect.name) } removeEffect={ () => processAction('remove_effect', effect.name) } /> + }) } + + ); +} diff --git a/src/views/room/widgets/camera/views/editor/effect-list/CameraWidgetEffectListView.types.ts b/src/views/room/widgets/camera/views/editor/effect-list/CameraWidgetEffectListView.types.ts new file mode 100644 index 00000000..7bdfccf1 --- /dev/null +++ b/src/views/room/widgets/camera/views/editor/effect-list/CameraWidgetEffectListView.types.ts @@ -0,0 +1,11 @@ +import { IRoomCameraWidgetEffect, IRoomCameraWidgetSelectedEffect } from '@nitrots/nitro-renderer'; +import { CameraPictureThumbnail } from '../../../common/CameraPictureThumbnail'; + +export interface CameraWidgetEffectListViewProps +{ + myLevel: number; + selectedEffects: IRoomCameraWidgetSelectedEffect[]; + effects: IRoomCameraWidgetEffect[]; + thumbnails: CameraPictureThumbnail[]; + processAction: (type: string, name: string) => void; +} diff --git a/tsconfig.json b/tsconfig.json index d05ec3bd..ac2a9fd7 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -23,6 +23,7 @@ "jsx": "react-jsx" }, "include": [ - "src" + "src", + "node_modules/@nitrots/nitro-renderer/src/**/*.ts", ] }