[UI > Card] Move most actions into overflow action sheet (#43)

* [Plugin/ThemeCard] Move most actions into overflow action sheet

* [UI > Card] Move overflow menu, add icon to header

---------

Co-authored-by: Beef <beefers@riseup.net>
This commit is contained in:
Jack 2023-03-23 19:07:03 -04:00 committed by Beef
parent 72c9055b5a
commit ef702fb5ca
3 changed files with 79 additions and 44 deletions

View file

@ -1,9 +1,12 @@
import { ReactNative as RN, stylesheet } from "@metro/common"; import { ReactNative as RN, stylesheet } from "@metro/common";
import { findByProps } from "@metro/filters";
import { Forms } from "@ui/components"; import { Forms } from "@ui/components";
import { getAssetIDByName } from "@ui/assets"; import { getAssetIDByName } from "@ui/assets";
import { semanticColors } from "@ui/color"; import { semanticColors } from "@ui/color";
const { FormRow, FormSwitch, FormRadio } = Forms; const { FormRow, FormSwitch, FormRadio } = Forms;
const { hideActionSheet } = findByProps("openLazy", "hideActionSheet");
const { showSimpleActionSheet } = findByProps("showSimpleActionSheet");
// TODO: These styles work weirdly. iOS has cramped text, Android with low DPI probably does too. Fix? // TODO: These styles work weirdly. iOS has cramped text, Android with low DPI probably does too. Fix?
const styles = stylesheet.createThemedStyleSheet({ const styles = stylesheet.createThemedStyleSheet({
@ -36,6 +39,11 @@ interface Action {
onPress: () => void; onPress: () => void;
} }
interface OverflowAction extends Action {
label: string;
isDestructive?: boolean;
}
interface CardProps { interface CardProps {
index?: number; index?: number;
headerLabel: string | React.ComponentType; headerLabel: string | React.ComponentType;
@ -45,13 +53,15 @@ interface CardProps {
onToggleChange?: (v: boolean) => void; onToggleChange?: (v: boolean) => void;
descriptionLabel?: string | React.ComponentType; descriptionLabel?: string | React.ComponentType;
actions?: Action[]; actions?: Action[];
overflowTitle?: string;
overflowActions?: OverflowAction[];
} }
export default function Card(props: CardProps) { export default function Card(props: CardProps) {
let pressableState = props.toggleValue ?? false; let pressableState = props.toggleValue ?? false;
return ( return (
<RN.View style={[styles.card, {marginTop: props.index === 0 ? 10 : 0}]}> <RN.View style={[styles.card, { marginTop: props.index === 0 ? 10 : 0 }]}>
<FormRow <FormRow
style={styles.header} style={styles.header}
label={props.headerLabel} label={props.headerLabel}
@ -76,6 +86,19 @@ export default function Card(props: CardProps) {
label={props.descriptionLabel} label={props.descriptionLabel}
trailing={ trailing={
<RN.View style={styles.actions}> <RN.View style={styles.actions}>
{props.overflowActions && <RN.TouchableOpacity
onPress={() => showSimpleActionSheet({
key: "CardOverflow",
header: {
title: props.overflowTitle,
icon: props.headerIcon && <FormRow.Icon style={{ marginRight: 8 }} source={getAssetIDByName(props.headerIcon)} />,
onClose: () => hideActionSheet(),
},
options: props.overflowActions?.map(i => ({ ...i, icon: getAssetIDByName(i.icon) })),
})}
>
<RN.Image style={styles.icon} source={getAssetIDByName("ic_more_24px")} />
</RN.TouchableOpacity>}
{props.actions?.map(({ icon, onPress }) => ( {props.actions?.map(({ icon, onPress }) => (
<RN.TouchableOpacity <RN.TouchableOpacity
onPress={onPress} onPress={onPress}

View file

@ -20,7 +20,7 @@ export default function PluginCard({ plugin, index }: PluginCardProps) {
if (removed) return null; if (removed) return null;
return ( return (
<Card <Card
index={index} index={index}
// TODO: Actually make use of user IDs // TODO: Actually make use of user IDs
headerLabel={`${plugin.manifest.name} by ${plugin.manifest.authors.map(i => i.name).join(", ")}`} headerLabel={`${plugin.manifest.name} by ${plugin.manifest.authors.map(i => i.name).join(", ")}`}
@ -35,9 +35,28 @@ export default function PluginCard({ plugin, index }: PluginCardProps) {
} }
}} }}
descriptionLabel={plugin.manifest.description} descriptionLabel={plugin.manifest.description}
actions={[ overflowTitle={plugin.manifest.name}
overflowActions={[
{
icon: "ic_download_24px",
label: plugin.update ? "Disable updates" : "Enable updates",
onPress: () => {
plugin.update = !plugin.update;
showToast(`${plugin.update ? "Enabled" : "Disabled"} updates for ${plugin.manifest.name}.`, getAssetIDByName("toast_image_saved"));
}
},
{
icon: "copy",
label: "Copy plugin URL",
onPress: () => {
clipboard.setString(plugin.id);
showToast("Copied plugin URL to clipboard.", getAssetIDByName("toast_copy_link"));
}
},
{ {
icon: "ic_message_delete", icon: "ic_message_delete",
label: "Delete plugin",
isDestructive: true,
onPress: () => showConfirmationAlert({ onPress: () => showConfirmationAlert({
title: "Wait!", title: "Wait!",
content: `Are you sure you wish to delete ${plugin.manifest.name}?`, content: `Are you sure you wish to delete ${plugin.manifest.name}?`,
@ -54,20 +73,8 @@ export default function PluginCard({ plugin, index }: PluginCardProps) {
} }
}), }),
}, },
{ ]}
icon: "copy", actions={[
onPress: () => {
clipboard.setString(plugin.id);
showToast("Copied plugin URL to clipboard.", getAssetIDByName("toast_copy_link"));
},
},
{
icon: plugin.update ? "Check" : "Small",
onPress: () => {
plugin.update = !plugin.update;
showToast(`${plugin.update ? "Enabled" : "Disabled"} updates for ${plugin.manifest.name}.`, getAssetIDByName("toast_image_saved"));
}
},
...(settings ? [{ ...(settings ? [{
icon: "settings", icon: "settings",
onPress: () => navigation.push("VendettaCustomPage", { onPress: () => navigation.push("VendettaCustomPage", {

View file

@ -25,7 +25,7 @@ export default function ThemeCard({ theme, index }: ThemeCardProps) {
const authors = theme.data.authors; const authors = theme.data.authors;
return ( return (
<Card <Card
index={index} index={index}
headerLabel={`${theme.data.name} ${authors ? `by ${authors.map(i => i.name).join(", ")}` : ""}`} headerLabel={`${theme.data.name} ${authors ? `by ${authors.map(i => i.name).join(", ")}` : ""}`}
descriptionLabel={theme.data.description ?? "No description."} descriptionLabel={theme.data.description ?? "No description."}
@ -34,34 +34,11 @@ export default function ThemeCard({ theme, index }: ThemeCardProps) {
onToggleChange={(v: boolean) => { onToggleChange={(v: boolean) => {
selectAndReload(v, theme.id); selectAndReload(v, theme.id);
}} }}
actions={[ overflowTitle={theme.data.name}
{ overflowActions={[
icon: "ic_message_delete",
onPress: () => showConfirmationAlert({
title: "Wait!",
content: `Are you sure you wish to delete ${theme.data.name}?`,
confirmText: "Delete",
cancelText: "Cancel",
confirmColor: ButtonColors.RED,
onConfirm: () => {
removeTheme(theme.id).then((wasSelected) => {
setRemoved(true);
if (wasSelected) selectAndReload(false, theme.id);
}).catch((e: Error) => {
showToast(e.message, getAssetIDByName("Small"));
});
}
}),
},
{
icon: "copy",
onPress: () => {
clipboard.setString(theme.id);
showToast("Copied theme URL to clipboard.", getAssetIDByName("toast_copy_link"));
},
},
{ {
icon: "ic_sync_24px", icon: "ic_sync_24px",
label: "Refetch",
onPress: () => { onPress: () => {
fetchTheme(theme.id).then(() => { fetchTheme(theme.id).then(() => {
if (theme.selected) { if (theme.selected) {
@ -81,6 +58,34 @@ export default function ThemeCard({ theme, index }: ThemeCardProps) {
}); });
}, },
}, },
{
icon: "copy",
label: "Copy theme URL",
onPress: () => {
clipboard.setString(theme.id);
showToast("Copied theme URL to clipboard.", getAssetIDByName("toast_copy_link"));
}
},
{
icon: "ic_message_delete",
label: "Delete theme",
isDestructive: true,
onPress: () => showConfirmationAlert({
title: "Wait!",
content: `Are you sure you wish to delete ${theme.data.name}?`,
confirmText: "Delete",
cancelText: "Cancel",
confirmColor: ButtonColors.RED,
onConfirm: () => {
removeTheme(theme.id).then((wasSelected) => {
setRemoved(true);
if (wasSelected) selectAndReload(false, theme.id);
}).catch((e: Error) => {
showToast(e.message, getAssetIDByName("Small"));
});
}
})
},
]} ]}
/> />
) )