fix(channels): every subscribe and unsubscribe refreshes the same reads
Each of the four sites kept its own invalidation list and each missed something (the channel menu's unsubscribe left the feed showing the channel's videos; the channel page's left the counts stale). lib/subscriptionCache invalidateSubscription is the one set; a contract test holds every call site to it.
This commit is contained in:
@@ -2,6 +2,7 @@ import { useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import { qk } from "../lib/queryKeys";
|
||||
import { invalidateSubscription } from "../lib/subscriptionCache";
|
||||
import { UserPlus } from "lucide-react";
|
||||
import { api, type DiscoveredChannel } from "../lib/api";
|
||||
import { accountKey, LS } from "../lib/storage";
|
||||
@@ -58,9 +59,7 @@ export default function ChannelDiscovery({
|
||||
onSuccess: (_data, c) => {
|
||||
// The channel moves from "discovery" to "subscribed", and its videos will start
|
||||
// arriving — refresh both lists plus the per-user status.
|
||||
qc.invalidateQueries({ queryKey: qk.discoveredChannels() });
|
||||
qc.invalidateQueries({ queryKey: qk.channels() });
|
||||
qc.invalidateQueries({ queryKey: qk.myStatus() });
|
||||
invalidateSubscription(qc, c.id);
|
||||
const name = c.title ?? c.id;
|
||||
// Name the channel and ship a typed payload so the inbox can offer "open in the
|
||||
// Channel manager" / "open on YouTube" links — kept after reload, when live
|
||||
|
||||
@@ -4,6 +4,7 @@ import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import { useLiveQuery } from "../lib/useLiveQuery";
|
||||
import { channelDetailPollMs, useBackfillFeedRefresh } from "../lib/channelDetailPoll";
|
||||
import { qk } from "../lib/queryKeys";
|
||||
import { invalidateSubscription } from "../lib/subscriptionCache";
|
||||
import { useNavigationActions } from "./navigationContext";
|
||||
import { PriorityStepper } from "./ui/PriorityStepper";
|
||||
import { useChannelPriority } from "../lib/useChannelPriority";
|
||||
@@ -181,12 +182,7 @@ export default function ChannelPage({
|
||||
const subscribe = useMutation({
|
||||
mutationFn: () => api.subscribeChannel(channelId),
|
||||
onSuccess: () => {
|
||||
qc.invalidateQueries({ queryKey: qk.channel(channelId) });
|
||||
qc.invalidateQueries({ queryKey: qk.feed() });
|
||||
qc.invalidateQueries({ queryKey: qk.channels() });
|
||||
// Match ChannelDiscovery: the channel leaves discovery and the manager's stats move.
|
||||
qc.invalidateQueries({ queryKey: qk.myStatus() });
|
||||
qc.invalidateQueries({ queryKey: qk.discoveredChannels() });
|
||||
invalidateSubscription(qc, channelId);
|
||||
notify({ level: "info", message: t("channel.subscribed", { name: ch?.title ?? "" }) });
|
||||
},
|
||||
// A 403 (missing write scope) otherwise fails silently on the channel page (no wizard handle
|
||||
@@ -195,11 +191,7 @@ export default function ChannelPage({
|
||||
});
|
||||
const unsubscribe = useMutation({
|
||||
mutationFn: () => api.unsubscribeChannel(channelId),
|
||||
onSuccess: () => {
|
||||
qc.invalidateQueries({ queryKey: qk.channel(channelId) });
|
||||
qc.invalidateQueries({ queryKey: qk.feed() });
|
||||
qc.invalidateQueries({ queryKey: qk.channels() });
|
||||
},
|
||||
onSuccess: () => invalidateSubscription(qc, channelId),
|
||||
onError: (err) => notifyYouTubeActionError(err, t("channels.notify.unsubscribeFailed")),
|
||||
});
|
||||
// "Get full history": the catalog only holds the recent-backfill/RSS subset of a channel's
|
||||
|
||||
@@ -2,6 +2,7 @@ import { useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { ArrowUpDown, EyeOff, ExternalLink, History, Tags, Tv, UserMinus } from "lucide-react";
|
||||
import { qk } from "../lib/queryKeys";
|
||||
import { invalidateSubscription } from "../lib/subscriptionCache";
|
||||
import { api, type ManagedChannel, type Tag } from "../lib/api";
|
||||
import { priorityChoices } from "../lib/channelOrder";
|
||||
import { channelYouTubeUrl } from "../lib/format";
|
||||
@@ -79,8 +80,7 @@ export function useChannelActions({
|
||||
const unsubscribe = useMutation({
|
||||
mutationFn: (c: ManagedChannel) => api.unsubscribeChannel(c.id),
|
||||
onSuccess: (_d, c) => {
|
||||
qc.invalidateQueries({ queryKey: qk.channels() });
|
||||
qc.invalidateQueries({ queryKey: qk.myStatus() });
|
||||
invalidateSubscription(qc, c.id);
|
||||
notify({ level: "success", message: t("channels.notify.unsubscribed", { name: nameOf(c) }) });
|
||||
},
|
||||
onError: (e) =>
|
||||
|
||||
Reference in New Issue
Block a user