Skip to content
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

[Fixed]: Scanning modal remains open and blank when camera access is denied (#329) #406

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
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
78 changes: 60 additions & 18 deletions src/components/Scanner.vue
Original file line number Diff line number Diff line change
@@ -1,44 +1,86 @@
<template>
<ion-toolbar>
<ion-buttons slot="end" @click="closeScanner()" >
<ion-button >
<ion-buttons slot="end" @click="closeScanner()">
<ion-button>
<ion-icon :icon="closeOutline" />
</ion-button>
</ion-buttons>
</ion-toolbar>
</ion-buttons>
</ion-toolbar>

<div class="scanner">
<!-- Conditionally render the "Start Scanning" button based on camera access -->
<ion-button v-if="!hasCameraAccess" @click="openScanner">Start Scanning</ion-button>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We do not need to make any change in this component, because once we have added the check of camera access in the parent component then this component will not open and thus there is no use of having any checks in this component.


<!-- Load the barcode scanner only if camera access is granted -->
<StreamBarcodeReader
v-if="hasCameraAccess"
@decode="onDecode"
@loaded="onLoaded"
/>
</div>
</div>
</template>

<script>
import { StreamBarcodeReader } from "vue-barcode-reader";
import { IonButton,IonButtons, IonIcon, IonToolbar, modalController } from '@ionic/vue';
import { IonButton, IonButtons, IonIcon, IonToolbar, modalController, alertController } from '@ionic/vue';
import { closeOutline } from 'ionicons/icons';

export default {
name: 'Scanner',
components: {
IonButton,
IonButtons,
IonIcon,
IonIcon,
IonToolbar,
StreamBarcodeReader,
},
},
data() {
return {
hasCameraAccess: false, // Track if the camera access is granted
};
},
methods: {
onDecode (result) {
modalController.dismiss({dismissed: true}, result);
// Request camera access when the user interacts
async requestCameraAccess() {
try {
const stream = await navigator.mediaDevices.getUserMedia({ video: true });
if (stream) {
this.hasCameraAccess = true; // Access granted, show scanner
}
} catch (error) {
console.error('Camera access denied:', error);
this.hasCameraAccess = false; // Access denied, show alert
this.showAlert('Camera permission is denied. Please enable the camera permission in your device settings to use the scanner.');
}
},
// Show an alert when camera access is denied
async showAlert(message) {
const alert = await alertController.create({
header: 'Camera Permission Denied',
message: message,
buttons: ['OK'],
});
await alert.present();
},
// Triggered by user clicking "Start Scanning" button
async openScanner() {
await this.requestCameraAccess(); // Ask for camera access
if (this.hasCameraAccess) {
// Button will automatically disappear and scanner will be displayed
}
},
onDecode(result) {
modalController.dismiss({ dismissed: true }, result);
},
closeScanner() {
modalController.dismiss({ dismissed: true });
},
closeScanner(){
modalController.dismiss({dismissed: true});
}
},
setup() {
return {
closeOutline
}
}
}
</script>
closeOutline,
};
},
};
</script>

14 changes: 13 additions & 1 deletion src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,4 +52,16 @@ const handleDateTimeInput = (dateTimeValue: any) => {
return DateTime.fromISO(dateTime).toMillis()
}

export { handleDateTimeInput, showToast, hasError, copyToClipboard }
const hasCameraAccess = async () => {
try {
const stream = await navigator.mediaDevices.getUserMedia({ video: true });
if (stream) {
showToast(translate('Camera access granted.'));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We do not need to display toast when the user has camera permission, because if the camera permission is already provided, then the modal will open up, and then displaying a toast will be a redundant behaviour.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, I think we can remove the stream check from here

}
} catch (error) {
console.error('Camera access denied:', error);
showToast(translate('Camera permission is denied. Please enable the camera permission in your device settings to use the scanner.'));
}
}

export { handleDateTimeInput, showToast, hasError, copyToClipboard, hasCameraAccess }
5 changes: 5 additions & 0 deletions src/views/PurchaseOrderDetail.vue
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@ import LocationPopover from '@/components/LocationPopover.vue'
import ImageModal from '@/components/ImageModal.vue';
import { copyToClipboard, hasError, showToast } from '@/utils';
import { Actions, hasPermission } from '@/authorization'
import { hasCameraAccess } from '@/utils/';

export default defineComponent({
name: "PurchaseOrderDetails",
Expand Down Expand Up @@ -249,6 +250,10 @@ export default defineComponent({
return imageModal.present();
},
async scan() {
if (!hasCameraAccess()) {
showToast(translate("Camera access is required to scan items."));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as ReturnDetails.

return;
}
const modal = await modalController
.create({
component: Scanner,
Expand Down
5 changes: 5 additions & 0 deletions src/views/ReturnDetails.vue
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ import ImageModal from '@/components/ImageModal.vue';
import { hasError } from '@/utils';
import { showToast } from '@/utils'
import { Actions, hasPermission } from '@/authorization'
import { hasCameraAccess } from '@/utils';

export default defineComponent({
name: "ReturnDetails",
Expand Down Expand Up @@ -268,6 +269,10 @@ export default defineComponent({
this.queryString = ''
},
async scanCode () {
if (!hasCameraAccess()) {
showToast(translate("Camera access is required to scan items."));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As we are having a toast message in the hasCameraAccess function, so we do not need to have another toast here. From here we can simply return the flow without any toast or log.

return;
}
const modal = await modalController
.create({
component: Scanner,
Expand Down
5 changes: 5 additions & 0 deletions src/views/ShipmentDetails.vue
Original file line number Diff line number Diff line change
Expand Up @@ -121,8 +121,9 @@
import Scanner from "@/components/Scanner.vue";
import LocationPopover from '@/components/LocationPopover.vue'
import ImageModal from '@/components/ImageModal.vue';
import { hasError, showToast } from '@/utils'

Check warning on line 124 in src/views/ShipmentDetails.vue

View workflow job for this annotation

GitHub Actions / call-workflow-in-another-repo / reusable_workflow_job (18.x)

'hasError' is defined but never used

Check warning on line 124 in src/views/ShipmentDetails.vue

View workflow job for this annotation

GitHub Actions / call-workflow-in-another-repo / reusable_workflow_job (20.x)

'hasError' is defined but never used
import { Actions, hasPermission } from '@/authorization'
import { hasCameraAccess } from '@/utils'

export default defineComponent({
name: "ShipmentDetails",
Expand Down Expand Up @@ -287,6 +288,10 @@
this.queryString = ''
},
async scanCode () {
if (!hasCameraAccess()) {
showToast(translate("Camera access is required to scan items."));
return;
}
const modal = await modalController
.create({
component: Scanner,
Expand Down
Loading