Change code to double-quotes.

master
Tom Hacohen 4 years ago
parent 1327279816
commit 9ec5d2a708

@ -65,7 +65,7 @@ module.exports = {
"react/no-unknown-property": ["error"],
"quotes": "off",
"@typescript-eslint/quotes": ["error", "single", { "allowTemplateLiterals": true, "avoidEscape": true }],
"@typescript-eslint/quotes": ["error", "double", { "allowTemplateLiterals": true, "avoidEscape": true }],
"semi": "off",
"@typescript-eslint/semi": ["error", "always", { "omitLastInOneLineBlock": true }],
"comma-dangle": ["error", {

@ -1,15 +1,15 @@
// SPDX-FileCopyrightText: © 2017 EteSync Authors
// SPDX-License-Identifier: AGPL-3.0-only
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import App from './App';
import * as React from "react";
import * as ReactDOM from "react-dom";
import { Provider } from "react-redux";
import App from "./App";
import { store } from './store';
import { store } from "./store";
it('renders without crashing', () => {
const div = document.createElement('div');
it("renders without crashing", () => {
const div = document.createElement("div");
ReactDOM.render(
<Provider store={store}>
<App />

@ -1,93 +1,93 @@
// SPDX-FileCopyrightText: © 2017 EteSync Authors
// SPDX-License-Identifier: AGPL-3.0-only
import * as React from 'react';
import { List as ImmutableList } from 'immutable';
import { connect } from 'react-redux';
import { withRouter } 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';
import lightBlue from '@material-ui/core/colors/lightBlue';
import AppBar from '@material-ui/core/AppBar';
import Toolbar from '@material-ui/core/Toolbar';
import Drawer from '@material-ui/core/Drawer';
import IconButton from '@material-ui/core/IconButton';
import Badge from '@material-ui/core/Badge';
import NavigationMenu from '@material-ui/icons/Menu';
import NavigationBack from '@material-ui/icons/ArrowBack';
import NavigationRefresh from '@material-ui/icons/Refresh';
import ErrorsIcon from '@material-ui/icons/Error';
import 'react-virtualized/styles.css'; // only needs to be imported once
import './App.css';
import ConfirmationDialog from './widgets/ConfirmationDialog';
import PrettyError from './widgets/PrettyError';
import { List, ListItem } from './widgets/List';
import withSpin from './widgets/withSpin';
import ErrorBoundary from './components/ErrorBoundary';
import SideMenu from './SideMenu';
import LoginGate from './LoginGate';
import { RouteResolver } from './routes';
import * as store from './store';
import * as actions from './store/actions';
import { credentialsSelector } from './login';
import { History } from 'history';
import * as React from "react";
import { List as ImmutableList } from "immutable";
import { connect } from "react-redux";
import { withRouter } 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";
import lightBlue from "@material-ui/core/colors/lightBlue";
import AppBar from "@material-ui/core/AppBar";
import Toolbar from "@material-ui/core/Toolbar";
import Drawer from "@material-ui/core/Drawer";
import IconButton from "@material-ui/core/IconButton";
import Badge from "@material-ui/core/Badge";
import NavigationMenu from "@material-ui/icons/Menu";
import NavigationBack from "@material-ui/icons/ArrowBack";
import NavigationRefresh from "@material-ui/icons/Refresh";
import ErrorsIcon from "@material-ui/icons/Error";
import "react-virtualized/styles.css"; // only needs to be imported once
import "./App.css";
import ConfirmationDialog from "./widgets/ConfirmationDialog";
import PrettyError from "./widgets/PrettyError";
import { List, ListItem } from "./widgets/List";
import withSpin from "./widgets/withSpin";
import ErrorBoundary from "./components/ErrorBoundary";
import SideMenu from "./SideMenu";
import LoginGate from "./LoginGate";
import { RouteResolver } from "./routes";
import * as store from "./store";
import * as actions from "./store/actions";
import { credentialsSelector } from "./login";
import { History } from "history";
export const routeResolver = new RouteResolver({
home: '',
home: "",
pim: {
contacts: {
_id: {
_base: ':itemUid',
edit: 'edit',
log: 'log',
_base: ":itemUid",
edit: "edit",
log: "log",
},
new: 'new',
new: "new",
},
events: {
_id: {
_base: ':itemUid',
edit: 'edit',
duplicate: 'duplicate',
log: 'log',
_base: ":itemUid",
edit: "edit",
duplicate: "duplicate",
log: "log",
},
new: 'new',
new: "new",
},
tasks: {
_id: {
_base: ':itemUid',
edit: 'edit',
log: 'log',
_base: ":itemUid",
edit: "edit",
log: "log",
},
new: 'new',
new: "new",
},
},
journals: {
_id: {
_base: ':journalUid',
edit: 'edit',
_base: ":journalUid",
edit: "edit",
items: {
_id: {
_base: ':itemUid',
_base: ":itemUid",
},
},
entries: {
_id: {
_base: ':entryUid',
_base: ":entryUid",
},
},
members: {
},
},
new: 'new',
import: 'import',
new: "new",
import: "import",
},
settings: {
},
@ -132,7 +132,7 @@ const AppBarWitHistory = withRouter(
}
</div>
<div style={{ flexGrow: 1, fontSize: '1.25em' }} id="appbar-title" />
<div style={{ flexGrow: 1, fontSize: "1.25em" }} id="appbar-title" />
<div style={{ marginRight: -12 }} id="appbar-buttons">
{iconElementRight}
@ -145,8 +145,8 @@ const AppBarWitHistory = withRouter(
private canGoBack() {
return (
(this.props.history!.length > 1) &&
(this.props.history!.location.pathname !== routeResolver.getRoute('pim')) &&
(this.props.history!.location.pathname !== routeResolver.getRoute('home'))
(this.props.history!.location.pathname !== routeResolver.getRoute("pim")) &&
(this.props.history!.location.pathname !== routeResolver.getRoute("home"))
);
}
@ -191,13 +191,13 @@ class App extends React.PureComponent {
const muiTheme = createMuiTheme({
palette: {
type: darkMode ? 'dark' : undefined,
type: darkMode ? "dark" : undefined,
primary: amber,
secondary: {
light: lightBlue.A200,
main: lightBlue.A400,
dark: lightBlue.A700,
contrastText: '#fff',
contrastText: "#fff",
},
},
});
@ -213,7 +213,7 @@ class App extends React.PureComponent {
return (
<ThemeProvider theme={muiTheme}>
<BrowserRouter>
<div style={styles.main} className={darkMode ? 'theme-dark' : 'theme-light'}>
<div style={styles.main} className={darkMode ? "theme-dark" : "theme-light"}>
<AppBarWitHistory
toggleDrawerIcon={<IconButton onClick={this.toggleDrawer}><NavigationMenu /></IconButton>}
iconElementRight={
@ -245,7 +245,7 @@ class App extends React.PureComponent {
{errors.map((error, index) => (
<ListItem
key={index}
style={{ height: 'unset' }}
style={{ height: "unset" }}
onClick={() => (window as any).navigator.clipboard.writeText(`${error.message}\n\n${error.stack}`)}
>
<PrettyError error={error} />

@ -1,14 +1,14 @@
// SPDX-FileCopyrightText: © 2017 EteSync Authors
// SPDX-License-Identifier: AGPL-3.0-only
import * as React from 'react';
import * as EteSync from 'etesync';
import Button from '@material-ui/core/Button';
import TextField from '@material-ui/core/TextField';
import * as React from "react";
import * as EteSync from "etesync";
import Button from "@material-ui/core/Button";
import TextField from "@material-ui/core/TextField";
import Container from './widgets/Container';
import { useSelector } from 'react-redux';
import { StoreState, CredentialsData, UserInfoData, EntriesListData } from './store';
import Container from "./widgets/Container";
import { useSelector } from "react-redux";
import { StoreState, CredentialsData, UserInfoData, EntriesListData } from "./store";
interface PropsType {
etesync: CredentialsData;
@ -16,9 +16,9 @@ interface PropsType {
}
export default function Debug(props: PropsType) {
const [stateJournalUid, setJournalUid] = React.useState('');
const [entriesUids, setEntriesUids] = React.useState('');
const [result, setResult] = React.useState('');
const [stateJournalUid, setJournalUid] = React.useState("");
const [entriesUids, setEntriesUids] = React.useState("");
const [result, setResult] = React.useState("");
const journals = useSelector((state: StoreState) => state.cache.journals!);
const journalEntries = useSelector((state: StoreState) => state.cache.entries);
@ -32,7 +32,7 @@ export default function Debug(props: PropsType) {
<Container>
<div>
<TextField
style={{ width: '100%' }}
style={{ width: "100%" }}
type="text"
label="Journal UID"
value={stateJournalUid}
@ -41,7 +41,7 @@ export default function Debug(props: PropsType) {
</div>
<div>
<TextField
style={{ width: '100%' }}
style={{ width: "100%" }}
type="text"
multiline
label="Entry UIDs"
@ -60,13 +60,13 @@ export default function Debug(props: PropsType) {
const journalUid = stateJournalUid.trim();
const journal = journals.get(journalUid);
if (!journal) {
setResult('Error: journal uid not found.');
setResult("Error: journal uid not found.");
return;
}
const wantedEntries = {};
const wantAll = (entriesUids.trim() === 'all');
entriesUids.split('\n').forEach((ent) => wantedEntries[ent.trim()] = true);
const wantAll = (entriesUids.trim() === "all");
entriesUids.split("\n").forEach((ent) => wantedEntries[ent.trim()] = true);
const cryptoManager = journal.getCryptoManager(derived, keyPair);
let prevUid: string | null = null;
@ -79,7 +79,7 @@ export default function Debug(props: PropsType) {
return (wantAll || wantedEntries[entry.uid]) ? syncEntry : undefined;
}).filter((x) => x !== undefined);
setResult(syncEntries.map((ent) => ent?.content).join('\n\n'));
setResult(syncEntries.map((ent) => ent?.content).join("\n\n"));
}}
>
Decrypt

@ -1,29 +1,29 @@
// SPDX-FileCopyrightText: © 2017 EteSync Authors
// SPDX-License-Identifier: AGPL-3.0-only
import * as React from 'react';
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 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 Dropzone from "react-dropzone";
import LoadingIndicator from '../widgets/LoadingIndicator';
import LoadingIndicator from "../widgets/LoadingIndicator";
import { SyncInfoJournal } from '../SyncGate';
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 { 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';
import * as uuid from "uuid";
import * as ICAL from "ical.js";
import { ContactType, EventType, TaskType, PimType } from "../pim-types";
interface PropsType {
etesync: CredentialsData;
@ -55,14 +55,14 @@ class ImportDialog extends React.Component<PropsType> {
let acceptTypes;
let dropFunction;
if (collectionInfo.type === 'ADDRESS_BOOK') {
acceptTypes = ['text/vcard', 'text/directory', 'text/x-vcard', '.vcf'];
if (collectionInfo.type === "ADDRESS_BOOK") {
acceptTypes = ["text/vcard", "text/directory", "text/x-vcard", ".vcf"];
dropFunction = this.onFileDropContact;
} else if (collectionInfo.type === 'CALENDAR') {
acceptTypes = ['text/calendar', '.ics', '.ical'];
} else if (collectionInfo.type === "CALENDAR") {
acceptTypes = ["text/calendar", ".ics", ".ical"];
dropFunction = this.onFileDropEvent;
} else if (collectionInfo.type === 'TASKS') {
acceptTypes = ['text/calendar', '.ics', '.ical'];
} else if (collectionInfo.type === "TASKS") {
acceptTypes = ["text/calendar", ".ics", ".ical"];
dropFunction = this.onFileDropTask;
}
@ -75,7 +75,7 @@ class ImportDialog extends React.Component<PropsType> {
<DialogTitle>Import entries from file?</DialogTitle>
<DialogContent>
{loading ?
<LoadingIndicator style={{ display: 'block', margin: 'auto' }} />
<LoadingIndicator style={{ display: "block", margin: "auto" }} />
:
<Dropzone
onDrop={dropFunction}
@ -111,13 +111,13 @@ class ImportDialog extends React.Component<PropsType> {
reader.onabort = () => {
this.setState({ loading: false });
console.error('Import Aborted');
alert('file reading was aborted');
console.error("Import Aborted");
alert("file reading was aborted");
};
reader.onerror = (e) => {
this.setState({ loading: false });
console.error(e);
alert('file reading has failed');
alert("file reading has failed");
};
reader.onload = async () => {
try {
@ -144,7 +144,7 @@ class ImportDialog extends React.Component<PropsType> {
);
} catch (e) {
console.error(e);
alert('An error has occurred, please contact developers.');
alert("An error has occurred, please contact developers.");
throw e;
} finally {
if (this.props.onClose) {
@ -160,8 +160,8 @@ class ImportDialog extends React.Component<PropsType> {
reader.readAsText(file);
});
} else {
alert('Failed importing file. Is the file type supported?');
console.log('Failed importing files. Rejected:', rejectedFiles);
alert("Failed importing file. Is the file type supported?");
console.log("Failed importing files. Rejected:", rejectedFiles);
}
}
@ -183,7 +183,7 @@ class ImportDialog extends React.Component<PropsType> {
private onFileDropEvent(acceptedFiles: File[], rejectedFiles: File[]) {
const itemsCreator = (fileText: string) => {
const calendarComp = new ICAL.Component(ICAL.parse(fileText));
return calendarComp.getAllSubcomponents('vevent').map((comp) => {
return calendarComp.getAllSubcomponents("vevent").map((comp) => {
const ret = new EventType(comp);
if (!ret.uid) {
ret.uid = uuid.v4();
@ -198,7 +198,7 @@ class ImportDialog extends React.Component<PropsType> {
private onFileDropTask(acceptedFiles: File[], rejectedFiles: File[]) {
const itemsCreator = (fileText: string) => {
const calendarComp = new ICAL.Component(ICAL.parse(fileText));
return calendarComp.getAllSubcomponents('vtodo').map((comp) => {
return calendarComp.getAllSubcomponents("vtodo").map((comp) => {
const ret = new TaskType(comp);
if (!ret.uid) {
ret.uid = uuid.v4();

@ -1,26 +1,26 @@
// 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 * 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 AppBarOverride from "../widgets/AppBarOverride";
import Container from "../widgets/Container";
import JournalEntries from '../components/JournalEntries';
import ImportDialog from './ImportDialog';
import JournalEntries from "../components/JournalEntries";
import ImportDialog from "./ImportDialog";
import { SyncInfo, SyncInfoJournal } from '../SyncGate';
import { SyncInfo, SyncInfoJournal } from "../SyncGate";
import { Link } from 'react-router-dom';
import { Link } from "react-router-dom";
import { routeResolver } from '../App';
import { routeResolver } from "../App";
import { CredentialsData, UserInfoData } from '../store';
import { CredentialsData, UserInfoData } from "../store";
interface PropsType {
etesync: CredentialsData;
@ -61,14 +61,14 @@ class Journal extends React.Component<PropsType> {
<IconButton
component={Link}
title="Edit"
{...{ to: routeResolver.getRoute('journals._id.edit', { journalUid: journal.uid }) }}
{...{ to: routeResolver.getRoute("journals._id.edit", { journalUid: journal.uid }) }}
>
<IconEdit />
</IconButton>
<IconButton
component={Link}
title="Members"
{...{ to: routeResolver.getRoute('journals._id.members', { journalUid: journal.uid }) }}
{...{ to: routeResolver.getRoute("journals._id.members", { journalUid: journal.uid }) }}
>
<IconMembers />
</IconButton>

@ -1,26 +1,26 @@
// 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';
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;
@ -39,7 +39,7 @@ export default function JournalEdit(props: PropsType) {
const [errors, setErrors] = React.useState<FormErrors>({});
const [showDeleteDialog, setShowDeleteDialog] = React.useState(false);
const [info, setInfo] = React.useState<EteSync.CollectionInfo>();
const [selectedColor, setSelectedColor] = React.useState('');
const [selectedColor, setSelectedColor] = React.useState("");
React.useEffect(() => {
if (props.item !== undefined) {
@ -50,9 +50,9 @@ export default function JournalEdit(props: PropsType) {
} else {
setInfo({
uid: EteSync.genUid(),
type: 'ADDRESS_BOOK',
displayName: '',
description: '',
type: "ADDRESS_BOOK",
displayName: "",
description: "",
} as EteSync.CollectionInfo);
}
}, [props.item]);
@ -64,7 +64,7 @@ export default function JournalEdit(props: PropsType) {
function onSubmit(e: React.FormEvent<any>) {
e.preventDefault();
const saveErrors: FormErrors = {};
const fieldRequired = 'This field is required!';
const fieldRequired = "This field is required!";
const { onSave } = props;
@ -76,7 +76,7 @@ export default function JournalEdit(props: PropsType) {
}
if (selectedColor && !color) {
saveErrors.color = 'Must be of the form #RRGGBB or #RRGGBBAA or empty';
saveErrors.color = "Must be of the form #RRGGBB or #RRGGBBAA or empty";
}
if (Object.keys(saveErrors).length > 0) {
@ -94,28 +94,28 @@ export default function JournalEdit(props: PropsType) {
const { item, onDelete, onCancel } = props;
const pageTitle = (item !== undefined) ? item.displayName : 'New Journal';
const pageTitle = (item !== undefined) ? item.displayName : "New Journal";
const styles = {
fullWidth: {
width: '100%',
width: "100%",
},
submit: {
marginTop: 40,
marginBottom: 20,
textAlign: 'right' as any,
textAlign: "right" as any,
},
};
const journalTypes = {
ADDRESS_BOOK: 'Address Book',
CALENDAR: 'Calendar',
TASKS: 'Task List',
ADDRESS_BOOK: "Address Book",
CALENDAR: "Calendar",
TASKS: "Task List",
};
let collectionColorBox: React.ReactNode;
switch (info.type) {
case 'CALENDAR':
case 'TASKS':
case "CALENDAR":
case "TASKS":
collectionColorBox = (
<ColorPicker
defaultColor={defaultColor}
@ -130,7 +130,7 @@ export default function JournalEdit(props: PropsType) {
return (
<>
<AppBarOverride title={pageTitle} />
<Container style={{ maxWidth: '30rem' }}>
<Container style={{ maxWidth: "30rem" }}>
<form onSubmit={onSubmit}>
<FormControl disabled={props.item !== undefined} style={styles.fullWidth}>
<InputLabel>
@ -180,7 +180,7 @@ export default function JournalEdit(props: PropsType) {
{props.item &&
<Button
variant="contained"
style={{ marginLeft: 15, backgroundColor: colors.red[500], color: 'white' }}
style={{ marginLeft: 15, backgroundColor: colors.red[500], color: "white" }}
onClick={onDeleteRequest}
>
<IconDelete style={{ marginRight: 8 }} />

@ -1,20 +1,20 @@
// SPDX-FileCopyrightText: © 2017 EteSync Authors
// SPDX-License-Identifier: AGPL-3.0-only
import * as React from 'react';
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 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 LoadingIndicator from "../widgets/LoadingIndicator";
import ConfirmationDialog from "../widgets/ConfirmationDialog";
import PrettyFingerprint from "../widgets/PrettyFingerprint";
import * as EteSync from 'etesync';
import { CredentialsData } from '../store';
import * as EteSync from "etesync";
import { CredentialsData } from "../store";
import { handleInputChange } from '../helpers';
import { handleInputChange } from "../helpers";
interface PropsType {
etesync: CredentialsData;
@ -25,8 +25,8 @@ interface PropsType {
class JournalMemberAddDialog extends React.PureComponent<PropsType> {
public state = {
addUser: '',
publicKey: '',
addUser: "",
publicKey: "",
readOnly: false,
userChosen: false,
error: undefined as Error | undefined,
@ -75,7 +75,7 @@ class JournalMemberAddDialog extends React.PureComponent<PropsType> {
<p>
Verify {addUser}'s security fingerprint to ensure the encryption is secure.
</p>
<div style={{ textAlign: 'center' }}>
<div style={{ textAlign: "center" }}>
<PrettyFingerprint publicKey={publicKey} />
</div>
</ConfirmationDialog>
@ -99,7 +99,7 @@ class JournalMemberAddDialog extends React.PureComponent<PropsType> {
name="addUser"
type="email"
placeholder="User email"
style={{ width: '100%' }}
style={{ width: "100%" }}
value={addUser}
onChange={this.handleInputChange}
/>

@ -1,26 +1,26 @@
// SPDX-FileCopyrightText: © 2017 EteSync Authors
// SPDX-License-Identifier: AGPL-3.0-only
import * as React from 'react';
import sjcl from 'sjcl';
import * as React from "react";
import sjcl from "sjcl";
import { List, ListItem } from '../widgets/List';
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';
import { Theme, withTheme } from "@material-ui/core/styles";
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 AppBarOverride from "../widgets/AppBarOverride";
import Container from "../widgets/Container";
import LoadingIndicator from "../widgets/LoadingIndicator";
import ConfirmationDialog from "../widgets/ConfirmationDialog";
import JournalMemberAddDialog from './JournalMemberAddDialog';
import JournalMemberAddDialog from "./JournalMemberAddDialog";
import * as EteSync from 'etesync';
import { CredentialsData, UserInfoData } from '../store';
import * as EteSync from "etesync";
import { CredentialsData, UserInfoData } from "../store";
import { SyncInfoJournal } from '../SyncGate';
import { SyncInfoJournal } from "../SyncGate";
interface PropsType {
etesync: CredentialsData;
@ -57,7 +57,7 @@ class JournalMembers extends React.PureComponent<PropsTypeInner> {
return (
<>
<AppBarOverride title={`${info.displayName} - Members`} />
<Container style={{ maxWidth: '30rem' }}>
<Container style={{ maxWidth: "30rem" }}>
{members ?
<List>
<ListItem rightIcon={<IconMemberAdd />} onClick={() => this.setState({ addMemberOpen: true })}>
@ -150,7 +150,7 @@ class JournalMembers extends React.PureComponent<PropsTypeInner> {
const creds = etesync.credentials;
const apiBase = etesync.serviceApiUrl;
const journalMembersManager = new EteSync.JournalMembersManager(creds, apiBase, info.uid);
journalMembersManager.delete({ user: revokeUser!, key: '' }).then(() => {
journalMembersManager.delete({ user: revokeUser!, key: "" }).then(() => {
this.fetchMembers();
});
this.setState({

@ -1,27 +1,27 @@
// SPDX-FileCopyrightText: © 2017 EteSync Authors
// SPDX-License-Identifier: AGPL-3.0-only
import * as React from 'react';
import { History } from 'history';
import * as React from "react";
import { History } from "history";
import { Link } from 'react-router-dom';
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 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 { List, ListItem } from "../widgets/List";
import AppBarOverride from '../widgets/AppBarOverride';
import Container from '../widgets/Container';
import AppBarOverride from "../widgets/AppBarOverride";
import Container from "../widgets/Container";
import { routeResolver } from '../App';
import { routeResolver } from "../App";
import { JournalsData, UserInfoData, CredentialsData } from '../store';
import ColorBox from '../widgets/ColorBox';
import { colorIntToHtml } from '../journal-processors';
import { JournalsData, UserInfoData, CredentialsData } from "../store";
import ColorBox from "../widgets/ColorBox";
import { colorIntToHtml } from "../journal-processors";
interface PropsType {
etesync: CredentialsData;
@ -34,7 +34,7 @@ export default function JournalsList(props: PropsType) {
const derived = props.etesync.encryptionKey;
function journalClicked(journalUid: string) {
props.history.push(routeResolver.getRoute('journals._id', { journalUid }));
props.history.push(routeResolver.getRoute("journals._id", { journalUid }));
}
const journalMap = props.journals.reduce(
@ -45,8 +45,8 @@ export default function JournalsList(props: PropsType) {
const info = journal.getInfo(cryptoManager);
let colorBox: React.ReactElement | undefined;
switch (info.type) {
case 'CALENDAR':
case 'TASKS':
case "CALENDAR":
case "TASKS":
colorBox = (
<ColorBox size={24} color={colorIntToHtml(info.color)} />
);
@ -74,7 +74,7 @@ export default function JournalsList(props: PropsType) {
<IconButton
component={Link}
title="New"
{...{ to: routeResolver.getRoute('journals.new') }}
{...{ to: routeResolver.getRoute("journals.new") }}
>
<IconAdd />
</IconButton>

@ -1,22 +1,22 @@
// SPDX-FileCopyrightText: © 2017 EteSync Authors
// SPDX-License-Identifier: AGPL-3.0-only
import * as React from 'react';
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 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 { List, ListItem } from "../widgets/List";
import AppBarOverride from '../widgets/AppBarOverride';
import Container from '../widgets/Container';
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';
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;
@ -37,8 +37,8 @@ export default function JournalsList(props: PropsType) {
const info = journal.getInfo(cryptoManager);
let colorBox: React.ReactElement | undefined;
switch (info.type) {
case 'CALENDAR':
case 'TASKS':
case "CALENDAR":
case "TASKS":
colorBox = (
<ColorBox size={24} color={colorIntToHtml(info.color)} />
);

@ -1,23 +1,23 @@
// 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 * 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 Journal from "./Journal";
import JournalEdit from "./JournalEdit";
import JournalMembers from "./JournalMembers";
import JournalsList from "./JournalsList";
import JournalsListImport from "./JournalsListImport";
import { routeResolver } from '../App';
import { routeResolver } from "../App";
import { store, JournalsData, UserInfoData, CredentialsData } from '../store';
import { addJournal, deleteJournal, updateJournal } from '../store/actions';
import { SyncInfo } from '../SyncGate';
import { store, JournalsData, UserInfoData, CredentialsData } from "../store";
import { addJournal, deleteJournal, updateJournal } from "../store/actions";
import { SyncInfo } from "../SyncGate";
import * as EteSync from 'etesync';
import * as EteSync from "etesync";
class Journals extends React.PureComponent {
public props: {
@ -40,7 +40,7 @@ class Journals extends React.PureComponent {
return (
<Switch>
<Route
path={routeResolver.getRoute('journals')}
path={routeResolver.getRoute("journals")}
exact
render={({ history }) => (
<>
@ -54,7 +54,7 @@ class Journals extends React.PureComponent {
)}
/>
<Route
path={routeResolver.getRoute('journals.import')}
path={routeResolver.getRoute("journals.import")}
exact
render={() => (
<>
@ -68,7 +68,7 @@ class Journals extends React.PureComponent {
)}
/>
<Route
path={routeResolver.getRoute('journals.new')}
path={routeResolver.getRoute("journals.new")}
render={() => (
<JournalEdit
syncInfo={this.props.syncInfo}
@ -79,7 +79,7 @@ class Journals extends React.PureComponent {
)}
/>
<Route
path={routeResolver.getRoute('journals._id')}
path={routeResolver.getRoute("journals._id")}
render={({ match }) => {
const journalUid = match.params.journalUid;
@ -94,7 +94,7 @@ class Journals extends React.PureComponent {
return (
<Switch>
<Route
path={routeResolver.getRoute('journals._id.edit')}
path={routeResolver.getRoute("journals._id.edit")}
render={() => (
<JournalEdit
syncInfo={this.props.syncInfo}
@ -106,7 +106,7 @@ class Journals extends React.PureComponent {
)}
/>
<Route
path={routeResolver.getRoute('journals._id.members')}
path={routeResolver.getRoute("journals._id.members")}
render={() => (
<JournalMembers
etesync={this.props.etesync}
@ -116,7 +116,7 @@ class Journals extends React.PureComponent {
)}
/>
<Route
path={routeResolver.getRoute('journals._id')}
path={routeResolver.getRoute("journals._id")}
render={() => (
<Journal
etesync={this.props.etesync}
@ -169,7 +169,7 @@ class Journals extends React.PureComponent {
journal.setInfo(cryptoManager, info);
store.dispatch<any>(deleteJournal(this.props.etesync, journal)).then(() =>
this.props.history.push(routeResolver.getRoute('journals'))
this.props.history.push(routeResolver.getRoute("journals"))
);
}

@ -1,15 +1,15 @@
// 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 React from "react";
import { Route, Switch, withRouter } from "react-router";
import * as EteSync from 'etesync';
import * as EteSync from "etesync";
import { routeResolver } from '../App';
import { routeResolver } from "../App";
import { History } from 'history';
import { SyncInfo } from '../SyncGate';
import { History } from "history";
import { SyncInfo } from "../SyncGate";
function objValues(obj: any) {
return Object.keys(obj).map((x) => obj[x]);
@ -33,7 +33,7 @@ export function journalView(JournalList: any, JournalItem: any) {
const uid = contact.uid;
this.props.history!.push(
routeResolver.getRoute('journals._id.items._id', { journalUid: this.props.journal.uid, itemUid: encodeURIComponent(uid) }));
routeResolver.getRoute("journals._id.items._id", { journalUid: this.props.journal.uid, itemUid: encodeURIComponent(uid) }));
}
public render() {
@ -42,7 +42,7 @@ export function journalView(JournalList: any, JournalItem: any) {
return (
<Switch>
<Route
path={routeResolver.getRoute('journals._id')}
path={routeResolver.getRoute("journals._id")}
exact
render={() => (
<JournalList syncInfo={this.props.syncInfo} entries={objValues(items)} onItemClick={this.itemClicked} />
@ -50,7 +50,7 @@ export function journalView(JournalList: any, JournalItem: any) {
}
/>
<Route
path={routeResolver.getRoute('journals._id.items._id')}
path={routeResolver.getRoute("journals._id.items._id")}
exact
render={({ match }) => {

@ -1,26 +1,26 @@
// SPDX-FileCopyrightText: © 2017 EteSync Authors
// SPDX-License-Identifier: AGPL-3.0-only
import * as React from 'react';
import * as React from "react";
import { Action } from 'redux-actions';
import { Action } from "redux-actions";
import Container from './widgets/Container';
import ExternalLink from './widgets/ExternalLink';
import SyncGate from './SyncGate';
import LoginForm from './components/LoginForm';
import EncryptionLoginForm from './components/EncryptionLoginForm';
import Container from "./widgets/Container";
import ExternalLink from "./widgets/ExternalLink";
import SyncGate from "./SyncGate";
import LoginForm from "./components/LoginForm";
import EncryptionLoginForm from "./components/EncryptionLoginForm";
import { store, StoreState, CredentialsDataRemote } from './store';
import { deriveKey, fetchCredentials, fetchUserInfo, logout } from './store/actions';
import { store, StoreState, CredentialsDataRemote } from "./store";
import { deriveKey, fetchCredentials, fetchUserInfo, logout } from "./store/actions";
import * as EteSync from 'etesync';
import * as C from './constants';
import * as EteSync from "etesync";
import * as C from "./constants";
import SignedPagesBadge from './images/signed-pages-badge.svg';
import LoadingIndicator from './widgets/LoadingIndicator';
import { useCredentials, useRemoteCredentials } from './login';
import { useSelector } from 'react-redux';
import SignedPagesBadge from "./images/signed-pages-badge.svg";
import LoadingIndicator from "./widgets/LoadingIndicator";
import { useCredentials, useRemoteCredentials } from "./login";
import { useSelector } from "react-redux";
function EncryptionPart(props: { credentials: CredentialsDataRemote }) {
@ -60,7 +60,7 @@ function EncryptionPart(props: { credentials: CredentialsDataRemote }) {
try {
userInfo.verify(userInfoCryptoManager);
} catch (e) {
setError(new EteSync.EncryptionPasswordError('Wrong encryption password'));
setError(new EteSync.EncryptionPasswordError("Wrong encryption password"));
return;
}
}
@ -70,7 +70,7 @@ function EncryptionPart(props: { credentials: CredentialsDataRemote }) {
const isNewUser = !userInfo;
return (
<Container style={{ maxWidth: '30rem' }}>
<Container style={{ maxWidth: "30rem" }}>
<h2>Encryption Password</h2>
{(isNewUser) ?
<div>
@ -115,17 +115,17 @@ export default function LoginGate() {
if (remoteCredentials === null) {
const style = {
isSafe: {
textDecoration: 'none',
display: 'block',
textDecoration: "none",
display: "block",
},
divider: {
margin: '30px 0',
color: '#00000025',
margin: "30px 0",
color: "#00000025",
},
};
return (
<Container style={{ maxWidth: '30rem' }}>
<Container style={{ maxWidth: "30rem" }}>
<h2>Please Log In</h2>
<LoginForm
onSubmit={onFormSubmit}
@ -140,7 +140,7 @@ export default function LoginGate() {
<li><ExternalLink style={style.isSafe} href={C.homePage}>
The EteSync Website
</ExternalLink></li>
<li><ExternalLink style={style.isSafe} href={C.faq + '#web-client'}>
<li><ExternalLink style={style.isSafe} href={C.faq + "#web-client"}>
Is the web client safe to use?
</ExternalLink></li>
<li><ExternalLink style={style.isSafe} href={C.sourceCode}>Source code</ExternalLink></li>

@ -1,38 +1,38 @@
// SPDX-FileCopyrightText: © 2017 EteSync Authors
// SPDX-License-Identifier: AGPL-3.0-only
import * as React from 'react';
import Fab from '@material-ui/core/Fab';
import ContentAdd from '@material-ui/icons/Add';
import Tab from '@material-ui/core/Tab';
import Tabs from '@material-ui/core/Tabs';
import { Theme, withTheme } from '@material-ui/core/styles';
import * as React from "react";
import Fab from "@material-ui/core/Fab";
import ContentAdd from "@material-ui/icons/Add";
import Tab from "@material-ui/core/Tab";
import Tabs from "@material-ui/core/Tabs";
import { Theme, withTheme } from "@material-ui/core/styles";
import * as ICAL from 'ical.js';
import * as ICAL from "ical.js";
import * as EteSync from 'etesync';
import * as EteSync from "etesync";
import { Location, History } from 'history';
import { Location, History } from "history";
import Container from '../widgets/Container';
import Container from "../widgets/Container";
import SearchableAddressBook from '../components/SearchableAddressBook';
import Calendar from '../components/Calendar';
import TaskList from '../components/Tasks/TaskList';
import SearchableAddressBook from "../components/SearchableAddressBook";
import Calendar from "../components/Calendar";
import TaskList from "../components/Tasks/TaskList";
import { EventType, ContactType, TaskType, PimType } from '../pim-types';
import { EventType, ContactType, TaskType, PimType } from "../pim-types";
import { routeResolver } from '../App';
import { routeResolver } from "../App";
import { historyPersistor } from '../persist-state-history';
import { SyncInfo } from '../SyncGate';
import { UserInfoData, CredentialsData } from '../store';
import { historyPersistor } from "../persist-state-history";
import { SyncInfo } from "../SyncGate";
import { UserInfoData, CredentialsData } from "../store";
const addressBookTitle = 'Address Book';
const calendarTitle = 'Calendar';
const tasksTitle = 'Tasks';
const addressBookTitle = "Address Book";
const calendarTitle = "Calendar";
const tasksTitle = "Tasks";
const PersistCalendar = historyPersistor('Calendar')(Calendar);
const PersistCalendar = historyPersistor("Calendar")(Calendar);
interface PropsType {
contacts: ContactType[];
@ -68,7 +68,7 @@ class PimMain extends React.PureComponent<PropsType> {
const itemUid = `${(event as any).journalUid}|${encodeURIComponent(event.uid)}`;
this.props.history!.push(
routeResolver.getRoute('pim.events._id', { itemUid }));
routeResolver.getRoute("pim.events._id", { itemUid }));
}
public taskClicked(event: ICAL.Event) {
@ -76,7 +76,7 @@ class PimMain extends React.PureComponent<PropsType> {
const itemUid = `${(event as any).journalUid}|${encodeURIComponent(event.uid)}`;
this.props.history!.push(
routeResolver.getRoute('pim.tasks._id.edit', { itemUid }));
routeResolver.getRoute("pim.tasks._id.edit", { itemUid }));
}
public contactClicked(contact: ContactType) {
@ -84,12 +84,12 @@ class PimMain extends React.PureComponent<PropsType> {
const itemUid = `${(contact as any).journalUid}|${encodeURIComponent(contact.uid)}`;
this.props.history!.push(
routeResolver.getRoute('pim.contacts._id', { itemUid }));
routeResolver.getRoute("pim.contacts._id", { itemUid }));
}
public newEvent(start?: Date, end?: Date) {
this.props.history!.push(
routeResolver.getRoute('pim.events.new'),
routeResolver.getRoute("pim.events.new"),
{ start, end }
);
}
@ -97,13 +97,13 @@ class PimMain extends React.PureComponent<PropsType> {
public floatingButtonClicked() {
if (this.state.tab === 0) {
this.props.history!.push(
routeResolver.getRoute('pim.contacts.new')
routeResolver.getRoute("pim.contacts.new")
);
} else if (this.state.tab === 1) {
this.newEvent();
} else if (this.state.tab === 2) {
this.props.history!.push(
routeResolver.getRoute('pim.tasks.new')
routeResolver.getRoute("pim.tasks.new")
);
}
}
@ -114,11 +114,11 @@ class PimMain extends React.PureComponent<PropsType> {
const style = {
floatingButton: {
margin: 0,
top: 'auto',
top: "auto",
right: 20,
bottom: 20,
left: 'auto',
position: 'fixed',
left: "auto",
position: "fixed",
} as any,
};
@ -176,4 +176,4 @@ class PimMain extends React.PureComponent<PropsType> {
}
}
export default withTheme(historyPersistor('PimMain')(PimMain));
export default withTheme(historyPersistor("PimMain")(PimMain));

@ -1,47 +1,47 @@
// SPDX-FileCopyrightText: © 2017 EteSync Authors
// SPDX-License-Identifier: AGPL-3.0-only
import * as React from 'react';
import { Route, Switch } from 'react-router';
import Button from '@material-ui/core/Button';
import IconEdit from '@material-ui/icons/Edit';
import IconDuplicate from '@material-ui/icons/FileCopy';
import IconChangeHistory from '@material-ui/icons/ChangeHistory';
import { withStyles } from '@material-ui/core/styles';
import * as React from "react";
import { Route, Switch } from "react-router";
import Button from "@material-ui/core/Button";
import IconEdit from "@material-ui/icons/Edit";
import IconDuplicate from "@material-ui/icons/FileCopy";
import IconChangeHistory from "@material-ui/icons/ChangeHistory";
import { withStyles } from "@material-ui/core/styles";
import { RouteComponentProps, withRouter } from 'react-router';
import { RouteComponentProps, withRouter } from "react-router";
import { Action } from 'redux-actions';
import { Action } from "redux-actions";
import * as EteSync from 'etesync';
import * as EteSync from "etesync";
import { createSelector } from 'reselect';
import { createSelector } from "reselect";
import { History } from 'history';
import { History } from "history";
import { PimType, ContactType, EventType, TaskType } from '../pim-types';
import { PimType, ContactType, EventType, TaskType } from "../pim-types";
import Container from '../widgets/Container';
import Container from "../widgets/Container";
import JournalEntries from '../components/JournalEntries';
import ContactEdit from '../components/ContactEdit';
import Contact from '../components/Contact';
import EventEdit from '../components/EventEdit';
import Event from '../components/Event';
import TaskEdit from '../components/Tasks/TaskEdit';
import Task from '../components/Tasks/Task';
import PimMain from './PimMain';
import JournalEntries from "../components/JournalEntries";
import ContactEdit from "../components/ContactEdit";
import Contact from "../components/Contact";
import EventEdit from "../components/EventEdit";
import Event from "../components/Event";
import TaskEdit from "../components/Tasks/TaskEdit";
import Task from "../components/Tasks/Task";
import PimMain from "./PimMain";
import { routeResolver } from '../App';
import { routeResolver } from "../App";
import { store, CredentialsData, UserInfoData } from '../store';
import { fetchEntries } from '../store/actions';
import { store, CredentialsData, UserInfoData } from "../store";
import { fetchEntries } from "../store/actions";
import { SyncInfo } from '../SyncGate';
import { SyncInfo } from "../SyncGate";
import { addJournalEntry } from '../etesync-helpers';
import { addJournalEntry } from "../etesync-helpers";
import { syncEntriesToItemMap, syncEntriesToEventItemMap, syncEntriesToTaskItemMap } from '../journal-processors';
import { syncEntriesToItemMap, syncEntriesToEventItemMap, syncEntriesToTaskItemMap } from "../journal-processors";
const itemsSelector = createSelector(
(props: {syncInfo: SyncInfo}) => props.syncInfo,
@ -58,13 +58,13 @@ const itemsSelector = createSelector(
const collectionInfo = syncJournal.collection;
if (collectionInfo.type === 'ADDRESS_BOOK') {
if (collectionInfo.type === "ADDRESS_BOOK") {
addressBookItems = syncEntriesToItemMap(collectionInfo, syncEntries, addressBookItems);
collectionsAddressBook.push(collectionInfo);
} else if (collectionInfo.type === 'CALENDAR') {
} else if (collectionInfo.type === "CALENDAR") {
calendarItems = syncEntriesToEventItemMap(collectionInfo, syncEntries, calendarItems);
collectionsCalendar.push(collectionInfo);
} else if (collectionInfo.type === 'TASKS') {
} else if (collectionInfo.type === "TASKS") {
taskListItems = syncEntriesToTaskItemMap(collectionInfo, syncEntries, taskListItems);
collectionsTaskList.push(collectionInfo);
}
@ -88,9 +88,9 @@ const ItemChangeLog = React.memo((props: any) => {
paramItemUid,
} = props;
const tmp = paramItemUid.split('|');
const tmp = paramItemUid.split("|");
const journalUid = tmp.shift();
const uid = tmp.join('|');
const uid = tmp.join("|");
const journalItem = syncInfo.get(journalUid);
return (
@ -152,10 +152,10 @@ const CollectionRoutes = withStyles(styles)(withRouter(
return (
<Switch>
<Route
path={routeResolver.getRoute(props.routePrefix + '.new')}
path={routeResolver.getRoute(props.routePrefix + ".new")}
exact
render={() => (
<Container style={{ maxWidth: '30rem' }}>
<Container style={{ maxWidth: "30rem" }}>
<ComponentEdit
key={props.routePrefix}
collections={props.collections}
@ -167,7 +167,7 @@ const CollectionRoutes = withStyles(styles)(withRouter(
)}
/>
<Route
path={routeResolver.getRoute(props.routePrefix + '._id.edit')}
path={routeResolver.getRoute(props.routePrefix + "._id.edit")}
exact
render={({ match }) => {
const itemUid = decodeURIComponent(match.params.itemUid);
@ -175,7 +175,7 @@ const CollectionRoutes = withStyles(styles)(withRouter(
return PageNotFound();
}
return (
<Container style={{ maxWidth: '30rem' }}>
<Container style={{ maxWidth: "30rem" }}>
{(itemUid in props.items) &&
<ComponentEdit
key={(props.items[itemUid] as any).journalUid}
@ -192,9 +192,9 @@ const CollectionRoutes = withStyles(styles)(withRouter(
);
}}
/>
{props.routePrefix === 'pim.events' &&
{props.routePrefix === "pim.events" &&
<Route
path={routeResolver.getRoute(props.routePrefix + '._id.duplicate')}
path={routeResolver.getRoute(props.routePrefix + "._id.duplicate")}
exact
render={({ match }) => {
const itemUid = decodeURIComponent(match.params.itemUid);
@ -202,7 +202,7 @@ const CollectionRoutes = withStyles(styles)(withRouter(
return PageNotFound();
}
return (
<Container style={{ maxWidth: '30rem' }}>
<Container style={{ maxWidth: "30rem" }}>
{(itemUid in props.items) &&
<ComponentEdit
key={(props.items[itemUid] as any).journalUid}
@ -222,7 +222,7 @@ const CollectionRoutes = withStyles(styles)(withRouter(
/>
}
<Route
path={routeResolver.getRoute(props.routePrefix + '._id.log')}
path={routeResolver.getRoute(props.routePrefix + "._id.log")}
exact
render={({ match }) => {
const paramItemUid = decodeURIComponent(match.params.itemUid);
@ -240,7 +240,7 @@ const CollectionRoutes = withStyles(styles)(withRouter(
}}
/>
<Route
path={routeResolver.getRoute(props.routePrefix + '._id')}
path={routeResolver.getRoute(props.routePrefix + "._id")}
exact
render={({ match, history }) => {
const itemUid = decodeURIComponent(match.params.itemUid);
@ -249,13 +249,13 @@ const CollectionRoutes = withStyles(styles)(withRouter(
}
return (
<Container>
<div style={{ textAlign: 'right', marginBottom: 15 }}>
<div style={{ textAlign: "right", marginBottom: 15 }}>
<Button
variant="contained"
className={classes.button}
onClick={() =>
history.push(routeResolver.getRoute(
props.routePrefix + '._id.log',
props.routePrefix + "._id.log",
{ itemUid: match.params.itemUid }))
}
>
@ -271,7 +271,7 @@ const CollectionRoutes = withStyles(styles)(withRouter(
style={{ marginLeft: 15 }}
onClick={() =>
history.push(routeResolver.getRoute(
props.routePrefix + '._id.edit',
props.routePrefix + "._id.edit",
{ itemUid: match.params.itemUid }))
}
>
@ -279,7 +279,7 @@ const CollectionRoutes = withStyles(styles)(withRouter(
Edit
</Button>
{props.routePrefix === 'pim.events' &&
{props.routePrefix === "pim.events" &&
<Button
color="secondary"
variant="contained"
@ -288,7 +288,7 @@ const CollectionRoutes = withStyles(styles)(withRouter(
style={{ marginLeft: 15 }}
onClick={() =>
history.push(routeResolver.getRoute(
props.routePrefix + '._id.duplicate',
props.routePrefix + "._id.duplicate",
{ itemUid: match.params.itemUid }))
}
>
@ -385,7 +385,7 @@ class Pim extends React.PureComponent {
this.props.etesync, this.props.userInfo, journal,
prevUid, action, item.toIcal()));
(deleteItem as any).then(() => {
this.props.history.push(routeResolver.getRoute('pim'));
this.props.history.push(routeResolver.getRoute("pim"));
});
});
}
@ -401,7 +401,7 @@ class Pim extends React.PureComponent {
return (
<Switch>
<Route
path={routeResolver.getRoute('pim')}
path={routeResolver.getRoute("pim")}
exact
render={({ history }) => (
<PimMain
@ -418,7 +418,7 @@ class Pim extends React.PureComponent {
)}
/>
<Route
path={routeResolver.getRoute('pim.contacts')}
path={routeResolver.getRoute("pim.contacts")}
render={() => (
<CollectionRoutes
syncInfo={this.props.syncInfo}
@ -434,7 +434,7 @@ class Pim extends React.PureComponent {
)}
/>
<Route
path={routeResolver.getRoute('pim.events')}
path={routeResolver.getRoute("pim.events")}
render={() => (
<CollectionRoutes
syncInfo={this.props.syncInfo}
@ -450,7 +450,7 @@ class Pim extends React.PureComponent {
)}
/>
<Route
path={routeResolver.getRoute('pim.tasks')}
path={routeResolver.getRoute("pim.tasks")}
render={() => (
<CollectionRoutes
syncInfo={this.props.syncInfo}

@ -1,23 +1,23 @@
// SPDX-FileCopyrightText: © 2017 EteSync Authors
// SPDX-License-Identifier: AGPL-3.0-only
import * as React from 'react';
import { useSelector, useDispatch } from 'react-redux';
import * as React from "react";
import { useSelector, useDispatch } from "react-redux";
import Select from '@material-ui/core/Select';
import MenuItem from '@material-ui/core/MenuItem';
import FormControl from '@material-ui/core/FormControl';
import FormControlLabel from '@material-ui/core/FormControlLabel';
import FormGroup from '@material-ui/core/FormGroup';
import Switch from '@material-ui/core/Switch';
import InputLabel from '@material-ui/core/InputLabel';
import Select from "@material-ui/core/Select";
import MenuItem from "@material-ui/core/MenuItem";
import FormControl from "@material-ui/core/FormControl";
import FormControlLabel from "@material-ui/core/FormControlLabel";
import FormGroup from "@material-ui/core/FormGroup";
import Switch from "@material-ui/core/Switch";
import InputLabel from "@material-ui/core/InputLabel";
import { StoreState } from '../store';
import { setSettings } from '../store/actions';
import { StoreState } from "../store";
import { setSettings } from "../store/actions";
import Container from '../widgets/Container';
import AppBarOverride from '../widgets/AppBarOverride';
import PrettyFingerprint from '../widgets/PrettyFingerprint';
import Container from "../widgets/Container";
import AppBarOverride from "../widgets/AppBarOverride";
import PrettyFingerprint from "../widgets/PrettyFingerprint";
function SecurityFingerprint() {
const userInfo = useSelector((state: StoreState) => state.cache.userInfo);
@ -58,7 +58,7 @@ export default React.memo(function Settings() {
<h1>Security Fingerprint</h1>
<SecurityFingerprint />
<h1>Date & Time</h1>
<FormControl style={{ width: '15em' }}>
<FormControl style={{ width: "15em" }}>
<InputLabel>
Locale
</InputLabel>

@ -1,28 +1,28 @@
// SPDX-FileCopyrightText: © 2017 EteSync Authors
// SPDX-License-Identifier: AGPL-3.0-only
import * as React from 'react';
import { connect } from 'react-redux';
import { RouteComponentProps, withRouter } from 'react-router';
import { List, ListItem, ListSubheader, ListDivider } from '../widgets/List';
import { Theme, withTheme } from '@material-ui/core/styles';
import ActionCode from '@material-ui/icons/Code';
import ActionHome from '@material-ui/icons/Home';
import ActionSettings from '@material-ui/icons/Settings';
import ActionJournals from '@material-ui/icons/LibraryBooks';
import ActionBugReport from '@material-ui/icons/BugReport';
import ActionQuestionAnswer from '@material-ui/icons/QuestionAnswer';
import LogoutIcon from '@material-ui/icons/PowerSettingsNew';
import IconImport from '@material-ui/icons/ImportExport';
import * as React from "react";
import { connect } from "react-redux";
import { RouteComponentProps, withRouter } from "react-router";
import { List, ListItem, ListSubheader, ListDivider } from "../widgets/List";
import { Theme, withTheme } from "@material-ui/core/styles";
import ActionCode from "@material-ui/icons/Code";
import ActionHome from "@material-ui/icons/Home";
import ActionSettings from "@material-ui/icons/Settings";
import ActionJournals from "@material-ui/icons/LibraryBooks";
import ActionBugReport from "@material-ui/icons/BugReport";
import ActionQuestionAnswer from "@material-ui/icons/QuestionAnswer";
import LogoutIcon from "@material-ui/icons/PowerSettingsNew";
import IconImport from "@material-ui/icons/ImportExport";
import logo from '../images/logo.svg';
import logo from "../images/logo.svg";
import { routeResolver } from '../App';
import { routeResolver } from "../App";
import { store, UserInfoData, StoreState, CredentialsData } from '../store';
import { logout } from '../store/actions';
import { store, UserInfoData, StoreState, CredentialsData } from "../store";
import { logout } from "../store/actions";
import * as C from '../constants';
import * as C from "../constants";
interface PropsType {
etesync: CredentialsData | null;
@ -60,7 +60,7 @@ class SideMenu extends React.PureComponent<PropsTypeInner> {
leftIcon={<ActionJournals />}
onClick={() => {
this.props.onCloseDrawerRequest();
this.props.history.push(routeResolver.getRoute('journals'));
this.props.history.push(routeResolver.getRoute("journals"));
}}
/>
<ListItem
@ -68,7 +68,7 @@ class SideMenu extends React.PureComponent<PropsTypeInner> {
leftIcon={<IconImport />}
onClick={() => {
this.props.onCloseDrawerRequest();
this.props.history.push(routeResolver.getRoute('journals.import'));
this.props.history.push(routeResolver.getRoute("journals.import"));
}}
/>
<ListItem
@ -76,7 +76,7 @@ class SideMenu extends React.PureComponent<PropsTypeInner> {
leftIcon={<ActionSettings />}
onClick={() => {
this.props.onCloseDrawerRequest();
this.props.history.push(routeResolver.getRoute('settings'));
this.props.history.push(routeResolver.getRoute("settings"));
}}
/>
<ListItem primaryText="Log Out" leftIcon={<LogoutIcon />} onClick={this.logout} />
@ -85,7 +85,7 @@ class SideMenu extends React.PureComponent<PropsTypeInner> {
}
return (
<div style={{ overflowX: 'hidden', width: 250 }}>
<div style={{ overflowX: "hidden", width: 250 }}>
<div className="App-drawer-header">
<img alt="App logo" className="App-drawer-logo" src={logo} />
<div style={{ color: theme.palette.secondary.contrastText }}>
@ -98,7 +98,7 @@ class SideMenu extends React.PureComponent<PropsTypeInner> {
leftIcon={<ActionHome />}
onClick={() => {
this.props.onCloseDrawerRequest();
this.props.history.push(routeResolver.getRoute('home'));
this.props.history.push(routeResolver.getRoute("home"));
}}
/>
{loggedInItems}

@ -1,31 +1,31 @@
// SPDX-FileCopyrightText: © 2017 EteSync Authors
// SPDX-License-Identifier: AGPL-3.0-only
import * as React from 'react';
import { useSelector } from 'react-redux';
import { Route, Switch, Redirect, RouteComponentProps, withRouter } from 'react-router';
import * as React from "react";
import { useSelector } from "react-redux";
import { Route, Switch, Redirect, RouteComponentProps, withRouter } from "react-router";
import moment from 'moment';
import 'moment/locale/en-gb';
import moment from "moment";
import "moment/locale/en-gb";
import { List, Map } from 'immutable';
import { createSelector } from 'reselect';
import { List, Map } from "immutable";
import { createSelector } from "reselect";
import { routeResolver } from './App';
import { routeResolver } from "./App";
import AppBarOverride from './widgets/AppBarOverride';
import LoadingIndicator from './widgets/LoadingIndicator';
import AppBarOverride from "./widgets/AppBarOverride";
import LoadingIndicator from "./widgets/LoadingIndicator";
import Journals from './Journals';
import Settings from './Settings';
import Debug from './Debug';
import Pim from './Pim';
import Journals from "./Journals";
import Settings from "./Settings";
import Debug from "./Debug";
import Pim from "./Pim";
import * as EteSync from 'etesync';
import { CURRENT_VERSION } from 'etesync';
import * as EteSync from "etesync";
import { CURRENT_VERSION } from "etesync";
import { store, JournalsData, EntriesData, StoreState, CredentialsData, UserInfoData } from './store';
import { addJournal, fetchAll, fetchEntries, fetchUserInfo, createUserInfo } from './store/actions';
import { store, JournalsData, EntriesData, StoreState, CredentialsData, UserInfoData } from "./store";
import { addJournal, fetchAll, fetchEntries, fetchUserInfo, createUserInfo } from "./store/actions";
export interface SyncInfoJournal {
journal: EteSync.Journal;
@ -119,16 +119,16 @@ export default withRouter(function SyncGate(props: RouteComponentProps<{}> & Pro
[
{
type: 'ADDRESS_BOOK',
name: 'My Contacts',
type: "ADDRESS_BOOK",
name: "My Contacts",
},
{
type: 'CALENDAR',
name: 'My Calendar',
type: "CALENDAR",
name: "My Calendar",
},
{
type: 'TASKS',
name: 'My Tasks',
type: "TASKS",
name: "My Tasks",
},
].forEach((collectionDesc) => {
const collection = new EteSync.CollectionInfo();
@ -179,7 +179,7 @@ export default withRouter(function SyncGate(props: RouteComponentProps<{}> & Pro
((fetchCount > 0) &&
((entryArrays.size === 0) || entryArrays.some((x) => (x.size === 0))))
) {
return (<LoadingIndicator style={{ display: 'block', margin: '40px auto' }} />);
return (<LoadingIndicator style={{ display: "block", margin: "40px auto" }} />);
}
// FIXME: Shouldn't be here
@ -191,14 +191,14 @@ export default withRouter(function SyncGate(props: RouteComponentProps<{}> & Pro
return (
<Switch>
<Route
path={routeResolver.getRoute('home')}
path={routeResolver.getRoute("home")}
exact
render={() => (
<Redirect to={routeResolver.getRoute('pim')} />
<Redirect to={routeResolver.getRoute("pim")} />
)}
/>
<Route
path={routeResolver.getRoute('pim')}
path={routeResolver.getRoute("pim")}
render={({ history }) => (
<>
<AppBarOverride title="EteSync" />
@ -212,7 +212,7 @@ export default withRouter(function SyncGate(props: RouteComponentProps<{}> & Pro
)}
/>
<Route
path={routeResolver.getRoute('journals')}
path={routeResolver.getRoute("journals")}
render={({ location, history }) => (
<Journals
etesync={etesync}
@ -225,14 +225,14 @@ export default withRouter(function SyncGate(props: RouteComponentProps<{}> & Pro
)}
/>
<Route
path={routeResolver.getRoute('settings')}
path={routeResolver.getRoute("settings")}
exact
render={() => (
<Settings />
)}
/>
<Route
path={routeResolver.getRoute('debug')}
path={routeResolver.getRoute("debug")}
exact
render={() => (
<Debug

@ -1,17 +1,17 @@
// SPDX-FileCopyrightText: © 2017 EteSync Authors
// SPDX-License-Identifier: AGPL-3.0-only
import * as React from 'react';
import * as React from "react";
import { createSelector } from 'reselect';
import { createSelector } from "reselect";
import * as colors from '@material-ui/core/colors';
import { AutoSizer, List as VirtualizedList } from 'react-virtualized';
import * as colors from "@material-ui/core/colors";
import { AutoSizer, List as VirtualizedList } from "react-virtualized";
import { Avatar } from '../widgets/Avatar';
import { List, ListItem } from '../widgets/List';
import { Avatar } from "../widgets/Avatar";
import { List, ListItem } from "../widgets/List";
import { ContactType } from '../pim-types';
import { ContactType } from "../pim-types";
function getContactColor(contact: ContactType) {
const colorOptions = [
@ -73,10 +73,10 @@ const sortSelector = createSelector(
(entries: ContactType[]) => entries,
(entries) => {
return entries.sort((_a, _b) => {
const a = _a.fn ?? '';
const b = _b.fn ?? '';
const a = _a.fn ?? "";
const b = _b.fn ?? "";
return a.localeCompare(b, undefined, { sensitivity: 'base' });
return a.localeCompare(b, undefined, { sensitivity: "base" });
});
}
);
@ -96,7 +96,7 @@ class AddressBook extends React.PureComponent<PropsType> {
: sortedEntries;
return (
<List style={{ height: 'calc(100vh - 300px)' }}>
<List style={{ height: "calc(100vh - 300px)" }}>
<AutoSizer>
{({ height, width }) => (
<VirtualizedList

@ -1,15 +1,15 @@
// SPDX-FileCopyrightText: © 2017 EteSync Authors
// SPDX-License-Identifier: AGPL-3.0-only
import * as React from 'react';
import { Calendar as BigCalendar, momentLocalizer, View } from 'react-big-calendar';
import 'react-big-calendar/lib/css/react-big-calendar.css';
import moment from 'moment';
import * as ICAL from 'ical.js';
import * as React from "react";
import { Calendar as BigCalendar, momentLocalizer, View } from "react-big-calendar";
import "react-big-calendar/lib/css/react-big-calendar.css";
import moment from "moment";
import * as ICAL from "ical.js";
import { EventType } from '../pim-types';
import { EventType } from "../pim-types";
import './Calendar.css';
import "./Calendar.css";
const calendarLocalizer = momentLocalizer(moment);
@ -25,8 +25,8 @@ function eventPropGetter(event: EventType) {
}
function agendaHeaderFormat(date: {start: Date, end: Date}, _culture: string, localizer: any) {
const format = 'll';
return localizer.format(date.start, format) + ' - ' + localizer.format(date.end, format);
const format = "ll";
return localizer.format(date.start, format) + " - " + localizer.format(date.end, format);
}
interface PropsType {
@ -75,7 +75,7 @@ class Calendar extends React.PureComponent<PropsType> {
});
return (
<div style={{ width: '100%', height: 'calc(100vh - 230px)', minHeight: 500 }}>
<div style={{ width: "100%", height: "calc(100vh - 230px)", minHeight: 500 }}>
<BigCalendar
defaultDate={new Date()}
scrollToTime={new Date(1970, 1, 1, 8)}

@ -1,21 +1,21 @@
// SPDX-FileCopyrightText: © 2017 EteSync Authors
// SPDX-License-Identifier: AGPL-3.0-only
import * as React from 'react';
import moment from 'moment';
import * as React from "react";
import moment from "moment";
import { List, ListItem, ListDivider as Divider } from '../widgets/List';
import IconHome from '@material-ui/icons/Home';
import IconDate from '@material-ui/icons/DateRange';
import CommunicationCall from '@material-ui/icons/Call';
import CommunicationChatBubble from '@material-ui/icons/ChatBubble';
import CommunicationEmail from '@material-ui/icons/Email';
import CopyIcon from '../icons/Copy';
import { List, ListItem, ListDivider as Divider } from "../widgets/List";
import IconHome from "@material-ui/icons/Home";
import IconDate from "@material-ui/icons/DateRange";
import CommunicationCall from "@material-ui/icons/Call";
import CommunicationChatBubble from "@material-ui/icons/ChatBubble";
import CommunicationEmail from "@material-ui/icons/Email";
import CopyIcon from "../icons/Copy";
import PimItemHeader from './PimItemHeader';
import PimItemHeader from "./PimItemHeader";
import { ContactType } from '../pim-types';
import { IconButton, Avatar } from '@material-ui/core';
import { ContactType } from "../pim-types";
import { IconButton, Avatar } from "@material-ui/core";
class Contact extends React.PureComponent {
public props: {
@ -24,15 +24,15 @@ class Contact extends React.PureComponent {
public render() {
if (this.props.item === undefined) {
throw Error('Contact should be defined!');
throw Error("Contact should be defined!");
}
const contact = this.props.item;
const name = contact.fn;
const revProp = contact.comp.getFirstProperty('rev');
const revProp = contact.comp.getFirstProperty("rev");
const lastModified = (revProp) ?
'Modified: ' + moment(revProp.getFirstValue().toJSDate()).format('LLLL') : undefined;
"Modified: " + moment(revProp.getFirstValue().toJSDate()).format("LLLL") : undefined;
const lists = [];
@ -74,60 +74,60 @@ class Contact extends React.PureComponent {
}
lists.push(getAllType(
'tel',
"tel",
{
leftIcon: <CommunicationCall />,
},
(x) => ('tel:' + x)
(x) => ("tel:" + x)
));
lists.push(getAllType(
'email',
"email",
{
leftIcon: <CommunicationEmail />,
},
(x) => ('mailto:' + x)
(x) => ("mailto:" + x)
));
lists.push(getAllType(
'impp',
"impp",
{
leftIcon: <CommunicationChatBubble />,
},
(x) => x,
(x) => (x.substring(x.indexOf(':') + 1)),
(x) => (x.substring(0, x.indexOf(':')))
(x) => (x.substring(x.indexOf(":") + 1)),
(x) => (x.substring(0, x.indexOf(":")))
));
lists.push(getAllType(
'adr',
"adr",
{
leftIcon: <IconHome />,
}
));
lists.push(getAllType(
'bday',
"bday",
{
leftIcon: <IconDate />,
},
undefined,
((x: any) => moment(x.toJSDate()).format('dddd, LL')),
() => 'Birthday'
((x: any) => moment(x.toJSDate()).format("dddd, LL")),
() => "Birthday"
));
lists.push(getAllType(
'anniversary',
"anniversary",
{
leftIcon: <IconDate />,
},
undefined,
((x: any) => moment(x.toJSDate()).format('dddd, LL')),
() => 'Anniversary'
((x: any) => moment(x.toJSDate()).format("dddd, LL")),
() => "Anniversary"
));
const skips = ['tel', 'email', 'impp', 'adr', 'bday', 'anniversary', 'rev',
'prodid', 'uid', 'fn', 'n', 'version', 'photo', 'note'];
const skips = ["tel", "email", "impp", "adr", "bday", "anniversary", "rev",
"prodid", "uid", "fn", "n", "version", "photo", "note"];
const theRest = contact.comp.getAllProperties().filter((prop) => (
skips.indexOf(prop.name) === -1
)).map((prop, idx) => {
@ -146,14 +146,14 @@ class Contact extends React.PureComponent {
});
{
const note = contact.comp.getFirstPropertyValue('note');
const note = contact.comp.getFirstPropertyValue("note");
const item = (
<ListItem
key="note"
insetChildren
secondaryText="note"
>
<pre style={{ wordWrap: 'break-word', whiteSpace: 'pre-wrap', overflowX: 'auto' }}>{note}</pre>
<pre style={{ wordWrap: "break-word", whiteSpace: "pre-wrap", overflowX: "auto" }}>{note}</pre>
</ListItem>
);
theRest.push([item]);
@ -176,13 +176,13 @@ class Contact extends React.PureComponent {
}
}
const contactImageSrc = contact.comp.getFirstProperty('photo')?.getFirstValue();
const contactImageSrc = contact.comp.getFirstProperty("photo")?.getFirstValue();
return (
<div>
<PimItemHeader text={name} rightItem={contactImageSrc && (<Avatar style={{ width: '3em', height: '3em' }} src={contactImageSrc} />)}>
<PimItemHeader text={name} rightItem={contactImageSrc && (<Avatar style={{ width: "3em", height: "3em" }} src={contactImageSrc} />)}>
{lastModified && (
<span style={{ fontSize: '90%' }}>{lastModified}</span>
<span style={{ fontSize: "90%" }}>{lastModified}</span>
)}
</PimItemHeader>
{lists.map((list, idx) => (

@ -1,52 +1,52 @@
// 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 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 * as colors from '@material-ui/core/colors';
import * as React from "react";
import IconButton from "@material-ui/core/IconButton";
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 * as colors from "@material-ui/core/colors";
import IconDelete from '@material-ui/icons/Delete';
import IconAdd from '@material-ui/icons/Add';
import IconClear from '@material-ui/icons/Clear';
import IconCancel from '@material-ui/icons/Clear';
import IconSave from '@material-ui/icons/Save';
import IconDelete from "@material-ui/icons/Delete";
import IconAdd from "@material-ui/icons/Add";
import IconClear from "@material-ui/icons/Clear";
import IconCancel from "@material-ui/icons/Clear";
import IconSave from "@material-ui/icons/Save";
import ConfirmationDialog from '../widgets/ConfirmationDialog';
import ConfirmationDialog from "../widgets/ConfirmationDialog";
import * as uuid from 'uuid';
import * as ICAL from 'ical.js';
import * as uuid from "uuid";
import * as ICAL from "ical.js";
import * as EteSync from 'etesync';
import * as EteSync from "etesync";
import { ContactType } from '../pim-types';
import { ContactType } from "../pim-types";
import { History } from 'history';
import { History } from "history";
const telTypes = [
{ type: 'Home' },
{ type: 'Work' },
{ type: 'Cell' },
{ type: 'Other' },
{ type: "Home" },
{ type: "Work" },
{ type: "Cell" },
{ type: "Other" },
];
const emailTypes = telTypes;
const addressTypes = [
{ type: 'Home' },
{ type: 'Work' },
{ type: 'Other' },
{ type: "Home" },
{ type: "Work" },
{ type: "Other" },
];
const imppTypes = [
{ type: 'Jabber' },
{ type: 'Hangouts' },
{ type: 'Other' },
{ type: "Jabber" },
{ type: "Hangouts" },
{ type: "Other" },
];
const TypeSelector = (props: any) => {
@ -70,8 +70,8 @@ class ValueType {
public value: string;
constructor(type?: string, value?: string) {
this.type = type ? type : 'home';
this.value = value ? value : '';
this.type = type ? type : "home";
this.value = value ? value : "";
}
}
@ -150,22 +150,22 @@ class ContactEdit extends React.PureComponent<PropsType> {
constructor(props: any) {
super(props);
this.state = {
uid: '',
fn: '',
lastName: '',
firstName: '',
middleName: '',
namePrefix: '',
nameSuffix: '',
uid: "",
fn: "",
lastName: "",
firstName: "",
middleName: "",
namePrefix: "",
nameSuffix: "",
phone: [new ValueType()],
email: [new ValueType()],
address: [new ValueType()],
impp: [new ValueType('jabber')],
org: '',
note: '',
title: '',
impp: [new ValueType("jabber")],
org: "",
note: "",
title: "",
journalUid: '',
journalUid: "",
showDeleteDialog: false,
};
@ -173,7 +173,7 @@ class ContactEdit extends React.PureComponent<PropsType> {
const contact = this.props.item;
this.state.uid = contact.uid;
this.state.fn = contact.fn ? contact.fn : '';
this.state.fn = contact.fn ? contact.fn : "";
if (contact.n) {
this.state.lastName = contact.n[0];
this.state.firstName = contact.n[1];
@ -181,18 +181,18 @@ class ContactEdit extends React.PureComponent<PropsType> {
this.state.namePrefix = contact.n[3];
this.state.nameSuffix = contact.n[4];
} else {
let name = this.state.fn.trim().split(',');
if (name.length > 2 && name[0] !== '' && name[name.length - 1] !== '') {
this.state.nameSuffix = name.pop() || '';
let name = this.state.fn.trim().split(",");
if (name.length > 2 && name[0] !== "" && name[name.length - 1] !== "") {
this.state.nameSuffix = name.pop() || "";
}
name = name.join(',').split(' ');
name = name.join(",").split(" ");
if (name.length === 1) {
this.state.firstName = name[0];
} else if (name.length === 2) {
this.state.firstName = name[0];
this.state.lastName = name[1];
} else if (name.length > 2) {
this.state.firstName = name.slice(0, name.length - 2).join(' ');
this.state.firstName = name.slice(0, name.length - 2).join(" ");
this.state.middleName = name[name.length - 2];
this.state.lastName = name[name.length - 1];
}
@ -208,19 +208,19 @@ class ContactEdit extends React.PureComponent<PropsType> {
))
);
this.state.phone = propToValueType(contact.comp, 'tel');
this.state.email = propToValueType(contact.comp, 'email');
this.state.address = propToValueType(contact.comp, 'adr');
this.state.impp = propToValueType(contact.comp, 'impp');
this.state.phone = propToValueType(contact.comp, "tel");
this.state.email = propToValueType(contact.comp, "email");
this.state.address = propToValueType(contact.comp, "adr");
this.state.impp = propToValueType(contact.comp, "impp");
const propToStringType = (comp: ICAL.Component, propName: string) => {
const val = comp.getFirstPropertyValue(propName);
return val ? val : '';
return val ? val : "";
};
this.state.org = propToStringType(contact.comp, 'org');
this.state.title = propToStringType(contact.comp, 'title');
this.state.note = propToStringType(contact.comp, 'note');
this.state.org = propToStringType(contact.comp, "org");
this.state.title = propToStringType(contact.comp, "title");
this.state.note = propToStringType(contact.comp, "note");
} else {
this.state.uid = uuid.v4();
@ -241,7 +241,7 @@ class ContactEdit extends React.PureComponent<PropsType> {
}
public addValueType(name: string, _type?: string) {
const type = _type ? _type : 'home';
const type = _type ? _type : "home";
this.setState((prevState) => {
const newArray = prevState[name].slice(0);
newArray.push(new ValueType(type));
@ -293,14 +293,14 @@ class ContactEdit extends React.PureComponent<PropsType> {
const contact = (this.props.item) ?
this.props.item.clone()
:
new ContactType(new ICAL.Component(['vcard', [], []]))
new ContactType(new ICAL.Component(["vcard", [], []]))
;
const comp = contact.comp;
comp.updatePropertyWithValue('prodid', '-//iCal.js EteSync Web');
comp.updatePropertyWithValue('version', '4.0');
comp.updatePropertyWithValue('uid', this.state.uid);
comp.updatePropertyWithValue('rev', ICAL.Time.now());
comp.updatePropertyWithValue("prodid", "-//iCal.js EteSync Web");
comp.updatePropertyWithValue("version", "4.0");
comp.updatePropertyWithValue("uid", this.state.uid);
comp.updatePropertyWithValue("rev", ICAL.Time.now());
const lastName = this.state.lastName.trim();
const firstName = this.state.firstName.trim();
@ -310,13 +310,13 @@ class ContactEdit extends React.PureComponent<PropsType> {
let fn = `${namePrefix} ${firstName} ${middleName} ${lastName}`.trim();
if (fn === '') {
if (fn === "") {
fn = nameSuffix;
} else if (nameSuffix !== '') {
} else if (nameSuffix !== "") {
fn = `${fn}, ${nameSuffix}`;
}
comp.updatePropertyWithValue('fn', fn);
comp.updatePropertyWithValue("fn", fn);
const name = [lastName,
firstName,
@ -325,39 +325,39 @@ class ContactEdit extends React.PureComponent<PropsType> {
nameSuffix,
];
comp.updatePropertyWithValue('n', name);
comp.updatePropertyWithValue("n", name);
function setProperties(name: string, source: ValueType[]) {
comp.removeAllProperties(name);
source.forEach((x) => {
if (x.value === '') {
if (x.value === "") {
return;
}
const prop = new ICAL.Property(name, comp);
prop.setParameter('type', x.type);
prop.setParameter("type", x.type);
prop.setValue(x.value);
comp.addProperty(prop);
});
}
setProperties('tel', this.state.phone);
setProperties('email', this.state.email);
setProperties('adr', this.state.address);
setProperties('impp', this.state.impp.map((x) => (
{ type: x.type, value: x.type + ':' + x.value }
setProperties("tel", this.state.phone);
setProperties("email", this.state.email);
setProperties("adr", this.state.address);
setProperties("impp", this.state.impp.map((x) => (
{ type: x.type, value: x.type + ":" + x.value }
)));
function setProperty(name: string, value: string) {
comp.removeAllProperties(name);
if (value !== '') {
if (value !== "") {
comp.updatePropertyWithValue(name, value);
}
}
setProperty('org', this.state.org);
setProperty('title', this.state.title);
setProperty('note', this.state.note);
setProperty("org", this.state.org);
setProperty("title", this.state.title);
setProperty("note", this.state.note);
this.props.onSave(contact, this.state.journalUid, this.props.item)
@ -377,20 +377,20 @@ class ContactEdit extends React.PureComponent<PropsType> {
form: {
},
fullWidth: {
width: '100%',
boxSizing: 'border-box' as any,
width: "100%",
boxSizing: "border-box" as any,
},
submit: {
marginTop: 40,
marginBottom: 20,
textAlign: 'right' as any,
textAlign: "right" as any,
},
};
return (
<React.Fragment>
<h2>
{this.props.item ? 'Edit Contact' : 'New Contact'}
{this.props.item ? "Edit Contact" : "New Contact"}
</h2>
<form style={styles.form} onSubmit={this.onSubmit}>
<FormControl disabled={this.props.item !== undefined} style={styles.fullWidth}>
@ -411,7 +411,7 @@ class ContactEdit extends React.PureComponent<PropsType> {
<TextField
name="namePrefix"
placeholder="Prefix"
style={{ marginTop: '2rem', ...styles.fullWidth }}
style={{ marginTop: "2rem", ...styles.fullWidth }}
value={this.state.namePrefix}
onChange={this.handleInputChange}
/>
@ -419,7 +419,7 @@ class ContactEdit extends React.PureComponent<PropsType> {
<TextField
name="firstName"
placeholder="First name"
style={{ marginTop: '2rem', ...styles.fullWidth }}
style={{ marginTop: "2rem", ...styles.fullWidth }}
value={this.state.firstName}
onChange={this.handleInputChange}
/>
@ -427,7 +427,7 @@ class ContactEdit extends React.PureComponent<PropsType> {
<TextField
name="middleName"
placeholder="Middle name"
style={{ marginTop: '2rem', ...styles.fullWidth }}
style={{ marginTop: "2rem", ...styles.fullWidth }}
value={this.state.middleName}
onChange={this.handleInputChange}
/>
@ -435,7 +435,7 @@ class ContactEdit extends React.PureComponent<PropsType> {
<TextField
name="lastName"
placeholder="Last name"
style={{ marginTop: '2rem', ...styles.fullWidth }}
style={{ marginTop: "2rem", ...styles.fullWidth }}
value={this.state.lastName}
onChange={this.handleInputChange}
/>
@ -443,7 +443,7 @@ class ContactEdit extends React.PureComponent<PropsType> {
<TextField
name="nameSuffix"
placeholder="Suffix"
style={{ marginTop: '2rem', ...styles.fullWidth }}
style={{ marginTop: "2rem", ...styles.fullWidth }}
value={this.state.nameSuffix}
onChange={this.handleInputChange}
/>
@ -451,7 +451,7 @@ class ContactEdit extends React.PureComponent<PropsType> {
<div>
Phone numbers
<IconButton
onClick={() => this.addValueType('phone')}
onClick={() => this.addValueType("phone")}
title="Add phone number"
>
<IconAdd />
@ -474,7 +474,7 @@ class ContactEdit extends React.PureComponent<PropsType> {
<div>
Emails
<IconButton
onClick={() => this.addValueType('email')}
onClick={() => this.addValueType("email")}
title="Add email address"
>
<IconAdd />
@ -497,7 +497,7 @@ class ContactEdit extends React.PureComponent<PropsType> {
<div>
IMPP
<IconButton
onClick={() => this.addValueType('impp', 'jabber')}
onClick={() => this.addValueType("impp", "jabber")}
title="Add impp address"
>
<IconAdd />
@ -520,7 +520,7 @@ class ContactEdit extends React.PureComponent<PropsType> {
<div>
Addresses
<IconButton
onClick={() => this.addValueType('address')}
onClick={() => this.addValueType("address")}
title="Add address"
>
<IconAdd />
@ -578,7 +578,7 @@ class ContactEdit extends React.PureComponent<PropsType> {
{this.props.item &&
<Button
variant="contained"
style={{ marginLeft: 15, backgroundColor: colors.red[500], color: 'white' }}
style={{ marginLeft: 15, backgroundColor: colors.red[500], color: "white" }}
onClick={this.onDeleteRequest}
>
<IconDelete style={{ marginRight: 8 }} />

@ -1,9 +1,9 @@
// 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 * as React from "react";
import Button from "@material-ui/core/Button";
import TextField from "@material-ui/core/TextField";
interface FormErrors {
errorEncryptionPassword?: string;
@ -25,7 +25,7 @@ class EncryptionLoginForm extends React.PureComponent {
super(props);
this.state = {
errors: {},
encryptionPassword: '',
encryptionPassword: "",
};
this.generateEncryption = this.generateEncryption.bind(this);
this.handleInputChange = this.handleInputChange.bind(this);
@ -45,7 +45,7 @@ class EncryptionLoginForm extends React.PureComponent {
const encryptionPassword = this.state.encryptionPassword;
const errors: FormErrors = {};
const fieldRequired = 'This field is required!';
const fieldRequired = "This field is required!";
if (!encryptionPassword) {
errors.errorEncryptionPassword = fieldRequired;
}
@ -66,7 +66,7 @@ class EncryptionLoginForm extends React.PureComponent {
},
submit: {
marginTop: 40,
textAlign: 'right' as any,
textAlign: "right" as any,
},
};
@ -92,7 +92,7 @@ class EncryptionLoginForm extends React.PureComponent {
color="secondary"
disabled={this.props.loading}
>
{this.props.loading ? 'Loading…' : 'Continue'}
{this.props.loading ? "Loading…" : "Continue"}
</Button>
</div>
</form>

@ -1,13 +1,13 @@
// SPDX-FileCopyrightText: © 2017 EteSync Authors
// SPDX-License-Identifier: AGPL-3.0-only
import * as React from 'react';
import * as React from "react";
import { store, persistor } from '../store';
import { resetKey } from '../store/actions';
import { store, persistor } from "../store";
import { resetKey } from "../store/actions";
import { EncryptionPasswordError, IntegrityError } from 'etesync';
import PrettyError from '../widgets/PrettyError';
import { EncryptionPasswordError, IntegrityError } from "etesync";
import PrettyError from "../widgets/PrettyError";
interface PropsType {
children: React.ReactNode | React.ReactNode[];

@ -1,13 +1,13 @@
// SPDX-FileCopyrightText: © 2017 EteSync Authors
// SPDX-License-Identifier: AGPL-3.0-only
import * as React from 'react';
import * as React from "react";
import PimItemHeader from './PimItemHeader';
import PimItemHeader from "./PimItemHeader";
import { formatDateRange, formatOurTimezoneOffset } from '../helpers';
import { formatDateRange, formatOurTimezoneOffset } from "../helpers";
import { EventType } from '../pim-types';
import { EventType } from "../pim-types";
class Event extends React.PureComponent {
public props: {
@ -16,7 +16,7 @@ class Event extends React.PureComponent {
public render() {
if (this.props.item === undefined) {
throw Error('Event should be defined!');
throw Error("Event should be defined!");
}
const style = {
@ -35,9 +35,9 @@ class Event extends React.PureComponent {
<div><u>{this.props.item.location}</u></div>
</PimItemHeader>
<div style={style.content}>
<p style={{ wordWrap: 'break-word' }}>{this.props.item.description}</p>
<p style={{ wordWrap: "break-word" }}>{this.props.item.description}</p>
{(this.props.item.attendees.length > 0) && (
<div>Attendees: {this.props.item.attendees.map((x) => (x.getFirstValue())).join(', ')}</div>)}
<div>Attendees: {this.props.item.attendees.map((x) => (x.getFirstValue())).join(", ")}</div>)}
</div>
</React.Fragment>
);

@ -1,45 +1,45 @@
// SPDX-FileCopyrightText: © 2017 EteSync Authors
// SPDX-License-Identifier: AGPL-3.0-only
import * as React from 'react';
import * as React from "react";
import FormGroup from '@material-ui/core/FormGroup';
import FormControlLabel from '@material-ui/core/FormControlLabel';
import Switch from '@material-ui/core/Switch';
import FormGroup from "@material-ui/core/FormGroup";
import FormControlLabel from "@material-ui/core/FormControlLabel";
import Switch from "@material-ui/core/Switch";
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 FormHelperText from '@material-ui/core/FormHelperText';
import InputLabel from '@material-ui/core/InputLabel';
import * as colors from '@material-ui/core/colors';
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 FormHelperText from "@material-ui/core/FormHelperText";
import InputLabel from "@material-ui/core/InputLabel";
import * as colors from "@material-ui/core/colors";
import IconDelete from '@material-ui/icons/Delete';
import IconCancel from '@material-ui/icons/Clear';
import IconSave from '@material-ui/icons/Save';
import IconDelete from "@material-ui/icons/Delete";
import IconCancel from "@material-ui/icons/Clear";
import IconSave from "@material-ui/icons/Save";
import DateTimePicker from '../widgets/DateTimePicker';
import DateTimePicker from "../widgets/DateTimePicker";
import ConfirmationDialog from '../widgets/ConfirmationDialog';
import TimezonePicker from '../widgets/TimezonePicker';
import Toast from '../widgets/Toast';
import ConfirmationDialog from "../widgets/ConfirmationDialog";
import TimezonePicker from "../widgets/TimezonePicker";
import Toast from "../widgets/Toast";
import { Location } from 'history';
import { withRouter } from 'react-router';
import { Location } from "history";
import { withRouter } from "react-router";
import * as uuid from 'uuid';
import * as ICAL from 'ical.js';
import * as uuid from "uuid";
import * as ICAL from "ical.js";
import * as EteSync from 'etesync';
import * as EteSync from "etesync";
import { getCurrentTimezone } from '../helpers';
import { getCurrentTimezone } from "../helpers";
import { EventType, timezoneLoadFromName } from '../pim-types';
import RRule, { RRuleOptions } from '../widgets/RRule';
import { EventType, timezoneLoadFromName } from "../pim-types";
import RRule, { RRuleOptions } from "../widgets/RRule";
import { History } from 'history';
import { History } from "history";
interface PropsType {
collections: EteSync.CollectionInfo[];
@ -73,14 +73,14 @@ class EventEdit extends React.PureComponent<PropsType> {
constructor(props: any) {
super(props);
this.state = {
uid: '',
title: '',
uid: "",
title: "",
allDay: false,
location: '',
description: '',
location: "",
description: "",
timezone: null,
journalUid: '',
journalUid: "",
showDeleteDialog: false,
};
@ -107,18 +107,18 @@ class EventEdit extends React.PureComponent<PropsType> {
}
if (this.props.duplicate) {
this.state.title = event.title ? `Copy of ${event.title}` : '';
this.state.title = event.title ? `Copy of ${event.title}` : "";
} else {
this.state.uid = event.uid;
this.state.title = event.title ? event.title : '';
this.state.title = event.title ? event.title : "";
}
this.state.allDay = allDay;
this.state.start = event.startDate.convertToZone(ICAL.Timezone.localTimezone).toJSDate();
this.state.end = endDate.convertToZone(ICAL.Timezone.localTimezone).toJSDate();
this.state.location = event.location ? event.location : '';
this.state.description = event.description ? event.description : '';
this.state.location = event.location ? event.location : "";
this.state.description = event.description ? event.description : "";
this.state.timezone = event.timezone;
const rruleProp = this.props.item?.component.getFirstPropertyValue<ICAL.Recur>('rrule');
const rruleProp = this.props.item?.component.getFirstPropertyValue<ICAL.Recur>("rrule");
if (rruleProp) {
this.state.rrule = rruleProp.toJSON() as any;
if (this.state.rrule && rruleProp.until) {
@ -165,7 +165,7 @@ class EventEdit extends React.PureComponent<PropsType> {
this.setState({ allDay: !this.state.allDay });
}
public toggleRecurring() {
const value = this.state.rrule ? undefined : { freq: 'WEEKLY', interval: 1 };
const value = this.state.rrule ? undefined : { freq: "WEEKLY", interval: 1 };
this.setState({ rrule: value });
}
@ -175,18 +175,18 @@ class EventEdit extends React.PureComponent<PropsType> {
}
public handleCloseToast(_event?: React.SyntheticEvent, reason?: string) {
if (reason === 'clickaway') {
if (reason === "clickaway") {
return;
}
this.setState({ error: '' });
this.setState({ error: "" });
}
public onSubmit(e: React.FormEvent<any>) {
e.preventDefault();
if ((!this.state.start) || (!this.state.end)) {
this.setState({ error: 'Both start and end time must be set!' });
this.setState({ error: "Both start and end time must be set!" });
return;
}
@ -209,7 +209,7 @@ class EventEdit extends React.PureComponent<PropsType> {
}
if (startDate.compare(endDate) >= 0) {
this.setState({ error: 'End time must be later than start time!' });
this.setState({ error: "End time must be later than start time!" });
return;
}
@ -233,10 +233,10 @@ class EventEdit extends React.PureComponent<PropsType> {
}
}
if (this.state.rrule) {
event.component.updatePropertyWithValue('rrule', new ICAL.Recur(this.state.rrule!));
event.component.updatePropertyWithValue("rrule", new ICAL.Recur(this.state.rrule!));
}
event.component.updatePropertyWithValue('last-modified', ICAL.Time.now());
event.component.updatePropertyWithValue("last-modified", ICAL.Time.now());
this.props.onSave(event, this.state.journalUid, this.props.item)
.then(() => {
@ -255,14 +255,14 @@ class EventEdit extends React.PureComponent<PropsType> {
form: {
},
fullWidth: {
width: '100%',
boxSizing: 'border-box' as any,
width: "100%",
boxSizing: "border-box" as any,
marginTop: 16,
},
submit: {
marginTop: 40,
marginBottom: 20,
textAlign: 'right' as any,
textAlign: "right" as any,
},
};
@ -272,11 +272,11 @@ class EventEdit extends React.PureComponent<PropsType> {
return (
<>
<h2>
{(this.props.item && !this.props.duplicate) ? 'Edit Event' : 'New Event'}
{(this.props.item && !this.props.duplicate) ? "Edit Event" : "New Event"}
</h2>
{recurring && (
<div>
<span style={{ color: 'red' }}>IMPORTANT: </span>
<span style={{ color: "red" }}>IMPORTANT: </span>
This is a recurring event, for now, only editing the whole series
(by editing the first instance) is supported.
</div>
@ -385,7 +385,7 @@ class EventEdit extends React.PureComponent<PropsType> {
{this.state.rrule &&
<RRule
onChange={this.handleRRuleChange}
rrule={this.state.rrule ? this.state.rrule : { freq: 'DAILY', interval: 1 }}
rrule={this.state.rrule ? this.state.rrule : { freq: "DAILY", interval: 1 }}
/>
}
<div style={styles.submit}>
@ -400,7 +400,7 @@ class EventEdit extends React.PureComponent<PropsType> {
{this.props.item &&
<Button
variant="contained"
style={{ marginLeft: 15, backgroundColor: colors.red[500], color: 'white' }}
style={{ marginLeft: 15, backgroundColor: colors.red[500], color: "white" }}
onClick={this.onDeleteRequest}
>
<IconDelete style={{ marginRight: 8 }} />

@ -1,31 +1,31 @@
// SPDX-FileCopyrightText: © 2017 EteSync Authors
// SPDX-License-Identifier: AGPL-3.0-only
import * as Immutable from 'immutable';
import * as Immutable from "immutable";
import { AutoSizer, List as VirtualizedList } from 'react-virtualized';
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 * 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 { 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';
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;
@ -48,8 +48,8 @@ function RollbackToHereDialog(props: RollbackToHereDialogPropsType) {
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');
const itemComp = comp.getFirstSubcomponent("vevent") ?? comp.getFirstSubcomponent("vtodo") ?? comp;
const itemUid = itemComp.getFirstPropertyValue("uid");
if (itemUid && !changes.has(itemUid)) {
changes.set(itemUid, entry);
@ -94,7 +94,7 @@ function RollbackToHereDialog(props: RollbackToHereDialogPropsType) {
</DialogTitle>
<DialogContent>
{loading ? (
<LoadingIndicator style={{ display: 'block', margin: 'auto' }} />
<LoadingIndicator style={{ display: "block", margin: "auto" }} />
) : (
<p>
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.
@ -154,7 +154,7 @@ class JournalEntries extends React.PureComponent {
try {
comp = parseString(syncEntry.content);
} catch (e) {
const icon = (<IconError style={{ color: 'red' }} />);
const icon = (<IconError style={{ color: "red" }} />);
return (
<ListItem
key={key}
@ -173,16 +173,16 @@ class JournalEntries extends React.PureComponent {
let icon;
if (syncEntry.action === EteSync.SyncEntryAction.Add) {
icon = (<IconAdd style={{ color: '#16B14B' }} />);
icon = (<IconAdd style={{ color: "#16B14B" }} />);
} else if (syncEntry.action === EteSync.SyncEntryAction.Change) {
icon = (<IconEdit style={{ color: '#FEB115' }} />);
icon = (<IconEdit style={{ color: "#FEB115" }} />);
} else if (syncEntry.action === EteSync.SyncEntryAction.Delete) {
icon = (<IconDelete style={{ color: '#F20C0C' }} />);
icon = (<IconDelete style={{ color: "#F20C0C" }} />);
}
let name;
let uid;
if (comp.name === 'vcalendar') {
if (comp.name === "vcalendar") {
if (EventType.isEvent(comp)) {
const vevent = EventType.fromVCalendar(comp);
name = vevent.summary;
@ -192,13 +192,13 @@ class JournalEntries extends React.PureComponent {
name = vtodo.summary;
uid = vtodo.uid;
}
} else if (comp.name === 'vcard') {
} else if (comp.name === "vcard") {
const vcard = new ContactType(comp);
name = vcard.fn;
uid = vcard.uid;
} else {
name = 'Error processing entry';
uid = '';
name = "Error processing entry";
uid = "";
}
if (this.props.uid && (this.props.uid !== uid)) {
@ -265,7 +265,7 @@ class JournalEntries extends React.PureComponent {
</Button>
</DialogActions>
</Dialog>
<List style={{ height: 'calc(100vh - 300px)' }}>
<List style={{ height: "calc(100vh - 300px)" }}>
<AutoSizer>
{({ height, width }) => (
<VirtualizedList

@ -1,16 +1,16 @@
// 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 FormGroup from '@material-ui/core/FormGroup';
import FormControlLabel from '@material-ui/core/FormControlLabel';
import Switch from '@material-ui/core/Switch';
import * as React from "react";
import Button from "@material-ui/core/Button";
import TextField from "@material-ui/core/TextField";
import FormGroup from "@material-ui/core/FormGroup";
import FormControlLabel from "@material-ui/core/FormControlLabel";
import Switch from "@material-ui/core/Switch";
import ExternalLink from '../widgets/ExternalLink';
import ExternalLink from "../widgets/ExternalLink";
import * as C from '../constants';
import * as C from "../constants";
interface FormErrors {
errorEmail?: string;
@ -40,9 +40,9 @@ class LoginForm extends React.PureComponent {
this.state = {
showAdvanced: false,
errors: {},
server: '',
username: '',
password: '',
server: "",
username: "",
password: "",
};
this.generateEncryption = this.generateEncryption.bind(this);
this.toggleAdvancedSettings = this.toggleAdvancedSettings.bind(this);
@ -65,7 +65,7 @@ class LoginForm extends React.PureComponent {
const password = this.state.password;
const errors: FormErrors = {};
const fieldRequired = 'This field is required!';
const fieldRequired = "This field is required!";
if (!username) {
errors.errorEmail = fieldRequired;
}
@ -73,9 +73,9 @@ class LoginForm extends React.PureComponent {
errors.errorPassword = fieldRequired;
}
if (process.env.NODE_ENV !== 'development') {
if (this.state.showAdvanced && !this.state.server.startsWith('https://')) {
errors.errorServer = 'Server URI must start with https://';
if (process.env.NODE_ENV !== "development") {
if (this.state.showAdvanced && !this.state.server.startsWith("https://")) {
errors.errorServer = "Server URI must start with https://";
}
}
@ -105,7 +105,7 @@ class LoginForm extends React.PureComponent {
},
submit: {
marginTop: 40,
textAlign: 'right' as any,
textAlign: "right" as any,
},
};
@ -133,7 +133,7 @@ class LoginForm extends React.PureComponent {
{(this.props.error) && (<div>Error! {this.props.error.message}</div>)}
<form style={styles.form} onSubmit={this.generateEncryption}>
<TextField
type={this.state.showAdvanced ? 'text' : 'email'}
type={this.state.showAdvanced ? "text" : "email"}
style={styles.textField}
error={!!this.state.errors.errorEmail}
helperText={this.state.errors.errorEmail}
@ -177,7 +177,7 @@ class LoginForm extends React.PureComponent {
color="secondary"
disabled={this.props.loading}
>
{this.props.loading ? 'Loading…' : 'Log In'}
{this.props.loading ? "Loading…" : "Log In"}
</Button>
</div>
</form>

@ -1,10 +1,10 @@
// SPDX-FileCopyrightText: © 2017 EteSync Authors
// SPDX-License-Identifier: AGPL-3.0-only
import * as React from 'react';
import Color from 'color';
import * as React from "react";
import Color from "color";
import { Theme, withTheme } from '@material-ui/core/styles';
import { Theme, withTheme } from "@material-ui/core/styles";
export default withTheme((props: {text: string, backgroundColor?: string, children?: any, rightItem?: React.ReactNode, theme: Theme}) => {
const backgroundColor = props.backgroundColor ?? props.theme.palette.secondary.main;
@ -14,8 +14,8 @@ export default withTheme((props: {text: string, backgroundColor?: string, childr
backgroundColor,
color: foregroundColor,
padding: 15,
display: 'flex',
justifyContent: 'space-between',
display: "flex",
justifyContent: "space-between",
},
headerText: {
marginTop: 10,

@ -1,17 +1,17 @@
// SPDX-FileCopyrightText: © 2017 EteSync Authors
// SPDX-License-Identifier: AGPL-3.0-only
import * as React from 'react';
import * as React from "react";
import TextField from '@material-ui/core/TextField';
import TextField from "@material-ui/core/TextField";
import IconButton from '@material-ui/core/IconButton';
import IconSearch from '@material-ui/icons/Search';
import IconClear from '@material-ui/icons/Clear';
import IconButton from "@material-ui/core/IconButton";
import IconSearch from "@material-ui/icons/Search";
import IconClear from "@material-ui/icons/Clear";
import { ContactType } from '../pim-types';
import { ContactType } from "../pim-types";
import AddressBook from '../components/AddressBook';
import AddressBook from "../components/AddressBook";
class SearchableAddressBook extends React.PureComponent {
public props: {
@ -25,7 +25,7 @@ class SearchableAddressBook extends React.PureComponent {
constructor(props: any) {
super(props);
this.state = { searchQuery: '' };
this.state = { searchQuery: "" };
this.handleInputChange = this.handleInputChange.bind(this);
}
@ -43,19 +43,19 @@ class SearchableAddressBook extends React.PureComponent {
...rest
} = this.props;
const reg = new RegExp(this.state.searchQuery, 'i');
const reg = new RegExp(this.state.searchQuery, "i");
return (
<React.Fragment>
<TextField
name="searchQuery"
value={this.state.searchQuery}
style={{ fontSize: '120%', marginLeft: 20 }}
style={{ fontSize: "120%", marginLeft: 20 }}
placeholder="Find Contacts"
onChange={this.handleInputChange}
/>
{this.state.searchQuery &&
<IconButton onClick={() => this.setState({ searchQuery: '' })}>
<IconButton onClick={() => this.setState({ searchQuery: "" })}>
<IconClear />
</IconButton>
}

@ -1,17 +1,17 @@
// SPDX-FileCopyrightText: © 2017 EteSync Authors
// SPDX-License-Identifier: AGPL-3.0-only
import * as React from 'react';
import * as React from "react";
import * as EteSync from 'etesync';
import * as EteSync from "etesync";
import ICAL from 'ical.js';
import ICAL from "ical.js";
import uuid from 'uuid';
import uuid from "uuid";
import TextField from '@material-ui/core/TextField';
import TextField from "@material-ui/core/TextField";
import { TaskType, PimType, TaskStatusType } from '../../pim-types';
import { TaskType, PimType, TaskStatusType } from "../../pim-types";
interface PropsType {
style: React.CSSProperties;
@ -20,7 +20,7 @@ interface PropsType {
}
function QuickAdd(props: PropsType) {
const [title, setTitle] = React.useState('');
const [title, setTitle] = React.useState("");
const { style, onSubmit: save, defaultCollection } = props;
@ -39,7 +39,7 @@ function QuickAdd(props: PropsType) {
save(task, defaultCollection.uid, undefined);
setTitle('');
setTitle("");
}

@ -1,16 +1,16 @@
import * as React from 'react';
import * as React from "react";
import { useSelector, useDispatch } from 'react-redux';
import { useSelector, useDispatch } from "react-redux";
import InboxIcon from '@material-ui/icons/Inbox';
import LabelIcon from '@material-ui/icons/LabelOutlined';
import TodayIcon from '@material-ui/icons/Today';
import InboxIcon from "@material-ui/icons/Inbox";
import LabelIcon from "@material-ui/icons/LabelOutlined";
import TodayIcon from "@material-ui/icons/Today";
import { setSettings } from '../../store/actions';
import { StoreState } from '../../store';
import { setSettings } from "../../store/actions";
import { StoreState } from "../../store";
import { List, ListItem, ListSubheader } from '../../widgets/List';
import { TaskType } from '../../pim-types';
import { List, ListItem, ListSubheader } from "../../widgets/List";
import { TaskType } from "../../pim-types";
interface ListItemPropsType {
name: string | null;
@ -34,7 +34,7 @@ function SidebarListItem(props: ListItemPropsType) {
onClick={handleClick}
selected={name === filterBy}
leftIcon={icon}
rightIcon={<span style={{ width: '100%', textAlign: 'right' }}>{(amount > 0) && amount}</span>}
rightIcon={<span style={{ width: "100%", textAlign: "right" }}>{(amount > 0) && amount}</span>}
primaryText={primaryText}
/>
);

@ -1,13 +1,13 @@
// SPDX-FileCopyrightText: © 2017 EteSync Authors
// SPDX-License-Identifier: AGPL-3.0-only
import * as React from 'react';
import * as React from "react";
import PimItemHeader from '../PimItemHeader';
import PimItemHeader from "../PimItemHeader";
import { formatDate, formatOurTimezoneOffset } from '../../helpers';
import { formatDate, formatOurTimezoneOffset } from "../../helpers";
import { TaskType } from '../../pim-types';
import { TaskType } from "../../pim-types";
class Task extends React.PureComponent {
public props: {
@ -16,7 +16,7 @@ class Task extends React.PureComponent {
public render() {
if (this.props.item === undefined) {
throw Error('Task should be defined!');
throw Error("Task should be defined!");
}
const { item } = this.props;
@ -42,9 +42,9 @@ class Task extends React.PureComponent {
<div><u>{this.props.item.location}</u></div>
</PimItemHeader>
<div style={style.content}>
<p style={{ wordWrap: 'break-word' }}>{this.props.item.description}</p>
<p style={{ wordWrap: "break-word" }}>{this.props.item.description}</p>
{(this.props.item.attendees.length > 0) && (
<div>Attendees: {this.props.item.attendees.map((x) => (x.getFirstValue())).join(', ')}</div>)}
<div>Attendees: {this.props.item.attendees.map((x) => (x.getFirstValue())).join(", ")}</div>)}
</div>
</React.Fragment>
);

@ -1,51 +1,51 @@
// SPDX-FileCopyrightText: © 2017 EteSync Authors
// SPDX-License-Identifier: AGPL-3.0-only
import * as React from 'react';
import * as React from "react";
import FormGroup from '@material-ui/core/FormGroup';
import FormControlLabel from '@material-ui/core/FormControlLabel';
import Switch from '@material-ui/core/Switch';
import FormGroup from "@material-ui/core/FormGroup";
import FormControlLabel from "@material-ui/core/FormControlLabel";
import Switch from "@material-ui/core/Switch";
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 FormHelperText from '@material-ui/core/FormHelperText';
import InputLabel from '@material-ui/core/InputLabel';
import * as colors from '@material-ui/core/colors';
import FormLabel from '@material-ui/core/FormLabel';
import RadioGroup from '@material-ui/core/RadioGroup';
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 FormHelperText from "@material-ui/core/FormHelperText";
import InputLabel from "@material-ui/core/InputLabel";
import * as colors from "@material-ui/core/colors";
import FormLabel from "@material-ui/core/FormLabel";
import RadioGroup from "@material-ui/core/RadioGroup";
import Autocomplete from '@material-ui/lab/Autocomplete';
import Autocomplete from "@material-ui/lab/Autocomplete";
import IconDelete from '@material-ui/icons/Delete';
import IconCancel from '@material-ui/icons/Clear';
import IconSave from '@material-ui/icons/Save';
import IconDelete from "@material-ui/icons/Delete";
import IconCancel from "@material-ui/icons/Clear";
import IconSave from "@material-ui/icons/Save";
import DateTimePicker from '../../widgets/DateTimePicker';
import DateTimePicker from "../../widgets/DateTimePicker";
import ConfirmationDialog from '../../widgets/ConfirmationDialog';
import TimezonePicker from '../../widgets/TimezonePicker';
import Toast from '../../widgets/Toast';
import ConfirmationDialog from "../../widgets/ConfirmationDialog";
import TimezonePicker from "../../widgets/TimezonePicker";
import Toast from "../../widgets/Toast";
import { Location } from 'history';
import { withRouter } from 'react-router';
import { Location } from "history";
import { withRouter } from "react-router";
import * as uuid from 'uuid';
import * as ICAL from 'ical.js';
import * as uuid from "uuid";
import * as ICAL from "ical.js";
import * as EteSync from 'etesync';
import * as EteSync from "etesync";
import { getCurrentTimezone, mapPriority } from '../../helpers';
import { getCurrentTimezone, mapPriority } from "../../helpers";
import { TaskType, TaskStatusType, timezoneLoadFromName, TaskPriorityType, TaskTags } from '../../pim-types';
import { TaskType, TaskStatusType, timezoneLoadFromName, TaskPriorityType, TaskTags } from "../../pim-types";
import { History } from 'history';
import { History } from "history";
import ColoredRadio from '../../widgets/ColoredRadio';
import RRule, { RRuleOptions } from '../../widgets/RRule';
import ColoredRadio from "../../widgets/ColoredRadio";
import RRule, { RRuleOptions } from "../../widgets/RRule";
interface PropsType {
collections: EteSync.CollectionInfo[];
@ -81,17 +81,17 @@ class TaskEdit extends React.PureComponent<PropsType> {
constructor(props: any) {
super(props);
this.state = {
uid: '',
title: '',
uid: "",
title: "",
status: TaskStatusType.NeedsAction,
priority: TaskPriorityType.Undefined,
includeTime: false,
location: '',
description: '',
location: "",
description: "",
tags: [],
timezone: null,
journalUid: '',
journalUid: "",
showDeleteDialog: false,
};
@ -99,7 +99,7 @@ class TaskEdit extends React.PureComponent<PropsType> {
const task = this.props.item;
this.state.uid = task.uid;
this.state.title = task.title ? task.title : '';
this.state.title = task.title ? task.title : "";
this.state.status = task.status ?? TaskStatusType.NeedsAction;
this.state.priority = task.priority ?? TaskPriorityType.Undefined;
if (task.startDate) {
@ -116,8 +116,8 @@ class TaskEdit extends React.PureComponent<PropsType> {
this.state.rrule.until = rrule.until;
}
}
this.state.location = task.location ? task.location : '';
this.state.description = task.description ? task.description : '';
this.state.location = task.location ? task.location : "";
this.state.description = task.description ? task.description : "";
this.state.timezone = task.timezone;
this.state.tags = task.tags;
} else {
@ -160,15 +160,15 @@ class TaskEdit extends React.PureComponent<PropsType> {
}
public handleCloseToast(_event?: React.SyntheticEvent, reason?: string) {
if (reason === 'clickaway') {
if (reason === "clickaway") {
return;
}
this.setState({ error: '' });
this.setState({ error: "" });
}
public toggleRecurring() {
const value = this.state.rrule ? undefined : { freq: 'WEEKLY', interval: 1 };
const value = this.state.rrule ? undefined : { freq: "WEEKLY", interval: 1 };
this.setState({ rrule: value });
}
@ -180,7 +180,7 @@ class TaskEdit extends React.PureComponent<PropsType> {
e.preventDefault();
if (this.state.rrule && !(this.state.start || this.state.due)) {
this.setState({ error: 'A recurring task must have either Hide Until or Due Date set!' });
this.setState({ error: "A recurring task must have either Hide Until or Due Date set!" });
return;
}
@ -203,7 +203,7 @@ class TaskEdit extends React.PureComponent<PropsType> {
if (startDate && dueDate) {
if (startDate.compare(dueDate) >= 0) {
this.setState({ error: 'End time must be later than start time!' });
this.setState({ error: "End time must be later than start time!" });
return;
}
}
@ -243,7 +243,7 @@ class TaskEdit extends React.PureComponent<PropsType> {
}
}
task.component.updatePropertyWithValue('last-modified', ICAL.Time.now());
task.component.updatePropertyWithValue("last-modified", ICAL.Time.now());
this.props.onSave(task, this.state.journalUid, this.props.item)
.then(() => {
@ -258,7 +258,7 @@ class TaskEdit extends React.PureComponent<PropsType> {
this.props.history.goBack();
})
.catch(() => {
this.setState({ error: 'Could not save task' });
this.setState({ error: "Could not save task" });
});
}
@ -273,14 +273,14 @@ class TaskEdit extends React.PureComponent<PropsType> {
form: {
},
fullWidth: {
width: '100%',
boxSizing: 'border-box' as any,
width: "100%",
boxSizing: "border-box" as any,
marginTop: 16,
},
submit: {
marginTop: 40,
marginBottom: 20,
textAlign: 'right' as any,
textAlign: "right" as any,
},
};
@ -290,11 +290,11 @@ class TaskEdit extends React.PureComponent<PropsType> {
return (
<React.Fragment>
<h2>
{this.props.item ? 'Edit Task' : 'New Task'}
{this.props.item ? "Edit Task" : "New Task"}
</h2>
{recurring && (
<div>
<span style={{ color: 'red' }}>IMPORTANT: </span>
<span style={{ color: "red" }}>IMPORTANT: </span>
This is a recurring task, for now, only editing the whole series
(by editing the first instance) is supported.
</div>
@ -349,7 +349,7 @@ class TaskEdit extends React.PureComponent<PropsType> {
<RadioGroup
row
value={mapPriority(this.state.priority)}
onChange={(e) => this.handleChange('priority', Number(e.target.value))}
onChange={(e) => this.handleChange("priority", Number(e.target.value))}
>
<ColoredRadio value={TaskPriorityType.Undefined} label="None" color={colors.grey[600]} />
<ColoredRadio value={TaskPriorityType.Low} label="Low" color={colors.blue[600]} />
@ -418,7 +418,7 @@ class TaskEdit extends React.PureComponent<PropsType> {
{this.state.rrule &&
<RRule
onChange={this.handleRRuleChange}
rrule={this.state.rrule ? this.state.rrule : { freq: 'DAILY', interval: 1 }}
rrule={this.state.rrule ? this.state.rrule : { freq: "DAILY", interval: 1 }}
/>
}
@ -445,7 +445,7 @@ class TaskEdit extends React.PureComponent<PropsType> {
multiple
options={TaskTags}
value={this.state.tags}
onChange={(_e, value) => this.handleChange('tags', value)}
onChange={(_e, value) => this.handleChange("tags", value)}
renderInput={(params) => (
<TextField
{...params}
@ -468,7 +468,7 @@ class TaskEdit extends React.PureComponent<PropsType> {
{this.props.item &&
<Button
variant="contained"
style={{ marginLeft: 15, backgroundColor: colors.red[500], color: 'white' }}
style={{ marginLeft: 15, backgroundColor: colors.red[500], color: "white" }}
onClick={this.onDeleteRequest}
>
<IconDelete style={{ marginRight: 8 }} />

@ -1,34 +1,34 @@
// SPDX-FileCopyrightText: © 2017 EteSync Authors
// SPDX-License-Identifier: AGPL-3.0-only
import * as React from 'react';
import * as React from "react";
import * as EteSync from 'etesync';
import * as EteSync from "etesync";
import { List } from '../../widgets/List';
import Toast, { PropsType as ToastProps } from '../../widgets/Toast';
import { List } from "../../widgets/List";
import Toast, { PropsType as ToastProps } from "../../widgets/Toast";
import { TaskType, PimType, TaskStatusType } from '../../pim-types';
import Divider from '@material-ui/core/Divider';
import Grid from '@material-ui/core/Grid';
import { useTheme, makeStyles } from '@material-ui/core/styles';
import { TaskType, PimType, TaskStatusType } from "../../pim-types";
import Divider from "@material-ui/core/Divider";
import Grid from "@material-ui/core/Grid";
import { useTheme, makeStyles } from "@material-ui/core/styles";
import { useSelector, useDispatch } from 'react-redux';
import { useSelector, useDispatch } from "react-redux";
import Fuse from 'fuse.js';
import Fuse from "fuse.js";
import TaskListItem from './TaskListItem';
import Sidebar from './Sidebar';
import Toolbar from './Toolbar';
import QuickAdd from './QuickAdd';
import TaskListItem from "./TaskListItem";
import Sidebar from "./Sidebar";
import Toolbar from "./Toolbar";
import QuickAdd from "./QuickAdd";
import { StoreState, UserInfoData } from '../../store';
import { formatDate } from '../../helpers';
import { SyncInfo } from '../../SyncGate';
import { fetchEntries } from '../../store/actions';
import { Action } from 'redux-actions';
import { addJournalEntries } from '../../etesync-helpers';
import { useCredentials } from '../../login';
import { StoreState, UserInfoData } from "../../store";
import { formatDate } from "../../helpers";
import { SyncInfo } from "../../SyncGate";
import { fetchEntries } from "../../store/actions";
import { Action } from "redux-actions";
import { addJournalEntries } from "../../etesync-helpers";
import { useCredentials } from "../../login";
function sortCompleted(a: TaskType, b: TaskType) {
return (!!a.finished === !!b.finished) ? 0 : (a.finished) ? 1 : -1;
@ -55,8 +55,8 @@ function sortPriority(aIn: TaskType, bIn: TaskType) {
}
function sortTitle(aIn: TaskType, bIn: TaskType) {
const a = aIn.title ?? '';
const b = bIn.title ?? '';
const a = aIn.title ?? "";
const b = bIn.title ?? "";
return a.localeCompare(b);
}
@ -64,22 +64,22 @@ function getSortFunction(sortOrder: string) {
const sortFunctions: (typeof sortTitle)[] = [sortCompleted];
switch (sortOrder) {
case 'smart':
case "smart":
sortFunctions.push(sortPriority);
sortFunctions.push(sortDueDate);
sortFunctions.push(sortTitle);
break;
case 'dueDate':
case "dueDate":
sortFunctions.push(sortDueDate);
break;
case 'priority':
case "priority":
sortFunctions.push(sortPriority);
sortFunctions.push(sortDueDate);
break;
case 'title':
case "title":
sortFunctions.push(sortTitle);
break;
case 'lastModifiedDate':
case "lastModifiedDate":
// Do nothing because it's the last sort function anyway
break;
}
@ -116,8 +116,8 @@ interface PropsType {
export default function TaskList(props: PropsType) {
const [showCompleted, setShowCompleted] = React.useState(false);
const [showHidden, setShowHidden] = React.useState(false);
const [searchTerm, setSearchTerm] = React.useState('');
const [toast, setToast] = React.useState<{ message: string, severity: ToastProps['severity'] }>({ message: '', severity: undefined });
const [searchTerm, setSearchTerm] = React.useState("");
const [toast, setToast] = React.useState<{ message: string, severity: ToastProps["severity"] }>({ message: "", severity: undefined });
const settings = useSelector((state: StoreState) => state.settings.taskSettings);
const { filterBy, sortBy } = settings;
const etesync = useCredentials()!;
@ -136,7 +136,7 @@ export default function TaskList(props: PropsType) {
const syncJournal = props.syncInfo.get((task as any).journalUid);
if (syncJournal === undefined) {
setToast({ message: 'Could not sync.', severity: 'error' });
setToast({ message: "Could not sync.", severity: "error" });
return;
}
@ -170,11 +170,11 @@ export default function TaskList(props: PropsType) {
})
.then(() => {
if (nextTask) {
setToast({ message: `${nextTask.title} rescheduled for ${formatDate(nextTask.startDate ?? nextTask.dueDate)}`, severity: 'success' });
setToast({ message: `${nextTask.title} rescheduled for ${formatDate(nextTask.startDate ?? nextTask.dueDate)}`, severity: "success" });
}
})
.catch(() => {
setToast({ message: 'Failed to save changes. This may be due to a network error.', severity: 'error' });
setToast({ message: "Failed to save changes. This may be due to a network error.", severity: "error" });
});
};
@ -187,8 +187,8 @@ export default function TaskList(props: PropsType) {
maxPatternLength: 32,
minMatchCharLength: 2,
keys: [
'title',
'desc',
"title",
"desc",
],
}).search(searchTerm);
return result.map((x) => x.item);
@ -201,11 +201,11 @@ export default function TaskList(props: PropsType) {
let entries;
const tagPrefix = 'tag:';
const tagPrefix = "tag:";
if (filterBy?.startsWith(tagPrefix)) {
const tag = filterBy.slice(tagPrefix.length);
entries = potentialEntries.filter((x) => x.tags.includes(tag));
} else if (filterBy === 'today') {
} else if (filterBy === "today") {
entries = potentialEntries.filter((x) => x.dueToday);
} else {
entries = potentialEntries;
@ -267,16 +267,16 @@ export default function TaskList(props: PropsType) {
<Grid item xs>
{props.collections?.[0] && <QuickAdd style={{ flexGrow: 1, marginRight: '0.75em' }} onSubmit={props.onItemSave} defaultCollection={props.collections?.[0]} />}
{props.collections?.[0] && <QuickAdd style={{ flexGrow: 1, marginRight: "0.75em" }} onSubmit={props.onItemSave} defaultCollection={props.collections?.[0]} />}
<Divider style={{ marginTop: '1em' }} />
<Divider style={{ marginTop: "1em" }} />
<List>
{itemList}
</List>
</Grid>
<Toast open={!!toast.message} severity={toast.severity} onClose={() => setToast({ message: '', severity: undefined })} autoHideDuration={3000}>
<Toast open={!!toast.message} severity={toast.severity} onClose={() => setToast({ message: "", severity: undefined })} autoHideDuration={3000}>
{toast.message}
</Toast>
</Grid>

@ -1,17 +1,17 @@
// SPDX-FileCopyrightText: © 2017 EteSync Authors
// SPDX-License-Identifier: AGPL-3.0-only
import * as React from 'react';
import * as React from "react";
import { TaskType, TaskPriorityType } from '../../pim-types';
import { ListItem } from '../../widgets/List';
import { TaskType, TaskPriorityType } from "../../pim-types";
import { ListItem } from "../../widgets/List";
import Checkbox from '@material-ui/core/Checkbox';
import CheckBoxOutlineBlankIcon from '@material-ui/icons/CheckBoxOutlineBlank';
import * as colors from '@material-ui/core/colors';
import Chip from '@material-ui/core/Chip';
import Checkbox from "@material-ui/core/Checkbox";
import CheckBoxOutlineBlankIcon from "@material-ui/icons/CheckBoxOutlineBlank";
import * as colors from "@material-ui/core/colors";
import Chip from "@material-ui/core/Chip";
import { mapPriority, formatDate } from '../../helpers';
import { mapPriority, formatDate } from "../../helpers";
const checkboxColor = {
[TaskPriorityType.Undefined]: colors.grey[600],
@ -27,7 +27,7 @@ const TagsList = React.memo((props: { tags: string[] }) => (
color="secondary"
size="small"
label={tag}
style={{ marginRight: '0.75em' }}
style={{ marginRight: "0.75em" }}
component="li"
/>)}
</ul>));
@ -48,15 +48,15 @@ export default React.memo(function TaskListItem(props: PropsType) {
} = props;
const title = task.title;
const dueDateText = task.dueDate ? `Due ${formatDate(task.dueDate)}` : '';
const freqText = task.rrule ? `(repeats ${task.rrule.freq.toLowerCase()})` : '';
const dueDateText = task.dueDate ? `Due ${formatDate(task.dueDate)}` : "";
const freqText = task.rrule ? `(repeats ${task.rrule.freq.toLowerCase()})` : "";
const secondaryText = `${dueDateText} ${freqText}`;
return (
<ListItem
primaryText={title}
secondaryText={secondaryText}
secondaryTextColor={task.overdue ? 'error' : 'textSecondary'}
secondaryTextColor={task.overdue ? "error" : "textSecondary"}
nestedItems={nestedItems}
onClick={() => onClick(task)}
leftIcon={

@ -1,36 +1,36 @@
import * as React from 'react';
import * as React from "react";
import * as EteSync from 'etesync';
import * as EteSync from "etesync";
import Switch from '@material-ui/core/Switch';
import IconButton from '@material-ui/core/IconButton';
import MoreVertIcon from '@material-ui/icons/MoreVert';
import MenuItem from '@material-ui/core/MenuItem';
import SortIcon from '@material-ui/icons/Sort';
import SearchIcon from '@material-ui/icons/Search';
import CloseIcon from '@material-ui/icons/Close';
import TextField from '@material-ui/core/TextField';
import { makeStyles } from '@material-ui/core/styles';
import { Transition } from 'react-transition-group';
import InputAdornment from '@material-ui/core/InputAdornment';
import Switch from "@material-ui/core/Switch";
import IconButton from "@material-ui/core/IconButton";
import MoreVertIcon from "@material-ui/icons/MoreVert";
import MenuItem from "@material-ui/core/MenuItem";
import SortIcon from "@material-ui/icons/Sort";
import SearchIcon from "@material-ui/icons/Search";
import CloseIcon from "@material-ui/icons/Close";
import TextField from "@material-ui/core/TextField";
import { makeStyles } from "@material-ui/core/styles";
import { Transition } from "react-transition-group";
import InputAdornment from "@material-ui/core/InputAdornment";
import { PimType } from '../../pim-types';
import { PimType } from "../../pim-types";
import { useSelector, useDispatch } from 'react-redux';
import { useSelector, useDispatch } from "react-redux";
import { setSettings } from '../../store/actions';
import { StoreState } from '../../store';
import { setSettings } from "../../store/actions";
import { StoreState } from "../../store";
import Menu from '../../widgets/Menu';
import { ListItemText, ListItemSecondaryAction } from '@material-ui/core';
import Menu from "../../widgets/Menu";
import { ListItemText, ListItemSecondaryAction } from "@material-ui/core";
const transitionTimeout = 300;
const transitionStyles = {
entering: { visibility: 'visible', width: '100%', overflow: 'hidden' },
entered: { visibility: 'visible', width: '100%' },
exiting: { visibility: 'visible', width: '0%', overflow: 'hidden' },
exited: { visibility: 'hidden', width: '0%' },
entering: { visibility: "visible", width: "100%", overflow: "hidden" },
entered: { visibility: "visible", width: "100%" },
exiting: { visibility: "visible", width: "0%", overflow: "hidden" },
exited: { visibility: "hidden", width: "0%" },
};
const useStyles = makeStyles((theme) => ({
@ -69,7 +69,7 @@ export default function Toolbar(props: PropsType) {
const toggleSearchField = () => {
if (showSearchField) {
setSearchTerm('');
setSearchTerm("");
}
setShowSearchField(!showSearchField);
};
@ -86,7 +86,7 @@ export default function Toolbar(props: PropsType) {
});
return (
<div style={{ display: 'flex', justifyContent: 'flex-end', alignItems: 'center' }}>
<div style={{ display: "flex", justifyContent: "flex-end", alignItems: "center" }}>
<Transition in={showSearchField} timeout={transitionTimeout}>
{(state) => (
<TextField
@ -110,7 +110,7 @@ export default function Toolbar(props: PropsType) {
</Transition>
<div className={classes.button}>
<IconButton size="small" onClick={toggleSearchField} title={showSearchField ? 'Close' : 'Search'}>
<IconButton size="small" onClick={toggleSearchField} title={showSearchField ? "Close" : "Search"}>
{showSearchField ? <CloseIcon /> : <SearchIcon />}
</IconButton>
</div>
@ -160,13 +160,13 @@ export default function Toolbar(props: PropsType) {
onClose={() => setOptionsAnchorEl(null)}
>
<MenuItem>
<ListItemText style={{ marginRight: '1.5em' }}>Show completed</ListItemText>
<ListItemText style={{ marginRight: "1.5em" }}>Show completed</ListItemText>
<ListItemSecondaryAction>
<Switch checked={showCompleted} onChange={(_e, checked) => setShowCompleted(checked)} edge="end" />
</ListItemSecondaryAction>
</MenuItem>
<MenuItem>
<ListItemText style={{ marginRight: '1.5em' }}>Show hidden</ListItemText>
<ListItemText style={{ marginRight: "1.5em" }}>Show hidden</ListItemText>
<ListItemSecondaryAction>
<Switch checked={showHidden} onChange={(_e, checked) => setShowHidden(checked)} edge="end" />
</ListItemSecondaryAction>

@ -1,12 +1,12 @@
// SPDX-FileCopyrightText: © 2017 EteSync Authors
// SPDX-License-Identifier: AGPL-3.0-only
export const appName = 'EteSync';
export const homePage = 'https://www.etesync.com/';
export const faq = homePage + 'faq/';
export const sourceCode = 'https://github.com/etesync/etesync-web';
export const reportIssue = sourceCode + '/issues';
export const appName = "EteSync";
export const homePage = "https://www.etesync.com/";
export const faq = homePage + "faq/";
export const sourceCode = "https://github.com/etesync/etesync-web";
export const reportIssue = sourceCode + "/issues";
export const forgotPassword = 'https://www.etesync.com/accounts/password/reset/';
export const forgotPassword = "https://www.etesync.com/accounts/password/reset/";
export const serviceApiBase = process.env.REACT_APP_DEFAULT_API_PATH || 'https://api.etesync.com/';
export const serviceApiBase = process.env.REACT_APP_DEFAULT_API_PATH || "https://api.etesync.com/";

@ -1,10 +1,10 @@
// SPDX-FileCopyrightText: © 2017 EteSync Authors
// SPDX-License-Identifier: AGPL-3.0-only
import * as EteSync from 'etesync';
import * as EteSync from "etesync";
import { CredentialsData, UserInfoData } from './store';
import { addEntries } from './store/actions';
import { CredentialsData, UserInfoData } from "./store";
import { addEntries } from "./store/actions";
export function createJournalEntry(
etesync: CredentialsData,

@ -1,10 +1,10 @@
// SPDX-FileCopyrightText: © 2017 EteSync Authors
// SPDX-License-Identifier: AGPL-3.0-only
import * as React from 'react';
import * as ICAL from 'ical.js';
import moment from 'moment';
import { TaskPriorityType } from './pim-types';
import * as React from "react";
import * as ICAL from "ical.js";
import moment from "moment";
import { TaskPriorityType } from "./pim-types";
// Generic handling of input changes
export function handleInputChange(self: React.Component, part?: string) {
@ -14,7 +14,7 @@ export function handleInputChange(self: React.Component, part?: string) {
let newState;
if (event.target.type === 'checkbox') {
if (event.target.type === "checkbox") {
newState = {
[name]: event.target.checked,
};
@ -54,8 +54,8 @@ export function insertSorted<T>(array: T[] = [], newItem: T, key: string) {
return array;
}
const allDayFormat = 'dddd, LL';
const fullFormat = 'LLLL';
const allDayFormat = "dddd, LL";
const fullFormat = "LLLL";
export function formatDate(date: ICAL.Time) {
const mDate = moment(date.toJSDate());
@ -74,15 +74,15 @@ export function formatDateRange(start: ICAL.Time, end: ICAL.Time) {
// All day
if (start.isDate) {
if (mEnd.diff(mStart, 'days', true) === 1) {
if (mEnd.diff(mStart, "days", true) === 1) {
return mStart.format(allDayFormat);
} else {
strStart = mStart.format(allDayFormat);
strEnd = mEnd.clone().subtract(1, 'day').format(allDayFormat);
strEnd = mEnd.clone().subtract(1, "day").format(allDayFormat);
}
} else if (mStart.isSame(mEnd, 'day')) {
} else if (mStart.isSame(mEnd, "day")) {
strStart = mStart.format(fullFormat);
strEnd = mEnd.format('LT');
strEnd = mEnd.format("LT");
if (mStart.isSame(mEnd)) {
return strStart;
@ -92,17 +92,17 @@ export function formatDateRange(start: ICAL.Time, end: ICAL.Time) {
strEnd = mEnd.format(fullFormat);
}
return strStart + ' - ' + strEnd;
return strStart + " - " + strEnd;
}
export function formatOurTimezoneOffset() {
let offset = new Date().getTimezoneOffset();
const prefix = (offset > 0) ? '-' : '+';
const prefix = (offset > 0) ? "-" : "+";
offset = Math.abs(offset);
const hours = Math.floor(offset / 60);
const minutes = offset % 60;
return `GMT${prefix}${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}`;
return `GMT${prefix}${hours.toString().padStart(2, "0")}:${minutes.toString().padStart(2, "0")}`;
}
export function getCurrentTimezone() {

@ -1,9 +1,9 @@
// SPDX-FileCopyrightText: © 2017 EteSync Authors
// SPDX-License-Identifier: AGPL-3.0-only
import * as React from 'react';
import * as React from "react";
import SvgIcon from '@material-ui/core/SvgIcon';
import SvgIcon from "@material-ui/core/SvgIcon";
export default function CopyIcon(props: any) {
return (

@ -1,15 +1,15 @@
// SPDX-FileCopyrightText: © 2017 EteSync Authors
// SPDX-License-Identifier: AGPL-3.0-only
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { PersistGate } from 'redux-persist/es/integration/react';
import App from './App';
import registerServiceWorker from './registerServiceWorker';
import './index.css';
import * as React from "react";
import * as ReactDOM from "react-dom";
import { Provider } from "react-redux";
import { PersistGate } from "redux-persist/es/integration/react";
import App from "./App";
import registerServiceWorker from "./registerServiceWorker";
import "./index.css";
import { store, persistor } from './store';
import { store, persistor } from "./store";
ReactDOM.render(
<Provider store={store}>
@ -17,6 +17,6 @@ ReactDOM.render(
<App />
</PersistGate>
</Provider>,
document.getElementById('root') as HTMLElement
document.getElementById("root") as HTMLElement
);
registerServiceWorker();

@ -1,21 +1,21 @@
// SPDX-FileCopyrightText: © 2017 EteSync Authors
// SPDX-License-Identifier: AGPL-3.0-only
import { colorHtmlToInt, colorIntToHtml } from './journal-processors';
import { colorHtmlToInt, colorIntToHtml } from "./journal-processors";
it('Color conversion', () => {
it("Color conversion", () => {
const testColors = [
'#aaaaaaaa',
'#00aaaaaa',
'#0000aaaa',
'#000000aa',
'#00000000',
'#bb00bbbb',
'#bb0000bb',
'#bb000000',
'#11110011',
'#11110000',
'#11111100',
"#aaaaaaaa",
"#00aaaaaa",
"#0000aaaa",
"#000000aa",
"#00000000",
"#bb00bbbb",
"#bb0000bb",
"#bb000000",
"#11110011",
"#11110000",
"#11111100",
];
for (const color of testColors) {

@ -1,13 +1,13 @@
// SPDX-FileCopyrightText: © 2017 EteSync Authors
// SPDX-License-Identifier: AGPL-3.0-only
import { List } from 'immutable';
import { List } from "immutable";
import { EventType, ContactType, TaskType } from './pim-types';
import { store } from './store';
import { appendError } from './store/actions';
import { EventType, ContactType, TaskType } from "./pim-types";
import { store } from "./store";
import { appendError } from "./store/actions";
import * as EteSync from 'etesync';
import * as EteSync from "etesync";
export function syncEntriesToItemMap(
collection: EteSync.CollectionInfo, entries: List<EteSync.SyncEntry>, base: {[key: string]: ContactType} = {}) {
@ -44,7 +44,7 @@ export function syncEntriesToItemMap(
return items;
}
export const defaultColor = '#8BC34A';
export const defaultColor = "#8BC34A";
export function colorIntToHtml(color?: number) {
if (color === undefined) {
@ -60,10 +60,10 @@ export function colorIntToHtml(color?: number) {
function toHex(num: number) {
const ret = num.toString(16);
return (ret.length === 1) ? '0' + ret : ret;
return (ret.length === 1) ? "0" + ret : ret;
}
return '#' + toHex(red) + toHex(green) + toHex(blue) + toHex(alpha);
return "#" + toHex(red) + toHex(green) + toHex(blue) + toHex(alpha);
}
export function colorHtmlToInt(color?: string) {

@ -1,10 +1,10 @@
// SPDX-FileCopyrightText: © 2017 EteSync Authors
// SPDX-License-Identifier: AGPL-3.0-only
import { shallowEqual, useSelector } from 'react-redux';
import { createSelector } from 'reselect';
import { shallowEqual, useSelector } from "react-redux";
import { createSelector } from "reselect";
import * as store from '../store';
import * as store from "../store";
export const remoteCredentialsSelector = createSelector(
(state: store.StoreState) => state.credentials.credentials,

@ -1,8 +1,8 @@
// SPDX-FileCopyrightText: © 2017 EteSync Authors
// SPDX-License-Identifier: AGPL-3.0-only
import * as React from 'react';
import { withRouter } from 'react-router';
import * as React from "react";
import { withRouter } from "react-router";
// FIXME: Should probably tie this to the history object, or at least based on the depth of the history
const stateCache = {};
@ -29,7 +29,7 @@ export function historyPersistor(tag: string) {
}
public getKeyForTag(props: any, tagName: string) {
return props.location.pathname + ':' + tagName;
return props.location.pathname + ":" + tagName;
}
});
};

@ -1,12 +1,12 @@
// SPDX-FileCopyrightText: © 2017 EteSync Authors
// SPDX-License-Identifier: AGPL-3.0-only
import * as ICAL from 'ical.js';
import * as zones from './data/zones.json';
import moment from 'moment';
import * as uuid from 'uuid';
import * as ICAL from "ical.js";
import * as zones from "./data/zones.json";
import moment from "moment";
import * as uuid from "uuid";
export const PRODID = '-//iCal.js EteSync iOS';
export const PRODID = "-//iCal.js EteSync iOS";
export interface PimType {
uid: string;
@ -32,11 +32,11 @@ export function timezoneLoadFromName(timezone: string | null) {
return ICAL.TimezoneService.get(timezone);
}
const component = new ICAL.Component('vtimezone');
const component = new ICAL.Component("vtimezone");
zone.ics.forEach((zonePart: string) => {
component.addSubcomponent(new ICAL.Component(ICAL.parse(zonePart)));
});
component.addPropertyWithValue('tzid', timezone);
component.addPropertyWithValue("tzid", timezone);
const retZone = new ICAL.Timezone({
component,
@ -49,17 +49,17 @@ export function timezoneLoadFromName(timezone: string | null) {
}
export function parseString(content: string) {
content = content.replace(/^[a-zA-Z0-9]*\./gm, ''); // FIXME: ugly hack to ignore item groups.
content = content.replace(/^[a-zA-Z0-9]*\./gm, ""); // FIXME: ugly hack to ignore item groups.
return new ICAL.Component(ICAL.parse(content));
}
export class EventType extends ICAL.Event implements PimType {
public static isEvent(comp: ICAL.Component) {
return !!comp.getFirstSubcomponent('vevent');
return !!comp.getFirstSubcomponent("vevent");
}
public static fromVCalendar(comp: ICAL.Component) {
const event = new EventType(comp.getFirstSubcomponent('vevent'));
const event = new EventType(comp.getFirstSubcomponent("vevent"));
// FIXME: we need to clone it so it loads the correct timezone and applies it
timezoneLoadFromName(event.timezone);
return event.clone();
@ -106,25 +106,25 @@ export class EventType extends ICAL.Event implements PimType {
}
get lastModified() {
return this.component.getFirstPropertyValue('last-modified');
return this.component.getFirstPropertyValue("last-modified");
}
set lastModified(time: ICAL.Time) {
this.component.updatePropertyWithValue('last-modified', time);
this.component.updatePropertyWithValue("last-modified", time);
}
get rrule() {
return this.component.getFirstPropertyValue('rrule');
return this.component.getFirstPropertyValue("rrule");
}
set rrule(rule: ICAL.Recur) {
this.component.updatePropertyWithValue('rrule', rule);
this.component.updatePropertyWithValue("rrule", rule);
}
public toIcal() {
const comp = new ICAL.Component(['vcalendar', [], []]);
comp.updatePropertyWithValue('prodid', PRODID);
comp.updatePropertyWithValue('version', '2.0');
const comp = new ICAL.Component(["vcalendar", [], []]);
comp.updatePropertyWithValue("prodid", PRODID);
comp.updatePropertyWithValue("version", "2.0");
comp.addSubcomponent(this.component);
ICAL.helpers.updateTimezones(comp);
@ -139,10 +139,10 @@ export class EventType extends ICAL.Event implements PimType {
}
export enum TaskStatusType {
NeedsAction = 'NEEDS-ACTION',
Completed = 'COMPLETED',
InProcess = 'IN-PROCESS',
Cancelled = 'CANCELLED',
NeedsAction = "NEEDS-ACTION",
Completed = "COMPLETED",
InProcess = "IN-PROCESS",
Cancelled = "CANCELLED",
}
export enum TaskPriorityType {
@ -152,11 +152,11 @@ export enum TaskPriorityType {
Low = 9
}
export const TaskTags = ['Work', 'Home'];
export const TaskTags = ["Work", "Home"];
export class TaskType extends EventType {
public static fromVCalendar(comp: ICAL.Component) {
const task = new TaskType(comp.getFirstSubcomponent('vtodo'));
const task = new TaskType(comp.getFirstSubcomponent("vtodo"));
// FIXME: we need to clone it so it loads the correct timezone and applies it
timezoneLoadFromName(task.timezone);
return task.clone();
@ -169,7 +169,7 @@ export class TaskType extends EventType {
public color: string;
constructor(comp?: ICAL.Component | null) {
super(comp ? comp : new ICAL.Component('vtodo'));
super(comp ? comp : new ICAL.Component("vtodo"));
}
get finished() {
@ -178,64 +178,64 @@ export class TaskType extends EventType {
}
set status(status: TaskStatusType) {
this.component.updatePropertyWithValue('status', status);
this.component.updatePropertyWithValue("status", status);
}
get status(): TaskStatusType {
return this.component.getFirstPropertyValue('status');
return this.component.getFirstPropertyValue("status");
}
set priority(priority: TaskPriorityType) {
this.component.updatePropertyWithValue('priority', priority);
this.component.updatePropertyWithValue("priority", priority);
}
get priority() {
return this.component.getFirstPropertyValue('priority');
return this.component.getFirstPropertyValue("priority");
}
set tags(tags: string[]) {
this.component.updatePropertyWithValue('categories', tags.join(','));
this.component.updatePropertyWithValue("categories", tags.join(","));
}
get tags() {
const tags = this.component.getFirstPropertyValue('categories');
return tags ? tags.split(',') : [];
const tags = this.component.getFirstPropertyValue("categories");
return tags ? tags.split(",") : [];
}
set dueDate(date: ICAL.Time | undefined) {
if (date) {
this.component.updatePropertyWithValue('due', date);
this.component.updatePropertyWithValue("due", date);
} else {
this.component.removeAllProperties('due');
this.component.removeAllProperties("due");
}
}
get dueDate() {
return this.component.getFirstPropertyValue('due');
return this.component.getFirstPropertyValue("due");
}
set completionDate(date: ICAL.Time | undefined) {
if (date) {
this.component.updatePropertyWithValue('completed', date);
this.component.updatePropertyWithValue("completed", date);
} else {
this.component.removeAllProperties('completed');
this.component.removeAllProperties("completed");
}
}
get completionDate() {
return this.component.getFirstPropertyValue('completed');
return this.component.getFirstPropertyValue("completed");
}
set relatedTo(parentUid: string | undefined) {
if (parentUid !== undefined) {
this.component.updatePropertyWithValue('related-to', parentUid);
this.component.updatePropertyWithValue("related-to", parentUid);
} else {
this.component.removeAllProperties('related-to');
this.component.removeAllProperties("related-to");
}
}
get relatedTo(): string | undefined {
return this.component.getFirstPropertyValue('related-to');
return this.component.getFirstPropertyValue("related-to");
}
get endDate() {
@ -248,7 +248,7 @@ export class TaskType extends EventType {
}
get dueToday() {
return this.dueDate && moment(this.dueDate.toJSDate()).isSameOrBefore(moment(), 'day');
return this.dueDate && moment(this.dueDate.toJSDate()).isSameOrBefore(moment(), "day");
}
get overdue() {
@ -258,7 +258,7 @@ export class TaskType extends EventType {
const dueDate = moment(this.dueDate.toJSDate());
const now = moment();
return (this.dueDate.isDate) ? dueDate.isBefore(now, 'day') : dueDate.isBefore(now);
return (this.dueDate.isDate) ? dueDate.isBefore(now, "day") : dueDate.isBefore(now);
}
get hidden() {
@ -349,23 +349,23 @@ export class ContactType implements PimType {
}
get uid() {
return this.comp.getFirstPropertyValue('uid');
return this.comp.getFirstPropertyValue("uid");
}
set uid(uid: string) {
this.comp.updatePropertyWithValue('uid', uid);
this.comp.updatePropertyWithValue("uid", uid);
}
get fn() {
return this.comp.getFirstPropertyValue('fn');
return this.comp.getFirstPropertyValue("fn");
}
get n() {
return this.comp.getFirstPropertyValue('n');
return this.comp.getFirstPropertyValue("n");
}
get group() {
const kind = this.comp.getFirstPropertyValue('kind');
return kind in ['group', 'organization'];
const kind = this.comp.getFirstPropertyValue("kind");
return kind in ["group", "organization"];
}
}

@ -13,9 +13,9 @@
// This link also includes instructions on opting out of this behavior.
const isLocalhost = Boolean(
window.location.hostname === 'localhost' ||
window.location.hostname === "localhost" ||
// [::1] is the IPv6 localhost address.
window.location.hostname === '[::1]' ||
window.location.hostname === "[::1]" ||
// 127.0.0.1/8 is considered localhost for IPv4.
window.location.hostname.match(
/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
@ -23,7 +23,7 @@ const isLocalhost = Boolean(
);
export default function register() {
if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
if (process.env.NODE_ENV === "production" && "serviceWorker" in navigator) {
// The URL constructor is available in all browsers that support SW.
const publicUrl = new URL(
process.env.PUBLIC_URL!,
@ -36,7 +36,7 @@ export default function register() {
return;
}
window.addEventListener('load', () => {
window.addEventListener("load", () => {
const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
if (!isLocalhost) {
@ -58,18 +58,18 @@ function registerValidSW(swUrl: string) {
const installingWorker = registration.installing;
if (installingWorker) {
installingWorker.onstatechange = () => {
if (installingWorker.state === 'installed') {
if (installingWorker.state === "installed") {
if (navigator.serviceWorker.controller) {
// At this point, the old content will have been purged and
// the fresh content will have been added to the cache.
// It's the perfect time to display a 'New content is
// available; please refresh.' message in your web app.
console.log('New content is available; please refresh.');
console.log("New content is available; please refresh.");
} else {
// At this point, everything has been precached.
// It's the perfect time to display a
// 'Content is cached for offline use.' message.
console.log('Content is cached for offline use.');
console.log("Content is cached for offline use.");
}
}
};
@ -77,7 +77,7 @@ function registerValidSW(swUrl: string) {
};
})
.catch((error) => {
console.error('Error during service worker registration:', error);
console.error("Error during service worker registration:", error);
});
}
@ -88,7 +88,7 @@ function checkValidServiceWorker(swUrl: string) {
// Ensure service worker exists, and that we really are getting a JS file.
if (
response.status === 404 ||
response.headers.get('content-type')!.indexOf('javascript') === -1
response.headers.get("content-type")!.indexOf("javascript") === -1
) {
// No service worker found. Probably a different app. Reload the page.
navigator.serviceWorker.ready.then((registration) => {
@ -103,13 +103,13 @@ function checkValidServiceWorker(swUrl: string) {
})
.catch(() => {
console.log(
'No internet connection found. App is running in offline mode.'
"No internet connection found. App is running in offline mode."
);
});
}
export function unregister() {
if ('serviceWorker' in navigator) {
if ("serviceWorker" in navigator) {
navigator.serviceWorker.ready.then((registration) => {
registration.unregister();
});

@ -1,63 +1,63 @@
// SPDX-FileCopyrightText: © 2017 EteSync Authors
// SPDX-License-Identifier: AGPL-3.0-only
import { RouteResolver } from './routes';
import { RouteResolver } from "./routes";
const routes = {
home: '',
home: "",
post: {
_base: 'post',
_base: "post",
_id: {
_base: ':postId',
comment: 'comment/:commentId',
revision: 'history/:revisionId/:someOtherVar/test',
_base: ":postId",
comment: "comment/:commentId",
revision: "history/:revisionId/:someOtherVar/test",
},
},
};
const routeResolver = new RouteResolver(routes);
it('translating routes', () => {
it("translating routes", () => {
// Working basic resolves
expect(routeResolver.getRoute('home')).toBe('/');
expect(routeResolver.getRoute('post')).toBe('/post');
expect(routeResolver.getRoute('post._id')).toBe('/post/:postId');
expect(routeResolver.getRoute('post._id.comment')).toBe('/post/:postId/comment/:commentId');
expect(routeResolver.getRoute("home")).toBe("/");
expect(routeResolver.getRoute("post")).toBe("/post");
expect(routeResolver.getRoute("post._id")).toBe("/post/:postId");
expect(routeResolver.getRoute("post._id.comment")).toBe("/post/:postId/comment/:commentId");
// Working translation resolves
expect(routeResolver.getRoute('home')).toBe('/');
expect(routeResolver.getRoute('post')).toBe('/post');
expect(routeResolver.getRoute('post._id', { postId: 3 })).toBe('/post/3');
expect(routeResolver.getRoute('post._id.comment',
{ postId: 3, commentId: 5 })).toBe('/post/3/comment/5');
expect(routeResolver.getRoute('post._id.revision',
{ postId: 3, revisionId: 5, someOtherVar: 'a' })).toBe('/post/3/history/5/a/test');
expect(routeResolver.getRoute("home")).toBe("/");
expect(routeResolver.getRoute("post")).toBe("/post");
expect(routeResolver.getRoute("post._id", { postId: 3 })).toBe("/post/3");
expect(routeResolver.getRoute("post._id.comment",
{ postId: 3, commentId: 5 })).toBe("/post/3/comment/5");
expect(routeResolver.getRoute("post._id.revision",
{ postId: 3, revisionId: 5, someOtherVar: "a" })).toBe("/post/3/history/5/a/test");
// Failing basic resolves
expect(() => {
routeResolver.getRoute('bad');
routeResolver.getRoute("bad");
}).toThrow();
expect(() => {
routeResolver.getRoute('home.bad');
routeResolver.getRoute("home.bad");
}).toThrow();
expect(() => {
routeResolver.getRoute('post._id.bad');
routeResolver.getRoute("post._id.bad");
}).toThrow();
// Failing translations
expect(() => {
routeResolver.getRoute('home', { test: 4 });
routeResolver.getRoute("home", { test: 4 });
}).toThrow();
expect(() => {
routeResolver.getRoute('post._id', { test: 4 });
routeResolver.getRoute("post._id", { test: 4 });
}).toThrow();
expect(() => {
routeResolver.getRoute('post._id', { postId: 3, test: 4 });
routeResolver.getRoute("post._id", { postId: 3, test: 4 });
}).toThrow();
expect(() => {
routeResolver.getRoute('post._id.comment', { postId: 3, commentId: 5, test: 4 });
routeResolver.getRoute("post._id.comment", { postId: 3, commentId: 5, test: 4 });
}).toThrow();
expect(() => {
routeResolver.getRoute('post._id.comment', { postId: 3 });
routeResolver.getRoute("post._id.comment", { postId: 3 });
}).toThrow();
});

@ -16,8 +16,8 @@ export class RouteResolver {
let dict = this.routes;
let path: string[] = [];
name.split('.').forEach((key) => {
const val = (typeof dict[key] === 'string') ? dict[key] : (dict[key]._base) ? dict[key]._base : key;
name.split(".").forEach((key) => {
const val = (typeof dict[key] === "string") ? dict[key] : (dict[key]._base) ? dict[key]._base : key;
path.push(val);
dict = dict[key];
@ -27,11 +27,11 @@ export class RouteResolver {
const keys = Object.assign({}, _keys);
path = path.map((pathComponent) => {
return pathComponent.split('/').map((val) => {
if (val[0] === ':') {
return pathComponent.split("/").map((val) => {
if (val[0] === ":") {
const ret = keys[val.slice(1)];
if (ret === undefined) {
throw new Error('Missing key: ' + val.slice(1));
throw new Error("Missing key: " + val.slice(1));
}
delete keys[val.slice(1)];
@ -39,14 +39,14 @@ export class RouteResolver {
}
return val;
}).join('/');
}).join("/");
});
if (Object.keys(keys).length !== 0) {
throw new Error('Too many keys for route.');
throw new Error("Too many keys for route.");
}
}
return '/' + path.join('/');
return "/" + path.join("/");
}
}

@ -1,12 +1,12 @@
// SPDX-FileCopyrightText: © 2017 EteSync Authors
// SPDX-License-Identifier: AGPL-3.0-only
import { Action, createAction, createActions } from 'redux-actions';
import { Action, createAction, createActions } from "redux-actions";
import * as EteSync from 'etesync';
import { UserInfo } from 'etesync';
import * as EteSync from "etesync";
import { UserInfo } from "etesync";
import { CredentialsData, CredentialsDataRemote, EntriesData, SettingsType } from './';
import { CredentialsData, CredentialsDataRemote, EntriesData, SettingsType } from "./";
export const { fetchCredentials } = createActions({
FETCH_CREDENTIALS: (username: string, password: string, server: string) => {
@ -33,7 +33,7 @@ export const { fetchCredentials } = createActions({
});
export const logout = createAction(
'LOGOUT',
"LOGOUT",
(etesync: CredentialsDataRemote) => {
(async () => {
const authenticator = new EteSync.Authenticator(etesync.serviceApiUrl);
@ -54,7 +54,7 @@ export const { deriveKey } = createActions({
});
export const resetKey = createAction(
'RESET_KEY',
"RESET_KEY",
() => {
return null;
}
@ -79,7 +79,7 @@ export const { fetchListJournal } = createActions({
});
export const addJournal = createAction(
'ADD_JOURNAL',
"ADD_JOURNAL",
(etesync: CredentialsData, journal: EteSync.Journal) => {
const creds = etesync.credentials;
const apiBase = etesync.serviceApiUrl;
@ -93,7 +93,7 @@ export const addJournal = createAction(
);
export const updateJournal = createAction(
'UPDATE_JOURNAL',
"UPDATE_JOURNAL",
(etesync: CredentialsData, journal: EteSync.Journal) => {
const creds = etesync.credentials;
const apiBase = etesync.serviceApiUrl;
@ -107,7 +107,7 @@ export const updateJournal = createAction(
);
export const deleteJournal = createAction(
'DELETE_JOURNAL',
"DELETE_JOURNAL",
(etesync: CredentialsData, journal: EteSync.Journal) => {
const creds = etesync.credentials;
const apiBase = etesync.serviceApiUrl;
@ -158,7 +158,7 @@ export const { fetchUserInfo } = createActions({
});
export const createUserInfo = createAction(
'CREATE_USER_INFO',
"CREATE_USER_INFO",
(etesync: CredentialsData, userInfo: UserInfo) => {
const creds = etesync.credentials;
const apiBase = etesync.serviceApiUrl;
@ -197,14 +197,14 @@ export function fetchAll(etesync: CredentialsData, currentEntries: EntriesData)
export const appendError = createAction(
'APPEND_ERROR',
"APPEND_ERROR",
(_etesync: CredentialsData, error: Error | Error[]) => {
return Array.isArray(error) ? error : [error];
}
);
export const clearErros = createAction(
'CLEAR_ERRORS',
"CLEAR_ERRORS",
(_etesync: CredentialsData) => {
return true;
}
@ -212,7 +212,7 @@ export const clearErros = createAction(
// FIXME: Move the rest to their own file
export const setSettings = createAction(
'SET_SETTINGS',
"SET_SETTINGS",
(settings: Partial<SettingsType>) => {
return { ...settings };
}

@ -1,19 +1,19 @@
// SPDX-FileCopyrightText: © 2017 EteSync Authors
// SPDX-License-Identifier: AGPL-3.0-only
import * as localforage from 'localforage';
import { combineReducers } from 'redux';
import { createMigrate, persistReducer, createTransform } from 'redux-persist';
import session from 'redux-persist/lib/storage/session';
import * as localforage from "localforage";
import { combineReducers } from "redux";
import { createMigrate, persistReducer, createTransform } from "redux-persist";
import session from "redux-persist/lib/storage/session";
import { List, Map as ImmutableMap } from 'immutable';
import { List, Map as ImmutableMap } from "immutable";
import * as EteSync from 'etesync';
import * as EteSync from "etesync";
import {
JournalsData, EntriesData, UserInfoData,
CredentialsDataRemote, SettingsType,
fetchCount, journals, entries, credentials, userInfo, settingsReducer, encryptionKeyReducer, errorsReducer,
} from './reducers';
} from "./reducers";
export interface StoreState {
fetchCount: number;
@ -34,14 +34,14 @@ const settingsMigrations = {
...state,
taskSettings: {
filterBy: null,
sortBy: 'smart',
sortBy: "smart",
},
};
},
};
const settingsPersistConfig = {
key: 'settings',
key: "settings",
version: 0,
storage: localforage,
migrate: createMigrate(settingsMigrations, { debug: false }),
@ -54,14 +54,14 @@ const credentialsMigrations = {
};
const credentialsPersistConfig = {
key: 'credentials',
key: "credentials",
version: 0,
storage: localforage,
migrate: createMigrate(credentialsMigrations, { debug: false }),
};
const encryptionKeyPersistConfig = {
key: 'encryptionKey',
key: "encryptionKey",
storage: session,
};
@ -127,15 +127,15 @@ const userInfoDeserialize = (state: EteSync.UserInfoJson) => {
};
const cacheSerialize = (state: any, key: string | number) => {
if (key === 'entries') {
if (key === "entries") {
const ret = {};
state.forEach((value: List<EteSync.Entry>, mapKey: string) => {
ret[mapKey] = entriesSerialize(value);
});
return ret;
} else if (key === 'journals') {
} else if (key === "journals") {
return journalsSerialize(state);
} else if (key === 'userInfo') {
} else if (key === "userInfo") {
return userInfoSerialize(state);
}
@ -143,15 +143,15 @@ const cacheSerialize = (state: any, key: string | number) => {
};
const cacheDeserialize = (state: any, key: string | number) => {
if (key === 'entries') {
if (key === "entries") {
const ret = {};
Object.keys(state).forEach((mapKey) => {
ret[mapKey] = entriesDeserialize(state[mapKey]);
});
return ImmutableMap(ret);
} else if (key === 'journals') {
} else if (key === "journals") {
return journalsDeserialize(state);
} else if (key === 'userInfo') {
} else if (key === "userInfo") {
return userInfoDeserialize(state);
}
@ -176,7 +176,7 @@ const cacheMigrations = {
};
const cachePersistConfig = {
key: 'cache',
key: "cache",
version: 2,
storage: localforage,
transforms: [createTransform(cacheSerialize, cacheDeserialize)],

@ -1,21 +1,21 @@
// SPDX-FileCopyrightText: © 2017 EteSync Authors
// SPDX-License-Identifier: AGPL-3.0-only
import { addEntries, fetchEntries } from './actions';
import { entries, EntriesData } from './reducers';
import { addEntries, fetchEntries } from "./actions";
import { entries, EntriesData } from "./reducers";
import { Map } from 'immutable';
import { Map } from "immutable";
import * as EteSync from 'etesync';
import * as EteSync from "etesync";
it('Entries reducer', () => {
const jId = '24324324324';
it("Entries reducer", () => {
const jId = "24324324324";
let state = Map({}) as EntriesData;
const entry = new EteSync.Entry();
entry.deserialize({
content: 'someContent',
uid: '6355209e2a2c26a6c1e6e967c2032737d538f602cf912474da83a2902f8a0a83',
content: "someContent",
uid: "6355209e2a2c26a6c1e6e967c2032737d538f602cf912474da83a2902f8a0a83",
});
const action = {

@ -1,25 +1,25 @@
// SPDX-FileCopyrightText: © 2017 EteSync Authors
// SPDX-License-Identifier: AGPL-3.0-only
import { createStore, applyMiddleware } from 'redux';
import { persistStore } from 'redux-persist';
import thunkMiddleware from 'redux-thunk';
import { createLogger } from 'redux-logger';
import { createStore, applyMiddleware } from "redux";
import { persistStore } from "redux-persist";
import thunkMiddleware from "redux-thunk";
import { createLogger } from "redux-logger";
import promiseMiddleware from './promise-middleware';
import promiseMiddleware from "./promise-middleware";
import reducers from './construct';
import reducers from "./construct";
// Workaround babel limitation
export * from './reducers';
export * from './construct';
export * from "./reducers";
export * from "./construct";
const middleware = [
thunkMiddleware,
promiseMiddleware,
];
if (process.env.NODE_ENV === 'development') {
if (process.env.NODE_ENV === "development") {
middleware.push(createLogger());
}

@ -4,7 +4,7 @@
// Based on: https://github.com/acdlite/redux-promise/blob/master/src/index.js
function isPromise(val: any): val is Promise<any> {
return val && typeof val.then === 'function';
return val && typeof val.then === "function";
}
export default function promiseMiddleware({ dispatch }: any) {

@ -1,14 +1,14 @@
// SPDX-FileCopyrightText: © 2017 EteSync Authors
// SPDX-License-Identifier: AGPL-3.0-only
import { Action, ActionMeta, ActionFunctionAny, combineActions, handleAction, handleActions } from 'redux-actions';
import { shallowEqual } from 'react-redux';
import { Action, ActionMeta, ActionFunctionAny, combineActions, handleAction, handleActions } from "redux-actions";
import { shallowEqual } from "react-redux";
import { List, Map as ImmutableMap } from 'immutable';
import { List, Map as ImmutableMap } from "immutable";
import * as EteSync from 'etesync';
import * as EteSync from "etesync";
import * as actions from './actions';
import * as actions from "./actions";
export interface CredentialsDataRemote {
serviceApiUrl: string;
@ -218,10 +218,10 @@ const fetchActions = [
] as Array<ActionFunctionAny<Action<any>>>;
for (const func in actions) {
if (func.startsWith('fetch') ||
func.startsWith('add') ||
func.startsWith('update') ||
func.startsWith('delete')) {
if (func.startsWith("fetch") ||
func.startsWith("add") ||
func.startsWith("update") ||
func.startsWith("delete")) {
fetchActions.push(actions[func]);
}
@ -286,11 +286,11 @@ export const settingsReducer = handleActions(
),
},
{
locale: 'en-gb',
locale: "en-gb",
darkMode: false,
taskSettings: {
filterBy: null,
sortBy: 'smart',
sortBy: "smart",
},
}
);

@ -4,7 +4,7 @@
// Disable some style eslint rules for things we can't control
/* eslint-disable @typescript-eslint/camelcase, @typescript-eslint/class-name-casing */
declare module 'ical.js' {
declare module "ical.js" {
function parse(input: string): any[];
export class helpers {
@ -151,7 +151,7 @@ declare module 'ical.js' {
static public remove(tzid: string): Timezone | null;
}
export type FrequencyValues = 'YEARLY' | 'MONTHLY' | 'WEEKLY' | 'DAILY' | 'HOURLY' | 'MINUTELY' | 'SECONDLY';
export type FrequencyValues = "YEARLY" | "MONTHLY" | "WEEKLY" | "DAILY" | "HOURLY" | "MINUTELY" | "SECONDLY";
export enum WeekDay {
SU = 1,
@ -191,7 +191,7 @@ declare module 'ical.js' {
public count: number | null;
public clone(): Recur;
public toJSON(): Omit<RecurData, 'until'> & { until?: string };
public toJSON(): Omit<RecurData, "until"> & { until?: string };
public iterator(startTime?: Time): RecurIterator;
public isByCount(): boolean;
}

@ -1,6 +1,6 @@
// SPDX-FileCopyrightText: © 2017 EteSync Authors
// SPDX-License-Identifier: AGPL-3.0-only
declare module 'redux-persist';
declare module 'redux-persist/lib/storage/session';
declare module 'redux-persist/es/integration/react';
declare module "redux-persist";
declare module "redux-persist/lib/storage/session";
declare module "redux-persist/es/integration/react";

@ -1,12 +1,12 @@
// SPDX-FileCopyrightText: © 2017 EteSync Authors
// SPDX-License-Identifier: AGPL-3.0-only
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import * as React from "react";
import * as ReactDOM from "react-dom";
export default (props: {title: string, children?: React.ReactNode | React.ReactNode[]}) => {
const titleEl = document.querySelector('#appbar-title');
const buttonsEl = document.querySelector('#appbar-buttons');
const titleEl = document.querySelector("#appbar-title");
const buttonsEl = document.querySelector("#appbar-buttons");
return (
<>

@ -1,7 +1,7 @@
// SPDX-FileCopyrightText: © 2017 EteSync Authors
// SPDX-License-Identifier: AGPL-3.0-only
import * as React from 'react';
import * as React from "react";
export const Avatar = React.memo((props: { children: React.ReactNode[] | React.ReactNode, size?: number, style?: any }) => {
const size = (props.size) ? props.size : 40;
@ -9,12 +9,12 @@ export const Avatar = React.memo((props: { children: React.ReactNode[] | React.R
return (
<div
style={{
backgroundColor: 'grey',
color: 'white',
display: 'inline-flex',
justifyContent: 'center',
alignItems: 'center',
borderRadius: '50%',
backgroundColor: "grey",
color: "white",
display: "inline-flex",
justifyContent: "center",
alignItems: "center",
borderRadius: "50%",
height: size,
width: size,
...props.style,

@ -1,7 +1,7 @@
// SPDX-FileCopyrightText: © 2017 EteSync Authors
// SPDX-License-Identifier: AGPL-3.0-only
import * as React from 'react';
import * as React from "react";
interface PropsType {
color: string;

@ -1,11 +1,11 @@
// SPDX-FileCopyrightText: © 2017 EteSync Authors
// SPDX-License-Identifier: AGPL-3.0-only
import * as React from 'react';
import * as React from "react";
import ColorBox from './ColorBox';
import { colorHtmlToInt } from '../journal-processors';
import { TextField, ButtonBase } from '@material-ui/core';
import ColorBox from "./ColorBox";
import { colorHtmlToInt } from "../journal-processors";
import { TextField, ButtonBase } from "@material-ui/core";
interface PropsType {
color: string;
@ -20,18 +20,18 @@ interface PropsType {
export default function ColorPicker(props: PropsType) {
const colors = [
[
'#F44336',
'#E91E63',
'#673AB7',
'#3F51B5',
'#2196F3',
"#F44336",
"#E91E63",
"#673AB7",
"#3F51B5",
"#2196F3",
],
[
'#03A9F4',
'#4CAF50',
'#8BC34A',
'#FFEB3B',
'#FF9800',
"#03A9F4",
"#4CAF50",
"#8BC34A",
"#FFEB3B",
"#FF9800",
],
];
const color = props.color;
@ -39,7 +39,7 @@ export default function ColorPicker(props: PropsType) {
return (
<div>
{colors.map((colorGroup, idx) => (
<div key={idx} style={{ flex: 1, flexDirection: 'row', justifyContent: 'space-between' }}>
<div key={idx} style={{ flex: 1, flexDirection: "row", justifyContent: "space-between" }}>
{colorGroup.map((colorOption) => (
<ButtonBase
style={{ margin: 5, borderRadius: 36 / 2 }}
@ -51,9 +51,9 @@ export default function ColorPicker(props: PropsType) {
))}
</div>
))}
<div style={{ flex: 1, alignItems: 'center', flexDirection: 'row', margin: 5 }}>
<div style={{ flex: 1, alignItems: "center", flexDirection: "row", margin: 5 }}>
<ColorBox
style={{ display: 'inline-block' }}
style={{ display: "inline-block" }}
size={36}
color={(color && colorHtmlToInt(color)) ? color : props.defaultColor}
/>
@ -61,8 +61,8 @@ export default function ColorPicker(props: PropsType) {
style={{ marginLeft: 10, flex: 1 }}
error={!!props.error}
onChange={(event: React.FormEvent<{ value: string }>) => props.onChange(event.currentTarget.value)}
placeholder={props.placeholder ?? 'E.g. #aabbcc'}
label={props.label ?? 'Color'}
placeholder={props.placeholder ?? "E.g. #aabbcc"}
label={props.label ?? "Color"}
value={color}
helperText={props.error}
/>

@ -1,12 +1,12 @@
// SPDX-FileCopyrightText: © 2017 EteSync Authors
// SPDX-License-Identifier: AGPL-3.0-only
import * as React from 'react';
import * as React from "react";
import { makeStyles } from '@material-ui/core/styles';
import Radio from '@material-ui/core/Radio';
import { Omit } from '@material-ui/types';
import FormControlLabel, { FormControlLabelProps } from '@material-ui/core/FormControlLabel';
import { makeStyles } from "@material-ui/core/styles";
import Radio from "@material-ui/core/Radio";
import { Omit } from "@material-ui/types";
import FormControlLabel, { FormControlLabelProps } from "@material-ui/core/FormControlLabel";
interface Props {
color: string;
@ -19,7 +19,7 @@ const useStyles = makeStyles({
},
});
export default function ColoredRadio(props: Props & Omit<FormControlLabelProps, keyof Props | 'control'>) {
export default function ColoredRadio(props: Props & Omit<FormControlLabelProps, keyof Props | "control">) {
const { color, label, value, ...other } = props;
const { root } = useStyles(props);

@ -1,13 +1,13 @@
// SPDX-FileCopyrightText: © 2017 EteSync Authors
// SPDX-License-Identifier: AGPL-3.0-only
import * as React from 'react';
import * as React from "react";
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 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";
export default React.memo((_props: any) => {
const {
@ -41,7 +41,7 @@ export default React.memo((_props: any) => {
color="primary"
onClick={onOk}
>
{labelOk || 'Confirm'}
{labelOk || "Confirm"}
</Button>
</DialogActions>
</Dialog>

@ -1,10 +1,10 @@
// SPDX-FileCopyrightText: © 2017 EteSync Authors
// SPDX-License-Identifier: AGPL-3.0-only
import * as React from 'react';
import Paper from '@material-ui/core/Paper';
import * as React from "react";
import Paper from "@material-ui/core/Paper";
import './Container.css';
import "./Container.css";
export default (props: {style?: any, children: any}) => (
<div className="Container" style={props.style}>

@ -1,12 +1,12 @@
// SPDX-FileCopyrightText: © 2017 EteSync Authors
// SPDX-License-Identifier: AGPL-3.0-only
import * as React from 'react';
import * as React from "react";
import MomentUtils from '@date-io/moment';
import { MuiPickersUtilsProvider, KeyboardDatePicker, KeyboardDateTimePicker } from '@material-ui/pickers';
import MomentUtils from "@date-io/moment";
import { MuiPickersUtilsProvider, KeyboardDatePicker, KeyboardDateTimePicker } from "@material-ui/pickers";
import moment from 'moment';
import moment from "moment";
interface PropsType {
placeholder: string;
@ -23,7 +23,7 @@ class DateTimePicker extends React.PureComponent<PropsType> {
public render() {
const Picker = (this.props.dateOnly) ? KeyboardDatePicker : KeyboardDateTimePicker;
const dateFormat = (this.props.dateOnly) ? 'DD/MM/YYYY' : 'DD/MM/YYYY HH:mm';
const dateFormat = (this.props.dateOnly) ? "DD/MM/YYYY" : "DD/MM/YYYY HH:mm";
return (
<MuiPickersUtilsProvider utils={MomentUtils}>
<Picker
@ -33,7 +33,7 @@ class DateTimePicker extends React.PureComponent<PropsType> {
ampm={false}
showTodayButton
KeyboardButtonProps={{
'aria-label': 'change date',
"aria-label": "change date",
}}
/>
</MuiPickersUtilsProvider>

@ -1,7 +1,7 @@
// SPDX-FileCopyrightText: © 2017 EteSync Authors
// SPDX-License-Identifier: AGPL-3.0-only
import * as React from 'react';
import * as React from "react";
export const ExternalLink = React.memo(({ children, ...props }: any) => (
<a target="_blank" rel="noopener noreferrer" {...props}>

@ -1,17 +1,17 @@
// SPDX-FileCopyrightText: © 2017 EteSync Authors
// SPDX-License-Identifier: AGPL-3.0-only
import * as React from 'react';
import * as React from "react";
import { createStyles, makeStyles } from '@material-ui/core/styles';
import MuiList from '@material-ui/core/List';
import MuiListItem from '@material-ui/core/ListItem';
import MuiListSubheader from '@material-ui/core/ListSubheader';
import ListItemIcon from '@material-ui/core/ListItemIcon';
import ListItemText from '@material-ui/core/ListItemText';
import Divider from '@material-ui/core/Divider';
import { createStyles, makeStyles } from "@material-ui/core/styles";
import MuiList from "@material-ui/core/List";
import MuiListItem from "@material-ui/core/ListItem";
import MuiListSubheader from "@material-ui/core/ListSubheader";
import ListItemIcon from "@material-ui/core/ListItemIcon";
import ListItemText from "@material-ui/core/ListItemText";
import Divider from "@material-ui/core/Divider";
import ExternalLink from './ExternalLink';
import ExternalLink from "./ExternalLink";
const useStyles = makeStyles((theme) => (createStyles({
inset: {
@ -46,7 +46,7 @@ interface ListItemPropsType {
insetChildren?: boolean;
nestedItems?: React.ReactNode[];
selected?: boolean;
secondaryTextColor?: 'initial' | 'inherit' | 'primary' | 'secondary' | 'textPrimary' | 'textSecondary' | 'error';
secondaryTextColor?: "initial" | "inherit" | "primary" | "secondary" | "textPrimary" | "textSecondary" | "error";
}
export const ListItem = React.memo(function ListItem(_props: ListItemPropsType) {
@ -70,7 +70,7 @@ export const ListItem = React.memo(function ListItem(_props: ListItemPropsType)
button: true,
href,
onClick,
component: (href) ? ExternalLink : 'div',
component: (href) ? ExternalLink : "div",
} : undefined;
return (

@ -1,8 +1,8 @@
// SPDX-FileCopyrightText: © 2017 EteSync Authors
// SPDX-License-Identifier: AGPL-3.0-only
import * as React from 'react';
import CircularProgress from '@material-ui/core/CircularProgress';
import * as React from "react";
import CircularProgress from "@material-ui/core/CircularProgress";
export default (props: any) => {
return (

@ -1,19 +1,19 @@
// SPDX-FileCopyrightText: © 2017 EteSync Authors
// SPDX-License-Identifier: AGPL-3.0-only
import * as React from 'react';
import * as React from "react";
import MuiMenu, { MenuProps } from '@material-ui/core/Menu';
import { PopoverOrigin } from '@material-ui/core/Popover';
import MuiMenu, { MenuProps } from "@material-ui/core/Menu";
import { PopoverOrigin } from "@material-ui/core/Popover";
const anchorOrigin: PopoverOrigin = {
vertical: 'bottom',
horizontal: 'right',
vertical: "bottom",
horizontal: "right",
};
const transferOrigin: PopoverOrigin = {
vertical: 'top',
horizontal: 'right',
vertical: "top",
horizontal: "right",
};
export default function Menu(props: MenuProps) {

@ -1,11 +1,11 @@
// SPDX-FileCopyrightText: © 2017 EteSync Authors
// SPDX-License-Identifier: AGPL-3.0-only
import * as React from 'react';
import * as React from "react";
export const PrettyError = React.memo((props: any) => (
<div>
<pre style={{ fontWeight: 'bold' }}>
<pre style={{ fontWeight: "bold" }}>
{props.error.message}
</pre>

@ -1,10 +1,10 @@
// SPDX-FileCopyrightText: © 2017 EteSync Authors
// SPDX-License-Identifier: AGPL-3.0-only
import * as React from 'react';
import sjcl from 'sjcl';
import * as React from "react";
import sjcl from "sjcl";
import { byte, base64 } from 'etesync';
import { byte, base64 } from "etesync";
function byteArray4ToNumber(bytes: byte[], offset: number) {
// tslint:disable:no-bitwise
@ -18,7 +18,7 @@ function byteArray4ToNumber(bytes: byte[], offset: number) {
function getEncodedChunk(publicKey: byte[], offset: number) {
const chunk = byteArray4ToNumber(publicKey, offset) % 100000;
return chunk.toString().padStart(5, '0');
return chunk.toString().padStart(5, "0");
}
interface PropsType {
@ -31,12 +31,12 @@ class PrettyFingerprint extends React.PureComponent<PropsType> {
sjcl.hash.sha256.hash(sjcl.codec.base64.toBits(this.props.publicKey))
);
const spacing = ' ';
const spacing = " ";
const prettyPublicKey =
getEncodedChunk(fingerprint, 0) + spacing +
getEncodedChunk(fingerprint, 4) + spacing +
getEncodedChunk(fingerprint, 8) + spacing +
getEncodedChunk(fingerprint, 12) + '\n' +
getEncodedChunk(fingerprint, 12) + "\n" +
getEncodedChunk(fingerprint, 16) + spacing +
getEncodedChunk(fingerprint, 20) + spacing +
getEncodedChunk(fingerprint, 24) + spacing +

@ -1,10 +1,10 @@
// SPDX-FileCopyrightText: © 2017 EteSync Authors
// SPDX-License-Identifier: AGPL-3.0-only
import * as React from 'react';
import { TextField, Select, MenuItem, InputLabel, FormControl } from '@material-ui/core';
import DateTimePicker from '../widgets/DateTimePicker';
import * as ICAL from 'ical.js';
import * as React from "react";
import { TextField, Select, MenuItem, InputLabel, FormControl } from "@material-ui/core";
import DateTimePicker from "../widgets/DateTimePicker";
import * as ICAL from "ical.js";
export type RRuleOptions = ICAL.RecurData;
@ -53,13 +53,13 @@ const menuItemsEnds = [Ends.Forever, Ends.Until, Ends.After].map((key) => {
let displayhName;
switch (key) {
case Ends.Forever:
displayhName = 'Forever';
displayhName = "Forever";
break;
case Ends.Until:
displayhName = 'Until';
displayhName = "Until";
break;
case Ends.After:
displayhName = 'For';
displayhName = "For";
break;
}
@ -67,7 +67,7 @@ const menuItemsEnds = [Ends.Forever, Ends.Until, Ends.After].map((key) => {
<MenuItem key={key} value={key}>{displayhName}</MenuItem>
);
});
const menuItemsFrequency = ['YEARLY', 'MONTHLY', 'WEEKLY', 'DAILY'].map((value) => {
const menuItemsFrequency = ["YEARLY", "MONTHLY", "WEEKLY", "DAILY"].map((value) => {
return (
<MenuItem key={value} value={value}>{value.toLowerCase()}</MenuItem>
);
@ -108,7 +108,7 @@ function sanitizeByDay(item: string | string[] | undefined) {
}
const styles = {
multiSelect: { minWidth: 120, maxWidth: '100%' },
multiSelect: { minWidth: 120, maxWidth: "100%" },
width: { width: 120 },
};
@ -146,12 +146,12 @@ export default function RRule(props: PropsType) {
}
}
return (
<div style={{ paddingLeft: '2em' }}>
<div style={{ display: 'flex', alignItems: 'center' }}>
<div style={{ paddingLeft: "2em" }}>
<div style={{ display: "flex", alignItems: "center" }}>
<span>Repeat</span>
<Select
value={options.freq}
style={{ marginLeft: '0.5em' }}
style={{ marginLeft: "0.5em" }}
onChange={(event: React.FormEvent<{ value: unknown }>) => {
const freq = (event.target as HTMLSelectElement).value as ICAL.FrequencyValues;
updateRule({ freq: freq }, true);
@ -159,16 +159,16 @@ export default function RRule(props: PropsType) {
>
{menuItemsFrequency}
</Select>
<span style={{ marginLeft: '0.5em' }}>every</span>
<span style={{ marginLeft: "0.5em" }}>every</span>
<TextField
style={{ marginLeft: '0.5em', width: '2.2em' }}
style={{ marginLeft: "0.5em", width: "2.2em" }}
type="number"
inputProps={{ min: 1, max: 99 }}
value={options.interval ?? 1}
onChange={(event: React.FormEvent<{ value: unknown }>) => {
event.preventDefault();
const inputNode = event.currentTarget as HTMLInputElement;
if (inputNode.value === '') {
if (inputNode.value === "") {
updateRule({ interval: 1 });
} else if (inputNode.valueAsNumber) {
updateRule({ interval: inputNode.valueAsNumber });
@ -176,7 +176,7 @@ export default function RRule(props: PropsType) {
}}
/>
</div>
{(options.freq && options.freq !== 'DAILY') &&
{(options.freq && options.freq !== "DAILY") &&
<div>
<FormControl>
<InputLabel>Weekdays</InputLabel>
@ -194,8 +194,8 @@ export default function RRule(props: PropsType) {
</div>
}
{!disableComplex && (
<div style={{ display: 'flex' }}>
{(options.freq === 'MONTHLY') &&
<div style={{ display: "flex" }}>
{(options.freq === "MONTHLY") &&
<Select value={options.bysetpos ? MonthRepeat.Bysetpos : MonthRepeat.Bymonthday}
onChange={(event: React.FormEvent<{ value: unknown }>) => {
const value = Number((event.target as HTMLInputElement).value);
@ -225,7 +225,7 @@ export default function RRule(props: PropsType) {
)}
<div>
{options.freq === 'MONTHLY' &&
{options.freq === "MONTHLY" &&
<TextField
type="number"
value={options.bymonthday ? options.bymonthday[0] : undefined}
@ -236,7 +236,7 @@ export default function RRule(props: PropsType) {
event.preventDefault();
const value = (event.currentTarget as HTMLInputElement).value;
const numberValue = Number(value);
if (value === '') {
if (value === "") {
updateRule({ bymonthday: undefined });
} else if (numberValue < 32 && numberValue > 0) {
updateRule({ bymonthday: [numberValue] });
@ -244,7 +244,7 @@ export default function RRule(props: PropsType) {
}}
/>
}
{options.freq === 'YEARLY' &&
{options.freq === "YEARLY" &&
<div>
<FormControl>
<InputLabel>Months</InputLabel>
@ -262,10 +262,10 @@ export default function RRule(props: PropsType) {
</div>
}
</div>
<div style={{ display: 'inline-flex', alignItems: 'center' }}>
<div style={{ display: "inline-flex", alignItems: "center" }}>
<Select
value={getEnds()}
style={{ marginRight: '0.5em' }}
style={{ marginRight: "0.5em" }}
onChange={(event: React.FormEvent<{ value: unknown }>) => {
const value = Number((event.target as HTMLSelectElement).value);
let updateOptions;
@ -296,19 +296,19 @@ export default function RRule(props: PropsType) {
<TextField
type="number"
value={options.count}
style={{ width: '4em' }}
style={{ width: "4em" }}
inputProps={{ min: 1, step: 1 }}
onChange={(event: React.FormEvent<{ value: unknown }>) => {
event.preventDefault();
const inputNode = event.currentTarget as HTMLInputElement;
if (inputNode.value === '') {
if (inputNode.value === "") {
updateRule({ count: 1 });
} else if (inputNode.valueAsNumber) {
updateRule({ count: inputNode.valueAsNumber });
}
}}
/>
<span style={{ marginLeft: '0.5em' }}>events</span>
<span style={{ marginLeft: "0.5em" }}>events</span>
</>
}
</div>

@ -1,12 +1,12 @@
// SPDX-FileCopyrightText: © 2017 EteSync Authors
// SPDX-License-Identifier: AGPL-3.0-only
import * as React from 'react';
import * as React from "react";
import Autocomplete from '@material-ui/lab/Autocomplete';
import TextField from '@material-ui/core/TextField';
import Autocomplete from "@material-ui/lab/Autocomplete";
import TextField from "@material-ui/core/TextField";
import * as zones from '../data/zones.json';
import * as zones from "../data/zones.json";
const zonelist = Object.keys(zones.zones).sort();
interface PropsType {
@ -21,7 +21,7 @@ export default React.memo(function TimezonePicker(props: PropsType) {
options={zonelist}
value={props.value}
onChange={(_e: any, value: string) => props.onChange(value)}
getOptionLabel={(option) => option.replace('_', ' ')}
getOptionLabel={(option) => option.replace("_", " ")}
style={props.style}
renderInput={(params) => (
<TextField {...params} label="Timezone" fullWidth />

@ -1,16 +1,16 @@
// SPDX-FileCopyrightText: © 2017 EteSync Authors
// SPDX-License-Identifier: AGPL-3.0-only
import * as React from 'react';
import * as React from "react";
import Snackbar from '@material-ui/core/Snackbar';
import Alert from '@material-ui/lab/Alert';
import Snackbar from "@material-ui/core/Snackbar";
import Alert from "@material-ui/lab/Alert";
export interface PropsType {
open: boolean;
children: React.ReactNode;
onClose?: (event?: React.SyntheticEvent, reason?: string) => void;
severity?: 'error' | 'info' | 'success' | 'warning';
severity?: "error" | "info" | "success" | "warning";
autoHideDuration?: number;
}

@ -1,9 +1,9 @@
// SPDX-FileCopyrightText: © 2017 EteSync Authors
// SPDX-License-Identifier: AGPL-3.0-only
import * as React from 'react';
import * as React from "react";
import './withSpin.css';
import "./withSpin.css";
const withSpin = (Component: any) => {
return React.memo((_props: any) => {
@ -12,7 +12,7 @@ const withSpin = (Component: any) => {
...props
} = _props;
return (
<Component {...props} className={spin ? 'withSpin-spin' : ''} />
<Component {...props} className={spin ? "withSpin-spin" : ""} />
);
});
};

Loading…
Cancel
Save