// SPDX-FileCopyrightText: © 2017 EteSync Authors // SPDX-License-Identifier: AGPL-3.0-only import * as React from "react"; import { useDispatch, useSelector } from "react-redux"; import { useHistory, useLocation } from "react-router"; import { BrowserRouter } from "react-router-dom"; import { MuiThemeProvider as ThemeProvider, createMuiTheme } from "@material-ui/core/styles"; // v1.x import amber from "@material-ui/core/colors/amber"; import lightBlue from "@material-ui/core/colors/lightBlue"; import AppBar from "@material-ui/core/AppBar"; import Toolbar from "@material-ui/core/Toolbar"; import Drawer from "@material-ui/core/Drawer"; import IconButton from "@material-ui/core/IconButton"; import Badge from "@material-ui/core/Badge"; import NavigationMenu from "@material-ui/icons/Menu"; import NavigationBack from "@material-ui/icons/ArrowBack"; import NavigationRefresh from "@material-ui/icons/Refresh"; import ErrorsIcon from "@material-ui/icons/Error"; import "react-virtualized/styles.css"; // only needs to be imported once import "./App.css"; import ConfirmationDialog from "./widgets/ConfirmationDialog"; import PrettyError from "./widgets/PrettyError"; import { List, ListItem } from "./widgets/List"; import withSpin from "./widgets/withSpin"; import ErrorBoundary from "./components/ErrorBoundary"; import SideMenu from "./SideMenu"; import LoginGate from "./LoginGate"; import { RouteResolver } from "./routes"; import * as store from "./store"; import * as actions from "./store/actions"; import { useCredentials } from "./credentials"; import { SyncManager } from "./sync/SyncManager"; export const routeResolver = new RouteResolver({ home: "", pim: { contacts: { _id: { _base: ":itemUid", edit: "edit", log: "log", }, new: "new", }, events: { _id: { _base: ":itemUid", edit: "edit", duplicate: "duplicate", log: "log", }, new: "new", }, tasks: { _id: { _base: ":itemUid", edit: "edit", log: "log", }, new: "new", }, }, collections: { _id: { _base: ":colUid", edit: "edit", items: { _id: { _base: ":itemUid", }, }, entries: { _id: { _base: ":entryUid", }, }, members: { }, }, new: "new", import: "import", }, settings: { }, debug: { }, }); interface AppBarPropsType { toggleDrawerIcon: any; iconElementRight: any; } function AppBarWitHistory(props: AppBarPropsType) { const location = useLocation(); const history = useHistory(); function canGoBack() { return ( (history!.length > 1) && (location.pathname !== routeResolver.getRoute("pim.contacts")) && (location.pathname !== routeResolver.getRoute("pim.events")) && (location.pathname !== routeResolver.getRoute("pim.tasks")) && (location.pathname !== routeResolver.getRoute("home")) ); } function goBack() { history!.goBack(); } const { toggleDrawerIcon, iconElementRight, ...rest } = props; return ( {!canGoBack() ? toggleDrawerIcon : } {iconElementRight} ); } const IconRefreshWithSpin = withSpin(NavigationRefresh); export default function App() { const [drawerOpen, setDrawerOpen] = React.useState(false); const [errorsDialog, setErrorsDialog] = React.useState(false); const dispatch = useDispatch(); const etebase = useCredentials(); const darkMode = useSelector((state: store.StoreState) => state.settings.darkMode); const fetchCount = useSelector((state: store.StoreState) => state.fetchCount); const errors = useSelector((state: store.StoreState) => state.errors); async function refresh() { const syncManager = SyncManager.getManager(etebase!); const sync = syncManager.sync(); dispatch(actions.performSync(sync)); await sync; } function autoRefresh() { if (navigator.onLine && etebase && !(window.location.pathname.match(/.*\/(new|edit|duplicate)$/))) { refresh(); } } React.useEffect(() => { const interval = 60 * 1000; const id = setInterval(autoRefresh, interval); return () => clearInterval(id); }); function toggleDrawer() { setDrawerOpen(!drawerOpen); } function closeDrawer() { setDrawerOpen(false); } const credentials = etebase ?? null; const fetching = fetchCount > 0; const muiTheme = createMuiTheme({ palette: { type: darkMode ? "dark" : undefined, primary: amber, secondary: { light: lightBlue.A200, main: lightBlue.A400, dark: lightBlue.A700, contrastText: "#fff", }, }, }); const styles = { main: { backgroundColor: muiTheme.palette.background.default, color: muiTheme.palette.text.primary, flexGrow: 1, }, }; return ( } iconElementRight={ <> {(errors.size > 0) && ( setErrorsDialog(true)} title="Parse Errors"> )} > } /> setErrorsDialog(false)} onOk={() => setErrorsDialog(false)} > This should not happen, please contact developers! {errors.map((error, index) => ( (window as any).navigator.clipboard.writeText(`${error.message}\n\n${error.stack}`)} > ))} ); }