This repository has been archived on 2022-03-12. You can view files and clone it, but cannot push or open issues or pull requests.
reValuate/node_modules/expo-permissions/build/ExpoPermissions.web.js

338 lines
12 KiB
JavaScript
Raw Normal View History

2021-04-02 02:24:13 +03:00
import { PermissionStatus, } from './Permissions.types';
/*
* TODO: Bacon: https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia#Permissions
* Add messages to manifest like we do with iOS info.plist
*/
// https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia#Using_the_new_API_in_older_browsers
// Older browsers might not implement mediaDevices at all, so we set an empty object first
function _getUserMedia(constraints) {
if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
return navigator.mediaDevices.getUserMedia(constraints);
}
// Some browsers partially implement mediaDevices. We can't just assign an object
// with getUserMedia as it would overwrite existing properties.
// Here, we will just add the getUserMedia property if it's missing.
// First get ahold of the legacy getUserMedia, if present
const getUserMedia = navigator.getUserMedia ||
navigator.webkitGetUserMedia ||
navigator.mozGetUserMedia ||
function () {
const error = new Error('Permission unimplemented');
error.code = 0;
error.name = 'NotAllowedError';
throw error;
};
return new Promise((resolve, reject) => {
getUserMedia.call(navigator, constraints, resolve, reject);
});
}
async function askForMediaPermissionAsync(options) {
try {
await _getUserMedia(options);
return {
status: PermissionStatus.GRANTED,
expires: 'never',
canAskAgain: true,
granted: true,
};
}
catch ({ message }) {
// name: NotAllowedError
// code: 0
if (message === 'Permission dismissed') {
// message: Permission dismissed
return {
status: PermissionStatus.UNDETERMINED,
expires: 'never',
canAskAgain: true,
granted: false,
};
}
else {
// TODO: Bacon: [OSX] The system could deny access to chrome.
// TODO: Bacon: add: { status: 'unimplemented' }
// message: Permission denied
return {
status: PermissionStatus.DENIED,
expires: 'never',
canAskAgain: true,
granted: false,
};
}
}
}
async function askForMicrophonePermissionAsync() {
return await askForMediaPermissionAsync({ audio: true });
}
async function askForCameraPermissionAsync() {
return await askForMediaPermissionAsync({ video: true });
}
async function askForLocationPermissionAsync() {
return new Promise(resolve => {
navigator.geolocation.getCurrentPosition(() => resolve({
status: PermissionStatus.GRANTED,
expires: 'never',
canAskAgain: true,
granted: true,
}), ({ code }) => {
// https://developer.mozilla.org/en-US/docs/Web/API/PositionError/code
if (code === 1) {
resolve({
status: PermissionStatus.DENIED,
expires: 'never',
canAskAgain: true,
granted: false,
});
}
else {
resolve({
status: PermissionStatus.UNDETERMINED,
expires: 'never',
canAskAgain: true,
granted: false,
});
}
});
});
}
async function getPermissionWithQueryAsync(name) {
if (!navigator || !navigator.permissions || !navigator.permissions.query)
return null;
const { state } = await navigator.permissions.query({ name });
if (state === 'prompt') {
return PermissionStatus.UNDETERMINED;
}
else if (state === 'granted') {
return PermissionStatus.GRANTED;
}
else if (state === 'denied') {
return PermissionStatus.DENIED;
}
return null;
}
async function enumerateDevices() {
if (navigator && navigator.mediaDevices && navigator.mediaDevices.enumerateDevices) {
return await navigator.mediaDevices.enumerateDevices();
}
// @ts-ignore: This is deprecated but we should still attempt to use it.
if (window.MediaStreamTrack && typeof window.MediaStreamTrack.getSources === 'function') {
// @ts-ignore
return await MediaStreamTrack.getSources();
}
return null;
}
async function askSensorPermissionAsync() {
const requestPermission = getRequestMotionPermission();
// Technically this is incorrect because it doesn't account for iOS 12.2 Safari.
// But unfortunately we can only abstract so much.
if (!requestPermission)
return PermissionStatus.GRANTED;
// If this isn't invoked in a touch-event then it never resolves.
// Safari probably should throw an error but because it doesn't we have no way of informing the developer.
const status = await requestPermission();
switch (status) {
case 'granted':
return PermissionStatus.GRANTED;
case 'denied':
return PermissionStatus.DENIED;
default:
return PermissionStatus.UNDETERMINED;
}
}
async function getMediaMaybeGrantedAsync(targetKind) {
const devices = await enumerateDevices();
if (!devices) {
return false;
}
const result = await devices
.filter(({ kind }) => kind === targetKind)
.some(({ label }) => label !== '');
// Granted or denied or undetermined or no devices
return result;
}
async function getPermissionAsync(permission, shouldAsk) {
switch (permission) {
case 'userFacingNotifications':
case 'notifications':
{
if (!shouldAsk) {
const status = await getPermissionWithQueryAsync('notifications');
if (status) {
return {
status,
expires: 'never',
granted: status === PermissionStatus.GRANTED,
canAskAgain: true,
};
}
}
const { Notification = {} } = window;
if (Notification.requestPermission) {
let status = Notification.permission;
if (shouldAsk) {
status = await Notification.requestPermission();
}
if (!status || status === 'default') {
return {
status: PermissionStatus.UNDETERMINED,
expires: 'never',
canAskAgain: true,
granted: false,
};
}
return {
status,
expires: 'never',
canAskAgain: true,
granted: status === PermissionStatus.GRANTED,
};
}
}
break;
case 'motion': {
if (shouldAsk) {
const status = await askSensorPermissionAsync();
return {
status,
expires: 'never',
granted: status === PermissionStatus.GRANTED,
canAskAgain: false,
};
}
// We can infer from the requestor if this is an older browser.
const status = getRequestMotionPermission()
? PermissionStatus.UNDETERMINED
: isIOS()
? PermissionStatus.DENIED
: PermissionStatus.GRANTED;
return {
status,
expires: 'never',
canAskAgain: true,
granted: status === PermissionStatus.GRANTED,
};
}
case 'location':
{
const maybeStatus = await getPermissionWithQueryAsync('geolocation');
if (maybeStatus) {
if (maybeStatus === PermissionStatus.UNDETERMINED && shouldAsk) {
return await askForLocationPermissionAsync();
}
return {
status: maybeStatus,
expires: 'never',
canAskAgain: true,
granted: maybeStatus === PermissionStatus.GRANTED,
};
}
else if (shouldAsk) {
// TODO: Bacon: should this function as ask async when not in chrome?
return await askForLocationPermissionAsync();
}
}
break;
case 'audioRecording':
{
const maybeStatus = await getPermissionWithQueryAsync('microphone');
if (maybeStatus) {
if (maybeStatus === PermissionStatus.UNDETERMINED && shouldAsk) {
return await askForMicrophonePermissionAsync();
}
return {
status: maybeStatus,
expires: 'never',
canAskAgain: true,
granted: maybeStatus === PermissionStatus.GRANTED,
};
}
else if (shouldAsk) {
return await askForMicrophonePermissionAsync();
}
else {
const maybeGranted = await getMediaMaybeGrantedAsync('audioinput');
if (maybeGranted) {
return {
status: PermissionStatus.GRANTED,
expires: 'never',
canAskAgain: true,
granted: true,
};
}
// TODO: Bacon: Get denied or undetermined...
}
}
break;
case 'camera':
{
const maybeStatus = await getPermissionWithQueryAsync('camera');
if (maybeStatus) {
if (maybeStatus === PermissionStatus.UNDETERMINED && shouldAsk) {
return await askForCameraPermissionAsync();
}
return {
status: maybeStatus,
expires: 'never',
canAskAgain: true,
granted: maybeStatus === PermissionStatus.GRANTED,
};
}
else if (shouldAsk) {
return await askForCameraPermissionAsync();
}
else {
const maybeGranted = await getMediaMaybeGrantedAsync('videoinput');
if (maybeGranted) {
return {
status: PermissionStatus.GRANTED,
expires: 'never',
canAskAgain: true,
granted: true,
};
}
// TODO: Bacon: Get denied or undetermined...
}
}
break;
default:
break;
}
return {
status: PermissionStatus.UNDETERMINED,
expires: 'never',
canAskAgain: true,
granted: false,
};
}
export default {
get name() {
return 'ExpoPermissions';
},
async getAsync(permissionTypes) {
const results = {};
for (const permissionType of new Set(permissionTypes)) {
results[permissionType] = await getPermissionAsync(permissionType, /* shouldAsk */ false);
}
return results;
},
async askAsync(permissionTypes) {
const results = {};
for (const permissionType of new Set(permissionTypes)) {
results[permissionType] = await getPermissionAsync(permissionType, /* shouldAsk */ true);
}
return results;
},
};
export function getRequestMotionPermission() {
if (typeof DeviceMotionEvent !== 'undefined' && !!DeviceMotionEvent?.requestPermission) {
return DeviceMotionEvent.requestPermission;
}
return null;
}
// https://stackoverflow.com/a/9039885/4047926
function isIOS() {
const isIOSUA = /(iPad|iPhone|iPod)/g.test(navigator.userAgent);
const isIE11 = !!window['MSStream'];
return isIOSUA && !isIE11;
}
//# sourceMappingURL=ExpoPermissions.web.js.map