feat(AddonPage): Add sort options
This commit is contained in:
parent
f542e8ce47
commit
7bd53f7985
5 changed files with 70 additions and 25 deletions
|
|
@ -4,51 +4,80 @@ import { findAssetId } from "@lib/api/assets";
|
|||
import { settings } from "@lib/api/settings";
|
||||
import { useProxy } from "@lib/api/storage";
|
||||
import { HTTP_REGEX_MULTI } from "@lib/utils/constants";
|
||||
import { lazyDestructure } from "@lib/utils/lazy";
|
||||
import { findByProps } from "@metro";
|
||||
import { clipboard } from "@metro/common";
|
||||
import { FlashList, FloatingActionButton, HelpMessage } from "@metro/common/components";
|
||||
import { FlashList, FloatingActionButton, HelpMessage, IconButton } from "@metro/common/components";
|
||||
import { showInputAlert } from "@ui/alerts";
|
||||
import { ErrorBoundary, Search } from "@ui/components";
|
||||
import fuzzysort from "fuzzysort";
|
||||
import { ComponentType, ReactNode, useMemo } from "react";
|
||||
import { View } from "react-native";
|
||||
|
||||
const { showSimpleActionSheet, hideActionSheet } = lazyDestructure(() => findByProps("showSimpleActionSheet"));
|
||||
|
||||
type SearchKeywords = Array<string | ((obj: any & {}) => string)>;
|
||||
|
||||
interface AddonPageProps<T extends object> {
|
||||
interface AddonPageProps<T extends object, I = any> {
|
||||
title: string;
|
||||
items: I[];
|
||||
sortOptions?: Record<string, (a: I, b: I) => number>;
|
||||
fetchFunction?: (url: string) => Promise<void>;
|
||||
items: any[];
|
||||
resolveItem?: (value: any) => T | undefined;
|
||||
safeModeMessage: string;
|
||||
safeModeExtras?: ReactNode;
|
||||
card: ComponentType<CardWrapper<T>>;
|
||||
resolveItem?: (value: I) => T | undefined;
|
||||
safeModeHint?: {
|
||||
message?: string;
|
||||
footer?: ReactNode;
|
||||
}
|
||||
searchKeywords: SearchKeywords;
|
||||
onFabPress?: () => void;
|
||||
CardComponent: ComponentType<CardWrapper<T>>;
|
||||
ListHeaderComponent?: ComponentType<any>;
|
||||
ListFooterComponent?: ComponentType<any>;
|
||||
}
|
||||
|
||||
export default function AddonPage<T extends object>({ card: CardComponent, ...props }: AddonPageProps<T>) {
|
||||
export default function AddonPage<T extends object>({ CardComponent, ...props }: AddonPageProps<T>) {
|
||||
useProxy(settings);
|
||||
|
||||
const [search, setSearch] = React.useState("");
|
||||
const [sortFn, setSortFn] = React.useState<((a: unknown, b: unknown) => number) | null>(() => null);
|
||||
|
||||
const results = useMemo(() => {
|
||||
let values = props.items;
|
||||
if (props.resolveItem) values = values.map(props.resolveItem);
|
||||
const items = values.filter(i => i && typeof i === "object");
|
||||
if (!search && sortFn) items.sort(sortFn);
|
||||
|
||||
return fuzzysort.go(search, items, { keys: props.searchKeywords, all: true });
|
||||
}, [props.items, search]);
|
||||
}, [props.items, sortFn, search]);
|
||||
|
||||
const headerElement = (
|
||||
<View style={{ paddingBottom: 8 }}>
|
||||
{props.ListHeaderComponent && <props.ListHeaderComponent />}
|
||||
{settings.safeMode?.enabled && <View style={{ marginBottom: 10 }}>
|
||||
<HelpMessage messageType={0}>{props.safeModeMessage}</HelpMessage>
|
||||
{props.safeModeExtras}
|
||||
<HelpMessage messageType={0}>
|
||||
{props.safeModeHint?.message}
|
||||
</HelpMessage>
|
||||
{props.safeModeHint?.footer}
|
||||
</View>}
|
||||
<Search onChangeText={v => setSearch(v)} />
|
||||
<View style={{ flexDirection: "row", gap: 8 }}>
|
||||
<Search style={{ flexGrow: 1 }} isRound={!!props.sortOptions} onChangeText={v => setSearch(v)} />
|
||||
{props.sortOptions && <IconButton
|
||||
icon={findAssetId("ic_forum_channel_sort_order_24px")}
|
||||
variant="secondary"
|
||||
disabled={!!search}
|
||||
onPress={() => showSimpleActionSheet({
|
||||
key: "AddonListSortOptions",
|
||||
header: {
|
||||
title: "Sort Options",
|
||||
onClose: () => hideActionSheet("AddonListSortOptions"),
|
||||
},
|
||||
options: Object.entries(props.sortOptions!).map(([name, fn]) => ({
|
||||
label: name,
|
||||
onPress: () => setSortFn(() => fn)
|
||||
}))
|
||||
})}
|
||||
/>}
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -18,10 +18,14 @@ export default function Fonts() {
|
|||
<AddonPage<FontDefinition>
|
||||
title={Strings.FONTS}
|
||||
searchKeywords={["name", "description"]}
|
||||
sortOptions={{
|
||||
"Name (A-Z)": (a, b) => a.name.localeCompare(b.name),
|
||||
"Name (Z-A)": (a, b) => b.name.localeCompare(a.name)
|
||||
}}
|
||||
fetchFunction={installFont}
|
||||
items={Object.values(fonts)}
|
||||
safeModeMessage={Strings.SAFE_MODE_NOTICE_FONTS}
|
||||
card={FontCard}
|
||||
safeModeHint={{ message: Strings.SAFE_MODE_NOTICE_FONTS }}
|
||||
CardComponent={FontCard}
|
||||
onFabPress={() => {
|
||||
navigation.push("BUNNY_CUSTOM_PAGE", {
|
||||
title: "Import Font",
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ function PluginPage(props: PluginPageProps) {
|
|||
const items = props.useItems();
|
||||
|
||||
return <AddonPage<UnifiedPluginModel>
|
||||
card={PluginCard}
|
||||
CardComponent={PluginCard}
|
||||
title={Strings.PLUGINS}
|
||||
searchKeywords={[
|
||||
"name",
|
||||
|
|
@ -48,7 +48,11 @@ function PluginPage(props: PluginPageProps) {
|
|||
(a: Author | string) => typeof a === "string" ? a : a.name
|
||||
).join()
|
||||
]}
|
||||
safeModeMessage={Strings.SAFE_MODE_NOTICE_PLUGINS}
|
||||
sortOptions={{
|
||||
"Name (A-Z)": (a, b) => a.name.localeCompare(b.name),
|
||||
"Name (Z-A)": (a, b) => b.name.localeCompare(a.name)
|
||||
}}
|
||||
safeModeHint={{ message: Strings.SAFE_MODE_NOTICE_PLUGINS }}
|
||||
items={items}
|
||||
{...props}
|
||||
/>;
|
||||
|
|
|
|||
|
|
@ -19,16 +19,22 @@ export default function Themes() {
|
|||
"data.description",
|
||||
p => p.data.authors?.map((a: Author) => a.name).join(", ")
|
||||
]}
|
||||
sortOptions={{
|
||||
"Name (A-Z)": (a, b) => a.name.localeCompare(b.name),
|
||||
"Name (Z-A)": (a, b) => b.name.localeCompare(a.name)
|
||||
}}
|
||||
fetchFunction={installTheme}
|
||||
items={Object.values(themes)}
|
||||
safeModeMessage={formatString("SAFE_MODE_NOTICE_THEMES", { enabled: Boolean(settings.safeMode?.currentThemeId) })}
|
||||
safeModeExtras={settings.safeMode?.currentThemeId ? <Button
|
||||
text={Strings.DISABLE_THEME}
|
||||
size="small"
|
||||
onPress={() => delete settings.safeMode?.currentThemeId}
|
||||
style={{ marginTop: 8 }}
|
||||
/> : undefined}
|
||||
card={ThemeCard}
|
||||
safeModeHint={{
|
||||
message: formatString("SAFE_MODE_NOTICE_THEMES", { enabled: Boolean(settings.safeMode?.currentThemeId) }),
|
||||
footer: settings.safeMode?.currentThemeId && <Button
|
||||
size="small"
|
||||
text={Strings.DISABLE_THEME}
|
||||
onPress={() => delete settings.safeMode?.currentThemeId}
|
||||
style={{ marginTop: 8 }}
|
||||
/>
|
||||
}}
|
||||
CardComponent={ThemeCard}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,13 +8,14 @@ export interface SearchProps {
|
|||
onChangeText?: (v: string) => void;
|
||||
placeholder?: string;
|
||||
style?: ViewStyle;
|
||||
isRound?: boolean;
|
||||
}
|
||||
|
||||
function SearchIcon() {
|
||||
return <Image style={{ width: 16, height: 16 }} source={findAssetId("search")} />;
|
||||
}
|
||||
|
||||
export default ({ onChangeText, placeholder, style }: SearchProps) => {
|
||||
export default ({ onChangeText, placeholder, style, isRound }: SearchProps) => {
|
||||
const [query, setQuery] = React.useState("");
|
||||
|
||||
const onChange = (value: string) => {
|
||||
|
|
@ -34,6 +35,7 @@ export default ({ onChangeText, placeholder, style }: SearchProps) => {
|
|||
size="md"
|
||||
autoCapitalize="none"
|
||||
autoCorrect={false}
|
||||
isRound={isRound}
|
||||
value={query}
|
||||
/>
|
||||
</View>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue