diff --git a/src/core/ui/settings/pages/Themes/ThemeCard.tsx b/src/core/ui/settings/pages/Themes/ThemeCard.tsx index 3f296ec..c3fb5db 100644 --- a/src/core/ui/settings/pages/Themes/ThemeCard.tsx +++ b/src/core/ui/settings/pages/Themes/ThemeCard.tsx @@ -1,83 +1,48 @@ -import { formatString, Strings } from "@core/i18n"; import AddonCard, { CardWrapper } from "@core/ui/components/AddonCard"; -import { showConfirmationAlert } from "@core/vendetta/alerts"; -import { useProxy } from "@core/vendetta/storage"; -import { fetchTheme, removeTheme, selectTheme, themes, VdThemeInfo } from "@lib/addons/themes"; +import { VdThemeInfo, themes, selectTheme } from "@lib/addons/themes"; import { findAssetId } from "@lib/api/assets"; import { settings } from "@lib/api/settings"; -import { clipboard } from "@metro/common"; -import { showToast } from "@ui/toasts"; - -function selectAndApply(value: boolean, theme: VdThemeInfo) { - try { - selectTheme(value ? theme : null); - } catch (e: any) { - console.error("Error while selectAndApply,", e); - } -} +import { showSheet } from "@lib/ui/sheets"; +import { NavigationNative, React } from "@metro/common"; export default function ThemeCard({ item: theme }: CardWrapper) { - useProxy(theme); + const navigation = NavigationNative.useNavigation(); + const [removed, setRemoved] = React.useState(false); - const [removed, setRemoved] = React.useState(false); + // This is needed because of Reactâ„¢ + if (removed) return null; - // This is needed because of Reactâ„¢ - if (removed) return null; + const { authors } = theme.data; - const { authors } = theme.data; - - return ( - i.name).join(", ")}` : ""} - descriptionLabel={theme.data.description ?? "No description."} - toggleType={!settings.safeMode?.enabled ? "radio" : undefined} - toggleValue={() => themes[theme.id].selected} - onToggleChange={(v: boolean) => { - selectAndApply(v, theme); - }} - overflowTitle={theme.data.name} - overflowActions={[ - { - icon: "ic_sync_24px", - label: Strings.REFETCH, - onPress: () => { - fetchTheme(theme.id, theme.selected).then(() => { - showToast(Strings.THEME_REFETCH_SUCCESSFUL, findAssetId("toast_image_saved")); - }).catch(() => { - showToast(Strings.THEME_REFETCH_FAILED, findAssetId("Small")); - }); - }, - }, - { - icon: "copy", - label: Strings.COPY_URL, - onPress: () => { - clipboard.setString(theme.id); - showToast.showCopyToClipboard(); - } - }, - { - icon: "ic_message_delete", - label: Strings.DELETE, - isDestructive: true, - onPress: () => showConfirmationAlert({ - title: Strings.HOLD_UP, - content: formatString("ARE_YOU_SURE_TO_DELETE_THEME", { name: theme.data.name }), - confirmText: Strings.DELETE, - cancelText: Strings.CANCEL, - confirmColor: "red", - onConfirm: () => { - removeTheme(theme.id).then(wasSelected => { - setRemoved(true); - if (wasSelected) selectAndApply(false, theme); - }).catch((e: Error) => { - showToast(e.message, findAssetId("Small")); - }); - } - }) - }, - ]} - /> - ); + return ( + i.name).join(", ")}` : "" + } + descriptionLabel={theme.data.description ?? "No description."} + toggleType={!settings.safeMode?.enabled ? "radio" : undefined} + toggleValue={() => themes[theme.id].selected} + onToggleChange={(v: boolean) => { + try { + selectTheme(v ? theme : null); + } catch (e: any) { + console.error("Error while selecting theme:", e); + } + }} + overflowTitle={theme.data.name} + actions={[ + { + icon: "CircleInformationIcon-primary", + onPress: () => { + const importPromise = import("./sheets/ThemeInfoActionSheet"); + showSheet("ThemeInfoActionSheet", importPromise, { + theme, + navigation, + }); + }, + }, + ]} + /> + ); } diff --git a/src/core/ui/settings/pages/Themes/sheets/ThemeInfoActionSheet.tsx b/src/core/ui/settings/pages/Themes/sheets/ThemeInfoActionSheet.tsx new file mode 100644 index 0000000..2c61a0c --- /dev/null +++ b/src/core/ui/settings/pages/Themes/sheets/ThemeInfoActionSheet.tsx @@ -0,0 +1,198 @@ +import { findAssetId } from "@lib/api/assets"; +import { purgeStorage } from "@lib/api/storage"; +import { hideSheet } from "@lib/ui/sheets"; +import { ActionSheet, Card, IconButton, Text } from "@metro/common/components"; +import { clipboard } from "@metro/common"; +import React, { ComponentProps, useEffect, useState } from "react"; +import { ScrollView, View, TouchableOpacity } from "react-native"; +import { showToast } from "@ui/toasts"; +import { showConfirmationAlert } from "@core/vendetta/alerts"; +import { fetchTheme, removeTheme, selectTheme, VdThemeInfo, } from "@lib/addons/themes"; +import { formatString, Strings } from "@core/i18n"; +import { semanticColors } from "@ui/color"; + +interface ThemeInfoActionSheetProps { + theme: VdThemeInfo; + navigation: any; +} + +function ThemeInfoIconButton(props: ComponentProps) { + const { onPress } = props; + props.onPress &&= () => { + hideSheet("ThemeInfoActionSheet"); + onPress?.(); + }; + + return ; +} + +// theme title component for header +function TitleComponent({ theme }: { theme: VdThemeInfo }) { + const { authors } = theme.data; + + return ( + + + {theme.data.name} + + + {authors && authors.length > 0 && ( + a.id)} + > + + by {authors.map((a) => a.name).join(", ")} + + + )} + + + ); +} + +export default function ThemeInfoActionSheet({ + theme, + navigation, +}: ThemeInfoActionSheetProps) { + // use component state to track theme data + const [themeState, setThemeState] = useState({ ...theme }); + const [loading, setLoading] = useState(false); + + // periodic refresh to detect external changes to theme state + useEffect(() => { + const interval = setInterval(() => { + setThemeState({ ...theme }); + }, 500); + + return () => clearInterval(interval); + }, [theme]); + + const copyThemeUrl = () => { + // Copy theme ID as URL + clipboard.setString(themeState.id); + if (typeof showToast?.showCopyToClipboard === "function") { + showToast.showCopyToClipboard(); + } else { + showToast("Copied to clipboard"); + } + }; + + const refetchTheme = async () => { + setLoading(true); + try { + await fetchTheme(themeState.id, themeState.selected); + showToast("Theme refreshed successfully"); + } catch (e) { + console.error("Failed to refresh theme:", e); + showToast("Failed to refresh theme"); + } finally { + setLoading(false); + } + }; + + const removeThemeHandler = () => { + showConfirmationAlert({ + title: Strings.HOLD_UP, + content: formatString("ARE_YOU_SURE_TO_DELETE_THEME", { + name: themeState.data.name, + }), + confirmText: Strings.DELETE, + cancelText: Strings.CANCEL, + confirmColor: "red", + onConfirm: async () => { + hideSheet("ThemeInfoActionSheet"); + try { + const wasSelected = await removeTheme(themeState.id); + if (wasSelected) selectTheme(null); + showToast("Theme removed successfully"); + } catch (e) { + console.error("Failed to remove theme:", e); + showToast("Failed to remove theme"); + } + }, + }); + }; + + const applyTheme = () => { + try { + selectTheme(themeState); + hideSheet("ThemeInfoActionSheet"); + showToast(`Applied theme: ${themeState.data.name}`); + } catch (e) { + console.error("Failed to apply theme:", e); + showToast("Failed to apply theme"); + } + }; + + return ( + + + + + + + + + + + + + Description + + + {themeState.data.description || "No description provided."} + + + + + ); +}