parent
ec9d8d3329
commit
effd74e0b2
|
@ -0,0 +1,62 @@
|
|||
import * as React from 'react';
|
||||
|
||||
import { useSelector, useDispatch } from 'react-redux';
|
||||
|
||||
import InboxIcon from '@material-ui/icons/Inbox';
|
||||
import LabelIcon from '@material-ui/icons/LabelOutlined';
|
||||
|
||||
import { setSettings } from '../../store/actions';
|
||||
import { StoreState } from '../../store';
|
||||
|
||||
import { List, ListItem, ListSubheader } from '../../widgets/List';
|
||||
|
||||
interface ListItemPropsType {
|
||||
name: string | null;
|
||||
icon?: React.ReactElement;
|
||||
primaryText: string;
|
||||
amount?: number;
|
||||
}
|
||||
|
||||
function SidebarListItem(props: ListItemPropsType) {
|
||||
const { name, icon, primaryText, amount } = props;
|
||||
const dispatch = useDispatch();
|
||||
const taskSettings = useSelector((state: StoreState) => state.settings.taskSettings);
|
||||
const { filterBy } = taskSettings;
|
||||
|
||||
const handleClick = () => {
|
||||
dispatch(setSettings({ taskSettings: { ...taskSettings, filterBy: name } }));
|
||||
};
|
||||
|
||||
return (
|
||||
<ListItem
|
||||
onClick={handleClick}
|
||||
selected={name === filterBy}
|
||||
leftIcon={icon}
|
||||
rightIcon={<span style={{ width: '100%', textAlign: 'right' }}>{amount}</span>}
|
||||
primaryText={primaryText}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default function Sidebar(props: { tags: Map<string, number>, totalTasks: number }) {
|
||||
const { tags, totalTasks } = props;
|
||||
|
||||
const tagsList = [...tags].sort(([a], [b]) => a.localeCompare(b)).map(([tag, amount]) => (
|
||||
<SidebarListItem
|
||||
key={tag}
|
||||
name={`tag:${tag}`}
|
||||
primaryText={tag}
|
||||
icon={<LabelIcon />}
|
||||
amount={amount}
|
||||
/>
|
||||
));
|
||||
|
||||
return (
|
||||
<List dense>
|
||||
<SidebarListItem name={null} primaryText="All" icon={<InboxIcon />} amount={totalTasks} />
|
||||
|
||||
<ListSubheader>Tags</ListSubheader>
|
||||
{tagsList}
|
||||
</List>
|
||||
);
|
||||
}
|
|
@ -13,9 +13,16 @@ import { TaskType, PimType } from '../../pim-types';
|
|||
import FormControlLabel from '@material-ui/core/FormControlLabel';
|
||||
import Checkbox from '@material-ui/core/Checkbox';
|
||||
import Divider from '@material-ui/core/Divider';
|
||||
import Grid from '@material-ui/core/Grid';
|
||||
import { useTheme } from '@material-ui/core/styles';
|
||||
|
||||
import { useSelector } from 'react-redux';
|
||||
|
||||
import TaskListItem from './TaskListItem';
|
||||
import QuickAdd from './QuickAdd';
|
||||
import Sidebar from './Sidebar';
|
||||
|
||||
import { StoreState } from '../../store';
|
||||
|
||||
const sortSelector = createSelector(
|
||||
(entries: TaskType[]) => entries,
|
||||
|
@ -31,7 +38,26 @@ interface PropsType {
|
|||
|
||||
export default React.memo(function TaskList(props: PropsType) {
|
||||
const [showCompleted, setShowCompleted] = React.useState(false);
|
||||
const entries = props.entries.filter((x) => showCompleted || !x.finished);
|
||||
const settings = useSelector((state: StoreState) => state.settings.taskSettings);
|
||||
const { filterBy } = settings;
|
||||
const theme = useTheme();
|
||||
|
||||
const potentialEntries = props.entries.filter((x) => showCompleted || !x.finished);
|
||||
let entries;
|
||||
const tagPrefix = 'tag:';
|
||||
if (filterBy?.startsWith(tagPrefix)) {
|
||||
const tag = filterBy.slice(tagPrefix.length);
|
||||
entries = potentialEntries.filter((x) => x.tags.includes(tag));
|
||||
} else {
|
||||
entries = potentialEntries;
|
||||
}
|
||||
|
||||
// TODO: memoize
|
||||
const tags = new Map<string, number>();
|
||||
potentialEntries.forEach((entry) => entry.tags.forEach((tag) => {
|
||||
tags.set(tag, (tags.get(tag) ?? 0) + 1);
|
||||
}));
|
||||
|
||||
const sortedEntries = sortSelector(entries);
|
||||
|
||||
const itemList = sortedEntries.map((entry) => {
|
||||
|
@ -48,23 +74,29 @@ export default React.memo(function TaskList(props: PropsType) {
|
|||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<Grid container spacing={4}>
|
||||
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
|
||||
{props.collections && <QuickAdd onSubmit={props.onItemSave} defaultCollection={props.collections[0]} />}
|
||||
<Grid item xs={3} style={{ borderRight: `1px solid ${theme.palette.divider}` }}>
|
||||
<Sidebar totalTasks={potentialEntries.length} tags={tags} />
|
||||
</Grid>
|
||||
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Checkbox checked={showCompleted} onChange={() => setShowCompleted(!showCompleted)} />
|
||||
}
|
||||
label="Show Completed"
|
||||
/>
|
||||
</div>
|
||||
<Grid item xs>
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
|
||||
{props.collections && <QuickAdd onSubmit={props.onItemSave} defaultCollection={props.collections[0]} />}
|
||||
|
||||
<Divider style={{ marginTop: '1em' }} />
|
||||
<List>
|
||||
{itemList}
|
||||
</List>
|
||||
</>
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Checkbox checked={showCompleted} onChange={() => setShowCompleted(!showCompleted)} />
|
||||
}
|
||||
label="Show Completed"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Divider style={{ marginTop: '1em' }} />
|
||||
<List>
|
||||
{itemList}
|
||||
</List>
|
||||
</Grid>
|
||||
</Grid>
|
||||
);
|
||||
});
|
||||
|
|
|
@ -28,13 +28,13 @@ export type UserInfoData = EteSync.UserInfo;
|
|||
|
||||
export const encryptionKeyReducer = handleActions(
|
||||
{
|
||||
[actions.deriveKey.toString()]: (_state: {key: string | null}, action: any) => (
|
||||
[actions.deriveKey.toString()]: (_state: { key: string | null }, action: any) => (
|
||||
{ key: action.payload }
|
||||
),
|
||||
[actions.resetKey.toString()]: (_state: {key: string | null}, _action: any) => (
|
||||
[actions.resetKey.toString()]: (_state: { key: string | null }, _action: any) => (
|
||||
{ key: null }
|
||||
),
|
||||
[actions.logout.toString()]: (_state: {key: string | null}, _action: any) => {
|
||||
[actions.logout.toString()]: (_state: { key: string | null }, _action: any) => {
|
||||
return { out: true, key: null };
|
||||
},
|
||||
},
|
||||
|
@ -272,6 +272,9 @@ export const errorsReducer = handleActions(
|
|||
// FIXME Move all the below (potentially the fetchCount ones too) to their own file
|
||||
export interface SettingsType {
|
||||
locale: string;
|
||||
taskSettings: {
|
||||
filterBy: string | null;
|
||||
};
|
||||
}
|
||||
|
||||
export const settingsReducer = handleActions(
|
||||
|
@ -280,5 +283,10 @@ export const settingsReducer = handleActions(
|
|||
{ ...state, ...action.payload }
|
||||
),
|
||||
},
|
||||
{ locale: 'en-gb' }
|
||||
{
|
||||
locale: 'en-gb',
|
||||
taskSettings: {
|
||||
filterBy: null,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
|
|
@ -45,6 +45,7 @@ interface ListItemPropsType {
|
|||
href?: string;
|
||||
insetChildren?: boolean;
|
||||
nestedItems?: React.ReactNode[];
|
||||
selected?: boolean;
|
||||
}
|
||||
|
||||
export const ListItem = React.memo(function ListItem(_props: ListItemPropsType) {
|
||||
|
@ -60,6 +61,7 @@ export const ListItem = React.memo(function ListItem(_props: ListItemPropsType)
|
|||
style,
|
||||
insetChildren,
|
||||
nestedItems,
|
||||
selected,
|
||||
} = _props;
|
||||
|
||||
const extraProps = (onClick || href) ? {
|
||||
|
@ -74,6 +76,7 @@ export const ListItem = React.memo(function ListItem(_props: ListItemPropsType)
|
|||
<MuiListItem
|
||||
style={style}
|
||||
onClick={onClick}
|
||||
selected={selected}
|
||||
{...(extraProps as any)}
|
||||
>
|
||||
{leftIcon && (
|
||||
|
|
Loading…
Reference in New Issue