|
|
|
// SPDX-FileCopyrightText: © 2017 EteSync Authors
|
|
|
|
// SPDX-License-Identifier: AGPL-3.0-only
|
|
|
|
|
|
|
|
import * as React from "react";
|
|
|
|
import * as Etebase from "etebase";
|
|
|
|
import { Redirect, useLocation } from "react-router";
|
|
|
|
import Button from "@material-ui/core/Button";
|
|
|
|
import Alert from "@material-ui/lab/Alert";
|
|
|
|
|
|
|
|
import { routeResolver } from "./App";
|
|
|
|
|
|
|
|
import Container from "./widgets/Container";
|
|
|
|
import LoadingIndicator from "./widgets/LoadingIndicator";
|
|
|
|
import Wizard, { WizardNavigationBar, PagePropsType } from "./widgets/Wizard";
|
|
|
|
|
|
|
|
import { SyncManager } from "./sync/SyncManager";
|
|
|
|
|
|
|
|
import { store } from "./store";
|
|
|
|
import { useCredentials } from "./credentials";
|
|
|
|
|
|
|
|
import wizardWelcome from "./images/wizard-welcome.svg";
|
|
|
|
import wizardCreate from "./images/wizard-create.svg";
|
|
|
|
|
|
|
|
interface LocationState {
|
|
|
|
from: {
|
|
|
|
pathname: string;
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
const wizardPages = [
|
|
|
|
(props: PagePropsType) => (
|
|
|
|
<>
|
|
|
|
<div style={{ display: "flex", flexDirection: "column", flex: 1, justifyContent: "center", alignItems: "center" }}>
|
|
|
|
<h2 style={{ textAlign: "center" }}>Welcome to EteSync!</h2>
|
|
|
|
<p style={{ textAlign: "center" }}>
|
|
|
|
Please follow these few quick steps to help you get started.
|
|
|
|
</p>
|
|
|
|
<img src={wizardWelcome} style={{ maxWidth: "30em", marginTop: "2em" }} />
|
|
|
|
</div>
|
|
|
|
<WizardNavigationBar {...props} />
|
|
|
|
</>
|
|
|
|
),
|
|
|
|
(props: PagePropsType) => (
|
|
|
|
<SetupCollectionsPage {...props} />
|
|
|
|
),
|
|
|
|
];
|
|
|
|
|
|
|
|
function SetupCollectionsPage(props: PagePropsType) {
|
|
|
|
const etebase = useCredentials()!;
|
|
|
|
const [error, setError] = React.useState<Error>();
|
|
|
|
const [loading, setLoading] = React.useState(false);
|
|
|
|
async function onNext() {
|
|
|
|
setLoading(true);
|
|
|
|
try {
|
|
|
|
const colMgr = etebase.getCollectionManager();
|
|
|
|
const types = [
|
|
|
|
["etebase.vcard", "My Contacts"],
|
|
|
|
["etebase.vevent", "My Calendar"],
|
|
|
|
["etebase.vtodo", "My Tasks"],
|
|
|
|
];
|
|
|
|
for (const [type, name] of types) {
|
|
|
|
const meta: Etebase.ItemMetadata = {
|
|
|
|
name,
|
|
|
|
mtime: (new Date()).getTime(),
|
|
|
|
};
|
|
|
|
const collection = await colMgr.create(type, meta, "");
|
|
|
|
await colMgr.upload(collection);
|
|
|
|
}
|
|
|
|
|
|
|
|
props.next?.();
|
|
|
|
} catch (e) {
|
|
|
|
setError(e);
|
|
|
|
} finally {
|
|
|
|
setLoading(false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const next = (loading) ? undefined : onNext;
|
|
|
|
return (
|
|
|
|
<>
|
|
|
|
<div style={{ display: "flex", flexDirection: "column", flex: 1, justifyContent: "center", alignItems: "center" }}>
|
|
|
|
<h2 style={{ textAlign: "center" }}>Setup Collections</h2>
|
|
|
|
<p style={{ textAlign: "center", maxWidth: "50em" }}>
|
|
|
|
In order to start using EteSync you need to create collections to store your data. Clicking <i>Finish</i> below will create a default calendar, address book and a task list for you.
|
|
|
|
</p>
|
|
|
|
{(loading) ? (
|
|
|
|
<LoadingIndicator style={{ display: "block", margin: "40px auto" }} />
|
|
|
|
) : (error) ? (
|
|
|
|
<>
|
|
|
|
<Alert severity="error">{error.message}</Alert>
|
|
|
|
<Button
|
|
|
|
variant="contained"
|
|
|
|
color="primary"
|
|
|
|
onClick={props.next}
|
|
|
|
>
|
|
|
|
Skip
|
|
|
|
</Button>
|
|
|
|
</>
|
|
|
|
) : (
|
|
|
|
<img src={wizardCreate} style={{ maxWidth: "30em", marginTop: "2em" }} />
|
|
|
|
)}
|
|
|
|
</div>
|
|
|
|
<WizardNavigationBar {...props} next={next} />
|
|
|
|
</>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
export default function WizardPage() {
|
|
|
|
const [tryCount, setTryCount] = React.useState(0);
|
|
|
|
const [ranWizard, setRanWizard] = React.useState(false);
|
|
|
|
const [syncError, setSyncError] = React.useState<Error>();
|
|
|
|
const etebase = useCredentials();
|
|
|
|
const location = useLocation();
|
|
|
|
const [loading, setLoading] = React.useState(true);
|
|
|
|
|
|
|
|
React.useEffect(() => {
|
|
|
|
setLoading(true);
|
|
|
|
setSyncError(undefined);
|
|
|
|
(async () => {
|
|
|
|
const syncManager = SyncManager.getManager(etebase!);
|
|
|
|
const sync = syncManager.sync(true);
|
|
|
|
try {
|
|
|
|
await sync;
|
|
|
|
|
|
|
|
const cachedCollection = store.getState().cache.collections;
|
|
|
|
// XXX new account - though should change test to see if there are any PIM types
|
|
|
|
if (cachedCollection.size > 0) {
|
|
|
|
setRanWizard(true);
|
|
|
|
}
|
|
|
|
} catch (e) {
|
|
|
|
setSyncError(e);
|
|
|
|
}
|
|
|
|
setLoading(false);
|
|
|
|
})();
|
|
|
|
}, [tryCount]);
|
|
|
|
|
|
|
|
if (syncError) {
|
|
|
|
return (
|
|
|
|
<Container>
|
|
|
|
<div style={{ display: "flex", flexDirection: "column", flex: 1, justifyContent: "center", alignItems: "center" }}>
|
|
|
|
<h2 style={{ textAlign: "center" }}>Important</h2>
|
|
|
|
<p style={{ textAlign: "center" }}>
|
|
|
|
{syncError?.message}
|
|
|
|
</p>
|
|
|
|
<Button
|
|
|
|
variant="contained"
|
|
|
|
color="primary"
|
|
|
|
onClick={() => setTryCount(tryCount + 1)}
|
|
|
|
>
|
|
|
|
Retry
|
|
|
|
</Button>
|
|
|
|
</div>
|
|
|
|
</Container>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (loading) {
|
|
|
|
return (<LoadingIndicator style={{ display: "block", margin: "40px auto" }} />);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!ranWizard) {
|
|
|
|
return (
|
|
|
|
<Wizard pages={wizardPages} onFinish={() => setRanWizard(true)} style={{ display: "flex", flexDirection: "column", flex: 1 }} />
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
const { from } = location.state as LocationState || { from: { pathname: routeResolver.getRoute("home") } };
|
|
|
|
return (
|
|
|
|
<Redirect to={from.pathname} />
|
|
|
|
);
|
|
|
|
}
|