From 0ed9fd6f30a51c8a3e7bb4e96616c02aacb08fae Mon Sep 17 00:00:00 2001 From: Sangeeth Sivan <74818788+berzerkeer@users.noreply.github.com> Date: Thu, 10 Aug 2023 12:18:51 +0530 Subject: [PATCH] feat: code scanner widget default camera (#26083) ## Description - support for default mobile camera for code scanner widget #### PR fixes following issue(s) Fixes #25766 #### Media #### Type of change - New feature (non-breaking change which adds functionality) #### How Has This Been Tested? > Please describe the tests that you ran to verify your changes. Also list any relevant details for your test configuration. > Delete anything that is not relevant - [x] Manual - [x] Jest - [ ] Cypress > > #### Test Plan > Add Testsmith test cases links that relate to this PR > > #### Issues raised during DP testing > Link issues raised during DP testing for better visiblity and tracking (copy link from comments dropped on this PR) > > > ## Checklist: #### Dev activity - [x] My code follows the style guidelines of this project - [x] I have performed a self-review of my own code - [ ] I have commented my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [x] I have added tests that prove my fix is effective or that my feature works - [x] New and existing unit tests pass locally with my changes - [ ] PR is being merged under a feature flag #### QA activity: - [ ] [Speedbreak features](https://github.com/appsmithorg/TestSmith/wiki/Guidelines-for-test-plans#speedbreakers-) have been covered - [ ] Test plan covers all impacted features and [areas of interest](https://github.com/appsmithorg/TestSmith/wiki/Guidelines-for-test-plans#areas-of-interest-) - [ ] Test plan has been peer reviewed by project stakeholders and other QA members - [ ] Manually tested functionality on DP - [ ] We had an implementation alignment call with stakeholders post QA Round 2 - [ ] Cypress test cases have been added and approved by SDET/manual QA - [ ] Added `Test Plan Approved` label after Cypress tests were reviewed - [ ] Added `Test Plan Approved` label after JUnit tests were reviewed --- .../widgets/CameraWidget/component/index.tsx | 2 +- .../src/widgets/CameraWidget/constants.ts | 5 --- .../src/widgets/CameraWidget/widget/index.tsx | 8 +--- .../CodeScannerWidget/component/index.tsx | 24 ++++++++---- .../widgets/CodeScannerWidget/constants.ts | 1 + .../CodeScannerWidget/widget/index.tsx | 1 + .../widget/propertyConfig/contentConfig.ts | 37 +++++++++++++++++++ .../propertyConfig/contetConfig.test.ts | 36 ++++++++++++++++++ app/client/src/widgets/constants.ts | 5 +++ .../component => }/utils.test.ts | 0 .../{CameraWidget/component => }/utils.ts | 0 11 files changed, 100 insertions(+), 19 deletions(-) create mode 100644 app/client/src/widgets/CodeScannerWidget/widget/propertyConfig/contetConfig.test.ts rename app/client/src/widgets/{CameraWidget/component => }/utils.test.ts (100%) rename app/client/src/widgets/{CameraWidget/component => }/utils.ts (100%) 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