Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

expo-permissions

Package Overview
Dependencies
Maintainers
21
Versions
58
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

expo-permissions - npm Package Compare versions

Comparing version 8.0.0-rc.0 to 8.0.0

android/src/main/java/expo/modules/permissions/PermissionsModule.kt

2

build/CoalescedPermissions.d.ts
import { PermissionExpiration, PermissionMap, PermissionStatus } from './Permissions.types';
export declare function coalesceStatuses(permissions: PermissionMap): PermissionStatus;
export declare function coalesceExpirations(permissions: PermissionMap): PermissionExpiration;
export declare function coalesceCanAskAgain(permissions: PermissionMap): boolean;
export declare function coalesceGranted(permissions: PermissionMap): boolean;

@@ -31,2 +31,8 @@ import { PermissionStatus } from './Permissions.types';

}
export function coalesceCanAskAgain(permissions) {
return Object.keys(permissions).reduce((canAskAgain, type) => canAskAgain && permissions[type].canAskAgain, true);
}
export function coalesceGranted(permissions) {
return Object.keys(permissions).reduce((granted, type) => granted && permissions[type].granted, true);
}
//# sourceMappingURL=CoalescedPermissions.js.map

@@ -32,3 +32,8 @@ import { PermissionStatus, } from './Permissions.types';

await _getUserMedia(options);
return { status: PermissionStatus.GRANTED, expires: 'never' };
return {
status: PermissionStatus.GRANTED,
expires: 'never',
canAskAgain: true,
granted: true,
};
}

@@ -40,3 +45,8 @@ catch ({ message }) {

// message: Permission dismissed
return { status: PermissionStatus.UNDETERMINED, expires: 'never' };
return {
status: PermissionStatus.UNDETERMINED,
expires: 'never',
canAskAgain: true,
granted: false,
};
}

@@ -47,3 +57,8 @@ else {

// message: Permission denied
return { status: PermissionStatus.DENIED, expires: 'never' };
return {
status: PermissionStatus.DENIED,
expires: 'never',
canAskAgain: true,
granted: false,
};
}

@@ -60,9 +75,24 @@ }

return new Promise(resolve => {
navigator.geolocation.getCurrentPosition(() => resolve({ status: PermissionStatus.GRANTED, expires: 'never' }), ({ code }) => {
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' });
resolve({
status: PermissionStatus.DENIED,
expires: 'never',
canAskAgain: true,
granted: false,
});
}
else {
resolve({ status: PermissionStatus.UNDETERMINED, expires: 'never' });
resolve({
status: PermissionStatus.UNDETERMINED,
expires: 'never',
canAskAgain: true,
granted: false,
});
}

@@ -72,2 +102,39 @@ });

}
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 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) {

@@ -78,2 +145,13 @@ switch (permission) {

{
if (!shouldAsk) {
const status = await getPermissionWithQueryAsync('notifications');
if (status) {
return {
status,
expires: 'never',
granted: status === PermissionStatus.GRANTED,
canAskAgain: true,
};
}
}
const { Notification = {} } = window;

@@ -86,5 +164,15 @@ if (Notification.requestPermission) {

if (!status || status === 'default') {
return { status: PermissionStatus.UNDETERMINED, expires: 'never' };
return {
status: PermissionStatus.UNDETERMINED,
expires: 'never',
canAskAgain: true,
granted: false,
};
}
return { status, expires: 'never' };
return {
status,
expires: 'never',
canAskAgain: true,
granted: status === PermissionStatus.GRANTED,
};
}

@@ -95,12 +183,13 @@ }

{
const { navigator = {} } = window;
if (navigator.permissions) {
const { state } = await navigator.permissions.query({ name: 'geolocation' });
if (state !== PermissionStatus.GRANTED && state !== PermissionStatus.DENIED) {
if (shouldAsk) {
return await askForLocationPermissionAsync();
}
return { status: PermissionStatus.UNDETERMINED, expires: 'never' };
const maybeStatus = await getPermissionWithQueryAsync('geolocation');
if (maybeStatus) {
if (maybeStatus === PermissionStatus.UNDETERMINED && shouldAsk) {
return await askForLocationPermissionAsync();
}
return { status: state, expires: 'never' };
return {
status: maybeStatus,
expires: 'never',
canAskAgain: true,
granted: maybeStatus === PermissionStatus.GRANTED,
};
}

@@ -114,16 +203,62 @@ else if (shouldAsk) {

case 'audioRecording':
if (shouldAsk) {
return await askForMicrophonePermissionAsync();
{
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...
}
}
else {
//TODO: Bacon: Is it possible to get this permission?
}
break;
case 'camera':
if (shouldAsk) {
return await askForCameraPermissionAsync();
{
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...
}
}
else {
//TODO: Bacon: Is it possible to get this permission?
}
break;

@@ -133,3 +268,8 @@ default:

}
return { status: PermissionStatus.UNDETERMINED, expires: 'never' };
return {
status: PermissionStatus.UNDETERMINED,
expires: 'never',
canAskAgain: true,
granted: false,
};
}

@@ -136,0 +276,0 @@ export default {

@@ -1,2 +0,3 @@

import { coalesceExpirations, coalesceStatuses } from './CoalescedPermissions';
import { Platform } from 'react-native';
import { coalesceExpirations, coalesceStatuses, coalesceCanAskAgain, coalesceGranted, } from './CoalescedPermissions';
import Permissions from './ExpoPermissions';

@@ -16,7 +17,32 @@ import { PermissionStatus, } from './Permissions.types';

export async function getAsync(...types) {
if (Platform.OS === 'ios') {
return await _handleMultiPermissionsRequestIOSAsync(types, Permissions.getAsync);
}
return await _handlePermissionsRequestAsync(types, Permissions.getAsync);
}
export async function askAsync(...types) {
if (Platform.OS === 'ios') {
return await _handleMultiPermissionsRequestIOSAsync(types, Permissions.askAsync);
}
return await _handlePermissionsRequestAsync(types, Permissions.askAsync);
}
async function _handleSinglePermissionRequestIOSAsync(type, handlePermission) {
return await handlePermission(type);
}
async function _handleMultiPermissionsRequestIOSAsync(types, handlePermission) {
if (!types.length) {
throw new Error('At least one permission type must be specified');
}
let permissions = {};
for (let type of types) {
permissions[type] = await _handleSinglePermissionRequestIOSAsync(type, handlePermission);
}
return {
status: coalesceStatuses(permissions),
expires: coalesceExpirations(permissions),
canAskAgain: coalesceCanAskAgain(permissions),
granted: coalesceGranted(permissions),
permissions,
};
}
async function _handlePermissionsRequestAsync(types, handlePermissions) {

@@ -30,2 +56,4 @@ if (!types.length) {

expires: coalesceExpirations(permissions),
canAskAgain: coalesceCanAskAgain(permissions),
granted: coalesceGranted(permissions),
permissions,

@@ -32,0 +60,0 @@ };

23

build/Permissions.types.d.ts

@@ -0,22 +1,15 @@

import { PermissionResponse as UMPermissionResponse, PermissionStatus, PermissionExpiration } from 'unimodules-permissions-interface';
export declare type PermissionType = 'camera' | 'cameraRoll' | 'audioRecording' | 'location' | 'userFacingNotifications' | 'notifications' | 'contacts' | 'calendar' | 'reminders' | 'systemBrightness';
export declare type PermissionResponse = {
status: PermissionStatus;
expires: PermissionExpiration;
export interface PermissionResponse extends UMPermissionResponse {
permissions: PermissionMap;
};
export declare type PermissionMap = {
}
export interface PermissionMap {
[permissionType: string]: PermissionInfo;
};
export declare type PermissionInfo = {
status: PermissionStatus;
expires: PermissionExpiration;
}
export interface PermissionInfo extends UMPermissionResponse {
ios?: PermissionDetailsLocationIOS;
android?: PermissionDetailsLocationAndroid;
};
export declare enum PermissionStatus {
UNDETERMINED = "undetermined",
GRANTED = "granted",
DENIED = "denied"
}
export declare type PermissionExpiration = 'never' | number;
export { PermissionStatus };
export { PermissionExpiration };
export declare type PermissionDetailsLocationIOS = {

@@ -23,0 +16,0 @@ scope: 'whenInUse' | 'always';

@@ -1,7 +0,3 @@

export var PermissionStatus;
(function (PermissionStatus) {
PermissionStatus["UNDETERMINED"] = "undetermined";
PermissionStatus["GRANTED"] = "granted";
PermissionStatus["DENIED"] = "denied";
})(PermissionStatus || (PermissionStatus = {}));
import { PermissionStatus, } from 'unimodules-permissions-interface';
export { PermissionStatus };
//# sourceMappingURL=Permissions.types.js.map
{
"name": "expo-permissions",
"version": "8.0.0-rc.0",
"version": "8.0.0",
"description": "Allows you prompt for various permissions to access device sensors, personal data, etc.",

@@ -43,3 +43,3 @@ "main": "build/Permissions.js",

},
"gitHead": "c93271fac3b0e9e8874be0f7a2cfc8f5a47562b9"
"gitHead": "ec7878b9ce54f2537721218ae0fe4017e4004806"
}

@@ -37,1 +37,15 @@ import { PermissionExpiration, PermissionMap, PermissionStatus } from './Permissions.types';

}
export function coalesceCanAskAgain(permissions: PermissionMap): boolean {
return Object.keys(permissions).reduce<boolean>(
(canAskAgain, type) => canAskAgain && permissions[type].canAskAgain,
true
);
}
export function coalesceGranted(permissions: PermissionMap): boolean {
return Object.keys(permissions).reduce<boolean>(
(granted, type) => granted && permissions[type].granted,
true
);
}
import {
PermissionInfo,
PermissionMap,
PermissionType,
PermissionStatus,
PermissionType,
PermissionInfo,
} from './Permissions.types';

@@ -46,3 +46,8 @@

await _getUserMedia(options);
return { status: PermissionStatus.GRANTED, expires: 'never' };
return {
status: PermissionStatus.GRANTED,
expires: 'never',
canAskAgain: true,
granted: true,
};
} catch ({ message }) {

@@ -53,3 +58,8 @@ // name: NotAllowedError

// message: Permission dismissed
return { status: PermissionStatus.UNDETERMINED, expires: 'never' };
return {
status: PermissionStatus.UNDETERMINED,
expires: 'never',
canAskAgain: true,
granted: false,
};
} else {

@@ -59,3 +69,8 @@ // TODO: Bacon: [OSX] The system could deny access to chrome.

// message: Permission denied
return { status: PermissionStatus.DENIED, expires: 'never' };
return {
status: PermissionStatus.DENIED,
expires: 'never',
canAskAgain: true,
granted: false,
};
}

@@ -76,9 +91,25 @@ }

navigator.geolocation.getCurrentPosition(
() => resolve({ status: PermissionStatus.GRANTED, expires: 'never' }),
() =>
resolve({
status: PermissionStatus.GRANTED,
expires: 'never',
canAskAgain: true,
granted: true,
}),
({ code }: PositionError) => {
// https://developer.mozilla.org/en-US/docs/Web/API/PositionError/code
if (code === 1) {
resolve({ status: PermissionStatus.DENIED, expires: 'never' });
resolve({
status: PermissionStatus.DENIED,
expires: 'never',
canAskAgain: true,
granted: false,
});
} else {
resolve({ status: PermissionStatus.UNDETERMINED, expires: 'never' });
resolve({
status: PermissionStatus.UNDETERMINED,
expires: 'never',
canAskAgain: true,
granted: false,
});
}

@@ -90,2 +121,41 @@ }

async function getPermissionWithQueryAsync(name: PermissionName): Promise<PermissionStatus | null> {
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(): Promise<MediaDeviceInfo[] | null> {
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 getMediaMaybeGrantedAsync(targetKind: MediaDeviceKind): Promise<boolean> {
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(

@@ -99,2 +169,14 @@ permission: PermissionType,

{
if (!shouldAsk) {
const status = await getPermissionWithQueryAsync('notifications');
if (status) {
return {
status,
expires: 'never',
granted: status === PermissionStatus.GRANTED,
canAskAgain: true,
};
}
}
const { Notification = {} } = window as any;

@@ -107,5 +189,15 @@ if (Notification.requestPermission) {

if (!status || status === 'default') {
return { status: PermissionStatus.UNDETERMINED, expires: 'never' };
return {
status: PermissionStatus.UNDETERMINED,
expires: 'never',
canAskAgain: true,
granted: false,
};
}
return { status, expires: 'never' };
return {
status,
expires: 'never',
canAskAgain: true,
granted: status === PermissionStatus.GRANTED,
};
}

@@ -116,13 +208,13 @@ }

{
const { navigator = {} } = window as any;
if (navigator.permissions) {
const { state } = await navigator.permissions.query({ name: 'geolocation' });
if (state !== PermissionStatus.GRANTED && state !== PermissionStatus.DENIED) {
if (shouldAsk) {
return await askForLocationPermissionAsync();
}
return { status: PermissionStatus.UNDETERMINED, expires: 'never' };
const maybeStatus = await getPermissionWithQueryAsync('geolocation');
if (maybeStatus) {
if (maybeStatus === PermissionStatus.UNDETERMINED && shouldAsk) {
return await askForLocationPermissionAsync();
}
return { status: state, expires: 'never' };
return {
status: maybeStatus,
expires: 'never',
canAskAgain: true,
granted: maybeStatus === PermissionStatus.GRANTED,
};
} else if (shouldAsk) {

@@ -135,13 +227,57 @@ // TODO: Bacon: should this function as ask async when not in chrome?

case 'audioRecording':
if (shouldAsk) {
return await askForMicrophonePermissionAsync();
} else {
//TODO: Bacon: Is it possible to get this permission?
{
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':
if (shouldAsk) {
return await askForCameraPermissionAsync();
} else {
//TODO: Bacon: Is it possible to get this permission?
{
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...
}
}

@@ -152,3 +288,8 @@ break;

}
return { status: PermissionStatus.UNDETERMINED, expires: 'never' };
return {
status: PermissionStatus.UNDETERMINED,
expires: 'never',
canAskAgain: true,
granted: false,
};
}

@@ -155,0 +296,0 @@

@@ -1,3 +0,10 @@

import { coalesceExpirations, coalesceStatuses } from './CoalescedPermissions';
import { Platform } from 'react-native';
import {
coalesceExpirations,
coalesceStatuses,
coalesceCanAskAgain,
coalesceGranted,
} from './CoalescedPermissions';
import Permissions from './ExpoPermissions';
import {

@@ -33,2 +40,5 @@ PermissionResponse,

export async function getAsync(...types: PermissionType[]): Promise<PermissionResponse> {
if (Platform.OS === 'ios') {
return await _handleMultiPermissionsRequestIOSAsync(types, Permissions.getAsync);
}
return await _handlePermissionsRequestAsync(types, Permissions.getAsync);

@@ -38,5 +48,37 @@ }

export async function askAsync(...types: PermissionType[]): Promise<PermissionResponse> {
if (Platform.OS === 'ios') {
return await _handleMultiPermissionsRequestIOSAsync(types, Permissions.askAsync);
}
return await _handlePermissionsRequestAsync(types, Permissions.askAsync);
}
async function _handleSinglePermissionRequestIOSAsync(
type: PermissionType,
handlePermission: (type: PermissionType) => Promise<PermissionInfo>
): Promise<PermissionInfo> {
return await handlePermission(type);
}
async function _handleMultiPermissionsRequestIOSAsync(
types: PermissionType[],
handlePermission: (type: PermissionType) => Promise<PermissionInfo>
): Promise<PermissionResponse> {
if (!types.length) {
throw new Error('At least one permission type must be specified');
}
let permissions = {};
for (let type of types) {
permissions[type] = await _handleSinglePermissionRequestIOSAsync(type, handlePermission);
}
return {
status: coalesceStatuses(permissions),
expires: coalesceExpirations(permissions),
canAskAgain: coalesceCanAskAgain(permissions),
granted: coalesceGranted(permissions),
permissions,
};
}
async function _handlePermissionsRequestAsync(

@@ -54,4 +96,6 @@ types: PermissionType[],

expires: coalesceExpirations(permissions),
canAskAgain: coalesceCanAskAgain(permissions),
granted: coalesceGranted(permissions),
permissions,
};
}

@@ -0,1 +1,7 @@

import {
PermissionResponse as UMPermissionResponse,
PermissionStatus,
PermissionExpiration,
} from 'unimodules-permissions-interface';
export type PermissionType =

@@ -13,25 +19,19 @@ | 'camera'

export type PermissionResponse = {
status: PermissionStatus;
expires: PermissionExpiration;
export interface PermissionResponse extends UMPermissionResponse {
permissions: PermissionMap;
};
}
export type PermissionMap = { [permissionType: string /* PermissionType */]: PermissionInfo };
export interface PermissionMap {
[permissionType: string /* PermissionType */]: PermissionInfo;
}
export type PermissionInfo = {
status: PermissionStatus;
expires: PermissionExpiration;
export interface PermissionInfo extends UMPermissionResponse {
ios?: PermissionDetailsLocationIOS;
android?: PermissionDetailsLocationAndroid;
};
export enum PermissionStatus {
UNDETERMINED = 'undetermined',
GRANTED = 'granted',
DENIED = 'denied',
}
export type PermissionExpiration = 'never' | number;
export { PermissionStatus };
export { PermissionExpiration };
export type PermissionDetailsLocationIOS = {

@@ -38,0 +38,0 @@ scope: 'whenInUse' | 'always';

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc