Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds some initial improvements to Tasks #74

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions package.json
Expand Up @@ -10,6 +10,7 @@
"@material-ui/pickers": "^3.2.10",
"@material-ui/styles": "^4.6.0",
"etesync": "^0.3.1",
"fuse.js": "^3.4.6",
"ical.js": "^1.2.2",
"immutable": "^4.0.0-rc.12",
"localforage": "^1.7.3",
Expand Down
2 changes: 1 addition & 1 deletion src/App.tsx
Expand Up @@ -41,7 +41,7 @@ const muiTheme = createMuiTheme({
light: lightBlue.A200,
main: lightBlue.A400,
dark: lightBlue.A700,
contrastText: 'white',
contrastText: '#fff',
},
},
});
Expand Down
4 changes: 2 additions & 2 deletions src/Journals/Journal.tsx
Expand Up @@ -11,8 +11,8 @@ import SearchableAddressBook from '../components/SearchableAddressBook';
import Contact from '../components/Contact';
import Calendar from '../components/Calendar';
import Event from '../components/Event';
import Task from '../components/Task';
import TaskList from '../components/TaskList';
import Task from '../components/Tasks/Task';
import TaskList from '../components/Tasks/TaskList';

import AppBarOverride from '../widgets/AppBarOverride';
import Container from '../widgets/Container';
Expand Down
4 changes: 2 additions & 2 deletions src/Pim/PimMain.tsx
Expand Up @@ -13,7 +13,7 @@ import Container from '../widgets/Container';

import SearchableAddressBook from '../components/SearchableAddressBook';
import Calendar from '../components/Calendar';
import TaskList from '../components/TaskList';
import TaskList from '../components/Tasks/TaskList';

import { EventType, ContactType, TaskType } from '../pim-types';

Expand Down Expand Up @@ -64,7 +64,7 @@ class PimMain extends React.PureComponent<PropsType> {
const itemUid = `${(event as any).journalUid}|${event.uid}`;

this.props.history!.push(
routeResolver.getRoute('pim.tasks._id', { itemUid }));
routeResolver.getRoute('pim.tasks._id.edit', { itemUid }));
}

public contactClicked(contact: ContactType) {
Expand Down
31 changes: 19 additions & 12 deletions src/Pim/index.tsx
Expand Up @@ -24,8 +24,8 @@ import ContactEdit from '../components/ContactEdit';
import Contact from '../components/Contact';
import EventEdit from '../components/EventEdit';
import Event from '../components/Event';
import TaskEdit from '../components/TaskEdit';
import Task from '../components/Task';
import TaskEdit from '../components/Tasks/TaskEdit';
import Task from '../components/Tasks/Task';
import PimMain from './PimMain';

import { routeResolver } from '../App';
Expand All @@ -44,14 +44,14 @@ function objValues(obj: any) {
}

const itemsSelector = createSelector(
(props: {syncInfo: SyncInfo}) => props.syncInfo,
(props: { syncInfo: SyncInfo }) => props.syncInfo,
AbleLincoln marked this conversation as resolved.
Show resolved Hide resolved
(syncInfo) => {
const collectionsAddressBook: EteSync.CollectionInfo[] = [];
const collectionsCalendar: EteSync.CollectionInfo[] = [];
const collectionsTaskList: EteSync.CollectionInfo[] = [];
let addressBookItems: {[key: string]: ContactType} = {};
let calendarItems: {[key: string]: EventType} = {};
let taskListItems: {[key: string]: TaskType} = {};
let addressBookItems: { [key: string]: ContactType } = {};
let calendarItems: { [key: string]: EventType } = {};
let taskListItems: { [key: string]: TaskType } = {};
syncInfo.forEach(
(syncJournal) => {
const syncEntries = syncJournal.entries;
Expand Down Expand Up @@ -106,7 +106,7 @@ type CollectionRoutesPropsType = RouteComponentProps<{}> & {
collections: EteSync.CollectionInfo[];
componentEdit: any;
componentView: any;
items: {[key: string]: PimType};
items: { [key: string]: PimType };
onItemSave: (item: PimType, journalUid: string, originalContact?: PimType) => void;
onItemDelete: (item: PimType, journalUid: string) => void;
onItemCancel: () => void;
Expand All @@ -115,10 +115,10 @@ type CollectionRoutesPropsType = RouteComponentProps<{}> & {

const styles = (theme: any) => ({
button: {
marginLeft: theme.spacing.unit,
marginLeft: theme.spacing(1),
AbleLincoln marked this conversation as resolved.
Show resolved Hide resolved
},
leftIcon: {
marginRight: theme.spacing.unit,
marginRight: theme.spacing(1),
},
});

Expand Down Expand Up @@ -191,7 +191,7 @@ const CollectionRoutes = withStyles(styles)(withRouter(
}
>
<IconChangeHistory className={classes.leftIcon} />
Change History
Change History
</Button>

<Button
Expand Down Expand Up @@ -220,6 +220,11 @@ const CollectionRoutes = withStyles(styles)(withRouter(
}
));

export const PimContext = React.createContext({
onItemSave: (_item: PimType, _journalUid: string, _originalEvent?: PimType, _goBack = true) => { return },
collectionsTaskList: [] as EteSync.CollectionInfo[],
});

AbleLincoln marked this conversation as resolved.
Show resolved Hide resolved
class Pim extends React.PureComponent {
public props: {
etesync: CredentialsData;
Expand All @@ -235,7 +240,7 @@ class Pim extends React.PureComponent {
this.onItemSave = this.onItemSave.bind(this);
}

public onItemSave(item: PimType, journalUid: string, originalEvent?: PimType) {
public onItemSave(item: PimType, journalUid: string, originalEvent?: PimType, goBack = true) {
AbleLincoln marked this conversation as resolved.
Show resolved Hide resolved
const syncJournal = this.props.syncInfo.get(journalUid);

if (syncJournal === undefined) {
Expand Down Expand Up @@ -265,7 +270,7 @@ class Pim extends React.PureComponent {
this.props.etesync, this.props.userInfo, journal,
prevUid, action, item.toIcal()));
(saveEvent as any).then(() => {
this.props.history.goBack();
if (goBack) { this.props.history.goBack() }
});
});
}
Expand Down Expand Up @@ -313,6 +318,7 @@ class Pim extends React.PureComponent {
const { collectionsAddressBook, collectionsCalendar, collectionsTaskList, addressBookItems, calendarItems, taskListItems } = itemsSelector(this.props);

return (
<PimContext.Provider value={{ onItemSave: this.onItemSave, collectionsTaskList }}>
<Switch>
<Route
path={routeResolver.getRoute('pim')}
Expand Down Expand Up @@ -375,6 +381,7 @@ class Pim extends React.PureComponent {
)}
/>
</Switch>
</PimContext.Provider>
);
}
}
Expand Down
71 changes: 0 additions & 71 deletions src/components/TaskList.tsx

This file was deleted.

50 changes: 50 additions & 0 deletions src/components/Tasks/AddNewTaskItem.tsx
@@ -0,0 +1,50 @@
import React, { useState, useContext } from 'react';
AbleLincoln marked this conversation as resolved.
Show resolved Hide resolved

import ICAL from 'ical.js';
import uuid from 'uuid';

import Checkbox from '@material-ui/core/Checkbox';
import TextField from '@material-ui/core/TextField';

import { TaskType, TaskStatusType, TaskPriorityType } from '../../pim-types';
import { ListItem } from '../../widgets/List';
import { PimContext } from '../../Pim';

const AddNewTaskItem = () => {
const [title, setTitle] = useState('');
const { onItemSave: save, collectionsTaskList: collections } = useContext(PimContext);

const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setTitle(e.target.value);
};

const handleKeyPress = (e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.key === 'Enter') {
e.preventDefault();

const event = new TaskType(null);
AbleLincoln marked this conversation as resolved.
Show resolved Hide resolved
event.uid = uuid.v4();
event.title = title;
event.status = TaskStatusType.NeedsAction;
event.priority = TaskPriorityType.None;
AbleLincoln marked this conversation as resolved.
Show resolved Hide resolved
event.lastModified = ICAL.Time.now();

save(event, collections[0].uid, undefined, false);
AbleLincoln marked this conversation as resolved.
Show resolved Hide resolved

setTitle('');
}
};

return (
<ListItem leftIcon={<Checkbox disabled />}>
<TextField
label="New task"
value={title}
onChange={handleChange}
onKeyPress={handleKeyPress}
/>
</ListItem>
);
};

export default AddNewTaskItem;
32 changes: 32 additions & 0 deletions src/components/Tasks/ColoredRadio.tsx
@@ -0,0 +1,32 @@
import React from 'react';
AbleLincoln marked this conversation as resolved.
Show resolved Hide resolved

import { makeStyles } from '@material-ui/core/styles';
import * as colors from '@material-ui/core/colors';
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;
label: string;
}

const useStyles = makeStyles({
root: {
color: (props: Props) => colors[props.color][600],
AbleLincoln marked this conversation as resolved.
Show resolved Hide resolved
},
});

const ColoredRadio = (props: Props & Omit<FormControlLabelProps, keyof Props | 'control'>) => {
const { color, label, value, ...other } = props;
const { root } = useStyles(props);

return <FormControlLabel
className={root}
label={label}
control={<Radio color="default" className={root} value={value} />}
{...other}
/>;
};

export default ColoredRadio;
81 changes: 81 additions & 0 deletions src/components/Tasks/Sidebar.tsx
@@ -0,0 +1,81 @@
import React from 'react';

import { useSelector, useDispatch } from 'react-redux';

import List from '@material-ui/core/List';
import ListSubheader from '@material-ui/core/ListSubheader';
import ListItem from '@material-ui/core/ListItem';
import ListItemText from '@material-ui/core/ListItemText';
import ListItemIcon from '@material-ui/core/ListItemIcon';
import { IconProps } from '@material-ui/core/Icon';
import TodayIcon from '@material-ui/icons/Today';
import LabelIcon from '@material-ui/icons/LabelOutlined';

import { setSettings } from '../../store/actions';
import { StoreState } from '../../store';

interface ListItemPropsType {
name: string;
Icon?: React.ComponentType<IconProps>;
primaryText: string;
secondaryText?: string;
}

const SidebarListItem = (props: ListItemPropsType) => {
const { name, Icon, primaryText, secondaryText } = props;
const dispatch = useDispatch();
const settings = useSelector((state: StoreState) => state.settings.tasks);
const { filterBy } = settings;

const handleClick = () => {
dispatch(setSettings({ tasks: { ...settings, filterBy: name } }));
};

return (
<ListItem
button
onClick={handleClick}
selected={name === filterBy}
>
{Icon ?
<ListItemIcon>
<Icon fontSize="small" />
</ListItemIcon> :
''
}
<ListItemText primary={primaryText} secondary={secondaryText || ''} />
</ListItem>
);
};

const Sidebar = (props: { tags: any }) => {
const { tags } = props;

const settings = useSelector((state: StoreState) => state.settings.tasks);
const { filterBy, searchTerm } = settings;

const tagsList = Object.entries(tags).map(([tag, amount]) => (
<SidebarListItem
key={tag}
name={tag}
primaryText={tag}
secondaryText={String(amount)}
Icon={LabelIcon} />
));

return (
<List dense>
{filterBy === 'search' && <SidebarListItem name="search" primaryText={`Search "${searchTerm}"`} />}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This feels wrong to me. "Search" shouldn't be a filtering option, because you should be able to e.g. "show/hide deleted" + search at the same time. This can easily be solved by having search be a special page (I made another comment somewhere that shared my thoughts on this).


<SidebarListItem name="all" primaryText="View all tasks" />

<ListSubheader>Filter</ListSubheader>
<SidebarListItem name="today" primaryText="Today" Icon={TodayIcon} />

<ListSubheader>Tags</ListSubheader>
{tagsList}
</List>
);
};

export default React.memo(Sidebar);