UNPKG

@altrx/gundb-react-auth

Version:
625 lines (539 loc) 18.1 kB
import React, { useState, useEffect } from 'react'; function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; } var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {}; function createCommonjsModule(fn, basedir, module) { return module = { path: basedir, exports: {}, require: function (path, base) { return commonjsRequire(); } }, fn(module, module.exports), module.exports; } function commonjsRequire () { throw new Error('Dynamic requires are not currently supported by @rollup/plugin-commonjs'); } var gundbReactHooks_umd = createCommonjsModule(function (module, exports) { (function (global, factory) { factory(exports, React) ; })(commonjsGlobal, function (exports, react) { function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); } var encryptData = function encryptData(data, keys, sea) { try { return Promise.resolve(keys && sea ? sea.encrypt(data, keys) : data); } catch (e) { return Promise.reject(e); } }; var decryptData = function decryptData(data, keys, sea) { try { return Promise.resolve(keys && sea ? sea.decrypt(data, keys) : data); } catch (e) { return Promise.reject(e); } }; var debouncedUpdates = function debouncedUpdates(dispatcher, timeout) { if (timeout === void 0) { timeout = 100; } var updates = []; var handler; return function (update) { updates.push(update); if (!handler) { handler = setTimeout(function () { var newStateSlice = updates.reduce(function (previousState, _ref) { var id = _ref.id, data = _ref.data; previousState[id] = data; return previousState; }, {}); dispatcher(newStateSlice); updates = []; handler = null; }, timeout); } return function () { clearTimeout(handler); updates = []; handler = null; }; }; }; var reducer = function reducer(state, _ref2) { var _extends2; var data = _ref2.data, type = _ref2.type; switch (type) { case 'add': return _extends({}, state, data); case 'update': return _extends({}, state, (_extends2 = {}, _extends2[data.nodeID] = data, _extends2)); case 'remove': delete state[data]; return _extends({}, state); default: throw new Error(); } }; var useIsMounted = function useIsMounted() { var isMounted = react.useRef(false); react.useEffect(function () { isMounted.current = true; return function () { return isMounted.current = false; }; }, []); return isMounted; }; var useSafeReducer = function useSafeReducer(reducer, initialState) { var _useReducer = react.useReducer(reducer, initialState), state = _useReducer[0], dispatch = _useReducer[1]; var isMounted = useIsMounted(); function safeDispatch(args) { if (isMounted.current) { dispatch(args); } } return [state, safeDispatch]; }; var useGun = function useGun(Gun, opts) { var _useState = react.useState(Gun(_extends({}, opts))), gun = _useState[0], setGun = _useState[1]; react.useEffect(function () { if (opts) { setGun(Gun(_extends({}, opts))); } }, [Gun, opts]); return gun; }; var useGunNamespace = function useGunNamespace(gun, soul) { var _useState2 = react.useState(soul ? gun.user(soul) : gun.user()), namespace = _useState2[0], setNamespace = _useState2[1]; react.useEffect(function () { if (gun && !namespace) { setNamespace(soul ? gun.user(soul) : gun.user()); } }, [namespace, gun, soul]); return namespace; }; var useGunKeyAuth = function useGunKeyAuth(gun, keys, triggerAuth) { if (triggerAuth === void 0) { triggerAuth = true; } // Will attempt to perform a login (when triggerAuth is set to true), // or, if false, returns a namespaced gun node var namespacedGraph = useGunNamespace(gun); var _useState3 = react.useState(false), isLoggedIn = _useState3[0], setIsLoggedIn = _useState3[1]; gun.on('auth', function () { setIsLoggedIn(true); }); react.useEffect(function () { if (namespacedGraph && !namespacedGraph.is && keys && triggerAuth) { namespacedGraph.auth(keys); } }, [triggerAuth, namespacedGraph, keys]); return [namespacedGraph, isLoggedIn]; }; var useGunKeys = function useGunKeys(sea, existingKeys) { var _useState4 = react.useState(existingKeys), newKeys = _useState4[0], setNewKeys = _useState4[1]; react.useEffect(function () { var getKeySet = function getKeySet() { try { return Promise.resolve(sea.pair()).then(function (pair) { setNewKeys(pair); }); } catch (e) { return Promise.reject(e); } }; if (!newKeys && !existingKeys) { getKeySet(); } if (existingKeys) { setNewKeys(existingKeys); } }, [existingKeys, newKeys, sea]); return newKeys; }; var useGunState = function useGunState(ref, opts) { if (opts === void 0) { opts = { appKeys: '', sea: null, interval: 100, useOpen: false }; } var _opts = opts, appKeys = _opts.appKeys, sea = _opts.sea, _opts$interval = _opts.interval, interval = _opts$interval === void 0 ? 100 : _opts$interval, _opts$useOpen = _opts.useOpen, useOpen = _opts$useOpen === void 0 ? false : _opts$useOpen; var _useState5 = react.useState(ref), gunAppGraph = _useState5[0]; var _useSafeReducer = useSafeReducer(reducer, {}), fields = _useSafeReducer[0], dispatch = _useSafeReducer[1]; var handler = react.useRef(null); var isMounted = useIsMounted(); react.useEffect(function () { var debouncedHandlers = []; if (isMounted.current) { var updater = debouncedUpdates(function (data) { dispatch({ type: 'add', data: data }); }, interval); var gunCb = function gunCb(encryptedField, nodeID, message, event) { try { return Promise.resolve(decryptData(encryptedField, appKeys, sea)).then(function (decryptedField) { Object.keys(decryptedField).forEach(function (key) { var cleanFn = updater({ id: key, data: decryptedField[key] }); debouncedHandlers.push(cleanFn); }); if (!handler.current) { handler.current = event; } }); } catch (e) { return Promise.reject(e); } }; if (useOpen) { if (!gunAppGraph.open) { throw new Error('Please include gun/lib/open.'); } else { gunAppGraph.open(gunCb); } } else { gunAppGraph.on(gunCb); } } return function () { if (handler.current) { //cleanup gun .on listener handler.current.off(); } if (debouncedHandlers.length) { //cleanup timeouts debouncedHandlers.forEach(function (c) { return c(); }); } }; // We just need to set the listener once // eslint-disable-next-line react-hooks/exhaustive-deps }, []); // Working with root node fields var put = function put(data) { try { return Promise.resolve(encryptData(data, appKeys, sea)).then(function (encryptedData) { return Promise.resolve(new Promise(function (resolve, reject) { return gunAppGraph.put(encryptedData, function (ack) { return ack.err ? reject(ack.err) : resolve(data); }); })).then(function () {}); }); } catch (e) { return Promise.reject(e); } }; var remove = function remove(field) { try { return Promise.resolve(new Promise(function (resolve, reject) { return gunAppGraph.put(null, function (ack) { return ack.err ? reject(ack.err) : resolve(field); }); })).then(function () { dispatch({ type: 'remove', data: field }); }); } catch (e) { return Promise.reject(e); } }; return { fields: fields, put: put, remove: remove }; }; var useGunCollectionState = function useGunCollectionState(ref, opts) { if (opts === void 0) { opts = { appKeys: '', sea: null, interval: 100, useOpen: false }; } var _opts2 = opts, appKeys = _opts2.appKeys, sea = _opts2.sea, _opts2$interval = _opts2.interval, interval = _opts2$interval === void 0 ? 100 : _opts2$interval, useOpen = _opts2.useOpen; var _useState6 = react.useState(ref), gunAppGraph = _useState6[0]; var _useSafeReducer2 = useSafeReducer(reducer, {}), collection = _useSafeReducer2[0], dispatch = _useSafeReducer2[1]; var handler = react.useRef(null); var isMounted = useIsMounted(); // Working with Sets react.useEffect(function () { var debouncedHandlers = []; if (isMounted.current) { var updater = debouncedUpdates(function (data) { dispatch({ type: 'add', data: data }); }, interval); var gunCb = function gunCb(encryptedNode, nodeID, message, event) { try { return Promise.resolve(decryptData(encryptedNode, appKeys, sea)).then(function (item) { if (item) { var cleanFn = updater({ id: nodeID, data: _extends({}, item, { nodeID: nodeID }) }); debouncedHandlers.push(cleanFn); } if (!handler.current) { handler.current = event; } }); } catch (e) { return Promise.reject(e); } }; if (useOpen) { if (!gunAppGraph.open) { throw new Error('Please include gun/lib/open.'); } else { gunAppGraph.map().open(gunCb); } } else { gunAppGraph.map().on(gunCb); } } return function () { if (handler.current) { //cleanup gun .on listener handler.current.off(); } if (debouncedHandlers.length) { //cleanup timeouts debouncedHandlers.forEach(function (c) { return c(); }); } }; // We just need to set the listener once // eslint-disable-next-line react-hooks/exhaustive-deps }, []); var updateInSet = function updateInSet(nodeID, data) { try { return Promise.resolve(encryptData(data, appKeys, sea)).then(function (encryptedData) { return Promise.resolve(new Promise(function (resolve, reject) { return gunAppGraph.get(nodeID).put(encryptedData, function (ack) { return ack.err ? reject(ack.err) : resolve(data); }); })).then(function () { dispatch({ type: 'update', data: _extends({ nodeID: nodeID }, data) }); }); }); } catch (e) { return Promise.reject(e); } }; var addToSet = function addToSet(data, nodeID) { try { return Promise.resolve(encryptData(data, appKeys, sea)).then(function (encryptedData) { var _temp = function () { if (!nodeID) { return Promise.resolve(new Promise(function (resolve, reject) { return gunAppGraph.set(encryptedData, function (ack) { return ack.err ? reject(ack.err) : resolve(data); }); })).then(function () {}); } else { return Promise.resolve(new Promise(function (resolve, reject) { return gunAppGraph.get(nodeID).put(encryptedData, function (ack) { return ack.err ? reject(ack.err) : resolve(data); }); })).then(function () {}); } }(); if (_temp && _temp.then) return _temp.then(function () {}); }); } catch (e) { return Promise.reject(e); } }; var removeFromSet = function removeFromSet(nodeID) { try { return Promise.resolve(new Promise(function (resolve, reject) { return gunAppGraph.get(nodeID).put(null, function (ack) { return ack.err ? reject(ack.err) : resolve(nodeID); }); })).then(function () {}); } catch (e) { return Promise.reject(e); } }; return { collection: collection, addToSet: addToSet, updateInSet: updateInSet, removeFromSet: removeFromSet }; }; exports.debouncedUpdates = debouncedUpdates; exports.decryptData = decryptData; exports.encryptData = encryptData; exports.reducer = reducer; exports.useGun = useGun; exports.useGunCollectionState = useGunCollectionState; exports.useGunKeyAuth = useGunKeyAuth; exports.useGunKeys = useGunKeys; exports.useGunNamespace = useGunNamespace; exports.useGunState = useGunState; exports.useIsMounted = useIsMounted; exports.useSafeReducer = useSafeReducer; }); }); const GunContext = React.createContext({}); GunContext.displayName = 'GunContext'; const GunProvider = (_ref) => { let { Gun, sea, keyFieldName = 'keys', storage, gunOpts } = _ref, props = _objectWithoutPropertiesLoose(_ref, ["Gun", "sea", "keyFieldName", "storage", "gunOpts"]); if (!sea || !Gun || !gunOpts) { throw new Error(`Provide gunOpts, Gun and sea`); } const [{ isReadyToAuth, existingKeys, keyStatus }, setStatuses] = useState({ isReadyToAuth: '', existingKeys: null, keyStatus: '' }); const gun = gundbReactHooks_umd.useGun(Gun, gunOpts); // new keypair const newKeys = gundbReactHooks_umd.useGunKeys(sea); const [user, isLoggedIn] = gundbReactHooks_umd.useGunKeyAuth(gun, existingKeys, isReadyToAuth === 'ready'); useEffect(() => { if (isLoggedIn && existingKeys && keyStatus === 'new') { const storeKeys = async () => { await storage.setItem(keyFieldName, JSON.stringify(existingKeys)); }; storeKeys(); } }, [isLoggedIn, existingKeys, keyFieldName, storage, keyStatus, user]); useEffect(() => { if (!existingKeys) { const getKeys = async () => { const k = await storage.getItem(keyFieldName); const ks = JSON.parse(k || 'null'); setStatuses({ isReadyToAuth: 'ready', existingKeys: ks, keyStatus: ks ? 'existing' : 'new' }); }; getKeys(); } }, [storage, keyFieldName, setStatuses, existingKeys]); const login = React.useCallback(async keys => { // use keys sent by the user or a new set setStatuses({ isReadyToAuth: 'ready', existingKeys: keys || newKeys, keyStatus: 'new' }); }, [setStatuses, newKeys]); const logout = React.useCallback(onLoggedOut => { const removeKeys = async () => { await storage.removeItem(keyFieldName); onLoggedOut(); }; removeKeys(); }, [keyFieldName, storage]); const value = React.useMemo(() => { const newGunInstance = (opts = gunOpts) => { return Gun(opts); }; return { gun, user, login, logout, sea, appKeys: existingKeys || newKeys, isLoggedIn, newGunInstance }; }, [gun, user, login, logout, sea, newKeys, existingKeys, isLoggedIn, Gun, gunOpts]); return React.createElement(GunContext.Provider, Object.assign({ value: value }, props)); }; function useAuth() { const context = React.useContext(GunContext); if (context === undefined) { throw new Error(`useAuth must be used within a GunProvider`); } return context; } export { GunProvider, useAuth }; //# sourceMappingURL=gundb-react-auth.modern.js.map