2024-03-08 02:54:20 +08:00
|
|
|
import { Strings } from "@core/i18n";
|
2024-05-23 19:29:56 +08:00
|
|
|
import { CardWrapper } from "@core/ui/components/AddonCard";
|
2024-05-23 01:41:43 +08:00
|
|
|
import { requireAssetIndex } from "@lib/api/assets";
|
2024-03-06 01:57:59 +08:00
|
|
|
import { useProxy } from "@lib/api/storage";
|
2024-02-28 14:09:42 +08:00
|
|
|
import { settings } from "@lib/settings";
|
2024-03-06 01:57:59 +08:00
|
|
|
import { HTTP_REGEX_MULTI } from "@lib/utils/constants";
|
2024-03-07 09:09:31 +08:00
|
|
|
import { clipboard } from "@metro/common";
|
2024-05-23 19:29:56 +08:00
|
|
|
import { FloatingActionButton, HelpMessage } from "@metro/common/components";
|
2024-02-28 14:09:42 +08:00
|
|
|
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";
|
2024-05-18 02:55:20 +08:00
|
|
|
import { createContext } from "react";
|
2024-03-07 09:09:31 +08:00
|
|
|
import { FlatList, View } from "react-native";
|
2023-04-15 02:57:03 +01:00
|
|
|
|
2024-05-18 02:55:20 +08: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>;
|
2024-05-18 02:55:20 +08:00
|
|
|
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>>;
|
2024-05-18 02:55:20 +08:00
|
|
|
isRemoveMode?: boolean;
|
2024-05-20 00:04:25 +08:00
|
|
|
onFABPress?: () => void;
|
2023-04-15 02:57:03 +01:00
|
|
|
}
|
|
|
|
|
|
2024-05-18 02:55:20 +08: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",
|
2024-05-18 02:55:20 +08:00
|
|
|
"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
|
|
|
}
|
|
|
|
|
|
2024-05-23 19:29:56 +08:00
|
|
|
export default function AddonPage<T>({ card: CardComponent, ...props }: AddonPageProps<T>) {
|
|
|
|
|
useProxy(props.items);
|
2024-05-18 02:55:20 +08:00
|
|
|
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 */}
|
2024-03-07 09:09:31 +08:00
|
|
|
<FlatList
|
2024-05-23 19:29:56 +08:00
|
|
|
ListHeaderComponent={<View>
|
2024-03-07 09:09:31 +08:00
|
|
|
{settings.safeMode?.enabled && <View style={{ marginBottom: 10 }}>
|
2024-05-23 19:29:56 +08:00
|
|
|
<HelpMessage messageType={0}>{props.safeModeMessage}</HelpMessage>
|
|
|
|
|
{props.safeModeExtras}
|
2024-03-07 09:09:31 +08:00
|
|
|
</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
|
|
|
/>
|
2024-05-23 19:29:56 +08:00
|
|
|
</View>}
|
2023-05-13 00:15:11 +01:00
|
|
|
style={{ paddingHorizontal: 10, paddingTop: 10 }}
|
2024-05-23 19:29:56 +08:00
|
|
|
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}>
|
2024-05-18 02:55:20 +08:00
|
|
|
<CardComponent item={item} index={index} />
|
|
|
|
|
</RemoveModeContext.Provider>}
|
2023-05-13 00:15:11 +01:00
|
|
|
/>
|
2024-03-06 01:57:59 +08:00
|
|
|
<FloatingActionButton
|
2024-05-23 01:41:43 +08:00
|
|
|
icon={requireAssetIndex("PlusLargeIcon")}
|
2024-05-23 19:29:56 +08:00
|
|
|
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,
|
2024-05-23 19:29:56 +08:00
|
|
|
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
|
|
|
);
|
|
|
|
|
}
|