Invitations: implement viewing, accepting and rejecting invitations.
parent
aed87399c0
commit
c7a4110cbb
@ -0,0 +1,136 @@
|
||||
// SPDX-FileCopyrightText: © 2020 EteSync Authors
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import * as React from "react";
|
||||
import { Switch, Route, Redirect } from "react-router";
|
||||
|
||||
import * as Etebase from "etebase";
|
||||
|
||||
import { useCredentials } from "../credentials";
|
||||
import { routeResolver } from "../App";
|
||||
import LoadingIndicator from "../widgets/LoadingIndicator";
|
||||
|
||||
import AppBarOverride from "../widgets/AppBarOverride";
|
||||
import { List, ListItem } from "../widgets/List";
|
||||
import Container from "../widgets/Container";
|
||||
import { IconButton } from "@material-ui/core";
|
||||
import IconAccept from "@material-ui/icons/Done";
|
||||
import IconReject from "@material-ui/icons/Close";
|
||||
import ConfirmationDialog from "../widgets/ConfirmationDialog";
|
||||
import PrettyFingerprint from "../widgets/PrettyFingerprint";
|
||||
|
||||
async function loadInvitations(etebase: Etebase.Account) {
|
||||
const ret: Etebase.SignedInvitation[] = [];
|
||||
const invitationManager = etebase.getInvitationManager();
|
||||
|
||||
let iterator: string | null = null;
|
||||
let done = false;
|
||||
while (!done) {
|
||||
// FIXME: shouldn't be any
|
||||
const invitations: any = await invitationManager.listIncoming({ iterator, limit: 30 });
|
||||
iterator = invitations.iterator;
|
||||
done = invitations.done;
|
||||
|
||||
ret.push(...invitations.data);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
export default function Invitations() {
|
||||
return (
|
||||
<Switch>
|
||||
<Route
|
||||
path={routeResolver.getRoute("collections.invitations")}
|
||||
exact
|
||||
>
|
||||
<Redirect to={routeResolver.getRoute("collections.invitations.incoming")} />
|
||||
</Route>
|
||||
<Route
|
||||
path={routeResolver.getRoute("collections.invitations.incoming")}
|
||||
exact
|
||||
>
|
||||
<InvitationsIncoming
|
||||
/>
|
||||
</Route>
|
||||
</Switch>
|
||||
);
|
||||
}
|
||||
|
||||
function InvitationsIncoming() {
|
||||
const [invitations, setInvitations] = React.useState<Etebase.SignedInvitation[]>();
|
||||
const [chosenInvitation, setChosenInvitation] = React.useState<Etebase.SignedInvitation>();
|
||||
const etebase = useCredentials()!;
|
||||
|
||||
React.useEffect(() => {
|
||||
loadInvitations(etebase).then(setInvitations);
|
||||
}, [etebase]);
|
||||
|
||||
function removeInvitation(invite: Etebase.SignedInvitation) {
|
||||
setInvitations(invitations?.filter((x) => x.uid !== invite.uid));
|
||||
}
|
||||
|
||||
async function reject(invite: Etebase.SignedInvitation) {
|
||||
const invitationManager = etebase.getInvitationManager();
|
||||
await invitationManager.reject(invite);
|
||||
removeInvitation(invite);
|
||||
}
|
||||
|
||||
async function accept(invite: Etebase.SignedInvitation) {
|
||||
const invitationManager = etebase.getInvitationManager();
|
||||
await invitationManager.accept(invite);
|
||||
setChosenInvitation(undefined);
|
||||
removeInvitation(invite);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<AppBarOverride title="Incoming Invitations" />
|
||||
<Container style={{ maxWidth: "30rem" }}>
|
||||
{invitations ?
|
||||
<List>
|
||||
{(invitations.length > 0 ?
|
||||
invitations.map((invite, idx) => (
|
||||
<ListItem
|
||||
key={invite.uid}
|
||||
rightIcon={(
|
||||
<>
|
||||
<IconButton title="Reject" onClick={() => reject(invite)}>
|
||||
<IconReject color="error" />
|
||||
</IconButton>
|
||||
<IconButton title="Accept" onClick={() => setChosenInvitation(invite)}>
|
||||
<IconAccept color="secondary" />
|
||||
</IconButton>
|
||||
</>
|
||||
)}
|
||||
>
|
||||
Invitation {idx + 1}
|
||||
</ListItem>
|
||||
))
|
||||
:
|
||||
<ListItem>
|
||||
No invitations
|
||||
</ListItem>
|
||||
)}
|
||||
</List>
|
||||
:
|
||||
<LoadingIndicator />
|
||||
}
|
||||
</Container>
|
||||
{chosenInvitation && (
|
||||
<ConfirmationDialog
|
||||
title="Accept invitation"
|
||||
labelOk="OK"
|
||||
open={!!chosenInvitation}
|
||||
onOk={() => accept(chosenInvitation)}
|
||||
onCancel={() => setChosenInvitation(undefined)}
|
||||
>
|
||||
Please verify the inviter's security fingerprint to ensure the invitation is secure:
|
||||
<div style={{ textAlign: "center" }}>
|
||||
<PrettyFingerprint publicKey={chosenInvitation.fromPubkey} />
|
||||
</div>
|
||||
</ConfirmationDialog>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
Loading…
Reference in New Issue