create-expo-cljs-app
Version:
Create a react native application with Expo and Shadow-CLJS!
83 lines (70 loc) • 3.29 kB
text/typescript
import { useCallback, useEffect, useRef, useState } from 'react';
import { PermissionResponse } from './PermissionsInterface';
// These types are identical, but improves the readability for suggestions in editors
type RequestPermissionMethod<Permission extends PermissionResponse> = () => Promise<Permission>;
type GetPermissionMethod<Permission extends PermissionResponse> = () => Promise<Permission>;
interface PermissionHookMethods<Permission extends PermissionResponse, Options = never> {
/** The permission method that requests the user to grant permission. */
requestMethod: (options?: Options) => Promise<Permission>;
/** The permission method that only fetches the current permission status. */
getMethod: (options?: Options) => Promise<Permission>;
}
interface PermissionHookBehavior {
/** If the hook should automatically fetch the current permission status, without asking the user. */
get?: boolean;
/** If the hook should automatically request the user to grant permission. */
request?: boolean;
}
export type PermissionHookOptions<Options extends object> = PermissionHookBehavior & Options;
/**
* Get or request permission for protected functionality within the app.
* It uses separate permission requesters to interact with a single permission.
* By default, the hook will only retrieve the permission status.
*/
function usePermission<Permission extends PermissionResponse, Options extends object>(
methods: PermissionHookMethods<Permission, Options>,
options?: PermissionHookOptions<Options>
): [Permission | null, RequestPermissionMethod<Permission>, GetPermissionMethod<Permission>] {
const isMounted = useRef(true);
const [status, setStatus] = useState<Permission | null>(null);
const { get = true, request = false, ...permissionOptions } = options || {};
const getPermission = useCallback(async () => {
const response = await methods.getMethod(
Object.keys(permissionOptions).length > 0 ? (permissionOptions as Options) : undefined
);
if (isMounted.current) setStatus(response);
return response;
}, [methods.getMethod]);
const requestPermission = useCallback(async () => {
const response = await methods.requestMethod(
Object.keys(permissionOptions).length > 0 ? (permissionOptions as Options) : undefined
);
if (isMounted.current) setStatus(response);
return response;
}, [methods.requestMethod]);
useEffect(
function runMethods() {
if (request) requestPermission();
if (!request && get) getPermission();
},
[get, request, requestPermission, getPermission]
);
// Workaround for unmounting components receiving state updates
useEffect(function didMount() {
isMounted.current = true;
return () => {
isMounted.current = false;
};
}, []);
return [status, requestPermission, getPermission];
}
/**
* Create a new permission hook with the permission methods built-in.
* This can be used to quickly create specific permission hooks in every module.
*/
export function createPermissionHook<Permission extends PermissionResponse, Options extends object>(
methods: PermissionHookMethods<Permission, Options>
) {
return (options?: PermissionHookOptions<Options>) =>
usePermission<Permission, Options>(methods, options);
}