diff --git a/plugins/better-timestamps/src/ui/Settings.tsx b/plugins/better-timestamps/src/ui/Settings.tsx index aee8330..e784202 100644 --- a/plugins/better-timestamps/src/ui/Settings.tsx +++ b/plugins/better-timestamps/src/ui/Settings.tsx @@ -1,13 +1,12 @@ import { React, ReactNative, moment } from "@vendetta/metro/common"; import { storage } from "@vendetta/plugin"; import { useProxy } from "@vendetta/storage"; -import { Forms } from "@vendetta/ui/components"; +import { TableRow, TableRowGroup, TableSwitchRow, TextInput } from "@shared/ui/table"; import SettingsScaffold from "@shared/ui/SettingsScaffold"; import { resolveSemanticColorSafe } from "@shared/lib/color"; import renderTimestamp, { TimestampMode } from "../lib/renderTimestamp"; const { Text } = ReactNative; -const { FormSection, FormRow, FormDivider, FormSwitchRow, FormInput } = Forms; const MODES: { label: string; key: TimestampMode }[] = [ { label: "Calendar", key: "calendar" }, @@ -21,41 +20,40 @@ export default function Settings() { return ( - - {MODES.map((mode, i) => { + + {MODES.map((mode) => { const selected = storage.selected === mode.key; let preview = ""; try { preview = renderTimestamp(moment(), mode.key); } catch { preview = ""; } return ( - ✓ : undefined} onPress={() => { storage.selected = mode.key; }} /> {mode.key === "custom" && selected && ( - { storage.customFormat = v; }} /> )} - {i !== MODES.length - 1 && } ); })} - - - + + { storage.separateMessages = v; }} /> - + ); } diff --git a/plugins/custom-user-tags/src/ui/Settings.tsx b/plugins/custom-user-tags/src/ui/Settings.tsx index 5ea176b..e7f280d 100644 --- a/plugins/custom-user-tags/src/ui/Settings.tsx +++ b/plugins/custom-user-tags/src/ui/Settings.tsx @@ -1,7 +1,7 @@ import { React, ReactNative } from "@vendetta/metro/common"; import { useProxy } from "@vendetta/storage"; import { storage } from "@vendetta/plugin"; -import { Forms } from "@vendetta/ui/components"; +import { TableRowGroup, TextInput } from "@shared/ui/table"; import { showToast } from "@vendetta/ui/toasts"; import SettingsScaffold from "@shared/ui/SettingsScaffold"; import ListSection from "@shared/ui/ListSection"; @@ -11,7 +11,6 @@ import NoteBox from "@shared/ui/NoteBox"; import { allTags, setUserTag, removeUserTag } from "../lib/tags"; const { View } = ReactNative; -const { FormSection, FormInput } = Forms; function AddTagForm() { const [userId, setUserId] = React.useState(""); @@ -21,13 +20,13 @@ function AddTagForm() { return ( - - + - + - + - + } diff --git a/plugins/fake-profile-themes-and-effects/src/ui/components/Builder.tsx b/plugins/fake-profile-themes-and-effects/src/ui/components/Builder.tsx index c57c5e3..d1b1f78 100644 --- a/plugins/fake-profile-themes-and-effects/src/ui/components/Builder.tsx +++ b/plugins/fake-profile-themes-and-effects/src/ui/components/Builder.tsx @@ -1,14 +1,15 @@ import { showToast } from "@vendetta/ui/toasts"; import { findByProps } from "@vendetta/metro"; import React, { useMemo, useState, useEffect } from "react"; -import { View, Text } from "react-native"; +import { View } from "react-native"; import { buildFPTE, hasFPTE, stripFPTE } from "@fpte/lib/fpte"; import { type ProfileEffectConfig, UserStore, UserProfileStore } from "@fpte/lib/stores"; import { useAccentColor, usePrimaryColor, useShowPreview } from "@fpte/patches/patchUseProfileTheme"; import { showColorPicker, showEffectPicker } from "@fpte/ui/actionSheets"; import { resolveSemanticColorSafe, useAvatarColors, useThemeContext } from "@fpte/ui/color"; import { BuilderButton, Button, StaticEffect } from "@fpte/ui/components"; -import { FormCardSection, FormSwitchRow } from "@fpte/ui/components/forms"; +import { FormSwitchRow } from "@fpte/ui/components/forms"; +import { TableRowGroup } from "@shared/ui/table"; const UserProfileActionCreators = findByProps("saveProfileChanges"); @@ -103,23 +104,7 @@ export function Builder({ guildId }: BuilderProps) { const applyButtonVisible = hasSelection || fpteActive; return ( - - FPTE Builder - - {fpteActive ? "Active" : "Inactive"} - - - } - cardStyle={{ backgroundColor: "transparent" }} - > + */} - + ); } \ No newline at end of file diff --git a/plugins/fake-profile-themes-and-effects/src/ui/components/forms.ts b/plugins/fake-profile-themes-and-effects/src/ui/components/forms.ts index ded3c69..e2ece5d 100644 --- a/plugins/fake-profile-themes-and-effects/src/ui/components/forms.ts +++ b/plugins/fake-profile-themes-and-effects/src/ui/components/forms.ts @@ -1,4 +1,4 @@ -import { Forms } from "@vendetta/ui/components"; +import { TableRow, TableRowGroup, TableSwitchRow } from "@shared/ui/table"; import type { ComponentType, ReactNode } from "react"; import type { ColorValue, PressableProps, ViewProps } from "react-native"; @@ -69,15 +69,6 @@ export interface FormRowProps extends Pick, hasError?: boolean | undefined; } -export interface FormRadioProps { - selected?: boolean | undefined; -} - -export interface FormRadioRowProps extends FormRadioProps, FormRowProps { - align?: "left" | "right"; - value?: boolean | undefined; -} - export interface FormSwitchProps extends Pick { value?: boolean | undefined; onValueChange?: ((value: boolean) => void) | undefined; @@ -90,19 +81,8 @@ export interface FormSwitchRowProps extends Pick { - title?: ReactNode; - headerComponent?: ReactNode; - titleStyle?: TextProps["style"]; - cardStyle?: ViewProps["style"]; -} +export const FormSection: ComponentType = TableRowGroup; -export const FormSection: ComponentType = Forms.FormSection; +export const FormRow: ComponentType = TableRow; -export const FormRow: ComponentType = Forms.FormRow; - -export const FormRadioRow: ComponentType = Forms.FormRadioRow; - -export const FormSwitchRow: ComponentType = Forms.FormSwitchRow; - -export const FormCardSection: ComponentType = Forms.FormCardSection; +export const FormSwitchRow: ComponentType = TableSwitchRow; diff --git a/plugins/fake-profile-themes-and-effects/src/ui/pages/Settings.tsx b/plugins/fake-profile-themes-and-effects/src/ui/pages/Settings.tsx index 1b0c5ff..62f1bfb 100644 --- a/plugins/fake-profile-themes-and-effects/src/ui/pages/Settings.tsx +++ b/plugins/fake-profile-themes-and-effects/src/ui/pages/Settings.tsx @@ -3,25 +3,23 @@ import { useProxy } from "@vendetta/storage"; import React from "react"; import { ScrollView } from "react-native"; -import { FormRadioRow, FormRow, FormSection, FormSwitchRow } from "@fpte/ui/components/forms"; +import { FormSwitchRow } from "@fpte/ui/components/forms"; +import { TableRadioGroup, TableRadioRow, TableRowGroup } from "@shared/ui/table"; export function Settings() { useProxy(storage); return ( - - - { storage.prioritizeNitro = true; }} - /> - { storage.prioritizeNitro = false; }} - /> + + { storage.prioritizeNitro = value === "nitro"; }} + > + + + { storage.forceFallbackEffectPicker = value; }} /> - + ); } diff --git a/plugins/key-inspector/src/ui/Settings.tsx b/plugins/key-inspector/src/ui/Settings.tsx index 3c1791b..7d27c53 100644 --- a/plugins/key-inspector/src/ui/Settings.tsx +++ b/plugins/key-inspector/src/ui/Settings.tsx @@ -1,7 +1,7 @@ import { React, ReactNative, clipboard } from "@vendetta/metro/common"; import { findByProps, findByName, findByPropsAll, findByNameAll } from "@vendetta/metro"; import { showToast } from "@vendetta/ui/toasts"; -import { Forms } from "@vendetta/ui/components"; +import { TableRowGroup, TextInput } from "@shared/ui/table"; import SettingsScaffold from "@shared/ui/SettingsScaffold"; import NoteBox from "@shared/ui/NoteBox"; import PrimaryButton from "@shared/ui/PrimaryButton"; @@ -14,7 +14,6 @@ import { captureFluxEvents } from "../lib/fluxLogger"; import { captureComponentRenders } from "../lib/renderLogger"; const { View, Text } = ReactNative; -const { FormSection, FormInput } = Forms; // Every raw Text below used to have no explicit color at all - illegible black-on-black on // Discord's dark theme, since RN's Text defaults to black with no theming of its own. @@ -24,7 +23,7 @@ function FullScanSection() { const [summary, setSummary] = React.useState(null); return ( - + {summary} )} - + ); } @@ -63,10 +62,10 @@ function EvalSection() { }; return ( - + - - + ); } @@ -125,16 +124,16 @@ function FluxLoggerSection() { }; return ( - + - - - + ); } @@ -186,16 +185,16 @@ function RenderLoggerSection() { }; return ( - + - - - + + - + ); } @@ -242,10 +241,10 @@ function RawSearchSection() { }; return ( - + - - + nameQuery.trim() && run(`rawFindByName("${nameQuery.trim()}")`, () => rawFindByName(nameQuery.trim()))} /> - + typeQuery.trim() && run(`rawFindByTypeName("${typeQuery.trim()}")`, () => rawFindByTypeName(typeQuery.trim()))} /> - + - + ); } @@ -362,7 +361,7 @@ function FiberCaptureSection() { }; return ( - + {status} @@ -375,13 +374,13 @@ function FiberCaptureSection() { module-registry guessing) from window.__keyInspectorFiberRoot afterward. - + ); } function ApiTreeSection() { return ( - + - + ); } @@ -477,10 +476,10 @@ function ManualSearchSection() { }; return ( - + - - - + ); } diff --git a/plugins/message-snippets/src/ui/Settings.tsx b/plugins/message-snippets/src/ui/Settings.tsx index f1da953..c18fdb8 100644 --- a/plugins/message-snippets/src/ui/Settings.tsx +++ b/plugins/message-snippets/src/ui/Settings.tsx @@ -1,7 +1,7 @@ import { React, ReactNative } from "@vendetta/metro/common"; import { useProxy } from "@vendetta/storage"; import { storage } from "@vendetta/plugin"; -import { Forms } from "@vendetta/ui/components"; +import { TableRowGroup, TextInput } from "@shared/ui/table"; import { showToast } from "@vendetta/ui/toasts"; import SettingsScaffold from "@shared/ui/SettingsScaffold"; import ListSection from "@shared/ui/ListSection"; @@ -9,7 +9,6 @@ import PrimaryButton from "@shared/ui/PrimaryButton"; import { getSnippets, saveSnippet, deleteSnippet } from "../lib/snippets"; const { View } = ReactNative; -const { FormSection, FormInput } = Forms; function NewSnippetForm() { const [name, setName] = React.useState(""); @@ -18,8 +17,8 @@ function NewSnippetForm() { return ( - - + + - + - + - + This plugin doesn't have anything to configure here - there's no API key or toggle, because pronouns aren't Discord data. They come from PronounDB, a separate service @@ -27,7 +26,7 @@ export default function Settings() { Linking.openURL(PRONOUNDB_URL)} /> - + ); } diff --git a/plugins/radial-status/src/ui/Settings.tsx b/plugins/radial-status/src/ui/Settings.tsx index 8194679..337860a 100644 --- a/plugins/radial-status/src/ui/Settings.tsx +++ b/plugins/radial-status/src/ui/Settings.tsx @@ -1,13 +1,11 @@ import { React } from "@vendetta/metro/common"; import { useProxy } from "@vendetta/storage"; import { storage } from "@vendetta/plugin"; -import { Forms } from "@vendetta/ui/components"; +import { TableRowGroup, TextInput, TableSwitchRow } from "@shared/ui/table"; import SettingsScaffold from "@shared/ui/SettingsScaffold"; import ColorInput from "@shared/ui/ColorInput"; import NoteBox from "@shared/ui/NoteBox"; -const { FormSection, FormInput, FormSwitchRow } = Forms; - // The ring patch itself does a generic colors[status] lookup, not one hardcoded to three statuses - // offline just needed its own configurable entry here to work the same way the other three already // do. Discord doesn't show a colored dot for offline at all normally, so this is opt-in (blank by @@ -21,8 +19,8 @@ const STATUSES: { key: string; label: string; defaultColor?: string }[] = [ function numberInput(title: string, key: "ringThickness", placeholder: string) { return ( - { @@ -34,6 +32,7 @@ function numberInput(title: string, key: "ringThickness", placeholder: string) { ); } + export default function Settings() { useProxy(storage); storage.colors ??= {}; @@ -47,14 +46,14 @@ export default function Settings() { avatar sizes seen in each of those contexts, so other unrelated circular UI elements are left alone. - - + { storage.enabled = v; }} /> - - + + {STATUSES.map(({ key, label, defaultColor }) => ( ))} - - + + {numberInput("Ring thickness", "ringThickness", "2")} - + ); } diff --git a/plugins/role-color-everywhere/src/ui/Settings.tsx b/plugins/role-color-everywhere/src/ui/Settings.tsx index 92ca89c..6a3d7d9 100644 --- a/plugins/role-color-everywhere/src/ui/Settings.tsx +++ b/plugins/role-color-everywhere/src/ui/Settings.tsx @@ -1,50 +1,48 @@ import { React } from "@vendetta/metro/common"; import { storage } from "@vendetta/plugin"; import { useProxy } from "@vendetta/storage"; -import { Forms } from "@vendetta/ui/components"; +import { TableRowGroup, TableSwitchRow, TextInput } from "@shared/ui/table"; import SettingsScaffold from "@shared/ui/SettingsScaffold"; import NoteBox from "@shared/ui/NoteBox"; -const { FormSection, FormSwitchRow, FormInput } = Forms; - export default function Settings() { useProxy(storage); return ( - - + { storage.hideTyping = !v; }} /> - { storage.noMention = !v; }} /> - { storage.noRole = !v; }} /> - { storage.noVoice = !v; }} /> - 0} onValueChange={(v: boolean) => { storage.chatInterpolation = v ? 60 : 0; }} /> {storage.chatInterpolation > 0 && ( - { @@ -52,7 +50,7 @@ export default function Settings() { }} /> )} - + Some of these surfaces (especially the member list headers) rely on component internals Discord doesn't officially expose, so a future Discord update may silently turn one of diff --git a/plugins/rose-utils/src/modules/MessageLogger/ui/LogViewerPage.tsx b/plugins/rose-utils/src/modules/MessageLogger/ui/LogViewerPage.tsx index cf260d7..62257dd 100644 --- a/plugins/rose-utils/src/modules/MessageLogger/ui/LogViewerPage.tsx +++ b/plugins/rose-utils/src/modules/MessageLogger/ui/LogViewerPage.tsx @@ -1,6 +1,6 @@ import { findByName, findByProps } from "@vendetta/metro"; import { React, ReactNative, clipboard } from "@vendetta/metro/common"; -import { Forms } from "@vendetta/ui/components"; +import { TextInput } from "@shared/ui/table"; import { showToast } from "@vendetta/ui/toasts"; import { getAssetIDByName } from "@vendetta/ui/assets"; import { resolveSemanticColorSafe } from "@shared/lib/color"; @@ -17,7 +17,6 @@ const modalCloseButton = const Navigator = findByName("Navigator") ?? findByProps("Navigator")?.Navigator; const { View, Text, ScrollView, TouchableOpacity } = ReactNative; -const { FormInput } = Forms; const PAGE_SIZE = 100; @@ -172,8 +171,8 @@ function LogList() { return ( - - + ))} {errorEntries.length > 0 && ( - { const report = errorEntries.map(([label, stack]) => `[${module.label}] ${label}\n${stack}`).join("\n\n"); @@ -136,7 +135,7 @@ function ModuleSection({ module }: { module: AnyModule }) { }} /> )} - + ); } @@ -212,8 +211,8 @@ export default function Settings() { - - - + - { storage.showUnreadBadges = v; }} /> - { storage.showGuildNames = v; }} /> - { storage.hideDmTile = v; }} /> - - - + + { storage.autoCollapseFolders = v; }} /> - { storage.hideFolderIcons = v; }} /> - + The folder options above are ported from fres621's BetterFolders - its other half (hiding icons in the native left server rail) patched a component that no longer diff --git a/plugins/staff-tags/src/ui/pages/Settings.tsx b/plugins/staff-tags/src/ui/pages/Settings.tsx index 99e72df..54cfad8 100644 --- a/plugins/staff-tags/src/ui/pages/Settings.tsx +++ b/plugins/staff-tags/src/ui/pages/Settings.tsx @@ -1,12 +1,11 @@ import { React } from "@vendetta/metro/common"; import { storage } from "@vendetta/plugin"; import { useProxy } from "@vendetta/storage"; -import { Forms } from "@vendetta/ui/components"; +import { TableRowGroup, TableSwitchRow, TextInput } from "@shared/ui/table"; import SettingsScaffold from "@shared/ui/SettingsScaffold"; import ColorInput from "@shared/ui/ColorInput"; import { TAG_DEFINITIONS, tagSettings } from "../../lib/getTag"; -const { FormSection, FormSwitchRow, FormInput } = Forms; function TagSettingsSection({ id, defaultText, defaultColor }: { id: string; defaultText: string; defaultColor: string }) { const settings = tagSettings(id); @@ -15,20 +14,20 @@ function TagSettingsSection({ id, defaultText, defaultColor }: { id: string; def const enabled = settings.enabled !== false; return ( - - + { settings.enabled = v; }} /> - { settings.text = v; }} /> - { settings.color = v; }} /> )} - { settings.gradientColor = v; }} /> )} - + ); } @@ -66,14 +65,14 @@ export default function Settings() { return ( - - + { storage.useRoleColor = v; }} /> - + {TAG_DEFINITIONS.map((def) => ( ))} diff --git a/plugins/view-raw/src/ui/Settings.tsx b/plugins/view-raw/src/ui/Settings.tsx index dd452bc..71c5082 100644 --- a/plugins/view-raw/src/ui/Settings.tsx +++ b/plugins/view-raw/src/ui/Settings.tsx @@ -1,5 +1,5 @@ import { React } from "@vendetta/metro/common"; -import { Forms } from "@vendetta/ui/components"; +import { TableRowGroup, TableRow } from "@shared/ui/table"; import { showToast } from "@vendetta/ui/toasts"; import { clipboard } from "@vendetta/metro/common"; import SettingsScaffold from "@shared/ui/SettingsScaffold"; @@ -7,8 +7,6 @@ import NoteBox from "@shared/ui/NoteBox"; import PrimaryButton from "@shared/ui/PrimaryButton"; import { getLastDetection } from "../lib/diagnostics"; -const { FormSection, FormRow } = Forms; - const STRATEGY_LABEL: Record = { buttons: "Strategy 1 (button list) - working normally", actionSheetRow: "Strategy 2 (row group) - working normally", @@ -21,9 +19,9 @@ export default function Settings() { return ( - + {detection ? ( - @@ -41,7 +39,7 @@ export default function Settings() { showToast("Copied - paste this if you're reporting an issue", undefined); }} /> - + "View Raw" is added to the message long-press menu by detecting Discord's internal action-sheet layout, which isn't an official API and can change between app versions. diff --git a/plugins/you-bar-plus/src/ui/Settings.tsx b/plugins/you-bar-plus/src/ui/Settings.tsx index 3ec69d0..8f6ffa8 100644 --- a/plugins/you-bar-plus/src/ui/Settings.tsx +++ b/plugins/you-bar-plus/src/ui/Settings.tsx @@ -1,12 +1,11 @@ import { React } from "@vendetta/metro/common"; import { storage } from "@vendetta/plugin"; import { useProxy } from "@vendetta/storage"; -import { Forms } from "@vendetta/ui/components"; +import { TableRowGroup, TableSwitchRow } from "@shared/ui/table"; import SettingsScaffold from "@shared/ui/SettingsScaffold"; import NoteBox from "@shared/ui/NoteBox"; import { updateYouBar } from "../patches/youBarButtons"; -const { FormSection, FormSwitchRow } = Forms; export default function Settings() { useProxy(storage); @@ -21,8 +20,8 @@ export default function Settings() { original, this patches the YouBar the moment it loads - if you enable it while Discord's already running, you may need to restart once for the buttons to appear. - - + - - + ); } diff --git a/shared/ui/ListSection.tsx b/shared/ui/ListSection.tsx index 872984e..8c4a11c 100644 --- a/shared/ui/ListSection.tsx +++ b/shared/ui/ListSection.tsx @@ -1,9 +1,8 @@ import { React, ReactNative } from "@vendetta/metro/common"; -import { Forms } from "@vendetta/ui/components"; +import { TableRow, TableRowGroup } from "./table"; import NoteBox from "./NoteBox"; const { Text } = ReactNative; -const { FormSection, FormRow } = Forms; export interface ListItem { key: string; @@ -19,12 +18,12 @@ export interface ListItem { */ export default function ListSection({ title, items, emptyText }: { title: string; items: ListItem[]; emptyText: string }) { return ( - + {items.length === 0 ? ( {emptyText} ) : ( items.map((item) => ( - )) )} - + ); } diff --git a/shared/ui/table.ts b/shared/ui/table.ts new file mode 100644 index 0000000..ea61e75 --- /dev/null +++ b/shared/ui/table.ts @@ -0,0 +1,29 @@ +import { findByProps } from "@vendetta/metro"; +import { Forms } from "@vendetta/ui/components"; + +/** + * Discord's redesigned "Table" row family (TableRow/TableRowGroup/TableSwitchRow/...) replaced + * the old Forms.Form* components in the app itself a while ago - Forms.* still works (Vendetta's + * compat layer maps it to the legacy components, see metro/common/components.ts in the loader), + * but it's the deprecated skin. These are found the same way ViewRaw finds ActionSheetRow: a + * direct findByProps lookup for the real component, since the vendetta.ui.components compat + * object never exposed the Table family at all. Falls back to the matching legacy Form + * component if a lookup ever comes back empty, same "isolate the failure" approach as the rest + * of this repo - the fallback's prop names don't perfectly match (e.g. `icon` vs `leading`), so a + * few things would render without an icon/arrow in that fallback path, but nothing crashes. + */ +const find = (prop: string): any => findByProps(prop)?.[prop]; + +const RealTableRow: any = find("TableRow"); + +export const TableRow: any = RealTableRow ?? Forms.FormRow; +export const TableRowGroup: any = find("TableRowGroup") ?? Forms.FormSection; +export const TableSwitchRow: any = find("TableSwitchRow") ?? Forms.FormSwitchRow; +export const TableRadioRow: any = find("TableRadioRow") ?? Forms.FormRadioRow; +export const TableRadioGroup: any = find("TableRadioGroup") ?? Forms.FormRadioGroup; +export const TableCheckboxRow: any = find("TableCheckboxRow") ?? Forms.FormCheckboxRow; +export const TextInput: any = find("TextInput") ?? Forms.FormInput; + +export const TableRowIcon: any = RealTableRow?.Icon ?? Forms.FormRow.Icon; +export const TableRowArrow: any = RealTableRow?.Arrow ?? Forms.FormRow.Arrow; +export const TableRowTrailingText: any = RealTableRow?.TrailingText;