[UI] Overall overhaul
This commit is contained in:
parent
474aad2a47
commit
32b3a0295c
9 changed files with 81 additions and 78 deletions
38
src/ui/settings/components/AddonPage.tsx
Normal file
38
src/ui/settings/components/AddonPage.tsx
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
import { Indexable } from "@types";
|
||||||
|
import { ReactNative as RN } from "@metro/common";
|
||||||
|
import { useProxy } from "@lib/storage";
|
||||||
|
import { HelpMessage, ErrorBoundary, Search } from "@ui/components";
|
||||||
|
import { CardWrapper } from "@ui/settings/components/Card";
|
||||||
|
import settings from "@lib/settings";
|
||||||
|
|
||||||
|
interface AddonPageProps<T> {
|
||||||
|
items: Indexable<T & { id: string }>;
|
||||||
|
safeModeMessage: string;
|
||||||
|
safeModeExtras?: JSX.Element | JSX.Element[];
|
||||||
|
card: React.ComponentType<CardWrapper<T>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function AddonPage<T>({ items, safeModeMessage, safeModeExtras, card: CardComponent }: AddonPageProps<T>) {
|
||||||
|
useProxy(settings)
|
||||||
|
useProxy(items);
|
||||||
|
const [search, setSearch] = React.useState("");
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ErrorBoundary>
|
||||||
|
<RN.ScrollView style={{ flex: 1 }} contentContainerStyle={{ padding: 10 }}>
|
||||||
|
{settings.safeMode?.enabled && <RN.View style={{ marginBottom: 10 }}>
|
||||||
|
<HelpMessage messageType={0}>{safeModeMessage}</HelpMessage>
|
||||||
|
{safeModeExtras}
|
||||||
|
</RN.View>}
|
||||||
|
<Search
|
||||||
|
style={{ marginBottom: 10 }}
|
||||||
|
onChangeText={(v: string) => setSearch(v.toLowerCase())}
|
||||||
|
placeholder="Search"
|
||||||
|
/>
|
||||||
|
{/* TODO: When I am more awake, implement better searching than just by ID */}
|
||||||
|
{/* TODO: Also when I am more awake, make the search bar not scroll with the cards */}
|
||||||
|
{Object.values(items).filter(i => i.id?.toLowerCase().includes(search)).map((i, id) => <CardComponent item={i} index={id} />)}
|
||||||
|
</RN.ScrollView>
|
||||||
|
</ErrorBoundary>
|
||||||
|
)
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
import { Asset } from "@types";
|
import { Asset } from "@types";
|
||||||
import { ReactNative as RN, stylesheet, clipboard } from "@metro/common";
|
import { ReactNative as RN, clipboard } from "@metro/common";
|
||||||
import { showToast } from "@ui/toasts";
|
import { showToast } from "@ui/toasts";
|
||||||
import { getAssetIDByName } from "@ui/assets";
|
import { getAssetIDByName } from "@ui/assets";
|
||||||
import { Forms } from "@ui/components";
|
import { Forms } from "@ui/components";
|
||||||
|
@ -10,18 +10,11 @@ interface AssetDisplayProps {
|
||||||
|
|
||||||
const { FormRow } = Forms;
|
const { FormRow } = Forms;
|
||||||
|
|
||||||
const styles = stylesheet.createThemedStyleSheet({
|
|
||||||
asset: {
|
|
||||||
width: 32,
|
|
||||||
height: 32,
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
export default function AssetDisplay({ asset }: AssetDisplayProps) {
|
export default function AssetDisplay({ asset }: AssetDisplayProps) {
|
||||||
return (
|
return (
|
||||||
<FormRow
|
<FormRow
|
||||||
label={`${asset.name} - ${asset.id}`}
|
label={`${asset.name} - ${asset.id}`}
|
||||||
trailing={<RN.Image source={asset.id} style={styles.asset} />}
|
trailing={<RN.Image source={asset.id} style={{ width: 32, height: 32 }} />}
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
clipboard.setString(asset.name);
|
clipboard.setString(asset.name);
|
||||||
showToast("Copied asset name to clipboard.", getAssetIDByName("toast_copy_link"));
|
showToast("Copied asset name to clipboard.", getAssetIDByName("toast_copy_link"));
|
||||||
|
|
|
@ -13,8 +13,6 @@ const styles = stylesheet.createThemedStyleSheet({
|
||||||
card: {
|
card: {
|
||||||
backgroundColor: semanticColors?.BACKGROUND_SECONDARY,
|
backgroundColor: semanticColors?.BACKGROUND_SECONDARY,
|
||||||
borderRadius: 5,
|
borderRadius: 5,
|
||||||
marginHorizontal: 10,
|
|
||||||
marginBottom: 10,
|
|
||||||
},
|
},
|
||||||
header: {
|
header: {
|
||||||
padding: 0,
|
padding: 0,
|
||||||
|
@ -44,6 +42,11 @@ interface OverflowAction extends Action {
|
||||||
isDestructive?: boolean;
|
isDestructive?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface CardWrapper<T> {
|
||||||
|
item: T;
|
||||||
|
index: number;
|
||||||
|
}
|
||||||
|
|
||||||
interface CardProps {
|
interface CardProps {
|
||||||
index?: number;
|
index?: number;
|
||||||
headerLabel: string | React.ComponentType;
|
headerLabel: string | React.ComponentType;
|
||||||
|
@ -61,7 +64,7 @@ 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}
|
||||||
|
|
|
@ -5,20 +5,15 @@ import { getAssetIDByName } from "@ui/assets";
|
||||||
import { showToast } from "@ui/toasts";
|
import { showToast } from "@ui/toasts";
|
||||||
import { showConfirmationAlert } from "@ui/alerts";
|
import { showConfirmationAlert } from "@ui/alerts";
|
||||||
import { removePlugin, startPlugin, stopPlugin, getSettings, fetchPlugin } from "@lib/plugins";
|
import { removePlugin, startPlugin, stopPlugin, getSettings, fetchPlugin } from "@lib/plugins";
|
||||||
import Card from "@ui/settings/components/Card";
|
import Card, { CardWrapper } from "@ui/settings/components/Card";
|
||||||
|
|
||||||
interface PluginCardProps {
|
async function stopThenStart(plugin: Plugin, callback: Function) {
|
||||||
plugin: Plugin;
|
|
||||||
index: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function stopThenStart(plugin: Plugin, func: Function) {
|
|
||||||
if (plugin.enabled) stopPlugin(plugin.id, false);
|
if (plugin.enabled) stopPlugin(plugin.id, false);
|
||||||
func();
|
callback();
|
||||||
if (plugin.enabled) await startPlugin(plugin.id);
|
if (plugin.enabled) await startPlugin(plugin.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function PluginCard({ plugin, index }: PluginCardProps) {
|
export default function PluginCard({ item: plugin, index }: CardWrapper<Plugin>) {
|
||||||
const settings = getSettings(plugin.id);
|
const settings = getSettings(plugin.id);
|
||||||
const navigation = NavigationNative.useNavigation();
|
const navigation = NavigationNative.useNavigation();
|
||||||
const [removed, setRemoved] = React.useState(false);
|
const [removed, setRemoved] = React.useState(false);
|
||||||
|
|
|
@ -12,7 +12,7 @@ export default function SettingsSection() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ErrorBoundary>
|
<ErrorBoundary>
|
||||||
<FormSection key="Vendetta" title="Vendetta">
|
<FormSection key="Vendetta" title={`Vendetta${settings.safeMode?.enabled ? " (Safe Mode)" : ""}`}>
|
||||||
<FormRow
|
<FormRow
|
||||||
label="General"
|
label="General"
|
||||||
leading={<FormRow.Icon source={getAssetIDByName("settings")} />}
|
leading={<FormRow.Icon source={getAssetIDByName("settings")} />}
|
||||||
|
|
|
@ -7,19 +7,14 @@ import { getAssetIDByName } from "@ui/assets";
|
||||||
import { showConfirmationAlert } from "@ui/alerts";
|
import { showConfirmationAlert } from "@ui/alerts";
|
||||||
import { showToast } from "@ui/toasts";
|
import { showToast } from "@ui/toasts";
|
||||||
import settings from "@lib/settings";
|
import settings from "@lib/settings";
|
||||||
import Card from "@ui/settings/components/Card";
|
import Card, { CardWrapper } from "@ui/settings/components/Card";
|
||||||
|
|
||||||
interface ThemeCardProps {
|
|
||||||
theme: Theme;
|
|
||||||
index: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function selectAndReload(value: boolean, id: string) {
|
async function selectAndReload(value: boolean, id: string) {
|
||||||
await selectTheme(value ? id : "default");
|
await selectTheme(value ? id : "default");
|
||||||
BundleUpdaterManager.reload();
|
BundleUpdaterManager.reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function ThemeCard({ theme, index }: ThemeCardProps) {
|
export default function ThemeCard({ item: theme, index }: CardWrapper<Theme>) {
|
||||||
useProxy(settings);
|
useProxy(settings);
|
||||||
const [removed, setRemoved] = React.useState(false);
|
const [removed, setRemoved] = React.useState(false);
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,6 @@ import AssetDisplay from "@ui/settings/components/AssetDisplay";
|
||||||
|
|
||||||
const { FormDivider } = Forms;
|
const { FormDivider } = Forms;
|
||||||
|
|
||||||
|
|
||||||
export default function AssetBrowser() {
|
export default function AssetBrowser() {
|
||||||
const [search, setSearch] = React.useState("");
|
const [search, setSearch] = React.useState("");
|
||||||
|
|
||||||
|
@ -14,17 +13,14 @@ export default function AssetBrowser() {
|
||||||
<ErrorBoundary>
|
<ErrorBoundary>
|
||||||
<RN.View style={{ flex: 1 }}>
|
<RN.View style={{ flex: 1 }}>
|
||||||
<Search
|
<Search
|
||||||
|
style={{ margin: 10 }}
|
||||||
onChangeText={(v: string) => setSearch(v)}
|
onChangeText={(v: string) => setSearch(v)}
|
||||||
placeholder="Search"
|
placeholder="Search"
|
||||||
/>
|
/>
|
||||||
<RN.FlatList
|
<RN.FlatList
|
||||||
data={Object.values(all).filter(a => a.name.includes(search) || a.id.toString() === search)}
|
data={Object.values(all).filter(a => a.name.includes(search) || a.id.toString() === search)}
|
||||||
renderItem={({ item }) => (
|
renderItem={({ item }) => <AssetDisplay asset={item} />}
|
||||||
<>
|
ItemSeparatorComponent={FormDivider}
|
||||||
<AssetDisplay asset={item} />
|
|
||||||
<FormDivider />
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
keyExtractor={item => item.name}
|
keyExtractor={item => item.name}
|
||||||
/>
|
/>
|
||||||
</RN.View>
|
</RN.View>
|
||||||
|
|
|
@ -1,27 +1,18 @@
|
||||||
import { ReactNative as RN } from "@metro/common";
|
import { Plugin } from "@types";
|
||||||
import { useProxy } from "@lib/storage";
|
import { useProxy } from "@lib/storage";
|
||||||
import { plugins } from "@lib/plugins";
|
import { plugins } from "@lib/plugins";
|
||||||
import { HelpMessage } from "@ui/components";
|
|
||||||
import settings from "@lib/settings";
|
import settings from "@lib/settings";
|
||||||
|
import AddonPage from "@ui/settings/components/AddonPage";
|
||||||
import PluginCard from "@ui/settings/components/PluginCard";
|
import PluginCard from "@ui/settings/components/PluginCard";
|
||||||
import ErrorBoundary from "@ui/components/ErrorBoundary";
|
|
||||||
|
|
||||||
export default function Plugins() {
|
export default function Plugins() {
|
||||||
useProxy(settings)
|
useProxy(settings)
|
||||||
useProxy(plugins);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ErrorBoundary>
|
<AddonPage<Plugin>
|
||||||
<RN.View style={{ flex: 1 }}>
|
items={plugins}
|
||||||
{settings.safeMode?.enabled && <RN.View style={{ margin: 10 }}>
|
safeModeMessage="You are in Safe Mode, so plugins cannot be loaded. Disable any misbehaving plugins, then return to Normal Mode from the General settings page."
|
||||||
<HelpMessage messageType={0}>You are in Safe Mode, so plugins cannot be loaded. Disable any misbehaving plugins, then return to Normal Mode from the General settings page. </HelpMessage>
|
card={PluginCard}
|
||||||
</RN.View>}
|
|
||||||
<RN.FlatList
|
|
||||||
data={Object.values(plugins)}
|
|
||||||
renderItem={({ item, index }) => <PluginCard plugin={item} index={index} />}
|
|
||||||
keyExtractor={item => item.id}
|
|
||||||
/>
|
/>
|
||||||
</RN.View>
|
|
||||||
</ErrorBoundary>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
|
@ -1,21 +1,19 @@
|
||||||
import { ButtonColors } from "@types";
|
import { Theme, ButtonColors } from "@types";
|
||||||
import { ReactNative as RN } from "@metro/common";
|
|
||||||
import { themes } from "@lib/themes";
|
|
||||||
import { useProxy } from "@lib/storage";
|
import { useProxy } from "@lib/storage";
|
||||||
import { ErrorBoundary, Button, HelpMessage } from "@ui/components";
|
import { themes } from "@lib/themes";
|
||||||
|
import { Button } from "@ui/components";
|
||||||
import settings from "@lib/settings";
|
import settings from "@lib/settings";
|
||||||
|
import AddonPage from "@ui/settings/components/AddonPage";
|
||||||
import ThemeCard from "@ui/settings/components/ThemeCard";
|
import ThemeCard from "@ui/settings/components/ThemeCard";
|
||||||
|
|
||||||
export default function Themes() {
|
export default function Themes() {
|
||||||
useProxy(settings);
|
useProxy(settings);
|
||||||
useProxy(themes);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ErrorBoundary>
|
<AddonPage<Theme>
|
||||||
<RN.View style={{ flex: 1 }}>
|
items={themes}
|
||||||
{settings.safeMode?.enabled && <RN.View style={{ margin: 10 }}>
|
safeModeMessage={`You are in Safe Mode, meaning themes have been temporarily disabled.${settings.safeMode?.currentThemeId ? " If a theme appears to be causing the issue, you can press below to disable it persistently." : ""}`}
|
||||||
<HelpMessage messageType={0}>You are in Safe Mode, meaning themes have been temporarily disabled.{settings.safeMode?.currentThemeId && " If a theme appears to be causing the issue, you can press below to disable it persistently."}</HelpMessage>
|
safeModeExtras={settings.safeMode?.currentThemeId ? <Button
|
||||||
{settings.safeMode?.currentThemeId && <Button
|
|
||||||
text="Disable Theme"
|
text="Disable Theme"
|
||||||
color={ButtonColors.BRAND}
|
color={ButtonColors.BRAND}
|
||||||
size="small"
|
size="small"
|
||||||
|
@ -23,14 +21,8 @@ export default function Themes() {
|
||||||
delete settings.safeMode?.currentThemeId;
|
delete settings.safeMode?.currentThemeId;
|
||||||
}}
|
}}
|
||||||
style={{ marginTop: 8 }}
|
style={{ marginTop: 8 }}
|
||||||
/>}
|
/> : undefined}
|
||||||
</RN.View>}
|
card={ThemeCard}
|
||||||
<RN.FlatList
|
|
||||||
data={Object.values(themes)}
|
|
||||||
renderItem={({ item, index }) => <ThemeCard theme={item} index={index} />}
|
|
||||||
keyExtractor={item => item.id}
|
|
||||||
/>
|
/>
|
||||||
</RN.View>
|
|
||||||
</ErrorBoundary>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
Loading…
Reference in a new issue