GoonCord/src/ui/settings/components/PluginCard.tsx

134 lines
5.8 KiB
TypeScript
Raw Normal View History

import { ReactNative as RN, stylesheet, constants, NavigationNative } from "@metro/common";
import { findByProps } from "@metro/filters";
import { Forms, General } from "@ui/components";
import { Plugin } from "@types";
2023-01-04 22:39:28 +00:00
import { getAssetIDByName } from "@ui/assets";
2023-01-07 23:05:14 +00:00
import { showToast } from "@ui/toasts";
import { removePlugin, startPlugin, stopPlugin, getSettings } from "@lib/plugins";
import copyText from "@utils/copyText";
//! This module is only found on 165.0+, under the assumption that iOS 165.0 is the same as Android 165.0.
//* In 167.1 (Android alpha at the time of writing), stylesheet.ThemeColorMap is gone, and constants.ThemeColorMap has broken behaviour.
//? SemanticColor is effectively ThemeColorMap
//? RawColor is effectively Colors
// TODO: Should this hotfix be moved elsewhere? Maybe a prop on window object, for plugins to use.
const colorModule = findByProps("SemanticColorsByThemeTable");
const colorMap = (colorModule?.SemanticColor ?? constants.ThemeColorMap);
2023-02-16 02:16:51 +00:00
// TODO: Replace with our eventual dialog API
const Dialog = findByProps("show", "openLazy", "close");
2023-01-03 08:05:16 +00:00
const { FormRow, FormSwitch } = Forms;
const { TouchableOpacity, Image } = General;
2023-02-16 02:16:51 +00:00
// TODO: These styles work weirdly. iOS has cramped text, Android with low DPI probably does too. Fix?
const styles = stylesheet.createThemedStyleSheet({
card: {
backgroundColor: colorMap?.BACKGROUND_SECONDARY,
borderRadius: 5,
2023-02-16 02:16:51 +00:00
marginHorizontal: 10,
marginBottom: 10,
},
header: {
2023-02-16 02:16:51 +00:00
padding: 0,
backgroundColor: colorMap?.BACKGROUND_TERTIARY,
borderTopLeftRadius: 5,
borderTopRightRadius: 5,
},
actions: {
2023-01-14 02:41:28 +00:00
flexDirection: "row-reverse",
alignItems: "center",
},
icon: {
width: 22,
height: 22,
2023-01-07 23:05:14 +00:00
marginLeft: 5,
tintColor: colorMap?.INTERACTIVE_NORMAL,
}
})
interface PluginCardProps {
plugin: Plugin;
}
export default function PluginCard({ plugin }: PluginCardProps) {
const settings = getSettings(plugin.id);
const navigation = NavigationNative.useNavigation();
2023-01-07 23:05:14 +00:00
const [removed, setRemoved] = React.useState(false);
2023-02-16 02:16:51 +00:00
// This is needed because of React™
if (removed) return null;
2023-01-07 23:05:14 +00:00
return (
<RN.View style={styles.card}>
<FormRow
style={styles.header}
2023-02-16 02:16:51 +00:00
// TODO: Actually make use of user IDs
2023-01-04 22:39:28 +00:00
label={`${plugin.manifest.name} by ${plugin.manifest.authors.map(i => i.name).join(", ")}`}
2023-01-16 08:11:46 +00:00
leading={<FormRow.Icon source={getAssetIDByName(plugin.manifest.vendetta?.icon || "ic_application_command_24px")} />}
trailing={
<FormSwitch
2023-02-16 02:16:51 +00:00
style={RN.Platform.OS === "android" && { marginVertical: -15 }}
value={plugin.enabled}
onValueChange={(v: boolean) => {
2023-02-20 18:43:50 +00:00
try {
if (v) startPlugin(plugin.id); else stopPlugin(plugin.id);
} catch (e) {
showToast((e as Error).message, getAssetIDByName("Small"));
}
}}
/>
}
/>
<FormRow
label={plugin.manifest.description}
trailing={
<RN.View style={styles.actions}>
2023-01-07 23:05:14 +00:00
<TouchableOpacity
2023-02-16 02:16:51 +00:00
onPress={() => Dialog.show({
title: "Wait!",
body: `Are you sure you wish to delete ${plugin.manifest.name}?`,
confirmText: "Delete",
cancelText: "Cancel",
confirmColor: "red",
onConfirm: () => {
2023-02-20 18:43:50 +00:00
try {
removePlugin(plugin.id);
setRemoved(true);
} catch (e) {
showToast((e as Error).message, getAssetIDByName("Small"));
}
2023-02-16 02:16:51 +00:00
}
})}
2023-01-07 23:05:14 +00:00
>
<Image style={styles.icon} source={getAssetIDByName("ic_message_delete")} />
</TouchableOpacity>
2023-01-14 02:41:28 +00:00
<TouchableOpacity
onPress={() => {
copyText(plugin.id);
showToast("Copied plugin URL to clipboard.", getAssetIDByName("toast_copy_link"));
}}
>
<Image style={styles.icon} source={getAssetIDByName("copy")} />
</TouchableOpacity>
2023-01-07 23:05:14 +00:00
<TouchableOpacity
onPress={() => {
plugin.update = !plugin.update;
showToast(`${plugin.update ? "Enabled" : "Disabled"} updates for ${plugin.manifest.name}.`, getAssetIDByName("toast_image_saved"));
}}
>
<Image style={styles.icon} source={getAssetIDByName(plugin.update ? "Check" : "Small")} />
2023-01-07 23:05:14 +00:00
</TouchableOpacity>
{settings && <TouchableOpacity onPress={() => navigation.push("VendettaCustomPage", {
title: plugin.manifest.name,
render: settings,
})}>
<Image style={styles.icon} source={getAssetIDByName("settings")} />
</TouchableOpacity>}
</RN.View>
}
/>
</RN.View>
)
}