Tasks: implement sorting

Merge of #104
master
Andrew P Maney 5 years ago committed by GitHub
parent b69b51f558
commit 1de7a2ebdd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -3,8 +3,6 @@
import * as React from 'react';
import { createSelector } from 'reselect';
import * as EteSync from 'etesync';
import { List } from '../../widgets/List';
@ -22,10 +20,72 @@ import Toolbar from './Toolbar';
import { StoreState } from '../../store';
const sortSelector = createSelector(
(entries: TaskType[]) => entries,
(entries) => entries.sort((a, b) => a.title.localeCompare(b.title))
);
function sortCompleted(a: TaskType, b: TaskType) {
return (!!a.finished === !!b.finished) ? 0 : (a.finished) ? 1 : -1;
}
function sortLastModifiedDate(aIn: TaskType, bIn: TaskType) {
const a = aIn.lastModified?.toJSDate() ?? new Date(0);
const b = bIn.lastModified?.toJSDate() ?? new Date(0);
return (a > b) ? -1 : (a < b) ? 1 : 0;
}
function sortDueDate(aIn: TaskType, bIn: TaskType) {
const impossiblyLargeDate = 8640000000000000;
const a = aIn.dueDate?.toJSDate() ?? new Date(impossiblyLargeDate);
const b = bIn.dueDate?.toJSDate() ?? new Date(impossiblyLargeDate);
return (a < b) ? -1 : (a > b) ? 1 : 0;
}
function sortPriority(aIn: TaskType, bIn: TaskType) {
// Intentionally converts 0/undefined to Infinity to sort to back of the list
const a = aIn.priority || Infinity;
const b = bIn.priority || Infinity;
return a - b;
}
function sortTitle(aIn: TaskType, bIn: TaskType) {
const a = aIn.title ?? '';
const b = bIn.title ?? '';
return a.localeCompare(b);
}
function getSortFunction(sortOrder: string) {
const sortFunctions: (typeof sortTitle)[] = [sortCompleted];
switch (sortOrder) {
case 'smart':
sortFunctions.push(sortPriority);
sortFunctions.push(sortDueDate);
sortFunctions.push(sortTitle);
break;
case 'dueDate':
sortFunctions.push(sortDueDate);
break;
case 'priority':
sortFunctions.push(sortPriority);
break;
case 'title':
sortFunctions.push(sortTitle);
break;
case 'lastModifiedDate':
// Do nothing because it's the last sort function anyway
break;
}
sortFunctions.push(sortLastModifiedDate);
return (a: TaskType, b: TaskType) => {
for (const sortFunction of sortFunctions) {
const ret = sortFunction(a, b);
if (ret !== 0) {
return ret;
}
}
return 0;
};
}
interface PropsType {
entries: TaskType[];
@ -37,7 +97,7 @@ interface PropsType {
export default function TaskList(props: PropsType) {
const [showCompleted, setShowCompleted] = React.useState(false);
const settings = useSelector((state: StoreState) => state.settings.taskSettings);
const { filterBy } = settings;
const { filterBy, sortBy } = settings;
const theme = useTheme();
const potentialEntries = React.useMemo(
@ -57,7 +117,7 @@ export default function TaskList(props: PropsType) {
entries = potentialEntries;
}
const sortedEntries = sortSelector(entries);
const sortedEntries = entries.sort(getSortFunction(sortBy));
const itemList = sortedEntries.map((entry) => {
const uid = entry.uid;

@ -8,11 +8,17 @@ import IconButton from '@material-ui/core/IconButton';
import MoreVertIcon from '@material-ui/icons/MoreVert';
import Menu from '@material-ui/core/Menu';
import MenuItem from '@material-ui/core/MenuItem';
import SortIcon from '@material-ui/icons/Sort';
import QuickAdd from './QuickAdd';
import { PimType } from '../../pim-types';
import { useSelector, useDispatch } from 'react-redux';
import { setSettings } from '../../store/actions';
import { StoreState } from '../../store';
interface PropsType {
defaultCollection: EteSync.CollectionInfo;
onItemSave: (item: PimType, journalUid: string, originalItem?: PimType) => Promise<void>;
@ -23,16 +29,24 @@ interface PropsType {
export default function Toolbar(props: PropsType) {
const { defaultCollection, onItemSave, showCompleted, setShowCompleted } = props;
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
const [sortAnchorEl, setSortAnchorEl] = React.useState<null | HTMLElement>(null);
const [optionsAnchorEl, setOptionsAnchorEl] = React.useState<null | HTMLElement>(null);
const handleClick = (event: React.MouseEvent<HTMLElement>) => {
setAnchorEl(event.currentTarget);
};
const dispatch = useDispatch();
const taskSettings = useSelector((state: StoreState) => state.settings.taskSettings);
const { sortBy } = taskSettings;
const handleClose = () => {
setAnchorEl(null);
const handleSortChange = (sort: string) => {
dispatch(setSettings({ taskSettings: { ...taskSettings, sortBy: sort } }));
setSortAnchorEl(null);
};
const SortMenuItem = React.forwardRef(function SortMenuItem(props: { name: string, label: string }, ref) {
return (
<MenuItem innerRef={ref} selected={sortBy === props.name} onClick={() => handleSortChange(props.name)}>{props.label}</MenuItem>
);
});
return (
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
{defaultCollection && <QuickAdd style={{ flexGrow: 1, marginRight: '0.75em' }} onSubmit={onItemSave} defaultCollection={defaultCollection} />}
@ -42,16 +56,40 @@ export default function Toolbar(props: PropsType) {
aria-label="more"
aria-controls="long-menu"
aria-haspopup="true"
onClick={handleClick}
onClick={(e) => setSortAnchorEl(e.currentTarget)}
>
<SortIcon />
</IconButton>
<Menu
anchorEl={sortAnchorEl}
keepMounted
open={!!sortAnchorEl}
onClose={() => setSortAnchorEl(null)}
>
<SortMenuItem name="smart" label="Smart" />
<SortMenuItem name="dueDate" label="Due Date" />
<SortMenuItem name="priority" label="Priority" />
<SortMenuItem name="title" label="Title" />
<SortMenuItem name="lastModifiedDate" label="Last Modified" />
</Menu>
</div>
<div>
<IconButton
aria-label="more"
aria-controls="long-menu"
aria-haspopup="true"
onClick={(e) => setOptionsAnchorEl(e.currentTarget)}
>
<MoreVertIcon />
</IconButton>
<Menu
id="simple-menu"
anchorEl={anchorEl}
anchorEl={optionsAnchorEl}
keepMounted
open={!!anchorEl}
onClose={handleClose}
open={!!optionsAnchorEl}
onClose={() => setOptionsAnchorEl(null)}
>
<MenuItem>
<FormControlLabel

@ -28,9 +28,23 @@ export interface StoreState {
errors: List<Error>;
}
const settingsMigrations = {
0: (state: any) => {
return {
...state,
taskSettings: {
filterBy: null,
sortBy: 'smart',
},
};
},
};
const settingsPersistConfig = {
key: 'settings',
version: 0,
storage: localforage,
migrate: createMigrate(settingsMigrations, { debug: false }),
};
const credentialsMigrations = {

@ -274,6 +274,7 @@ export interface SettingsType {
locale: string;
taskSettings: {
filterBy: string | null;
sortBy: string;
};
}
@ -287,6 +288,7 @@ export const settingsReducer = handleActions(
locale: 'en-gb',
taskSettings: {
filterBy: null,
sortBy: 'smart',
},
}
);

Loading…
Cancel
Save