YouBar+: fix presence updates never firing (wrong Flux event name) and inbox toggle needing a restart

This commit is contained in:
Ally Founder 2026-08-12 14:02:04 -04:00
parent d6d3c711c7
commit 1663602e72
3 changed files with 40 additions and 19 deletions

View file

@ -1,11 +1,10 @@
import { logger } from "@vendetta";
import { storage } from "@vendetta/plugin";
import patchYouBarButtons from "./patches/youBarButtons";
import { startTrackingNotifications } from "./lib/notifications";
import { setInboxTracking } from "./lib/notifications";
import Settings from "./ui/Settings";
let unpatchButtons: () => void = () => {};
let stopTrackingNotifications: () => void = () => {};
let patched = false;
let retryHandle: ReturnType<typeof setInterval> | undefined;
@ -39,11 +38,7 @@ export default {
storage.showInboxButton ??= false;
storage.notifications ??= [];
// Same "may require a restart" contract as the other buttons - only tracks in the
// background when the feature is actually on, not for every YouBar+ user by default.
if (storage.showInboxButton) {
stopTrackingNotifications = startTrackingNotifications();
}
setInboxTracking(!!storage.showInboxButton);
attempt();
if (!patched) {
@ -57,7 +52,7 @@ export default {
onUnload: () => {
stopRetrying();
unpatchButtons();
stopTrackingNotifications();
setInboxTracking(false);
},
settings: Settings
};

View file

@ -263,9 +263,10 @@ function handleThreadMembersUpdate(payload: any) {
// Friends only, no bots - a status update from a random server member or a bot's "Playing" state
// isn't the kind of thing worth an inbox entry for.
function handlePresenceUpdate(payload: any) {
function handlePresenceUpdate(update: any) {
try {
const userId = payload?.user?.id ?? payload?.userId;
const user = update?.user;
const userId = user?.id;
if (!userId) return;
const currentUser = UserStore?.getCurrentUser?.();
@ -273,11 +274,9 @@ function handlePresenceUpdate(payload: any) {
const friendIds: string[] = RelationshipStore?.getFriendIDs?.() ?? [];
if (!friendIds.includes(userId)) return;
if (user.bot) return;
const user = UserStore?.getUser?.(userId);
if (user?.bot) return;
const activities = payload?.activities;
const activities = update?.activities;
const activity = Array.isArray(activities) ? activities.find((a: any) => a?.type !== 4) : undefined;
const signature = activity ? `${activity.type}:${activity.name}` : "";
@ -287,7 +286,7 @@ function handlePresenceUpdate(payload: any) {
if (!activity) return; // cleared their activity - nothing to notify about
const verb = ACTIVITY_VERBS[activity.type] ?? "Playing";
const displayName = user?.globalName || user?.username || payload?.user?.globalName || payload?.user?.username || "A friend";
const displayName = user.globalName || user.username || "A friend";
pushNotification({
id: `presence-${userId}-${Date.now()}`,
@ -297,14 +296,24 @@ function handlePresenceUpdate(payload: any) {
guildName: "",
channelName: "",
timestamp: new Date().toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" }),
author: user ?? payload?.user,
author: user,
});
} catch (err) {
console.error("[YouBar+] Inbox presence error:", err);
}
}
export function startTrackingNotifications(): () => void {
// The raw gateway op is singular PRESENCE_UPDATE, but Discord's own socket layer batches them
// before they ever reach Flux - confirmed against the decompiled bundle's handlePresenceUpdates,
// which reads payload.updates (an array of {guildId, user, status, activities}). Subscribing to
// the singular name means this handler simply never fires.
function handlePresenceUpdates(payload: any) {
const updates = payload?.updates;
if (!Array.isArray(updates)) return;
for (const update of updates) handlePresenceUpdate(update);
}
function startTrackingNotifications(): () => void {
memoryNotifications = Array.isArray(storage.notifications) ? [...storage.notifications] : [];
const unsubMessage = fluxSubscribe("MESSAGE_CREATE", handleIncomingMessage);
@ -312,7 +321,7 @@ export function startTrackingNotifications(): () => void {
const unsubRelationship = fluxSubscribe("RELATIONSHIP_ADD", handleRelationshipAdd);
const unsubFriendAccepted = fluxSubscribe("FRIEND_REQUEST_ACCEPTED", handleFriendRequestAccepted);
const unsubThreadMembers = fluxSubscribe("THREAD_MEMBERS_UPDATE", handleThreadMembersUpdate);
const unsubPresence = fluxSubscribe("PRESENCE_UPDATE", handlePresenceUpdate);
const unsubPresence = fluxSubscribe("PRESENCE_UPDATES", handlePresenceUpdates);
return () => {
if (saveTimeout) clearTimeout(saveTimeout);
@ -326,3 +335,18 @@ export function startTrackingNotifications(): () => void {
lastActivitySignature.clear();
};
}
let stopTracking: () => void = () => {};
let tracking = false;
// Lets the settings toggle flip tracking on/off live instead of requiring a restart to take effect.
export function setInboxTracking(enabled: boolean) {
if (enabled === tracking) return;
tracking = enabled;
if (enabled) {
stopTracking = startTrackingNotifications();
} else {
stopTracking();
stopTracking = () => {};
}
}

View file

@ -5,6 +5,7 @@ import { TableRowGroup, TableSwitchRow } from "@shared/ui/table";
import SettingsScaffold from "@shared/ui/SettingsScaffold";
import NoteBox from "@shared/ui/NoteBox";
import { updateYouBar } from "../patches/youBarButtons";
import { setInboxTracking } from "../lib/notifications";
export default function Settings() {
@ -34,10 +35,11 @@ export default function Settings() {
/>
<TableSwitchRow
label="Inbox button"
subLabel="Show a categorized mentions/replies/reactions inbox in the YouBar - requires a restart to take effect"
subLabel="Show a categorized mentions/replies/reactions inbox in the YouBar"
value={!!storage.showInboxButton}
onValueChange={(v: boolean) => {
storage.showInboxButton = v;
setInboxTracking(v);
updateYouBar();
}}
/>