JournalMembers: change to be a function component.

master
Tom Hacohen 4 years ago
parent 6e4689f697
commit 6d18d494a7

@ -12,9 +12,8 @@ import ConfirmationDialog from "../widgets/ConfirmationDialog";
import PrettyFingerprint from "../widgets/PrettyFingerprint";
import * as EteSync from "etesync";
import { CredentialsData } from "../store";
import { handleInputChange } from "../helpers";
import { CredentialsData } from "../store";
interface PropsType {
etesync: CredentialsData;
@ -23,127 +22,104 @@ interface PropsType {
onClose: () => void;
}
class JournalMemberAddDialog extends React.PureComponent<PropsType> {
public state = {
addUser: "",
publicKey: "",
readOnly: false,
userChosen: false,
error: undefined as Error | undefined,
};
private handleInputChange: any;
constructor(props: PropsType) {
super(props);
this.handleInputChange = handleInputChange(this);
this.onAddRequest = this.onAddRequest.bind(this);
this.onOk = this.onOk.bind(this);
}
public render() {
const { onClose } = this.props;
const { addUser, userChosen, publicKey, error } = this.state;
if (error) {
return (
<>
<ConfirmationDialog
title="Error adding member"
labelOk="OK"
open
onOk={onClose}
onCancel={onClose}
>
User ({addUser}) not found. Have they setup their encryption password from one of the apps?
</ConfirmationDialog>
</>
);
}
export default function JournalMemberAddDialog(props: PropsType) {
const [addUser, setAddUser] = React.useState("");
const [publicKey, setPublicKey] = React.useState("");
const [readOnly, setReadOnly] = React.useState(false);
const [userChosen, setUserChosen] = React.useState(false);
const [error, setError] = React.useState<Error>();
if (publicKey) {
return (
<>
<ConfirmationDialog
title="Verify security fingerprint"
labelOk="OK"
open
onOk={this.onOk}
onCancel={onClose}
>
<p>
Verify {addUser}'s security fingerprint to ensure the encryption is secure.
</p>
<div style={{ textAlign: "center" }}>
<PrettyFingerprint publicKey={publicKey} />
</div>
</ConfirmationDialog>
</>
);
} else {
return (
<>
<ConfirmationDialog
title="Add member"
labelOk="OK"
open={!userChosen}
onOk={this.onAddRequest}
onCancel={onClose}
>
{userChosen ?
<LoadingIndicator />
:
<>
<TextField
name="addUser"
type="email"
placeholder="User email"
style={{ width: "100%" }}
value={addUser}
onChange={this.handleInputChange}
/>
<FormControlLabel
control={
<Checkbox
checked={this.state.readOnly}
onChange={(event) => this.setState({ readOnly: event.target.checked })}
/>
}
label="Read only?"
/>
</>
}
</ConfirmationDialog>
</>
);
}
}
private onAddRequest(_user: string) {
this.setState({
userChosen: true,
});
function onAddRequest(_user: string) {
setUserChosen(true);
const { etesync } = this.props;
const { addUser } = this.state;
const { etesync } = props;
const creds = etesync.credentials;
const apiBase = etesync.serviceApiUrl;
const userInfoManager = new EteSync.UserInfoManager(creds, apiBase);
userInfoManager.fetch(addUser).then((userInfo) => {
this.setState({
publicKey: userInfo.publicKey,
});
setPublicKey(userInfo.publicKey);
}).catch((error) => {
this.setState({ error });
setError(error);
});
}
private onOk() {
const { addUser, publicKey, readOnly } = this.state;
this.props.onOk(addUser, publicKey, readOnly);
function onOk() {
props.onOk(addUser, publicKey, readOnly);
}
}
export default JournalMemberAddDialog;
const { onClose } = props;
if (error) {
return (
<>
<ConfirmationDialog
title="Error adding member"
labelOk="OK"
open
onOk={onClose}
onCancel={onClose}
>
User ({addUser}) not found. Have they setup their encryption password from one of the apps?
</ConfirmationDialog>
</>
);
}
if (publicKey) {
return (
<>
<ConfirmationDialog
title="Verify security fingerprint"
labelOk="OK"
open
onOk={onOk}
onCancel={onClose}
>
<p>
Verify {addUser}'s security fingerprint to ensure the encryption is secure.
</p>
<div style={{ textAlign: "center" }}>
<PrettyFingerprint publicKey={publicKey} />
</div>
</ConfirmationDialog>
</>
);
} else {
return (
<>
<ConfirmationDialog
title="Add member"
labelOk="OK"
open={!userChosen}
onOk={onAddRequest}
onCancel={onClose}
>
{userChosen ?
<LoadingIndicator />
:
<>
<TextField
name="addUser"
type="email"
placeholder="User email"
style={{ width: "100%" }}
value={addUser}
onChange={(ev) => setAddUser(ev.target.value)}
/>
<FormControlLabel
control={
<Checkbox
checked={readOnly}
onChange={(event) => setReadOnly(event.target.checked)}
/>
}
label="Read only?"
/>
</>
}
</ConfirmationDialog>
</>
);
}
}

@ -6,7 +6,6 @@ import sjcl from "sjcl";
import { List, ListItem } from "../widgets/List";
import { Theme, withTheme } from "@material-ui/core/styles";
import IconMemberAdd from "@material-ui/icons/PersonAdd";
import VisibilityIcon from "@material-ui/icons/Visibility";
@ -28,140 +27,48 @@ interface PropsType {
userInfo: UserInfoData;
}
interface PropsTypeInner extends PropsType {
theme: Theme;
}
class JournalMembers extends React.PureComponent<PropsTypeInner> {
public state = {
members: null as EteSync.JournalMemberJson[] | null,
revokeUser: null as string | null,
addMemberOpen: false,
};
constructor(props: PropsTypeInner) {
super(props);
this.onRevokeRequest = this.onRevokeRequest.bind(this);
this.onRevokeDo = this.onRevokeDo.bind(this);
this.onMemberAdd = this.onMemberAdd.bind(this);
}
public render() {
const { syncJournal } = this.props;
const { members, revokeUser, addMemberOpen } = this.state;
export default function JournalMembers(props: PropsType) {
const [members, setMembers] = React.useState<EteSync.JournalMemberJson[] | null>(null);
const [revokeUser, setRevokeUser] = React.useState<string | null>(null);
const [addMemberOpen, setAddMemberOpen] = React.useState(false);
const info = syncJournal.collection;
const sharingAllowed = syncJournal.journal.version > 1;
return (
<>
<AppBarOverride title={`${info.displayName} - Members`} />
<Container style={{ maxWidth: "30rem" }}>
{members ?
<List>
<ListItem rightIcon={<IconMemberAdd />} onClick={() => this.setState({ addMemberOpen: true })}>
Add member
</ListItem>
{(members.length > 0 ?
members.map((member) => (
<ListItem
key={member.user}
onClick={() => this.onRevokeRequest(member.user)}
rightIcon={(member.readOnly) ? (<VisibilityIcon />) : undefined}
>
{member.user}
</ListItem>
))
:
<ListItem>
No members
</ListItem>
)}
</List>
:
<LoadingIndicator />
}
</Container>
<ConfirmationDialog
title="Remove member"
labelOk="OK"
open={revokeUser !== null}
onOk={this.onRevokeDo}
onCancel={() => this.setState({ revokeUser: null })}
>
Would you like to revoke {revokeUser}'s access?<br />
Please be advised that a malicious user would potentially be able to retain access to encryption keys. Please refer to the FAQ for more information.
</ConfirmationDialog>
{addMemberOpen &&
(sharingAllowed ?
<JournalMemberAddDialog
etesync={this.props.etesync}
info={info}
onOk={this.onMemberAdd}
onClose={() => this.setState({ addMemberOpen: false })}
/>
:
<ConfirmationDialog
title="Now Allowed"
labelOk="OK"
open
onOk={() => this.setState({ addMemberOpen: false })}
onClose={() => this.setState({ addMemberOpen: false })}
>
Sharing of old-style journals is not allowed. In order to share this journal, create a new one, and copy its contents over using the "import" dialog. If you are experiencing any issues, please contact support.
</ConfirmationDialog>
)
}
</>
);
}
public componentDidMount() {
this.fetchMembers();
}
private fetchMembers() {
const { etesync, syncJournal } = this.props;
function fetchMembers() {
const { etesync, syncJournal } = props;
const info = syncJournal.collection;
const creds = etesync.credentials;
const apiBase = etesync.serviceApiUrl;
const journalMembersManager = new EteSync.JournalMembersManager(creds, apiBase, info.uid);
journalMembersManager.list().then((members) => {
this.setState({
members,
});
setMembers(members);
});
}
private onRevokeRequest(user: string) {
this.setState({
revokeUser: user,
});
React.useEffect(() => {
fetchMembers();
}, []);
function onRevokeRequest(user: string) {
setRevokeUser(user);
}
private onRevokeDo() {
const { etesync, syncJournal } = this.props;
const { revokeUser } = this.state;
function onRevokeDo() {
const { etesync, syncJournal } = props;
const info = syncJournal.collection;
const creds = etesync.credentials;
const apiBase = etesync.serviceApiUrl;
const journalMembersManager = new EteSync.JournalMembersManager(creds, apiBase, info.uid);
journalMembersManager.delete({ user: revokeUser!, key: "" }).then(() => {
this.fetchMembers();
});
this.setState({
revokeUser: null,
fetchMembers();
});
setRevokeUser(null);
}
private onMemberAdd(user: string, publicKey: string, readOnly: boolean) {
const { etesync, syncJournal, userInfo } = this.props;
function onMemberAdd(user: string, publicKey: string, readOnly: boolean) {
const { etesync, syncJournal, userInfo } = props;
const journal = syncJournal.journal;
const derived = this.props.etesync.encryptionKey;
const derived = props.etesync.encryptionKey;
const keyPair = userInfo.getKeyPair(userInfo.getCryptoManager(derived));
const cryptoManager = journal.getCryptoManager(derived, keyPair);
@ -173,12 +80,76 @@ class JournalMembers extends React.PureComponent<PropsTypeInner> {
const apiBase = etesync.serviceApiUrl;
const journalMembersManager = new EteSync.JournalMembersManager(creds, apiBase, journal.uid);
journalMembersManager.create({ user, key: encryptedKey, readOnly }).then(() => {
this.fetchMembers();
});
this.setState({
addMemberOpen: false,
fetchMembers();
});
setAddMemberOpen(false);
}
}
export default withTheme(JournalMembers);
const { syncJournal } = props;
const info = syncJournal.collection;
const sharingAllowed = syncJournal.journal.version > 1;
return (
<>
<AppBarOverride title={`${info.displayName} - Members`} />
<Container style={{ maxWidth: "30rem" }}>
{members ?
<List>
<ListItem rightIcon={<IconMemberAdd />} onClick={() => setAddMemberOpen(true)}>
Add member
</ListItem>
{(members.length > 0 ?
members.map((member) => (
<ListItem
key={member.user}
onClick={() => onRevokeRequest(member.user)}
rightIcon={(member.readOnly) ? (<VisibilityIcon />) : undefined}
>
{member.user}
</ListItem>
))
:
<ListItem>
No members
</ListItem>
)}
</List>
:
<LoadingIndicator />
}
</Container>
<ConfirmationDialog
title="Remove member"
labelOk="OK"
open={revokeUser !== null}
onOk={onRevokeDo}
onCancel={() => setRevokeUser(null)}
>
Would you like to revoke {revokeUser}'s access?<br />
Please be advised that a malicious user would potentially be able to retain access to encryption keys. Please refer to the FAQ for more information.
</ConfirmationDialog>
{addMemberOpen &&
(sharingAllowed ?
<JournalMemberAddDialog
etesync={props.etesync}
info={info}
onOk={onMemberAdd}
onClose={() => setAddMemberOpen(false)}
/>
:
<ConfirmationDialog
title="Now Allowed"
labelOk="OK"
open
onOk={() => setAddMemberOpen(false)}
onClose={() => setAddMemberOpen(false)}
>
Sharing of old-style journals is not allowed. In order to share this journal, create a new one, and copy its contents over using the "import" dialog. If you are experiencing any issues, please contact support.
</ConfirmationDialog>
)
}
</>
);
}

Loading…
Cancel
Save