From c73416bdb8229f3efb799f32ab9bb743a9ceb02d Mon Sep 17 00:00:00 2001 From: Jack Date: Sat, 27 May 2023 16:15:09 +0200 Subject: [PATCH] [Feature] Hide category (#1060) --- .../src/components/budget/index.js | 17 +- .../src/components/budget/misc.js | 146 ++++++++++++++++-- .../1685007876842_add_category_hidden.sql | 6 + .../loot-core/src/server/aql/schema/index.ts | 2 + upcoming-release-notes/1060.md | 6 + 5 files changed, 159 insertions(+), 18 deletions(-) create mode 100644 packages/loot-core/migrations/1685007876842_add_category_hidden.sql create mode 100644 upcoming-release-notes/1060.md diff --git a/packages/desktop-client/src/components/budget/index.js b/packages/desktop-client/src/components/budget/index.js index 602b071a2e..5367edec47 100644 --- a/packages/desktop-client/src/components/budget/index.js +++ b/packages/desktop-client/src/components/budget/index.js @@ -229,10 +229,14 @@ class Budget extends PureComponent { }), }); } else { - this.props.updateCategory(category); + const cat = { + ...category, + hidden: category.hidden ? 1 : 0, + }; + this.props.updateCategory(cat); this.setState({ - categoryGroups: updateCategory(categoryGroups, category), + categoryGroups: updateCategory(categoryGroups, cat), }); } }; @@ -278,9 +282,14 @@ class Budget extends PureComponent { }), }); } else { - this.props.updateGroup(group); + const grp = { + ...group, + hidden: group.hidden ? 1 : 0, + }; + + this.props.updateGroup(grp); this.setState({ - categoryGroups: updateGroup(categoryGroups, group), + categoryGroups: updateGroup(categoryGroups, grp), }); } }; diff --git a/packages/desktop-client/src/components/budget/misc.js b/packages/desktop-client/src/components/budget/misc.js index c3f569fb0c..5f1554de3f 100644 --- a/packages/desktop-client/src/components/budget/misc.js +++ b/packages/desktop-client/src/components/budget/misc.js @@ -6,7 +6,11 @@ import React, { useState, useMemo, } from 'react'; +import { connect } from 'react-redux'; +import { bindActionCreators } from 'redux'; + +import * as actions from 'loot-core/src/client/actions'; import * as monthUtils from 'loot-core/src/shared/months'; import useResizeObserver from '../../hooks/useResizeObserver'; @@ -14,6 +18,7 @@ import ExpandArrow from '../../icons/v0/ExpandArrow'; import ArrowThinLeft from '../../icons/v1/ArrowThinLeft'; import ArrowThinRight from '../../icons/v1/ArrowThinRight'; import CheveronDown from '../../icons/v1/CheveronDown'; +import DotsHorizontalTriple from '../../icons/v1/DotsHorizontalTriple'; import { styles, colors } from '../../style'; import { View, @@ -42,7 +47,7 @@ function getScrollbarWidth() { return Math.max(styles.scrollbarWidth - 2, 0); } -export class BudgetTable extends Component { +class BudgetTable extends Component { constructor(props) { super(props); this.budgetCategoriesRef = createRef(); @@ -50,6 +55,7 @@ export class BudgetTable extends Component { this.state = { editing: null, draggingState: null, + showHiddenCategories: props.prefs['budget.showHiddenCategories'] ?? false, }; } @@ -166,6 +172,15 @@ export class BudgetTable extends Component { this.setState({ editing: null }); } + toggleHiddenCategories = () => { + this.setState(prevState => ({ + showHiddenCategories: !prevState.showHiddenCategories, + })); + this.props.savePrefs({ + 'budget.showHiddenCategories': !this.state.showHiddenCategories, + }); + }; + render() { let { type, @@ -188,7 +203,7 @@ export class BudgetTable extends Component { onShowNewGroup, onHideNewGroup, } = this.props; - let { editing, draggingState } = this.state; + let { editing, draggingState, showHiddenCategories } = this.state; return ( - + (this.budgetDataNode = el)} > ({ + prefs: state.prefs.local, + }), + dispatch => bindActionCreators(actions, dispatch), + null, + { forwardRef: true }, +)(BudgetTable); + +export { connected as BudgetTable }; + export function SidebarCategory({ innerRef, category, @@ -313,6 +343,7 @@ export function SidebarCategory({ alignItems: 'center', userSelect: 'none', WebkitUserSelect: 'none', + opacity: category.hidden ? 0.33 : undefined, }} >
{ if (type === 'rename') { onEditName(category.id); - } else { + } else if (type === 'delete') { onDelete(category.id); + } else if (type === 'toggleVisibility') { + onSave({ ...category, hidden: !category.hidden }); } setMenuOpen(false); }} items={[ + { + name: 'toggleVisibility', + text: category.hidden ? 'Show' : 'Hide', + }, { name: 'rename', text: 'Rename' }, { name: 'delete', text: 'Delete' }, ]} @@ -513,13 +550,19 @@ export function SidebarGroup({ onEdit(group.id); } else if (type === 'add-category') { onShowNewCategory(group.id); - } else { + } else if (type === 'delete') { onDelete(group.id); + } else if (type === 'toggleVisibility') { + onSave({ ...group, hidden: !group.hidden }); } setMenuOpen(false); }} items={[ { name: 'add-category', text: 'Add category' }, + { + name: 'toggleVisibility', + text: group.hidden ? 'Show' : 'Hide', + }, { name: 'rename', text: 'Rename' }, onDelete && { name: 'delete', text: 'Delete' }, ]} @@ -612,7 +655,11 @@ function RenderMonths({ component: Component, editingIndex, args, style }) { }); } -const BudgetTotals = memo(function BudgetTotals({ MonthComponent }) { +const BudgetTotals = memo(function BudgetTotals({ + MonthComponent, + toggleHiddenCategories, +}) { + const [menuOpen, setMenuOpen] = useState(false); return ( - Category + Category + @@ -688,7 +779,10 @@ function ExpenseGroup({ {dragState && !dragState.preview && dragState.type === 'group' && ( + { - let items = [{ type: 'expense-group', value: group }]; + if (group.hidden && !showHiddenCategories) { + return []; + } + + const groupCategories = group.categories.filter( + cat => showHiddenCategories || !cat.hidden, + ); + + let items = [{ type: 'expense-group', value: { ...group } }]; if (newCategoryForGroup === group.id) { items.push({ type: 'new-category' }); @@ -951,7 +1061,7 @@ const BudgetCategories = memo( return [ ...items, - ...(collapsed.includes(group.id) ? [] : group.categories).map( + ...(collapsed.includes(group.id) ? [] : groupCategories).map( cat => ({ type: 'expense-category', value: cat, @@ -973,7 +1083,9 @@ const BudgetCategories = memo( newCategoryForGroup === incomeGroup.id && { type: 'new-category' }, ...(collapsed.includes(incomeGroup.id) ? [] - : incomeGroup.categories + : incomeGroup.categories.filter( + cat => showHiddenCategories || !cat.hidden, + ) ).map(cat => ({ type: 'income-category', value: cat, @@ -983,7 +1095,13 @@ const BudgetCategories = memo( } return items; - }, [categoryGroups, collapsed, newCategoryForGroup, isAddingGroup]); + }, [ + categoryGroups, + collapsed, + newCategoryForGroup, + isAddingGroup, + showHiddenCategories, + ]); let [dragState, setDragState] = useState(null); let [savedCollapsed, setSavedCollapsed] = useState(null); diff --git a/packages/loot-core/migrations/1685007876842_add_category_hidden.sql b/packages/loot-core/migrations/1685007876842_add_category_hidden.sql new file mode 100644 index 0000000000..04b4a69d8b --- /dev/null +++ b/packages/loot-core/migrations/1685007876842_add_category_hidden.sql @@ -0,0 +1,6 @@ +BEGIN TRANSACTION; + +ALTER TABLE categories ADD COLUMN hidden BOOLEAN NOT NULL DEFAULT 0; +ALTER TABLE category_groups ADD COLUMN hidden BOOLEAN NOT NULL DEFAULT 0; + +COMMIT; diff --git a/packages/loot-core/src/server/aql/schema/index.ts b/packages/loot-core/src/server/aql/schema/index.ts index 466de6c452..e409b28bed 100644 --- a/packages/loot-core/src/server/aql/schema/index.ts +++ b/packages/loot-core/src/server/aql/schema/index.ts @@ -72,6 +72,7 @@ export const schema = { id: f('id'), name: f('string'), is_income: f('boolean'), + hidden: f('boolean'), group: f('id', { ref: 'category_groups' }), sort_order: f('float'), tombstone: f('boolean'), @@ -80,6 +81,7 @@ export const schema = { id: f('id'), name: f('string'), is_income: f('boolean'), + hidden: f('boolean'), sort_order: f('float'), tombstone: f('boolean'), }, diff --git a/upcoming-release-notes/1060.md b/upcoming-release-notes/1060.md new file mode 100644 index 0000000000..4ad96c9fea --- /dev/null +++ b/upcoming-release-notes/1060.md @@ -0,0 +1,6 @@ +--- +category: Features +authors: [Miodec] +--- + +Added the ability to hide category groups while keeping them in the same category group.