PlatformIndicators: update indicators in real time if status changes

Closes #3516
This commit is contained in:
Vendicated 2025-09-23 23:58:38 +02:00
parent 746c824020
commit cb845b5224
No known key found for this signature in database
GPG key ID: D66986BAF75ECF18
4 changed files with 21 additions and 38 deletions

View file

@ -6,13 +6,15 @@ import type { FluxEvents } from "./fluxEvents";
export { FluxEvents }; export { FluxEvents };
type FluxEventsAutoComplete = LiteralUnion<FluxEvents, string>;
export interface FluxDispatcher { export interface FluxDispatcher {
_actionHandlers: any; _actionHandlers: any;
_subscriptions: any; _subscriptions: any;
dispatch(event: { [key: string]: unknown; type: FluxEvents; }): Promise<void>; dispatch(event: { [key: string]: unknown; type: FluxEventsAutoComplete; }): Promise<void>;
isDispatching(): boolean; isDispatching(): boolean;
subscribe(event: FluxEvents, callback: (data: any) => void): void; subscribe(event: FluxEventsAutoComplete, callback: (data: any) => void): void;
unsubscribe(event: FluxEvents, callback: (data: any) => void): void; unsubscribe(event: FluxEventsAutoComplete, callback: (data: any) => void): void;
wait(callback: () => void): void; wait(callback: () => void): void;
} }

View file

@ -222,7 +222,7 @@ export function subscribePluginFluxEvents(p: Plugin, fluxDispatcher: typeof Flux
for (const [event, handler] of Object.entries(p.flux)) { for (const [event, handler] of Object.entries(p.flux)) {
const wrappedHandler = p.flux[event] = function () { const wrappedHandler = p.flux[event] = function () {
try { try {
const res = handler.apply(p, arguments as any); const res = handler!.apply(p, arguments as any);
return res instanceof Promise return res instanceof Promise
? res.catch(e => logger.error(`${p.name}: Error while handling ${event}\n`, e)) ? res.catch(e => logger.error(`${p.name}: Error while handling ${event}\n`, e))
: res; : res;
@ -242,7 +242,7 @@ export function unsubscribePluginFluxEvents(p: Plugin, fluxDispatcher: typeof Fl
logger.debug("Unsubscribing from flux events of plugin", p.name); logger.debug("Unsubscribing from flux events of plugin", p.name);
for (const [event, handler] of Object.entries(p.flux)) { for (const [event, handler] of Object.entries(p.flux)) {
fluxDispatcher.unsubscribe(event as FluxEvents, handler); fluxDispatcher.unsubscribe(event as FluxEvents, handler!);
} }
} }
} }

View file

@ -22,12 +22,11 @@ import { addProfileBadge, BadgePosition, BadgeUserArgs, ProfileBadge, removeProf
import { addMemberListDecorator, removeMemberListDecorator } from "@api/MemberListDecorators"; import { addMemberListDecorator, removeMemberListDecorator } from "@api/MemberListDecorators";
import { addMessageDecoration, removeMessageDecoration } from "@api/MessageDecorations"; import { addMessageDecoration, removeMessageDecoration } from "@api/MessageDecorations";
import { Settings } from "@api/Settings"; import { Settings } from "@api/Settings";
import ErrorBoundary from "@components/ErrorBoundary";
import { Devs } from "@utils/constants"; import { Devs } from "@utils/constants";
import definePlugin, { OptionType } from "@utils/types"; import definePlugin, { OptionType } from "@utils/types";
import { User } from "@vencord/discord-types"; import { User } from "@vencord/discord-types";
import { filters, findStoreLazy, mapMangledModuleLazy } from "@webpack"; import { filters, findStoreLazy, mapMangledModuleLazy } from "@webpack";
import { PresenceStore, Tooltip, UserStore } from "@webpack/common"; import { AuthenticationStore, PresenceStore, Tooltip, UserStore, useStateFromStores } from "@webpack/common";
export interface Session { export interface Session {
sessionId: string; sessionId: string;
@ -85,7 +84,7 @@ const PlatformIcon = ({ platform, status, small }: { platform: Platform, status:
}; };
function ensureOwnStatus(user: User) { function ensureOwnStatus(user: User) {
if (user.id === UserStore.getCurrentUser().id) { if (user.id === AuthenticationStore.getId()) {
const sessions = SessionsStore.getSessions(); const sessions = SessionsStore.getSessions();
if (typeof sessions !== "object") return null; if (typeof sessions !== "object") return null;
const sortedSessions = Object.values(sessions).sort(({ status: a }, { status: b }) => { const sortedSessions = Object.values(sessions).sort(({ status: a }, { status: b }) => {
@ -104,7 +103,7 @@ function ensureOwnStatus(user: User) {
}, {}); }, {});
const { clientStatuses } = PresenceStore.getState(); const { clientStatuses } = PresenceStore.getState();
clientStatuses[UserStore.getCurrentUser().id] = ownStatus; clientStatuses[AuthenticationStore.getId()] = ownStatus;
} }
} }
@ -115,7 +114,7 @@ function getBadges({ userId }: BadgeUserArgs): ProfileBadge[] {
ensureOwnStatus(user); ensureOwnStatus(user);
const status = PresenceStore.getState()?.clientStatuses?.[user.id] as Record<Platform, string>; const status = PresenceStore.getClientStatus(user.id) as Record<Platform, string>;
if (!status) return []; if (!status) return [];
return Object.entries(status).map(([platform, status]) => ({ return Object.entries(status).map(([platform, status]) => ({
@ -134,11 +133,9 @@ function getBadges({ userId }: BadgeUserArgs): ProfileBadge[] {
} }
const PlatformIndicator = ({ user, small = false }: { user: User; small?: boolean; }) => { const PlatformIndicator = ({ user, small = false }: { user: User; small?: boolean; }) => {
if (!user || user.bot) return null;
ensureOwnStatus(user); ensureOwnStatus(user);
const status = PresenceStore.getState()?.clientStatuses?.[user.id] as Record<Platform, string>; const status = useStateFromStores([PresenceStore], () => PresenceStore.getClientStatus(user.id) as Record<Platform, string>);
if (!status) return null; if (!status) return null;
const icons = Object.entries(status).map(([platform, status]) => ( const icons = Object.entries(status).map(([platform, status]) => (
@ -170,10 +167,8 @@ const badge: ProfileBadge = {
const indicatorLocations = { const indicatorLocations = {
list: { list: {
description: "In the member list", description: "In the member list",
onEnable: () => addMemberListDecorator("platform-indicator", props => onEnable: () => addMemberListDecorator("platform-indicator", ({ user }) =>
<ErrorBoundary noop> user && !user.bot ? <PlatformIndicator user={user} small={true} /> : null
<PlatformIndicator user={props.user} small={true} />
</ErrorBoundary>
), ),
onDisable: () => removeMemberListDecorator("platform-indicator") onDisable: () => removeMemberListDecorator("platform-indicator")
}, },
@ -184,11 +179,10 @@ const indicatorLocations = {
}, },
messages: { messages: {
description: "Inside messages", description: "Inside messages",
onEnable: () => addMessageDecoration("platform-indicator", props => onEnable: () => addMessageDecoration("platform-indicator", props => {
<ErrorBoundary noop> const user = props.message?.author;
<PlatformIndicator user={props.message?.author} /> return user && !user.bot ? <PlatformIndicator user={props.message?.author} /> : null;
</ErrorBoundary> }),
),
onDisable: () => removeMessageDecoration("platform-indicator") onDisable: () => removeMessageDecoration("platform-indicator")
} }
}; };
@ -201,19 +195,6 @@ export default definePlugin({
start() { start() {
const settings = Settings.plugins.PlatformIndicators; const settings = Settings.plugins.PlatformIndicators;
const { displayMode } = settings;
// transfer settings from the old ones, which had a select menu instead of booleans
if (displayMode) {
if (displayMode !== "both") settings[displayMode] = true;
else {
settings.list = true;
settings.badges = true;
}
settings.messages = true;
delete settings.displayMode;
}
Object.entries(indicatorLocations).forEach(([key, value]) => { Object.entries(indicatorLocations).forEach(([key, value]) => {
if (settings[key]) value.onEnable(); if (settings[key]) value.onEnable();
}); });

View file

@ -151,9 +151,9 @@ export interface PluginDef {
/** /**
* Allows you to subscribe to Flux events * Allows you to subscribe to Flux events
*/ */
flux?: { flux?: Partial<{
[E in LiteralUnion<FluxEvents, string>]?: (event: any) => void | Promise<void>; [E in LiteralUnion<FluxEvents, string>]: (event: any) => void | Promise<void>;
}; }>;
/** /**
* Allows you to manipulate context menus * Allows you to manipulate context menus
*/ */