fix permission request & display photo center
This commit is contained in:
parent
92f9137cbb
commit
30a8f01441
2 changed files with 191 additions and 81 deletions
|
|
@ -1,52 +1,171 @@
|
|||
import { useQuasar } from 'quasar'
|
||||
import { onBeforeUnmount, ref } from 'vue'
|
||||
import { usePrivacyStore } from '@/stores/privacy'
|
||||
|
||||
type BrowserPermissionState = PermissionState | 'unsupported'
|
||||
|
||||
type PermissionQueryName = 'camera' | 'geolocation'
|
||||
|
||||
export function usePermissions() {
|
||||
const $q = useQuasar()
|
||||
const privacyStore = usePrivacyStore()
|
||||
const cameraPermissionState = ref<BrowserPermissionState>('prompt')
|
||||
const locationPermissionState = ref<BrowserPermissionState>('prompt')
|
||||
|
||||
// const checkCameraPermission = (): boolean => {
|
||||
// if (!privacyStore.isAccepted) {
|
||||
// privacyStore.modalPrivacy = true
|
||||
// $q.notify({
|
||||
// type: 'warning',
|
||||
// message: 'กรุณายอมรับนโยบายคุ้มครองข้อมูลส่วนบุคคลก่อนใช้งานกล้อง',
|
||||
// position: 'top',
|
||||
// })
|
||||
// return false
|
||||
// }
|
||||
// return true
|
||||
// }
|
||||
let cameraPermissionStatus: PermissionStatus | null = null
|
||||
let locationPermissionStatus: PermissionStatus | null = null
|
||||
|
||||
// const checkLocationPermission = (): boolean => {
|
||||
// if (!privacyStore.isAccepted) {
|
||||
// privacyStore.modalPrivacy = true
|
||||
// $q.notify({
|
||||
// type: 'warning',
|
||||
// message: 'กรุณายอมรับนโยบายคุ้มครองข้อมูลส่วนบุคคลก่อนใช้งานแผนที่',
|
||||
// position: 'top',
|
||||
// })
|
||||
// return false
|
||||
// }
|
||||
// return true
|
||||
// }
|
||||
const isPermissionsApiSupported = () =>
|
||||
typeof navigator !== 'undefined' && 'permissions' in navigator
|
||||
|
||||
const setPermissionState = (
|
||||
target: typeof cameraPermissionState | typeof locationPermissionState,
|
||||
state: BrowserPermissionState
|
||||
) => {
|
||||
target.value = state
|
||||
}
|
||||
|
||||
const setPermissionChangeListener = (
|
||||
name: PermissionQueryName,
|
||||
status: PermissionStatus
|
||||
) => {
|
||||
status.onchange = () => {
|
||||
const target =
|
||||
name === 'camera' ? cameraPermissionState : locationPermissionState
|
||||
setPermissionState(target, status.state)
|
||||
}
|
||||
}
|
||||
|
||||
async function queryPermissionState(name: PermissionQueryName) {
|
||||
if (!isPermissionsApiSupported()) {
|
||||
return null
|
||||
}
|
||||
|
||||
try {
|
||||
return await navigator.permissions.query({ name } as PermissionDescriptor)
|
||||
} catch (error) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
async function syncPermissionState(name: PermissionQueryName) {
|
||||
const target =
|
||||
name === 'camera' ? cameraPermissionState : locationPermissionState
|
||||
const previousStatus =
|
||||
name === 'camera' ? cameraPermissionStatus : locationPermissionStatus
|
||||
|
||||
if (previousStatus) {
|
||||
previousStatus.onchange = null
|
||||
}
|
||||
|
||||
const status = await queryPermissionState(name)
|
||||
if (!status) {
|
||||
if (target.value === 'prompt') {
|
||||
setPermissionState(target, 'unsupported')
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if (name === 'camera') {
|
||||
cameraPermissionStatus = status
|
||||
} else {
|
||||
locationPermissionStatus = status
|
||||
}
|
||||
|
||||
setPermissionState(target, status.state)
|
||||
setPermissionChangeListener(name, status)
|
||||
}
|
||||
|
||||
async function syncPermissionStates() {
|
||||
await Promise.all([
|
||||
syncPermissionState('camera'),
|
||||
syncPermissionState('geolocation'),
|
||||
])
|
||||
}
|
||||
|
||||
const checkPrivacyAccepted = (): boolean => {
|
||||
if (!privacyStore.isAccepted) {
|
||||
privacyStore.modalPrivacy = true
|
||||
// $q.notify({
|
||||
// type: 'warning',
|
||||
// message: 'กรุณายอมรับนโยบายคุ้มครองข้อมูลส่วนบุคคลก่อนใช้งาน',
|
||||
// position: 'center',
|
||||
// })
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
async function requestCameraPermission() {
|
||||
if (!checkPrivacyAccepted()) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (cameraPermissionState.value === 'granted') {
|
||||
return true
|
||||
}
|
||||
|
||||
if (!navigator.mediaDevices?.getUserMedia) {
|
||||
setPermissionState(cameraPermissionState, 'unsupported')
|
||||
return false
|
||||
}
|
||||
|
||||
try {
|
||||
const stream = await navigator.mediaDevices.getUserMedia({
|
||||
video: { facingMode: 'user' },
|
||||
audio: false,
|
||||
})
|
||||
|
||||
stream.getTracks().forEach((track) => track.stop())
|
||||
setPermissionState(cameraPermissionState, 'granted')
|
||||
return true
|
||||
} catch (error) {
|
||||
setPermissionState(cameraPermissionState, 'denied')
|
||||
return false
|
||||
} finally {
|
||||
await syncPermissionState('camera')
|
||||
}
|
||||
}
|
||||
|
||||
async function requestLocationPermission() {
|
||||
if (!checkPrivacyAccepted()) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (locationPermissionState.value === 'granted') {
|
||||
return true
|
||||
}
|
||||
|
||||
if (!navigator.geolocation) {
|
||||
setPermissionState(locationPermissionState, 'unsupported')
|
||||
return false
|
||||
}
|
||||
|
||||
return new Promise<boolean>((resolve) => {
|
||||
navigator.geolocation.getCurrentPosition(
|
||||
async () => {
|
||||
setPermissionState(locationPermissionState, 'granted')
|
||||
await syncPermissionState('geolocation')
|
||||
resolve(true)
|
||||
},
|
||||
async () => {
|
||||
setPermissionState(locationPermissionState, 'denied')
|
||||
await syncPermissionState('geolocation')
|
||||
resolve(false)
|
||||
}
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
if (cameraPermissionStatus) {
|
||||
cameraPermissionStatus.onchange = null
|
||||
}
|
||||
|
||||
if (locationPermissionStatus) {
|
||||
locationPermissionStatus.onchange = null
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
// checkCameraPermission,
|
||||
// checkLocationPermission,
|
||||
cameraPermissionState,
|
||||
locationPermissionState,
|
||||
checkPrivacyAccepted,
|
||||
syncPermissionStates,
|
||||
requestCameraPermission,
|
||||
requestLocationPermission,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,11 @@ import MapCheck from '@/components/AscGISMap.vue'
|
|||
const mixin = useCounterMixin()
|
||||
const { date2Thai, showLoader, hideLoader, messageError, dialogConfirm } = mixin
|
||||
const $q = useQuasar()
|
||||
const { checkPrivacyAccepted } = usePermissions()
|
||||
const {
|
||||
checkPrivacyAccepted,
|
||||
syncPermissionStates,
|
||||
requestCameraPermission,
|
||||
} = usePermissions()
|
||||
const privacyStore = usePrivacyStore()
|
||||
const positionKeycloakStore = usePositionKeycloakStore()
|
||||
const MOCK_CHECK_DELAY_MS = 800
|
||||
|
|
@ -471,34 +475,14 @@ async function openCamera() {
|
|||
return
|
||||
}
|
||||
|
||||
if (!isPermissionCameraDenied.value) {
|
||||
try {
|
||||
if (cameraIsOn.value) {
|
||||
camera.value?.stop()
|
||||
} else {
|
||||
// Keep the initial stream aligned with the Camera component's front-camera constraint.
|
||||
await camera.value?.start()
|
||||
const devices: any = await camera.value?.devices(['videoinput'])
|
||||
if (devices) {
|
||||
availableCameras.value = devices
|
||||
if (cameraIsOn.value) {
|
||||
camera.value?.stop()
|
||||
cameraIsOn.value = false
|
||||
return
|
||||
}
|
||||
|
||||
// Avoid an extra stop/start cycle here; on iOS it can override the initial camera selection.
|
||||
const activeId = camera.value?.currentDeviceID()
|
||||
const activeIdx = activeId
|
||||
? devices.findIndex((d: any) => d.deviceId === activeId)
|
||||
: -1
|
||||
currentCameraIndex.value = activeIdx >= 0 ? activeIdx : 0
|
||||
currentCameraType.value = identifyCameraType(
|
||||
devices[currentCameraIndex.value]?.label || ''
|
||||
)
|
||||
}
|
||||
}
|
||||
cameraIsOn.value = !cameraIsOn.value
|
||||
} catch (error) {
|
||||
console.error('Error opening camera:', error)
|
||||
messageError($q, error, 'ไม่สามารถเปิดกล้องได้ กรุณาลองใหม่อีกครั้ง')
|
||||
}
|
||||
} else {
|
||||
const hasCameraPermission = await requestCameraPermission()
|
||||
if (!hasCameraPermission) {
|
||||
messageError(
|
||||
$q,
|
||||
'',
|
||||
|
|
@ -506,6 +490,29 @@ async function openCamera() {
|
|||
)
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
// Keep the initial stream aligned with the Camera component's front-camera constraint.
|
||||
await camera.value?.start()
|
||||
const devices: any = await camera.value?.devices(['videoinput'])
|
||||
if (devices) {
|
||||
availableCameras.value = devices
|
||||
|
||||
// Avoid an extra stop/start cycle here; on iOS it can override the initial camera selection.
|
||||
const activeId = camera.value?.currentDeviceID()
|
||||
const activeIdx = activeId
|
||||
? devices.findIndex((d: any) => d.deviceId === activeId)
|
||||
: -1
|
||||
currentCameraIndex.value = activeIdx >= 0 ? activeIdx : 0
|
||||
currentCameraType.value = identifyCameraType(
|
||||
devices[currentCameraIndex.value]?.label || ''
|
||||
)
|
||||
}
|
||||
cameraIsOn.value = true
|
||||
} catch (error) {
|
||||
console.error('Error opening camera:', error)
|
||||
messageError($q, error, 'ไม่สามารถเปิดกล้องได้ กรุณาลองใหม่อีกครั้ง')
|
||||
}
|
||||
}
|
||||
|
||||
/** change camera device*/
|
||||
|
|
@ -880,21 +887,6 @@ function handleVisibilityChange() {
|
|||
}
|
||||
}
|
||||
|
||||
const isPermissionCameraDenied = ref<boolean>(false) // ตัวแปรสำหรับตรวจสอบการปฏิเสธสิทธิ์กล้อง
|
||||
|
||||
async function requestCamera() {
|
||||
try {
|
||||
// Probe camera permission with the same front-camera constraint, then release the stream immediately.
|
||||
const stream = await navigator.mediaDevices.getUserMedia({
|
||||
video: { facingMode: 'user' },
|
||||
audio: false,
|
||||
})
|
||||
stream.getTracks().forEach((track) => track.stop())
|
||||
} catch (err) {
|
||||
isPermissionCameraDenied.value = true
|
||||
}
|
||||
}
|
||||
|
||||
/** Hook*/
|
||||
onMounted(async () => {
|
||||
// เริ่มต้น clock เสมอ
|
||||
|
|
@ -911,11 +903,11 @@ onMounted(async () => {
|
|||
startChecking()
|
||||
}
|
||||
|
||||
await syncPermissionStates()
|
||||
|
||||
// เรียกแผนที่เฉพาะเมื่อยอมรับ privacy แล้ว
|
||||
if (privacyStore.isAccepted) {
|
||||
mapRef.value?.requestLocationPermission()
|
||||
// iOS uses the file-input fallback and does not need a getUserMedia probe
|
||||
if (!useNativePhotoCapture.value) requestCamera()
|
||||
}
|
||||
})
|
||||
|
||||
|
|
@ -954,11 +946,10 @@ onBeforeUnmount(() => {
|
|||
|
||||
watch(
|
||||
() => privacyStore.isAccepted,
|
||||
(newVal) => {
|
||||
async (newVal) => {
|
||||
if (newVal) {
|
||||
await syncPermissionStates()
|
||||
mapRef.value?.requestLocationPermission()
|
||||
// iOS uses the file-input fallback and does not need a getUserMedia probe
|
||||
if (!useNativePhotoCapture.value) requestCamera()
|
||||
}
|
||||
}
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue