diff --git a/src/core/ui/components/AddonPage.tsx b/src/core/ui/components/AddonPage.tsx index 7122e39..84956ec 100644 --- a/src/core/ui/components/AddonPage.tsx +++ b/src/core/ui/components/AddonPage.tsx @@ -1,28 +1,28 @@ import { CardWrapper } from "@core/ui/components/AddonCard"; -import { useProxy } from "@core/vendetta/storage"; import { findAssetId } from "@lib/api/assets"; import { settings } from "@lib/api/settings"; -import AlertModal, { AlertActionButton } from "@lib/ui/components/wrappers/AlertModal"; +import { dismissAlert, openAlert } from "@lib/ui/alerts"; +import { showSheet } from "@lib/ui/sheets"; import isValidHttpUrl from "@lib/utils/isValidHttpUrl"; import { lazyDestructure } from "@lib/utils/lazy"; import { findByProps } from "@metro"; -import { clipboard } from "@metro/common"; -import { Button, FlashList, FloatingActionButton, HelpMessage, IconButton, Stack, Text, TextInput } from "@metro/common/components"; +import { clipboard, NavigationNative } from "@metro/common"; +import { AlertActionButton, AlertModal, Button, FlashList, FloatingActionButton, HelpMessage, IconButton, Stack, Text, TextInput } from "@metro/common/components"; import { ErrorBoundary, Search } from "@ui/components"; +import { isNotNil } from "es-toolkit"; import fuzzysort from "fuzzysort"; -import { ComponentType, ReactNode, useCallback, useMemo } from "react"; +import { ComponentType, ReactNode, useCallback, useEffect, useMemo } from "react"; import { Image, ScrollView, View } from "react-native"; const { showSimpleActionSheet, hideActionSheet } = lazyDestructure(() => findByProps("showSimpleActionSheet")); -const { openAlert, dismissAlert } = lazyDestructure(() => findByProps("openAlert", "dismissAlert")); -type SearchKeywords = Array string)>; +type SearchKeywords = Array string)>; interface AddonPageProps { title: string; items: I[]; - searchKeywords: SearchKeywords; - sortOptions?: Record number>; + searchKeywords: SearchKeywords; + sortOptions?: Record number>; resolveItem?: (value: I) => T | undefined; safeModeHint?: { message?: string; @@ -34,6 +34,9 @@ interface AddonPageProps { fetchFn?: (url: string) => Promise; onPress?: () => void; }; + + OptionsActionSheetComponent?: ComponentType; + CardComponent: ComponentType>; ListHeaderComponent?: ComponentType; ListFooterComponent?: ComponentType; @@ -55,7 +58,7 @@ function InputAlert(props: { label: string, fetchFn: (url: string) => Promise Promise({ CardComponent, ...props }: AddonPageProps) { - useProxy(settings); - const [search, setSearch] = React.useState(""); - const [sortFn, setSortFn] = React.useState<((a: unknown, b: unknown) => number) | null>(() => null); + const [sortFn, setSortFn] = React.useState<((a: T, b: T) => number) | null>(() => null); + const navigation = NavigationNative.useNavigation(); + + useEffect(() => { + if (props.OptionsActionSheetComponent) { + navigation.setOptions({ + headerRight: () => showSheet("AddonMoreSheet", props.OptionsActionSheetComponent!)} + /> + }); + } + }, [navigation]); const results = useMemo(() => { let values = props.items; - if (props.resolveItem) values = values.map(props.resolveItem); - const items = values.filter(i => i && typeof i === "object"); + if (props.resolveItem) values = values.map(props.resolveItem).filter(isNotNil); + const items = values.filter(i => isNotNil(i) && typeof i === "object"); if (!search && sortFn) items.sort(sortFn); return fuzzysort.go(search, items, { keys: props.searchKeywords, all: true }); @@ -134,7 +149,7 @@ export default function AddonPage({ CardComponent, ...props }: if (results.length === 0 && !search) { return - + Oops! Nothing to see here… yet! @@ -175,7 +190,7 @@ export default function AddonPage({ CardComponent, ...props }: })} />} - {props.ListHeaderComponent && !search && } + {props.ListHeaderComponent && } ); @@ -187,12 +202,12 @@ export default function AddonPage({ CardComponent, ...props }: estimatedItemSize={136} ListHeaderComponent={headerElement} ListEmptyComponent={() => - + Hmmm... could not find that! } - contentContainerStyle={{ padding: 8, paddingHorizontal: 12 }} + contentContainerStyle={{ padding: 8, paddingHorizontal: 12, paddingBottom: 90 }} ItemSeparatorComponent={() => } ListFooterComponent={props.ListFooterComponent} renderItem={({ item }: any) => } diff --git a/src/core/ui/settings/pages/Themes/index.tsx b/src/core/ui/settings/pages/Themes/index.tsx index 8bdb1cf..5595edb 100644 --- a/src/core/ui/settings/pages/Themes/index.tsx +++ b/src/core/ui/settings/pages/Themes/index.tsx @@ -2,26 +2,31 @@ import { formatString, Strings } from "@core/i18n"; import AddonPage from "@core/ui/components/AddonPage"; import ThemeCard from "@core/ui/settings/pages/Themes/ThemeCard"; import { useProxy } from "@core/vendetta/storage"; -import { installTheme, Theme, themes } from "@lib/addons/themes"; +import { getCurrentTheme, installTheme, themes, VdThemeInfo } from "@lib/addons/themes"; +import { colorsPref } from "@lib/addons/themes/colors/preferences"; +import { updateBunnyColor } from "@lib/addons/themes/colors/updater"; import { Author } from "@lib/addons/types"; +import { findAssetId } from "@lib/api/assets"; import { settings } from "@lib/api/settings"; -import { Button } from "@metro/common/components"; +import { useObservable } from "@lib/api/storage"; +import { ActionSheet, BottomSheetTitleHeader, Button, TableRadioGroup, TableRadioRow, TableRowIcon } from "@metro/common/components"; +import { View } from "react-native"; export default function Themes() { useProxy(settings); useProxy(themes); return ( - + title={Strings.THEMES} searchKeywords={[ "data.name", "data.description", - p => p.data.authors?.map((a: Author) => a.name).join(", ") + p => p.data.authors?.map((a: Author) => a.name).join(", ") ?? "" ]} sortOptions={{ - "Name (A-Z)": (a, b) => a.name.localeCompare(b.name), - "Name (Z-A)": (a, b) => b.name.localeCompare(a.name) + "Name (A-Z)": (a, b) => a.data.name.localeCompare(b.data.name), + "Name (Z-A)": (a, b) => b.data.name.localeCompare(a.data.name) }} installAction={{ label: "Install a theme", @@ -38,6 +43,39 @@ export default function Themes() { /> }} CardComponent={ThemeCard} + OptionsActionSheetComponent={() => { + useObservable([colorsPref]); + + return + + + { + colorsPref.type = type !== "auto" ? type as "dark" | "light" : undefined; + getCurrentTheme()?.data && updateBunnyColor(getCurrentTheme()!.data!, { update: true }); + }} + > + } label="Auto" value="auto" /> + } label="Dark" value="dark" /> + } label="Light" value="light" /> + + { + colorsPref.customBackground = type !== "shown" ? type as "hidden" : null; + }} + > + } label="Show" value={"shown"} /> + } label="Hide" value={"hidden"} /> + + + ; + }} /> ); } diff --git a/src/lib/addons/themes/index.ts b/src/lib/addons/themes/index.ts index dcae18d..e898926 100644 --- a/src/lib/addons/themes/index.ts +++ b/src/lib/addons/themes/index.ts @@ -1,6 +1,7 @@ import { awaitStorage, createFileBackend, createMMKVBackend, createStorage, wrapSync } from "@core/vendetta/storage"; import { writeFile } from "@lib/api/native/fs"; import { getStoredTheme, getThemeFilePath, isPyonLoader, isThemeSupported } from "@lib/api/native/loader"; +import { awaitStorage as newAwaitStorage } from "@lib/api/storage"; import { safeFetch } from "@lib/utils"; import { Platform } from "react-native"; @@ -133,7 +134,7 @@ export async function initThemes() { writeFile("../vendetta_theme.json", "null"); } - await awaitStorage(colorsPref); + await newAwaitStorage(colorsPref); const currentTheme = getThemeFromLoader(); initColors(currentTheme?.data ?? null); diff --git a/src/lib/ui/alerts.ts b/src/lib/ui/alerts.ts new file mode 100644 index 0000000..f80fe38 --- /dev/null +++ b/src/lib/ui/alerts.ts @@ -0,0 +1,4 @@ +import { lazyDestructure } from "@lib/utils/lazy"; +import { findByProps } from "@metro"; + +export const { openAlert, dismissAlert } = lazyDestructure(() => findByProps("openAlert", "dismissAlert")); diff --git a/src/lib/ui/index.ts b/src/lib/ui/index.ts index f4c027e..6417d63 100644 --- a/src/lib/ui/index.ts +++ b/src/lib/ui/index.ts @@ -1,4 +1,5 @@ +export * as alerts from "./alerts"; export * as components from "./components"; export * as settings from "./settings"; export * as sheets from "./sheets"; diff --git a/src/lib/ui/types.ts b/src/lib/ui/types.ts index 33dc3b9..0daa132 100644 --- a/src/lib/ui/types.ts +++ b/src/lib/ui/types.ts @@ -1,3 +1,4 @@ import { LiteralUnion } from "type-fest"; -export type DiscordTextStyles = LiteralUnion<"heading-sm/normal" | "heading-sm/medium" | "heading-sm/semibold" | "heading-sm/bold" | "heading-sm/extrabold" | "heading-md/normal" | "heading-md/medium" | "heading-md/semibold" | "heading-md/bold" | "heading-md/extrabold" | "heading-lg/normal" | "heading-lg/medium" | "heading-lg/semibold" | "heading-lg/bold" | "heading-lg/extrabold" | "heading-xl/normal" | "heading-xl/medium" | "heading-xl/semibold" | "heading-xl/bold" | "heading-xl/extrabold" | "heading-xxl/normal" | "heading-xxl/medium" | "heading-xxl/semibold" | "heading-xxl/bold" | "heading-xxl/extrabold" | "eyebrow" | "heading-deprecated-12/normal" | "heading-deprecated-12/medium" | "heading-deprecated-12/semibold" | "heading-deprecated-12/bold" | "heading-deprecated-12/extrabold" | "redesign/heading-18/bold" | "text-xxs/normal" | "text-xxs/medium" | "text-xxs/semibold" | "text-xxs/bold" | "text-xs/normal" | "text-xs/medium" | "text-xs/semibold" | "text-xs/bold" | "text-sm/normal" | "text-sm/medium" | "text-sm/semibold" | "text-sm/bold" | "text-md/normal" | "text-md/medium" | "text-md/semibold" | "text-md/bold" | "text-lg/normal" | "text-lg/medium" | "text-lg/semibold" | "text-lg/bold" | "redesign/message-preview/normal" | "redesign/message-preview/medium" | "redesign/message-preview/semibold" | "redesign/message-preview/bold" | "redesign/channel-title/normal" | "redesign/channel-title/medium" | "redesign/channel-title/semibold" | "redesign/channel-title/bold" | "display-sm" | "display-md" | "display-lg", string>; +export type TextStyles = LiteralUnion<"heading-sm/normal" | "heading-sm/medium" | "heading-sm/semibold" | "heading-sm/bold" | "heading-sm/extrabold" | "heading-md/normal" | "heading-md/medium" | "heading-md/semibold" | "heading-md/bold" | "heading-md/extrabold" | "heading-lg/normal" | "heading-lg/medium" | "heading-lg/semibold" | "heading-lg/bold" | "heading-lg/extrabold" | "heading-xl/normal" | "heading-xl/medium" | "heading-xl/semibold" | "heading-xl/bold" | "heading-xl/extrabold" | "heading-xxl/normal" | "heading-xxl/medium" | "heading-xxl/semibold" | "heading-xxl/bold" | "heading-xxl/extrabold" | "eyebrow" | "heading-deprecated-12/normal" | "heading-deprecated-12/medium" | "heading-deprecated-12/semibold" | "heading-deprecated-12/bold" | "heading-deprecated-12/extrabold" | "redesign/heading-18/bold" | "text-xxs/normal" | "text-xxs/medium" | "text-xxs/semibold" | "text-xxs/bold" | "text-xs/normal" | "text-xs/medium" | "text-xs/semibold" | "text-xs/bold" | "text-sm/normal" | "text-sm/medium" | "text-sm/semibold" | "text-sm/bold" | "text-md/normal" | "text-md/medium" | "text-md/semibold" | "text-md/bold" | "text-lg/normal" | "text-lg/medium" | "text-lg/semibold" | "text-lg/bold" | "redesign/message-preview/normal" | "redesign/message-preview/medium" | "redesign/message-preview/semibold" | "redesign/message-preview/bold" | "redesign/channel-title/normal" | "redesign/channel-title/medium" | "redesign/channel-title/semibold" | "redesign/channel-title/bold" | "display-sm" | "display-md" | "display-lg", string>; +export type ThemeColors = LiteralUnion<"action-sheet-gradient-bg" | "activity-card-background" | "activity-card-icon-overlay" | "alert-bg" | "android-navigation-bar-background" | "android-navigation-scrim-background" | "android-ripple" | "autocomplete-bg" | "background-accent" | "background-floating" | "background-mentioned" | "background-mentioned-hover" | "background-message-automod" | "background-message-automod-hover" | "background-message-highlight" | "background-message-highlight-hover" | "background-message-hover" | "background-mobile-primary" | "background-mobile-secondary" | "background-modifier-accent" | "background-modifier-accent-2" | "background-modifier-active" | "background-modifier-hover" | "background-modifier-selected" | "background-nested-floating" | "background-primary" | "background-secondary" | "background-secondary-alt" | "background-tertiary" | "badge-brand-bg" | "badge-brand-text" | "bg-backdrop" | "bg-backdrop-no-opacity" | "bg-base-primary" | "bg-base-secondary" | "bg-base-tertiary" | "bg-brand" | "bg-mod-faint" | "bg-mod-strong" | "bg-mod-subtle" | "bg-surface-overlay" | "bg-surface-overlay-tmp" | "bg-surface-raised" | "black" | "blur-fallback" | "blur-fallback-pressed" | "border-faint" | "border-strong" | "border-subtle" | "bug-reporter-modal-submitting-background" | "button-creator-revenue-background" | "button-danger-background" | "button-danger-background-active" | "button-danger-background-disabled" | "button-danger-background-hover" | "button-outline-brand-background" | "button-outline-brand-background-active" | "button-outline-brand-background-hover" | "button-outline-brand-border" | "button-outline-brand-border-active" | "button-outline-brand-border-hover" | "button-outline-brand-text" | "button-outline-brand-text-active" | "button-outline-brand-text-hover" | "button-outline-danger-background" | "button-outline-danger-background-active" | "button-outline-danger-background-hover" | "button-outline-danger-border" | "button-outline-danger-border-active" | "button-outline-danger-border-hover" | "button-outline-danger-text" | "button-outline-danger-text-active" | "button-outline-danger-text-hover" | "button-outline-positive-background" | "button-outline-positive-background-active" | "button-outline-positive-background-hover" | "button-outline-positive-border" | "button-outline-positive-border-active" | "button-outline-positive-border-hover" | "button-outline-positive-text" | "button-outline-positive-text-active" | "button-outline-positive-text-hover" | "button-outline-primary-background" | "button-outline-primary-background-active" | "button-outline-primary-background-hover" | "button-outline-primary-border" | "button-outline-primary-border-active" | "button-outline-primary-border-hover" | "button-outline-primary-text" | "button-outline-primary-text-active" | "button-outline-primary-text-hover" | "button-positive-background" | "button-positive-background-active" | "button-positive-background-disabled" | "button-positive-background-hover" | "button-secondary-background" | "button-secondary-background-active" | "button-secondary-background-disabled" | "button-secondary-background-hover" | "card-gradient-bg" | "card-gradient-pressed-bg" | "card-primary-bg" | "card-primary-pressed-bg" | "card-secondary-bg" | "card-secondary-pressed-bg" | "channel-icon" | "channel-text-area-placeholder" | "channels-default" | "channeltextarea-background" | "chat-background" | "chat-banner-bg" | "chat-border" | "chat-input-container-background" | "chat-swipe-to-reply-background" | "chat-swipe-to-reply-gradient-background" | "coachmark-bg" | "content-inventory-media-seekbar-container" | "content-inventory-overlay-text-primary" | "content-inventory-overlay-text-secondary" | "content-inventory-overlay-ui-mod" | "content-inventory-overlay-ui-mod-bg" | "context-menu-backdrop-background" | "control-brand-foreground" | "control-brand-foreground-new" | "creator-revenue-icon-gradient-end" | "creator-revenue-icon-gradient-start" | "creator-revenue-info-box-background" | "creator-revenue-info-box-border" | "creator-revenue-locked-channel-icon" | "creator-revenue-progress-bar" | "deprecated-card-bg" | "deprecated-card-editable-bg" | "deprecated-quickswitcher-input-background" | "deprecated-quickswitcher-input-placeholder" | "deprecated-store-bg" | "deprecated-text-input-bg" | "deprecated-text-input-border" | "deprecated-text-input-border-disabled" | "deprecated-text-input-border-hover" | "deprecated-text-input-prefix" | "display-banner-overflow-background" | "divider-strong" | "divider-subtle" | "embed-background" | "embed-background-alternate" | "embed-title" | "expression-picker-bg" | "focus-primary" | "forum-post-extra-media-count-container-background" | "forum-post-tag-background" | "guild-icon-inactive-bg" | "guild-icon-inactive-nested-bg" | "guild-notifications-bottom-sheet-pill-background" | "halo-positive" | "header-muted" | "header-primary" | "header-secondary" | "home-background" | "home-card-resting-border" | "icon-muted" | "icon-primary" | "icon-secondary" | "icon-transparent" | "info-box-background" | "info-danger-background" | "info-danger-foreground" | "info-danger-text" | "info-help-background" | "info-help-foreground" | "info-help-text" | "info-positive-background" | "info-positive-foreground" | "info-positive-text" | "info-warning-background" | "info-warning-foreground" | "info-warning-text" | "input-background" | "input-focused-border" | "input-placeholder-text" | "interactive-active" | "interactive-hover" | "interactive-muted" | "interactive-normal" | "legacy-android-blur-overlay-default" | "legacy-android-blur-overlay-ultra-thin" | "legacy-blur-fallback-default" | "legacy-blur-fallback-ultra-thin" | "live-stage-tile-border" | "logo-primary" | "mention-background" | "mention-foreground" | "menu-item-danger-active-bg" | "menu-item-danger-hover-bg" | "menu-item-default-active-bg" | "menu-item-default-hover-bg" | "modal-background" | "modal-footer-background" | "navigator-header-tint" | "panel-bg" | "polls-normal-fill-hover" | "polls-normal-image-background" | "polls-victor-fill" | "polls-voted-fill" | "premium-nitro-pink-text" | "profile-gradient-card-background" | "profile-gradient-message-input-border" | "profile-gradient-note-background" | "profile-gradient-overlay" | "profile-gradient-overlay-synced-with-user-theme" | "profile-gradient-profile-body-background-hover" | "profile-gradient-role-pill-background" | "profile-gradient-role-pill-border" | "profile-gradient-section-box" | "redesign-activity-card-background" | "redesign-activity-card-background-pressed" | "redesign-activity-card-badge-icon" | "redesign-activity-card-border" | "redesign-activity-card-overflow-background" | "redesign-button-active-background" | "redesign-button-active-pressed-background" | "redesign-button-active-text" | "redesign-button-danger-background" | "redesign-button-danger-pressed-background" | "redesign-button-danger-text" | "redesign-button-destructive-background" | "redesign-button-destructive-pressed-background" | "redesign-button-destructive-text" | "redesign-button-overlay-alpha-background" | "redesign-button-overlay-alpha-pressed-background" | "redesign-button-overlay-alpha-text" | "redesign-button-overlay-background" | "redesign-button-overlay-pressed-background" | "redesign-button-overlay-text" | "redesign-button-positive-background" | "redesign-button-positive-pressed-background" | "redesign-button-positive-text" | "redesign-button-primary-alt-background" | "redesign-button-primary-alt-border" | "redesign-button-primary-alt-on-blurple-background" | "redesign-button-primary-alt-on-blurple-border" | "redesign-button-primary-alt-on-blurple-pressed-background" | "redesign-button-primary-alt-on-blurple-pressed-border" | "redesign-button-primary-alt-on-blurple-text" | "redesign-button-primary-alt-pressed-background" | "redesign-button-primary-alt-pressed-border" | "redesign-button-primary-alt-pressed-text" | "redesign-button-primary-alt-text" | "redesign-button-primary-background" | "redesign-button-primary-on-blurple-pressed-text" | "redesign-button-primary-overlay-background" | "redesign-button-primary-overlay-pressed-background" | "redesign-button-primary-overlay-text" | "redesign-button-primary-pressed-background" | "redesign-button-primary-text" | "redesign-button-secondary-background" | "redesign-button-secondary-border" | "redesign-button-secondary-overlay-background" | "redesign-button-secondary-overlay-pressed-background" | "redesign-button-secondary-overlay-text" | "redesign-button-secondary-pressed-background" | "redesign-button-secondary-pressed-border" | "redesign-button-secondary-text" | "redesign-button-selected-background" | "redesign-button-selected-pressed-background" | "redesign-button-selected-text" | "redesign-button-tertiary-background" | "redesign-button-tertiary-pressed-background" | "redesign-button-tertiary-pressed-text" | "redesign-button-tertiary-text" | "redesign-channel-category-name-text" | "redesign-channel-message-preview-text" | "redesign-channel-name-muted-text" | "redesign-channel-name-text" | "redesign-chat-input-background" | "redesign-image-button-pressed-background" | "redesign-input-control-active-bg" | "redesign-input-control-selected" | "redesign-only-background-active" | "redesign-only-background-default" | "redesign-only-background-overlay" | "redesign-only-background-raised" | "redesign-only-background-sunken" | "scrollbar-auto-scrollbar-color-thumb" | "scrollbar-auto-scrollbar-color-track" | "scrollbar-auto-thumb" | "scrollbar-auto-track" | "scrollbar-thin-thumb" | "scrollbar-thin-track" | "spoiler-hidden-background" | "spoiler-revealed-background" | "stage-card-pill-bg" | "status-danger" | "status-danger-background" | "status-danger-text" | "status-dnd" | "status-idle" | "status-offline" | "status-online" | "status-positive" | "status-positive-background" | "status-positive-text" | "status-speaking" | "status-warning" | "status-warning-background" | "status-warning-text" | "text-brand" | "text-danger" | "text-link" | "text-link-low-saturation" | "text-low-contrast" | "text-message-preview-low-sat" | "text-muted" | "text-muted-on-default" | "text-normal" | "text-positive" | "text-primary" | "text-secondary" | "text-warning" | "textbox-markdown-syntax" | "theme-locked-blur-fallback" | "thread-channel-spine" | "toast-bg" | "typing-indicator-bg" | "user-profile-header-overflow-background" | "voice-video-video-tile-background" | "voice-video-video-tile-blur-fallback" | "white" | "you-bar-bg", string>; diff --git a/src/metro/common/components.ts b/src/metro/common/components.ts index 49e69c6..514ffb9 100644 --- a/src/metro/common/components.ts +++ b/src/metro/common/components.ts @@ -12,7 +12,7 @@ const bySingularProp = createFilterDefinition<[string]>( ); const findSingular = (prop: string) => proxyLazy(() => findExports(bySingularProp(prop))?.[prop]); -const findProp = (prop: string) => proxyLazy(() => findByProps(prop)[prop]); +const findProp = (...props: string[]) => proxyLazy(() => findByProps(...props)[props[0]]); // Discord export const LegacyAlert = findByDisplayNameLazy("FluxContainer(Alert)"); @@ -27,18 +27,22 @@ export const ActionSheetRow = findProp("ActionSheetRow"); // Buttons export const Button = findSingular("Button") as t.Button; -export const TwinButtons = findProp("TwinButtons"); +export const TwinButtons = findProp("TwinButtons") as t.TwinButtons; export const IconButton = findSingular("IconButton") as t.IconButton; export const RowButton = findProp("RowButton") as t.RowButton; export const PressableScale = findProp("PressableScale"); // Tables -export const TableRow = findProp("TableRow"); -export const TableRowIcon = findProp("TableRowIcon"); -export const TableRowTrailingText = findProp("TableRowTrailingText"); -export const TableRowGroup = findProp("TableRowGroup"); -export const TableSwitchRow = findProp("TableSwitchRow"); +export const TableRow = findProp("TableRow") as t.TableRow; +export const TableRowIcon = findProp("TableRowIcon") as t.TableRowIcon; +export const TableRowTrailingText = findProp("TableRowTrailingText") as t.TableRowTrailingText; +export const TableRowGroup = findProp("TableRowGroup") as t.TableRowGroup; +export const TableRadioGroup = findProp("TableRadioGroup") as t.TableRadioGroup; +export const TableRadioRow = findProp("TableRadioRow") as t.TableRadioRow; +export const TableSwitchRow = findProp("TableSwitchRow") as t.TableSwitchRow; +export const TableCheckboxRow = findProp("TableCheckboxRow") as t.TableCheckboxRow; + export const TableSwitch = findSingular("FormSwitch"); export const TableRadio = findSingular("FormRadio"); export const TableCheckbox = findSingular("FormCheckbox"); @@ -51,11 +55,22 @@ export const FormCheckbox = findSingular("FormCheckbox"); export const Card = findProp("Card"); export const RedesignCompat = proxyLazy(() => findByProps("RedesignCompat").RedesignCompat); +// Alert +export const AlertModal = findProp("AlertModal"); +export const AlertActionButton = findProp("AlertActionButton"); +export const AlertActions = findProp("AlertActions"); + +// Pile +export const AvatarPile = findSingular("AvatarPile"); + // Misc. export const Stack = findProp("Stack") as t.Stack; +export const Avatar = findProp("default", "AvatarSizes", "getStatusSize"); + // Inputs export const TextInput = findSingular("TextInput") as t.TextInput; +export const TextArea = findSingular("TextArea"); // SegmentedControl export const SegmentedControl = findProp("SegmentedControl") as t.SegmentedControl; diff --git a/src/metro/common/types/components.ts b/src/metro/common/types/components.ts index 1a8bbde..b801625 100644 --- a/src/metro/common/types/components.ts +++ b/src/metro/common/types/components.ts @@ -1,5 +1,6 @@ -import { DiscordTextStyles } from "@ui/types"; -import { MutableRefObject, ReactNode, RefObject } from "react"; +import { TextStyles, ThemeColors } from "@lib/ui/types"; +import { Falsey } from "lodash"; +import { FC, MutableRefObject, PropsWithoutRef, ReactNode, RefObject } from "react"; import type * as RN from "react-native"; import { ImageSourcePropType, PressableProps } from "react-native"; import { SharedValue } from "react-native-reanimated"; @@ -24,11 +25,16 @@ interface ButtonProps { scaleAmountInPx?: number; icon?: ImageSourcePropType | ReactNode; style?: Style; - grow?: boolean; } export type Button = React.ForwardRefExoticComponent; +interface TwinButtonsProps { + children: ReactNode; +} + +export type TwinButtons = React.ComponentType; + // Segmented Control interface SegmentedControlItem { id: string; @@ -103,7 +109,7 @@ interface TextInputProps extends Omit; trailingPressableProps?: PressableProps; trailingText?: string; - value?: string | RN.Falsy; + value?: string | Falsey; } export type TextInput = React.FC; @@ -144,8 +150,8 @@ interface ActionSheetProps { export type ActionSheet = React.FC>; type TextProps = React.ComponentProps & { - variant?: DiscordTextStyles; - color?: string; // TODO: type this + variant?: TextStyles; + color?: ThemeColors; lineClamp?: number; maxFontSizeMultiplier?: number; style?: RN.TextStyle; @@ -163,3 +169,76 @@ interface IconButtonProps { } export type IconButton = React.FC; + +export type PressableScale = React.FC>; + +interface TableRowBaseProps { + arrow?: boolean; + label: string | ReactNode; + subLabel?: string | ReactNode; + variant?: LiteralUnion<"danger", string>; + icon?: JSX.Element | Falsey; + disabled?: boolean; + trailing?: ReactNode | React.ComponentType; +} + +interface TableRowProps extends TableRowBaseProps { + onPress?: () => void; +} + +export type TableRow = React.FC & { + Icon: TableRowIcon; + TrailingText: TableRowTrailingText; + Arrow: FC<{}>; +}; + +interface TableRowTrailingTextProps { + text: string; +} + +export type TableRowTrailingText = FC; + +interface TableRowIconProps { + style?: RN.ImageStyle; + variant?: LiteralUnion<"danger", string>, + source: ImageSourcePropType | undefined; +} + +export type TableRowIcon = React.FC; + +interface TableRowGroupProps { + title: string; + children: ReactNode; +} + +export type TableRowGroup = React.FC; + +interface TableRadioGroupProps { + title: string; + value: string; + hasIcons?: boolean; + onChange: (type: T) => void; + children: ReactNode; +} + +export type TableRadioGroup = FC; + +interface TableRadioRowProps extends TableRowBaseProps { + value: string; +} + +export type TableRadioRow = FC; + +interface TableSwitchRowProps extends TableRowBaseProps { + value: boolean; + onValueChange: (value: boolean) => void; +} + +export type TableSwitchRow = FC; + +interface TableCheckboxRowProps extends TableRowBaseProps { + checked: boolean; + onPress: () => void; +} + +export type TableCheckboxRow = FC;