feat+refactor: Add empty list component and readd unproxied plugin warning
This commit is contained in:
parent
7bd53f7985
commit
8dc0b826ca
6 changed files with 93 additions and 69 deletions
|
|
@ -26,6 +26,7 @@
|
|||
"CONFIRMATION_LINK_IS_A_TYPE": "This link is a **{urlType, select, plugin {Plugin} theme {Theme} other {Add-on}}**, would you like to install it?",
|
||||
"CONNECT_TO_DEBUG_WEBSOCKET": "Connect to debug websocket",
|
||||
"CONNECT_TO_REACT_DEVTOOLS": "Connect to React DevTools",
|
||||
"CONTINUE": "Continue",
|
||||
"COPIED_TO_CLIPBOARD": "Copied to clipboard",
|
||||
"COPY_URL": "Copy URL",
|
||||
"DEBUG": "Debug",
|
||||
|
|
|
|||
|
|
@ -7,12 +7,12 @@ 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, IconButton } from "@metro/common/components";
|
||||
import { Button, FlashList, FloatingActionButton, HelpMessage, IconButton, Text } 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";
|
||||
import { ComponentType, ReactNode, useCallback, useMemo } from "react";
|
||||
import { Image, View } from "react-native";
|
||||
|
||||
const { showSimpleActionSheet, hideActionSheet } = lazyDestructure(() => findByProps("showSimpleActionSheet"));
|
||||
|
||||
|
|
@ -21,15 +21,19 @@ type SearchKeywords = Array<string | ((obj: any & {}) => string)>;
|
|||
interface AddonPageProps<T extends object, I = any> {
|
||||
title: string;
|
||||
items: I[];
|
||||
searchKeywords: SearchKeywords;
|
||||
sortOptions?: Record<string, (a: I, b: I) => number>;
|
||||
fetchFunction?: (url: string) => Promise<void>;
|
||||
resolveItem?: (value: I) => T | undefined;
|
||||
safeModeHint?: {
|
||||
message?: string;
|
||||
footer?: ReactNode;
|
||||
}
|
||||
searchKeywords: SearchKeywords;
|
||||
onFabPress?: () => void;
|
||||
installAction?: {
|
||||
label?: string;
|
||||
// Ignored when onPress is defined!
|
||||
fetchFn?: (url: string) => Promise<void>;
|
||||
onPress?: () => void;
|
||||
}
|
||||
CardComponent: ComponentType<CardWrapper<T>>;
|
||||
ListHeaderComponent?: ComponentType<any>;
|
||||
ListFooterComponent?: ComponentType<any>;
|
||||
|
|
@ -50,6 +54,42 @@ export default function AddonPage<T extends object>({ CardComponent, ...props }:
|
|||
return fuzzysort.go(search, items, { keys: props.searchKeywords, all: true });
|
||||
}, [props.items, sortFn, search]);
|
||||
|
||||
const onInstallPress = useCallback(() => {
|
||||
if (!props.installAction) return () => {};
|
||||
const { onPress, fetchFn } = props.installAction;
|
||||
if (fetchFn) {
|
||||
clipboard.getString().then((content: string) =>
|
||||
showInputAlert({
|
||||
title: props.installAction?.label,
|
||||
initialValue: content.match(HTTP_REGEX_MULTI)?.[0] ?? "",
|
||||
placeholder: Strings.URL_PLACEHOLDER,
|
||||
onConfirm: (input: string) => fetchFn(input),
|
||||
confirmText: Strings.INSTALL,
|
||||
cancelText: Strings.CANCEL,
|
||||
})
|
||||
);
|
||||
} else {
|
||||
onPress?.();
|
||||
}
|
||||
}, []);
|
||||
|
||||
if (results.length === 0 && !search) {
|
||||
return <View style={{ gap: 32, flexGrow: 1, justifyContent: "center", alignItems: "center" }}>
|
||||
<View style={{ gap: 8, alignItems: "center" }}>
|
||||
<Image source={findAssetId("empty_quick_switcher")} />
|
||||
<Text variant="text-lg/semibold" color="text-normal">
|
||||
Oops! Nothing to see here… yet!
|
||||
</Text>
|
||||
</View>
|
||||
<Button
|
||||
size="lg"
|
||||
icon={findAssetId("DownloadIcon")}
|
||||
text={props.installAction?.label ?? "Install"}
|
||||
onPress={onInstallPress}
|
||||
/>
|
||||
</View>;
|
||||
}
|
||||
|
||||
const headerElement = (
|
||||
<View style={{ paddingBottom: 8 }}>
|
||||
{props.ListHeaderComponent && <props.ListHeaderComponent />}
|
||||
|
|
@ -88,25 +128,20 @@ export default function AddonPage<T extends object>({ CardComponent, ...props }:
|
|||
extraData={search}
|
||||
estimatedItemSize={136}
|
||||
ListHeaderComponent={headerElement}
|
||||
ListEmptyComponent={() => <View style={{ gap: 12, padding: 12, alignItems: "center" }}>
|
||||
<Image source={findAssetId("devices_not_found")} />
|
||||
<Text variant="text-lg/semibold" color="text-normal">
|
||||
Hmmm... could not find that!
|
||||
</Text>
|
||||
</View>}
|
||||
contentContainerStyle={{ padding: 8, paddingHorizontal: 12 }}
|
||||
ItemSeparatorComponent={() => <View style={{ height: 8 }} />}
|
||||
ListFooterComponent={props.ListFooterComponent}
|
||||
renderItem={({ item }: any) => <CardComponent item={item.obj} result={item} />}
|
||||
/>
|
||||
{(props.fetchFunction ?? props.onFabPress) && <FloatingActionButton
|
||||
{props.installAction && <FloatingActionButton
|
||||
icon={findAssetId("PlusLargeIcon")}
|
||||
onPress={props.onFabPress ?? (() => {
|
||||
// from ./InstallButton.tsx
|
||||
clipboard.getString().then((content: string) =>
|
||||
showInputAlert({
|
||||
initialValue: content.match(HTTP_REGEX_MULTI)?.[0] ?? "",
|
||||
placeholder: Strings.URL_PLACEHOLDER,
|
||||
onConfirm: (input: string) => props.fetchFunction!(input),
|
||||
confirmText: Strings.INSTALL,
|
||||
cancelText: Strings.CANCEL,
|
||||
})
|
||||
);
|
||||
})}
|
||||
onPress={onInstallPress}
|
||||
/>}
|
||||
</ErrorBoundary>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,41 +0,0 @@
|
|||
import { Strings } from "@core/i18n";
|
||||
import { findAssetId } from "@lib/api/assets";
|
||||
import { HTTP_REGEX_MULTI } from "@lib/utils/constants";
|
||||
import { clipboard } from "@metro/common";
|
||||
import { showInputAlert } from "@ui/alerts";
|
||||
import { semanticColors } from "@ui/color";
|
||||
import { createStyles } from "@ui/styles";
|
||||
import { Image, TouchableOpacity } from "react-native";
|
||||
|
||||
const useStyles = createStyles({
|
||||
icon: {
|
||||
marginRight: 10,
|
||||
tintColor: semanticColors.HEADER_PRIMARY,
|
||||
},
|
||||
});
|
||||
|
||||
interface InstallButtonProps {
|
||||
alertTitle: string;
|
||||
installFunction: (id: string) => Promise<void>;
|
||||
}
|
||||
|
||||
export default function InstallButton({ alertTitle, installFunction: fetchFunction }: InstallButtonProps) {
|
||||
const styles = useStyles();
|
||||
|
||||
return (
|
||||
<TouchableOpacity onPress={() =>
|
||||
clipboard.getString().then((content: string) =>
|
||||
showInputAlert({
|
||||
title: alertTitle,
|
||||
initialValue: content.match(HTTP_REGEX_MULTI)?.[0] ?? "",
|
||||
placeholder: Strings.URL_PLACEHOLDER,
|
||||
onConfirm: (input: string) => fetchFunction(input),
|
||||
confirmText: Strings.INSTALL,
|
||||
cancelText: Strings.CANCEL,
|
||||
})
|
||||
)
|
||||
}>
|
||||
<Image style={styles.icon} source={findAssetId("ic_add_24px")} />
|
||||
</TouchableOpacity>
|
||||
);
|
||||
}
|
||||
|
|
@ -3,7 +3,7 @@ import AddonPage from "@core/ui/components/AddonPage";
|
|||
import FontEditor from "@core/ui/settings/pages/Fonts/FontEditor";
|
||||
import { settings } from "@lib/api/settings";
|
||||
import { useProxy } from "@lib/api/storage";
|
||||
import { FontDefinition, fonts, installFont } from "@lib/fonts";
|
||||
import { FontDefinition, fonts } from "@lib/fonts";
|
||||
import { NavigationNative } from "@metro/common";
|
||||
|
||||
import FontCard from "./FontCard";
|
||||
|
|
@ -22,15 +22,17 @@ export default function Fonts() {
|
|||
"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)}
|
||||
safeModeHint={{ message: Strings.SAFE_MODE_NOTICE_FONTS }}
|
||||
CardComponent={FontCard}
|
||||
onFabPress={() => {
|
||||
navigation.push("BUNNY_CUSTOM_PAGE", {
|
||||
title: "Import Font",
|
||||
render: () => <FontEditor />
|
||||
});
|
||||
installAction={{
|
||||
label: "Install a font",
|
||||
onPress: () => {
|
||||
navigation.push("BUNNY_CUSTOM_PAGE", {
|
||||
title: "Import Font",
|
||||
render: () => <FontEditor />
|
||||
});
|
||||
}
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -2,8 +2,12 @@ import { Strings } from "@core/i18n";
|
|||
import AddonPage from "@core/ui/components/AddonPage";
|
||||
import PluginCard from "@core/ui/settings/pages/Plugins/components/PluginCard";
|
||||
import { VdPluginManager } from "@core/vendetta/plugins";
|
||||
import { findAssetId } from "@lib/api/assets";
|
||||
import { settings } from "@lib/api/settings";
|
||||
import { useProxy } from "@lib/api/storage";
|
||||
import { showConfirmationAlert } from "@lib/ui/alerts";
|
||||
import { showToast } from "@lib/ui/toasts";
|
||||
import { BUNNY_PROXY_PREFIX, VD_PROXY_PREFIX } from "@lib/utils/constants";
|
||||
import { Author } from "@lib/utils/types";
|
||||
import { ComponentProps } from "react";
|
||||
|
||||
|
|
@ -64,7 +68,27 @@ export default function Plugins() {
|
|||
return <PluginPage
|
||||
useItems={() => useProxy(VdPluginManager.plugins) && Object.values(VdPluginManager.plugins)}
|
||||
resolveItem={unifyVdPlugin}
|
||||
fetchFunction={(url: string) => VdPluginManager.installPlugin(url)}
|
||||
installAction={{
|
||||
label: "Install a plugin",
|
||||
fetchFn: async (url: string) => {
|
||||
const install = () =>
|
||||
VdPluginManager.installPlugin(url)
|
||||
.then(() => showToast(Strings.TOASTS_INSTALLED_PLUGIN, findAssetId("Check")))
|
||||
.catch(x => showToast(x?.message ?? `${x}`, findAssetId("Small")));
|
||||
|
||||
if (!url.startsWith(VD_PROXY_PREFIX) && !url.startsWith(BUNNY_PROXY_PREFIX) && !settings.developerSettings)
|
||||
setImmediate(() => showConfirmationAlert({
|
||||
title: Strings.MODAL_UNPROXIED_PLUGIN_HEADER,
|
||||
content: Strings.MODAL_UNPROXIED_PLUGIN_DESC,
|
||||
confirmText: Strings.CONTINUE,
|
||||
onConfirm: install,
|
||||
cancelText: Strings.CANCEL
|
||||
}));
|
||||
else {
|
||||
return await install();
|
||||
}
|
||||
}
|
||||
}}
|
||||
/>;
|
||||
|
||||
// const navigation = NavigationNative.useNavigation();
|
||||
|
|
|
|||
|
|
@ -23,7 +23,10 @@ export default function Themes() {
|
|||
"Name (A-Z)": (a, b) => a.name.localeCompare(b.name),
|
||||
"Name (Z-A)": (a, b) => b.name.localeCompare(a.name)
|
||||
}}
|
||||
fetchFunction={installTheme}
|
||||
installAction={{
|
||||
label: "Install a theme",
|
||||
fetchFn: installTheme
|
||||
}}
|
||||
items={Object.values(themes)}
|
||||
safeModeHint={{
|
||||
message: formatString("SAFE_MODE_NOTICE_THEMES", { enabled: Boolean(settings.safeMode?.currentThemeId) }),
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue