Numbers of improvements
Mostltly refactorings, does not affect user experience
This commit is contained in:
parent
d93439d911
commit
8c8baaed5c
41 changed files with 600 additions and 184 deletions
20
build.mjs
20
build.mjs
|
|
@ -43,6 +43,26 @@ try {
|
|||
"@types": "src/utils/types.ts",
|
||||
},
|
||||
plugins: [
|
||||
{
|
||||
name: "runtimeGlobalAlias",
|
||||
setup: async build => {
|
||||
const globalMap = {
|
||||
"react": "globalThis.React",
|
||||
"react-native": "globalThis.ReactNative"
|
||||
};
|
||||
|
||||
Object.keys(globalMap).forEach(key => {
|
||||
const filter = new RegExp(`^${key}$`);
|
||||
build.onResolve({ filter }, args => ({
|
||||
namespace: "glob-" + key, path: args.path
|
||||
}));
|
||||
build.onLoad({ filter, namespace: "glob-" + key }, () => ({
|
||||
contents: `Object.defineProperty(module, 'exports', { get: () => ${globalMap[key]} })`,
|
||||
resolveDir: "src",
|
||||
}));
|
||||
});
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "buildLog",
|
||||
setup: async build => {
|
||||
|
|
|
|||
|
|
@ -22,10 +22,16 @@
|
|||
common
|
||||
}
|
||||
ui {
|
||||
components
|
||||
components {
|
||||
discord {
|
||||
Forms
|
||||
Redesign
|
||||
}
|
||||
}
|
||||
alerts
|
||||
color
|
||||
toasts
|
||||
styles
|
||||
}
|
||||
utils {
|
||||
constants
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "bunny-types",
|
||||
"version": "0.0.3",
|
||||
"version": "0.0.4",
|
||||
"description": "Types for Bunny, a mod for Discord's mobile apps",
|
||||
"author": "pylixonly",
|
||||
"publishConfig": {
|
||||
|
|
@ -30,4 +30,4 @@
|
|||
"ts-patch": "^3.1.2",
|
||||
"typescript": "^5.3.3"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -72,6 +72,7 @@
|
|||
"THEMES_RELOAD_FOR_CHANGES": "Reload the app to fully apply changes!",
|
||||
"TOASTS_INSTALLED_PLUGIN": "Installed plugin",
|
||||
"TOASTS_SUCCESSFULLY_INSTALLED": "Successfully installed",
|
||||
"TOASTS_COPIED_TO_CLIPBOARD": "Copied plugin URL to clipboard",
|
||||
"UH_OH": "Uh oh.",
|
||||
"UNINSTALL": "Uninstall",
|
||||
"UNINSTALL_TITLE": "Uninstall {title}",
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import { after } from "@lib/api/patcher";
|
|||
import { useProxy } from "@lib/api/storage";
|
||||
import { installPlugin, plugins, removePlugin } from "@lib/managers/plugins";
|
||||
import { installTheme, removeTheme, themes } from "@lib/managers/themes";
|
||||
import { Button } from "@lib/ui/components/discord/Redesign";
|
||||
import { findInReactTree } from "@lib/utils";
|
||||
import { DISCORD_SERVER_ID, HTTP_REGEX_MULTI, PLUGINS_CHANNEL_ID, PROXY_PREFIX, THEMES_CHANNEL_ID } from "@lib/utils/constants";
|
||||
import { findByName, findByProps } from "@metro/filters";
|
||||
|
|
@ -14,7 +15,6 @@ type PostType = "Plugin" | "Theme";
|
|||
|
||||
const ForumPostLongPressActionSheet = findByName("ForumPostLongPressActionSheet", false);
|
||||
const { ActionSheetRow } = findByProps("ActionSheetRow");
|
||||
const { Button } = findByProps("TableRow");
|
||||
const { useFirstForumPostMessage } = findByProps("useFirstForumPostMessage");
|
||||
const forumReactions = findByProps("MostCommonForumPostReaction");
|
||||
|
||||
|
|
|
|||
|
|
@ -5,10 +5,11 @@ import { useProxy } from "@lib/api/storage";
|
|||
import { findByProps } from "@lib/metro/filters";
|
||||
import { settings } from "@lib/settings";
|
||||
import { HTTP_REGEX_MULTI } from "@lib/utils/constants";
|
||||
import { clipboard, ReactNative as RN } from "@metro/common";
|
||||
import { clipboard } from "@metro/common";
|
||||
import { showInputAlert } from "@ui/alerts";
|
||||
import { ErrorBoundary, HelpMessage, Search } from "@ui/components";
|
||||
import fuzzysort from "fuzzysort";
|
||||
import { FlatList, View } from "react-native";
|
||||
|
||||
interface AddonPageProps<T> {
|
||||
title: string;
|
||||
|
|
@ -47,12 +48,12 @@ export default function AddonPage<T>({ title, fetchFunction, items, safeModeMess
|
|||
return (
|
||||
<ErrorBoundary>
|
||||
{/* TODO: Implement better searching than just by ID */}
|
||||
<RN.FlatList
|
||||
<FlatList
|
||||
ListHeaderComponent={<>
|
||||
{settings.safeMode?.enabled && <RN.View style={{ marginBottom: 10 }}>
|
||||
{settings.safeMode?.enabled && <View style={{ marginBottom: 10 }}>
|
||||
<HelpMessage messageType={0}>{safeModeMessage}</HelpMessage>
|
||||
{safeModeExtras}
|
||||
</RN.View>}
|
||||
</View>}
|
||||
<Search
|
||||
style={{ marginBottom: 15 }}
|
||||
onChangeText={(v: string) => setSearch(v.toLowerCase())}
|
||||
|
|
|
|||
|
|
@ -1,17 +1,16 @@
|
|||
import { Asset } from "@lib/api/assets";
|
||||
import { clipboard, ReactNative as RN } from "@metro/common";
|
||||
import { Forms } from "@ui/components";
|
||||
import { FormRow } from "@lib/ui/components/discord/Forms";
|
||||
import { clipboard } from "@metro/common";
|
||||
import { showToast } from "@ui/toasts";
|
||||
import { Image } from "react-native";
|
||||
|
||||
interface AssetDisplayProps { asset: Asset; }
|
||||
|
||||
const { FormRow } = Forms;
|
||||
|
||||
export default function AssetDisplay({ asset }: AssetDisplayProps) {
|
||||
return (
|
||||
<FormRow
|
||||
label={`${asset.name} - ${asset.id}`}
|
||||
trailing={<RN.Image source={asset.id} style={{ width: 32, height: 32 }} />}
|
||||
trailing={<Image source={asset.id} style={{ width: 32, height: 32 }} />}
|
||||
onPress={() => {
|
||||
clipboard.setString(asset.name);
|
||||
showToast.showCopyToClipboard();
|
||||
|
|
|
|||
|
|
@ -1,13 +1,12 @@
|
|||
import { getAssetIDByName } from "@lib/api/assets";
|
||||
import { ReactNative as RN, stylesheet } from "@metro/common";
|
||||
import { findByProps } from "@metro/filters";
|
||||
import { TableCheckboxRow, TableRow, TableRowGroup, TableRowIcon, TableSwitchRow } from "@lib/ui/components/discord/Redesign";
|
||||
import { semanticColors } from "@ui/color";
|
||||
import ContextMenu from "@ui/components/ContextMenu";
|
||||
|
||||
const { TableRow, TableRowIcon, TableSwitchRow, TableCheckboxRow, TableRowGroup } = findByProps("TableRow");
|
||||
import { createStyles } from "@ui/styles";
|
||||
import { Image, TouchableOpacity, View } from "react-native";
|
||||
|
||||
// TODO: These styles work weirdly. iOS has cramped text, Android with low DPI probably does too. Fix?
|
||||
const styles = stylesheet.createThemedStyleSheet({
|
||||
const useStyles = createStyles({
|
||||
card: {
|
||||
backgroundColor: semanticColors?.BACKGROUND_SECONDARY,
|
||||
borderRadius: 15,
|
||||
|
|
@ -66,6 +65,7 @@ interface CardProps {
|
|||
}
|
||||
|
||||
export default function Card(props: CardProps) {
|
||||
const styles = useStyles();
|
||||
let pressableState = props.toggleValue ?? false;
|
||||
|
||||
const headerProps = {
|
||||
|
|
@ -75,7 +75,7 @@ export default function Card(props: CardProps) {
|
|||
};
|
||||
|
||||
return (
|
||||
<RN.View style={[styles.card, { marginTop: props.index !== 0 ? 10 : 0 }]}>
|
||||
<View style={[styles.card, { marginTop: props.index !== 0 ? 10 : 0 }]}>
|
||||
<TableRowGroup>
|
||||
{props.toggleType && (props.toggleType === "switch" ?
|
||||
<TableSwitchRow
|
||||
|
|
@ -94,7 +94,7 @@ export default function Card(props: CardProps) {
|
|||
<TableRow
|
||||
label={props.descriptionLabel}
|
||||
trailing={
|
||||
<RN.View style={styles.actions}>
|
||||
<View style={styles.actions}>
|
||||
{props.overflowActions &&
|
||||
<ContextMenu
|
||||
triggerOnLongPress={false}
|
||||
|
|
@ -105,15 +105,15 @@ export default function Card(props: CardProps) {
|
|||
}))}
|
||||
align="auto"
|
||||
title={props.overflowTitle!!}
|
||||
children={props => <RN.TouchableOpacity {...props}>
|
||||
<RN.Image style={styles.icon} source={getAssetIDByName("ic_more_24px")} />
|
||||
</RN.TouchableOpacity>}
|
||||
children={props => <TouchableOpacity {...props}>
|
||||
<Image style={styles.icon} source={getAssetIDByName("ic_more_24px")} />
|
||||
</TouchableOpacity>}
|
||||
/>
|
||||
}
|
||||
</RN.View>
|
||||
</View>
|
||||
}
|
||||
/>
|
||||
</TableRowGroup>
|
||||
</RN.View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,13 @@
|
|||
import { Strings } from "@core/i18n";
|
||||
import { getAssetIDByName } from "@lib/api/assets";
|
||||
import { HTTP_REGEX_MULTI } from "@lib/utils/constants";
|
||||
import { clipboard, ReactNative as RN, stylesheet } from "@metro/common";
|
||||
import { clipboard } from "@metro/common";
|
||||
import { showInputAlert } from "@ui/alerts";
|
||||
import { semanticColors } from "@ui/color";
|
||||
import { createStyles } from "@ui/styles";
|
||||
import { Image, TouchableOpacity } from "react-native";
|
||||
|
||||
const styles = stylesheet.createThemedStyleSheet({
|
||||
const useStyles = createStyles({
|
||||
icon: {
|
||||
marginRight: 10,
|
||||
tintColor: semanticColors.HEADER_PRIMARY,
|
||||
|
|
@ -18,8 +20,10 @@ interface InstallButtonProps {
|
|||
}
|
||||
|
||||
export default function InstallButton({ alertTitle, installFunction: fetchFunction }: InstallButtonProps) {
|
||||
const styles = useStyles();
|
||||
|
||||
return (
|
||||
<RN.TouchableOpacity onPress={() =>
|
||||
<TouchableOpacity onPress={() =>
|
||||
clipboard.getString().then(content =>
|
||||
showInputAlert({
|
||||
title: alertTitle,
|
||||
|
|
@ -31,7 +35,7 @@ export default function InstallButton({ alertTitle, installFunction: fetchFuncti
|
|||
})
|
||||
)
|
||||
}>
|
||||
<RN.Image style={styles.icon} source={getAssetIDByName("ic_add_24px")} />
|
||||
</RN.TouchableOpacity>
|
||||
<Image style={styles.icon} source={getAssetIDByName("ic_add_24px")} />
|
||||
</TouchableOpacity>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,10 +3,9 @@ import { getScreens } from "@core/ui/settings/data";
|
|||
import { getAssetIDByName } from "@lib/api/assets";
|
||||
import { useProxy } from "@lib/api/storage";
|
||||
import { settings } from "@lib/settings";
|
||||
import { FormDivider, FormRow, FormSection } from "@lib/ui/components/discord/Forms";
|
||||
import { NavigationNative } from "@metro/common";
|
||||
import { ErrorBoundary, Forms } from "@ui/components";
|
||||
|
||||
const { FormRow, FormSection, FormDivider } = Forms;
|
||||
import { ErrorBoundary } from "@ui/components";
|
||||
|
||||
export default function SettingsSection() {
|
||||
const navigation = NavigationNative.useNavigation();
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { getAssetIDByName } from "@lib/api/assets";
|
||||
import { findByProps } from "@lib/metro/filters";
|
||||
import { FormText } from "@lib/ui/components/discord/Forms";
|
||||
import { TableRow } from "@lib/ui/components/discord/Redesign";
|
||||
import { clipboard } from "@metro/common";
|
||||
import { Forms } from "@ui/components";
|
||||
import { showToast } from "@ui/toasts";
|
||||
|
||||
interface VersionProps {
|
||||
|
|
@ -11,9 +11,6 @@ interface VersionProps {
|
|||
padding: boolean;
|
||||
}
|
||||
|
||||
const { TableRow } = findByProps("TableRow");
|
||||
const { FormText } = Forms;
|
||||
|
||||
export default function Version({ label, version, icon, padding }: VersionProps) {
|
||||
return (
|
||||
<TableRow
|
||||
|
|
|
|||
|
|
@ -13,11 +13,13 @@ import { findByProps } from "@lib/metro/filters";
|
|||
import { settings } from "@lib/settings";
|
||||
import { without } from "@lib/utils";
|
||||
import { PROXY_PREFIX } from "@lib/utils/constants";
|
||||
import { NavigationNative, ReactNative as RN, stylesheet } from "@metro/common";
|
||||
import { NavigationNative } from "@metro/common";
|
||||
import { showConfirmationAlert } from "@ui/alerts";
|
||||
import { semanticColors } from "@ui/color";
|
||||
import ErrorBoundary from "@ui/components/ErrorBoundary";
|
||||
import { createStyles } from "@ui/styles";
|
||||
import { showToast } from "@ui/toasts";
|
||||
import { View } from "react-native";
|
||||
|
||||
const { useSafeAreaInsets } = findByProps("useSafeAreaInsets");
|
||||
|
||||
|
|
@ -31,10 +33,10 @@ interface Screen {
|
|||
render: React.ComponentType<any>;
|
||||
}
|
||||
|
||||
const useStyles = stylesheet.createStyles({
|
||||
const useStyles = createStyles({
|
||||
container: {
|
||||
flex: 1,
|
||||
backgroundColor: semanticColors.BACKGROUND_MOBILE_PRIMARY
|
||||
backgroundColor: semanticColors.BACKGROUND_MOBILE_PRIMARY,
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -133,7 +135,9 @@ export const getYouData = () => {
|
|||
const styles = useStyles();
|
||||
const { bottom: paddingBottom } = useSafeAreaInsets();
|
||||
navigation.addListener("focus", () => navigation.setOptions(s.options));
|
||||
return <RN.View style={[{ paddingBottom }, styles.container]}><s.render {...route.params} /></RN.View>;
|
||||
return <View style={[{ paddingBottom }, styles.container]}>
|
||||
<s.render {...route.params} />
|
||||
</View>;
|
||||
});
|
||||
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -1,28 +1,27 @@
|
|||
import AssetDisplay from "@core/ui/components/AssetDisplay";
|
||||
import { all } from "@lib/api/assets";
|
||||
import { ReactNative as RN } from "@metro/common";
|
||||
import { ErrorBoundary, Forms, Search } from "@ui/components";
|
||||
|
||||
const { FormDivider } = Forms;
|
||||
import { FormDivider } from "@lib/ui/components/discord/Forms";
|
||||
import { ErrorBoundary, Search } from "@ui/components";
|
||||
import { FlatList, View } from "react-native";
|
||||
|
||||
export default function AssetBrowser() {
|
||||
const [search, setSearch] = React.useState("");
|
||||
|
||||
return (
|
||||
<ErrorBoundary>
|
||||
<RN.View style={{ flex: 1 }}>
|
||||
<View style={{ flex: 1 }}>
|
||||
<Search
|
||||
style={{ margin: 10 }}
|
||||
onChangeText={(v: string) => setSearch(v)}
|
||||
placeholder="Search"
|
||||
/>
|
||||
<RN.FlatList
|
||||
<FlatList
|
||||
data={Object.values(all).filter(a => a.name.includes(search) || a.id.toString() === search)}
|
||||
renderItem={({ item }) => <AssetDisplay asset={item} />}
|
||||
ItemSeparatorComponent={FormDivider}
|
||||
keyExtractor={item => item.name}
|
||||
/>
|
||||
</RN.View>
|
||||
</View>
|
||||
</ErrorBoundary>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,18 +5,19 @@ import { getReactDevToolsProp, getReactDevToolsVersion, isLoaderConfigSupported,
|
|||
import { useProxy } from "@lib/api/storage";
|
||||
import { connectToDebugger } from "@lib/debug";
|
||||
import { loaderConfig, settings } from "@lib/settings";
|
||||
import { NavigationNative, ReactNative as RN, stylesheet } from "@metro/common";
|
||||
import { FormText } from "@lib/ui/components/discord/Forms";
|
||||
import { Stack, TableRow, TableRowGroup, TableSwitchRow, TextInput } from "@lib/ui/components/discord/Redesign";
|
||||
import { NavigationNative } from "@metro/common";
|
||||
import { findByProps } from "@metro/filters";
|
||||
import { semanticColors } from "@ui/color";
|
||||
import { ErrorBoundary, Forms } from "@ui/components";
|
||||
import { ErrorBoundary } from "@ui/components";
|
||||
import { createStyles, TextStyleSheet } from "@ui/styles";
|
||||
import { ScrollView, StyleSheet } from "react-native";
|
||||
|
||||
const { Stack, TableRow, TableSwitchRow, TableRowGroup, TextInput } = findByProps("TableRow");
|
||||
const { FormText } = Forms;
|
||||
const { hideActionSheet } = findByProps("openLazy", "hideActionSheet");
|
||||
const { showSimpleActionSheet } = findByProps("showSimpleActionSheet");
|
||||
const { TextStyleSheet } = findByProps("TextStyleSheet");
|
||||
|
||||
const styles = stylesheet.createThemedStyleSheet({
|
||||
const useStyles = createStyles({
|
||||
leadingText: {
|
||||
...TextStyleSheet["heading-md/semibold"],
|
||||
color: semanticColors.TEXT_MUTED,
|
||||
|
|
@ -25,6 +26,7 @@ const styles = stylesheet.createThemedStyleSheet({
|
|||
});
|
||||
|
||||
export default function Developer() {
|
||||
const styles = useStyles();
|
||||
const navigation = NavigationNative.useNavigation();
|
||||
|
||||
useProxy(settings);
|
||||
|
|
@ -32,7 +34,7 @@ export default function Developer() {
|
|||
|
||||
return (
|
||||
<ErrorBoundary>
|
||||
<RN.ScrollView style={{ flex: 1 }} contentContainerStyle={{ paddingBottom: 38 }}>
|
||||
<ScrollView style={{ flex: 1 }} contentContainerStyle={{ paddingBottom: 38 }}>
|
||||
<Stack style={{ paddingVertical: 24, paddingHorizontal: 12 }} spacing={24}>
|
||||
<TextInput
|
||||
label="Debugger URL"
|
||||
|
|
@ -54,7 +56,7 @@ export default function Developer() {
|
|||
icon={<TableRow.Icon source={getAssetIDByName("ic_badge_staff")} />}
|
||||
onPress={() => window[getReactDevToolsProp() || "__vendetta_rdc"]?.connectToDevTools({
|
||||
host: settings.debuggerUrl.split(":")?.[0],
|
||||
resolveRNStyle: RN.StyleSheet.flatten,
|
||||
resolveRNStyle: StyleSheet.flatten,
|
||||
})}
|
||||
/>
|
||||
</>}
|
||||
|
|
@ -120,7 +122,7 @@ export default function Developer() {
|
|||
/>
|
||||
</TableRowGroup>
|
||||
</Stack>
|
||||
</RN.ScrollView>
|
||||
</ScrollView>
|
||||
</ErrorBoundary>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,17 +3,18 @@ import Version from "@core/ui/components/Version";
|
|||
import { getAssetIDByName } from "@lib/api/assets";
|
||||
import { useProxy } from "@lib/api/storage";
|
||||
import { getDebugInfo, toggleSafeMode } from "@lib/debug";
|
||||
import { findByProps } from "@lib/metro/filters";
|
||||
import { settings } from "@lib/settings";
|
||||
import { Stack, TableRow, TableRowGroup, TableSwitchRow } from "@lib/ui/components/discord/Redesign";
|
||||
import { DISCORD_SERVER, GITHUB } from "@lib/utils/constants";
|
||||
import { ReactNative as RN, stylesheet, url } from "@metro/common";
|
||||
import { url } from "@metro/common";
|
||||
import { semanticColors } from "@ui/color";
|
||||
import { Summary } from "@ui/components";
|
||||
import { createStyles } from "@ui/styles";
|
||||
import { NativeModules, Platform, ScrollView } from "react-native";
|
||||
|
||||
const { Stack, TableRow, TableSwitchRow, TableRowGroup } = findByProps("TableRow");
|
||||
const debugInfo = getDebugInfo();
|
||||
|
||||
const styles = stylesheet.createThemedStyleSheet({
|
||||
const useStyles = createStyles({
|
||||
container: {
|
||||
flex: 1,
|
||||
backgroundColor: semanticColors.BACKGROUND_MOBILE_SECONDARY,
|
||||
|
|
@ -21,6 +22,7 @@ const styles = stylesheet.createThemedStyleSheet({
|
|||
});
|
||||
|
||||
export default function General() {
|
||||
const styles = useStyles();
|
||||
useProxy(settings);
|
||||
|
||||
const versions = [
|
||||
|
|
@ -83,14 +85,14 @@ export default function General() {
|
|||
icon: "ic_phonelink_24px"
|
||||
},
|
||||
{
|
||||
label: RN.Platform.select({ android: Strings.CODENAME, ios: Strings.MACHINE_ID })!,
|
||||
label: Platform.select({ android: Strings.CODENAME, ios: Strings.MACHINE_ID })!,
|
||||
version: debugInfo.device.codename,
|
||||
icon: "ic_compose_24px"
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<RN.ScrollView style={styles.container}>
|
||||
<ScrollView style={styles.container}>
|
||||
<Stack style={{ paddingVertical: 24, paddingHorizontal: 12 }} spacing={24}>
|
||||
<TableRowGroup title={Strings.LINKS}>
|
||||
<TableRow
|
||||
|
|
@ -110,7 +112,7 @@ export default function General() {
|
|||
<TableRow
|
||||
label={Strings.RELOAD_DISCORD}
|
||||
icon={<TableRow.Icon source={getAssetIDByName("ic_message_retry")} />}
|
||||
onPress={() => RN.NativeModules.BundleUpdaterManager.reload()}
|
||||
onPress={() => NativeModules.BundleUpdaterManager.reload()}
|
||||
/>
|
||||
<TableRow
|
||||
label={settings.safeMode?.enabled ? Strings.RELOAD_IN_NORMAL_MODE : Strings.RELOAD_IN_SAFE_MODE}
|
||||
|
|
@ -136,6 +138,6 @@ export default function General() {
|
|||
</Summary>
|
||||
</TableRowGroup>
|
||||
</Stack>
|
||||
</RN.ScrollView>
|
||||
</ScrollView>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,9 @@ import * as debug from "@lib/debug";
|
|||
import * as plugins from "@lib/managers/plugins";
|
||||
import * as themes from "@lib/managers/themes";
|
||||
import { loaderConfig, settings } from "@lib/settings";
|
||||
import { Forms } from "@lib/ui/components/discord/Forms";
|
||||
import { CompatfulRedesign } from "@lib/ui/components/discord/Redesign";
|
||||
import { createThemedStyleSheet } from "@lib/ui/styles";
|
||||
import * as utils from "@lib/utils";
|
||||
import * as common from "@metro/common";
|
||||
import * as metro from "@metro/filters";
|
||||
|
|
@ -39,7 +42,10 @@ export const createVendettaObject = (unloads: any[]): any => ({
|
|||
i18n: common.i18n,
|
||||
url: common.url,
|
||||
toasts: common.toasts,
|
||||
stylesheet: common.stylesheet,
|
||||
stylesheet: {
|
||||
...CompatfulRedesign,
|
||||
createThemedStyleSheet
|
||||
},
|
||||
clipboard: common.clipboard,
|
||||
assets: common.assets,
|
||||
invites: common.invites,
|
||||
|
|
@ -80,8 +86,8 @@ export const createVendettaObject = (unloads: any[]): any => ({
|
|||
},
|
||||
ui: {
|
||||
components: {
|
||||
Forms: components.Forms,
|
||||
General: components.General,
|
||||
Forms,
|
||||
General: common.ReactNative,
|
||||
Alert: components.Alert,
|
||||
Button: components.Button,
|
||||
HelpMessage: components.HelpMessage,
|
||||
|
|
|
|||
|
|
@ -1,8 +1,7 @@
|
|||
import { ApplicationCommand, ApplicationCommandInputType, ApplicationCommandType } from "@lib/api/commands/types";
|
||||
import { after } from "@lib/api/patcher";
|
||||
import { commands as commandsModule } from "@metro/common";
|
||||
|
||||
import { ApplicationCommand, ApplicationCommandInputType, ApplicationCommandType } from "./types";
|
||||
|
||||
let commands: ApplicationCommand[] = [];
|
||||
|
||||
export function patchCommands() {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { FileManager, MMKVManager } from "@lib/api/native/modules";
|
||||
import { ReactNative as RN } from "@metro/common";
|
||||
import { Platform } from "react-native";
|
||||
|
||||
export interface StorageBackend {
|
||||
get: () => unknown | Promise<unknown>;
|
||||
|
|
@ -8,7 +8,7 @@ export interface StorageBackend {
|
|||
|
||||
const ILLEGAL_CHARS_REGEX = /[<>:"/\\|?*]/g;
|
||||
|
||||
const filePathFixer = (file: string): string => RN.Platform.select({
|
||||
const filePathFixer = (file: string): string => Platform.select({
|
||||
default: file,
|
||||
ios: FileManager.saveFileToGallery ? file : `Documents/${file}`,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -5,10 +5,9 @@ import { after } from "@lib/api/patcher";
|
|||
import { getThemeFromLoader, selectTheme } from "@lib/managers/themes";
|
||||
import { settings } from "@lib/settings";
|
||||
import logger from "@lib/utils/logger";
|
||||
import { ReactNative as RN } from "@metro/common";
|
||||
import { showToast } from "@ui/toasts";
|
||||
import { version } from "bunny-build";
|
||||
import type { PlatformConstants } from "react-native";
|
||||
import { Platform, type PlatformConstants } from "react-native";
|
||||
export let socket: WebSocket;
|
||||
|
||||
export interface RNConstants extends PlatformConstants {
|
||||
|
|
@ -88,7 +87,7 @@ export function getDebugInfo() {
|
|||
const padding = "for RN ";
|
||||
|
||||
// RN
|
||||
const PlatformConstants = RN.Platform.constants as RNConstants;
|
||||
const PlatformConstants = Platform.constants as RNConstants;
|
||||
const rnVer = PlatformConstants.reactNativeVersion;
|
||||
|
||||
return {
|
||||
|
|
@ -114,7 +113,7 @@ export function getDebugInfo() {
|
|||
buildType: hermesProps.Build,
|
||||
bytecodeVersion: hermesProps["Bytecode Version"],
|
||||
},
|
||||
...RN.Platform.select(
|
||||
...Platform.select(
|
||||
{
|
||||
android: {
|
||||
os: {
|
||||
|
|
@ -131,7 +130,7 @@ export function getDebugInfo() {
|
|||
}
|
||||
}
|
||||
)!,
|
||||
...RN.Platform.select(
|
||||
...Platform.select(
|
||||
{
|
||||
android: {
|
||||
device: {
|
||||
|
|
|
|||
|
|
@ -6,8 +6,9 @@ import { awaitSyncWrapper, createFileBackend, createMMKVBackend, createStorage,
|
|||
import { findInReactTree, safeFetch } from "@lib/utils";
|
||||
import logger from "@lib/utils/logger";
|
||||
import { Author } from "@lib/utils/types";
|
||||
import { chroma, ReactNative as RN } from "@metro/common";
|
||||
import { chroma } from "@metro/common";
|
||||
import { find, findByName, findByProps, findByStoreName } from "@metro/filters";
|
||||
import { ImageBackground, Platform, processColor, StyleSheet } from "react-native";
|
||||
|
||||
export interface ThemeData {
|
||||
name: string;
|
||||
|
|
@ -71,7 +72,7 @@ export function patchChatBackground() {
|
|||
if (!MessagesWrapper) return;
|
||||
|
||||
const patches = [
|
||||
after("default", MessagesWrapperConnected, (_, ret) => React.createElement(RN.ImageBackground, {
|
||||
after("default", MessagesWrapperConnected, (_, ret) => React.createElement(ImageBackground, {
|
||||
style: { flex: 1, height: "100%" },
|
||||
source: currentTheme?.data?.background?.url && { uri: currentTheme.data.background.url } || 0,
|
||||
blurRadius: typeof currentTheme?.data?.background?.blur === "number" ? currentTheme?.data?.background?.blur : 0,
|
||||
|
|
@ -81,7 +82,7 @@ export function patchChatBackground() {
|
|||
const Messages = findInReactTree(ret, x => x && "HACK_fixModalInteraction" in x.props && x?.props?.style);
|
||||
if (Messages)
|
||||
Messages.props.style = Object.assign(
|
||||
RN.StyleSheet.flatten(Messages.props.style ?? {}),
|
||||
StyleSheet.flatten(Messages.props.style ?? {}),
|
||||
{
|
||||
backgroundColor: "#0000"
|
||||
}
|
||||
|
|
@ -97,7 +98,7 @@ export function patchChatBackground() {
|
|||
function normalizeToHex(colorString: string): string {
|
||||
if (chroma.valid(colorString)) return chroma(colorString).hex();
|
||||
|
||||
const color = Number(RN.processColor(colorString));
|
||||
const color = Number(processColor(colorString));
|
||||
|
||||
return chroma.rgb(
|
||||
color >> 16 & 0xff, // red
|
||||
|
|
@ -126,7 +127,7 @@ function processData(data: ThemeData) {
|
|||
data.rawColors[key] = normalizeToHex(rawColors[key]);
|
||||
}
|
||||
|
||||
if (RN.Platform.OS === "android") applyAndroidAlphaKeys(rawColors);
|
||||
if (Platform.OS === "android") applyAndroidAlphaKeys(rawColors);
|
||||
}
|
||||
|
||||
return data;
|
||||
|
|
|
|||
|
|
@ -1,34 +1,6 @@
|
|||
import { find, findByProps, findByStoreName } from "@metro/filters";
|
||||
import type { StyleSheet } from "react-native";
|
||||
import { find, findByProps } from "@metro/filters";
|
||||
|
||||
export interface DiscordStyleSheet {
|
||||
[index: string]: any,
|
||||
createStyles: <T extends import("react-native").StyleSheet.NamedStyles<T>>(sheet: T | (() => T)) => () => T;
|
||||
createThemedStyleSheet: typeof import("react-native").StyleSheet.create;
|
||||
}
|
||||
|
||||
const ThemeStore = findByStoreName("ThemeStore");
|
||||
const colorModule = findByProps("colors", "unsafe_rawColors");
|
||||
const colorResolver = colorModule.internal ??= colorModule.meta;
|
||||
|
||||
// Reimplementation of Discord's createThemedStyleSheet, which was removed since 204201
|
||||
// Not exactly a 1:1 reimplementation, but sufficient to keep compatibility with existing plugins
|
||||
function createThemedStyleSheet<T extends StyleSheet.NamedStyles<T>>(sheet: T) {
|
||||
if (!colorModule) return;
|
||||
for (const key in sheet) {
|
||||
// @ts-ignore
|
||||
sheet[key] = new Proxy(ReactNative.StyleSheet.flatten(sheet[key]), {
|
||||
get(target, prop, receiver) {
|
||||
const res = Reflect.get(target, prop, receiver);
|
||||
return colorResolver.isSemanticColor(res)
|
||||
? colorResolver.resolveSemanticColor(ThemeStore.theme, res)
|
||||
: res;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return sheet;
|
||||
}
|
||||
|
||||
// Discord
|
||||
export const constants = findByProps("Fonts", "Permissions");
|
||||
|
|
@ -36,14 +8,6 @@ export const channels = findByProps("getVoiceChannelId");
|
|||
export const i18n = findByProps("Messages");
|
||||
export const url = findByProps("openURL", "openDeeplink");
|
||||
export const toasts = find(m => m.open && m.close && !m.startDrag && !m.init && !m.openReplay && !m.setAlwaysOnTop && !m.setAccountFlag);
|
||||
|
||||
// Compatible with pre-204201 versions since createThemedStyleSheet is undefined.
|
||||
export const stylesheet = {
|
||||
...find(m => m.createStyles && !m.ActionSheet),
|
||||
createThemedStyleSheet,
|
||||
...findByProps("createThemedStyleSheet") as {},
|
||||
} as DiscordStyleSheet;
|
||||
|
||||
export const clipboard = findByProps("setString", "getString", "hasString") as typeof import("@react-native-clipboard/clipboard").default;
|
||||
export const assets = findByProps("registerAsset");
|
||||
export const invites = findByProps("acceptInviteAndTransitionToInviteChannel");
|
||||
|
|
@ -59,7 +23,7 @@ export const FluxDispatcher = findByProps("_currentDispatchActionType");
|
|||
|
||||
// React
|
||||
export const React = window.React = findByProps("createElement") as typeof import("react");
|
||||
export const ReactNative = findByProps("AppRegistry") as typeof import("react-native");
|
||||
export const ReactNative = window.ReactNative = findByProps("AppRegistry") as typeof import("react-native");
|
||||
|
||||
// Moment
|
||||
export const moment = findByProps("isMoment") as typeof import("moment");
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import { getLoaderConfigPath } from "@lib/api/native/loader";
|
||||
import { createFileBackend, createMMKVBackend, createStorage, wrapSync } from "@lib/api/storage";
|
||||
|
||||
import { getLoaderConfigPath } from "./api/native/loader";
|
||||
|
||||
export interface Settings {
|
||||
debuggerUrl: string;
|
||||
developerSettings: boolean;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { color } from "@lib/managers/themes";
|
||||
import { findByStoreName } from "@lib/metro/filters";
|
||||
import { constants } from "@metro/common";
|
||||
|
||||
//! This module is only found on 165.0+, under the assumption that iOS 165.0 is the same as Android 165.0.
|
||||
|
|
@ -10,8 +11,19 @@ import { constants } from "@metro/common";
|
|||
// const colorModule = findByProps("colors", "meta");
|
||||
|
||||
// ? SemanticColor and default.colors are effectively ThemeColorMap
|
||||
export const semanticColors = (color?.default?.colors ?? constants?.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);
|
||||
export const rawColors = (color?.default?.unsafe_rawColors ?? constants?.Colors) as Record<string, string>;
|
||||
|
||||
const ThemeStore = findByStoreName("ThemeStore");
|
||||
const colorResolver = color.default.internal ??= color.default.meta;
|
||||
|
||||
export function isSemanticColor(sym: Symbol): boolean {
|
||||
return colorResolver.isSemanticColor(sym);
|
||||
}
|
||||
|
||||
export function resolveSemanticColor(sym: Symbol, theme = ThemeStore.theme): string {
|
||||
return colorResolver.resolveSemanticColor(theme, sym);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,15 @@
|
|||
import { constants,ReactNative as RN, stylesheet } from "@metro/common";
|
||||
import { constants } from "@metro/common";
|
||||
import { semanticColors } from "@ui/color";
|
||||
import { createStyles } from "@ui/styles";
|
||||
import { Platform, Text, TextInput, type TextStyle } from "react-native";
|
||||
|
||||
export interface CodeblockProps {
|
||||
selectable?: boolean;
|
||||
style?: import("react-native").TextStyle;
|
||||
style?: TextStyle;
|
||||
children?: string;
|
||||
}
|
||||
|
||||
const styles = stylesheet.createThemedStyleSheet({
|
||||
const useStyles = createStyles({
|
||||
codeBlock: {
|
||||
fontFamily: constants.Fonts.CODE_SEMIBOLD,
|
||||
fontSize: 12,
|
||||
|
|
@ -22,13 +24,13 @@ const styles = stylesheet.createThemedStyleSheet({
|
|||
});
|
||||
|
||||
// iOS doesn't support the selectable property on RN.Text...
|
||||
const InputBasedCodeblock = ({ style, children }: CodeblockProps) => <RN.TextInput editable={false} multiline style={[styles.codeBlock, style && style]} value={children} />;
|
||||
const TextBasedCodeblock = ({ selectable, style, children }: CodeblockProps) => <RN.Text selectable={selectable} style={[styles.codeBlock, style && style]}>{children}</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 RN.Platform.select({
|
||||
return Platform.select({
|
||||
ios: <InputBasedCodeblock style={style} children={children} />,
|
||||
default: <TextBasedCodeblock style={style} children={children} selectable />,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,9 +1,8 @@
|
|||
import { instead } from "@lib/api/patcher";
|
||||
import { ReactNative } from "@lib/metro/common";
|
||||
import { findByProps } from "@lib/metro/filters";
|
||||
import { AccessibilityInfo, Platform, View } from "react-native";
|
||||
|
||||
const { ContextMenu: _ContextMenu } = findByProps("ContextMenu");
|
||||
const { AccessibilityInfo } = ReactNative;
|
||||
|
||||
type ContextMenuItem = {
|
||||
label: string,
|
||||
|
|
@ -26,7 +25,7 @@ export default function ContextMenu(props: ContextMenuProps) {
|
|||
|
||||
React.useEffect(() => {
|
||||
// hacky discord fix for android where pressing back with context menu open crashes the client
|
||||
if (ReactNative.Platform.OS !== "android") return;
|
||||
if (Platform.OS !== "android") return;
|
||||
|
||||
// @ts-ignore
|
||||
const ctxMenuReactTag = ref.current?._children?.[0]?._nativeTag;
|
||||
|
|
@ -44,9 +43,9 @@ export default function ContextMenu(props: ContextMenuProps) {
|
|||
};
|
||||
}, []);
|
||||
|
||||
return <ReactNative.View ref={ref}>
|
||||
return <View ref={ref}>
|
||||
<_ContextMenu
|
||||
{...props}
|
||||
/>
|
||||
</ReactNative.View>;
|
||||
</View>;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,11 @@
|
|||
import { Strings } from "@core/i18n";
|
||||
import { React, ReactNative as RN, stylesheet } from "@metro/common";
|
||||
import { Button, Codeblock, Forms } from "@ui/components";
|
||||
import { React } from "@metro/common";
|
||||
import { Button, Codeblock } from "@ui/components";
|
||||
import { ScrollView } from "react-native";
|
||||
|
||||
import { createThemedStyleSheet } from "../styles";
|
||||
import { FormText } from "./discord/Forms";
|
||||
|
||||
|
||||
interface ErrorBoundaryState {
|
||||
hasErr: boolean;
|
||||
|
|
@ -11,7 +16,7 @@ export interface ErrorBoundaryProps {
|
|||
children: JSX.Element | JSX.Element[];
|
||||
}
|
||||
|
||||
const styles = stylesheet.createThemedStyleSheet({
|
||||
const styles = createThemedStyleSheet({
|
||||
view: {
|
||||
flex: 1,
|
||||
flexDirection: "column",
|
||||
|
|
@ -36,8 +41,8 @@ export default class ErrorBoundary extends React.Component<ErrorBoundaryProps, E
|
|||
if (!this.state.hasErr) return this.props.children;
|
||||
|
||||
return (
|
||||
<RN.ScrollView style={styles.view}>
|
||||
<Forms.FormText style={styles.title}>{Strings.UH_OH}</Forms.FormText>
|
||||
<ScrollView style={styles.view}>
|
||||
<FormText style={styles.title}>{Strings.UH_OH}</FormText>
|
||||
<Codeblock selectable style={{ marginBottom: 5 }}>{this.state.errText}</Codeblock>
|
||||
<Button
|
||||
color={Button.Colors.RED}
|
||||
|
|
@ -46,7 +51,7 @@ export default class ErrorBoundary extends React.Component<ErrorBoundaryProps, E
|
|||
onPress={() => this.setState({ hasErr: false, errText: undefined })}
|
||||
text={Strings.RETRY}
|
||||
/>
|
||||
</RN.ScrollView>
|
||||
</ScrollView>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import { ButtonColors } from "@lib/utils/types";
|
||||
import { findByProps } from "@metro/filters";
|
||||
import { Alert, Forms } from "@ui/components";
|
||||
import { Alert } from "@ui/components";
|
||||
import { FormInput } from "@ui/components/discord/Forms";
|
||||
|
||||
const { FormInput } = Forms;
|
||||
const Alerts = findByProps("openLazy", "close");
|
||||
|
||||
export interface InputAlertProps {
|
||||
|
|
|
|||
|
|
@ -1,10 +1,9 @@
|
|||
import { Strings } from "@core/i18n";
|
||||
import { getAssetIDByName } from "@lib/api/assets";
|
||||
import { ReactNative as RN } from "@metro/common";
|
||||
import { findByProps } from "@metro/filters";
|
||||
import ErrorBoundary from "@ui/components/ErrorBoundary";
|
||||
import { Image, View } from "react-native";
|
||||
|
||||
const { TextInput } = findByProps("TableRow");
|
||||
import { TextInput } from "./discord/Redesign";
|
||||
|
||||
export interface SearchProps {
|
||||
onChangeText?: (v: string) => void;
|
||||
|
|
@ -14,7 +13,7 @@ export interface SearchProps {
|
|||
|
||||
|
||||
function SearchIcon() {
|
||||
return <RN.Image style={{ transform: [{ scale: 0.8 }] }} source={getAssetIDByName("search")} />;
|
||||
return <Image style={{ transform: [{ scale: 0.8 }] }} source={getAssetIDByName("search")} />;
|
||||
}
|
||||
|
||||
export default ({ onChangeText, placeholder, style }: SearchProps) => {
|
||||
|
|
@ -26,8 +25,10 @@ export default ({ onChangeText, placeholder, style }: SearchProps) => {
|
|||
};
|
||||
|
||||
return <ErrorBoundary>
|
||||
<RN.View style={style}>
|
||||
<TextInput isClearable grow
|
||||
<View style={style}>
|
||||
<TextInput
|
||||
grow
|
||||
isClearable
|
||||
leadingIcon={SearchIcon}
|
||||
placeholder={placeholder ?? Strings.SEARCH}
|
||||
onChange={onChange}
|
||||
|
|
@ -37,6 +38,6 @@ export default ({ onChangeText, placeholder, style }: SearchProps) => {
|
|||
autoCorrect={false}
|
||||
value={query}
|
||||
/>
|
||||
</RN.View>
|
||||
</View>
|
||||
</ErrorBoundary>;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,9 +1,8 @@
|
|||
import { getAssetIDByName } from "@lib/api/assets";
|
||||
import { findByProps } from "@lib/metro/filters";
|
||||
import { ReactNative as RN } from "@metro/common";
|
||||
import { FormRow } from "@ui/components/discord/Forms";
|
||||
import { TableRow } from "@ui/components/discord/Redesign";
|
||||
import { LayoutAnimation, View } from "react-native";
|
||||
|
||||
const { FormRow } = findByProps("FormRow");
|
||||
const { TableRow } = findByProps("TableRow");
|
||||
export interface SummaryProps {
|
||||
label: string;
|
||||
icon?: string;
|
||||
|
|
@ -23,11 +22,11 @@ export default function Summary({ label, icon, noPadding = false, noAnimation =
|
|||
trailing={<FormRow.Arrow style={{ transform: [{ rotate: `${hidden ? 180 : 90}deg` }] }} />}
|
||||
onPress={() => {
|
||||
setHidden(!hidden);
|
||||
if (!noAnimation) RN.LayoutAnimation.configureNext(RN.LayoutAnimation.Presets.easeInEaseOut);
|
||||
if (!noAnimation) LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
|
||||
}}
|
||||
/>
|
||||
{!hidden && <>
|
||||
<RN.View style={!noPadding && { paddingHorizontal: 15 }}>{children}</RN.View>
|
||||
<View style={!noPadding && { paddingHorizontal: 15 }}>{children}</View>
|
||||
</>}
|
||||
</>
|
||||
);
|
||||
|
|
|
|||
32
src/lib/ui/components/discord/Forms.ts
Normal file
32
src/lib/ui/components/discord/Forms.ts
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
import { findByProps } from "@lib/metro";
|
||||
|
||||
export const Forms = findByProps("Form", "FormSection");
|
||||
|
||||
export const {
|
||||
Form,
|
||||
FormArrow,
|
||||
FormCTA,
|
||||
FormCTAButton,
|
||||
FormCardSection,
|
||||
FormCheckbox,
|
||||
FormCheckboxRow,
|
||||
FormCheckmark,
|
||||
FormDivider,
|
||||
FormHint,
|
||||
FormIcon,
|
||||
FormInput,
|
||||
FormLabel,
|
||||
FormRadio,
|
||||
FormRadioGroup,
|
||||
FormRadioRow,
|
||||
FormRow,
|
||||
FormSection,
|
||||
FormSelect,
|
||||
FormSliderRow,
|
||||
FormSubLabel,
|
||||
FormSwitch,
|
||||
FormSwitchRow,
|
||||
FormTernaryCheckBox,
|
||||
FormText,
|
||||
FormTitle
|
||||
} = Forms;
|
||||
149
src/lib/ui/components/discord/Redesign.ts
Normal file
149
src/lib/ui/components/discord/Redesign.ts
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
import { findByProps } from "@lib/metro";
|
||||
import { RedesignModuleKeys } from "@lib/ui/types";
|
||||
|
||||
export const Redesign = findByProps("TableRow") as Record<RedesignModuleKeys, any>;
|
||||
|
||||
export const CompatfulRedesign = findByProps("ActionSheetRow") as unknown as {
|
||||
TextStyleSheet: typeof import("@ui/styles").TextStyleSheet;
|
||||
[key: string]: any;
|
||||
};
|
||||
|
||||
export const {
|
||||
AlertActionButton,
|
||||
AlertModal,
|
||||
AlertModalContainer,
|
||||
AvatarDuoPile,
|
||||
AvatarPile,
|
||||
Backdrop,
|
||||
Button,
|
||||
Card,
|
||||
ContextMenu,
|
||||
ContextMenuContainer,
|
||||
FauxHeader,
|
||||
FloatingActionButton,
|
||||
GhostInput,
|
||||
GuildIconPile,
|
||||
HeaderActionButton,
|
||||
HeaderButton,
|
||||
HeaderSubmittingIndicator,
|
||||
IconButton,
|
||||
Input,
|
||||
InputButton,
|
||||
InputContainer,
|
||||
LayerContext,
|
||||
LayerScope,
|
||||
Modal,
|
||||
ModalActionButton,
|
||||
ModalContent,
|
||||
ModalDisclaimer,
|
||||
ModalFloatingAction,
|
||||
ModalFloatingActionSpacer,
|
||||
ModalFooter,
|
||||
ModalScreen,
|
||||
ModalStepIndicator,
|
||||
Navigator,
|
||||
NavigatorHeader,
|
||||
NavigatorScreen,
|
||||
Pile,
|
||||
PileOverflow,
|
||||
RedesignCompat,
|
||||
RedesignCompatContext,
|
||||
RowButton,
|
||||
SceneLoadingIndicator,
|
||||
SearchField,
|
||||
SegmentedControl,
|
||||
SegmentedControlPages,
|
||||
Slider,
|
||||
Stack,
|
||||
StepModal,
|
||||
StickyContext,
|
||||
StickyHeader,
|
||||
StickyWrapper,
|
||||
TableCheckboxRow,
|
||||
TableRadioGroup,
|
||||
TableRadioRow,
|
||||
TableRow,
|
||||
TableRowGroup,
|
||||
TableRowGroupTitle,
|
||||
TableRowIcon,
|
||||
TableSwitchRow,
|
||||
Tabs,
|
||||
TextArea,
|
||||
TextField,
|
||||
TextInput,
|
||||
Toast,
|
||||
dismissAlerts,
|
||||
getHeaderBackButton,
|
||||
getHeaderCloseButton,
|
||||
getHeaderConditionalBackButton,
|
||||
getHeaderNoTitle,
|
||||
getHeaderTextButton,
|
||||
hideContextMenu,
|
||||
navigatorShouldCrossfade,
|
||||
openAlert,
|
||||
useAccessibilityNativeStackOptions,
|
||||
useAndroidNavScrim,
|
||||
useCoachmark,
|
||||
useFloatingActionButtonScroll,
|
||||
useFloatingActionButtonState,
|
||||
useNativeStackNavigation,
|
||||
useNavigation,
|
||||
useNavigationTheme,
|
||||
useNavigatorBackPressHandler,
|
||||
useNavigatorScreens,
|
||||
useNavigatorShouldCrossfade,
|
||||
useSegmentedControlState,
|
||||
useStackNavigation,
|
||||
useTabNavigation,
|
||||
useTooltip
|
||||
} = Redesign;
|
||||
|
||||
export const {
|
||||
AccessibilityAnnouncer,
|
||||
AccessibilityPreferencesContext,
|
||||
AccessibilityView,
|
||||
AccessibilityViewAnimated,
|
||||
ActionSheet,
|
||||
ActionSheetCloseButton,
|
||||
ActionSheetContentContainer,
|
||||
ActionSheetIconHeader,
|
||||
ActionSheetPresenter,
|
||||
ActionSheetRow,
|
||||
ActionSheetSwitchRow,
|
||||
ActionSheetTitleHeader,
|
||||
AnimatedEnterExitItem,
|
||||
BottomSheetTextInput,
|
||||
Dialog,
|
||||
DisableCustomTheme,
|
||||
Menu,
|
||||
MenuGroup,
|
||||
MenuItem,
|
||||
MenuPopout,
|
||||
RootThemeContextProvider,
|
||||
SimpleActionSheet,
|
||||
Text,
|
||||
TextStyleSheet,
|
||||
ThemeContextFlags,
|
||||
ThemeContextForLegacyStyles,
|
||||
ThemeContextProvider,
|
||||
TransitionGroup,
|
||||
TransitionItem,
|
||||
TransitionStates,
|
||||
UseThemeContext,
|
||||
WCAGContrastRatios,
|
||||
createLegacyClassComponentStyles,
|
||||
createNativeStyleProperties,
|
||||
createStyleProperties,
|
||||
createStyles,
|
||||
darkenColor,
|
||||
experimental_createToken,
|
||||
getContrastingColor,
|
||||
getSemanticColorContextFromThemeContext,
|
||||
hasThemeFlag,
|
||||
setColorOpacity,
|
||||
setThemeFlag,
|
||||
showSimpleActionSheet,
|
||||
useLegacyClassComponentStyles,
|
||||
useThemeContext,
|
||||
useToken
|
||||
} = CompatfulRedesign;
|
||||
2
src/lib/ui/components/discord/index.ts
Normal file
2
src/lib/ui/components/discord/index.ts
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
export * as Forms from "./Forms";
|
||||
export * as Redesign from "./Redesign";
|
||||
|
|
@ -1,16 +1,17 @@
|
|||
import { findByDisplayName, findByName, findByProps } from "@metro/filters";
|
||||
|
||||
export * as discord from "./discord";
|
||||
|
||||
// Discord
|
||||
export const Forms = findByProps("Form", "FormSection");
|
||||
export const General = findByProps("Button", "Text", "View");
|
||||
export const Alert = findByDisplayName("FluxContainer(Alert)");
|
||||
export const Button = findByProps("Looks", "Colors", "Sizes") as React.ComponentType<any> & { Looks: any, Colors: any, Sizes: any };
|
||||
export const Button = findByProps("Looks", "Colors", "Sizes") as React.ComponentType<any> & { Looks: any, Colors: any, Sizes: any; };
|
||||
export const HelpMessage = findByName("HelpMessage");
|
||||
// React Native's included SafeAreaView only adds padding on iOS.
|
||||
export const SafeAreaView = findByProps("useSafeAreaInsets").SafeAreaView as any;
|
||||
|
||||
// Vendetta
|
||||
export { default as Codeblock } from "@ui/components/Codeblock";
|
||||
export { default as ContextMenu } from "@ui/components/ContextMenu";
|
||||
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,4 +1,5 @@
|
|||
export * as alerts from "./alerts";
|
||||
export * as color from "./color";
|
||||
export * as components from "./components";
|
||||
export * as styles from "./styles";
|
||||
export * as toasts from "./toasts";
|
||||
|
|
|
|||
|
|
@ -3,20 +3,18 @@ import { after } from "@lib/api/patcher";
|
|||
import { toggleSafeMode } from "@lib/debug";
|
||||
import { settings } from "@lib/settings";
|
||||
import { ButtonColors } from "@lib/utils/types";
|
||||
import { ReactNative as RN, stylesheet } from "@metro/common";
|
||||
import { findByName, findByProps, findByStoreName } from "@metro/filters";
|
||||
import { findByName, findByProps } from "@metro/filters";
|
||||
import { semanticColors } from "@ui/color";
|
||||
import { Button, Codeblock, ErrorBoundary as _ErrorBoundary, SafeAreaView } from "@ui/components";
|
||||
import { createThemedStyleSheet, TextStyleSheet } from "@ui/styles";
|
||||
import { Text, View } from "react-native";
|
||||
|
||||
const ErrorBoundary = findByName("ErrorBoundary");
|
||||
|
||||
// Let's just pray they have this.
|
||||
const { BadgableTabBar } = findByProps("BadgableTabBar");
|
||||
|
||||
const ThemeStore = findByStoreName("ThemeStore");
|
||||
|
||||
const { TextStyleSheet } = findByProps("TextStyleSheet");
|
||||
const styles = stylesheet.createThemedStyleSheet({
|
||||
const styles = createThemedStyleSheet({
|
||||
container: {
|
||||
flex: 1,
|
||||
backgroundColor: semanticColors.BACKGROUND_PRIMARY,
|
||||
|
|
@ -85,22 +83,22 @@ export default () => after("render", ErrorBoundary.prototype, function (this: an
|
|||
return (
|
||||
<_ErrorBoundary>
|
||||
<SafeAreaView style={styles.container}>
|
||||
<RN.View style={styles.header}>
|
||||
<View style={styles.header}>
|
||||
<ret.props.Illustration style={{ transform: [{ scale: 0.6 }], marginLeft: -40, marginRight: -80 }} />
|
||||
<RN.View style={{ flex: 2, paddingLeft: 24 }}>
|
||||
<RN.Text style={styles.headerTitle}>{ret.props.title}</RN.Text>
|
||||
<RN.Text style={styles.headerDescription}>{ret.props.body}</RN.Text>
|
||||
</RN.View>
|
||||
</RN.View>
|
||||
<RN.View style={{ flex: 6 }}>
|
||||
<RN.View style={{ paddingBottom: 8 }}>
|
||||
<View style={{ flex: 2, paddingLeft: 24 }}>
|
||||
<Text style={styles.headerTitle}>{ret.props.title}</Text>
|
||||
<Text style={styles.headerDescription}>{ret.props.body}</Text>
|
||||
</View>
|
||||
</View>
|
||||
<View style={{ flex: 6 }}>
|
||||
<View style={{ paddingBottom: 8 }}>
|
||||
{/* Are errors caught by ErrorBoundary guaranteed to have the component stack? */}
|
||||
<BadgableTabBar
|
||||
tabs={tabs}
|
||||
activeTab={this.state.activeTab}
|
||||
onTabSelected={(tab: string) => { this.setState({ activeTab: tab }); }}
|
||||
/>
|
||||
</RN.View>
|
||||
</View>
|
||||
<Codeblock
|
||||
selectable
|
||||
style={{ flex: 1, textAlignVertical: "top" }}
|
||||
|
|
@ -111,8 +109,8 @@ export default () => after("render", ErrorBoundary.prototype, function (this: an
|
|||
*/}
|
||||
{tabData?.trimWhitespace ? errorText.split("\n").filter(i => i.length !== 0).map(i => i.trim()).join("\n") : errorText}
|
||||
</Codeblock>
|
||||
</RN.View>
|
||||
<RN.View style={styles.footer}>
|
||||
</View>
|
||||
<View style={styles.footer}>
|
||||
{buttons.map(button => {
|
||||
const buttonIndex = buttons.indexOf(button) !== 0 ? 8 : 0;
|
||||
|
||||
|
|
@ -124,7 +122,7 @@ export default () => after("render", ErrorBoundary.prototype, function (this: an
|
|||
style={DeviceManager.isTablet ? { flex: `0.${buttons.length}`, marginLeft: buttonIndex } : { marginTop: buttonIndex }}
|
||||
/>;
|
||||
})}
|
||||
</RN.View>
|
||||
</View>
|
||||
</SafeAreaView>
|
||||
</_ErrorBoundary>
|
||||
);
|
||||
|
|
|
|||
30
src/lib/ui/styles.ts
Normal file
30
src/lib/ui/styles.ts
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
import { isSemanticColor, resolveSemanticColor } from "@ui/color";
|
||||
import { CompatfulRedesign } from "@ui/components/discord/Redesign";
|
||||
import { DiscordTextStyles } from "@ui/types";
|
||||
import { ImageStyle, StyleSheet, TextStyle, ViewStyle } from "react-native";
|
||||
|
||||
type NamedStyles<T> = { [P in keyof T]: ViewStyle | TextStyle | ImageStyle };
|
||||
|
||||
export const { TextStyleSheet } = CompatfulRedesign as unknown as {
|
||||
TextStyleSheet: { [key in DiscordTextStyles]: TextStyle; };
|
||||
};
|
||||
|
||||
export function createStyles<T extends NamedStyles<T>>(sheet: T | ((props: any) => T)): () => T {
|
||||
return CompatfulRedesign.createStyles(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
|
||||
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 typeof res === "symbol" && isSemanticColor(res) ? resolveSemanticColor(res) : res;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return sheet;
|
||||
}
|
||||
|
|
@ -1,6 +1,8 @@
|
|||
import { Strings } from "@core/i18n";
|
||||
import { getAssetIDByName } from "@lib/api/assets";
|
||||
import { ReactNative, toasts } from "@metro/common";
|
||||
import { toasts } from "@metro/common";
|
||||
import { findByProps } from "@metro/filters";
|
||||
import { Platform } from "react-native";
|
||||
|
||||
const { uuid4 } = findByProps("uuid4");
|
||||
|
||||
|
|
@ -13,9 +15,9 @@ export const showToast = (content: string, asset?: number) => toasts.open({
|
|||
icon: asset,
|
||||
});
|
||||
|
||||
showToast.showCopyToClipboard = (message = "Copied plugin URL to clipboard.") => {
|
||||
showToast.showCopyToClipboard = (message = Strings.TOASTS_COPIED_TO_CLIPBOARD) => {
|
||||
// On Android, only show toast for 12 and lower
|
||||
if (ReactNative.Platform.OS !== "android" || ReactNative.Platform.Version <= 32) {
|
||||
if (Platform.OS !== "android" || Platform.Version <= 32) {
|
||||
showToast(message, getAssetIDByName("toast_copy_link"));
|
||||
}
|
||||
};
|
||||
|
|
|
|||
163
src/lib/ui/types.ts
Normal file
163
src/lib/ui/types.ts
Normal file
|
|
@ -0,0 +1,163 @@
|
|||
import { OptionalKeys } from "@lib/utils/types";
|
||||
|
||||
export type DiscordTextStyles = OptionalKeys<[
|
||||
"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"
|
||||
]>;
|
||||
|
||||
export type RedesignModuleKeys = OptionalKeys<[
|
||||
"AlertActionButton",
|
||||
"AlertModal",
|
||||
"AlertModalContainer",
|
||||
"AvatarDuoPile",
|
||||
"AvatarPile",
|
||||
"BACKDROP_OPAQUE_MAX_OPACITY",
|
||||
"Backdrop",
|
||||
"Button",
|
||||
"Card",
|
||||
"ContextMenu",
|
||||
"ContextMenuContainer",
|
||||
"FauxHeader",
|
||||
"FloatingActionButton",
|
||||
"GhostInput",
|
||||
"GuildIconPile",
|
||||
"HeaderActionButton",
|
||||
"HeaderButton",
|
||||
"HeaderSubmittingIndicator",
|
||||
"IconButton",
|
||||
"Input",
|
||||
"InputButton",
|
||||
"InputContainer",
|
||||
"LayerContext",
|
||||
"LayerScope",
|
||||
"Modal",
|
||||
"ModalActionButton",
|
||||
"ModalContent",
|
||||
"ModalDisclaimer",
|
||||
"ModalFloatingAction",
|
||||
"ModalFloatingActionSpacer",
|
||||
"ModalFooter",
|
||||
"ModalScreen",
|
||||
"ModalStepIndicator",
|
||||
"NAV_BAR_HEIGHT",
|
||||
"NAV_BAR_HEIGHT_MULTILINE",
|
||||
"Navigator",
|
||||
"NavigatorHeader",
|
||||
"NavigatorScreen",
|
||||
"Pile",
|
||||
"PileOverflow",
|
||||
"RedesignCompat",
|
||||
"RedesignCompatContext",
|
||||
"RowButton",
|
||||
"STATUS_BAR_HEIGHT",
|
||||
"SceneLoadingIndicator",
|
||||
"SearchField",
|
||||
"SegmentedControl",
|
||||
"SegmentedControlPages",
|
||||
"Slider",
|
||||
"Stack",
|
||||
"StepModal",
|
||||
"StickyContext",
|
||||
"StickyHeader",
|
||||
"StickyWrapper",
|
||||
"TABLE_ROW_CONTENT_HEIGHT",
|
||||
"TABLE_ROW_HEIGHT",
|
||||
"TableCheckboxRow",
|
||||
"TableRadioGroup",
|
||||
"TableRadioRow",
|
||||
"TableRow",
|
||||
"TableRowGroup",
|
||||
"TableRowGroupTitle",
|
||||
"TableRowIcon",
|
||||
"TableSwitchRow",
|
||||
"Tabs",
|
||||
"TextArea",
|
||||
"TextField",
|
||||
"TextInput",
|
||||
"Toast",
|
||||
"dismissAlerts",
|
||||
"getHeaderBackButton",
|
||||
"getHeaderCloseButton",
|
||||
"getHeaderConditionalBackButton",
|
||||
"getHeaderNoTitle",
|
||||
"getHeaderTextButton",
|
||||
"hideContextMenu",
|
||||
"navigatorShouldCrossfade",
|
||||
"openAlert",
|
||||
"useAccessibilityNativeStackOptions",
|
||||
"useAndroidNavScrim",
|
||||
"useCoachmark",
|
||||
"useFloatingActionButtonScroll",
|
||||
"useFloatingActionButtonState",
|
||||
"useNativeStackNavigation",
|
||||
"useNavigation",
|
||||
"useNavigationTheme",
|
||||
"useNavigatorBackPressHandler",
|
||||
"useNavigatorScreens",
|
||||
"useNavigatorShouldCrossfade",
|
||||
"useSegmentedControlState",
|
||||
"useStackNavigation",
|
||||
"useTabNavigation",
|
||||
"useTooltip"
|
||||
]>;
|
||||
|
|
@ -1,3 +1,7 @@
|
|||
|
||||
export type StringUnion<T> = T | (string & Record<any, unknown>);
|
||||
export type OptionalKeys<T extends Array<string>> = StringUnion<T[number]>;
|
||||
|
||||
export interface Author {
|
||||
name: string;
|
||||
id?: string;
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import * as utils from "@lib/utils";
|
|||
function createBunnyObject(unloads: any[]) {
|
||||
return {
|
||||
api: {
|
||||
...api,
|
||||
commands: { ...api.commands },
|
||||
native: {
|
||||
fs: { ...api.native.fs },
|
||||
|
|
@ -27,26 +28,39 @@ function createBunnyObject(unloads: any[]) {
|
|||
}
|
||||
},
|
||||
managers: {
|
||||
...managers,
|
||||
plugins: { ...managers.plugins },
|
||||
themes: { ...managers.themes }
|
||||
},
|
||||
metro: {
|
||||
...metro,
|
||||
common: { ...metro.common },
|
||||
filters: { ...metro.filters },
|
||||
...metro.filters
|
||||
},
|
||||
ui: {
|
||||
components: { ...ui.components },
|
||||
...ui,
|
||||
components: {
|
||||
...ui.components,
|
||||
discord: {
|
||||
...ui.components.discord,
|
||||
Forms: { ...ui.components.discord.Forms },
|
||||
Redesign: { ...ui.components.discord.Redesign }
|
||||
}
|
||||
},
|
||||
alerts: { ...ui.alerts },
|
||||
color: { ...ui.color },
|
||||
toasts: { ...ui.toasts },
|
||||
styles: { ...ui.styles }
|
||||
},
|
||||
utils: {
|
||||
...utils,
|
||||
constants: { ...utils.constants },
|
||||
types: { ...utils.types },
|
||||
logger: { ...utils.logger }
|
||||
logger: { ...utils.logger },
|
||||
},
|
||||
settings: {
|
||||
...settings,
|
||||
settings: settings.settings,
|
||||
loaderConfig: { ...settings.loaderConfig }
|
||||
},
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@
|
|||
"@core/*": ["src/core/*"],
|
||||
"@metro/*": ["src/lib/metro/*"],
|
||||
"@ui/*": ["src/lib/ui/*"],
|
||||
"@types": ["src/utils/types.ts"],
|
||||
"@types": ["src/lib/utils/types.ts"],
|
||||
"bunny-build": ["src/modules.d.ts"]
|
||||
},
|
||||
"plugins": [
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue