diff --git a/app/client/src/widgets/CameraWidget/component/index.tsx b/app/client/src/widgets/CameraWidget/component/index.tsx index bd1cf54599..4d3f63b6fa 100644 --- a/app/client/src/widgets/CameraWidget/component/index.tsx +++ b/app/client/src/widgets/CameraWidget/component/index.tsx @@ -46,7 +46,7 @@ import { import type { ThemeProp } from "widgets/constants"; import { isAirgapped } from "@appsmith/utils/airgapHelpers"; import { importSvg } from "design-system-old"; -import { getVideoConstraints } from "./utils"; +import { getVideoConstraints } from "../../utils"; const CameraOfflineIcon = importSvg( () => import("assets/icons/widget/camera/camera-offline.svg"), diff --git a/app/client/src/widgets/CameraWidget/constants.ts b/app/client/src/widgets/CameraWidget/constants.ts index 0c3fa5b2d0..c7f25c95b2 100644 --- a/app/client/src/widgets/CameraWidget/constants.ts +++ b/app/client/src/widgets/CameraWidget/constants.ts @@ -45,8 +45,3 @@ export enum DeviceTypes { CAMERA = "CAMERA", } export type DeviceType = keyof typeof DeviceTypes; - -export enum DefaultMobileCameraTypes { - FRONT = "user", - BACK = "environment", -} diff --git a/app/client/src/widgets/CameraWidget/widget/index.tsx b/app/client/src/widgets/CameraWidget/widget/index.tsx index 5312c8ec96..f2db943f60 100644 --- a/app/client/src/widgets/CameraWidget/widget/index.tsx +++ b/app/client/src/widgets/CameraWidget/widget/index.tsx @@ -7,16 +7,12 @@ import { base64ToBlob, createBlobUrl } from "utils/AppsmithUtils"; import type { DerivedPropertiesMap } from "utils/WidgetFactory"; import type { WidgetProps, WidgetState } from "widgets/BaseWidget"; import BaseWidget from "widgets/BaseWidget"; -import { FileDataTypes } from "widgets/constants"; +import { DefaultMobileCameraTypes, FileDataTypes } from "widgets/constants"; import type { SetterConfig, Stylesheet } from "entities/AppTheming"; import CameraComponent from "../component"; import type { CameraMode } from "../constants"; -import { - CameraModeTypes, - DefaultMobileCameraTypes, - MediaCaptureStatusTypes, -} from "../constants"; +import { CameraModeTypes, MediaCaptureStatusTypes } from "../constants"; import type { AutocompletionDefinitions } from "widgets/constants"; import { BACK_CAMERA_LABEL, diff --git a/app/client/src/widgets/CodeScannerWidget/component/index.tsx b/app/client/src/widgets/CodeScannerWidget/component/index.tsx index 22677fac4f..af2f16f372 100644 --- a/app/client/src/widgets/CodeScannerWidget/component/index.tsx +++ b/app/client/src/widgets/CodeScannerWidget/component/index.tsx @@ -28,6 +28,8 @@ import { ScannerLayout } from "../constants"; import type { ThemeProp } from "widgets/constants"; import { usePageVisibility } from "react-page-visibility"; import { importSvg } from "design-system-old"; +import { getVideoConstraints } from "widgets/utils"; +import { isMobile } from "react-device-detect"; const CameraOfflineIcon = importSvg( () => import("assets/icons/widget/camera/camera-offline.svg"), @@ -415,9 +417,13 @@ function CodeScannerComponent(props: CodeScannerComponentProps) { const [error, setError] = useState(""); const [isImageMirrored, setIsImageMirrored] = useState(false); const [videoConstraints, setVideoConstraints] = - useState({ - facingMode: "environment", - }); + useState( + isMobile + ? { + facingMode: { ideal: props.defaultCamera }, + } + : {}, + ); /** * Check if the tab is active. @@ -458,10 +464,13 @@ function CodeScannerComponent(props: CodeScannerComponentProps) { const handleMediaDeviceChange = useCallback( (mediaDeviceInfo: MediaDeviceInfo) => { if (mediaDeviceInfo.kind === "videoinput") { - setVideoConstraints({ - ...videoConstraints, - deviceId: mediaDeviceInfo.deviceId, - }); + const constraints = getVideoConstraints( + videoConstraints, + isMobile, + "", + mediaDeviceInfo.deviceId, + ); + setVideoConstraints(constraints); } }, [], @@ -629,6 +638,7 @@ export interface CodeScannerComponentProps extends ComponentProps { onCodeDetected: (value: string) => void; scannerLayout: ScannerLayout; shouldButtonFitContent: boolean; + defaultCamera: string; } export default CodeScannerComponent; diff --git a/app/client/src/widgets/CodeScannerWidget/constants.ts b/app/client/src/widgets/CodeScannerWidget/constants.ts index 48f550ef65..1ff3534bac 100644 --- a/app/client/src/widgets/CodeScannerWidget/constants.ts +++ b/app/client/src/widgets/CodeScannerWidget/constants.ts @@ -15,6 +15,7 @@ export interface CodeScannerWidgetProps extends WidgetProps { iconAlign?: Alignment; placement?: ButtonPlacement; scannerLayout: ScannerLayout; + defaultCamera: string; } export enum ScannerLayout { diff --git a/app/client/src/widgets/CodeScannerWidget/widget/index.tsx b/app/client/src/widgets/CodeScannerWidget/widget/index.tsx index d467c36c4c..cd17fcb9ad 100644 --- a/app/client/src/widgets/CodeScannerWidget/widget/index.tsx +++ b/app/client/src/widgets/CodeScannerWidget/widget/index.tsx @@ -77,6 +77,7 @@ class CodeScannerWidget extends BaseWidget< borderRadius={this.props.borderRadius} boxShadow={this.props.boxShadow} buttonColor={this.props.buttonColor || this.props.accentColor} + defaultCamera={this.props.defaultCamera} iconAlign={this.props.iconAlign} iconName={this.props.iconName} isDisabled={this.props.isDisabled} diff --git a/app/client/src/widgets/CodeScannerWidget/widget/propertyConfig/contentConfig.ts b/app/client/src/widgets/CodeScannerWidget/widget/propertyConfig/contentConfig.ts index 30fcf96ac1..0cddc5239a 100644 --- a/app/client/src/widgets/CodeScannerWidget/widget/propertyConfig/contentConfig.ts +++ b/app/client/src/widgets/CodeScannerWidget/widget/propertyConfig/contentConfig.ts @@ -1,8 +1,16 @@ import type { PropertyPaneConfig } from "constants/PropertyControlConstants"; import { ValidationTypes } from "constants/WidgetValidation"; import { isAutoLayout } from "utils/autoLayout/flexWidgetUtils"; +import { DefaultMobileCameraTypes } from "widgets/constants"; import type { CodeScannerWidgetProps } from "widgets/CodeScannerWidget/constants"; import { ScannerLayout } from "widgets/CodeScannerWidget/constants"; +import { + BACK_CAMERA_LABEL, + DEFAULT_CAMERA_LABEL, + DEFAULT_CAMERA_LABEL_DESCRIPTION, + FRONT_CAMERA_LABEL, + createMessage, +} from "@appsmith/constants/messages"; export default [ { sectionName: "Basic", @@ -93,6 +101,35 @@ export default [ props.scannerLayout === ScannerLayout.ALWAYS_ON, dependencies: ["scannerLayout"], }, + { + propertyName: "defaultCamera", + label: createMessage(DEFAULT_CAMERA_LABEL), + helpText: createMessage(DEFAULT_CAMERA_LABEL_DESCRIPTION), + controlType: "DROP_DOWN", + defaultValue: DefaultMobileCameraTypes.BACK, + options: [ + { + label: createMessage(FRONT_CAMERA_LABEL), + value: DefaultMobileCameraTypes.FRONT, + }, + { + label: createMessage(BACK_CAMERA_LABEL), + value: DefaultMobileCameraTypes.BACK, + }, + ], + isBindProperty: true, + isTriggerProperty: false, + validation: { + type: ValidationTypes.TEXT, + params: { + allowedValues: [ + DefaultMobileCameraTypes.FRONT, + DefaultMobileCameraTypes.BACK, + ], + default: DefaultMobileCameraTypes.BACK, + }, + }, + }, ], }, diff --git a/app/client/src/widgets/CodeScannerWidget/widget/propertyConfig/contetConfig.test.ts b/app/client/src/widgets/CodeScannerWidget/widget/propertyConfig/contetConfig.test.ts new file mode 100644 index 0000000000..e02b34ef50 --- /dev/null +++ b/app/client/src/widgets/CodeScannerWidget/widget/propertyConfig/contetConfig.test.ts @@ -0,0 +1,36 @@ +import type { + PropertyPaneConfig, + PropertyPaneControlConfig, +} from "constants/PropertyControlConstants"; +import contentConfig from "./contentConfig"; +import { DefaultMobileCameraTypes } from "widgets/constants"; + +describe("codescanner property control", () => { + const propertyConfigs = contentConfig + .map((sectionConfig) => sectionConfig.children) + .flat() as PropertyPaneControlConfig[]; + + const defaultCameraProperty = propertyConfigs.find( + (propertyConfig) => propertyConfig.propertyName === "defaultCamera", + ) as PropertyPaneConfig; + + it("validates the codescanner widget has a default camera property", () => { + expect(defaultCameraProperty).not.toBeNull(); + }); + + it("validates the options for default mobile camera property", () => { + expect((defaultCameraProperty as any).options).toHaveLength(2); + expect((defaultCameraProperty as any).options[0].value).toEqual( + DefaultMobileCameraTypes.FRONT, + ); + expect((defaultCameraProperty as any).options[1].value).toEqual( + DefaultMobileCameraTypes.BACK, + ); + }); + + it("validates the default mobile camera value to be back camera", () => { + expect((defaultCameraProperty as any).defaultValue).toEqual( + DefaultMobileCameraTypes.BACK, + ); + }); +}); diff --git a/app/client/src/widgets/constants.ts b/app/client/src/widgets/constants.ts index 6aac918512..7ab57ada2a 100644 --- a/app/client/src/widgets/constants.ts +++ b/app/client/src/widgets/constants.ts @@ -377,3 +377,8 @@ export type ThemeProp = { }; export type SnipingModeProperty = Record<"data" | "run", string>; + +export enum DefaultMobileCameraTypes { + FRONT = "user", + BACK = "environment", +} diff --git a/app/client/src/widgets/CameraWidget/component/utils.test.ts b/app/client/src/widgets/utils.test.ts similarity index 100% rename from app/client/src/widgets/CameraWidget/component/utils.test.ts rename to app/client/src/widgets/utils.test.ts diff --git a/app/client/src/widgets/CameraWidget/component/utils.ts b/app/client/src/widgets/utils.ts similarity index 100% rename from app/client/src/widgets/CameraWidget/component/utils.ts rename to app/client/src/widgets/utils.ts