Add a collections section.

master
Tom Hacohen 4 years ago
parent 1b73899182
commit 96ae079145

@ -4,7 +4,7 @@
import * as React from "react";
import { List as ImmutableList } from "immutable";
import { connect, useDispatch } from "react-redux";
import { useHistory } from "react-router";
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";
@ -67,9 +67,9 @@ export const routeResolver = new RouteResolver({
new: "new",
},
},
journals: {
collections: {
_id: {
_base: ":journalUid",
_base: ":colUid",
edit: "edit",
items: {
_id: {

@ -0,0 +1,83 @@
// SPDX-FileCopyrightText: © 2017 EteSync Authors
// SPDX-License-Identifier: AGPL-3.0-only
import * as React from "react";
import { Link, useHistory } from "react-router-dom";
import IconButton from "@material-ui/core/IconButton";
import IconAdd from "@material-ui/icons/Add";
import ContactsIcon from "@material-ui/icons/Contacts";
import CalendarTodayIcon from "@material-ui/icons/CalendarToday";
import FormatListBulletedIcon from "@material-ui/icons/FormatListBulleted";
import { List, ListItem } from "../widgets/List";
import AppBarOverride from "../widgets/AppBarOverride";
import Container from "../widgets/Container";
import { routeResolver } from "../App";
import { CachedCollection } from "../Pim/helpers";
interface PropsType {
collections: CachedCollection[];
}
export default function CollectionList(props: PropsType) {
const history = useHistory();
const collectionMap = {
"etebase.vcard": [],
"etebase.vevent": [],
"etebase.vtodo": [],
};
function colClicked(colUid: string) {
history.push(routeResolver.getRoute("collections._id", { colUid }));
}
for (const col of props.collections) {
if (collectionMap[col.metadata.type]) {
const colorBox = undefined; // FIXME
collectionMap[col.metadata.type].push((
<ListItem key={col.collection.uid} rightIcon={colorBox} insetChildren
onClick={() => colClicked(col.collection.uid)}>
{col.metadata.name}
</ListItem>
));
}
}
return (
<Container>
<AppBarOverride title="Collections">
<IconButton
component={Link}
title="New"
{...{ to: routeResolver.getRoute("collections.new") }}
>
<IconAdd />
</IconButton>
</AppBarOverride>
<List>
<ListItem
primaryText="Address Books"
leftIcon={<ContactsIcon />}
nestedItems={collectionMap["etebase.vcard"]}
/>
<ListItem
primaryText="Calendars"
leftIcon={<CalendarTodayIcon />}
nestedItems={collectionMap["etebase.vevent"]}
/>
<ListItem
primaryText="Tasks"
leftIcon={<FormatListBulletedIcon />}
nestedItems={collectionMap["etebase.vtodo"]}
/>
</List>
</Container>
);
}

@ -0,0 +1,53 @@
// SPDX-FileCopyrightText: © 2020 EteSync Authors
// SPDX-License-Identifier: AGPL-3.0-only
import * as React from "react";
import { Switch, Route, useHistory } from "react-router";
import { useCredentials } from "../credentials";
import { useCollections } from "../etebase-helpers";
import { routeResolver } from "../App";
import LoadingIndicator from "../widgets/LoadingIndicator";
import { CachedCollection, getDecryptCollectionsFunction, PimFab } from "../Pim/helpers";
import CollectionList from "./CollectionList";
const decryptCollections = getDecryptCollectionsFunction();
export default function CollectionsMain() {
const [cachedCollections, setCachedCollections] = React.useState<CachedCollection[]>();
const history = useHistory();
const etebase = useCredentials()!;
const collections = useCollections(etebase);
React.useEffect(() => {
if (collections) {
decryptCollections(collections)
.then((entries) => setCachedCollections(entries));
// FIXME: handle failure to decrypt collections
}
}, [collections]);
if (!cachedCollections) {
return (
<LoadingIndicator />
);
}
return (
<Switch>
<Route
path={routeResolver.getRoute("collections")}
exact
>
<CollectionList collections={cachedCollections} />
<PimFab
onClick={() => history.push(
routeResolver.getRoute("collections.new")
)}
/>
</Route>
</Switch>
);
}

@ -21,7 +21,7 @@ export function getItemNavigationUid(item: PimType) {
return `${item.collectionUid}|${item.itemUid}`;
}
export function getDecryptCollectionsFunction(_colType: string) {
export function getDecryptCollectionsFunction(_colType?: string) {
return memoize(
async function (collections: Etebase.Collection[]) {
const entries: CachedCollection[] = [];

@ -44,11 +44,11 @@ export default function SideMenu(props: PropsType) {
loggedInItems = (
<React.Fragment>
<ListItem
primaryText="Journals"
primaryText="Collections"
leftIcon={<ActionJournals />}
onClick={() => {
props.onCloseDrawerRequest();
history.push(routeResolver.getRoute("journals"));
history.push(routeResolver.getRoute("collections"));
}}
/>
<ListItem
@ -56,7 +56,7 @@ export default function SideMenu(props: PropsType) {
leftIcon={<IconImport />}
onClick={() => {
props.onCloseDrawerRequest();
history.push(routeResolver.getRoute("journals.import"));
history.push(routeResolver.getRoute("collections.import"));
}}
/>
<ListItem

@ -8,7 +8,7 @@ import { Route, Switch, Redirect, useHistory } from "react-router";
import moment from "moment";
import "moment/locale/en-gb";
import { List, Map } from "immutable";
import { Map } from "immutable";
import { routeResolver } from "./App";
@ -18,13 +18,11 @@ import Container from "./widgets/Container";
import ContactsMain from "./Contacts/Main";
import CalendarsMain from "./Calendars/Main";
import TasksMain from "./Tasks/Main";
import CollectionsMain from "./Collections/Main";
import Journals from "./Journals";
import Settings from "./Settings";
import Debug from "./Debug";
import * as EteSync from "etesync";
import { SyncManager } from "./sync/SyncManager";
import { StoreState } from "./store";
@ -32,20 +30,12 @@ import { performSync } from "./store/actions";
import { useCredentials } from "./credentials";
import PimNavigationTabs from "./Pim/NavigationTabs";
export interface SyncInfoJournal {
journal: EteSync.Journal;
journalEntries: List<EteSync.Entry>;
collection: EteSync.CollectionInfo;
entries: List<EteSync.SyncEntry>;
}
export type SyncInfoJournal = any;
export type SyncInfo = Map<string, SyncInfoJournal>;
export default function SyncGate() {
const etebase = useCredentials();
const settings = useSelector((state: StoreState) => state.settings);
const userInfo = useSelector((state: StoreState) => state.cache.userInfo);
const journals = useSelector((state: StoreState) => state.cache.journals);
const dispatch = useDispatch();
const [loading, setLoading] = React.useState(true);
@ -69,9 +59,6 @@ export default function SyncGate() {
// FIXME: Shouldn't be here
moment.locale(settings.locale);
// FIXME: remove
const etesync = etebase as any;
return (
<Switch>
<Route
@ -118,23 +105,11 @@ export default function SyncGate() {
</Route>
</Switch>
</Route>
{false && (
<>
<Route
path={routeResolver.getRoute("journals")}
render={({ location, history }) => (
<Journals
etesync={etesync}
userInfo={userInfo}
syncInfo={false as any}
journals={journals}
location={location}
history={history}
/>
)}
/>
</>
)}
path={routeResolver.getRoute("collections")}
>
<CollectionsMain />
</Route>
<Route
path={routeResolver.getRoute("settings")}
exact

@ -57,10 +57,12 @@ export const getCollectionManager = memoize(function (etebase: Etebase.Account)
});
// React specific stuff
export function useCollections(etebase: Etebase.Account, colType: string) {
export function useCollections(etebase: Etebase.Account, colType?: string) {
const cachedCollections = useSelector((state: StoreState) => state.cache2.collections);
return usePromiseMemo(
getCollectionsByType(cachedCollections, colType, etebase),
(colType) ?
getCollectionsByType(cachedCollections, colType, etebase) :
getCollections(cachedCollections, etebase),
[etebase, cachedCollections, colType]
);
}

Loading…
Cancel
Save