/* * Copyright (C) 2026 Fluxer Contributors * * This file is part of Fluxer. * * Fluxer is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Fluxer is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with Fluxer. If not, see . */ import {useLingui} from '@lingui/react/macro'; import {MagnifyingGlassIcon} from '@phosphor-icons/react'; import {observer} from 'mobx-react-lite'; import React from 'react'; import * as ModalActionCreators from '~/actions/ModalActionCreators'; import {ChannelTypes} from '~/Constants'; import {Input} from '~/components/form/Input'; import {Select, type SelectOption} from '~/components/form/Select'; import styles from '~/components/modals/AddFavoriteChannelModal.module.css'; import * as Modal from '~/components/modals/Modal'; import selectorStyles from '~/components/modals/shared/SelectorModalStyles.module.css'; import {Button} from '~/components/uikit/Button/Button'; import {Checkbox} from '~/components/uikit/Checkbox/Checkbox'; import {Scroller} from '~/components/uikit/Scroller'; import type {ChannelRecord} from '~/records/ChannelRecord'; import ChannelStore from '~/stores/ChannelStore'; import FavoritesStore from '~/stores/FavoritesStore'; import GuildStore from '~/stores/GuildStore'; import UserGuildSettingsStore from '~/stores/UserGuildSettingsStore'; import * as ChannelUtils from '~/utils/ChannelUtils'; interface ChannelWithCategory { channel: ChannelRecord; categoryName: string | null; } export const AddFavoriteChannelModal = observer(({categoryId}: {categoryId?: string | null} = {}) => { const {t} = useLingui(); const guilds = GuildStore.getGuilds(); const firstGuildId = guilds.length > 0 ? guilds[0].id : null; const [selectedGuildId, setSelectedGuildId] = React.useState(firstGuildId); const [hideMutedChannels, setHideMutedChannels] = React.useState(false); const [searchQuery, setSearchQuery] = React.useState(''); const guildOptions: Array> = React.useMemo( () => guilds.map((guild) => ({ value: guild.id, label: guild.name ?? 'Unknown Guild', })), [guilds], ); const selectedGuild = selectedGuildId ? GuildStore.getGuild(selectedGuildId) : null; const channels = React.useMemo(() => { if (!selectedGuild) return []; const guildChannels = ChannelStore.getGuildChannels(selectedGuild.id); const result: Array = []; const query = searchQuery.toLowerCase().trim(); for (const channel of guildChannels) { if (channel.type !== ChannelTypes.GUILD_TEXT && channel.type !== ChannelTypes.GUILD_VOICE) { continue; } if (hideMutedChannels && UserGuildSettingsStore.isGuildOrChannelMuted(selectedGuild.id, channel.id)) { continue; } if (query && !channel.name?.toLowerCase().includes(query)) { continue; } let categoryName: string | null = null; if (channel.parentId) { const category = ChannelStore.getChannel(channel.parentId); if (category) { categoryName = category.name ?? null; } } result.push({channel, categoryName}); } return result.sort((a, b) => { if (a.categoryName === b.categoryName) { return (a.channel.position ?? 0) - (b.channel.position ?? 0); } if (!a.categoryName) return -1; if (!b.categoryName) return 1; return a.categoryName.localeCompare(b.categoryName); }); }, [selectedGuild, hideMutedChannels, searchQuery]); const handleToggleChannel = (channelId: string) => { if (!selectedGuild) return; const isAlreadyFavorite = !!FavoritesStore.getChannel(channelId); if (isAlreadyFavorite) { FavoritesStore.removeChannel(channelId); } else { FavoritesStore.addChannel(channelId, selectedGuild.id, categoryId ?? null); } }; return (
setSearchQuery(e.target.value)} placeholder={t`Search channels`} leftIcon={} className={selectorStyles.headerSearchInput} />