@uppy/provider-views
Version:
View library for Uppy remote provider plugins.
176 lines (175 loc) • 5.51 kB
JavaScript
import { h } from 'preact';
import { useCallback, useEffect, useRef, useState } from 'preact/hooks';
import { authorize, ensureScriptsInjected, InvalidTokenError, logout, pollPickingSession, showDrivePicker, showPhotosPicker } from './googlePicker.js';
import AuthView from '../ProviderView/AuthView.js';
import { GoogleDriveIcon, GooglePhotosIcon } from './icons.js';
function useStore(store, key) {
const [value, setValueState] = useState();
useEffect(() => {
;
(async () => {
setValueState(await store.getItem(key));
})();
}, [key, store]);
const setValue = useCallback(async v => {
setValueState(v);
if (v == null) {
return store.removeItem(key);
}
return store.setItem(key, v);
}, [key, store]);
return [value, setValue];
}
export default function GooglePickerView(_ref) {
let {
uppy,
i18n,
clientId,
onFilesPicked,
pickerType,
apiKey,
appId,
storage
} = _ref;
const [loading, setLoading] = useState(false);
const [accessToken, setAccessTokenStored] = useStore(storage, `uppy:google-${pickerType}-picker:accessToken`);
const pickingSessionRef = useRef();
const accessTokenRef = useRef(accessToken);
const shownPickerRef = useRef(false);
const setAccessToken = useCallback(t => {
uppy.log('Access token updated');
setAccessTokenStored(t);
accessTokenRef.current = t;
}, [setAccessTokenStored, uppy]);
// keep access token in sync with the ref
useEffect(() => {
accessTokenRef.current = accessToken;
}, [accessToken]);
const showPicker = useCallback(async signal => {
let newAccessToken = accessToken;
const doShowPicker = async token => {
if (pickerType === 'drive') {
await showDrivePicker({
token,
apiKey,
appId,
onFilesPicked,
signal
});
} else {
// photos
const onPickingSessionChange = newPickingSession => {
pickingSessionRef.current = newPickingSession;
};
await showPhotosPicker({
token,
pickingSession: pickingSessionRef.current,
onPickingSessionChange,
signal
});
}
};
setLoading(true);
try {
try {
await ensureScriptsInjected(pickerType);
if (newAccessToken == null) {
newAccessToken = await authorize({
clientId,
pickerType
});
}
if (newAccessToken == null) throw new Error();
await doShowPicker(newAccessToken);
shownPickerRef.current = true;
setAccessToken(newAccessToken);
} catch (err) {
if (err instanceof InvalidTokenError) {
uppy.log('Token is invalid or expired, reauthenticating');
newAccessToken = await authorize({
pickerType,
accessToken: newAccessToken,
clientId
});
// now try again:
await doShowPicker(newAccessToken);
shownPickerRef.current = true;
setAccessToken(newAccessToken);
} else {
throw err;
}
}
} catch (err) {
if (err instanceof Error && 'type' in err && err.type === 'popup_closed') {
// user closed the auth popup, ignore
} else {
setAccessToken(null);
uppy.log(err);
}
} finally {
setLoading(false);
}
}, [accessToken, apiKey, appId, clientId, onFilesPicked, pickerType, setAccessToken, uppy]);
useEffect(() => {
const abortController = new AbortController();
pollPickingSession({
pickingSessionRef,
accessTokenRef,
signal: abortController.signal,
onFilesPicked,
onError: err => uppy.log(err)
});
return () => abortController.abort();
}, [onFilesPicked, uppy]);
useEffect(() => {
// when mounting, once we have a token, be nice to the user and automatically show the picker
// accessToken === undefined means not yet loaded from storage, so wait for that first
if (accessToken === undefined || shownPickerRef.current) {
return undefined;
}
const abortController = new AbortController();
showPicker(abortController.signal);
return () => {
// only abort the picker if it's not yet shown
if (!shownPickerRef.current) abortController.abort();
};
}, [accessToken, showPicker]);
const handleLogoutClick = useCallback(async () => {
if (accessToken) {
await logout(accessToken);
setAccessToken(null);
pickingSessionRef.current = undefined;
}
}, [accessToken, setAccessToken]);
if (loading) {
return h("div", null, i18n('pleaseWait'), "...");
}
if (accessToken == null) {
return h(AuthView, {
pluginName: pickerType === 'drive' ? i18n('pluginNameGoogleDrivePicker') : i18n('pluginNameGooglePhotosPicker'),
pluginIcon: pickerType === 'drive' ? GoogleDriveIcon : GooglePhotosIcon,
handleAuth: showPicker,
i18n: i18n,
loading: loading
});
}
return h("div", {
style: {
textAlign: 'center'
}
}, h("button", {
type: "button",
className: "uppy-u-reset uppy-c-btn uppy-c-btn-primary",
style: {
display: 'block',
marginBottom: '1em'
},
disabled: loading,
onClick: () => showPicker()
}, pickerType === 'drive' ? i18n('pickFiles') : i18n('pickPhotos')), h("button", {
type: "button",
className: "uppy-u-reset uppy-c-btn",
disabled: loading,
onClick: handleLogoutClick
}, i18n('logOut')));
}