-
Notifications
You must be signed in to change notification settings - Fork 32
#1712 | [DMP 2025] Signature capture #1678
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
#1712 | [DMP 2025] Signature capture #1678
Conversation
d03763b to
c72ff90
Compare
WalkthroughIntroduces a Signature data type across the Android app: adds a Signature form element using react-native-signature-canvas, saves signatures as image files, renders previews in observations/media views, maps Signature to Images directory in services, passes scroll refs for input handling, updates translations, and adjusts webview dependency/patch. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor User
participant View as Parent View (ScrollView)
participant FEG as FormElementGroup
participant SFE as SignatureFormElement
participant FS as FileSystem (RNFS)
participant Store as Form State/Dispatcher
participant Obs as Observations View
Note over View,FEG: Scroll ref provided to manage canvas gestures
User->>SFE: Draws signature
SFE->>SFE: handleBegin() → disable parent scroll
SFE->>SFE: handleSignatureData(dataURL)
SFE->>FS: Write base64 image (UUID.ext) to disk
FS-->>SFE: File path/ok
SFE->>Store: Dispatch update with filename
SFE->>SFE: handleEnd() → enable parent scroll
Note over Store,Obs: Later, rendering existing observation
Obs->>FS: Build file:// URI from images dir + filename
FS-->>Obs: Local URI
Obs-->>User: Show signature preview image
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Pre-merge checks (3 passed)✅ Passed checks (3 passed)
Poem
✨ Finishing Touches
🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
🧹 Nitpick comments (2)
packages/openchs-android/src/views/common/Observations.js (1)
25-25: Avoid coupling to a UI component for a filesystem constantImport FileSystem directly instead of pulling SignatureFormElement just to access its static directory.
- import SignatureFormElement from "../form/formElement/SignatureFormElement"; + import FileSystem from "../../model/FileSystem";packages/openchs-android/src/views/form/formElement/SignatureFormElement.js (1)
31-33: Use the wrapper API to read the valuePrefer the wrapper’s getValue() to stay consistent with the rest of the codebase; fall back to answer if needed.
- get signatureFilename() { - return _.get(this, "props.value.answer"); - } + get signatureFilename() { + return (this.props.value && typeof this.props.value.getValue === 'function') + ? this.props.value.getValue() + : _.get(this, "props.value.answer"); + }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
⛔ Files ignored due to path filters (1)
packages/openchs-android/package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (4)
packages/openchs-android/package.json(1 hunks)packages/openchs-android/src/views/common/Observations.js(3 hunks)packages/openchs-android/src/views/form/FormElementGroup.js(2 hunks)packages/openchs-android/src/views/form/formElement/SignatureFormElement.js(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
packages/openchs-android/src/views/form/formElement/SignatureFormElement.js (1)
packages/openchs-android/src/model/FileSystem.js (1)
FileSystem(6-143)
🔇 Additional comments (3)
packages/openchs-android/package.json (1)
94-101: Peer dependencies are compatible – RN 0.72.8 satisfies both libraries’ requirements (signature-canvas needs webview ≥13 and webview 13.15.0 supports any React Native version), no further changes needed.packages/openchs-android/src/views/form/FormElementGroup.js (2)
46-46: Import of SignatureFormElement — LGTMImport path and placement are consistent with existing elements.
224-231: Signature element wiring — LGTM, confirm model support at runtimeRendering branch correctly mirrors other primitive elements. Please ensure the app ships with an openchs-models version that defines Concept.dataType.Signature to avoid this branch being unreachable.
packages/openchs-android/src/views/form/formElement/SignatureFormElement.js
Show resolved
Hide resolved
packages/openchs-android/src/views/form/formElement/SignatureFormElement.js
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/openchs-android/src/service/MediaService.js (1)
145-171: Do not reject small-but-valid images (signatures can be <1KB)The size threshold (MIN_FILE_SIZE_IN_BYTES=1024) can incorrectly treat valid, tiny PNGs as corrupt, breaking downloads and re-download logic.
Use file existence and stat > 0 instead of Content-Length thresholds:
- // If status is successful and either size is undefined (can't check) or size is reasonable - if (status >= 200 && status < 300 && (size === undefined || size > MIN_FILE_SIZE_IN_BYTES)) { + // If status is successful, verify on-disk size > 0 + if (status >= 200 && status < 300) { // Verify the file exists and has content return fs.stat(targetFilePath) .then(stats => { - if (stats.size > 0) { + if (stats.size > 0) { General.logDebug("MediaService", `The file saved to: ${res.path()} with size ${stats.size}`); return targetFilePath; } else { General.logDebug("MediaService", `File exists but has zero size: ${targetFilePath}`); return fs.unlink(targetFilePath).then(() => { throw new Error('Downloaded file has zero size'); }); } }) .catch(statError => { General.logDebug("MediaService", `Error checking file stats: ${statError.message}`); return fs.unlink(targetFilePath).then(() => { throw new Error(`Error verifying downloaded file: ${statError.message}`); }); }); } else { // If the file is empty or too small, delete it and throw an error return fs.unlink(targetFilePath).then(() => { createMediaDownloadAvniError(res, url); }); }- if (fileStats.size <= MIN_FILE_SIZE_IN_BYTES) { + if (fileStats.size <= 0) { // File exists but is likely an empty placeholder, delete and re-download await fs.unlink(filePathInDevice); return await this.downloadMediaFromS3ToPath(s3Key, filePathInDevice, ignoreFetchErrors); }Also applies to: 250-255
🧹 Nitpick comments (1)
packages/openchs-android/src/service/MediaQueueService.js (1)
87-102: Add a default case to guard against unknown media typesWithout a default, getDirByType can return undefined, producing paths like "undefined/filename" downstream.
Apply:
getDirByType(mediaQueueItem) { switch (mediaQueueItem.type) { case 'Image': case 'ImageV2': + case 'Signature': - case 'Signature': return FileSystem.getImagesDir(); case 'Profile-Pics': return FileSystem.getProfilePicsDir(); case 'Video': return FileSystem.getVideosDir(); case 'Audio': return FileSystem.getAudioDir(); case 'File': return FileSystem.getFileDir(); + default: + throw new Error(`Unhandled media type: ${mediaQueueItem.type}`); } }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
packages/openchs-android/patches/react-native-webview+11.23.0.patch(0 hunks)packages/openchs-android/src/service/MediaQueueService.js(1 hunks)packages/openchs-android/src/service/MediaService.js(5 hunks)packages/openchs-android/src/views/common/ExpandableMedia.js(1 hunks)packages/openchs-android/src/views/common/Observations.js(3 hunks)
💤 Files with no reviewable changes (1)
- packages/openchs-android/patches/react-native-webview+11.23.0.patch
🚧 Files skipped from review as they are similar to previous changes (1)
- packages/openchs-android/src/views/common/Observations.js
🧰 Additional context used
🧬 Code graph analysis (1)
packages/openchs-android/src/service/MediaService.js (1)
packages/openchs-android/src/model/FileSystem.js (1)
FileSystem(6-143)
🔇 Additional comments (3)
packages/openchs-android/src/views/common/ExpandableMedia.js (1)
116-125: Signature routed to ExpandableImage — looks goodConsistent with the rest of the stack; no issues spotted.
packages/openchs-android/src/service/MediaService.js (2)
192-208: Map Signature to Images directory — aligned with queue/service usageMatches MediaQueueService and UI routing. Good.
274-282: Error message key still image-specificThis branch sets error.userMessage = 'unableToFetchImagesError' even for non-image types (e.g., Audio, File). Consider a media-agnostic key or mapping by type.
Would you like me to scan i18n keys and propose a safe mapping per media type?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
♻️ Duplicate comments (1)
packages/openchs-android/src/views/form/formElement/SignatureFormElement.js (1)
34-54: Harden data-URL parsing and delete previous file on save (prevents crashes and storage leaks).Header.match can be null and will crash; also saving a new signature should clean up the previous file.
- updateValue(signatureValue) { - if (General.isNilOrEmpty(signatureValue)) { - this.onUpdateObservations(null); - return; - } - - const [header, base64Data] = signatureValue.split(','); - const mimeType = header.match(/data:(.*?);/)[1]; - const ext = mimeType.split('/')[1]; - - const fileName = `${General.randomUUID()}.${ext}`; - const filePath = `${SignatureFormElement.signatureFileDirectory}/${fileName}`; - - fs.writeFile(filePath, base64Data, 'base64') - .then(() => { - this.onUpdateObservations(fileName); - }) - .catch((error) => { - AlertMessage(`Error saving signature: ${error.message}`, "error"); - }); - } + updateValue(signatureValue) { + if (General.isNilOrEmpty(signatureValue)) { + this.onUpdateObservations(null); + return; + } + const parts = String(signatureValue).split(','); + const header = parts[0]; + const base64Data = parts[1]; + const match = header && header.match(/data:(.*?);/); + if (!match || !base64Data) { + AlertMessage(this.I18n.t("unexpectedSignatureFormat") || "Unexpected signature data format", "error"); + return; + } + const mimeType = match[1]; + const ext = (mimeType && mimeType.split('/')[1]) || 'png'; + + const fileName = `${General.randomUUID()}.${ext}`; + const filePath = `${SignatureFormElement.signatureFileDirectory}/${fileName}`; + const prevFile = this.signatureFilename; + + fs.writeFile(filePath, base64Data, 'base64') + .then(async () => { + if (prevFile && prevFile !== fileName) { + const prevPath = `${SignatureFormElement.signatureFileDirectory}/${prevFile}`; + await fs.unlink(prevPath).catch(() => {}); + } + this.onUpdateObservations(fileName); + }) + .catch((error) => { + AlertMessage(`Error saving signature: ${error.message}`, "error"); + }); + }
🧹 Nitpick comments (4)
packages/openchs-android/src/views/form/formElement/SignatureFormElement.js (4)
65-73: Deleting on clear — good; optionally log unlink errors.Helps diagnose file-system issues if cleanup fails.
- fs.unlink(prevPath).catch(() => { - }); + fs.unlink(prevPath).catch((e) => { + General.logError("SignatureFormElement", `Failed to delete previous signature: ${e.message}`); + });
111-113: Add accessibility props to the clear button.Improves screen-reader usability.
- <TouchableNativeFeedback onPress={() => this.clearValue()}> + <TouchableNativeFeedback + accessibilityRole="button" + accessibilityLabel={this.I18n.t("clear") || "Clear"} + onPress={() => this.clearValue()}>
105-110: Use “contain” for more predictable image preview scaling.Prevents potential clipping with “center”.
- <Image - resizeMode="center" + <Image + resizeMode="contain"
17-17: Consider storage hardening for PII.Signatures are sensitive; consider encrypting at rest or using app-internal storage if feasible, and ensure backups/sync respect privacy policies.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
packages/openchs-android/src/views/form/formElement/SignatureFormElement.js(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
packages/openchs-android/src/views/form/formElement/SignatureFormElement.js (1)
packages/openchs-android/src/model/FileSystem.js (1)
FileSystem(6-143)
🔇 Additional comments (1)
packages/openchs-android/src/views/form/formElement/SignatureFormElement.js (1)
17-18: Defer directory path resolution
Replace the static fieldstatic signatureFileDirectory = FileSystem.getImagesDir();with a runtime getter, e.g.:
static get signatureFileDirectory() { return FileSystem.getImagesDir(); }so that
getImagesDir()is invoked only afterFileSystem.init()has run.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (3)
packages/openchs-android/translations/en.json (1)
9-10: Casing consistency for buttons"save" is "SAVE" but "clear" is "Clear". Align casing per UI convention.
If you want all primary actions uppercase:
- "clear": "Clear", + "clear": "CLEAR",packages/openchs-android/translations/ta_IN.json (2)
9-10: Tamil: minor wording option"இங்கே கையெழுத்திடுங்கள்" is fine. Optional: prefer more native "இங்கே கையொப்பமிடுங்கள்".
254-254: Improve imperative phrasingUse a verb form: "Delete data and login" → "தரவை நீக்கி உள்நுழையவும்".
- "clearDataAndLogin": "தரவை நீக்கு மற்றும் உள்நுழைவு", + "clearDataAndLogin": "தரவை நீக்கி உள்நுழையவும்",
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (6)
packages/openchs-android/translations/en.json(1 hunks)packages/openchs-android/translations/gu_IN.json(1 hunks)packages/openchs-android/translations/hi_IN.json(1 hunks)packages/openchs-android/translations/ka_IN.json(1 hunks)packages/openchs-android/translations/mr_IN.json(1 hunks)packages/openchs-android/translations/ta_IN.json(2 hunks)
✅ Files skipped from review due to trivial changes (1)
- packages/openchs-android/translations/hi_IN.json
🔇 Additional comments (3)
packages/openchs-android/translations/ka_IN.json (1)
9-10: Kannada additions look correct"clear" and "signHere" read well and match tone.
packages/openchs-android/translations/mr_IN.json (1)
6-7: Marathi additions LGTMAccurate and idiomatic.
packages/openchs-android/translations/gu_IN.json (1)
6-7: Gujarati additions LGTMTranslations are correct and consistent.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
♻️ Duplicate comments (2)
packages/openchs-android/src/views/form/formElement/SignatureFormElement.js (2)
45-52: Optional: delete previous file after successful save to prevent orphansYou currently delete on “clear” (good), but if the element ever overwrites without clearing, the old file remains. Clean up the previous file post-write.
const fileName = `${General.randomUUID()}.${ext}`; const filePath = `${SignatureFormElement.signatureFileDirectory}/${fileName}`; + const prevFile = this.signatureFilename; fs.writeFile(filePath, base64Data, 'base64') - .then(() => { - this.onUpdateObservations(fileName); - }) + .then(async () => { + if (prevFile && prevFile !== fileName) { + const prevPath = `${SignatureFormElement.signatureFileDirectory}/${prevFile}`; + await fs.unlink(prevPath).catch(() => {}); + } + this.onUpdateObservations(fileName); + }) .catch((error) => { AlertMessage(`Error saving signature: ${error.message}`, "error"); });
41-44: Guard against malformed signature data to avoid crashes
header.match(...)[1]will throw ifsignatureValueisn’t a well-formed data URL (null match or missing comma). Add defensive parsing and a safe fallback ext.- const [header, base64Data] = signatureValue.split(','); - const mimeType = header.match(/data:(.*?);/)[1]; - const ext = mimeType.split('/')[1]; + const [header, base64Data] = (signatureValue || '').split(','); + const match = header && header.match(/data:(.*?);/); + if (!match || !base64Data) { + AlertMessage(this.I18n.t("unexpectedSignatureFormat") || "Unexpected signature data format", "error"); + return; + } + const mimeType = match[1]; + const ext = (mimeType.split('/')[1] || 'png').toLowerCase();
🧹 Nitpick comments (3)
packages/openchs-android/src/views/form/formElement/SignatureFormElement.js (1)
26-29: Unused ref: remove or wire up
this.signatureRefisn’t used. Drop it or use it (e.g., to trigger canvas clear). Keeping dead refs adds noise.constructor(props, context) { super(props, context); - this.signatureRef = React.createRef(); }packages/openchs-android/src/views/program/ManualProgramEligibilityView.js (1)
9-11: Wire a real ScrollView ref; current scrollRef prop is undefinedYou pass
scrollRef={this.scrollRef}but never define it nor attach it to a ScrollView. Signature scroll lock won’t work here.-import {View} from "react-native"; +import {View, ScrollView} from "react-native";constructor(props, context) { super(props, context, Reducers.reducerKeys.manualProgramEligibility); + this.scrollRef = React.createRef(); }<CHSContent ref="scroll"> - <AppHeader title={this.I18n.t('manualProgramEligibility')} func={() => this.onAppHeaderBack()} displayHomePressWarning={true}/> - <View style={{backgroundColor: '#ffffff', flexDirection: 'column'}}> + <ScrollView ref={this.scrollRef} keyboardShouldPersistTaps="handled"> + <AppHeader title={this.I18n.t('manualProgramEligibility')} func={() => this.onAppHeaderBack()} displayHomePressWarning={true}/> + <View style={{backgroundColor: '#ffffff', flexDirection: 'column'}}> <FormElementGroup group={this.state.formElementGroup} scrollRef={this.scrollRef} observationHolder={new ObservationsHolder(this.state.subjectProgramEligibility.observations)} actions={Actions} validationResults={this.state.validationResults} filteredFormElements={this.state.filteredFormElements} formElementsUserState={this.state.formElementsUserState} dataEntryDate={this.state.subjectProgramEligibility.checkDate} onValidationError={(x, y) => this.scrollToPosition(x, y)} /> <WizardButtons previous={{ visible: !this.state.wizard.isFirstPage(), func: () => this.previous(), label: this.I18n.t('previous') }} next={{ func: () => this.next(), label: this.I18n.t('next') }} /> </View> + </ScrollView>To verify parity with other views, check that this mirrors the pattern used in SubjectRegisterFormView.
packages/openchs-android/src/views/program/ChecklistItemView.js (1)
96-96: scrollRef passthrough — LGTM; consider keyboard tap handlingLooks good. Optional: align with other screens by adding keyboardShouldPersistTaps="handled" on the ScrollView to avoid focus-loss glitches during signature entry.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (13)
packages/openchs-android/src/views/BeneficiaryIdentificationPage.js(1 hunks)packages/openchs-android/src/views/familyfolder/FamilyRegisterFormView.js(1 hunks)packages/openchs-android/src/views/form/FormElementGroup.js(3 hunks)packages/openchs-android/src/views/form/formElement/SignatureFormElement.js(1 hunks)packages/openchs-android/src/views/individual/IndividualEncounterView.js(1 hunks)packages/openchs-android/src/views/individual/PersonRegisterFormView.js(1 hunks)packages/openchs-android/src/views/program/ChecklistItemView.js(1 hunks)packages/openchs-android/src/views/program/ManualProgramEligibilityView.js(1 hunks)packages/openchs-android/src/views/program/ProgramEncounterCancelView.js(2 hunks)packages/openchs-android/src/views/program/ProgramEncounterView.js(1 hunks)packages/openchs-android/src/views/program/ProgramFormComponent.js(1 hunks)packages/openchs-android/src/views/subject/SubjectRegisterFormView.js(1 hunks)packages/openchs-android/src/views/task/TaskFormView.js(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- packages/openchs-android/src/views/form/FormElementGroup.js
🧰 Additional context used
🧬 Code graph analysis (1)
packages/openchs-android/src/views/form/formElement/SignatureFormElement.js (1)
packages/openchs-android/src/model/FileSystem.js (1)
FileSystem(6-143)
🔇 Additional comments (8)
packages/openchs-android/src/views/subject/SubjectRegisterFormView.js (1)
141-146: LGTM: scrollRef correctly passed throughGood pattern: you create
this.scrollRef, attach it to ScrollView, and forward it to FormElementGroup for signature scroll control.packages/openchs-android/src/views/individual/PersonRegisterFormView.js (1)
143-143: scrollRef passthrough — LGTMMatches the established pattern; ScrollView already uses keyboardShouldPersistTaps="handled".
packages/openchs-android/src/views/BeneficiaryIdentificationPage.js (1)
67-67: scrollRef passthrough — LGTMCorrectly forwards the container ref to the form group.
packages/openchs-android/src/views/individual/IndividualEncounterView.js (1)
198-198: scrollRef passthrough — LGTMConsistent with other views; enables SignatureFormElement to manage scroll state.
packages/openchs-android/src/views/program/ProgramFormComponent.js (1)
135-135: scrollRef passthrough — LGTMGood addition; no issues spotted.
packages/openchs-android/src/views/task/TaskFormView.js (1)
76-76: scrollRef passthrough — LGTMMatches the project-wide integration for signature capture.
packages/openchs-android/src/views/familyfolder/FamilyRegisterFormView.js (1)
55-55: scrollRef passthrough — LGTMWiring looks correct and consistent.
packages/openchs-android/src/views/program/ProgramEncounterView.js (1)
217-227: Approve code changesFormElementGroup declares scrollRef in its propTypes and forwards it to SignatureFormElement, which safely calls
current.setNativeProps({scrollEnabled: false/true})on gesture begin/end. No further changes needed.
packages/openchs-android/src/views/program/ProgramEncounterCancelView.js
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
♻️ Duplicate comments (1)
packages/openchs-android/src/views/form/formElement/SignatureFormElement.js (1)
40-43: Guard data-URL parsing to avoid crashes on unexpected inputA malformed signatureValue will throw if match returns null. Add minimal checks.
- const [header, base64Data] = signatureValue.split(','); - const mimeType = header.match(/data:(.*?);/)[1]; - const ext = mimeType.split('/')[1]; + const parts = typeof signatureValue === "string" ? signatureValue.split(",") : []; + const header = parts[0]; + const base64Data = parts[1]; + const match = header && header.match(/data:(.*?);/); + if (!match || !base64Data) { + AlertMessage(this.I18n?.t?.("unexpectedSignatureFormat") || "Unexpected signature data format", "error"); + return; + } + const mimeType = match[1]; + const ext = (mimeType.split("/")[1]) || "png";
🧹 Nitpick comments (7)
packages/openchs-android/src/views/form/formElement/SignatureFormElement.js (7)
18-24: PropTypes missing for used propsparentElement and questionGroupIndex are used in onUpdateObservations but not declared in propTypes.
static propTypes = { element: PropTypes.object.isRequired, actionName: PropTypes.string.isRequired, value: PropTypes.object, validationResult: PropTypes.object, scrollRef: PropTypes.object, + parentElement: PropTypes.object, + questionGroupIndex: PropTypes.number, };Also applies to: 58-63
10-10: Remove unused importlodash is imported but not used.
-import _ from "lodash";
51-53: Localize error messageUse I18n so the error is translated.
- AlertMessage(`Error saving signature: ${error.message}`, "error"); + AlertMessage(this.I18n.t("errorSavingSignature", { message: error.message }) || `Error saving signature: ${error.message}`, "error");
26-28: Define ref or drop itref={this.signatureRef} assumes signatureRef exists. If AbstractFormElement doesn’t set it, define it here.
constructor(props, context) { super(props, context); + this.signatureRef = React.createRef(); }Also applies to: 124-131
87-96: Ensure scroll is re-enabled even on unmountIf the component unmounts while drawing, scroll may remain disabled. Add optional chaining and re-enable on unmount.
- this.props.scrollRef?.current?.setNativeProps( + this.props.scrollRef?.current?.setNativeProps?.( {scrollEnabled: false} ); ... - this.props.scrollRef?.current?.setNativeProps( + this.props.scrollRef?.current?.setNativeProps?.( {scrollEnabled: true} );Add this method:
componentWillUnmount() { this.props.scrollRef?.current?.setNativeProps?.({ scrollEnabled: true }); }
118-121: Improve accessibility for the clear actionProvide an accessible label and role for the clear button.
- <TouchableNativeFeedback onPress={() => this.clearValue()}> + <TouchableNativeFeedback + onPress={() => this.clearValue()} + accessibilityRole="button" + accessibilityLabel={this.I18n.t("clear")} + > <Icon name={"backspace"} style={[styles.icon]} /> </TouchableNativeFeedback>
17-17: Consider a dedicated, private directory for signaturesSignatures can be sensitive. Storing under the general Images directory may complicate retention. Consider a dedicated subdir (e.g., FileSystem.getSignaturesDir()) and/or internal app storage; apply retention/deletion policy aligned with org requirements.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
packages/openchs-android/src/views/form/formElement/SignatureFormElement.js(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
packages/openchs-android/src/views/form/formElement/SignatureFormElement.js (1)
packages/openchs-android/src/model/FileSystem.js (1)
FileSystem(6-143)
|
This closes avniproject/avni-product#1712 |
Demo Video
https://drive.google.com/file/d/1LW_uWRlkHzQskqolLWLapGCGrwldi6Xt/view?usp=sharing
Note
Depends on
Summary by CodeRabbit
New Features
Chores
Localization