import React, { useEffect, useState } from "react"; import { Preferences } from "@capacitor/preferences"; import Panel from "./Panel"; import AuthContext from "./AuthContext"; import { ResponseError } from "./errors"; import { url } from "./util"; function useStorageItem(key, fromString) { const [storedValue, setStoredValue] = useState(); useEffect(() => { async function loadValue() { try { const result = await Preferences.get({ key }); if (result.value != null) { const value = fromString ? fromString(result.value) : result.value; setStoredValue(value); } else { setStoredValue(undefined); } } catch (e) { return undefined; } } loadValue(); }, [setStoredValue, key, fromString]); const setValue = async (value) => { try { setStoredValue(value); await Preferences.set({ key, value, }); } catch (e) { console.error(e); } }; return [storedValue, setValue]; } function App() { const [num, setNum] = useStorageItem("num", Number); const [role, setRole] = useStorageItem("role"); const [token, setToken] = useStorageItem("token"); const [disabled, setDisabled] = useStorageItem( "disabled", (value) => value === "true" ); useEffect(() => { const setTokenData = (data) => { setToken(data.token); setRole(data.role); setDisabled(data.disabled); }; const timerID = window.setInterval( () => renewToken(token, setTokenData), 60000 // every minute ); return () => window.clearInterval(timerID); }, [token, setToken, setRole, setDisabled]); const login = (newToken, member) => { setNum(member.num); setRole(member.role); setDisabled(member.disabled); setToken(newToken); }; const logout = () => { setNum(null); setRole(null); setDisabled(null); setToken(null); Preferences.clear(); }; const value = { num, role, disabled, token }; return ( <AuthContext.Provider value={value}> <Panel onLogin={login} onLogout={logout} isLogged={isLoggedIn(token)} /> </AuthContext.Provider> ); } function getClaims(token) { const base64Url = token.split(".")[1]; const base64 = base64Url.replace(/-/g, "+").replace(/_/g, "/"); const jsonPayload = decodeURIComponent( atob(base64) .split("") .map(function (c) { return "%" + ("00" + c.charCodeAt(0).toString(16)).slice(-2); }) .join("") ); return JSON.parse(jsonPayload); } function renewToken(oldToken, setTokenData) { if (!oldToken) { return; } fetch(url("/api/token"), { headers: { "x-authentication": oldToken }, }) .then((response) => { if (!response.ok) { throw new ResponseError(response); } return response.json(); }) .then(setTokenData) .catch((error) => { if (error instanceof ResponseError && error.response.status === 401) { setTokenData({ token: "", role: "" }); } else { console.log("Error renewing token: " + error.message); } }); } function isLoggedIn(token) { if (!token) { return false; } const claims = getClaims(token); if (claims["exp"] === undefined) { return true; } return claims["exp"] > Date.now() / 1000; } export default App;