mirror of
https://github.com/actualbudget/actual.git
synced 2026-03-09 03:32:54 -05:00
Ensure Budget Views keep independent ordering
This commit is contained in:
@@ -3,14 +3,16 @@ import { Trans, useTranslation } from 'react-i18next';
|
||||
|
||||
import { Button } from '@actual-app/components/button';
|
||||
import { SvgDelete } from '@actual-app/components/icons/v0';
|
||||
import { SvgAdd, SvgEditPencil, SvgPencilWrite } from '@actual-app/components/icons/v1';
|
||||
import {
|
||||
SvgAdd,
|
||||
SvgEditPencil,
|
||||
SvgPencilWrite,
|
||||
} from '@actual-app/components/icons/v1';
|
||||
import { SpaceBetween } from '@actual-app/components/space-between';
|
||||
import { Text } from '@actual-app/components/text';
|
||||
import { theme } from '@actual-app/components/theme';
|
||||
import { View } from '@actual-app/components/view';
|
||||
|
||||
import { type CategoryEntity } from 'loot-core/types/models';
|
||||
|
||||
import {
|
||||
useDraggable,
|
||||
useDroppable,
|
||||
@@ -19,6 +21,7 @@ import {
|
||||
type OnDropCallback,
|
||||
type DragState,
|
||||
} from '@desktop-client/components/sort';
|
||||
import { useBudgetViews } from '@desktop-client/hooks/useBudgetViews';
|
||||
import { useCategories } from '@desktop-client/hooks/useCategories';
|
||||
import { useDragRef } from '@desktop-client/hooks/useDragRef';
|
||||
import { useSyncedPrefJson } from '@desktop-client/hooks/useSyncedPrefJson';
|
||||
@@ -123,7 +126,8 @@ function BudgetViewItem({
|
||||
export function BudgetViews() {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useDispatch();
|
||||
const { list: categories, grouped: categoryGroups = [] } = useCategories();
|
||||
const { list: categories } = useCategories();
|
||||
const { setViewCategoryOrder, setViewGroupOrder } = useBudgetViews();
|
||||
const [budgetViewMap = {}, setBudgetViewMapPref] = useSyncedPrefJson<
|
||||
'budget.budgetViewMap',
|
||||
Record<string, string[]>
|
||||
@@ -207,6 +211,8 @@ export function BudgetViews() {
|
||||
if (Array.isArray(customViews)) {
|
||||
setCustomViews(customViews.filter((v: BudgetView) => v.id !== viewId));
|
||||
}
|
||||
setViewCategoryOrder(viewId, []);
|
||||
setViewGroupOrder(viewId, []);
|
||||
},
|
||||
[
|
||||
budgetViewMap,
|
||||
@@ -214,6 +220,8 @@ export function BudgetViews() {
|
||||
views,
|
||||
setBudgetViewMapPref,
|
||||
setCustomViews,
|
||||
setViewCategoryOrder,
|
||||
setViewGroupOrder,
|
||||
t,
|
||||
],
|
||||
);
|
||||
|
||||
@@ -95,6 +95,9 @@ function BudgetInner(props: BudgetInnerProps) {
|
||||
string[] | null
|
||||
>(null);
|
||||
const {
|
||||
views = [],
|
||||
viewCategoryOrder = {},
|
||||
viewGroupOrder = {},
|
||||
setViewCategoryOrder,
|
||||
setViewGroupOrder,
|
||||
viewMap = {},
|
||||
@@ -128,6 +131,76 @@ function BudgetInner(props: BudgetInnerProps) {
|
||||
});
|
||||
}, [props.accountId]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!categoryGroups || categoryGroups.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const validGroupIds = new Set(categoryGroups.map(group => group.id));
|
||||
|
||||
Object.entries(viewGroupOrder).forEach(([viewId, order]) => {
|
||||
if (!Array.isArray(order) || order.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const filtered = order.filter(groupId => validGroupIds.has(groupId));
|
||||
|
||||
if (filtered.length !== order.length) {
|
||||
setViewGroupOrder(viewId, filtered);
|
||||
}
|
||||
});
|
||||
}, [categoryGroups, viewGroupOrder, setViewGroupOrder]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!categoryGroups || categoryGroups.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const globalCategoryOrder = categoryGroups.flatMap(group =>
|
||||
(group.categories || []).map(category => category.id),
|
||||
);
|
||||
|
||||
const getCategoriesForView = (viewId: string) =>
|
||||
globalCategoryOrder.filter(catId =>
|
||||
Array.isArray(viewMap[catId]) ? viewMap[catId].includes(viewId) : false,
|
||||
);
|
||||
|
||||
const getGroupsForView = (viewId: string) =>
|
||||
categoryGroups
|
||||
.filter(group =>
|
||||
(group.categories || []).some(category =>
|
||||
Array.isArray(viewMap[category.id])
|
||||
? viewMap[category.id].includes(viewId)
|
||||
: false,
|
||||
),
|
||||
)
|
||||
.map(group => group.id);
|
||||
|
||||
views.forEach(view => {
|
||||
if (!viewCategoryOrder[view.id]) {
|
||||
const categoryIds = getCategoriesForView(view.id);
|
||||
if (categoryIds.length > 0) {
|
||||
setViewCategoryOrder(view.id, categoryIds);
|
||||
}
|
||||
}
|
||||
|
||||
if (!viewGroupOrder[view.id]) {
|
||||
const groupIds = getGroupsForView(view.id);
|
||||
if (groupIds.length > 0) {
|
||||
setViewGroupOrder(view.id, groupIds);
|
||||
}
|
||||
}
|
||||
});
|
||||
}, [
|
||||
categoryGroups,
|
||||
views,
|
||||
viewMap,
|
||||
viewCategoryOrder,
|
||||
viewGroupOrder,
|
||||
setViewCategoryOrder,
|
||||
setViewGroupOrder,
|
||||
]);
|
||||
|
||||
const onMonthSelect = async (month, numDisplayed) => {
|
||||
setStartMonthPref(month);
|
||||
|
||||
|
||||
@@ -136,6 +136,23 @@ export function useBudgetViews(): {
|
||||
|
||||
// Set category order for a specific view
|
||||
const setViewCategoryOrder = (viewId: string, categoryIds: string[]) => {
|
||||
if (!categoryIds || categoryIds.length === 0) {
|
||||
if (viewCategoryOrder[viewId]) {
|
||||
const { [viewId]: _, ...rest } = viewCategoryOrder;
|
||||
setViewCategoryOrderPref(rest);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const existingOrder = viewCategoryOrder[viewId];
|
||||
if (
|
||||
existingOrder &&
|
||||
existingOrder.length === categoryIds.length &&
|
||||
existingOrder.every((id, index) => id === categoryIds[index])
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
setViewCategoryOrderPref({
|
||||
...viewCategoryOrder,
|
||||
[viewId]: categoryIds,
|
||||
@@ -143,6 +160,23 @@ export function useBudgetViews(): {
|
||||
};
|
||||
|
||||
const setViewGroupOrder = (viewId: string, groupIds: string[]) => {
|
||||
if (!groupIds || groupIds.length === 0) {
|
||||
if (viewGroupOrder[viewId]) {
|
||||
const { [viewId]: _, ...rest } = viewGroupOrder;
|
||||
setViewGroupOrderPref(rest);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const existingOrder = viewGroupOrder[viewId];
|
||||
if (
|
||||
existingOrder &&
|
||||
existingOrder.length === groupIds.length &&
|
||||
existingOrder.every((id, index) => id === groupIds[index])
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
setViewGroupOrderPref({
|
||||
...viewGroupOrder,
|
||||
[viewId]: groupIds,
|
||||
|
||||
Reference in New Issue
Block a user