Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions apps/monk-test-app/src/views/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ import { i18nCamera } from '@monkvision/camera-web';
import { useI18nLink } from '@monkvision/common';
import React from 'react';
import { useTranslation } from 'react-i18next';
import { CameraView } from './CameraView';
// import { TestView } from './TestView';
// import { CameraView } from './CameraView';
import { TestView } from './TestView';

export function App() {
const { i18n } = useTranslation();
useI18nLink(i18n, [i18nCamera]);

return <CameraView />;
return <TestView />;
}
8 changes: 6 additions & 2 deletions apps/monk-test-app/src/views/TestView/TestView.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import React from 'react';
import './TestView.css';
import { CaptureHUDButtons } from '@monkvision/inspection-capture-web';
import { PhotoCapture } from '@monkvision/inspection-capture-web';
import { sights } from '@monkvision/sights';

const sightsArray = Object.values(sights)
.filter((value) => value.category === 'exterior')
.slice(0, 5);
export function TestView() {
return (
<div className='test-view-container'>
<CaptureHUDButtons />
<PhotoCapture sights={sightsArray} />
</div>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ export const PhotoCapture = i18nWrap(({ sights }: PhotoCaptureProps) => {
});
const hud = (props: CameraHUDProps) => <PhotoCaptureHUD sights={sights} {...props} />;

const handleTakePicture = (picture: MonkPicture) => {
console.log('Picture Taken :', picture);
};

return (
<div style={styles['container']}>
<Camera
Expand All @@ -34,7 +38,7 @@ export const PhotoCapture = i18nWrap(({ sights }: PhotoCaptureProps) => {
resolution={cameraState.resolution}
format={cameraState.compressionFormat}
quality={Number(cameraState.quality)}
onPictureTaken={(picture: MonkPicture) => console.log('Picture Taken :', picture)}
onPictureTaken={handleTakePicture}
/>
</div>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,25 +1,61 @@
import { useMemo, useState } from 'react';
import { useMemo } from 'react';
import { Sight } from '@monkvision/types';
import { CameraHUDProps } from '@monkvision/camera-web/lib/Camera/CameraHUD.types';
import { PhotoCaptureHUDButtons } from './PhotoCaptureHUDButtons';
import { PhotoCaptureHUDAddDamagePreview } from './PhotoCaptureHUDAddDamagePreview';
import { PhotoCaptureHUDSightPreview } from './PhotoCaptureHUDSightPreview';
import { HUDMode, usePhotoCaptureHUDStyle } from './hook';
import { useSightState } from './hooks';
import { AddDamagePreviewMode, useSightState } from './hooks';

export interface PhotoCaptureHUDProps extends CameraHUDProps {
sights: Sight[];
}

/**
* Displays the camera preview and HUD components for capturing photos with sights.
*
* @component
*
* @param {Sight[]} sights - The array of sights available for selection.
*
* @example
* // Example usage of PhotoCaptureHUD component in Camera Component:
* import { Camera } from '@monkvision/camera-web';
* import { PhotoCaptureHUD } from '@monkvision/inspection-camera-web';
*
* export Function MyComponent () {
* const [sights, setSights] = useState<sights[]>([]);
* const hud = (props: CameraHUDProps) => <PhotoCaptureHUD sights={sights} {...props} />;
*
* return (
* <Camera HUDComponent={hud} ...props />
* );
*/
export function PhotoCaptureHUD({ sights, cameraPreview, handle }: PhotoCaptureHUDProps) {
const [mode, setMode] = useState<HUDMode>(HUDMode.DEFAULT);
const { selectedSight, setSelectedSight, sightsTaken, handleSightTaken } = useSightState(sights);
const {
selectedSight,
setSelectedSight,
sightsTaken,
handleSightTaken,
mode,
setMode,
addDamagePreviewMode,
setAddDamagePreviewMode,
} = useSightState(sights);
const style = usePhotoCaptureHUDStyle();

const hudPreview = useMemo(
() =>
mode === HUDMode.ADD_DAMAGE ? (
<PhotoCaptureHUDAddDamagePreview onCancel={() => setMode(HUDMode.DEFAULT)} />
<PhotoCaptureHUDAddDamagePreview
onCancel={() => {
setMode(HUDMode.DEFAULT);
setAddDamagePreviewMode(AddDamagePreviewMode.DEFAULT);
}}
sight={selectedSight}
addDamagePreviewMode={addDamagePreviewMode}
streamDimensions={handle?.dimensions}
/>
) : (
<PhotoCaptureHUDSightPreview
sights={sights}
Expand All @@ -30,7 +66,7 @@ export function PhotoCaptureHUD({ sights, cameraPreview, handle }: PhotoCaptureH
streamDimensions={handle?.dimensions}
/>
),
[mode, selectedSight, sightsTaken, handle?.dimensions],
[mode, selectedSight, sightsTaken, handle?.dimensions, addDamagePreviewMode],
);
return (
<div style={style.container}>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Button } from '@monkvision/common-ui-web';
import { useTranslation } from 'react-i18next';
import { usePhotoHUDButtonBackground } from '../../hooks';

export interface CancelButtonProps {
onCancel: () => void;
}

export function CancelButton({ onCancel }: CancelButtonProps) {
const { t } = useTranslation();
const { bgColor } = usePhotoHUDButtonBackground();

return (
<Button onClick={onCancel} style={{ backgroundColor: bgColor }}>
{t('photo.hud.addDamage.cancelBtn')}
</Button>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './CancelButton';
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { Styles } from '@monkvision/types';

export const styles: Styles = {
container: {
position: 'absolute',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
flexDirection: 'column',
top: '0',
right: '0',
left: '0',
bottom: '0',
},
top: {
position: 'absolute',
display: 'flex',
alignSelf: 'stretch',
flexDirection: 'row',
justifyContent: 'space-between',
margin: '10px',
zIndex: '9',
top: '0',
right: '0',
left: '0',
},
frameContainer: {
position: 'absolute',
width: '100%',
},
frame: {
position: 'absolute',
top: '25%',
left: '32%',
width: '36%',
height: '50%',
border: '2px solid #FFC000',
borderRadius: '10px',
boxShadow: '0px 0px 0px 100pc rgba(0, 0, 0, 0.5)',
},
label: {
position: 'absolute',
top: '0',
color: 'white',
margin: '20px',
padding: '10px 24px',
},
labelPortrait: {
__media: { portrait: true },
top: '10%',
},
infoCloseup: {
position: 'absolute',
bottom: '0',
color: 'white',
margin: '20px',
padding: '10px 24px',
textAlign: 'center',
},
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { PixelDimensions, Sight } from '@monkvision/types';
import { useTranslation } from 'react-i18next';
import { useSightLabel } from '@monkvision/common';
import { labels } from '@monkvision/sights';
import { styles } from './CloseupPreview.styles';
import { CancelButton } from '../CancelButton';
import { DamageCounter } from '../DamageCounter';
import { useCloseupPreviewStyle } from './hook';
import { AddDamagePreviewMode } from '../../hooks';

export interface CloseupPreviewProps {
sight?: Sight | undefined;
onCancel: () => void;
streamDimensions?: PixelDimensions | null;
}

export function CloseupPreview({ sight, onCancel, streamDimensions }: CloseupPreviewProps) {
const { t } = useTranslation();
const { label } = useSightLabel({ labels });
const style = useCloseupPreviewStyle();

const sightLabel = sight && label(sight);
const aspectRatio = streamDimensions
? `${streamDimensions?.width}/${streamDimensions?.height}`
: '16/9';

return (
<div style={styles['container']}>
<div style={{ ...styles['frameContainer'], aspectRatio }} data-testid='frame-container'>
<div style={styles['frame']} />
</div>
<div style={styles['top']}>
<DamageCounter addDamagePreviewMode={AddDamagePreviewMode.CLOSEUP_PREVIEW} />
<CancelButton onCancel={onCancel} />
</div>
<div style={style.label} data-testid='sight-label'>
{sightLabel}
</div>
<div style={styles['infoCloseup']}>{t('photo.hud.addDamage.infoCloseup')}</div>
</div>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { useResponsiveStyle } from '@monkvision/common';
import { styles } from './CloseupPreview.styles';

export function useCloseupPreviewStyle() {
const { responsive } = useResponsiveStyle();
return {
label: {
...styles['label'],
...responsive(styles['labelPortrait']),
},
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './CloseupPreview';
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { Styles } from '@monkvision/types';

export const styles: Styles = {
container: {
position: 'absolute',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
flexDirection: 'column',
top: '0',
right: '0',
left: '0',
bottom: '0',
},
top: {
position: 'absolute',
display: 'flex',
alignSelf: 'stretch',
flexDirection: 'row',
justifyContent: 'space-between',
margin: '10px',
zIndex: '9',
top: '0',
right: '0',
left: '0',
},
infoBtn: {
position: 'absolute',
margin: '20px',
bottom: '0',
},
svg: {
width: '15%',
},
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { Button, DynamicSVG } from '@monkvision/common-ui-web';
import { useTranslation } from 'react-i18next';
import { useState } from 'react';
import { AddDamagePreviewMode, usePhotoHUDButtonBackground } from '../../hooks';
import { styles } from './CrosshairPreview.styles';
import { DamageCounter } from '../DamageCounter';
import { CancelButton } from '../CancelButton';

export interface CrosshairPreviewProps {
onCancel: () => void;
}

export function CrosshairPreview({ onCancel }: CrosshairPreviewProps) {
const [showInfoBtn, setShowInfoBtn] = useState(true);

const { t } = useTranslation();
const { bgColor } = usePhotoHUDButtonBackground();

const svg =
'<svg fill="none" viewBox="0 0 108 109" xmlns="http://www.w4.org/2000/svg"><g filter="url(#svgtest_svg__a)"><mask id="svgtest_svg__b" width="98" height="98" x="5" y="4" fill="#000" maskUnits="userSpaceOnUse"><path fill="#fff" d="M5 4h98v98H5z"/><path fill-rule="evenodd" d="M52 7a2 2 0 1 1 4 0v16a2 2 0 1 1-4 0V7Zm0 76a2 2 0 1 1 4 0v16a2 2 0 1 1-4 0V83ZM8 51a2 2 0 1 0 0 4h16a2 2 0 1 0 0-4H8Zm74 2a2 2 0 0 1 2-2h16a2 2 0 1 1 0 4H84a2 2 0 0 1-2-2Zm-28-2a2 2 0 1 0 0 4 2 2 0 0 0 0-4Z" clip-rule="evenodd"/></mask><path fill="#FFC000" fill-rule="evenodd" d="M52 7a2 2 0 1 1 4 0v16a2 2 0 1 1-4 0V7Zm0 76a2 2 0 1 1 4 0v16a2 2 0 1 1-4 0V83ZM8 51a2 2 0 1 0 0 4h16a2 2 0 1 0 0-4H8Zm74 2a2 2 0 0 1 2-2h16a2 2 0 1 1 0 4H84a2 2 0 0 1-2-2Zm-28-2a2 2 0 1 0 0 4 2 2 0 0 0 0-4Z" clip-rule="evenodd"/><path fill="#795900" d="M54 4.75A2.25 2.25 0 0 0 51.75 7h.5c0-.966.783-1.75 1.75-1.75v-.5ZM56.25 7A2.25 2.25 0 0 0 54 4.75v.5c.967 0 1.75.784 1.75 1.75h.5Zm0 16V7h-.5v16h.5ZM54 25.25A2.25 2.25 0 0 0 56.25 23h-.5A1.75 1.75 0 0 1 54 24.75v.5ZM51.75 23A2.25 2.25 0 0 0 54 25.25v-.5A1.75 1.75 0 0 1 52.25 23h-.5Zm0-16v16h.5V7h-.5ZM54 80.75A2.25 2.25 0 0 0 51.75 83h.5c0-.966.783-1.75 1.75-1.75v-.5ZM56.25 83A2.25 2.25 0 0 0 54 80.75v.5c.967 0 1.75.784 1.75 1.75h.5Zm0 16V83h-.5v16h.5ZM54 101.25A2.25 2.25 0 0 0 56.25 99h-.5a1.75 1.75 0 0 1-1.75 1.75v.5ZM51.75 99a2.25 2.25 0 0 0 2.25 2.25v-.5A1.75 1.75 0 0 1 52.25 99h-.5Zm0-16v16h.5V83h-.5ZM6.25 53c0-.967.784-1.75 1.75-1.75v-.5A2.25 2.25 0 0 0 5.75 53h.5ZM8 54.75A1.75 1.75 0 0 1 6.25 53h-.5A2.25 2.25 0 0 0 8 55.25v-.5Zm16 0H8v.5h16v-.5ZM25.75 53A1.75 1.75 0 0 1 24 54.75v.5A2.25 2.25 0 0 0 26.25 53h-.5ZM24 51.25c.966 0 1.75.783 1.75 1.75h.5A2.25 2.25 0 0 0 24 50.75v.5Zm-16 0h16v-.5H8v.5Zm76-.5A2.25 2.25 0 0 0 81.75 53h.5c0-.967.784-1.75 1.75-1.75v-.5Zm16 0H84v.5h16v-.5Zm2.25 2.25a2.25 2.25 0 0 0-2.25-2.25v.5c.966 0 1.75.783 1.75 1.75h.5ZM100 55.25a2.25 2.25 0 0 0 2.25-2.25h-.5a1.75 1.75 0 0 1-1.75 1.75v.5Zm-16 0h16v-.5H84v.5ZM81.75 53A2.25 2.25 0 0 0 84 55.25v-.5A1.75 1.75 0 0 1 82.25 53h-.5Zm-29.5 0c0-.967.783-1.75 1.75-1.75v-.5A2.25 2.25 0 0 0 51.75 53h.5ZM54 54.75A1.75 1.75 0 0 1 52.25 53h-.5A2.25 2.25 0 0 0 54 55.25v-.5ZM55.75 53A1.75 1.75 0 0 1 54 54.75v.5A2.25 2.25 0 0 0 56.25 53h-.5ZM54 51.25c.967 0 1.75.783 1.75 1.75h.5A2.25 2.25 0 0 0 54 50.75v.5Z" mask="url(#svgtest_svg__b)"/></g><defs><filter id="svgtest_svg__a" width="106.5" height="107.5" x=".75" y=".75" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feColorMatrix in="SourceAlpha" result="hardAlpha" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/><feOffset dy="1"/><feGaussianBlur stdDeviation="2.5"/><feColorMatrix values="0 0 0 0 0.517647 0 0 0 0 0.517647 0 0 0 0 0.517647 0 0 0 0.2 0"/><feBlend in2="BackgroundImageFix" result="effect1_dropShadow_1083_28985"/><feColorMatrix in="SourceAlpha" result="hardAlpha" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/><feOffset dy="3"/><feGaussianBlur stdDeviation="2"/><feColorMatrix values="0 0 0 0 0.517647 0 0 0 0 0.517647 0 0 0 0 0.517647 0 0 0 0.12 0"/><feBlend in2="effect1_dropShadow_1083_28985" result="effect2_dropShadow_1083_28985"/><feColorMatrix in="SourceAlpha" result="hardAlpha" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/><feOffset dy="2"/><feGaussianBlur stdDeviation="2"/><feColorMatrix values="0 0 0 0 0.517647 0 0 0 0 0.517647 0 0 0 0 0.517647 0 0 0 0.14 0"/><feBlend in2="effect2_dropShadow_1083_28985" result="effect3_dropShadow_1083_28985"/><feBlend in="SourceGraphic" in2="effect3_dropShadow_1083_28985" result="shape"/></filter></defs></svg>';

return (
<div style={styles['container']}>
<DynamicSVG style={styles['svg']} svg={svg} />
<div style={styles['top']}>
<DamageCounter addDamagePreviewMode={AddDamagePreviewMode.DEFAULT} />
<CancelButton onCancel={onCancel} />
</div>
{showInfoBtn && (
<Button
icon='close'
style={{ ...styles['infoBtn'], backgroundColor: bgColor }}
onClick={() => setShowInfoBtn(false)}
>
{t('photo.hud.addDamage.infoBtn')}
</Button>
)}
</div>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './CrosshairPreview';
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { Styles } from '@monkvision/types';

export const styles: Styles = {
counter: {
display: 'flex',
color: 'white',
alignItems: 'center',
justifyContent: 'center',
padding: '6px 12px',
borderRadius: '8px',
zIndex: '9',
},
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { useTranslation } from 'react-i18next';
import { styles } from './DamageCounter.styles';
import { AddDamagePreviewMode, usePhotoHUDButtonBackground } from '../../hooks';

export interface DamageCounterProps {
addDamagePreviewMode: AddDamagePreviewMode;
}

export function DamageCounter({ addDamagePreviewMode }: DamageCounterProps) {
const { t } = useTranslation();
const { bgColor } = usePhotoHUDButtonBackground();

const previewMode = Object.values(AddDamagePreviewMode).indexOf(addDamagePreviewMode) + 1;
const totalDamage = Object.values(AddDamagePreviewMode).length;
const counterText =
addDamagePreviewMode === AddDamagePreviewMode.DEFAULT
? t('photo.hud.addDamage.damagedPart')
: t('photo.hud.addDamage.closeupPicture');

return (
<div
style={{ ...styles['counter'], backgroundColor: bgColor }}
data-testid={'damage-counter'}
>{`${previewMode} / ${totalDamage} • ${counterText}`}</div>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './DamageCounter';
Loading