From c69f5e27ad54abc80263742dd2a73e21074f1abb Mon Sep 17 00:00:00 2001 From: Tom Hacohen Date: Thu, 6 Aug 2020 10:20:27 +0300 Subject: [PATCH] Journals: remove unused journals components. --- src/Journals/ImportDialog.tsx | 207 ------------------- src/Journals/Journal.tsx | 104 ---------- src/Journals/JournalEdit.tsx | 214 ------------------- src/Journals/JournalMemberAddDialog.tsx | 125 ------------ src/Journals/JournalMembers.tsx | 155 -------------- src/Journals/JournalsList.tsx | 103 ---------- src/Journals/JournalsListImport.tsx | 96 --------- src/Journals/index.tsx | 181 ---------------- src/Journals/journalView.tsx | 68 ------ src/components/JournalEntries.tsx | 261 ------------------------ 10 files changed, 1514 deletions(-) delete mode 100644 src/Journals/ImportDialog.tsx delete mode 100644 src/Journals/Journal.tsx delete mode 100644 src/Journals/JournalEdit.tsx delete mode 100644 src/Journals/JournalMemberAddDialog.tsx delete mode 100644 src/Journals/JournalMembers.tsx delete mode 100644 src/Journals/JournalsList.tsx delete mode 100644 src/Journals/JournalsListImport.tsx delete mode 100644 src/Journals/index.tsx delete mode 100644 src/Journals/journalView.tsx delete mode 100644 src/components/JournalEntries.tsx diff --git a/src/Journals/ImportDialog.tsx b/src/Journals/ImportDialog.tsx deleted file mode 100644 index 6b39871..0000000 --- a/src/Journals/ImportDialog.tsx +++ /dev/null @@ -1,207 +0,0 @@ -// SPDX-FileCopyrightText: © 2017 EteSync Authors -// SPDX-License-Identifier: AGPL-3.0-only - -import * as React from "react"; - -import Button from "@material-ui/core/Button"; -import Dialog from "@material-ui/core/Dialog"; -import DialogActions from "@material-ui/core/DialogActions"; -import DialogContent from "@material-ui/core/DialogContent"; -import DialogContentText from "@material-ui/core/DialogContentText"; -import DialogTitle from "@material-ui/core/DialogTitle"; - -import Dropzone from "react-dropzone"; - -import LoadingIndicator from "../widgets/LoadingIndicator"; - -import { SyncInfoJournal } from "../SyncGate"; - -import { store, CredentialsData, UserInfoData } from "../store"; -import { addEntries } from "../store/actions"; -import { createJournalEntry } from "../etesync-helpers"; -import * as EteSync from "etesync"; - -import * as uuid from "uuid"; -import * as ICAL from "ical.js"; -import { ContactType, EventType, TaskType, PimType } from "../pim-types"; - -interface PropsType { - etesync: CredentialsData; - userInfo: UserInfoData; - syncJournal: SyncInfoJournal; - open: boolean; - onClose?: () => void; -} - -export default function ImportDialog(props: PropsType) { - const [loading, setLoading] = React.useState(false); - - function onFileDropCommon(itemsCreator: (fileText: string) => PimType[], acceptedFiles: File[], rejectedFiles: File[]) { - // XXX: implement handling of rejectedFiles - const reader = new FileReader(); - - reader.onabort = () => { - setLoading(false); - console.error("Import Aborted"); - alert("file reading was aborted"); - }; - reader.onerror = (e) => { - setLoading(false); - console.error(e); - alert("file reading has failed"); - }; - reader.onload = async () => { - try { - const fileText = reader.result as string; - const items = itemsCreator(fileText); - - const { syncJournal } = props; - const last = syncJournal.journalEntries.last() as EteSync.Entry; - const lastUid = last ? last.uid : null; - - // XXX implement chunked push most likely... - let prevUid = lastUid; - const journalItems = items.map((item) => { - const ret = createJournalEntry( - props.etesync, props.userInfo, syncJournal.journal, - prevUid, EteSync.SyncEntryAction.Add, item.toIcal()); - - prevUid = ret.uid; - return ret; - }); - - await store.dispatch( - addEntries(props.etesync, syncJournal.journal.uid, journalItems, lastUid) - ); - } catch (e) { - console.error(e); - alert("An error has occurred, please contact developers."); - throw e; - } finally { - if (props.onClose) { - setLoading(false); - props.onClose(); - } - } - }; - - if (acceptedFiles.length > 0) { - setLoading(true); - acceptedFiles.forEach((file) => { - reader.readAsText(file); - }); - } else { - alert("Failed importing file. Is the file type supported?"); - console.log("Failed importing files. Rejected:", rejectedFiles); - } - } - - function onFileDropContact(acceptedFiles: File[], rejectedFiles: File[]) { - const itemsCreator = (fileText: string) => { - const mainComp = ICAL.parse(fileText); - return mainComp.map((comp) => { - const ret = new ContactType(new ICAL.Component(comp)); - if (!ret.uid) { - ret.uid = uuid.v4(); - } - return ret; - }); - }; - - onFileDropCommon(itemsCreator, acceptedFiles, rejectedFiles); - } - - function onFileDropEvent(acceptedFiles: File[], rejectedFiles: File[]) { - const itemsCreator = (fileText: string) => { - const calendarComp = new ICAL.Component(ICAL.parse(fileText)); - return calendarComp.getAllSubcomponents("vevent").map((comp) => { - const ret = new EventType(comp); - if (!ret.uid) { - ret.uid = uuid.v4(); - } - return ret; - }); - }; - - onFileDropCommon(itemsCreator, acceptedFiles, rejectedFiles); - } - - function onFileDropTask(acceptedFiles: File[], rejectedFiles: File[]) { - const itemsCreator = (fileText: string) => { - const calendarComp = new ICAL.Component(ICAL.parse(fileText)); - return calendarComp.getAllSubcomponents("vtodo").map((comp) => { - const ret = new TaskType(comp); - if (!ret.uid) { - ret.uid = uuid.v4(); - } - return ret; - }); - }; - - onFileDropCommon(itemsCreator, acceptedFiles, rejectedFiles); - } - - function onClose() { - if (loading) { - return; - } - - if (props.onClose) { - props.onClose(); - } - } - - const { syncJournal } = props; - const collectionInfo = syncJournal.collection; - let acceptTypes; - let dropFunction; - - if (collectionInfo.type === "ADDRESS_BOOK") { - acceptTypes = ["text/vcard", "text/directory", "text/x-vcard", ".vcf"]; - dropFunction = onFileDropContact; - } else if (collectionInfo.type === "CALENDAR") { - acceptTypes = ["text/calendar", ".ics", ".ical"]; - dropFunction = onFileDropEvent; - } else if (collectionInfo.type === "TASKS") { - acceptTypes = ["text/calendar", ".ics", ".ical"]; - dropFunction = onFileDropTask; - } - - return ( - - - Import entries from file? - - {loading ? - - : - - {({ getRootProps, getInputProps }) => ( -
-
- - - To import entries from a file, drag 'n' drop it here, or click to open the file selector. - -
-
- )} -
- } -
- - - -
-
- ); -} diff --git a/src/Journals/Journal.tsx b/src/Journals/Journal.tsx deleted file mode 100644 index 7cc4a18..0000000 --- a/src/Journals/Journal.tsx +++ /dev/null @@ -1,104 +0,0 @@ -// SPDX-FileCopyrightText: © 2017 EteSync Authors -// SPDX-License-Identifier: AGPL-3.0-only - -import * as React from "react"; -import IconButton from "@material-ui/core/IconButton"; -import IconEdit from "@material-ui/icons/Edit"; -import IconMembers from "@material-ui/icons/People"; -import IconImport from "@material-ui/icons/ImportExport"; - - -import AppBarOverride from "../widgets/AppBarOverride"; -import Container from "../widgets/Container"; - -import JournalEntries from "../components/JournalEntries"; -import ImportDialog from "./ImportDialog"; - -import { SyncInfo, SyncInfoJournal } from "../SyncGate"; - -import { Link } from "react-router-dom"; - -import { routeResolver } from "../App"; - -import { CredentialsData, UserInfoData } from "../store"; - -interface PropsType { - etesync: CredentialsData; - userInfo: UserInfoData; - syncInfo: SyncInfo; - syncJournal: SyncInfoJournal; - isOwner: boolean; -} - -class Journal extends React.Component { - public state: { - tab: number; - importDialogOpen: boolean; - }; - - constructor(props: PropsType) { - super(props); - - this.importDialogToggle = this.importDialogToggle.bind(this); - this.state = { - tab: 0, - importDialogOpen: false, - }; - } - - public render() { - const { isOwner, syncJournal } = this.props; - - const journal = syncJournal.journal; - const collectionInfo = syncJournal.collection; - const syncEntries = syncJournal.entries; - - return ( - - - {isOwner && - <> - - - - - - - - } - - - - - - - - - - - ); - } - - private importDialogToggle() { - this.setState((state: any) => ({ importDialogOpen: !state.importDialogOpen })); - } -} - -export default Journal; diff --git a/src/Journals/JournalEdit.tsx b/src/Journals/JournalEdit.tsx deleted file mode 100644 index 14d2997..0000000 --- a/src/Journals/JournalEdit.tsx +++ /dev/null @@ -1,214 +0,0 @@ -// SPDX-FileCopyrightText: © 2017 EteSync Authors -// SPDX-License-Identifier: AGPL-3.0-only - -import * as React from "react"; -import Button from "@material-ui/core/Button"; -import TextField from "@material-ui/core/TextField"; -import Select from "@material-ui/core/Select"; -import MenuItem from "@material-ui/core/MenuItem"; -import FormControl from "@material-ui/core/FormControl"; -import InputLabel from "@material-ui/core/InputLabel"; -import IconDelete from "@material-ui/icons/Delete"; -import IconCancel from "@material-ui/icons/Clear"; -import IconSave from "@material-ui/icons/Save"; -import * as colors from "@material-ui/core/colors"; - -import AppBarOverride from "../widgets/AppBarOverride"; -import Container from "../widgets/Container"; -import ConfirmationDialog from "../widgets/ConfirmationDialog"; - -import * as EteSync from "etesync"; -import { SyncInfo } from "../SyncGate"; -import ColorPicker from "../widgets/ColorPicker"; -import { defaultColor, colorHtmlToInt, colorIntToHtml } from "../journal-processors"; - -interface PropsType { - syncInfo: SyncInfo; - item?: EteSync.CollectionInfo; - onSave: (info: EteSync.CollectionInfo, originalInfo?: EteSync.CollectionInfo) => void; - onDelete: (info: EteSync.CollectionInfo) => void; - onCancel: () => void; -} - -interface FormErrors { - displayName?: string; - color?: string; -} - -export default function JournalEdit(props: PropsType) { - const [errors, setErrors] = React.useState({}); - const [showDeleteDialog, setShowDeleteDialog] = React.useState(false); - const [info, setInfo] = React.useState(); - const [selectedColor, setSelectedColor] = React.useState(""); - - React.useEffect(() => { - if (props.item !== undefined) { - setInfo(props.item); - if (props.item.color) { - setSelectedColor(colorIntToHtml(props.item.color)); - } - } else { - setInfo({ - uid: EteSync.genUid(), - type: "ADDRESS_BOOK", - displayName: "", - description: "", - } as EteSync.CollectionInfo); - } - }, [props.item]); - - if (info === undefined) { - return ; - } - - function onSubmit(e: React.FormEvent) { - e.preventDefault(); - const saveErrors: FormErrors = {}; - const fieldRequired = "This field is required!"; - - const { onSave } = props; - - const displayName = info?.displayName; - const color = colorHtmlToInt(selectedColor); - - if (!displayName) { - saveErrors.displayName = fieldRequired; - } - - if (selectedColor && !color) { - saveErrors.color = "Must be of the form #RRGGBB or #RRGGBBAA or empty"; - } - - if (Object.keys(saveErrors).length > 0) { - setErrors(saveErrors); - return; - } - - const item = new EteSync.CollectionInfo({ ...info, color: color }); - onSave(item, props.item); - } - - function onDeleteRequest() { - setShowDeleteDialog(true); - } - - const { item, onDelete, onCancel } = props; - - const pageTitle = (item !== undefined) ? item.displayName : "New Journal"; - - const styles = { - fullWidth: { - width: "100%", - }, - submit: { - marginTop: 40, - marginBottom: 20, - textAlign: "right" as any, - }, - }; - - const journalTypes = { - ADDRESS_BOOK: "Address Book", - CALENDAR: "Calendar", - TASKS: "Task List", - }; - let collectionColorBox: React.ReactNode; - switch (info.type) { - case "CALENDAR": - case "TASKS": - collectionColorBox = ( - setSelectedColor(color)} - error={errors.color} - /> - ); - break; - } - - return ( - <> - - -
- - - Collection type - - - - ) => setInfo({ ...info, displayName: event.target.value })} - style={styles.fullWidth} - margin="normal" - error={!!errors.displayName} - helperText={errors.displayName} - /> - ) => setInfo({ ...info, description: event.target.value })} - style={styles.fullWidth} - margin="normal" - /> - {collectionColorBox} - -
- - - {props.item && - - } - - -
- -
- onDelete(props.item!)} - onCancel={() => setShowDeleteDialog(false)} - > - Are you sure you would like to delete this journal? - - - ); -} diff --git a/src/Journals/JournalMemberAddDialog.tsx b/src/Journals/JournalMemberAddDialog.tsx deleted file mode 100644 index d630214..0000000 --- a/src/Journals/JournalMemberAddDialog.tsx +++ /dev/null @@ -1,125 +0,0 @@ -// SPDX-FileCopyrightText: © 2017 EteSync Authors -// SPDX-License-Identifier: AGPL-3.0-only - -import * as React from "react"; - -import TextField from "@material-ui/core/TextField"; -import Checkbox from "@material-ui/core/Checkbox"; -import FormControlLabel from "@material-ui/core/FormControlLabel"; - -import LoadingIndicator from "../widgets/LoadingIndicator"; -import ConfirmationDialog from "../widgets/ConfirmationDialog"; -import PrettyFingerprint from "../widgets/PrettyFingerprint"; - -import * as EteSync from "etesync"; - -import { CredentialsData } from "../store"; - -interface PropsType { - etesync: CredentialsData; - info: EteSync.CollectionInfo; - onOk: (user: string, publicKey: string, readOnly: boolean) => void; - onClose: () => void; -} - -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(); - - function onAddRequest(_user: string) { - setUserChosen(true); - - const { etesync } = props; - - const creds = etesync.credentials; - const apiBase = etesync.serviceApiUrl; - const userInfoManager = new EteSync.UserInfoManager(creds, apiBase); - userInfoManager.fetch(addUser).then((userInfo) => { - setPublicKey(userInfo.publicKey); - }).catch((error) => { - setError(error); - }); - } - - function onOk() { - props.onOk(addUser, publicKey, readOnly); - } - - const { onClose } = props; - - if (error) { - return ( - <> - - User ({addUser}) not found. Have they setup their encryption password from one of the apps? - - - ); - } - - if (publicKey) { - return ( - <> - -

- Verify {addUser}'s security fingerprint to ensure the encryption is secure. -

-
- -
-
- - ); - } else { - return ( - <> - - {userChosen ? - - : - <> - setAddUser(ev.target.value)} - /> - setReadOnly(event.target.checked)} - /> - } - label="Read only?" - /> - - } - - - ); - } -} diff --git a/src/Journals/JournalMembers.tsx b/src/Journals/JournalMembers.tsx deleted file mode 100644 index 1e9f045..0000000 --- a/src/Journals/JournalMembers.tsx +++ /dev/null @@ -1,155 +0,0 @@ -// SPDX-FileCopyrightText: © 2017 EteSync Authors -// SPDX-License-Identifier: AGPL-3.0-only - -import * as React from "react"; -import sjcl from "sjcl"; - -import { List, ListItem } from "../widgets/List"; - -import IconMemberAdd from "@material-ui/icons/PersonAdd"; -import VisibilityIcon from "@material-ui/icons/Visibility"; - -import AppBarOverride from "../widgets/AppBarOverride"; -import Container from "../widgets/Container"; -import LoadingIndicator from "../widgets/LoadingIndicator"; -import ConfirmationDialog from "../widgets/ConfirmationDialog"; - -import JournalMemberAddDialog from "./JournalMemberAddDialog"; - -import * as EteSync from "etesync"; -import { CredentialsData, UserInfoData } from "../store"; - -import { SyncInfoJournal } from "../SyncGate"; - -interface PropsType { - etesync: CredentialsData; - syncJournal: SyncInfoJournal; - userInfo: UserInfoData; -} - -export default function JournalMembers(props: PropsType) { - const [members, setMembers] = React.useState(null); - const [revokeUser, setRevokeUser] = React.useState(null); - const [addMemberOpen, setAddMemberOpen] = React.useState(false); - - 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) => { - setMembers(members); - }); - } - - React.useEffect(() => { - fetchMembers(); - }, []); - - function onRevokeRequest(user: string) { - setRevokeUser(user); - } - - 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(() => { - fetchMembers(); - }); - setRevokeUser(null); - } - - function onMemberAdd(user: string, publicKey: string, readOnly: boolean) { - const { etesync, syncJournal, userInfo } = props; - const journal = syncJournal.journal; - const derived = props.etesync.encryptionKey; - - const keyPair = userInfo.getKeyPair(userInfo.getCryptoManager(derived)); - const cryptoManager = journal.getCryptoManager(derived, keyPair); - - const pubkeyBytes = sjcl.codec.bytes.fromBits(sjcl.codec.base64.toBits(publicKey)); - const encryptedKey = sjcl.codec.base64.fromBits(sjcl.codec.bytes.toBits(cryptoManager.getEncryptedKey(keyPair, pubkeyBytes))); - - const creds = etesync.credentials; - const apiBase = etesync.serviceApiUrl; - const journalMembersManager = new EteSync.JournalMembersManager(creds, apiBase, journal.uid); - journalMembersManager.create({ user, key: encryptedKey, readOnly }).then(() => { - fetchMembers(); - }); - setAddMemberOpen(false); - } - - const { syncJournal } = props; - - const info = syncJournal.collection; - const sharingAllowed = syncJournal.journal.version > 1; - - return ( - <> - - - {members ? - - } onClick={() => setAddMemberOpen(true)}> - Add member - - {(members.length > 0 ? - members.map((member) => ( - onRevokeRequest(member.user)} - rightIcon={(member.readOnly) ? () : undefined} - > - {member.user} - - )) - : - - No members - - )} - - : - - } - - setRevokeUser(null)} - > - Would you like to revoke {revokeUser}'s access?
- 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. -
- - {addMemberOpen && - (sharingAllowed ? - setAddMemberOpen(false)} - /> - : - 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. - - ) - } - - ); -} diff --git a/src/Journals/JournalsList.tsx b/src/Journals/JournalsList.tsx deleted file mode 100644 index ef08b3d..0000000 --- a/src/Journals/JournalsList.tsx +++ /dev/null @@ -1,103 +0,0 @@ -// SPDX-FileCopyrightText: © 2017 EteSync Authors -// SPDX-License-Identifier: AGPL-3.0-only - -import * as React from "react"; -import { History } from "history"; - -import { Link } 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 { JournalsData, UserInfoData, CredentialsData } from "../store"; -import ColorBox from "../widgets/ColorBox"; -import { colorIntToHtml } from "../journal-processors"; - -interface PropsType { - etesync: CredentialsData; - journals: JournalsData; - userInfo: UserInfoData; - history: History; -} - -export default function JournalsList(props: PropsType) { - const derived = props.etesync.encryptionKey; - - function journalClicked(journalUid: string) { - props.history.push(routeResolver.getRoute("journals._id", { journalUid })); - } - - const journalMap = props.journals.reduce( - (ret, journal) => { - const userInfo = props.userInfo; - const keyPair = userInfo.getKeyPair(userInfo.getCryptoManager(derived)); - const cryptoManager = journal.getCryptoManager(derived, keyPair); - const info = journal.getInfo(cryptoManager); - let colorBox: React.ReactElement | undefined; - switch (info.type) { - case "CALENDAR": - case "TASKS": - colorBox = ( - - ); - break; - } - ret[info.type] = ret[info.type] || []; - ret[info.type].push( - journalClicked(journal.uid)}> - {info.displayName} - - ); - - return ret; - }, - { - CALENDAR: [], - ADDRESS_BOOK: [], - TASKS: [], - }); - - return ( - - - - - - - - } - nestedItems={journalMap.ADDRESS_BOOK} - /> - - } - nestedItems={journalMap.CALENDAR} - /> - - } - nestedItems={journalMap.TASKS} - /> - - - ); -} diff --git a/src/Journals/JournalsListImport.tsx b/src/Journals/JournalsListImport.tsx deleted file mode 100644 index b6e6d0e..0000000 --- a/src/Journals/JournalsListImport.tsx +++ /dev/null @@ -1,96 +0,0 @@ -// SPDX-FileCopyrightText: © 2017 EteSync Authors -// SPDX-License-Identifier: AGPL-3.0-only - -import * as React from "react"; - -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 { JournalsData, UserInfoData, CredentialsData } from "../store"; -import ImportDialog from "./ImportDialog"; -import { SyncInfo, SyncInfoJournal } from "../SyncGate"; -import { colorIntToHtml } from "../journal-processors"; -import ColorBox from "../widgets/ColorBox"; - -interface PropsType { - etesync: CredentialsData; - journals: JournalsData; - userInfo: UserInfoData; - syncInfo: SyncInfo; -} - -export default function JournalsList(props: PropsType) { - const [selectedJournal, setSelectedJournal] = React.useState(); - const derived = props.etesync.encryptionKey; - - const journalMap = props.journals.reduce( - (ret, journal) => { - const userInfo = props.userInfo; - const keyPair = userInfo.getKeyPair(userInfo.getCryptoManager(derived)); - const cryptoManager = journal.getCryptoManager(derived, keyPair); - const info = journal.getInfo(cryptoManager); - let colorBox: React.ReactElement | undefined; - switch (info.type) { - case "CALENDAR": - case "TASKS": - colorBox = ( - - ); - break; - } - ret[info.type] = ret[info.type] || []; - ret[info.type].push( - setSelectedJournal(props.syncInfo.get(journal.uid))}> - {info.displayName} ({journal.uid.slice(0, 5)}) - - ); - - return ret; - }, - { - CALENDAR: [], - ADDRESS_BOOK: [], - TASKS: [], - }); - - return ( - - - - } - nestedItems={journalMap.ADDRESS_BOOK} - /> - - } - nestedItems={journalMap.CALENDAR} - /> - - } - nestedItems={journalMap.TASKS} - /> - - {selectedJournal && ( - setSelectedJournal(undefined)} - /> - )} - - ); -} diff --git a/src/Journals/index.tsx b/src/Journals/index.tsx deleted file mode 100644 index e102c4b..0000000 --- a/src/Journals/index.tsx +++ /dev/null @@ -1,181 +0,0 @@ -// SPDX-FileCopyrightText: © 2017 EteSync Authors -// SPDX-License-Identifier: AGPL-3.0-only - -import * as React from "react"; -import { Location, History } from "history"; -import { Route, Switch } from "react-router"; - -import Journal from "./Journal"; -import JournalEdit from "./JournalEdit"; -import JournalMembers from "./JournalMembers"; -import JournalsList from "./JournalsList"; -import JournalsListImport from "./JournalsListImport"; - -import { routeResolver } from "../App"; - -import { store, JournalsData, UserInfoData, CredentialsData } from "../store"; -import { addJournal, deleteJournal, updateJournal } from "../store/actions"; -import { SyncInfo } from "../SyncGate"; - -import * as EteSync from "etesync"; - -class Journals extends React.PureComponent { - public props: { - etesync: CredentialsData; - journals: JournalsData; - userInfo: UserInfoData; - syncInfo: SyncInfo; - history: History; - location: Location; - }; - - constructor(props: any) { - super(props); - this.onCancel = this.onCancel.bind(this); - this.onItemDelete = this.onItemDelete.bind(this); - this.onItemSave = this.onItemSave.bind(this); - } - - public render() { - return ( - - ( - <> - - - )} - /> - ( - <> - - - )} - /> - ( - - )} - /> - { - const journalUid = match.params.journalUid; - - const syncJournal = this.props.syncInfo.get(journalUid); - if (!syncJournal) { - return (
Journal not found!
); - } - - const isOwner = syncJournal.journal.owner === this.props.etesync.credentials.email; - - const collectionInfo = syncJournal.collection; - return ( - - ( - - )} - /> - ( - - )} - /> - ( - - )} - /> - - ); - }} - /> -
- ); - } - - public onItemSave(info: EteSync.CollectionInfo, originalInfo?: EteSync.CollectionInfo) { - const syncJournal = this.props.syncInfo.get(info.uid); - - const derived = this.props.etesync.encryptionKey; - const userInfo = this.props.userInfo; - const existingJournal = (syncJournal) ? syncJournal.journal.serialize() : { uid: info.uid }; - const journal = new EteSync.Journal(existingJournal); - const keyPair = userInfo.getKeyPair(userInfo.getCryptoManager(derived)); - const cryptoManager = journal.getCryptoManager(derived, keyPair); - journal.setInfo(cryptoManager, info); - - if (originalInfo) { - store.dispatch(updateJournal(this.props.etesync, journal)).then(() => - this.props.history.goBack() - ); - } else { - store.dispatch(addJournal(this.props.etesync, journal)).then(() => - this.props.history.goBack() - ); - } - } - - public onItemDelete(info: EteSync.CollectionInfo) { - const syncJournal = this.props.syncInfo.get(info.uid); - - const derived = this.props.etesync.encryptionKey; - const userInfo = this.props.userInfo; - const existingJournal = (syncJournal) ? syncJournal.journal.serialize() : { uid: info.uid }; - const journal = new EteSync.Journal(existingJournal); - const keyPair = userInfo.getKeyPair(userInfo.getCryptoManager(derived)); - const cryptoManager = journal.getCryptoManager(derived, keyPair); - journal.setInfo(cryptoManager, info); - - store.dispatch(deleteJournal(this.props.etesync, journal)).then(() => - this.props.history.push(routeResolver.getRoute("journals")) - ); - } - - public onCancel() { - this.props.history.goBack(); - } -} - -export default Journals; diff --git a/src/Journals/journalView.tsx b/src/Journals/journalView.tsx deleted file mode 100644 index 422ef44..0000000 --- a/src/Journals/journalView.tsx +++ /dev/null @@ -1,68 +0,0 @@ -// SPDX-FileCopyrightText: © 2017 EteSync Authors -// SPDX-License-Identifier: AGPL-3.0-only - -import * as React from "react"; -import { Route, Switch, withRouter } from "react-router"; - -import * as EteSync from "etesync"; - -import { routeResolver } from "../App"; - -import { History } from "history"; -import { SyncInfo } from "../SyncGate"; - -function objValues(obj: any) { - return Object.keys(obj).map((x) => obj[x]); -} - -export function journalView(JournalList: any, JournalItem: any) { - return withRouter(class extends React.PureComponent { - public props: { - journal: EteSync.Journal; - entries: {[key: string]: any}; - syncInfo?: SyncInfo; - history?: History; - }; - - constructor(props: any) { - super(props); - this.itemClicked = this.itemClicked.bind(this); - } - - public itemClicked(contact: any) { - const uid = contact.uid; - - this.props.history!.push( - routeResolver.getRoute("journals._id.items._id", { journalUid: this.props.journal.uid, itemUid: encodeURIComponent(uid) })); - } - - public render() { - const items = this.props.entries; - - return ( - - ( - - ) - } - /> - { - - return ( - - ); - }} - /> - - ); - } - }); -} - -export default journalView; diff --git a/src/components/JournalEntries.tsx b/src/components/JournalEntries.tsx deleted file mode 100644 index ced40ee..0000000 --- a/src/components/JournalEntries.tsx +++ /dev/null @@ -1,261 +0,0 @@ -// SPDX-FileCopyrightText: © 2017 EteSync Authors -// SPDX-License-Identifier: AGPL-3.0-only - -import * as Immutable from "immutable"; - -import { AutoSizer, List as VirtualizedList } from "react-virtualized"; - -import * as React from "react"; -import { List, ListItem } from "../widgets/List"; -import Dialog from "@material-ui/core/Dialog"; -import DialogTitle from "@material-ui/core/DialogTitle"; -import DialogContent from "@material-ui/core/DialogContent"; -import DialogActions from "@material-ui/core/DialogActions"; -import Button from "@material-ui/core/Button"; -import IconAdd from "@material-ui/icons/Add"; -import IconDelete from "@material-ui/icons/Delete"; -import IconEdit from "@material-ui/icons/Edit"; -import IconError from "@material-ui/icons/Error"; - -import { TaskType, EventType, ContactType, parseString } from "../pim-types"; - -import * as EteSync from "etesync"; -import LoadingIndicator from "../widgets/LoadingIndicator"; -import { useCredentials } from "../login"; -import { createJournalEntry } from "../etesync-helpers"; -import { useSelector, useDispatch } from "react-redux"; -import { StoreState } from "../store"; -import { addEntries } from "../store/actions"; - -interface RollbackToHereDialogPropsType { - journal: EteSync.Journal; - entries: Immutable.List; - entryUid: string; - open: boolean; - onClose: () => void; -} - -function RollbackToHereDialog(props: RollbackToHereDialogPropsType) { - const [loading, setLoading] = React.useState(false); - const etesync = useCredentials()!; - const dispatch = useDispatch(); - const userInfo = useSelector((state: StoreState) => state.cache.userInfo); - - async function go() { - setLoading(true); - - const changes = new Map(); - - for (const entry of props.entries.reverse()) { - const comp = parseString(entry.content); - const itemComp = comp.getFirstSubcomponent("vevent") ?? comp.getFirstSubcomponent("vtodo") ?? comp; - const itemUid = itemComp.getFirstPropertyValue("uid"); - - if (itemUid && !changes.has(itemUid)) { - changes.set(itemUid, entry); - } - - if (entry.uid === props.entryUid) { - break; - } - } - - const last = props.entries.last(null); - const lastUid = last?.uid ? last.uid : null; - - // XXX implement chunked push most likely... - let prevUid = lastUid; - const journalItems = []; - for (const syncEntry of changes.values()) { - if (syncEntry.action === EteSync.SyncEntryAction.Delete) { - const ret = createJournalEntry(etesync, userInfo, props.journal, prevUid, EteSync.SyncEntryAction.Add, syncEntry.content); - journalItems.push(ret); - - prevUid = ret.uid; - } - } - - if (journalItems.length > 0) { - await dispatch( - addEntries(etesync, props.journal.uid, journalItems, lastUid) - ); - } - - props.onClose(); - } - - return ( - - - Recover items - - - {loading ? ( - - ) : ( -

- This function restores all of the deleted items that happened after this change entry. It will not modify any items that haven't been changed since the item was deleted. -

- )} -
- - - - -
- ); -} - -interface PropsType { - journal: EteSync.Journal; - entries: Immutable.List; - uid?: string; -} - -export default function JournalEntries(props: PropsType) { - const [dialog, setDialog] = React.useState(); - const [rollbackDialogId, setRollbackDialogId] = React.useState(); - - if (props.journal === undefined) { - return (
Loading
); - } - - const rowRenderer = (params: { index: number, key: string, style: React.CSSProperties }) => { - const { key, index, style } = params; - // eslint-disable-next-line react/prop-types - const syncEntry = props.entries.get(props.entries.size - index - 1)!; - let comp; - try { - comp = parseString(syncEntry.content); - } catch (e) { - const icon = (); - return ( - setDialog(syncEntry)} - /> - ); - } - - let icon; - if (syncEntry.action === EteSync.SyncEntryAction.Add) { - icon = (); - } else if (syncEntry.action === EteSync.SyncEntryAction.Change) { - icon = (); - } else if (syncEntry.action === EteSync.SyncEntryAction.Delete) { - icon = (); - } - - let name; - let uid; - if (comp.name === "vcalendar") { - if (EventType.isEvent(comp)) { - const vevent = EventType.fromVCalendar(comp); - name = vevent.summary; - uid = vevent.uid; - } else { - const vtodo = TaskType.fromVCalendar(comp); - name = vtodo.summary; - uid = vtodo.uid; - } - } else if (comp.name === "vcard") { - const vcard = new ContactType(comp); - name = vcard.fn; - uid = vcard.uid; - } else { - name = "Error processing entry"; - uid = ""; - } - - // eslint-disable-next-line react/prop-types - if (props.uid && (props.uid !== uid)) { - return undefined; - } - - return ( - setDialog(syncEntry)} - /> - ); - }; - - return ( -
- setRollbackDialogId(undefined)} - /> - setDialog(undefined)} - > - - Raw Content - - -
Entry UID:
{dialog?.uid}
-
Content: -
{dialog?.content}
-
-
- - - - -
- - - {({ height, width }) => ( - - )} - - -
- ); -}