Delete ui directory
This commit is contained in:
parent
f8047bb853
commit
21bdd39803
19 changed files with 0 additions and 771 deletions
|
|
@ -1,4 +0,0 @@
|
|||
import { lazyDestructure } from "@lib/utils/lazy";
|
||||
import { findByProps } from "@metro";
|
||||
|
||||
export const { openAlert, dismissAlert } = lazyDestructure(() => findByProps("openAlert", "dismissAlert"));
|
||||
30
ui/color.ts
30
ui/color.ts
|
|
@ -1,30 +0,0 @@
|
|||
import { constants } from "@metro/common";
|
||||
import { findByProps, findByStoreNameLazy } from "@metro/wrappers";
|
||||
|
||||
//! This module is only found on 165.0+, under the assumption that iOS 165.0 is the same as Android 165.0.
|
||||
//* In 167.1, most if not all traces of the old color modules were removed.
|
||||
//* In 168.6, Discord restructured EVERYTHING again. SemanticColor on this module no longer works when passed to a stylesheet. We must now use what you see below.
|
||||
//* In 173.10, Discord restructured a lot of the app. These changes included making the color module impossible to early-find.
|
||||
// ? To stop duplication, it is now exported in our theming code.
|
||||
// ? These comments are preserved for historical purposes.
|
||||
// const colorModule = findByPropsProxy("colors", "meta");
|
||||
|
||||
const color = findByProps("SemanticColor");
|
||||
|
||||
// ? SemanticColor and default.colors are effectively ThemeColorMap
|
||||
export const semanticColors = (color?.default?.colors ?? constants?.ThemeColorMap) as Record<string, any>;
|
||||
|
||||
// ? RawColor and default.unsafe_rawColors are effectively Colors
|
||||
//* Note that constants.Colors does still appear to exist on newer versions despite Discord not internally using it - what the fuck?
|
||||
export const rawColors = (color?.default?.unsafe_rawColors ?? constants?.Colors) as Record<string, string>;
|
||||
|
||||
const ThemeStore = findByStoreNameLazy("ThemeStore");
|
||||
const colorResolver = color.default.meta ??= color.default.internal;
|
||||
|
||||
export function isSemanticColor(sym: any): boolean {
|
||||
return colorResolver.isSemanticColor(sym);
|
||||
}
|
||||
|
||||
export function resolveSemanticColor(sym: any, theme = ThemeStore.theme): string {
|
||||
return colorResolver.resolveSemanticColor(theme, sym);
|
||||
}
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
import { constants } from "@metro/common";
|
||||
import { semanticColors } from "@ui/color";
|
||||
import { createStyles } from "@ui/styles";
|
||||
import { Platform, Text, TextInput } from "react-native";
|
||||
|
||||
export interface CodeblockProps {
|
||||
selectable?: boolean;
|
||||
style?: import("react-native").TextStyle;
|
||||
children?: string;
|
||||
}
|
||||
|
||||
const useStyles = createStyles({
|
||||
codeBlock: {
|
||||
fontFamily: constants.Fonts.CODE_NORMAL,
|
||||
fontSize: 12,
|
||||
textAlignVertical: "center",
|
||||
backgroundColor: semanticColors.BACKGROUND_SECONDARY,
|
||||
color: semanticColors.TEXT_NORMAL,
|
||||
borderWidth: 1,
|
||||
borderRadius: 12,
|
||||
borderColor: semanticColors.BACKGROUND_TERTIARY,
|
||||
padding: 10,
|
||||
},
|
||||
});
|
||||
|
||||
// iOS doesn't support the selectable property on RN.Text...
|
||||
const InputBasedCodeblock = ({ style, children }: CodeblockProps) => <TextInput editable={false} multiline style={[useStyles().codeBlock, style && style]} value={children} />;
|
||||
const TextBasedCodeblock = ({ selectable, style, children }: CodeblockProps) => <Text selectable={selectable} style={[useStyles().codeBlock, style && style]}>{children}</Text>;
|
||||
|
||||
export default function Codeblock({ selectable, style, children }: CodeblockProps) {
|
||||
if (!selectable) return <TextBasedCodeblock style={style} children={children} />;
|
||||
|
||||
return Platform.select({
|
||||
ios: <InputBasedCodeblock style={style} children={children} />,
|
||||
default: <TextBasedCodeblock style={style} children={children} selectable />,
|
||||
});
|
||||
}
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
import ErrorCard from "@core/ui/reporter/components/ErrorCard";
|
||||
import { React } from "@metro/common";
|
||||
import { ThemeContext } from "@ui/styles";
|
||||
import { Falsy } from "react-native";
|
||||
|
||||
type ErrorBoundaryState = {
|
||||
hasErr: false;
|
||||
} | {
|
||||
hasErr: true;
|
||||
error: Error;
|
||||
};
|
||||
|
||||
export interface ErrorBoundaryProps {
|
||||
children: JSX.Element | Falsy | (JSX.Element | Falsy)[];
|
||||
}
|
||||
|
||||
export default class ErrorBoundary extends React.Component<ErrorBoundaryProps, ErrorBoundaryState> {
|
||||
constructor(props: ErrorBoundaryProps) {
|
||||
super(props);
|
||||
this.state = { hasErr: false };
|
||||
}
|
||||
|
||||
static contextType = ThemeContext;
|
||||
static getDerivedStateFromError = (error: Error) => ({ hasErr: true, error });
|
||||
|
||||
render() {
|
||||
if (!this.state.hasErr) return this.props.children;
|
||||
|
||||
return (
|
||||
<ErrorCard
|
||||
error={this.state.error}
|
||||
onRetryRender={() => this.setState({ hasErr: false })}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,58 +0,0 @@
|
|||
import { LegacyAlert, LegacyFormInput } from "@metro/common/components";
|
||||
import { findByPropsLazy } from "@metro/wrappers";
|
||||
|
||||
const Alerts = findByPropsLazy("openLazy", "close");
|
||||
|
||||
export interface InputAlertProps {
|
||||
title?: string;
|
||||
confirmText?: string;
|
||||
confirmColor?: string;
|
||||
onConfirm: (input: string) => (void | Promise<void>);
|
||||
cancelText?: string;
|
||||
placeholder?: string;
|
||||
initialValue?: string;
|
||||
secureTextEntry?: boolean;
|
||||
}
|
||||
|
||||
export default function InputAlert({ title, confirmText, confirmColor, onConfirm, cancelText, placeholder, initialValue = "", secureTextEntry }: InputAlertProps) {
|
||||
const [value, setValue] = React.useState(initialValue);
|
||||
const [error, setError] = React.useState("");
|
||||
|
||||
function onConfirmWrapper() {
|
||||
const asyncOnConfirm = Promise.resolve(onConfirm(value));
|
||||
|
||||
asyncOnConfirm.then(() => {
|
||||
Alerts.close();
|
||||
}).catch((e: Error) => {
|
||||
setError(e.message);
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<LegacyAlert
|
||||
title={title}
|
||||
confirmText={confirmText}
|
||||
confirmColor={confirmColor}
|
||||
isConfirmButtonDisabled={error.length !== 0}
|
||||
onConfirm={onConfirmWrapper}
|
||||
cancelText={cancelText}
|
||||
onCancel={() => Alerts.close()}
|
||||
>
|
||||
<LegacyFormInput
|
||||
placeholder={placeholder}
|
||||
value={value}
|
||||
onChange={(v: string | { text: string; }) => {
|
||||
setValue(typeof v === "string" ? v : v.text);
|
||||
if (error) setError("");
|
||||
}}
|
||||
returnKeyType="done"
|
||||
onSubmitEditing={onConfirmWrapper}
|
||||
error={error || undefined}
|
||||
secureTextEntry={secureTextEntry}
|
||||
autoFocus={true}
|
||||
showBorder={true}
|
||||
style={{ alignSelf: "stretch" }}
|
||||
/>
|
||||
</LegacyAlert>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,58 +0,0 @@
|
|||
import { React } from "@metro/common";
|
||||
import { Strings } from "@core/i18n";
|
||||
import { findAssetId } from "@lib/api/assets";
|
||||
import { TextInput } from "@metro/common/components";
|
||||
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;
|
||||
}
|
||||
|
||||
function SearchIcon() {
|
||||
return (
|
||||
<Image
|
||||
style={{ width: 16, height: 16 }}
|
||||
source={findAssetId("icon-search")!}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default ({ onChangeText, placeholder, style, isRound }: SearchProps) => {
|
||||
const [query, setQuery] = React.useState("");
|
||||
|
||||
const onChange = (value: string) => {
|
||||
setQuery(value);
|
||||
onChangeText?.(value);
|
||||
};
|
||||
|
||||
// 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>
|
||||
);
|
||||
};
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
import { findAssetId } from "@lib/api/assets";
|
||||
import { LegacyFormRow, TableRow } from "@metro/common/components";
|
||||
import { LayoutAnimation, View } from "react-native";
|
||||
|
||||
export interface SummaryProps {
|
||||
label: string;
|
||||
icon?: string;
|
||||
noPadding?: boolean;
|
||||
noAnimation?: boolean;
|
||||
children: JSX.Element | JSX.Element[];
|
||||
}
|
||||
|
||||
export default function Summary({ label, icon, noPadding = false, noAnimation = false, children }: SummaryProps) {
|
||||
const [hidden, setHidden] = React.useState(true);
|
||||
|
||||
return (
|
||||
<>
|
||||
<TableRow
|
||||
label={label}
|
||||
icon={icon && <TableRow.Icon source={findAssetId(icon)} />}
|
||||
trailing={<LegacyFormRow.Arrow style={{ transform: [{ rotate: `${hidden ? 180 : 90}deg` }] }} />}
|
||||
onPress={() => {
|
||||
setHidden(!hidden);
|
||||
if (!noAnimation) LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
|
||||
}}
|
||||
/>
|
||||
{!hidden && <>
|
||||
<View style={!noPadding && { paddingHorizontal: 15 }}>{children}</View>
|
||||
</>}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
export * as wrappers from "./wrappers";
|
||||
export { default as Codeblock } from "@ui/components/Codeblock";
|
||||
export { default as ErrorBoundary } from "@ui/components/ErrorBoundary";
|
||||
export { default as Search } from "@ui/components/Search";
|
||||
export { default as Summary } from "@ui/components/Summary";
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
import { lazyDestructure } from "@lib/utils/lazy";
|
||||
import { findByFilePath, findByProps } from "@metro";
|
||||
import { Text } from "@metro/common/components";
|
||||
import { Button } from "@metro/common/types/components";
|
||||
import { ComponentProps, ComponentType, ReactNode } from "react";
|
||||
import { View } from "react-native";
|
||||
|
||||
type ActionButtonProps = Omit<ComponentProps<Button>, "onPress"> & {
|
||||
onPress?: () => void | Promise<unknown>;
|
||||
};
|
||||
|
||||
const {
|
||||
AlertModal: _AlertModal,
|
||||
AlertActionButton: _AlertActionButton
|
||||
} = lazyDestructure(() => findByProps("AlertModal", "AlertActions"));
|
||||
|
||||
export const AlertActionButton = _AlertActionButton as ComponentType<ActionButtonProps>;
|
||||
|
||||
export default function AlertModal(props: Record<string, unknown>) {
|
||||
const forwardFailedModal = findByFilePath("modules/forwarding/native/ForwardFailedAlertModal.tsx");
|
||||
|
||||
// ponyfill for extraContent
|
||||
if (!forwardFailedModal && "extraContent" in props) {
|
||||
props.content = (
|
||||
<View style={{ gap: 16 }}>
|
||||
<Text variant="text-md/medium" color="text-muted">
|
||||
{props.content as string}
|
||||
</Text>
|
||||
<View>
|
||||
{props.extraContent as ReactNode}
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
|
||||
delete props.extraContent;
|
||||
}
|
||||
|
||||
return <_AlertModal {...props} />;
|
||||
}
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
import AlertModal, { AlertActionButton } from "./AlertModal";
|
||||
|
||||
export { AlertActionButton, AlertModal };
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
|
||||
export * as alerts from "./alerts";
|
||||
export * as components from "./components";
|
||||
export * as settings from "./settings";
|
||||
export * as sheets from "./sheets";
|
||||
export * as styles from "./styles";
|
||||
export * as toasts from "./toasts";
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
// Good luck reading this!
|
||||
import { lazy } from "react";
|
||||
import type { ImageURISource } from "react-native";
|
||||
|
||||
import { patchTabsUI } from "./patches/tabs";
|
||||
|
||||
export interface RowConfig {
|
||||
key: string;
|
||||
title: () => string;
|
||||
onPress?: () => any;
|
||||
render?: Parameters<typeof lazy>[0];
|
||||
icon?: ImageURISource | number;
|
||||
IconComponent?: React.ReactNode;
|
||||
usePredicate?: () => boolean;
|
||||
useTrailing?: () => string | JSX.Element;
|
||||
rawTabsConfig?: Record<string, any>;
|
||||
}
|
||||
|
||||
export const registeredSections = {} as {
|
||||
[key: string]: RowConfig[];
|
||||
};
|
||||
|
||||
export function registerSection(section: { name: string; items: RowConfig[] }) {
|
||||
registeredSections[section.name] = section.items;
|
||||
return () => delete registeredSections[section.name];
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export function patchSettings() {
|
||||
const unpatches = new Array<() => boolean>();
|
||||
|
||||
patchTabsUI(unpatches);
|
||||
|
||||
return () => unpatches.forEach((u) => u());
|
||||
}
|
||||
|
|
@ -1,123 +0,0 @@
|
|||
import { after } from "@lib/api/patcher";
|
||||
import { findInReactTree } from "@lib/utils";
|
||||
import { i18n, NavigationNative } from "@metro/common";
|
||||
import {
|
||||
LegacyFormDivider,
|
||||
LegacyFormIcon,
|
||||
LegacyFormRow,
|
||||
LegacyFormSection,
|
||||
} from "@metro/common/components";
|
||||
import { findByNameLazy } from "@metro/wrappers";
|
||||
import { registeredSections } from "@ui/settings";
|
||||
|
||||
import { CustomPageRenderer, wrapOnPress } from "./shared";
|
||||
|
||||
function SettingsSection() {
|
||||
const navigation = NavigationNative.useNavigation();
|
||||
|
||||
return (
|
||||
<>
|
||||
{Object.keys(registeredSections).map(
|
||||
(sect) =>
|
||||
registeredSections[sect].length > 0 && (
|
||||
<LegacyFormSection key={sect} title={sect}>
|
||||
{/** Is usePredicate here safe? */}
|
||||
{registeredSections[sect]
|
||||
.filter((r) => r.usePredicate?.() ?? true)
|
||||
.map((row, i, arr) => (
|
||||
<>
|
||||
<LegacyFormRow
|
||||
label={row.title()}
|
||||
leading={<LegacyFormIcon source={row.icon} />}
|
||||
trailing={
|
||||
<LegacyFormRow.Arrow
|
||||
label={row.useTrailing?.() || undefined}
|
||||
/>
|
||||
}
|
||||
onPress={wrapOnPress(
|
||||
row.onPress,
|
||||
navigation,
|
||||
row.render,
|
||||
row.title(),
|
||||
)}
|
||||
/>
|
||||
{i !== arr.length - 1 && <LegacyFormDivider />}
|
||||
</>
|
||||
))}
|
||||
</LegacyFormSection>
|
||||
),
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export function patchPanelUI(unpatches: (() => void | boolean)[]) {
|
||||
try {
|
||||
unpatches.push(
|
||||
after("default", findByNameLazy("getScreens", false), (_a, screens) => ({
|
||||
...screens,
|
||||
VendettaCustomPage: {
|
||||
title: "ShiggyCord",
|
||||
render: () => <CustomPageRenderer />,
|
||||
},
|
||||
BUNNY_CUSTOM_PAGE: {
|
||||
title: "ShiggyCord",
|
||||
render: () => <CustomPageRenderer />,
|
||||
},
|
||||
SHIGGYCORD_CUSTOM_PAGE: {
|
||||
title: "ShiggyCord",
|
||||
render: () => <CustomPageRenderer />,
|
||||
},
|
||||
})),
|
||||
);
|
||||
|
||||
const unpatch = after(
|
||||
"default",
|
||||
findByNameLazy("UserSettingsOverviewWrapper", false),
|
||||
(_a, ret) => {
|
||||
const UserSettingsOverview = findInReactTree(
|
||||
ret.props.children,
|
||||
(n) => n.type?.name === "UserSettingsOverview",
|
||||
);
|
||||
|
||||
unpatches.push(
|
||||
after(
|
||||
"renderSupportAndAcknowledgements",
|
||||
UserSettingsOverview.type.prototype,
|
||||
(_args, { props: { children } }) => {
|
||||
const index = children.findIndex(
|
||||
(c: any) => c?.type?.name === "UploadLogsButton",
|
||||
);
|
||||
if (index !== -1) children.splice(index, 1);
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
unpatches.push(
|
||||
after("render", UserSettingsOverview.type.prototype, (_args, res) => {
|
||||
const titles = [
|
||||
i18n.Messages.BILLING_SETTINGS,
|
||||
i18n.Messages.PREMIUM_SETTINGS,
|
||||
];
|
||||
|
||||
const sections =
|
||||
findInReactTree(
|
||||
res.props.children,
|
||||
(n) => n?.children?.[1]?.type === LegacyFormSection,
|
||||
)?.children || res.props.children;
|
||||
|
||||
if (sections) {
|
||||
const index = sections.findIndex((c: any) =>
|
||||
titles.includes(c?.props.label),
|
||||
);
|
||||
sections.splice(-~index || 4, 0, <SettingsSection />);
|
||||
}
|
||||
}),
|
||||
);
|
||||
},
|
||||
true,
|
||||
);
|
||||
|
||||
unpatches.push(unpatch);
|
||||
} catch {}
|
||||
}
|
||||
|
|
@ -1,44 +0,0 @@
|
|||
import { NavigationNative } from "@metro/common";
|
||||
import { findByPropsLazy } from "@metro/wrappers";
|
||||
import { ErrorBoundary } from "@ui/components";
|
||||
import { RowConfig } from "@ui/settings";
|
||||
|
||||
const tabsNavigationRef = findByPropsLazy("getRootNavigationRef");
|
||||
|
||||
export const CustomPageRenderer = React.memo(() => {
|
||||
const navigation = NavigationNative.useNavigation();
|
||||
const route = NavigationNative.useRoute();
|
||||
|
||||
const { render: PageComponent, ...args } = route.params;
|
||||
|
||||
React.useEffect(() => void navigation.setOptions({ ...args }), []);
|
||||
return (
|
||||
<ErrorBoundary>
|
||||
<PageComponent />
|
||||
</ErrorBoundary>
|
||||
);
|
||||
});
|
||||
|
||||
export function wrapOnPress(
|
||||
onPress: (() => unknown) | undefined,
|
||||
navigation?: any,
|
||||
renderPromise?: RowConfig["render"],
|
||||
screenOptions?: string | Record<string, any>,
|
||||
props?: any,
|
||||
) {
|
||||
return async () => {
|
||||
if (onPress) return void onPress();
|
||||
|
||||
const Component = await renderPromise!!().then((m) => m.default);
|
||||
|
||||
if (typeof screenOptions === "string") {
|
||||
screenOptions = { title: screenOptions };
|
||||
}
|
||||
|
||||
navigation ??= tabsNavigationRef.getRootNavigationRef();
|
||||
navigation.navigate("SHIGGYCORD_CUSTOM_PAGE", {
|
||||
...screenOptions,
|
||||
render: () => <Component {...props} />,
|
||||
});
|
||||
};
|
||||
}
|
||||
|
|
@ -1,141 +0,0 @@
|
|||
import { after } from "@lib/api/patcher";
|
||||
import { TableRow } from "@metro/common/components";
|
||||
import { findByNameLazy, findByPropsLazy } from "@metro/wrappers";
|
||||
import { registeredSections } from "@ui/settings";
|
||||
import { Strings } from "@core/i18n";
|
||||
|
||||
import { CustomPageRenderer, wrapOnPress } from "./shared";
|
||||
import { findInReactTree } from "@lib/utils";
|
||||
|
||||
const settingConstants = findByPropsLazy("SETTING_RENDERER_CONFIG");
|
||||
const createListModule = findByPropsLazy("createList");
|
||||
const SettingsOverviewScreen = findByNameLazy("SettingsOverviewScreen", false);
|
||||
|
||||
function useIsFirstRender() {
|
||||
let firstRender = false;
|
||||
React.useEffect(() => void (firstRender = true), []);
|
||||
return firstRender;
|
||||
}
|
||||
|
||||
export function patchTabsUI(unpatches: (() => void | boolean)[]) {
|
||||
const getRows = () =>
|
||||
Object.values(registeredSections)
|
||||
.flatMap((sect) =>
|
||||
sect.map((row) => ({
|
||||
[row.key]: {
|
||||
type: "pressable",
|
||||
title: row.title,
|
||||
icon: row.icon,
|
||||
IconComponent: () => <TableRow.Icon source={row.icon} />,
|
||||
usePredicate: row.usePredicate,
|
||||
useTrailing: row.useTrailing,
|
||||
onPress: wrapOnPress(row.onPress, null, row.render, row.title()),
|
||||
withArrow: true,
|
||||
...row.rawTabsConfig,
|
||||
},
|
||||
})),
|
||||
)
|
||||
.reduce((a, c) => Object.assign(a, c));
|
||||
|
||||
const origRendererConfig = settingConstants.SETTING_RENDERER_CONFIG;
|
||||
let rendererConfigValue = settingConstants.SETTING_RENDERER_CONFIG;
|
||||
|
||||
Object.defineProperty(settingConstants, "SETTING_RENDERER_CONFIG", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
get: () => ({
|
||||
...rendererConfigValue,
|
||||
VendettaCustomPage: {
|
||||
type: "route",
|
||||
title: () => "ShiggyCord",
|
||||
screen: {
|
||||
route: "VendettaCustomPage",
|
||||
getComponent: () => CustomPageRenderer,
|
||||
},
|
||||
},
|
||||
SHIGGYCORD_CUSTOM_PAGE: {
|
||||
type: "route",
|
||||
title: () => "ShiggyCord",
|
||||
screen: {
|
||||
route: "SHIGGYCORD_CUSTOM_PAGE",
|
||||
getComponent: () => CustomPageRenderer,
|
||||
},
|
||||
},
|
||||
BUNNY_CUSTOM_PAGE: {
|
||||
type: "route",
|
||||
title: () => "ShiggyCord",
|
||||
screen: {
|
||||
route: "BUNNY_CUSTOM_PAGE",
|
||||
getComponent: () => CustomPageRenderer,
|
||||
},
|
||||
},
|
||||
...getRows(),
|
||||
}),
|
||||
set: (v) => (rendererConfigValue = v),
|
||||
});
|
||||
|
||||
unpatches.push(() => {
|
||||
Object.defineProperty(settingConstants, "SETTING_RENDERER_CONFIG", {
|
||||
value: origRendererConfig,
|
||||
writable: true,
|
||||
get: undefined,
|
||||
set: undefined,
|
||||
});
|
||||
});
|
||||
|
||||
try {
|
||||
unpatches.push(
|
||||
after("createList", createListModule, function (args, ret) {
|
||||
const [config] = args;
|
||||
|
||||
if (config?.sections && Array.isArray(config.sections)) {
|
||||
const sections = config.sections;
|
||||
|
||||
const accountSectionIndex = sections.findIndex((i: any) =>
|
||||
i.settings?.includes("ACCOUNT"),
|
||||
);
|
||||
|
||||
if (accountSectionIndex !== -1) {
|
||||
// Credit to @palmdevs - https://discord.com/channels/1196075698301968455/1243605828783571024/1307940348378742816
|
||||
|
||||
let index = accountSectionIndex + 1;
|
||||
|
||||
Object.keys(registeredSections).forEach((sect) => {
|
||||
const alreadyExists = sections.some((s: any) => s.label === sect);
|
||||
if (!alreadyExists) {
|
||||
sections.splice(index++, 0, {
|
||||
label: sect,
|
||||
title: sect,
|
||||
settings: registeredSections[sect].map((a) => a.key),
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}),
|
||||
);
|
||||
} catch {
|
||||
unpatches.push(
|
||||
after("default", SettingsOverviewScreen, (_, ret) => {
|
||||
if (useIsFirstRender()) return; // :shrug:
|
||||
|
||||
const { sections } = findInReactTree(
|
||||
ret,
|
||||
(i) => i.props?.sections,
|
||||
).props;
|
||||
// Credit to @palmdevs - https://discord.com/channels/1196075698301968455/1243605828783571024/1307940348378742816
|
||||
let index =
|
||||
-~sections.findIndex((i: any) => i.settings.includes("ACCOUNT")) || 1;
|
||||
|
||||
Object.keys(registeredSections).forEach((sect) => {
|
||||
sections.splice(index++, 0, {
|
||||
label: sect,
|
||||
title: sect,
|
||||
settings: registeredSections[sect].map((a) => a.key),
|
||||
});
|
||||
});
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
16
ui/sheets.ts
16
ui/sheets.ts
|
|
@ -1,16 +0,0 @@
|
|||
import { findByPropsLazy } from "@metro/wrappers";
|
||||
|
||||
const actionSheet = findByPropsLazy("openLazy", "hideActionSheet");
|
||||
|
||||
export function showSheet<T extends React.ComponentType<any>>(
|
||||
key: string,
|
||||
lazyImport: Promise<{ default: T; }> | T,
|
||||
props?: React.ComponentProps<T>
|
||||
) {
|
||||
if (!("then" in lazyImport)) lazyImport = Promise.resolve({ default: lazyImport });
|
||||
actionSheet.openLazy(lazyImport, key, props ?? {});
|
||||
}
|
||||
|
||||
export function hideSheet(key: string) {
|
||||
actionSheet.hideActionSheet(key);
|
||||
}
|
||||
75
ui/styles.ts
75
ui/styles.ts
|
|
@ -1,75 +0,0 @@
|
|||
import { lazyDestructure, proxyLazy } from "@lib/utils/lazy";
|
||||
import { findByProps, findByPropsLazy } from "@metro/wrappers";
|
||||
import { isSemanticColor, resolveSemanticColor } from "@ui/color";
|
||||
import { TextStyles } from "@ui/types";
|
||||
import { ImageStyle, StyleSheet, TextStyle, ViewStyle } from "react-native";
|
||||
|
||||
type NamedStyles<T> = { [P in keyof T]: ViewStyle | TextStyle | ImageStyle };
|
||||
|
||||
const Styles = findByPropsLazy("createStyles");
|
||||
|
||||
export const { ThemeContext } = lazyDestructure(() => findByProps("ThemeContext"), { hint: "object" });
|
||||
export const { TextStyleSheet } = lazyDestructure(() => findByProps("TextStyleSheet")) as unknown as {
|
||||
TextStyleSheet: Record<TextStyles, TextStyle>;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get themed styles based on the current theme
|
||||
* @returns A hook that returns the themed stylesheet
|
||||
* @example
|
||||
* const useStyles = createStyles({
|
||||
* container: {
|
||||
* flex: 1,
|
||||
* backgroundColor: tokens.colors.BACKGROUND_PRIMARY,
|
||||
* },
|
||||
* });
|
||||
*
|
||||
* function MyComponent() {
|
||||
* const styles = useStyles();
|
||||
* return <View style={styles.container} />;
|
||||
* }
|
||||
*/
|
||||
export function createStyles<T extends NamedStyles<T>>(sheet: T | ((props: any) => T)): () => T {
|
||||
return proxyLazy(() => Styles.createStyles(sheet));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get themed styles based on the current theme for class components
|
||||
* @example
|
||||
* const getStyles = createStyles({
|
||||
* container: {
|
||||
* flex: 1,
|
||||
* backgroundColor: tokens.colors.BACKGROUND_PRIMARY,
|
||||
* },
|
||||
* });
|
||||
*
|
||||
* class MyComponent extends React.Component {
|
||||
* static contextType = ThemeContext;
|
||||
* render() {
|
||||
* const styles = getStyles(this.context);
|
||||
* return <View style={styles.container} />;
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
export function createLegacyClassComponentStyles<T extends NamedStyles<T>>(sheet: T | ((props: any) => T)): (ctxt: typeof ThemeContext) => T {
|
||||
return proxyLazy(() => Styles.createLegacyClassComponentStyles(sheet));
|
||||
}
|
||||
|
||||
/**
|
||||
* Reimplementation of Discord's createThemedStyleSheet, which was removed since 204201
|
||||
* Not exactly a 1:1 reimplementation, but sufficient to keep compatibility with existing plugins
|
||||
* @deprecated Use createStyles or createLegacyClassComponentStyles instead
|
||||
*/
|
||||
export function createThemedStyleSheet<T extends StyleSheet.NamedStyles<T>>(sheet: T) {
|
||||
for (const key in sheet) {
|
||||
// @ts-ignore
|
||||
sheet[key] = new Proxy(StyleSheet.flatten(sheet[key]), {
|
||||
get(target, prop, receiver) {
|
||||
const res = Reflect.get(target, prop, receiver);
|
||||
return isSemanticColor(res) ? resolveSemanticColor(res) : res;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return sheet;
|
||||
}
|
||||
20
ui/toasts.ts
20
ui/toasts.ts
|
|
@ -1,20 +0,0 @@
|
|||
import { Strings } from "@core/i18n";
|
||||
import { findAssetId } from "@lib/api/assets";
|
||||
import { lazyDestructure } from "@lib/utils/lazy";
|
||||
import { toasts } from "@metro/common";
|
||||
import { findByProps } from "@metro/wrappers";
|
||||
|
||||
const { uuid4 } = lazyDestructure(() => findByProps("uuid4"));
|
||||
|
||||
export const showToast = (content: string, asset?: number) => toasts.open({
|
||||
// ? In build 182205/44707, Discord changed their toasts, source is no longer used, rather icon, and a key is needed.
|
||||
// TODO: We could probably have the developer specify a key themselves, but this works to fix toasts
|
||||
key: `vd-toast-${uuid4()}`,
|
||||
content: content,
|
||||
source: asset,
|
||||
icon: asset,
|
||||
});
|
||||
|
||||
showToast.showCopyToClipboard = (message = Strings.COPIED_TO_CLIPBOARD) => {
|
||||
showToast(message, findAssetId("toast_copy_link"));
|
||||
};
|
||||
File diff suppressed because one or more lines are too long
Loading…
Add table
Reference in a new issue