GoonCord/src/core/ui/components/AddonPage.tsx

88 lines
3.5 KiB
TypeScript
Raw Normal View History

2024-03-08 02:54:20 +08:00
import { Strings } from "@core/i18n";
import { CardWrapper } from "@core/ui/components/AddonCard";
import { requireAssetIndex } from "@lib/api/assets";
2024-03-06 01:57:59 +08:00
import { useProxy } from "@lib/api/storage";
import { settings } from "@lib/settings";
2024-03-06 01:57:59 +08:00
import { HTTP_REGEX_MULTI } from "@lib/utils/constants";
import { clipboard } from "@metro/common";
import { FloatingActionButton, HelpMessage } from "@metro/common/components";
import { showInputAlert } from "@ui/alerts";
2024-03-07 12:35:07 +08:00
import { ErrorBoundary, Search } from "@ui/components";
2024-02-28 21:43:55 +08:00
import fuzzysort from "fuzzysort";
import { createContext } from "react";
import { FlatList, View } from "react-native";
2023-04-15 02:57:03 +01:00
export const RemoveModeContext = createContext(false);
2023-04-15 02:57:03 +01:00
interface AddonPageProps<T> {
2023-12-21 03:30:21 +08:00
title: string;
fetchFunction: (url: string) => Promise<void>;
items: Record<string, T & ({ id: string; } | { name: string; })>;
2023-04-15 02:57:03 +01:00
safeModeMessage: string;
safeModeExtras?: JSX.Element | JSX.Element[];
card: React.ComponentType<CardWrapper<T>>;
isRemoveMode?: boolean;
2024-05-20 00:04:25 +08:00
onFABPress?: () => void;
2023-04-15 02:57:03 +01:00
}
function getItemsByQuery<T extends AddonPageProps<unknown>["items"][string]>(items: T[], query: string): T[] {
2024-02-28 21:43:55 +08:00
if (!query) return items;
2024-03-06 01:57:59 +08:00
return fuzzysort.go(query, items, {
keys: [
"id",
"name",
2024-03-06 01:57:59 +08:00
"manifest.name",
"manifest.description",
"manifest.authors.0.name",
"manifest.authors.1.name"
]
}).map(r => r.obj);
2024-02-28 21:43:55 +08:00
}
export default function AddonPage<T>({ card: CardComponent, ...props }: AddonPageProps<T>) {
useProxy(props.items);
useProxy(settings);
2023-12-21 03:30:21 +08:00
2023-04-15 02:57:03 +01:00
const [search, setSearch] = React.useState("");
return (
<ErrorBoundary>
2023-05-13 00:15:11 +01:00
{/* TODO: Implement better searching than just by ID */}
<FlatList
ListHeaderComponent={<View>
{settings.safeMode?.enabled && <View style={{ marginBottom: 10 }}>
<HelpMessage messageType={0}>{props.safeModeMessage}</HelpMessage>
{props.safeModeExtras}
</View>}
2023-05-13 00:15:11 +01:00
<Search
onChangeText={(v: string) => setSearch(v.toLowerCase())}
2024-02-27 17:22:23 +08:00
placeholder={Strings.SEARCH}
2023-05-13 00:15:11 +01:00
/>
</View>}
2023-05-13 00:15:11 +01:00
style={{ paddingHorizontal: 10, paddingTop: 10 }}
contentContainerStyle={{ paddingBottom: 90, paddingHorizontal: 5, gap: 12 }}
data={getItemsByQuery(Object.values(props.items).filter(i => typeof i === "object"), search)}
renderItem={({ item, index }) => <RemoveModeContext.Provider value={!!props.isRemoveMode}>
<CardComponent item={item} index={index} />
</RemoveModeContext.Provider>}
2023-05-13 00:15:11 +01:00
/>
2024-03-06 01:57:59 +08:00
<FloatingActionButton
icon={requireAssetIndex("PlusLargeIcon")}
onPress={props.onFABPress ?? (() => {
2023-12-21 03:30:21 +08:00
// from ./InstallButton.tsx
2024-03-06 01:57:59 +08:00
clipboard.getString().then(content =>
2023-12-21 03:30:21 +08:00
showInputAlert({
initialValue: content.match(HTTP_REGEX_MULTI)?.[0] ?? "",
2024-03-08 02:54:20 +08:00
placeholder: Strings.URL_PLACEHOLDER,
onConfirm: (input: string) => props.fetchFunction(input),
2024-02-27 17:22:23 +08:00
confirmText: Strings.INSTALL,
cancelText: Strings.CANCEL,
2023-12-21 03:30:21 +08:00
})
2024-03-06 01:57:59 +08:00
);
2024-05-20 00:04:25 +08:00
})}
2023-12-21 03:30:21 +08:00
/>
2023-04-15 02:57:03 +01:00
</ErrorBoundary>
2024-03-06 01:57:59 +08:00
);
}