chore(channels-ui): Phase 2 #3 frontend cleanup — dedup 403 handler, count cell, LS keys, dead i18n
- Extract notifyYouTubeActionError (new lib/youtubeErrors.ts): the "403 → Connect your YouTube account, else fallback toast" logic was duplicated in Channels.tsx and ChannelDiscovery.tsx. (Separate file, not lib/notifications, to avoid the api ↔ notifications import cycle.) - Extract format.formatCountOrDash(): the `n != null ? formatViews(n) : "—"` count cell was repeated across the subs/videos columns in Channels + ChannelDiscovery. - Register channelsTable / channelDiscoveryTable in storage.ts LS instead of bare "siftlode.*" string literals. - Delete 7 dead channels.json keys (filterPlaceholder, tags.newTag/createTag, row.stored/subs/getFullHistory/getFullHistoryHint) from en/hu/de — verified 0 refs (createTag matches were the unrelated api.createTag method, not the key). Behavior-neutral. tsc green, knip no new unused, localdev boots.
This commit is contained in:
parent
8fb41383e6
commit
ee601363c2
8 changed files with 54 additions and 71 deletions
|
|
@ -16,10 +16,11 @@ import {
|
|||
UserMinus,
|
||||
X,
|
||||
} from "lucide-react";
|
||||
import { api, HttpError, type ManagedChannel, type Tag } from "../lib/api";
|
||||
import { accountKey } from "../lib/storage";
|
||||
import { api, type ManagedChannel, type Tag } from "../lib/api";
|
||||
import { notifyYouTubeActionError } from "../lib/youtubeErrors";
|
||||
import { accountKey, LS } from "../lib/storage";
|
||||
import { useDismiss } from "../lib/useDismiss";
|
||||
import { formatEta, formatTotalHours, formatViews, relativeTime } from "../lib/format";
|
||||
import { formatCountOrDash, formatEta, formatTotalHours, relativeTime } from "../lib/format";
|
||||
import { notify } from "../lib/notifications";
|
||||
import Tooltip from "./Tooltip";
|
||||
import DataTable, { type Column } from "./DataTable";
|
||||
|
|
@ -73,19 +74,6 @@ export default function Channels({
|
|||
const qc = useQueryClient();
|
||||
const confirm = useConfirm();
|
||||
|
||||
// A YouTube-gated action (sync, backfill, unsubscribe) that 403s means the user hasn't
|
||||
// granted the needed scope — surface that with a "Connect" action instead of a vague fail.
|
||||
const notifyActionError = (err: unknown, fallbackKey: string) => {
|
||||
if (err instanceof HttpError && err.status === 403) {
|
||||
notify({
|
||||
level: "error",
|
||||
message: t("channels.notify.needYouTube"),
|
||||
action: { label: t("channels.notify.connect"), onClick: onOpenWizard },
|
||||
});
|
||||
} else {
|
||||
notify({ level: "error", message: t(fallbackKey) });
|
||||
}
|
||||
};
|
||||
const channelsQuery = useQuery({ queryKey: ["channels"], queryFn: api.channels });
|
||||
const tagsQuery = useQuery({ queryKey: ["tags"], queryFn: api.tags });
|
||||
const statusQuery = useQuery({ queryKey: ["my-status"], queryFn: api.myStatus });
|
||||
|
|
@ -160,7 +148,7 @@ export default function Channels({
|
|||
invalidate();
|
||||
notify({ level: "success", message: t("channels.notify.synced", { count: r.subscriptions ?? 0 }) });
|
||||
},
|
||||
onError: (e) => notifyActionError(e, "channels.notify.syncFailed"),
|
||||
onError: (e) => notifyYouTubeActionError(e, t("channels.notify.syncFailed"), onOpenWizard),
|
||||
});
|
||||
const unsubscribe = useMutation({
|
||||
mutationFn: (v: { id: string; name: string }) => api.unsubscribeChannel(v.id),
|
||||
|
|
@ -169,7 +157,7 @@ export default function Channels({
|
|||
qc.invalidateQueries({ queryKey: ["my-status"] });
|
||||
notify({ level: "success", message: t("channels.notify.unsubscribed", { name: v.name }) });
|
||||
},
|
||||
onError: (e) => notifyActionError(e, "channels.notify.unsubscribeFailed"),
|
||||
onError: (e) => notifyYouTubeActionError(e, t("channels.notify.unsubscribeFailed"), onOpenWizard),
|
||||
});
|
||||
const resetBackfill = useMutation({
|
||||
mutationFn: (v: { id: string; name: string }) => api.resetChannelBackfill(v.id),
|
||||
|
|
@ -178,7 +166,7 @@ export default function Channels({
|
|||
qc.invalidateQueries({ queryKey: ["my-status"] });
|
||||
notify({ level: "success", message: t("channels.notify.resetDone", { name: v.name }) });
|
||||
},
|
||||
onError: (e) => notifyActionError(e, "channels.notify.resetFailed"),
|
||||
onError: (e) => notifyYouTubeActionError(e, t("channels.notify.resetFailed"), onOpenWizard),
|
||||
});
|
||||
const deepAll = useMutation({
|
||||
mutationFn: () => api.deepAll(true),
|
||||
|
|
@ -190,7 +178,7 @@ export default function Channels({
|
|||
message: t("channels.notify.fullHistoryRequested", { count: r.updated ?? 0 }),
|
||||
});
|
||||
},
|
||||
onError: (e) => notifyActionError(e, "channels.notify.fullHistoryFailed"),
|
||||
onError: (e) => notifyYouTubeActionError(e, t("channels.notify.fullHistoryFailed"), onOpenWizard),
|
||||
});
|
||||
|
||||
// Channel-name search + tag-chip filtering, applied client-side over the status-filtered list
|
||||
|
|
@ -294,9 +282,7 @@ export default function Channels({
|
|||
sortable: true,
|
||||
sortValue: (c) => c.subscriber_count ?? -1,
|
||||
render: (c) => (
|
||||
<span className="text-muted tabular-nums">
|
||||
{c.subscriber_count != null ? formatViews(c.subscriber_count) : "—"}
|
||||
</span>
|
||||
<span className="text-muted tabular-nums">{formatCountOrDash(c.subscriber_count)}</span>
|
||||
),
|
||||
},
|
||||
{
|
||||
|
|
@ -570,7 +556,7 @@ export default function Channels({
|
|||
rows={visibleChannels}
|
||||
columns={columns}
|
||||
rowKey={(c) => c.id}
|
||||
persistKey={accountKey("siftlode.channelsTable") ?? undefined}
|
||||
persistKey={accountKey(LS.channelsTable) ?? undefined}
|
||||
controlsPosition="top"
|
||||
controlsLeading={statusChips}
|
||||
rowClassName={(c) => (c.hidden ? "opacity-60" : "")}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue