mirror of
https://github.com/actualbudget/actual.git
synced 2026-03-09 06:02:22 -05:00
Move redux state to react-query - tags states (#6941)
* Move redux state to react-query - tags states * Add release notes for PR #6941 * Cleanup sendThrow * Cleanup * Update import * Fix import --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
ca944baee5
commit
a0378c10a9
@@ -22,16 +22,17 @@ import {
|
||||
useSelected,
|
||||
} from '@desktop-client/hooks/useSelected';
|
||||
import { useTags } from '@desktop-client/hooks/useTags';
|
||||
import { useDispatch } from '@desktop-client/redux';
|
||||
import { deleteAllTags, findTags } from '@desktop-client/tags/tagsSlice';
|
||||
import {
|
||||
useDeleteTagsMutation,
|
||||
useDiscoverTagsMutation,
|
||||
} from '@desktop-client/tags';
|
||||
|
||||
export function ManageTags() {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useDispatch();
|
||||
const [filter, setFilter] = useState('');
|
||||
const [hoveredTag, setHoveredTag] = useState<string>();
|
||||
const [create, setCreate] = useState(false);
|
||||
const tags = useTags();
|
||||
const { data: tags = [] } = useTags();
|
||||
|
||||
const filteredTags = useMemo(() => {
|
||||
return filter === ''
|
||||
@@ -43,10 +44,19 @@ export function ManageTags() {
|
||||
|
||||
const selectedInst = useSelected('manage-tags', filteredTags, []);
|
||||
|
||||
const { mutate: discoverTags } = useDiscoverTagsMutation();
|
||||
const { mutate: deleteTags } = useDeleteTagsMutation();
|
||||
|
||||
const onDeleteSelected = useCallback(async () => {
|
||||
dispatch(deleteAllTags([...selectedInst.items]));
|
||||
selectedInst.dispatch({ type: 'select-none' });
|
||||
}, [dispatch, selectedInst]);
|
||||
deleteTags(
|
||||
{ ids: [...selectedInst.items] },
|
||||
{
|
||||
onSuccess: () => {
|
||||
selectedInst.dispatch({ type: 'select-none' });
|
||||
},
|
||||
},
|
||||
);
|
||||
}, [deleteTags, selectedInst]);
|
||||
|
||||
return (
|
||||
<SelectedProvider instance={selectedInst}>
|
||||
@@ -75,7 +85,7 @@ export function ManageTags() {
|
||||
<SvgAdd width={10} height={10} style={{ marginRight: 3 }} />
|
||||
<Trans>Add New</Trans>
|
||||
</Button>
|
||||
<Button variant="bare" onPress={() => dispatch(findTags())}>
|
||||
<Button variant="bare" onPress={() => discoverTags()}>
|
||||
<SvgSearchAlternate
|
||||
width={10}
|
||||
height={10}
|
||||
|
||||
@@ -18,8 +18,7 @@ import {
|
||||
import { useInitialMount } from '@desktop-client/hooks/useInitialMount';
|
||||
import { useProperFocus } from '@desktop-client/hooks/useProperFocus';
|
||||
import { useTagCSS } from '@desktop-client/hooks/useTagCSS';
|
||||
import { useDispatch } from '@desktop-client/redux';
|
||||
import { createTag } from '@desktop-client/tags/tagsSlice';
|
||||
import { useCreateTagMutation } from '@desktop-client/tags';
|
||||
|
||||
type TagCreationRowProps = {
|
||||
tags: TagEntity[];
|
||||
@@ -28,7 +27,6 @@ type TagCreationRowProps = {
|
||||
|
||||
export const TagCreationRow = ({ onClose, tags }: TagCreationRowProps) => {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useDispatch();
|
||||
const [tag, setTag] = useState('');
|
||||
const [description, setDescription] = useState('');
|
||||
const [color, setColor] = useState<string | null>(null);
|
||||
@@ -67,12 +65,14 @@ export const TagCreationRow = ({ onClose, tags }: TagCreationRowProps) => {
|
||||
);
|
||||
};
|
||||
|
||||
const { mutate: createTag } = useCreateTagMutation();
|
||||
|
||||
const onAddTag = () => {
|
||||
if (!isTagValid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch(createTag({ tag, color, description }));
|
||||
createTag({ tag: { tag, color, description } });
|
||||
resetInputs();
|
||||
};
|
||||
|
||||
|
||||
@@ -6,8 +6,7 @@ import { ColorPicker } from '@actual-app/components/color-picker';
|
||||
import type { TagEntity } from 'loot-core/types/models';
|
||||
|
||||
import { useTagCSS } from '@desktop-client/hooks/useTagCSS';
|
||||
import { useDispatch } from '@desktop-client/redux';
|
||||
import { updateTag } from '@desktop-client/tags/tagsSlice';
|
||||
import { useUpdateTagMutation } from '@desktop-client/tags';
|
||||
|
||||
type TagEditorProps = {
|
||||
tag: TagEntity;
|
||||
@@ -15,8 +14,8 @@ type TagEditorProps = {
|
||||
};
|
||||
|
||||
export const TagEditor = ({ tag, ref }: TagEditorProps) => {
|
||||
const dispatch = useDispatch();
|
||||
const getTagCSS = useTagCSS();
|
||||
const { mutate: updateTag } = useUpdateTagMutation();
|
||||
|
||||
const formattedTag = <>#{tag.tag}</>;
|
||||
|
||||
@@ -24,7 +23,7 @@ export const TagEditor = ({ tag, ref }: TagEditorProps) => {
|
||||
<ColorPicker
|
||||
value={tag.color ?? undefined}
|
||||
onChange={color => {
|
||||
dispatch(updateTag({ ...tag, color: color.toString('hex') }));
|
||||
updateTag({ tag: { ...tag, color: color.toString('hex') } });
|
||||
}}
|
||||
>
|
||||
<Button variant="bare" className={getTagCSS(tag.tag)} ref={ref}>
|
||||
|
||||
@@ -22,8 +22,10 @@ import { useContextMenu } from '@desktop-client/hooks/useContextMenu';
|
||||
import { useNavigate } from '@desktop-client/hooks/useNavigate';
|
||||
import { useProperFocus } from '@desktop-client/hooks/useProperFocus';
|
||||
import { useSelectedDispatch } from '@desktop-client/hooks/useSelected';
|
||||
import { useDispatch } from '@desktop-client/redux';
|
||||
import { deleteTag, updateTag } from '@desktop-client/tags/tagsSlice';
|
||||
import {
|
||||
useDeleteTagMutation,
|
||||
useUpdateTagMutation,
|
||||
} from '@desktop-client/tags';
|
||||
|
||||
type TagRowProps = {
|
||||
tag: TagEntity;
|
||||
@@ -37,7 +39,6 @@ type TagRowProps = {
|
||||
export const TagRow = memo(
|
||||
({ tag, hovered, selected, onHover, focusedField, onEdit }: TagRowProps) => {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useDispatch();
|
||||
const dispatchSelected = useSelectedDispatch();
|
||||
const borderColor = selected ? theme.tableBorderSelected : 'none';
|
||||
|
||||
@@ -50,9 +51,11 @@ export const TagRow = memo(
|
||||
const { setMenuOpen, menuOpen, handleContextMenu, position } =
|
||||
useContextMenu();
|
||||
const navigate = useNavigate();
|
||||
const { mutate: updateTag } = useUpdateTagMutation();
|
||||
const { mutate: deleteTag } = useDeleteTagMutation();
|
||||
|
||||
const onUpdate = (description: string) => {
|
||||
dispatch(updateTag({ ...tag, description }));
|
||||
updateTag({ tag: { ...tag, description } });
|
||||
};
|
||||
|
||||
const onShowActivity = () => {
|
||||
@@ -108,7 +111,7 @@ export const TagRow = memo(
|
||||
onMenuSelect={name => {
|
||||
switch (name) {
|
||||
case 'delete':
|
||||
dispatch(deleteTag(tag));
|
||||
deleteTag({ id: tag.id });
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Unrecognized menu option: ${name}`);
|
||||
|
||||
@@ -10,7 +10,7 @@ import { useTags } from './useTags';
|
||||
import { useTheme } from '@desktop-client/style';
|
||||
|
||||
export function useTagCSS() {
|
||||
const tags = useTags();
|
||||
const { data: tags = [] } = useTags();
|
||||
const [theme] = useTheme();
|
||||
|
||||
return useCallback(
|
||||
|
||||
@@ -1,20 +1,7 @@
|
||||
import { useEffect } from 'react';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
|
||||
import { useInitialMount } from './useInitialMount';
|
||||
|
||||
import { useDispatch, useSelector } from '@desktop-client/redux';
|
||||
import { getTags } from '@desktop-client/tags/tagsSlice';
|
||||
import { tagQueries } from '@desktop-client/tags/queries';
|
||||
|
||||
export function useTags() {
|
||||
const dispatch = useDispatch();
|
||||
const isInitialMount = useInitialMount();
|
||||
const isTagsDirty = useSelector(state => state.tags.isTagsDirty);
|
||||
|
||||
useEffect(() => {
|
||||
if (isInitialMount || isTagsDirty) {
|
||||
dispatch(getTags());
|
||||
}
|
||||
}, [dispatch, isInitialMount, isTagsDirty]);
|
||||
|
||||
return useSelector(state => state.tags.tags);
|
||||
return useQuery(tagQueries.list());
|
||||
}
|
||||
|
||||
@@ -27,7 +27,6 @@ import * as payeesSlice from './payees/payeesSlice';
|
||||
import * as prefsSlice from './prefs/prefsSlice';
|
||||
import { aqlQuery } from './queries/aqlQuery';
|
||||
import { configureAppStore } from './redux/store';
|
||||
import * as tagsSlice from './tags/tagsSlice';
|
||||
import * as transactionsSlice from './transactions/transactionsSlice';
|
||||
import { redo, undo } from './undo';
|
||||
import * as usersSlice from './users/usersSlice';
|
||||
@@ -47,7 +46,6 @@ const boundActions = bindActionCreators(
|
||||
...payeesSlice.actions,
|
||||
...prefsSlice.actions,
|
||||
...transactionsSlice.actions,
|
||||
...tagsSlice.actions,
|
||||
...usersSlice.actions,
|
||||
},
|
||||
store.dispatch,
|
||||
|
||||
@@ -35,10 +35,6 @@ import {
|
||||
name as prefsSliceName,
|
||||
reducer as prefsSliceReducer,
|
||||
} from '@desktop-client/prefs/prefsSlice';
|
||||
import {
|
||||
name as tagsSliceName,
|
||||
reducer as tagsSliceReducer,
|
||||
} from '@desktop-client/tags/tagsSlice';
|
||||
import {
|
||||
name as transactionsSliceName,
|
||||
reducer as transactionsSliceReducer,
|
||||
@@ -57,7 +53,6 @@ const rootReducer = combineReducers({
|
||||
[payeesSliceName]: payeesSliceReducer,
|
||||
[prefsSliceName]: prefsSliceReducer,
|
||||
[transactionsSliceName]: transactionsSliceReducer,
|
||||
[tagsSliceName]: tagsSliceReducer,
|
||||
[usersSliceName]: usersSliceReducer,
|
||||
});
|
||||
|
||||
|
||||
2
packages/desktop-client/src/tags/index.ts
Normal file
2
packages/desktop-client/src/tags/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export * from './queries';
|
||||
export * from './mutations';
|
||||
158
packages/desktop-client/src/tags/mutations.ts
Normal file
158
packages/desktop-client/src/tags/mutations.ts
Normal file
@@ -0,0 +1,158 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import type { QueryClient, QueryKey } from '@tanstack/react-query';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
import { send } from 'loot-core/platform/client/connection';
|
||||
import type { TagEntity } from 'loot-core/types/models';
|
||||
|
||||
import { tagQueries } from './queries';
|
||||
|
||||
import { addNotification } from '@desktop-client/notifications/notificationsSlice';
|
||||
import { useDispatch } from '@desktop-client/redux';
|
||||
import type { AppDispatch } from '@desktop-client/redux/store';
|
||||
|
||||
function invalidateQueries(queryClient: QueryClient, queryKey?: QueryKey) {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: queryKey ?? tagQueries.lists(),
|
||||
});
|
||||
}
|
||||
|
||||
function dispatchErrorNotification(
|
||||
dispatch: AppDispatch,
|
||||
message: string,
|
||||
error?: Error,
|
||||
) {
|
||||
dispatch(
|
||||
addNotification({
|
||||
notification: {
|
||||
id: uuidv4(),
|
||||
type: 'error',
|
||||
message,
|
||||
pre: error ? error.message : undefined,
|
||||
},
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
type CreateTagPayload = {
|
||||
tag: Omit<TagEntity, 'id'>;
|
||||
};
|
||||
|
||||
export function useCreateTagMutation() {
|
||||
const queryClient = useQueryClient();
|
||||
const dispatch = useDispatch();
|
||||
const { t } = useTranslation();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: async ({ tag }: CreateTagPayload) => {
|
||||
return await send('tags-create', tag);
|
||||
},
|
||||
onSuccess: () => invalidateQueries(queryClient),
|
||||
onError: error => {
|
||||
console.error('Error creating tag:', error);
|
||||
dispatchErrorNotification(
|
||||
dispatch,
|
||||
t('There was an error creating the tag. Please try again.'),
|
||||
error,
|
||||
);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
type UpdateTagPayload = {
|
||||
tag: TagEntity;
|
||||
};
|
||||
|
||||
export function useUpdateTagMutation() {
|
||||
const queryClient = useQueryClient();
|
||||
const dispatch = useDispatch();
|
||||
const { t } = useTranslation();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: async ({ tag }: UpdateTagPayload) => {
|
||||
return await send('tags-update', tag);
|
||||
},
|
||||
onSuccess: () => invalidateQueries(queryClient),
|
||||
onError: error => {
|
||||
console.error('Error updating tag:', error);
|
||||
dispatchErrorNotification(
|
||||
dispatch,
|
||||
t('There was an error updating the tag. Please try again.'),
|
||||
error,
|
||||
);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
type DeleteTagPayload = {
|
||||
id: TagEntity['id'];
|
||||
};
|
||||
|
||||
export function useDeleteTagMutation() {
|
||||
const queryClient = useQueryClient();
|
||||
const dispatch = useDispatch();
|
||||
const { t } = useTranslation();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: async ({ id }: DeleteTagPayload) => {
|
||||
return await send('tags-delete', { id });
|
||||
},
|
||||
onSuccess: () => invalidateQueries(queryClient),
|
||||
onError: error => {
|
||||
console.error('Error deleting tag:', error);
|
||||
dispatchErrorNotification(
|
||||
dispatch,
|
||||
t('There was an error deleting the tag. Please try again.'),
|
||||
error,
|
||||
);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
type DeleteTagsPayload = {
|
||||
ids: Array<TagEntity['id']>;
|
||||
};
|
||||
|
||||
export function useDeleteTagsMutation() {
|
||||
const queryClient = useQueryClient();
|
||||
const dispatch = useDispatch();
|
||||
const { t } = useTranslation();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: async ({ ids }: DeleteTagsPayload) => {
|
||||
return await send('tags-delete-all', ids);
|
||||
},
|
||||
onSuccess: () => invalidateQueries(queryClient),
|
||||
onError: error => {
|
||||
console.error('Error deleting tags:', error);
|
||||
dispatchErrorNotification(
|
||||
dispatch,
|
||||
t('There was an error deleting the tags. Please try again.'),
|
||||
error,
|
||||
);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useDiscoverTagsMutation() {
|
||||
const queryClient = useQueryClient();
|
||||
const dispatch = useDispatch();
|
||||
const { t } = useTranslation();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: async () => {
|
||||
return await send('tags-discover');
|
||||
},
|
||||
onSuccess: () => invalidateQueries(queryClient),
|
||||
onError: error => {
|
||||
console.error('Error discovering tags:', error);
|
||||
dispatchErrorNotification(
|
||||
dispatch,
|
||||
t('There was an error discovering the tags. Please try again.'),
|
||||
error,
|
||||
);
|
||||
},
|
||||
});
|
||||
}
|
||||
20
packages/desktop-client/src/tags/queries.ts
Normal file
20
packages/desktop-client/src/tags/queries.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import { queryOptions } from '@tanstack/react-query';
|
||||
|
||||
import { send } from 'loot-core/platform/client/connection';
|
||||
import type { TagEntity } from 'loot-core/types/models';
|
||||
|
||||
export const tagQueries = {
|
||||
all: () => ['tags'],
|
||||
lists: () => [...tagQueries.all(), 'lists'],
|
||||
list: () =>
|
||||
queryOptions<TagEntity[]>({
|
||||
queryKey: [...tagQueries.lists()],
|
||||
queryFn: async () => {
|
||||
const tags: TagEntity[] = await send('tags-get');
|
||||
return tags;
|
||||
},
|
||||
placeholderData: [],
|
||||
// Manually invalidated when tags change
|
||||
staleTime: Infinity,
|
||||
}),
|
||||
};
|
||||
@@ -1,165 +0,0 @@
|
||||
import { createSlice } from '@reduxjs/toolkit';
|
||||
|
||||
import { send } from 'loot-core/platform/client/connection';
|
||||
import type { TagEntity } from 'loot-core/types/models';
|
||||
|
||||
import { resetApp } from '@desktop-client/app/appSlice';
|
||||
import { createAppAsyncThunk } from '@desktop-client/redux';
|
||||
|
||||
const sliceName = 'tags';
|
||||
|
||||
type TagsState = {
|
||||
tags: TagEntity[];
|
||||
isTagsLoading: boolean;
|
||||
isTagsLoaded: boolean;
|
||||
isTagsDirty: boolean;
|
||||
};
|
||||
|
||||
const initialState: TagsState = {
|
||||
tags: [],
|
||||
isTagsLoading: false,
|
||||
isTagsLoaded: false,
|
||||
isTagsDirty: false,
|
||||
};
|
||||
|
||||
const tagSlice = createSlice({
|
||||
name: sliceName,
|
||||
initialState,
|
||||
reducers: {
|
||||
markTagsDirty(state) {
|
||||
_markTagsDirty(state);
|
||||
},
|
||||
},
|
||||
extraReducers: builder => {
|
||||
builder.addCase(resetApp, () => initialState);
|
||||
|
||||
builder.addCase(createTag.fulfilled, _markTagsDirty);
|
||||
builder.addCase(deleteTag.fulfilled, _markTagsDirty);
|
||||
builder.addCase(deleteAllTags.fulfilled, _markTagsDirty);
|
||||
builder.addCase(updateTag.fulfilled, _markTagsDirty);
|
||||
|
||||
builder.addCase(reloadTags.fulfilled, (state, action) => {
|
||||
_loadTags(state, action.payload);
|
||||
});
|
||||
|
||||
builder.addCase(reloadTags.rejected, state => {
|
||||
state.isTagsLoading = false;
|
||||
});
|
||||
|
||||
builder.addCase(reloadTags.pending, state => {
|
||||
state.isTagsLoading = true;
|
||||
});
|
||||
|
||||
builder.addCase(getTags.fulfilled, (state, action) => {
|
||||
_loadTags(state, action.payload);
|
||||
});
|
||||
|
||||
builder.addCase(getTags.rejected, state => {
|
||||
state.isTagsLoading = false;
|
||||
});
|
||||
|
||||
builder.addCase(getTags.pending, state => {
|
||||
state.isTagsLoading = true;
|
||||
});
|
||||
|
||||
builder.addCase(findTags.fulfilled, (state, action) => {
|
||||
_loadTags(state, action.payload);
|
||||
});
|
||||
|
||||
builder.addCase(findTags.rejected, state => {
|
||||
state.isTagsLoading = false;
|
||||
});
|
||||
|
||||
builder.addCase(findTags.pending, state => {
|
||||
state.isTagsLoading = true;
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
export const getTags = createAppAsyncThunk(
|
||||
`${sliceName}/getTags`,
|
||||
async () => {
|
||||
const tags: TagEntity[] = await send('tags-get');
|
||||
return tags;
|
||||
},
|
||||
{
|
||||
condition: (_, { getState }) => {
|
||||
const { tags } = getState();
|
||||
return !tags.isTagsLoading && (tags.isTagsDirty || !tags.isTagsLoaded);
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
export const reloadTags = createAppAsyncThunk(
|
||||
`${sliceName}/reloadTags`,
|
||||
async () => {
|
||||
const tags: TagEntity[] = await send('tags-get');
|
||||
return tags;
|
||||
},
|
||||
);
|
||||
|
||||
export const createTag = createAppAsyncThunk(
|
||||
`${sliceName}/createTag`,
|
||||
async ({ tag, color, description }: Omit<TagEntity, 'id'>) => {
|
||||
const id = await send('tags-create', { tag, color, description });
|
||||
return id;
|
||||
},
|
||||
);
|
||||
|
||||
export const deleteTag = createAppAsyncThunk(
|
||||
`${sliceName}/deleteTag`,
|
||||
async (tag: TagEntity) => {
|
||||
const id = await send('tags-delete', tag);
|
||||
return id;
|
||||
},
|
||||
);
|
||||
|
||||
export const deleteAllTags = createAppAsyncThunk(
|
||||
`${sliceName}/deleteAllTags`,
|
||||
async (ids: Array<TagEntity['id']>) => {
|
||||
const id = await send('tags-delete-all', ids);
|
||||
return id;
|
||||
},
|
||||
);
|
||||
|
||||
export const updateTag = createAppAsyncThunk(
|
||||
`${sliceName}/updateTag`,
|
||||
async (tag: TagEntity) => {
|
||||
const id = await send('tags-update', tag);
|
||||
return id;
|
||||
},
|
||||
);
|
||||
|
||||
export const findTags = createAppAsyncThunk(
|
||||
`${sliceName}/findTags`,
|
||||
async () => {
|
||||
const tags: TagEntity[] = await send('tags-find');
|
||||
return tags;
|
||||
},
|
||||
);
|
||||
|
||||
export const { name, reducer, getInitialState } = tagSlice;
|
||||
|
||||
export const actions = {
|
||||
...tagSlice.actions,
|
||||
getTags,
|
||||
reloadTags,
|
||||
createTag,
|
||||
deleteTag,
|
||||
deleteAllTags,
|
||||
updateTag,
|
||||
findTags,
|
||||
};
|
||||
|
||||
export const { markTagsDirty } = tagSlice.actions;
|
||||
|
||||
function _loadTags(state: TagsState, tags: TagsState['tags']) {
|
||||
state.tags = tags;
|
||||
state.isTagsLoading = false;
|
||||
state.isTagsLoaded = true;
|
||||
state.isTagsDirty = false;
|
||||
}
|
||||
|
||||
function _markTagsDirty(state: TagsState) {
|
||||
state.isTagsDirty = true;
|
||||
}
|
||||
@@ -11,7 +11,7 @@ export type TagsHandlers = {
|
||||
'tags-delete': typeof deleteTag;
|
||||
'tags-delete-all': typeof deleteAllTags;
|
||||
'tags-update': typeof updateTag;
|
||||
'tags-find': typeof findTags;
|
||||
'tags-discover': typeof discoverTags;
|
||||
};
|
||||
|
||||
export const app = createApp<TagsHandlers>();
|
||||
@@ -20,7 +20,7 @@ app.method('tags-create', mutator(undoable(createTag)));
|
||||
app.method('tags-delete', mutator(undoable(deleteTag)));
|
||||
app.method('tags-delete-all', mutator(deleteAllTags));
|
||||
app.method('tags-update', mutator(undoable(updateTag)));
|
||||
app.method('tags-find', mutator(findTags));
|
||||
app.method('tags-discover', mutator(discoverTags));
|
||||
|
||||
async function getTags(): Promise<TagEntity[]> {
|
||||
return await db.getTags();
|
||||
@@ -77,7 +77,7 @@ async function updateTag(
|
||||
return tag;
|
||||
}
|
||||
|
||||
async function findTags(): Promise<TagEntity[]> {
|
||||
async function discoverTags(): Promise<TagEntity[]> {
|
||||
const taggedNotes = await db.findTags();
|
||||
|
||||
const tags = await getTags();
|
||||
|
||||
6
upcoming-release-notes/6941.md
Normal file
6
upcoming-release-notes/6941.md
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
category: Enhancements
|
||||
authors: [joel-jeremy]
|
||||
---
|
||||
|
||||
Migrate tag management from Redux to React Query for improved state handling and performance.
|
||||
Reference in New Issue
Block a user