add better type safety for favorite/rating update

This commit is contained in:
jeffvli
2025-11-24 03:01:05 -08:00
parent 5e5b4f1536
commit a4f53e5273
2 changed files with 227 additions and 195 deletions
@@ -50,16 +50,19 @@ export const applyFavoriteOptimisticUpdates = (
detailQueries.forEach(([queryKey, data]) => { detailQueries.forEach(([queryKey, data]) => {
if (data) { if (data) {
previousQueries.push({ data, queryKey }); previousQueries.push({ data, queryKey });
queryClient.setQueryData(queryKey, (prev: AlbumDetailResponse) => { queryClient.setQueryData(
if (prev && itemIdSet.has(prev.id)) { queryKey,
return { (prev: AlbumDetailResponse | undefined) => {
...prev, if (prev && itemIdSet.has(prev.id)) {
userFavorite: isFavorite, return {
}; ...prev,
} userFavorite: isFavorite,
};
}
return prev; return prev;
}); },
);
} }
}); });
} }
@@ -75,20 +78,23 @@ export const applyFavoriteOptimisticUpdates = (
listQueries.forEach(([queryKey, data]) => { listQueries.forEach(([queryKey, data]) => {
if (data) { if (data) {
previousQueries.push({ data, queryKey }); previousQueries.push({ data, queryKey });
queryClient.setQueryData(queryKey, (prev: AlbumListResponse) => { queryClient.setQueryData(
if (prev) { queryKey,
return { (prev: AlbumListResponse | undefined) => {
...prev, if (prev) {
items: prev.items.map((item: Album) => { return {
return itemIdSet.has(item.id) ...prev,
? { ...item, userFavorite: isFavorite } items: prev.items.map((item: Album) => {
: item; return itemIdSet.has(item.id)
}), ? { ...item, userFavorite: isFavorite }
}; : item;
} }),
};
}
return prev; return prev;
}); },
);
} }
}); });
} }
@@ -108,20 +114,33 @@ export const applyFavoriteOptimisticUpdates = (
previousQueries.push({ data, queryKey }); previousQueries.push({ data, queryKey });
queryClient.setQueryData( queryClient.setQueryData(
queryKey, queryKey,
(prev: { pageParams: string[]; pages: AlbumListResponse[] }) => { (
prev:
| undefined
| { pageParams: string[]; pages: AlbumListResponse[] },
) => {
if (prev) { if (prev) {
return { return {
...prev, ...prev,
pages: prev.pages.map((page: AlbumListResponse) => { pages: prev.pages.map(
return { (page: AlbumListResponse | undefined) => {
...page, if (page) {
items: page.items.map((item: Album) => { return {
return itemIdSet.has(item.id) ...page,
? { ...item, userFavorite: isFavorite } items: page.items.map((item: Album) => {
: item; return itemIdSet.has(item.id)
}), ? {
}; ...item,
}), userFavorite: isFavorite,
}
: item;
}),
};
}
return page;
},
),
}; };
} }
@@ -146,16 +165,19 @@ export const applyFavoriteOptimisticUpdates = (
detailQueries.forEach(([queryKey, data]) => { detailQueries.forEach(([queryKey, data]) => {
if (data) { if (data) {
previousQueries.push({ data, queryKey }); previousQueries.push({ data, queryKey });
queryClient.setQueryData(queryKey, (prev: AlbumArtistDetailResponse) => { queryClient.setQueryData(
if (prev && itemIdSet.has(prev.id)) { queryKey,
return { (prev: AlbumArtistDetailResponse | undefined) => {
...prev, if (prev && itemIdSet.has(prev.id)) {
userFavorite: isFavorite, return {
}; ...prev,
} userFavorite: isFavorite,
};
}
return prev; return prev;
}); },
);
} }
}); });
} }
@@ -170,20 +192,27 @@ export const applyFavoriteOptimisticUpdates = (
listQueries.forEach(([queryKey, data]) => { listQueries.forEach(([queryKey, data]) => {
if (data) { if (data) {
previousQueries.push({ data, queryKey }); previousQueries.push({ data, queryKey });
queryClient.setQueryData(queryKey, (prev: AlbumArtistListResponse) => { queryClient.setQueryData(
if (prev) { queryKey,
return { (prev: AlbumArtistListResponse | undefined) => {
...prev, if (prev) {
items: prev.items.map((item: AlbumArtist) => { return {
return itemIdSet.has(item.id) ...prev,
? { ...item, userFavorite: isFavorite } items: prev.items.map((item: AlbumArtist | undefined) => {
: item; if (item) {
}), return itemIdSet.has(item.id)
}; ? { ...item, userFavorite: isFavorite }
} : item;
}
return prev; return item;
}); }),
};
}
return prev;
},
);
} }
}); });
} }
@@ -203,23 +232,39 @@ export const applyFavoriteOptimisticUpdates = (
previousQueries.push({ data, queryKey }); previousQueries.push({ data, queryKey });
queryClient.setQueryData( queryClient.setQueryData(
queryKey, queryKey,
(prev: { pageParams: string[]; pages: AlbumArtistListResponse[] }) => { (
return { prev:
...prev, | undefined
pages: prev.pages.map((page: AlbumArtistListResponse) => { | { pageParams: string[]; pages: AlbumArtistListResponse[] },
return { ) => {
...page, if (prev) {
items: page.items.map((item: AlbumArtist) => { return {
return itemIdSet.has(item.id) ...prev,
? { pages: prev.pages.map(
...item, (page: AlbumArtistListResponse | undefined) => {
userFavorite: isFavorite, if (page) {
} return {
: item; ...page,
}), items: page.items.map(
}; (item: AlbumArtist) => {
}), return itemIdSet.has(item.id)
}; ? {
...item,
userFavorite: isFavorite,
}
: item;
},
),
};
}
return page;
},
),
};
}
return prev;
}, },
); );
} }
@@ -229,31 +274,6 @@ export const applyFavoriteOptimisticUpdates = (
break; break;
} }
case LibraryItem.ARTIST: { case LibraryItem.ARTIST: {
const detailQueryKey = queryKeys.artists.detail(variables.apiClientProps.serverId);
const detailQueries = queryClient.getQueriesData({
exact: false,
queryKey: detailQueryKey,
});
if (detailQueries.length) {
detailQueries.forEach(([queryKey, data]) => {
if (data) {
previousQueries.push({ data, queryKey });
queryClient.setQueryData(queryKey, (prev: AlbumArtistDetailResponse) => {
if (prev && itemIdSet.has(prev.id)) {
return {
...prev,
userFavorite: isFavorite,
};
}
return prev;
});
}
});
}
const listQueryKey = queryKeys.artists.list(variables.apiClientProps.serverId); const listQueryKey = queryKeys.artists.list(variables.apiClientProps.serverId);
const listQueries = queryClient.getQueriesData({ const listQueries = queryClient.getQueriesData({
@@ -265,16 +285,23 @@ export const applyFavoriteOptimisticUpdates = (
listQueries.forEach(([queryKey, data]) => { listQueries.forEach(([queryKey, data]) => {
if (data) { if (data) {
previousQueries.push({ data, queryKey }); previousQueries.push({ data, queryKey });
queryClient.setQueryData(queryKey, (prev: ArtistListResponse) => { queryClient.setQueryData(
return { queryKey,
...prev, (prev: ArtistListResponse | undefined) => {
items: prev.items.map((item: Artist) => { if (prev) {
return itemIdSet.has(item.id) return {
? { ...item, userFavorite: isFavorite } ...prev,
: item; items: prev.items.map((item: Artist) => {
}), return itemIdSet.has(item.id)
}; ? { ...item, userFavorite: isFavorite }
}); : item;
}),
};
}
return prev;
},
);
} }
}); });
} }
@@ -294,20 +321,28 @@ export const applyFavoriteOptimisticUpdates = (
previousQueries.push({ data, queryKey }); previousQueries.push({ data, queryKey });
queryClient.setQueryData( queryClient.setQueryData(
queryKey, queryKey,
(prev: { pageParams: string[]; pages: ArtistListResponse[] }) => { (
return { prev:
...prev, | undefined
pages: prev.pages.map((page: ArtistListResponse) => { | { pageParams: string[]; pages: ArtistListResponse[] },
return { ) => {
...page, if (prev) {
items: page.items.map((item: Artist) => { return {
return itemIdSet.has(item.id) ...prev,
? { ...item, userFavorite: isFavorite } pages: prev.pages.map((page: ArtistListResponse) => {
: item; return {
}), ...page,
}; items: page.items.map((item: Artist) => {
}), return itemIdSet.has(item.id)
}; ? { ...item, userFavorite: isFavorite }
: item;
}),
};
}),
};
}
return prev;
}, },
); );
} }
@@ -330,20 +365,23 @@ export const applyFavoriteOptimisticUpdates = (
albumDetailQueries.forEach(([queryKey, data]) => { albumDetailQueries.forEach(([queryKey, data]) => {
if (data) { if (data) {
previousQueries.push({ data, queryKey }); previousQueries.push({ data, queryKey });
queryClient.setQueryData(queryKey, (prev: AlbumDetailResponse) => { queryClient.setQueryData(
if (prev) { queryKey,
return { (prev: AlbumDetailResponse | undefined) => {
...prev, if (prev) {
songs: prev.songs?.map((song: Song) => { return {
return itemIdSet.has(song.id) ...prev,
? { ...song, userFavorite: isFavorite } songs: prev.songs?.map((song: Song) => {
: song; return itemIdSet.has(song.id)
}), ? { ...song, userFavorite: isFavorite }
}; : song;
} }),
};
}
return prev; return prev;
}); },
);
} }
}); });
} }
@@ -359,16 +397,19 @@ export const applyFavoriteOptimisticUpdates = (
detailQueries.forEach(([queryKey, data]) => { detailQueries.forEach(([queryKey, data]) => {
if (data) { if (data) {
previousQueries.push({ data, queryKey }); previousQueries.push({ data, queryKey });
queryClient.setQueryData(queryKey, (prev: SongDetailResponse) => { queryClient.setQueryData(
if (prev && itemIdSet.has(prev.id)) { queryKey,
return { (prev: SongDetailResponse | undefined) => {
...prev, if (prev && itemIdSet.has(prev.id)) {
userFavorite: isFavorite, return {
}; ...prev,
} userFavorite: isFavorite,
};
}
return prev; return prev;
}); },
);
} }
}); });
} }
@@ -47,12 +47,15 @@ export const applyRatingOptimisticUpdates = (
detailQueries.forEach(([queryKey, data]) => { detailQueries.forEach(([queryKey, data]) => {
if (data) { if (data) {
previousQueries.push({ data, queryKey }); previousQueries.push({ data, queryKey });
queryClient.setQueryData(queryKey, (prev: AlbumDetailResponse) => { queryClient.setQueryData(
if (prev && itemIdSet.has(prev.id)) { queryKey,
return { ...prev, userRating: rating }; (prev: AlbumDetailResponse | undefined) => {
} if (prev && itemIdSet.has(prev.id)) {
return prev; return { ...prev, userRating: rating };
}); }
return prev;
},
);
} }
}); });
} }
@@ -131,12 +134,15 @@ export const applyRatingOptimisticUpdates = (
detailQueries.forEach(([queryKey, data]) => { detailQueries.forEach(([queryKey, data]) => {
if (data) { if (data) {
previousQueries.push({ data, queryKey }); previousQueries.push({ data, queryKey });
queryClient.setQueryData(queryKey, (prev: AlbumArtistDetailResponse) => { queryClient.setQueryData(
if (prev && itemIdSet.has(prev.id)) { queryKey,
return { ...prev, userRating: rating }; (prev: AlbumArtistDetailResponse | undefined) => {
} if (prev && itemIdSet.has(prev.id)) {
return prev; return { ...prev, userRating: rating };
}); }
return prev;
},
);
} }
}); });
} }
@@ -203,27 +209,6 @@ export const applyRatingOptimisticUpdates = (
break; break;
} }
case LibraryItem.ARTIST: { case LibraryItem.ARTIST: {
const detailQueryKey = queryKeys.artists.detail(variables.apiClientProps.serverId);
const detailQueries = queryClient.getQueriesData({
exact: false,
queryKey: detailQueryKey,
});
if (detailQueries.length) {
detailQueries.forEach(([queryKey, data]) => {
if (data) {
previousQueries.push({ data, queryKey });
queryClient.setQueryData(queryKey, (prev: AlbumArtistDetailResponse) => {
if (prev && itemIdSet.has(prev.id)) {
return { ...prev, userRating: rating };
}
return prev;
});
}
});
}
const listQueryKey = queryKeys.artists.list(variables.apiClientProps.serverId); const listQueryKey = queryKeys.artists.list(variables.apiClientProps.serverId);
const listQueries = queryClient.getQueriesData({ const listQueries = queryClient.getQueriesData({
@@ -299,19 +284,22 @@ export const applyRatingOptimisticUpdates = (
albumDetailQueries.forEach(([queryKey, data]) => { albumDetailQueries.forEach(([queryKey, data]) => {
if (data) { if (data) {
previousQueries.push({ data, queryKey }); previousQueries.push({ data, queryKey });
queryClient.setQueryData(queryKey, (prev: AlbumDetailResponse) => { queryClient.setQueryData(
if (prev) { queryKey,
return { (prev: AlbumDetailResponse | undefined) => {
...prev, if (prev) {
songs: prev.songs?.map((song: Song) => { return {
return itemIdSet.has(song.id) ...prev,
? { ...song, userRating: rating } songs: prev.songs?.map((song: Song) => {
: song; return itemIdSet.has(song.id)
}), ? { ...song, userRating: rating }
}; : song;
} }),
return prev; };
}); }
return prev;
},
);
} }
}); });
} }
@@ -327,12 +315,15 @@ export const applyRatingOptimisticUpdates = (
detailQueries.forEach(([queryKey, data]) => { detailQueries.forEach(([queryKey, data]) => {
if (data) { if (data) {
previousQueries.push({ data, queryKey }); previousQueries.push({ data, queryKey });
queryClient.setQueryData(queryKey, (prev: SongDetailResponse) => { queryClient.setQueryData(
if (prev && itemIdSet.has(prev.id)) { queryKey,
return { ...prev, userRating: rating }; (prev: SongDetailResponse | undefined) => {
} if (prev && itemIdSet.has(prev.id)) {
return prev; return { ...prev, userRating: rating };
}); }
return prev;
},
);
} }
}); });
} }