nicolium: don't make unneeded mfa settings request

Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
This commit is contained in:
nicole mikołajczyk
2026-03-01 19:03:15 +01:00
parent 23995b3888
commit 456658d71d
13 changed files with 115 additions and 113 deletions

View File

@ -1,4 +1,5 @@
import { useMutation, useQueryClient, type InfiniteData } from '@tanstack/react-query';
import { PaginatedResponse, type PlApiClient } from 'pl-api';
import { useClient } from '@/hooks/use-client';
import { queryClient } from '@/queries/client';
@ -8,8 +9,6 @@ import { minifyAccountList } from '@/queries/utils/minify-list';
import { filterById } from '../utils/filter-id';
import type { PaginatedResponse, PlApiClient } from 'pl-api';
const appendFollowRequest = (accountId: string) =>
queryClient.setQueryData(queryKeys.accountsLists.followRequests, (data) => {
if (!data || data.pages.some((page) => page.items.includes(accountId))) return data;
@ -17,7 +16,7 @@ const appendFollowRequest = (accountId: string) =>
return {
...data,
pages: data.pages.map((page, index) =>
index === 0 ? { ...page, items: [accountId, ...page.items] } : page,
index === 0 ? new PaginatedResponse([accountId, ...page.items], page) : page,
),
};
});

View File

@ -9,6 +9,7 @@ import * as v from 'valibot';
import { useClient } from '@/hooks/use-client';
import { queryClient } from '@/queries/client';
import { removePageItem } from '@/utils/queries';
import { queryKeys } from '../keys';
import { makePaginatedResponseQuery } from '../utils/make-paginated-response-query';
@ -67,12 +68,11 @@ const useDeleteAnnouncementMutation = () => {
mutationFn: (id: string) => client.admin.announcements.deleteAnnouncement(id),
retry: false,
onSuccess: (_, deletedAnnouncementId) => {
queryClient.setQueryData(queryKeys.admin.announcements, (prevData) =>
create(prevData, (draft) => {
draft?.pages.forEach(
(page) => (page.items = page.items.filter(({ id }) => id !== deletedAnnouncementId)),
);
}),
removePageItem(
queryKeys.admin.announcements,
deletedAnnouncementId,
(announcement: { id: string }, id: string) => announcement.id === id,
true,
);
queryClient.invalidateQueries({ queryKey: queryKeys.announcements.root });
},

View File

@ -207,29 +207,27 @@ const useCreateChatMessage = () => {
queryClient.setQueryData(queryKeys.chats.chatMessages(variables.chatId), (prevResult) => {
if (!prevResult?.pages) return prevResult;
const newResult = { ...prevResult };
newResult.pages = newResult.pages.map((page, idx: number) => {
if (idx === 0) {
return {
...page,
items: [
normalizeChatMessage({
...v.parse(chatMessageSchema, {
chat_id: variables.chatId,
content: variables.content,
id: pendingId,
created_at: new Date().toISOString(),
account_id: account?.id,
unread: true,
}),
pending: true,
const [firstPage, ...restPages] = newResult.pages;
newResult.pages = [
new PaginatedResponse(
[
normalizeChatMessage({
...v.parse(chatMessageSchema, {
chat_id: variables.chatId,
content: variables.content,
id: pendingId,
created_at: new Date().toISOString(),
account_id: account?.id,
unread: true,
}),
...page.items,
],
};
}
return page;
});
pending: true,
}),
...firstPage.items,
],
firstPage,
),
...restPages,
];
return newResult;
});

View File

@ -1,4 +1,5 @@
import { useMutation } from '@tanstack/react-query';
import { PaginatedResponse } from 'pl-api';
import { useClient } from '@/hooks/use-client';
import { queryClient } from '@/queries/client';
@ -17,7 +18,7 @@ const appendGroupBlock = (groupId: string, accountId: string) =>
return {
...data,
pages: data.pages.map((page, index) =>
index === 0 ? { ...page, items: [accountId, ...page.items] } : page,
index === 0 ? new PaginatedResponse([accountId, ...page.items], page) : page,
),
};
});

View File

@ -1,7 +1,8 @@
import { type InfiniteData, useMutation, useQueryClient } from '@tanstack/react-query';
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { useClient } from '@/hooks/use-client';
import { makePaginatedResponseQuery } from '@/queries/utils/make-paginated-response-query';
import { removePageItem } from '@/utils/queries';
import { queryClient } from '../client';
import { queryKeys } from '../keys';
@ -10,18 +11,10 @@ import { minifyAccountList, minifyList } from '../utils/minify-list';
import type { GroupMember, GroupRole, PaginatedResponse } from 'pl-api';
const removeGroupMember = (groupId: string, accountId: string) =>
queryClient.setQueriesData<InfiniteData<PaginatedResponse<MinifiedGroupMember>>>(
{ queryKey: queryKeys.accountsLists.groupMembers.root(groupId) },
(data) =>
data
? {
...data,
pages: data.pages.map((page) => ({
...page,
items: page.items.filter((member) => member.account_id !== accountId),
})),
}
: undefined,
removePageItem(
queryKeys.accountsLists.groupMembers.root(groupId),
accountId,
(member: MinifiedGroupMember, accountId: string) => member.account_id === accountId,
);
const minifyGroupMember = ({ account, ...groupMember }: GroupMember) => ({

View File

@ -1,6 +1,12 @@
import { useInfiniteQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import 'intl-pluralrules';
import omit from 'lodash/omit';
import {
PaginatedResponse,
type GetGroupedNotificationsParams,
type Notification,
type NotificationGroup,
} from 'pl-api';
import { useCallback, useEffect, useMemo } from 'react';
import { useIntl } from 'react-intl';
@ -30,8 +36,6 @@ import { useFiltersByContext } from '../settings/use-filters';
import { useStatus } from '../statuses/use-status';
import { minifyGroupedNotifications } from '../utils/minify-list';
import type { GetGroupedNotificationsParams, Notification, NotificationGroup } from 'pl-api';
const FILTER_TYPES = {
all: undefined,
mention: ['mention', 'quote'],
@ -338,10 +342,10 @@ const prependNotification = (notification: NotificationGroup, filter: FilterType
return {
...data,
pages: [
{
...firstPage,
items: [notification, ...firstPage.items].toSorted(comparator).filter(filterUnique),
},
new PaginatedResponse<Array<NotificationGroup>, false>(
[notification, ...firstPage.items].toSorted(comparator).filter(filterUnique),
firstPage,
),
...restPages,
],
};

View File

@ -1,8 +1,6 @@
import { create } from 'mutative';
import { getClient } from '@/api';
import { removePageItem } from '@/utils/queries';
import { queryClient } from '../client';
import { queryKeys } from '../keys';
import { makePaginatedResponseQueryOptions } from '../utils/make-paginated-response-query-options';
import { mutationOptions } from '../utils/mutation-options';
@ -17,12 +15,11 @@ const revokeOauthTokenMutationOptions = (oauthTokenId: string) =>
mutationKey: ['security', 'oauthTokens', oauthTokenId],
mutationFn: () => getClient().settings.deleteOauthToken(oauthTokenId),
onSettled: () => {
queryClient.setQueryData(oauthTokensQueryOptions.queryKey, (data) =>
create(data, (draft) => {
draft?.pages.forEach(
(page) => (page.items = page.items.filter(({ id }) => id !== oauthTokenId)),
);
}),
removePageItem(
queryKeys.security.oauthTokens,
oauthTokenId,
(token: { id: string }, tokenId: string) => token.id === tokenId,
true,
);
},
});

View File

@ -1,15 +1,18 @@
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import { useClient } from '@/hooks/use-client';
import { useFeatures } from '@/hooks/use-features';
import { queryKeys } from '../keys';
const useMfaConfig = () => {
const client = useClient();
const features = useFeatures();
return useQuery({
queryKey: queryKeys.settings.mfa,
queryFn: () => client.settings.mfa.getMfaSettings(),
enabled: features.manageMfa,
});
};

View File

@ -1,9 +1,8 @@
import { infiniteQueryOptions } from '@tanstack/react-query';
import { create } from 'mutative';
import { getClient } from '@/api';
import { removePageItem } from '@/utils/queries';
import { queryClient } from '../client';
import { queryKeys } from '../keys';
import { makePaginatedResponseQueryOptions } from '../utils/make-paginated-response-query-options';
import { mutationOptions } from '../utils/mutation-options';
@ -23,12 +22,11 @@ const cancelScheduledStatusMutationOptions = (scheduledStatusId: string) =>
mutationKey: ['scheduledStatuses', scheduledStatusId],
mutationFn: () => getClient().scheduledStatuses.cancelScheduledStatus(scheduledStatusId),
onSettled: () => {
queryClient.setQueryData(scheduledStatusesQueryOptions.queryKey, (data) =>
create(data, (draft) => {
draft?.pages.forEach(
(page) => (page.items = page.items.filter(({ id }) => id !== scheduledStatusId)),
);
}),
removePageItem(
queryKeys.scheduledStatuses.all,
scheduledStatusId,
(status: { id: string }, id: string) => status.id === id,
true,
);
},
});

View File

@ -11,10 +11,10 @@ const recentEventsQueryOptions = makePaginatedResponseQueryOptions(
.publicTimeline({
only_events: true,
})
.then((res) => ({
...res,
items: res.items.filter(({ event }) => event),
}))
.then((res) => {
res.items = res.items.filter(({ event }) => event);
return res;
})
.then(minifyStatusList),
)();

View File

@ -1,31 +1,36 @@
import { create } from 'mutative';
import { PaginatedResponse } from 'pl-api';
import type { InfiniteData } from '@tanstack/react-query';
import type { PaginatedResponse } from 'pl-api';
const filterById =
(filteredId: string | Array<string>) =>
(data: InfiniteData<PaginatedResponse<string>> | undefined) => {
if (data) {
return create(data, (data) => {
let found = 0;
data.pages.forEach((page) => {
page.items = page.items.filter((id) => {
if (Array.isArray(filteredId)) {
const includes = filteredId.includes(id);
if (includes) found += 1;
return !includes;
}
if (id === filteredId) found = 1;
return id !== filteredId;
});
let found = 0;
let pages = data.pages.map(
(page) =>
new PaginatedResponse(
page.items.filter((id) => {
if (Array.isArray(filteredId)) {
const includes = filteredId.includes(id);
if (includes) found += 1;
return !includes;
}
if (id === filteredId) found = 1;
return id !== filteredId;
}),
page,
),
);
if (found) {
pages.forEach((page) => {
if (page.total) page.total -= found;
});
if (found)
data.pages.forEach((page) => {
if (page.total) page.total -= found;
});
});
}
return {
...data,
pages,
};
}
};

View File

@ -1,7 +1,8 @@
import { PaginatedResponse } from 'pl-api';
import { queryClient } from '@/queries/client';
import type { DataTag, InfiniteData, QueryKey } from '@tanstack/react-query';
import type { PaginatedResponse } from 'pl-api';
type InferPaginatedItem<TKey extends QueryKey> =
TKey extends DataTag<QueryKey, InfiniteData<PaginatedResponse<infer U, any>>> ? U : never;
@ -18,10 +19,7 @@ const updatePaginatedResponse = <
if (!data) return undefined;
return {
...data,
pages: data.pages.map((page) => ({
...page,
items: updater(page.items),
})),
pages: data.pages.map((page) => new PaginatedResponse(updater(page.items), page)),
};
});

View File

@ -1,7 +1,8 @@
import { PaginatedResponse } from 'pl-api';
import { queryClient } from '@/queries/client';
import type { DataTag, InfiniteData, QueryKey } from '@tanstack/react-query';
import type { PaginatedResponse } from 'pl-api';
interface Entity {
id: string;
@ -44,7 +45,7 @@ const updatePageItem = <T>(
if (data) {
const pages = data.pages.map((page) => {
const result = page.items.map((item) => (isItem(item, newItem) ? newItem : item));
return { ...page, result };
return new PaginatedResponse(result, page);
});
return { ...data, pages };
}
@ -59,27 +60,34 @@ const appendPageItem = <T>(
queryClient.setQueryData(queryKey, (data) => {
if (data) {
const pages = [...data.pages];
pages[0] = { ...pages[0], items: [newItem, ...pages[0].items] };
pages[0] = new PaginatedResponse([newItem, ...pages[0].items], pages[0]);
return { ...data, pages };
}
});
};
/** Remove an item inside if found. */
const removePageItem = <T>(
const removePageItem = <T1, T2>(
queryKey: QueryKey,
itemToRemove: T,
isItem: (item: T, newItem: T) => boolean,
itemToRemove: T2,
isItem: (item: T1, itemToRemove: T2) => boolean,
exact = false,
) => {
queryClient.setQueriesData<InfiniteData<PaginatedResponse<T>>>({ queryKey }, (data) => {
const updater = (data: InfiniteData<PaginatedResponse<T1>> | undefined) => {
if (data) {
const pages = data.pages.map((page) => {
const items = page.items.filter((item) => !isItem(item, itemToRemove));
return { ...page, items };
});
const pages = data.pages.map(
(page) =>
new PaginatedResponse(
page.items.filter((item) => !isItem(item, itemToRemove)),
page,
),
);
return { ...data, pages };
}
});
};
if (exact) queryClient.setQueryData(queryKey, updater);
else queryClient.setQueriesData({ queryKey }, updater);
};
const paginateQueryData = <T>(array: T[] | undefined) =>
@ -104,10 +112,8 @@ const sortQueryData = <T>(
const sortedQueryData = flattenedQueryData?.toSorted(comparator);
const paginatedPages = paginateQueryData(sortedQueryData);
const newPages =
paginatedPages?.map((page, idx) => ({
...prevResult.pages[idx],
result: page,
})) ?? [];
paginatedPages?.map((page, idx) => new PaginatedResponse(page, prevResult.pages[idx])) ??
[];
nextResult.pages = newPages;
return nextResult;