Update core plugin UI and add sort options to plugin list

- Refactor FixEmbed settings to use table-style rows and Stack layout -
Add sort options to core plugins page for search and sorting - Protect
certain core plugins from being toggled in PluginCard - Improve Search
input to support explicit round style - Bump build version to dev1.1.1-1
This commit is contained in:
kmmiio99o 2025-10-13 00:00:57 +02:00
parent 6b7ec92149
commit 52116478d5
6 changed files with 145 additions and 91 deletions

View file

@ -74,7 +74,7 @@ const config = {
constModules: {
globals: {
"bunny-build-info": {
version: `"1.1.0"`,
version: `"dev1.1.1-1"`,
},
},
},

View file

@ -5,13 +5,13 @@ import { logger } from "@lib/utils/logger";
import { settings } from "@lib/api/settings";
import { React } from "@metro/common";
const { FormSection, FormRow, FormSwitch, FormText } = findByProps(
"FormSection",
"FormRow",
"FormSwitch",
"FormText",
const { TableRowGroup, TableRow, TableSwitchRow, Stack } = findByProps(
"TableRowGroup",
"TableRow",
"TableSwitchRow",
"Stack",
);
const { ScrollView } = require("react-native");
const { ScrollView, Text } = require("react-native");
type FixEmbedSettings = {
enabled: boolean;
@ -101,60 +101,84 @@ export default defineCorePlugin({
setConfig((prev) => ({ ...prev, [key]: value }));
};
// Prefer table-style rows (TableRowGroup / TableSwitchRow) and Stack layout similar to other core plugins.
const {
TableRowGroup: _TRG,
TableRow: _TR,
TableSwitchRow: _TSR,
Stack: _S,
} = findByProps("TableRowGroup", "TableRow", "TableSwitchRow", "Stack");
// Fallback if the table-style components are not available in the host environment
if (!_TRG || !_TSR || !_TR || !_S) {
return React.createElement(
ScrollView,
{ style: { flex: 1, padding: 12 } },
React.createElement(
Text,
null,
"FixEmbed UI unavailable (missing TableRow components).",
),
);
}
// Use the resolved components
const TableRowGroup = _TRG;
const TableRow = _TR;
const TableSwitchRow = _TSR;
const Stack = _S;
return React.createElement(ScrollView, { style: { flex: 1 } }, [
React.createElement(
FormSection,
{ title: "General Settings" },
React.createElement(FormRow, {
label: "Enable FixEmbed",
subLabel: "Master switch for all link conversions",
trailing: React.createElement(FormSwitch, {
Stack,
{ spacing: 8, style: { padding: 10 } },
React.createElement(
TableRowGroup,
{ title: "General Settings" },
React.createElement(TableSwitchRow, {
label: "Enable FixEmbed",
subLabel: "Master switch for all link conversions",
value: config.enabled,
onValueChange: (v: boolean) => updateConfig("enabled", v),
}),
}),
),
React.createElement(FormSection, { title: "Platforms" }, [
React.createElement(FormRow, {
label: "Twitter/X",
trailing: React.createElement(FormSwitch, {
value: config.twitter,
disabled: !config.enabled,
onValueChange: (v: boolean) => updateConfig("twitter", v),
}),
}),
React.createElement(FormRow, {
label: "Instagram",
trailing: React.createElement(FormSwitch, {
value: config.instagram,
disabled: !config.enabled,
onValueChange: (v: boolean) => updateConfig("instagram", v),
}),
}),
React.createElement(FormRow, {
label: "TikTok",
trailing: React.createElement(FormSwitch, {
value: config.tiktok,
disabled: !config.enabled,
onValueChange: (v: boolean) => updateConfig("tiktok", v),
}),
}),
React.createElement(FormRow, {
label: "Reddit",
trailing: React.createElement(FormSwitch, {
value: config.reddit,
disabled: !config.enabled,
onValueChange: (v: boolean) => updateConfig("reddit", v),
}),
}),
]),
React.createElement(
FormSection,
{ title: "About" },
),
React.createElement(
FormText,
{ style: { padding: 12 } },
"This plugin automatically converts social media links to privacy-friendly alternative frontends for better embeds.",
TableRowGroup,
{ title: "Platforms" },
React.createElement(TableSwitchRow, {
label: "Twitter/X",
value: config.twitter,
onValueChange: (v: boolean) => updateConfig("twitter", v),
disabled: !config.enabled,
}),
React.createElement(TableSwitchRow, {
label: "Instagram",
value: config.instagram,
onValueChange: (v: boolean) => updateConfig("instagram", v),
disabled: !config.enabled,
}),
React.createElement(TableSwitchRow, {
label: "TikTok",
value: config.tiktok,
onValueChange: (v: boolean) => updateConfig("tiktok", v),
disabled: !config.enabled,
}),
React.createElement(TableSwitchRow, {
label: "Reddit",
value: config.reddit,
onValueChange: (v: boolean) => updateConfig("reddit", v),
disabled: !config.enabled,
}),
),
React.createElement(
TableRowGroup,
{ title: "About" },
React.createElement(TableRow, {
label: "Description",
subLabel:
"This plugin automatically converts social media links to privacy-friendly alternative frontends for better embeds.",
disabled: true,
}),
),
),
]);

View file

@ -29,6 +29,11 @@ export default function CorePlugins() {
?.map((a: any) => (typeof a === "string" ? a : a.name))
.join() || "",
]}
// Provide simple sort options so the Search input renders round and a sort menu appears
sortOptions={{
"Name (A-Z)": (a, b) => a.name.localeCompare(b.name),
"Name (Z-A)": (a, b) => b.name.localeCompare(a.name),
}}
// Only core plugins: map corePluginInstances -> registeredPlugins -> unify
items={[...corePluginInstances.keys()]
.map((id) => registeredPlugins.get(id))

View file

@ -1,8 +1,8 @@
import { React, NavigationNative, tokens } from "@metro/common";
import { CardWrapper } from "@core/ui/components/AddonCard";
import { UnifiedPluginModel } from "@core/ui/settings/pages/Plugins/models";
import { usePluginCardStyles } from "@core/ui/settings/pages/Plugins/usePluginCardStyles";
import { findAssetId } from "@lib/api/assets";
import { NavigationNative, tokens } from "@metro/common";
import {
Card,
IconButton,
@ -152,6 +152,11 @@ export default function PluginCard({
[plugin, result],
);
// Protect specific core plugins from being toggled
const idLower = ((plugin.id || "") as string).toLowerCase();
const isProtectedCore =
idLower.includes("quickinstall") || idLower === "bunny.badges";
return (
<CardContext.Provider value={cardContextValue}>
<Card>
@ -166,13 +171,18 @@ export default function PluginCard({
<View>
<Stack spacing={12} direction="horizontal">
<Actions />
<TableSwitch
value={plugin.isEnabled()}
onValueChange={(v: boolean) => {
plugin.toggle(v);
forceUpdate();
}}
/>
{/* Dim and disable the switch for protected core plugins */}
<View style={{ opacity: isProtectedCore ? 0.45 : 1 }}>
<TableSwitch
value={plugin.isEnabled()}
disabled={isProtectedCore}
onValueChange={(v: boolean) => {
if (isProtectedCore) return;
plugin.toggle(v);
forceUpdate();
}}
/>
</View>
</Stack>
</View>
</View>

View file

@ -1,3 +1,4 @@
import { React } from "@metro/common";
import { Strings } from "@core/i18n";
import { findAssetId } from "@lib/api/assets";
import { TextInput } from "@metro/common/components";
@ -5,39 +6,53 @@ import ErrorBoundary from "@ui/components/ErrorBoundary";
import { Image, View, ViewStyle } from "react-native";
export interface SearchProps {
onChangeText?: (v: string) => void;
placeholder?: string;
style?: ViewStyle;
isRound?: boolean;
onChangeText?: (v: string) => void;
placeholder?: string;
style?: ViewStyle;
isRound?: boolean;
}
function SearchIcon() {
return <Image style={{ width: 16, height: 16 }} source={findAssetId("icon-search")!} />;
return (
<Image
style={{ width: 16, height: 16 }}
source={findAssetId("icon-search")!}
/>
);
}
export default ({ onChangeText, placeholder, style, isRound }: SearchProps) => {
const [query, setQuery] = React.useState("");
const [query, setQuery] = React.useState("");
const onChange = (value: string) => {
setQuery(value);
onChangeText?.(value);
};
const onChange = (value: string) => {
setQuery(value);
onChangeText?.(value);
};
return <ErrorBoundary>
<View style={style}>
<TextInput
grow={true}
isClearable={true}
leadingIcon={SearchIcon}
placeholder={placeholder ?? Strings.SEARCH}
onChange={onChange}
returnKeyType="search"
size="md"
autoCapitalize="none"
autoCorrect={false}
isRound={isRound}
value={query}
/>
</View>
</ErrorBoundary>;
// When isRound is true, provide an explicit pill-style appearance so the
// input looks rounded regardless of underlying component defaults.
const inputStyle = isRound
? ({ borderRadius: 9999, overflow: "hidden" } as ViewStyle)
: undefined;
return (
<ErrorBoundary>
<View style={style}>
<TextInput
grow={true}
isClearable={true}
leadingIcon={SearchIcon}
placeholder={placeholder ?? Strings.SEARCH}
onChange={onChange}
returnKeyType="search"
size="md"
autoCapitalize="none"
autoCorrect={false}
isRound={isRound}
style={inputStyle}
value={query}
/>
</View>
</ErrorBoundary>
);
};

2
src/modules.d.ts vendored
View file

@ -1,5 +1,5 @@
declare module "bunny-build-info" {
const version: "1.1.0";
const version: "dev1.1.1-1";
}
declare module "*.png" {