mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-07 19:22:14 +09:00
Merge remote-tracking branch 'origin/main'
This commit is contained in:
@@ -418,6 +418,16 @@ function formatGuildChannelDetail(guild: Guild | undefined, channel: Channel): s
|
||||
return detail === '' ? null : detail;
|
||||
}
|
||||
|
||||
function formatGroupDMDetail(channel: Channel): string | null {
|
||||
if (channel.type !== ChannelTypes.GROUP_DM || (channel.name?.trim() ?? '') === '') return null;
|
||||
const names: Array<string> = [];
|
||||
for (const recipientId of channel.recipientIds) {
|
||||
const recipient = Users.getUser(recipientId);
|
||||
if (recipient != null) names.push(NicknameUtils.getNickname(recipient, null, channel.id));
|
||||
}
|
||||
return names.length === 0 ? null : names.join(', ');
|
||||
}
|
||||
|
||||
function resolveGuildChannelDisableReason(
|
||||
channel: Channel,
|
||||
guild: Guild | undefined,
|
||||
@@ -469,7 +479,7 @@ function resolveForwardDestinationOption(
|
||||
return Object.freeze({
|
||||
channel,
|
||||
destination,
|
||||
detail: null,
|
||||
detail: formatGroupDMDetail(channel),
|
||||
disableReason: resolveAgeRestrictedDisableReason(channel, mediaNeeds, i18n),
|
||||
displayName: ChannelUtils.getDMDisplayName(channel),
|
||||
key,
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
/* SPDX-License-Identifier: AGPL-3.0-or-later */
|
||||
|
||||
.frame {
|
||||
--forward-icon-size: 2rem;
|
||||
--forward-badge-size: 1.125rem;
|
||||
--forward-badge-offset: -0.1875rem;
|
||||
--forward-badge-gap: 0.125rem;
|
||||
--forward-badge-center: calc(var(--forward-icon-size) - var(--forward-badge-size) / 2 - var(--forward-badge-offset));
|
||||
--forward-badge-cutout: calc(var(--forward-badge-size) / 2 + var(--forward-badge-gap));
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
width: var(--forward-icon-size);
|
||||
height: var(--forward-icon-size);
|
||||
}
|
||||
|
||||
.cutout {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
mask-image: radial-gradient(
|
||||
circle at var(--forward-badge-center) var(--forward-badge-center),
|
||||
transparent var(--forward-badge-cutout),
|
||||
#000 calc(var(--forward-badge-cutout) + 0.03125rem)
|
||||
);
|
||||
}
|
||||
|
||||
.badge {
|
||||
position: absolute;
|
||||
right: var(--forward-badge-offset);
|
||||
bottom: var(--forward-badge-offset);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: var(--forward-badge-size);
|
||||
height: var(--forward-badge-size);
|
||||
border-radius: 50%;
|
||||
background-color: var(--background-tertiary);
|
||||
color: var(--text-primary-muted);
|
||||
}
|
||||
|
||||
.badgeIcon {
|
||||
width: 0.75rem;
|
||||
height: 0.75rem;
|
||||
}
|
||||
|
||||
.fallbackIcon {
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
flex-shrink: 0;
|
||||
color: var(--text-primary-muted);
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import type {ForwardDestinationOption} from '@app/features/app/components/dialogs/shared/UseForwardDestinations';
|
||||
import {GroupDMAvatar} from '@app/features/app/components/shared/GroupDMAvatar';
|
||||
import type {Channel} from '@app/features/channel/models/Channel';
|
||||
import * as ChannelUtils from '@app/features/channel/utils/ChannelUtils';
|
||||
import {GuildIcon} from '@app/features/guild/components/popouts/GuildIcon';
|
||||
import Guilds from '@app/features/guild/state/Guilds';
|
||||
import styles from '@app/features/messaging/components/modals/ForwardDestinationIcon.module.css';
|
||||
import {Avatar} from '@app/features/ui/components/Avatar';
|
||||
import {StatusAwareAvatar} from '@app/features/ui/components/StatusAwareAvatar';
|
||||
import Users from '@app/features/user/state/Users';
|
||||
import {ChannelTypes} from '@fluxer/constants/src/ChannelConstants';
|
||||
import {observer} from 'mobx-react-lite';
|
||||
import type {ReactNode} from 'react';
|
||||
|
||||
const DESTINATION_ICON_SIZE = 32;
|
||||
|
||||
function BadgedIcon({badge, children}: {badge: ReactNode; children: ReactNode}) {
|
||||
return (
|
||||
<div className={styles.frame} data-flx="messaging.forward-destination-icon.frame">
|
||||
<div className={styles.cutout} data-flx="messaging.forward-destination-icon.cutout">
|
||||
{children}
|
||||
</div>
|
||||
<div className={styles.badge} data-flx="messaging.forward-destination-icon.badge">
|
||||
{badge}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function renderChannelBadge(channel: Channel) {
|
||||
return ChannelUtils.getIcon(channel, {className: styles.badgeIcon, weight: 'bold'});
|
||||
}
|
||||
|
||||
function renderFallbackIcon(channel: Channel) {
|
||||
return ChannelUtils.getIcon(channel, {className: styles.fallbackIcon, weight: 'bold'});
|
||||
}
|
||||
|
||||
function renderGuildChannelIcon(channel: Channel) {
|
||||
const guild = channel.guildId == null ? undefined : Guilds.getGuild(channel.guildId);
|
||||
if (guild == null) {
|
||||
return renderFallbackIcon(channel);
|
||||
}
|
||||
return (
|
||||
<BadgedIcon
|
||||
badge={renderChannelBadge(channel)}
|
||||
data-flx="messaging.forward-destination-icon.render-guild-channel-icon.badged-icon"
|
||||
>
|
||||
<GuildIcon
|
||||
id={guild.id}
|
||||
name={guild.name}
|
||||
icon={guild.icon}
|
||||
sizePx={DESTINATION_ICON_SIZE}
|
||||
data-flx="messaging.forward-destination-icon.guild-icon"
|
||||
/>
|
||||
</BadgedIcon>
|
||||
);
|
||||
}
|
||||
|
||||
function renderPersonalNotesIcon(channel: Channel) {
|
||||
const currentUser = Users.currentUser;
|
||||
if (currentUser == null) {
|
||||
return renderFallbackIcon(channel);
|
||||
}
|
||||
return (
|
||||
<BadgedIcon
|
||||
badge={renderChannelBadge(channel)}
|
||||
data-flx="messaging.forward-destination-icon.render-personal-notes-icon.badged-icon"
|
||||
>
|
||||
<Avatar
|
||||
user={currentUser}
|
||||
size={DESTINATION_ICON_SIZE}
|
||||
status={null}
|
||||
data-flx="messaging.forward-destination-icon.avatar"
|
||||
/>
|
||||
</BadgedIcon>
|
||||
);
|
||||
}
|
||||
|
||||
export const ForwardDestinationIcon = observer(function ForwardDestinationIcon({
|
||||
option,
|
||||
}: {
|
||||
option: ForwardDestinationOption;
|
||||
}) {
|
||||
if (option.user != null) {
|
||||
return (
|
||||
<div className={styles.frame} data-flx="messaging.forward-destination-icon.frame--4">
|
||||
<StatusAwareAvatar
|
||||
user={option.user}
|
||||
size={DESTINATION_ICON_SIZE}
|
||||
data-flx="messaging.forward-destination-icon.status-aware-avatar"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
const channel = option.channel;
|
||||
if (channel == null) return null;
|
||||
switch (channel.type) {
|
||||
case ChannelTypes.GROUP_DM:
|
||||
return (
|
||||
<div className={styles.frame} data-flx="messaging.forward-destination-icon.frame--5">
|
||||
<GroupDMAvatar
|
||||
channel={channel}
|
||||
size={DESTINATION_ICON_SIZE}
|
||||
data-flx="messaging.forward-destination-icon.group-dm-avatar"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
case ChannelTypes.DM_PERSONAL_NOTES:
|
||||
return renderPersonalNotesIcon(channel);
|
||||
default:
|
||||
return renderGuildChannelIcon(channel);
|
||||
}
|
||||
});
|
||||
@@ -8,7 +8,6 @@ import {
|
||||
type ForwardDestinationOption,
|
||||
useForwardDestinations,
|
||||
} from '@app/features/app/components/dialogs/shared/UseForwardDestinations';
|
||||
import {GroupDMAvatar} from '@app/features/app/components/shared/GroupDMAvatar';
|
||||
import {Limits} from '@app/features/app/utils/UserLimits';
|
||||
import * as PrivateChannelCommands from '@app/features/channel/commands/PrivateChannelCommands';
|
||||
import {MessageCharacterCounter} from '@app/features/channel/components/MessageCharacterCounter';
|
||||
@@ -22,6 +21,7 @@ import {LexicalRichInput, type LexicalRichInputHandle} from '@app/features/lexic
|
||||
import * as MessageCommands from '@app/features/messaging/commands/MessageCommands';
|
||||
import {MessageForwardFailedModal} from '@app/features/messaging/components/alerts/MessageForwardFailedModal';
|
||||
import {showMessagingErrorModal} from '@app/features/messaging/components/alerts/MessagingErrorModalUtils';
|
||||
import {ForwardDestinationIcon} from '@app/features/messaging/components/modals/ForwardDestinationIcon';
|
||||
import {ForwardMessagePreview} from '@app/features/messaging/components/modals/ForwardMessagePreview';
|
||||
import modalStyles from '@app/features/messaging/components/modals/ForwardModal.module.css';
|
||||
import {shouldNavigateAfterForward} from '@app/features/messaging/components/modals/ForwardModalUtils';
|
||||
@@ -31,7 +31,6 @@ import type {MentionSegment} from '@app/features/messaging/utils/TextareaSegment
|
||||
import * as NavigationCommands from '@app/features/navigation/commands/NavigationCommands';
|
||||
import {Logger} from '@app/features/platform/utils/AppLogger';
|
||||
import {shouldDisableAutofocusOnMobile} from '@app/features/platform/utils/AutofocusUtils';
|
||||
import {remFromPx} from '@app/features/theme/layout/RemFromPx';
|
||||
import {Button} from '@app/features/ui/button/Button';
|
||||
import {Checkbox} from '@app/features/ui/checkbox/Checkbox';
|
||||
import * as ModalCommands from '@app/features/ui/commands/ModalCommands';
|
||||
@@ -39,17 +38,15 @@ import {modal} from '@app/features/ui/commands/ModalCommands';
|
||||
import * as ToastCommands from '@app/features/ui/commands/ToastCommands';
|
||||
import {Input} from '@app/features/ui/components/form/FormInput';
|
||||
import {Scroller} from '@app/features/ui/components/Scroller';
|
||||
import {StatusAwareAvatar} from '@app/features/ui/components/StatusAwareAvatar';
|
||||
import FocusRing from '@app/features/ui/focus_ring/FocusRing';
|
||||
import {Popout} from '@app/features/ui/popover/PopoverPopout';
|
||||
import MobileLayout from '@app/features/ui/state/MobileLayout';
|
||||
import type {User} from '@app/features/user/models/User';
|
||||
import {ChannelTypes} from '@fluxer/constants/src/ChannelConstants';
|
||||
import {MAX_MESSAGE_LENGTH_PREMIUM} from '@fluxer/constants/src/LimitConstants';
|
||||
import type {I18n} from '@lingui/core';
|
||||
import {msg} from '@lingui/core/macro';
|
||||
import {Trans, useLingui} from '@lingui/react/macro';
|
||||
import {HashIcon, MagnifyingGlassIcon, NotePencilIcon, SmileyIcon, SpeakerHighIcon} from '@phosphor-icons/react';
|
||||
import {MagnifyingGlassIcon, SmileyIcon} from '@phosphor-icons/react';
|
||||
import {clsx} from 'clsx';
|
||||
import {observer} from 'mobx-react-lite';
|
||||
import {type MouseEvent, useCallback, useEffect, useId, useRef, useState} from 'react';
|
||||
@@ -111,8 +108,6 @@ const SEND_SELECTED_COUNT_DESCRIPTOR = msg({
|
||||
});
|
||||
const logger = new Logger('ForwardModal');
|
||||
|
||||
const DESTINATION_ICON_SIZE = 32;
|
||||
|
||||
interface ForwardModalProps {
|
||||
message: Message;
|
||||
mediaSelection?: MessageCommands.ForwardMediaSelection;
|
||||
@@ -205,63 +200,6 @@ function insertForwardEmoji(handle: LexicalRichInputHandle | null, emoji: FlatEm
|
||||
return handle.insertEmoji(emoji);
|
||||
}
|
||||
|
||||
function renderForwardDestinationIcon(option: ForwardDestinationOption) {
|
||||
if (option.user != null) {
|
||||
return (
|
||||
<div className={selectorStyles.avatar} data-flx="messaging.forward-modal.get-channel-icon.div">
|
||||
<StatusAwareAvatar
|
||||
user={option.user}
|
||||
size={DESTINATION_ICON_SIZE}
|
||||
data-flx="messaging.forward-modal.get-channel-icon.status-aware-avatar"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
const channel = option.channel;
|
||||
if (channel == null) {
|
||||
return null;
|
||||
}
|
||||
if (channel.type === ChannelTypes.DM_PERSONAL_NOTES) {
|
||||
return (
|
||||
<NotePencilIcon
|
||||
className={selectorStyles.itemIcon}
|
||||
weight="fill"
|
||||
size={remFromPx(DESTINATION_ICON_SIZE)}
|
||||
data-flx="messaging.forward-modal.get-channel-icon.note-pencil-icon"
|
||||
/>
|
||||
);
|
||||
}
|
||||
if (channel.type === ChannelTypes.GROUP_DM) {
|
||||
return (
|
||||
<div className={selectorStyles.avatar} data-flx="messaging.forward-modal.get-channel-icon.div--2">
|
||||
<GroupDMAvatar
|
||||
channel={channel}
|
||||
size={DESTINATION_ICON_SIZE}
|
||||
data-flx="messaging.forward-modal.get-channel-icon.group-dm-avatar"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
if (channel.type === ChannelTypes.GUILD_VOICE) {
|
||||
return (
|
||||
<SpeakerHighIcon
|
||||
className={selectorStyles.itemIcon}
|
||||
weight="fill"
|
||||
size={remFromPx(DESTINATION_ICON_SIZE)}
|
||||
data-flx="messaging.forward-modal.get-channel-icon.speaker-high-icon"
|
||||
/>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<HashIcon
|
||||
className={selectorStyles.itemIcon}
|
||||
weight="bold"
|
||||
size={remFromPx(DESTINATION_ICON_SIZE)}
|
||||
data-flx="messaging.forward-modal.get-channel-icon.hash-icon"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
const EMPTY_FORWARD_COMMENT_SEGMENTS: ReadonlyArray<MentionSegment> = Object.freeze([]);
|
||||
|
||||
export const ForwardModal = observer(
|
||||
@@ -470,7 +408,10 @@ export const ForwardModal = observer(
|
||||
data-flx="messaging.forward-modal.button"
|
||||
>
|
||||
<div className={selectorStyles.itemContent} data-flx="messaging.forward-modal.div--5">
|
||||
{renderForwardDestinationIcon(option)}
|
||||
<ForwardDestinationIcon
|
||||
option={option}
|
||||
data-flx="messaging.forward-modal.forward-destination-icon"
|
||||
/>
|
||||
<div className={selectorStyles.itemInfo} data-flx="messaging.forward-modal.div--6">
|
||||
<span className={selectorStyles.itemName} data-flx="messaging.forward-modal.span">
|
||||
{option.displayName}
|
||||
|
||||
Reference in New Issue
Block a user