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'
|
import { usePrivacyStore } from '@/stores/privacy'
|
||||||
|
|
||||||
|
type BrowserPermissionState = PermissionState | 'unsupported'
|
||||||
|
|
||||||
|
type PermissionQueryName = 'camera' | 'geolocation'
|
||||||
|
|
||||||
export function usePermissions() {
|
export function usePermissions() {
|
||||||
const $q = useQuasar()
|
|
||||||
const privacyStore = usePrivacyStore()
|
const privacyStore = usePrivacyStore()
|
||||||
|
const cameraPermissionState = ref<BrowserPermissionState>('prompt')
|
||||||
|
const locationPermissionState = ref<BrowserPermissionState>('prompt')
|
||||||
|
|
||||||
// const checkCameraPermission = (): boolean => {
|
let cameraPermissionStatus: PermissionStatus | null = null
|
||||||
// if (!privacyStore.isAccepted) {
|
let locationPermissionStatus: PermissionStatus | null = null
|
||||||
// privacyStore.modalPrivacy = true
|
|
||||||
// $q.notify({
|
|
||||||
// type: 'warning',
|
|
||||||
// message: 'กรุณายอมรับนโยบายคุ้มครองข้อมูลส่วนบุคคลก่อนใช้งานกล้อง',
|
|
||||||
// position: 'top',
|
|
||||||
// })
|
|
||||||
// return false
|
|
||||||
// }
|
|
||||||
// return true
|
|
||||||
// }
|
|
||||||
|
|
||||||
// const checkLocationPermission = (): boolean => {
|
const isPermissionsApiSupported = () =>
|
||||||
// if (!privacyStore.isAccepted) {
|
typeof navigator !== 'undefined' && 'permissions' in navigator
|
||||||
// privacyStore.modalPrivacy = true
|
|
||||||
// $q.notify({
|
const setPermissionState = (
|
||||||
// type: 'warning',
|
target: typeof cameraPermissionState | typeof locationPermissionState,
|
||||||
// message: 'กรุณายอมรับนโยบายคุ้มครองข้อมูลส่วนบุคคลก่อนใช้งานแผนที่',
|
state: BrowserPermissionState
|
||||||
// position: 'top',
|
) => {
|
||||||
// })
|
target.value = state
|
||||||
// return false
|
}
|
||||||
// }
|
|
||||||
// return true
|
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 => {
|
const checkPrivacyAccepted = (): boolean => {
|
||||||
if (!privacyStore.isAccepted) {
|
if (!privacyStore.isAccepted) {
|
||||||
privacyStore.modalPrivacy = true
|
privacyStore.modalPrivacy = true
|
||||||
// $q.notify({
|
|
||||||
// type: 'warning',
|
|
||||||
// message: 'กรุณายอมรับนโยบายคุ้มครองข้อมูลส่วนบุคคลก่อนใช้งาน',
|
|
||||||
// position: 'center',
|
|
||||||
// })
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return true
|
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 {
|
return {
|
||||||
// checkCameraPermission,
|
cameraPermissionState,
|
||||||
// checkLocationPermission,
|
locationPermissionState,
|
||||||
checkPrivacyAccepted,
|
checkPrivacyAccepted,
|
||||||
|
syncPermissionStates,
|
||||||
|
requestCameraPermission,
|
||||||
|
requestLocationPermission,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,11 @@ import MapCheck from '@/components/AscGISMap.vue'
|
||||||
const mixin = useCounterMixin()
|
const mixin = useCounterMixin()
|
||||||
const { date2Thai, showLoader, hideLoader, messageError, dialogConfirm } = mixin
|
const { date2Thai, showLoader, hideLoader, messageError, dialogConfirm } = mixin
|
||||||
const $q = useQuasar()
|
const $q = useQuasar()
|
||||||
const { checkPrivacyAccepted } = usePermissions()
|
const {
|
||||||
|
checkPrivacyAccepted,
|
||||||
|
syncPermissionStates,
|
||||||
|
requestCameraPermission,
|
||||||
|
} = usePermissions()
|
||||||
const privacyStore = usePrivacyStore()
|
const privacyStore = usePrivacyStore()
|
||||||
const positionKeycloakStore = usePositionKeycloakStore()
|
const positionKeycloakStore = usePositionKeycloakStore()
|
||||||
const MOCK_CHECK_DELAY_MS = 800
|
const MOCK_CHECK_DELAY_MS = 800
|
||||||
|
|
@ -471,34 +475,14 @@ async function openCamera() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isPermissionCameraDenied.value) {
|
if (cameraIsOn.value) {
|
||||||
try {
|
camera.value?.stop()
|
||||||
if (cameraIsOn.value) {
|
cameraIsOn.value = false
|
||||||
camera.value?.stop()
|
return
|
||||||
} 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
|
|
||||||
|
|
||||||
// Avoid an extra stop/start cycle here; on iOS it can override the initial camera selection.
|
const hasCameraPermission = await requestCameraPermission()
|
||||||
const activeId = camera.value?.currentDeviceID()
|
if (!hasCameraPermission) {
|
||||||
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 {
|
|
||||||
messageError(
|
messageError(
|
||||||
$q,
|
$q,
|
||||||
'',
|
'',
|
||||||
|
|
@ -506,6 +490,29 @@ async function openCamera() {
|
||||||
)
|
)
|
||||||
return
|
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*/
|
/** 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*/
|
/** Hook*/
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
// เริ่มต้น clock เสมอ
|
// เริ่มต้น clock เสมอ
|
||||||
|
|
@ -911,11 +903,11 @@ onMounted(async () => {
|
||||||
startChecking()
|
startChecking()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await syncPermissionStates()
|
||||||
|
|
||||||
// เรียกแผนที่เฉพาะเมื่อยอมรับ privacy แล้ว
|
// เรียกแผนที่เฉพาะเมื่อยอมรับ privacy แล้ว
|
||||||
if (privacyStore.isAccepted) {
|
if (privacyStore.isAccepted) {
|
||||||
mapRef.value?.requestLocationPermission()
|
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(
|
watch(
|
||||||
() => privacyStore.isAccepted,
|
() => privacyStore.isAccepted,
|
||||||
(newVal) => {
|
async (newVal) => {
|
||||||
if (newVal) {
|
if (newVal) {
|
||||||
|
await syncPermissionStates()
|
||||||
mapRef.value?.requestLocationPermission()
|
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