/*
* 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 (