Skip to content

Commit

Permalink
fix status filter selection to behave more like linear when in specif…
Browse files Browse the repository at this point in the history
…ic views (backlog, active)
  • Loading branch information
tantaman committed Dec 15, 2023
1 parent 8c6374c commit 4da8271
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 35 deletions.
23 changes: 16 additions & 7 deletions client/src/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import {
getStatusFilter,
getViewFilter,
getViewStatuses,
hasNonViewFilters as doesHaveNonViewFilters,
} from './filters';

type AppProps = {
Expand Down Expand Up @@ -95,6 +96,7 @@ const App = ({rep, undoManager}: AppProps) => {
const [createdFilter] = useCreatedFilterState();
const [creatorFilter] = useCreatorFilterState();
const [modifiedFilter] = useModifiedFilterState();
const [hasNonViewFilters, setHasNonViewFilters] = useState(false);

const [issueOrder, setIssueOrder] = useState(getIssueOrder(view, orderBy));

Expand All @@ -104,14 +106,21 @@ const App = ({rep, undoManager}: AppProps) => {

const viewStatuses = getViewStatuses(view);
const statuses = getStatuses(statusFilter);
const statusFilterFn = getStatusFilter(viewStatuses, statuses);
const filterFns = [
getStatusFilter(viewStatuses, statuses),
statusFilterFn,
getPriorityFilter(getPriorities(priorityFilter)),
getCreatorFilter(getCreators(creatorFilter)),
getCreatedFilter(createdFilter),
getModifiedFilter(modifiedFilter),
];

const hasNonViewFilters = !!(
doesHaveNonViewFilters(viewStatuses, statuses) ||
filterFns.filter(f => f !== null && f !== statusFilterFn).length > 0
);
setHasNonViewFilters(hasNonViewFilters);

let {stream} = source;
for (const filter of filterFns) {
if (filter !== null) {
Expand All @@ -125,11 +134,11 @@ const App = ({rep, undoManager}: AppProps) => {
}, [
view,
issueOrder,
priorityFilter,
statusFilter,
createdFilter,
creatorFilter,
modifiedFilter,
priorityFilter?.join(),
statusFilter?.join(),
createdFilter?.join(),
creatorFilter?.join(),
modifiedFilter?.join(),
]);

const [, viewIssueCount] = useQuery(() => {
Expand Down Expand Up @@ -283,7 +292,7 @@ const App = ({rep, undoManager}: AppProps) => {
viewIssueCount={viewIssueCount || 0}
filteredIssues={filterdIssues}
rep={rep}
hasNonViewFilters={false}
hasNonViewFilters={hasNonViewFilters}
onCloseMenu={handleCloseMenu}
onToggleMenu={handleToggleMenu}
onUpdateIssues={handleUpdateIssues}
Expand Down
18 changes: 12 additions & 6 deletions client/src/filters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export function hasNonViewFilters(
statuses: Set<Status>,
) {
for (const s of statuses) {
if (!viewStatuses.has(s)) {
if (viewStatuses.has(s)) {
return true;
}
}
Expand Down Expand Up @@ -50,11 +50,11 @@ export function getStatusFilter(
viewStatuses: Set<Status>,
statuses: Set<Status>,
): null | ((issue: Issue) => boolean) {
const allStatuses = new Set<Status>([...viewStatuses, ...statuses]);
if (allStatuses.size === 0) {
const filterStatuses = statuses.size === 0 ? viewStatuses : statuses;
if (filterStatuses.size === 0) {
return null;
}
return issue => allStatuses.has(issue.status);
return issue => filterStatuses.has(issue.status);
}

export function getCreatorFilter(
Expand All @@ -75,13 +75,19 @@ export function getViewFilter(

export function getModifiedFilter(
args: DateQueryArg[] | null,
): (issue: Issue) => boolean {
): ((issue: Issue) => boolean) | null {
if (args === null) {
return null;
}
return createTimeFilter('modified', args);
}

export function getCreatedFilter(
args: DateQueryArg[] | null,
): (issue: Issue) => boolean {
): null | ((issue: Issue) => boolean) {
if (args === null) {
return null;
}
return createTimeFilter('created', args);
}

Expand Down
25 changes: 22 additions & 3 deletions client/src/layout/left-menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,14 @@ import IssueModal from '../issue/issue-modal';
import ReactLogo from '../assets/images/logo.svg?react';
import AboutModal from '../about-modal';
import {noop} from 'lodash';
import {useIssueDetailState, useViewState} from '../hooks/query-state-hooks';
import {
useIssueDetailState,
useStatusFilterState,
useViewState,
} from '../hooks/query-state-hooks';
import useQueryState, {identityProcessor} from '../hooks/useQueryState';
import {Description, Issue} from 'shared';
import {getViewStatuses} from '../filters';

interface Props {
// Show menu (for small screen only)
Expand All @@ -32,6 +37,7 @@ const LeftMenu = ({menuVisible, onCloseMenu = noop, onCreateIssue}: Props) => {
const ref = useRef<HTMLDivElement>() as RefObject<HTMLDivElement>;
const [issueModalVisible, setIssueModalVisible] = useState(false);
const [aboutModalVisible, setAboutModalVisible] = useState(true);
const [statusFilter, setStatusFilter] = useStatusFilterState();

const classes = classnames(
'absolute lg:static inset-0 lg:relative lg:translate-x-0 flex flex-col flex-shrink-0 w-56 font-sans text-sm border-r lg:shadow-none justify-items-start bg-gray border-gray-850 text-white bg-opacity-1',
Expand All @@ -49,6 +55,11 @@ const LeftMenu = ({menuVisible, onCloseMenu = noop, onCreateIssue}: Props) => {
}
});

function pruneStatuses(view: string) {
const viewStatuses = getViewStatuses(view);
return statusFilter?.filter(s => viewStatuses.has(s)) ?? null;
}

return (
<>
<div className={classes} ref={ref}>
Expand Down Expand Up @@ -108,7 +119,11 @@ const LeftMenu = ({menuVisible, onCloseMenu = noop, onCreateIssue}: Props) => {
<div
className="flex items-center pl-9 rounded cursor-pointer group h-8 hover:bg-gray-900"
onMouseDown={async () => {
await Promise.all([setView('active'), setIss(null)]);
await Promise.all([
setView('active'),
setStatusFilter(pruneStatuses('active')),
setIss(null),
]);
onCloseMenu && onCloseMenu();
}}
>
Expand All @@ -118,7 +133,11 @@ const LeftMenu = ({menuVisible, onCloseMenu = noop, onCreateIssue}: Props) => {
<div
className="flex items-center pl-9 rounded cursor-pointer group h-8 hover:bg-gray-900"
onMouseDown={async () => {
await Promise.all([setView('backlog'), setIss(null)]);
await Promise.all([
setView('backlog'),
setStatusFilter(pruneStatuses('backlog')),
setIss(null),
]);
onCloseMenu && onCloseMenu();
}}
>
Expand Down
45 changes: 26 additions & 19 deletions client/src/widgets/filter-menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@ import {Filter} from '../issue/issue';
import {useClickOutside} from '../hooks/useClickOutside';
import SignalStrongIcon from '../assets/icons/signal-strong.svg?react';
import TodoIcon from '../assets/icons/circle.svg?react';
import UserIcon from '../assets/icons/avatar.svg?react';
import DateIcon from '../assets/icons/due-date.svg?react';
import {statusOpts} from './priority-menu';
import {statuses} from './status-menu';
import {Priority, Status} from 'shared';
import UserIcon from './assets/icons/avatar.svg';
import DateIcon from './assets/icons/due-date.svg';
import useId from '@mui/utils/useId';
import {useViewState} from '../hooks/query-state-hooks';
import {getViewStatuses} from '../filters';

type DateCallback = (date: Date) => void;
interface Props {
Expand All @@ -35,6 +37,7 @@ const FilterMenu = ({
const [popperRef, setPopperRef] = useState<HTMLDivElement | null>(null);
const [filter, setFilter] = useState<Filter | null>(null);
const [filterDropDownVisible, setFilterDropDownVisible] = useState(false);
const [view] = useViewState();

const {styles, attributes, update} = usePopper(filterRef, popperRef, {
placement: 'bottom-start',
Expand Down Expand Up @@ -83,23 +86,27 @@ const FilterMenu = ({
);
});

case Filter.STATUS:
return statuses.map(([Icon, status, label], idx) => {
return (
<div
key={idx}
className="flex items-center h-8 px-3 text-gray focus:outline-none hover:text-gray-800 hover:bg-gray-300"
onMouseDown={() => {
onSelectStatus(status as Status);
setFilter(null);
setFilterDropDownVisible(false);
}}
>
<Icon className="mr-4" />
{label}
</div>
);
});
case Filter.STATUS: {
const viewStatuses = getViewStatuses(view);
return statuses
.filter(s => viewStatuses.size === 0 || viewStatuses.has(s[1]))
.map(([Icon, status, label], idx) => {
return (
<div
key={idx}
className="flex items-center h-8 px-3 text-gray focus:outline-none hover:text-gray-800 hover:bg-gray-300"
onMouseDown={() => {
onSelectStatus(status as Status);
setFilter(null);
setFilterDropDownVisible(false);
}}
>
<Icon className="mr-4" />
{label}
</div>
);
});
}
case Filter.CREATOR:
return [
<CreatorInput
Expand Down

0 comments on commit 4da8271

Please sign in to comment.