fix(app): show the server avatar on message notifications (#1879)

This commit is contained in:
Hampus authored and GitHub committed 2026-08-24 12:37:01 +02:00
1 parent e21544eac2
commit 5821a68816
5 files changed
+47 -14

No files matched your search

@@ -248,7 +248,7 @@ export const useRovingFocusList = <T extends HTMLElement>(options: UseRovingFocu
}, [node, enabled, getCachedFocusableElements]); }, [node, enabled, getCachedFocusableElements]);
useEffect(() => { useEffect(() => {
if (!node || !restoreFocusOnWindowFocus || !enabled) return; if (!node || !restoreFocusOnWindowFocus || !enabled) return;
const handleWindowFocus = () => { const handleWindowFocused = () => {
const {focusableSelector: selector, manageTabIndex: manage} = latestOptionsRef.current; const {focusableSelector: selector, manageTabIndex: manage} = latestOptionsRef.current;
const focusable = manage const focusable = manage
? getCachedFocusableElements(node, selector, true) ? getCachedFocusableElements(node, selector, true)
@@ -265,9 +265,9 @@ export const useRovingFocusList = <T extends HTMLElement>(options: UseRovingFocu
target.focus(); target.focus();
} }
}; };
window.addEventListener('focus', handleWindowFocus); window.addEventListener('focus', handleWindowFocused);
return () => { return () => {
window.removeEventListener('focus', handleWindowFocus); window.removeEventListener('focus', handleWindowFocused);
}; };
}, [node, restoreFocusOnWindowFocus, enabled, getCachedFocusableElements]); }, [node, restoreFocusOnWindowFocus, enabled, getCachedFocusableElements]);
useEffect(() => { useEffect(() => {
@@ -0,0 +1,35 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
import GuildMembers from '@app/features/member/state/GuildMembers';
import {isDesktop} from '@app/features/ui/utils/NativeUtils';
import type {User} from '@app/features/user/models/User';
import * as AvatarUtils from '@app/features/user/utils/AvatarUtils';
import type {MediaProxyImageSize} from '@fluxer/constants/src/MediaProxyImageSizes';
export const NATIVE_NOTIFICATION_ICON_CSS_SIZE: MediaProxyImageSize = 128;
export function getNotificationIconURL(user: Pick<User, 'id' | 'avatar'>, guildId?: string | null): string {
const member = guildId ? GuildMembers.getMember(guildId, user.id) : null;
const nativeIcon = isDesktop();
if (guildId && member) {
return nativeIcon
? AvatarUtils.getGuildMemberNotificationAvatarURL({
guildId,
userId: user.id,
avatar: user.avatar,
memberAvatar: member.avatar,
avatarUnset: member.isAvatarUnset(),
size: NATIVE_NOTIFICATION_ICON_CSS_SIZE,
})
: AvatarUtils.getGuildMemberDisplayAvatarURL({
guildId,
user,
memberAvatar: member.avatar,
avatarUnset: member.isAvatarUnset(),
animated: false,
});
}
return nativeIcon
? AvatarUtils.getUserNotificationAvatarURL(user, NATIVE_NOTIFICATION_ICON_CSS_SIZE)
: AvatarUtils.getUserAvatarURL(user, false);
}
@@ -2,6 +2,7 @@
import Authentication from '@app/features/auth/state/Authentication'; import Authentication from '@app/features/auth/state/Authentication';
import * as RouterUtils from '@app/features/navigation/utils/RouterUtils'; import * as RouterUtils from '@app/features/navigation/utils/RouterUtils';
import {getNotificationIconURL} from '@app/features/notification/utils/NotificationIconURL';
import {SoundType} from '@app/features/notification/utils/SoundUtils'; import {SoundType} from '@app/features/notification/utils/SoundUtils';
import type {NotificationAlertOptions} from '@app/features/platform/notifications/NotificationAlertOptions'; import type {NotificationAlertOptions} from '@app/features/platform/notifications/NotificationAlertOptions';
import {getNotificationAlertOptions} from '@app/features/platform/notifications/NotificationAlertOptions'; import {getNotificationAlertOptions} from '@app/features/platform/notifications/NotificationAlertOptions';
@@ -12,7 +13,6 @@ import Sound from '@app/features/ui/state/Sound';
import {getElectronAPI, hasUnavailableElectronNativeContext, isDesktop} from '@app/features/ui/utils/NativeUtils'; import {getElectronAPI, hasUnavailableElectronNativeContext, isDesktop} from '@app/features/ui/utils/NativeUtils';
import {isInstalledIOSPwa, isInstalledPwa, isMobileOrTablet} from '@app/features/ui/utils/PwaUtils'; import {isInstalledIOSPwa, isInstalledPwa, isMobileOrTablet} from '@app/features/ui/utils/PwaUtils';
import Users from '@app/features/user/state/Users'; import Users from '@app/features/user/state/Users';
import * as AvatarUtils from '@app/features/user/utils/AvatarUtils';
import type {I18n} from '@lingui/core'; import type {I18n} from '@lingui/core';
import {msg} from '@lingui/core/macro'; import {msg} from '@lingui/core/macro';
@@ -126,12 +126,13 @@ const requestBrowserPermission = async (): Promise<PermissionResult> => {
return 'denied'; return 'denied';
} }
}; };
const getCurrentUserAvatar = (): string | null => { const getCurrentUserAvatar = (): string | null => {
const currentUserId = Authentication.currentUserId; const currentUserId = Authentication.currentUserId;
if (!currentUserId) return null; if (!currentUserId) return null;
const currentUser = Users.getUser(currentUserId); const currentUser = Users.getUser(currentUserId);
if (!currentUser) return null; if (!currentUser) return null;
return AvatarUtils.getUserNotificationAvatarURL(currentUser); return getNotificationIconURL(currentUser);
}; };
async function handleDeniedPermission(i18n: I18n): Promise<void> { async function handleDeniedPermission(i18n: I18n): Promise<void> {
@@ -13,6 +13,7 @@ import * as MessageUtils from '@app/features/messaging/utils/MessageUtils';
import Navigation from '@app/features/navigation/state/Navigation'; import Navigation from '@app/features/navigation/state/Navigation';
import SelectedChannel from '@app/features/navigation/state/SelectedChannel'; import SelectedChannel from '@app/features/navigation/state/SelectedChannel';
import {buildMessageNotificationBody} from '@app/features/notification/utils/MessageNotificationPreview'; import {buildMessageNotificationBody} from '@app/features/notification/utils/MessageNotificationPreview';
import {getNotificationIconURL} from '@app/features/notification/utils/NotificationIconURL';
import * as NotificationUtils from '@app/features/notification/utils/NotificationUtils'; import * as NotificationUtils from '@app/features/notification/utils/NotificationUtils';
import * as PushSubscriptionService from '@app/features/platform/push/PushSubscriptionService'; import * as PushSubscriptionService from '@app/features/platform/push/PushSubscriptionService';
import {IS_DEV} from '@app/features/platform/types/Env'; import {IS_DEV} from '@app/features/platform/types/Env';
@@ -29,7 +30,6 @@ import {isInstalledPwa} from '@app/features/ui/utils/PwaUtils';
import type {User} from '@app/features/user/models/User'; import type {User} from '@app/features/user/models/User';
import UserGuildSettings from '@app/features/user/state/UserGuildSettings'; import UserGuildSettings from '@app/features/user/state/UserGuildSettings';
import Users from '@app/features/user/state/Users'; import Users from '@app/features/user/state/Users';
import * as AvatarUtils from '@app/features/user/utils/AvatarUtils';
import * as NicknameUtils from '@app/features/user/utils/NicknameUtils'; import * as NicknameUtils from '@app/features/user/utils/NicknameUtils';
import {FAVORITES_GUILD_ID as ME} from '@fluxer/constants/src/AppConstants'; import {FAVORITES_GUILD_ID as ME} from '@fluxer/constants/src/AppConstants';
import {ChannelTypes, MessageFlags, MessageTypes} from '@fluxer/constants/src/ChannelConstants'; import {ChannelTypes, MessageFlags, MessageTypes} from '@fluxer/constants/src/ChannelConstants';
@@ -283,10 +283,7 @@ class NotificationState {
} }
private markNotified(key: string): void { private markNotified(key: string): void {
const newCache = new LRUCache<string, boolean>({max: CACHE_SIZE}); this.notifiedMessageIds.set(key, true);
this.notifiedMessageIds.forEach((value, k) => newCache.set(k, value));
newCache.set(key, true);
this.notifiedMessageIds = newCache;
} }
private claimNotification(key: string): boolean { private claimNotification(key: string): boolean {
@@ -357,7 +354,7 @@ class NotificationState {
title, title,
subtitle, subtitle,
body, body,
icon: AvatarUtils.getUserNotificationAvatarURL(user), icon: getNotificationIconURL(user, channel.guildId),
url: notificationUrl, url: notificationUrl,
playSound: false, playSound: false,
}); });
@@ -447,7 +444,7 @@ class NotificationState {
this.unreadMessageBadgeEnabled = enabled; this.unreadMessageBadgeEnabled = enabled;
} }
handleWindowFocus({focused}: {focused: boolean}): void { handleWindowFocused({focused}: {focused: boolean}): void {
this.focused = focused; this.focused = focused;
if (focused) { if (focused) {
const channelId = SelectedChannel.currentChannelId; const channelId = SelectedChannel.currentChannelId;
@@ -524,7 +521,7 @@ class NotificationState {
id: cacheKey, id: cacheKey,
title, title,
body, body,
icon: AvatarUtils.getUserNotificationAvatarURL(user), icon: getNotificationIconURL(user),
url: Routes.ME, url: Routes.ME,
}).catch((error) => { }).catch((error) => {
logger.error('Failed to show relationship notification', {cacheKey}, error); logger.error('Failed to show relationship notification', {cacheKey}, error);
@@ -7,7 +7,7 @@ import Window from '@app/features/window/state/Window';
function applyFocusSideEffects(focused: boolean): void { function applyFocusSideEffects(focused: boolean): void {
GuildReadState.handleWindowFocused(); GuildReadState.handleWindowFocused();
Notification.handleWindowFocus({focused}); Notification.handleWindowFocused({focused});
if (focused) { if (focused) {
Idle.recordActivity(); Idle.recordActivity();
} }