fix(app): render mutual guilds correctly regardless of cache (#46)

This commit is contained in:
hampus-fluxer 2026-01-06 03:53:48 +01:00 committed by GitHub
parent 6f21a7e37b
commit 30869f8c3d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 49 additions and 12 deletions

View File

@ -94,6 +94,7 @@ import type {ContextMenuTargetElement} from '~/stores/ContextMenuStore';
import ContextMenuStore, {isContextMenuNodeTarget} from '~/stores/ContextMenuStore'; import ContextMenuStore, {isContextMenuNodeTarget} from '~/stores/ContextMenuStore';
import DeveloperOptionsStore from '~/stores/DeveloperOptionsStore'; import DeveloperOptionsStore from '~/stores/DeveloperOptionsStore';
import GuildMemberStore from '~/stores/GuildMemberStore'; import GuildMemberStore from '~/stores/GuildMemberStore';
import GuildStore from '~/stores/GuildStore';
import MemberPresenceSubscriptionStore from '~/stores/MemberPresenceSubscriptionStore'; import MemberPresenceSubscriptionStore from '~/stores/MemberPresenceSubscriptionStore';
import ModalStore from '~/stores/ModalStore'; import ModalStore from '~/stores/ModalStore';
import PermissionStore from '~/stores/PermissionStore'; import PermissionStore from '~/stores/PermissionStore';
@ -332,7 +333,22 @@ const ProfileModalContent: React.FC<ProfileModalContentProps> = observer(
ModalActionCreators.push(modal(() => <CustomStatusModal />)); ModalActionCreators.push(modal(() => <CustomStatusModal />));
}, []); }, []);
const mutualGuilds = React.useMemo(() => GuildMemberStore.getMutualGuilds(user.id), [user.id]); const profileMutualGuilds = profile?.mutualGuilds ?? [];
type MutualGuildDisplay = {
guild: GuildRecord;
nick: string | null;
};
const mutualGuildDisplayItems = React.useMemo(() => {
return profileMutualGuilds
.map((mutualGuild) => {
const guild = GuildStore.getGuild(mutualGuild.id);
if (!guild) {
return null;
}
return {guild, nick: mutualGuild.nick};
})
.filter((item): item is MutualGuildDisplay => item !== null);
}, [profileMutualGuilds]);
const mutualGroups = ChannelStore.dmChannels.filter( const mutualGroups = ChannelStore.dmChannels.filter(
(channel) => channel.isGroupDM() && channel.recipientIds.includes(user.id), (channel) => channel.isGroupDM() && channel.recipientIds.includes(user.id),
); );
@ -355,14 +371,14 @@ const ProfileModalContent: React.FC<ProfileModalContentProps> = observer(
const count = mutualGroups.length; const count = mutualGroups.length;
return t`Mutual Groups (${count})`; return t`Mutual Groups (${count})`;
} }
default: { default: {
const count = mutualGuilds.length; const count = profileMutualGuilds.length;
return t`Mutual Communities (${count})`; return t`Mutual Communities (${count})`;
}
} }
}, }
[t, mutualFriendsCount, mutualGroups.length, mutualGuilds.length], },
); [t, mutualFriendsCount, mutualGroups.length, profileMutualGuilds.length],
);
const openMutualMenu = React.useCallback( const openMutualMenu = React.useCallback(
(event: React.MouseEvent<HTMLButtonElement>) => { (event: React.MouseEvent<HTMLButtonElement>) => {
@ -571,16 +587,17 @@ const ProfileModalContent: React.FC<ProfileModalContentProps> = observer(
const renderMutualGuildsList = React.useCallback(() => { const renderMutualGuildsList = React.useCallback(() => {
return ( return (
<div className={userProfileModalStyles.mutualFriendsList}> <div className={userProfileModalStyles.mutualFriendsList}>
{mutualGuilds.map((guild) => ( {mutualGuildDisplayItems.map(({guild, nick}) => (
<MutualGuildItem <MutualGuildItem
key={guild.id} key={guild.id}
guild={guild} guild={guild}
nick={nick}
onClick={() => handleGuildClick(guild)} onClick={() => handleGuildClick(guild)}
onContextMenu={(e) => handleGuildContextMenu(e, guild)} onContextMenu={(e) => handleGuildContextMenu(e, guild)}
isContextMenuOpen={isContextMenuOpenFor} isContextMenuOpen={isContextMenuOpenFor}
/> />
))} ))}
{mutualGuilds.length === 0 && ( {profileMutualGuilds.length === 0 && (
<div className={userProfileModalStyles.emptyState}> <div className={userProfileModalStyles.emptyState}>
<UsersThreeIcon className={userProfileModalStyles.emptyStateIcon} /> <UsersThreeIcon className={userProfileModalStyles.emptyStateIcon} />
<Trans>No mutual communities found.</Trans> <Trans>No mutual communities found.</Trans>
@ -588,7 +605,13 @@ const ProfileModalContent: React.FC<ProfileModalContentProps> = observer(
)} )}
</div> </div>
); );
}, [handleGuildClick, handleGuildContextMenu, isContextMenuOpenFor, mutualGuilds]); }, [
handleGuildClick,
handleGuildContextMenu,
isContextMenuOpenFor,
mutualGuildDisplayItems,
profileMutualGuilds.length,
]);
const renderMutualTabContent = React.useCallback(() => { const renderMutualTabContent = React.useCallback(() => {
switch (mutualView) { switch (mutualView) {
@ -751,11 +774,13 @@ const MutualFriendItem = ({
const MutualGuildItem = ({ const MutualGuildItem = ({
guild, guild,
nick,
onClick, onClick,
onContextMenu, onContextMenu,
isContextMenuOpen, isContextMenuOpen,
}: { }: {
guild: GuildRecord; guild: GuildRecord;
nick: string | null;
onClick: () => void; onClick: () => void;
onContextMenu: (e: React.MouseEvent) => void; onContextMenu: (e: React.MouseEvent) => void;
isContextMenuOpen: (target: EventTarget | null) => boolean; isContextMenuOpen: (target: EventTarget | null) => boolean;
@ -782,6 +807,7 @@ const MutualGuildItem = ({
/> />
<div className={userProfileModalStyles.mutualFriendInfo}> <div className={userProfileModalStyles.mutualFriendInfo}>
<span className={userProfileModalStyles.mutualFriendName}>{guild.name}</span> <span className={userProfileModalStyles.mutualFriendName}>{guild.name}</span>
{nick && <span className={userProfileModalStyles.mutualFriendUsername}>{nick}</span>}
</div> </div>
</div> </div>
); );

View File

@ -24,6 +24,11 @@ import GuildMemberStore from '~/stores/GuildMemberStore';
import GuildStore from '~/stores/GuildStore'; import GuildStore from '~/stores/GuildStore';
import UserStore from '~/stores/UserStore'; import UserStore from '~/stores/UserStore';
export type ProfileMutualGuild = Readonly<{
id: string;
nick: string | null;
}>;
export type Profile = Readonly<{ export type Profile = Readonly<{
user: UserPartial; user: UserPartial;
user_profile: UserProfile; user_profile: UserProfile;
@ -34,6 +39,7 @@ export type Profile = Readonly<{
premium_since?: string; premium_since?: string;
premium_lifetime_sequence?: number; premium_lifetime_sequence?: number;
mutual_friends?: Array<UserPartial>; mutual_friends?: Array<UserPartial>;
mutual_guilds?: Array<ProfileMutualGuild>;
}>; }>;
export class ProfileRecord { export class ProfileRecord {
@ -46,6 +52,7 @@ export class ProfileRecord {
readonly premiumSince: Date | null; readonly premiumSince: Date | null;
readonly premiumLifetimeSequence: number | null; readonly premiumLifetimeSequence: number | null;
readonly mutualFriends: ReadonlyArray<UserPartial> | null; readonly mutualFriends: ReadonlyArray<UserPartial> | null;
readonly mutualGuilds: ReadonlyArray<ProfileMutualGuild> | null;
constructor(profile: Profile, guildId?: string) { constructor(profile: Profile, guildId?: string) {
this.userId = profile.user.id; this.userId = profile.user.id;
@ -57,6 +64,7 @@ export class ProfileRecord {
this.premiumSince = profile.premium_since ? new Date(profile.premium_since) : null; this.premiumSince = profile.premium_since ? new Date(profile.premium_since) : null;
this.premiumLifetimeSequence = profile.premium_lifetime_sequence ?? null; this.premiumLifetimeSequence = profile.premium_lifetime_sequence ?? null;
this.mutualFriends = profile.mutual_friends ? Object.freeze([...profile.mutual_friends]) : null; this.mutualFriends = profile.mutual_friends ? Object.freeze([...profile.mutual_friends]) : null;
this.mutualGuilds = profile.mutual_guilds ? Object.freeze([...profile.mutual_guilds]) : null;
} }
withUpdates(updates: Partial<Profile>): ProfileRecord { withUpdates(updates: Partial<Profile>): ProfileRecord {
@ -76,6 +84,7 @@ export class ProfileRecord {
? updates.premium_lifetime_sequence ? updates.premium_lifetime_sequence
: (this.premiumLifetimeSequence ?? undefined), : (this.premiumLifetimeSequence ?? undefined),
mutual_friends: updates.mutual_friends ?? (this.mutualFriends ? [...this.mutualFriends] : undefined), mutual_friends: updates.mutual_friends ?? (this.mutualFriends ? [...this.mutualFriends] : undefined),
mutual_guilds: updates.mutual_guilds ?? (this.mutualGuilds ? [...this.mutualGuilds] : undefined),
}, },
this.guildId ?? undefined, this.guildId ?? undefined,
); );
@ -129,7 +138,8 @@ export class ProfileRecord {
this.premiumType === other.premiumType && this.premiumType === other.premiumType &&
this.premiumSince === other.premiumSince && this.premiumSince === other.premiumSince &&
this.premiumLifetimeSequence === other.premiumLifetimeSequence && this.premiumLifetimeSequence === other.premiumLifetimeSequence &&
JSON.stringify(this.mutualFriends) === JSON.stringify(other.mutualFriends) JSON.stringify(this.mutualFriends) === JSON.stringify(other.mutualFriends) &&
JSON.stringify(this.mutualGuilds) === JSON.stringify(other.mutualGuilds)
); );
} }
@ -143,6 +153,7 @@ export class ProfileRecord {
premium_since: this.premiumSince?.toISOString() ?? undefined, premium_since: this.premiumSince?.toISOString() ?? undefined,
premium_lifetime_sequence: this.premiumLifetimeSequence ?? undefined, premium_lifetime_sequence: this.premiumLifetimeSequence ?? undefined,
mutual_friends: this.mutualFriends ? [...this.mutualFriends] : undefined, mutual_friends: this.mutualFriends ? [...this.mutualFriends] : undefined,
mutual_guilds: this.mutualGuilds ? [...this.mutualGuilds] : undefined,
}; };
} }
} }