import { useNavigation, useRoute } from '@react-navigation/native' import { getAssetIdByName } from '@revenge-mod/assets' import { styles } from '@revenge-mod/components/_' import Page from '@revenge-mod/components/Page' import SearchInput from '@revenge-mod/components/SearchInput' import { ActionSheetActionCreators } from '@revenge-mod/discord/actions' import { Design } from '@revenge-mod/discord/design' import { getInternalPluginMeta, InternalPluginFlags, isPluginEnabled, isPluginEssential, isPluginInternal, pList, } from '@revenge-mod/plugins/_' import { PluginFlags } from '@revenge-mod/plugins/constants' import { debounce } from '@revenge-mod/utils/callback' import { useCallback, useMemo, useState } from 'react' import { View } from 'react-native' import { ClickOutsideProvider } from 'react-native-click-outside' import RevengeIcon from '~assets/RevengeIcon' import { InstalledPluginMasonryFlashList } from '../components/PluginList' import PluginStatesProvider from '../components/PluginStateProvider' import { EnablePluginTooltipProvider, EssentialPluginTooltipProvider, } from '../components/TooltipProvider' import type { RouteProp } from '@react-navigation/core' import type { ReactNavigationParamList } from '@revenge-mod/externals/react-navigation' import type { FilterAndSortActionSheetProps } from '../components/FilterAndSortActionSheet' import type { RouteNames, Setting } from '../constants' const { Stack, IconButton, LayerScope } = Design const FiltersHorizontalIcon = getAssetIdByName('FiltersHorizontalIcon', 'png')! export default function RevengePluginsSettingScreen() { return ( ) } const SearchDebounceTime = 100 const Filters: FilterAndSortActionSheetProps['filters'] = { Enabled: { icon: getAssetIdByName('CircleCheckIcon')!, filter: plugin => isPluginEnabled(plugin), }, Disabled: { icon: getAssetIdByName('CircleXIcon')!, filter: plugin => !isPluginEnabled(plugin), }, 'Has Errors': { icon: getAssetIdByName('CircleXIcon')!, filter: plugin => plugin.errors.length > 0, }, Internal: { icon: RevengeIcon, desc: 'Included with Revenge.', filter: (_, meta) => isPluginInternal(meta), }, Essential: { icon: getAssetIdByName('StarIcon')!, desc: 'Required for Revenge to function properly.', filter: (_, meta) => isPluginEssential(meta), }, 'Non-APIs': { icon: getAssetIdByName('PaperIcon')!, desc: 'Exclude essential plugins that provide APIs for other plugins.', filter: (_, meta) => !(meta.iflags & InternalPluginFlags.API), }, } satisfies FilterAndSortActionSheetProps['filters'] const DefaultFilters: FilterAndSortActionSheetProps['filter'] = ['Non-APIs'] const DefaultSort: keyof typeof Sorts = 'Name' const Sorts = { Name: [ getAssetIdByName('IdIcon')!, (a, b) => a.manifest.name.localeCompare(b.manifest.name), ], 'Enabled first': [ getAssetIdByName('CircleCheckIcon')!, (a, b) => (b.flags & PluginFlags.Enabled) - (a.flags & PluginFlags.Enabled), ], } satisfies FilterAndSortActionSheetProps['sorts'] function Screen() { const navigation = useNavigation() const route = useRoute< RouteProp< ReactNavigationParamList, (typeof RouteNames)[typeof Setting.RevengePlugins] > >() const [search, setSearch] = useState('') const debouncedSetSearch = useCallback( debounce(setSearch, SearchDebounceTime), [], ) const filter = route.params?.filter ?? DefaultFilters const matchAll = route.params?.matchAll ?? true const reverse = route.params?.reverse ?? false const sort = route.params?.sort ?? DefaultSort const allPlugins = useMemo( () => [...pList.values()].map( plugin => [plugin, getInternalPluginMeta(plugin)!] as const, ), [], ) const plugins = useMemo( () => allPlugins .filter(([plugin, meta]) => { if (filter.length === 0) return true if (matchAll) return filter.every(f => Filters[f].filter(plugin, meta), ) return filter.some(f => Filters[f].filter(plugin, meta)) }) .filter(([plugin]) => { const { name, description, author } = plugin.manifest const query = search.toLowerCase() return ( name.toLowerCase().includes(query) || description.toLowerCase().includes(query) || author.toLowerCase().includes(query) ) }) .sort(([a], [b]) => { const result = Sorts[sort as keyof typeof Sorts][1](a, b) return reverse ? -result : result }), [allPlugins, filter, matchAll, reverse, sort, search], ) return ( <> ActionSheetActionCreators.openLazy( import('../components/FilterAndSortActionSheet'), 'filter-and-sort-plugins', { filters: Filters, filter, setFilter: filter => navigation.setParams({ filter }), matchAll, setMatchAll: matchAll => navigation.setParams({ matchAll }), reverse, setReverse: reverse => navigation.setParams({ reverse }), sorts: Sorts, sort, setSort: sort => navigation.setParams({ sort }), }, ) } /> ) }