i18n: bad implementation
This commit is contained in:
parent
cef960bc2d
commit
2ff46796bd
19 changed files with 212 additions and 96 deletions
|
|
@ -65,7 +65,7 @@ try {
|
|||
console.log(`swc plugin: time-string="${timeString}"`);
|
||||
})
|
||||
|
||||
build.onLoad({ filter: /\.[jt]sx?/ }, async args => {
|
||||
build.onLoad({ filter: /\.[jt]sx?$/ }, async args => {
|
||||
const result = await swc.transformFile(args.path, {
|
||||
jsc: {
|
||||
externalHelpers: true,
|
||||
|
|
|
|||
70
src/lib/i18n/default.json
Normal file
70
src/lib/i18n/default.json
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
{
|
||||
"ACTIONS": "Actions",
|
||||
"ARE_YOU_SURE_TO_CLEAR_DATA": "Are you sure you wish to clear the data of {name}?",
|
||||
"ARE_YOU_SURE_TO_DELETE_PLUGIN": "Are you sure you wish to delete {name}? This will clear all of the plugin's data.",
|
||||
"ARE_YOU_SURE_TO_DELETE_THEME": "Are you sure you wish to delete {name}?",
|
||||
"BRAND": "Brand",
|
||||
"BUNNY": "Bunny",
|
||||
"BUNNY_URL": "Bunny URL",
|
||||
"BYTECODE": "Bytecode",
|
||||
"CANCEL": "Cancel",
|
||||
"CLEAR": "Clear",
|
||||
"CLEAR_DATA": "Clear data",
|
||||
"CLEAR_DATA_FAILED": "Failed to clear data for {name}!",
|
||||
"CLEAR_DATA_SUCCESSFUL": "Cleared data for {name}.",
|
||||
"CODENAME": "Codename",
|
||||
"CONFIRMATION_LINK_IS_A_TYPE": "This link is a {urlType}, would you like to install it?",
|
||||
"COPY_URL": "Copy URL",
|
||||
"DELETE": "Delete",
|
||||
"DEVELOPER": "Developer",
|
||||
"DEVELOPER_SETTINGS": "Developer Settings",
|
||||
"DISABLE_THEME": "Disable Theme",
|
||||
"DISABLE_UPDATES": "Disable updates",
|
||||
"DISCORD_SERVER": "Discord Server",
|
||||
"ENABLE_UPDATES": "Enable updates",
|
||||
"GENERAL": "General",
|
||||
"GITHUB": "GitHub",
|
||||
"HOLD_UP": "Hold Up",
|
||||
"INFO": "Info",
|
||||
"INSTALL": "Install",
|
||||
"INSTALL_PLUGIN": "Install Plugin",
|
||||
"INSTALL_THEME": "Install Theme",
|
||||
"INSTALL_TITLE": "Install {title}",
|
||||
"LINKS": "Links",
|
||||
"LOADER": "Loader",
|
||||
"MACHINE_ID": "Machine ID",
|
||||
"MANUFACTURER": "Manufacturer",
|
||||
"MODAL_THEME_REFETCHED": "Theme refetched",
|
||||
"MODAL_THEME_REFETCHED_DESC": "A reload is required to see the changes. Do you want to reload now?",
|
||||
"MODAL_UNPROXIED_PLUGIN_DESC": "The plugin you are trying to install has not been proxied/verified by Vendetta staff. Are you sure you want to continue?",
|
||||
"MODAL_UNPROXIED_PLUGIN_HEADER": "Unproxied Plugin",
|
||||
"OPEN_IN_BROWSER": "Open in Browser",
|
||||
"OPERATING_SYSTEM": "Operating System",
|
||||
"OVERFLOW_PLUGIN_SETTINGS": "Plugin settings",
|
||||
"PHONE_MODEL": "Model",
|
||||
"PLATFORM": "Platform",
|
||||
"PLUGINS": "Plugins",
|
||||
"REFETCH": "Refetch",
|
||||
"RELOAD": "Reload",
|
||||
"RELOAD_DISCORD": "Reload Discord",
|
||||
"RELOAD_IN_NORMAL_MODE": "Reload in Normal Mode",
|
||||
"RELOAD_IN_NORMAL_MODE_DESC": "This will reload Discord normally",
|
||||
"RELOAD_IN_SAFE_MODE": "Reload in Safe Mode",
|
||||
"RELOAD_IN_SAFE_MODE_DESC": "This will reload Discord without loading addons",
|
||||
"RETRY": "Retry",
|
||||
"SAFE_MODE": "Safe Mode",
|
||||
"SAFE_MODE_NOTICE_PLUGINS": "You are in Safe Mode, so plugins cannot be loaded. Disable any misbehaving plugins, then return to Normal Mode from the General settings page.",
|
||||
"SAFE_MODE_NOTICE_THEMES": "You are in Safe Mode, meaning themes have been temporarily disabled.",
|
||||
"SAFE_MODE_NOTICE_THEMES_EXTEND": "If a theme appears to be causing the issue, you can press below to disable it persistently.",
|
||||
"SEARCH": "Search",
|
||||
"THEME_REFETCH_FAILED": "Failed to refetch theme!",
|
||||
"THEME_REFETCH_SUCCESSFUL": "Successfully refetched theme.",
|
||||
"THEMES": "Themes",
|
||||
"THEMES_RELOAD_FOR_CHANGES": "Reload the app to fully apply changes!",
|
||||
"TOASTS_INSTALLED_PLUGIN": "Installed plugin",
|
||||
"TOASTS_SUCCESSFULLY_INSTALLED": "Successfully installed",
|
||||
"UH_OH": "Uh oh.",
|
||||
"UNINSTALL": "Uninstall",
|
||||
"UNINSTALL_TITLE": "Uninstall {title}",
|
||||
"VERSIONS": "Versions"
|
||||
}
|
||||
33
src/lib/i18n/index.ts
Normal file
33
src/lib/i18n/index.ts
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
import langDefault from "./default.json";
|
||||
|
||||
type I18nKey = keyof typeof langDefault;
|
||||
|
||||
export const Strings = new Proxy({}, {
|
||||
get: (_t, prop: keyof typeof langDefault) => {
|
||||
return langDefault[prop];
|
||||
}
|
||||
}) as Record<I18nKey, string>;
|
||||
|
||||
export function formatString(key: I18nKey, val: Record<string, any>) {
|
||||
const str = Strings[key];
|
||||
|
||||
return str.replaceAll(/{(.*)}/g, (_, cap) => {
|
||||
return val[cap] ?? cap;
|
||||
});
|
||||
}
|
||||
|
||||
export function formatStringSplit(key: I18nKey, val: Record<string, any>) {
|
||||
const str = Strings[key];
|
||||
|
||||
const splitted = str.split(/({[^}]+})/);
|
||||
|
||||
for (const inp in val) {
|
||||
for (let i = 0; i < splitted.length; i++) {
|
||||
if (splitted[i] === `{${inp}}`) {
|
||||
splitted[i] = val[inp];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return splitted;
|
||||
}
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
import { Strings } from "@/lib/i18n";
|
||||
import { React, ReactNative as RN, stylesheet } from "@metro/common";
|
||||
import { Forms, Button, Codeblock } from "@ui/components";
|
||||
|
||||
|
|
@ -36,14 +37,14 @@ export default class ErrorBoundary extends React.Component<ErrorBoundaryProps, E
|
|||
|
||||
return (
|
||||
<RN.ScrollView style={styles.view}>
|
||||
<Forms.FormText style={styles.title}>Uh oh.</Forms.FormText>
|
||||
<Forms.FormText style={styles.title}>{Strings.UH_OH}</Forms.FormText>
|
||||
<Codeblock selectable style={{ marginBottom: 5 }}>{this.state.errText}</Codeblock>
|
||||
<Button
|
||||
color={Button.Colors.RED}
|
||||
size={Button.Sizes.MEDIUM}
|
||||
look={Button.Looks.FILLED}
|
||||
onPress={() => this.setState({ hasErr: false, errText: undefined })}
|
||||
text="Retry"
|
||||
text={Strings.RETRY}
|
||||
/>
|
||||
</RN.ScrollView>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import { ReactNative as RN } from "@metro/common";
|
|||
import { findByProps } from "@metro/filters";
|
||||
import ErrorBoundary from "@ui/components/ErrorBoundary";
|
||||
import { getAssetIDByName } from "@ui/assets";
|
||||
import { Strings } from "@/lib/i18n";
|
||||
|
||||
const { TextInput } = findByProps("TableRow");
|
||||
|
||||
|
|
@ -28,7 +29,7 @@ export default ({ onChangeText, placeholder, style }: SearchProps) => {
|
|||
<RN.View style={style}>
|
||||
<TextInput isClearable grow
|
||||
leadingIcon={SearchIcon}
|
||||
placeholder={placeholder ?? "Search"}
|
||||
placeholder={placeholder ?? Strings.SEARCH}
|
||||
onChange={onChange}
|
||||
returnKeyType={"search"}
|
||||
size="md"
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import { getAssetIDByName } from "@ui/assets";
|
|||
import { showToast } from "@ui/toasts";
|
||||
import { useProxy } from "@/lib/storage";
|
||||
import { isThemeSupported } from "@/lib/loader";
|
||||
import { Strings, formatStringSplit } from "@/lib/i18n";
|
||||
|
||||
type PostType = "Plugin" | "Theme";
|
||||
|
||||
|
|
@ -94,7 +95,7 @@ const actionSheetPatch = () => after("default", ForumPostLongPressActionSheet, (
|
|||
actions.unshift(<ActionsSection key="install">
|
||||
<ActionSheetRow
|
||||
icon={<ActionSheetRow.Icon source={getAssetIDByName(installed ? "ic_message_delete" : "DownloadIcon")} />}
|
||||
label={`${installed ? "Uninstall" : "Install"} ${postType}`}
|
||||
label={formatStringSplit(installed ? "UNINSTALL_TITLE" : "INSTALL_TITLE", { title: postType })}
|
||||
disabled={loading}
|
||||
onPress={installOrRemove}
|
||||
/>
|
||||
|
|
@ -112,7 +113,7 @@ const installButtonPatch = () => after("MostCommonForumPostReaction", forumReact
|
|||
loading={loading}
|
||||
disabled={loading}
|
||||
variant={installed ? "danger" : "primary"}
|
||||
text={installed ? "Uninstall" : "Install"}
|
||||
text={installed ? Strings.UNINSTALL : Strings.INSTALL}
|
||||
onPress={installOrRemove}
|
||||
icon={getAssetIDByName(installed ? "ic_message_delete" : "DownloadIcon")}
|
||||
style={{ marginLeft: 8 }}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import { showConfirmationAlert } from "@ui/alerts";
|
|||
import { getAssetIDByName } from "@ui/assets";
|
||||
import { showToast } from "@ui/toasts";
|
||||
import { isThemeSupported } from "@/lib/loader";
|
||||
import { Strings, formatString, formatStringSplit } from "@lib/i18n";
|
||||
|
||||
const showSimpleActionSheet = find((m) => m?.showSimpleActionSheet && !Object.getOwnPropertyDescriptor(m, "showSimpleActionSheet")?.get);
|
||||
const handleClick = findByProps("handleClick");
|
||||
|
|
@ -28,7 +29,7 @@ function typeFromUrl(url: string) {
|
|||
function installWithToast(type: "Plugin" | "Theme", url: string) {
|
||||
(type === "Plugin" ? installPlugin : installTheme)(url)
|
||||
.then(() => {
|
||||
showToast("Successfully installed", getAssetIDByName("Check"));
|
||||
showToast(Strings.TOASTS_SUCCESSFULLY_INSTALLED, getAssetIDByName("Check"));
|
||||
})
|
||||
.catch((e: Error) => {
|
||||
showToast(e.message, getAssetIDByName("Small"));
|
||||
|
|
@ -50,7 +51,7 @@ export default () => {
|
|||
if (!urlType) return;
|
||||
|
||||
options.push({
|
||||
label: `Install ${urlType}`,
|
||||
label: formatString("INSTALL_TITLE", { urlType }),
|
||||
onPress: () => installWithToast(urlType, url),
|
||||
});
|
||||
})
|
||||
|
|
@ -67,12 +68,12 @@ export default () => {
|
|||
if (urlType === "Theme" && getChannel(getChannelId())?.parent_id !== THEMES_CHANNEL_ID) return orig.apply(this, args);
|
||||
|
||||
showConfirmationAlert({
|
||||
title: "Hold Up",
|
||||
content: ["This link is a ", <RN.Text style={TextStyleSheet["text-md/semibold"]}>{urlType}</RN.Text>, ", would you like to install it?"],
|
||||
title: Strings.HOLD_UP,
|
||||
content: formatStringSplit("CONFIRMATION_LINK_IS_A_TYPE", { urlType: <RN.Text style={TextStyleSheet["text-md/semibold"]}>{urlType}</RN.Text> }),
|
||||
onConfirm: () => installWithToast(urlType, url),
|
||||
confirmText: "Install",
|
||||
cancelText: "Cancel",
|
||||
secondaryConfirmText: "Open in Browser",
|
||||
confirmText: Strings.INSTALL,
|
||||
cancelText: Strings.CANCEL,
|
||||
secondaryConfirmText: Strings.OPEN_IN_BROWSER,
|
||||
onConfirmSecondary: () => openURL(url),
|
||||
});
|
||||
})
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import { findByProps } from "@/lib/metro/filters";
|
|||
import { HTTP_REGEX_MULTI } from "@/lib/constants";
|
||||
import { showInputAlert } from "@/ui/alerts";
|
||||
import { getAssetIDByName } from "@/ui/assets";
|
||||
import { Strings, formatString } from "@/lib/i18n";
|
||||
|
||||
interface AddonPageProps<T> {
|
||||
title: string;
|
||||
|
|
@ -40,7 +41,7 @@ export default function AddonPage<T>({ title, fetchFunction, items, safeModeMess
|
|||
<Search
|
||||
style={{ marginBottom: 15 }}
|
||||
onChangeText={(v: string) => setSearch(v.toLowerCase())}
|
||||
placeholder="Search"
|
||||
placeholder={Strings.SEARCH}
|
||||
/>
|
||||
</>}
|
||||
onScroll={e => {
|
||||
|
|
@ -55,19 +56,19 @@ export default function AddonPage<T>({ title, fetchFunction, items, safeModeMess
|
|||
renderItem={({ item, index }) => <CardComponent item={item} index={index} />}
|
||||
/>
|
||||
<FloatingActionButton
|
||||
text={`Install ${title}`}
|
||||
text={formatString("INSTALL_TITLE", { title })}
|
||||
icon={getAssetIDByName("DownloadIcon")}
|
||||
state={{ collapseText }}
|
||||
onPress={() => {
|
||||
// from ./InstallButton.tsx
|
||||
clipboard.getString().then((content) =>
|
||||
showInputAlert({
|
||||
title: `Install ${title}`,
|
||||
title: formatString("INSTALL_TITLE", { title }),
|
||||
initialValue: content.match(HTTP_REGEX_MULTI)?.[0] ?? "",
|
||||
placeholder: "https://example.com/",
|
||||
onConfirm: (input: string) => fetchFunction(input),
|
||||
confirmText: "Install",
|
||||
cancelText: "Cancel",
|
||||
confirmText: Strings.INSTALL,
|
||||
cancelText: Strings.CANCEL,
|
||||
})
|
||||
)
|
||||
}}
|
||||
|
|
|
|||
|
|
@ -4,9 +4,7 @@ import { findByProps } from "@metro/filters";
|
|||
import { getAssetIDByName } from "@ui/assets";
|
||||
import { semanticColors } from "@ui/color";
|
||||
|
||||
const { TableRow, TableRowIcon, TableSwitchRow, TableCheckboxRow, TableRowGroup, IconButton } = findByProps("TableRow");
|
||||
const { hideActionSheet } = findByProps("openLazy", "hideActionSheet");
|
||||
const { showSimpleActionSheet } = findByProps("showSimpleActionSheet");
|
||||
const { TableRow, TableRowIcon, TableSwitchRow, TableCheckboxRow, TableRowGroup } = findByProps("TableRow");
|
||||
|
||||
// TODO: These styles work weirdly. iOS has cramped text, Android with low DPI probably does too. Fix?
|
||||
const styles = stylesheet.createThemedStyleSheet({
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { HTTP_REGEX_MULTI } from "@lib/constants";
|
|||
import { showInputAlert } from "@ui/alerts";
|
||||
import { getAssetIDByName } from "@ui/assets";
|
||||
import { semanticColors } from "@ui/color";
|
||||
import { Strings } from "@/lib/i18n";
|
||||
|
||||
const styles = stylesheet.createThemedStyleSheet({
|
||||
icon: {
|
||||
|
|
@ -25,8 +26,8 @@ export default function InstallButton({ alertTitle, installFunction: fetchFuncti
|
|||
initialValue: content.match(HTTP_REGEX_MULTI)?.[0] ?? "",
|
||||
placeholder: "https://example.com/",
|
||||
onConfirm: (input: string) => fetchFunction(input),
|
||||
confirmText: "Install",
|
||||
cancelText: "Cancel",
|
||||
confirmText: Strings.INSTALL,
|
||||
cancelText: Strings.CANCEL,
|
||||
})
|
||||
)
|
||||
}>
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import { showConfirmationAlert } from "@ui/alerts";
|
|||
import Card, { CardWrapper } from "@ui/settings/components/Card";
|
||||
import { useProxy } from "@/lib/storage";
|
||||
import { ButtonColors } from "@/lib/types";
|
||||
import { Strings, formatString } from "@/lib/i18n";
|
||||
|
||||
async function stopThenStart(plugin: BunnyPlugin, callback: Function) {
|
||||
if (plugin.enabled) stopPlugin(plugin.id, false);
|
||||
|
|
@ -43,7 +44,7 @@ export default function PluginCard({ item: plugin, index }: CardWrapper<BunnyPlu
|
|||
overflowTitle={plugin.manifest.name}
|
||||
overflowActions={[
|
||||
...(settings ? [{
|
||||
label: "Plugin settings",
|
||||
label: Strings.OVERFLOW_PLUGIN_SETTINGS,
|
||||
icon: "settings",
|
||||
onPress: () => navigation.push("VendettaCustomPage", {
|
||||
title: plugin.manifest.name,
|
||||
|
|
@ -52,7 +53,7 @@ export default function PluginCard({ item: plugin, index }: CardWrapper<BunnyPlu
|
|||
}] : []),
|
||||
{
|
||||
icon: "ic_sync_24px",
|
||||
label: "Refetch",
|
||||
label: Strings.REFETCH,
|
||||
onPress: async () => {
|
||||
stopThenStart(plugin, () => {
|
||||
fetchPlugin(plugin.id).then(async () => {
|
||||
|
|
@ -65,7 +66,7 @@ export default function PluginCard({ item: plugin, index }: CardWrapper<BunnyPlu
|
|||
},
|
||||
{
|
||||
icon: "copy",
|
||||
label: "Copy URL",
|
||||
label: Strings.COPY_URL,
|
||||
onPress: () => {
|
||||
clipboard.setString(plugin.id);
|
||||
showToast.showCopyToClipboard();
|
||||
|
|
@ -73,7 +74,7 @@ export default function PluginCard({ item: plugin, index }: CardWrapper<BunnyPlu
|
|||
},
|
||||
{
|
||||
icon: "ic_download_24px",
|
||||
label: plugin.update ? "Disable updates" : "Enable updates",
|
||||
label: plugin.update ? Strings.DISABLE_UPDATES : Strings.ENABLE_UPDATES,
|
||||
onPress: () => {
|
||||
plugin.update = !plugin.update;
|
||||
showToast(`${plugin.update ? "Enabled" : "Disabled"} updates for ${plugin.manifest.name}.`, getAssetIDByName("toast_image_saved"));
|
||||
|
|
@ -81,21 +82,21 @@ export default function PluginCard({ item: plugin, index }: CardWrapper<BunnyPlu
|
|||
},
|
||||
{
|
||||
icon: "ic_duplicate",
|
||||
label: "Clear data",
|
||||
label: Strings.CLEAR_DATA,
|
||||
isDestructive: true,
|
||||
onPress: () => showConfirmationAlert({
|
||||
title: "Wait!",
|
||||
content: `Are you sure you wish to clear the data of ${plugin.manifest.name}?`,
|
||||
confirmText: "Clear",
|
||||
cancelText: "Cancel",
|
||||
title: Strings.HOLD_UP,
|
||||
content: formatString("ARE_YOU_SURE_TO_CLEAR_DATA", { name: plugin.manifest.name }),
|
||||
confirmText: Strings.CLEAR,
|
||||
cancelText: Strings.CANCEL,
|
||||
confirmColor: ButtonColors.RED,
|
||||
onConfirm: () => {
|
||||
stopThenStart(plugin, () => {
|
||||
try {
|
||||
MMKVManager.removeItem(plugin.id);
|
||||
showToast(`Cleared data for ${plugin.manifest.name}.`, getAssetIDByName("trash"));
|
||||
showToast(formatString("CLEAR_DATA_SUCCESSFUL", { name: plugin.manifest.name }), getAssetIDByName("trash"));
|
||||
} catch {
|
||||
showToast(`Failed to clear data for ${plugin.manifest.name}!`, getAssetIDByName("Small"));
|
||||
showToast(formatString("CLEAR_DATA_FAILED", { name: plugin.manifest.name }), getAssetIDByName("Small"));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
@ -103,13 +104,13 @@ export default function PluginCard({ item: plugin, index }: CardWrapper<BunnyPlu
|
|||
},
|
||||
{
|
||||
icon: "ic_message_delete",
|
||||
label: "Delete",
|
||||
label: Strings.DELETE,
|
||||
isDestructive: true,
|
||||
onPress: () => showConfirmationAlert({
|
||||
title: "Wait!",
|
||||
content: `Are you sure you wish to delete ${plugin.manifest.name}? This will clear all of the plugin's data.`,
|
||||
confirmText: "Delete",
|
||||
cancelText: "Cancel",
|
||||
title: Strings.HOLD_UP,
|
||||
content: formatString("ARE_YOU_SURE_TO_DELETE_PLUGIN", {name: plugin.manifest.name}),
|
||||
confirmText: Strings.DELETE,
|
||||
cancelText: Strings.CANCEL,
|
||||
confirmColor: ButtonColors.RED,
|
||||
onConfirm: () => {
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import { getAssetIDByName } from "@ui/assets";
|
|||
import { getRenderableScreens } from "@ui/settings/data";
|
||||
import { ErrorBoundary, Forms } from "@ui/components";
|
||||
import settings from "@lib/settings";
|
||||
import { Strings } from "@/lib/i18n";
|
||||
|
||||
const { FormRow, FormSection, FormDivider } = Forms;
|
||||
|
||||
|
|
@ -15,7 +16,7 @@ export default function SettingsSection() {
|
|||
|
||||
return (
|
||||
<ErrorBoundary>
|
||||
<FormSection key="Vendetta" title={`Bunny${settings.safeMode?.enabled ? " (Safe Mode)" : ""}`}>
|
||||
<FormSection key="Vendetta" title={`${Strings.BUNNY}${settings.safeMode?.enabled ? ` (${Strings.SAFE_MODE})` : ""}`}>
|
||||
{screens.map((s, i) => (
|
||||
<>
|
||||
<FormRow
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import { showToast } from "@ui/toasts";
|
|||
import settings from "@lib/settings";
|
||||
import Card, { CardWrapper } from "@ui/settings/components/Card";
|
||||
import { ButtonColors } from "@/lib/types";
|
||||
import { Strings, formatString } from "@/lib/i18n";
|
||||
|
||||
async function selectAndApply(value: boolean, id: string) {
|
||||
try {
|
||||
|
|
@ -15,7 +16,7 @@ async function selectAndApply(value: boolean, id: string) {
|
|||
value ? applyTheme(themes[id]) : applyTheme(null);
|
||||
|
||||
// TODO: Implement native side reload-less & check if it's applied by 100%
|
||||
showToast("Reload the app to fully apply changes!", getAssetIDByName("yellow-alert"));
|
||||
showToast(Strings.THEMES_RELOAD_FOR_CHANGES, getAssetIDByName("yellow-alert"));
|
||||
} catch (e: any) {
|
||||
console.error("Error while selectAndApply,", e)
|
||||
}
|
||||
|
|
@ -45,29 +46,29 @@ export default function ThemeCard({ item: theme, index }: CardWrapper<Theme>) {
|
|||
overflowActions={[
|
||||
{
|
||||
icon: "ic_sync_24px",
|
||||
label: "Refetch",
|
||||
label: Strings.REFETCH,
|
||||
onPress: () => {
|
||||
fetchTheme(theme.id, theme.selected).then(() => {
|
||||
if (theme.selected) {
|
||||
showConfirmationAlert({
|
||||
title: "Theme refetched",
|
||||
content: "A reload is required to see the changes. Do you want to reload now?",
|
||||
confirmText: "Reload",
|
||||
cancelText: "Cancel",
|
||||
title: Strings.MODAL_THEME_REFETCHED,
|
||||
content: Strings.MODAL_THEME_REFETCHED_DESC,
|
||||
confirmText: Strings.RELOAD,
|
||||
cancelText: Strings.CANCEL,
|
||||
confirmColor: ButtonColors.RED,
|
||||
onConfirm: () => BundleUpdaterManager.reload(),
|
||||
})
|
||||
} else {
|
||||
showToast("Successfully refetched theme.", getAssetIDByName("toast_image_saved"));
|
||||
showToast(Strings.THEME_REFETCH_SUCCESSFUL, getAssetIDByName("toast_image_saved"));
|
||||
}
|
||||
}).catch(() => {
|
||||
showToast("Failed to refetch theme!", getAssetIDByName("Small"));
|
||||
showToast(Strings.THEME_REFETCH_FAILED, getAssetIDByName("Small"));
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
icon: "copy",
|
||||
label: "Copy URL",
|
||||
label: Strings.COPY_URL,
|
||||
onPress: () => {
|
||||
clipboard.setString(theme.id);
|
||||
showToast.showCopyToClipboard();
|
||||
|
|
@ -75,13 +76,13 @@ export default function ThemeCard({ item: theme, index }: CardWrapper<Theme>) {
|
|||
},
|
||||
{
|
||||
icon: "ic_message_delete",
|
||||
label: "Delete",
|
||||
label: Strings.DELETE,
|
||||
isDestructive: true,
|
||||
onPress: () => showConfirmationAlert({
|
||||
title: "Wait!",
|
||||
content: `Are you sure you wish to delete ${theme.data.name}?`,
|
||||
confirmText: "Delete",
|
||||
cancelText: "Cancel",
|
||||
title: Strings.HOLD_UP,
|
||||
content: formatString("ARE_YOU_SURE_TO_DELETE_THEME", { name: theme.data.name }),
|
||||
confirmText: Strings.DELETE,
|
||||
cancelText: Strings.CANCEL,
|
||||
confirmColor: ButtonColors.RED,
|
||||
onConfirm: () => {
|
||||
removeTheme(theme.id).then((wasSelected) => {
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import Developer from "@ui/settings/pages/Developer";
|
|||
import { PROXY_PREFIX } from "@/lib/constants";
|
||||
import { findByProps } from "@/lib/metro/filters";
|
||||
import { isThemeSupported } from "@/lib/loader";
|
||||
import { Strings } from "@/lib/i18n";
|
||||
|
||||
const { useSafeAreaInsets } = findByProps("useSafeAreaInsets");
|
||||
|
||||
|
|
@ -45,29 +46,29 @@ const keyMap = (screens: Screen[], data: string | ((s: Screen) => any) | null) =
|
|||
export const getScreens = (youKeys = false): Screen[] => [
|
||||
{
|
||||
key: formatKey("VendettaSettings", youKeys),
|
||||
title: "General",
|
||||
title: Strings.GENERAL,
|
||||
icon: "settings",
|
||||
render: General,
|
||||
},
|
||||
{
|
||||
key: formatKey("VendettaPlugins", youKeys),
|
||||
title: "Plugins",
|
||||
title: Strings.PLUGINS,
|
||||
icon: "debug",
|
||||
options: {
|
||||
headerRight: () => (
|
||||
<InstallButton
|
||||
alertTitle="Install Plugin"
|
||||
alertTitle={Strings.INSTALL_PLUGIN}
|
||||
installFunction={async (input) => {
|
||||
if (!input.startsWith(PROXY_PREFIX) && !settings.developerSettings)
|
||||
setImmediate(() => showConfirmationAlert({
|
||||
title: "Unproxied Plugin",
|
||||
content: "The plugin you are trying to install has not been proxied/verified by Vendetta staff. Are you sure you want to continue?",
|
||||
confirmText: "Install",
|
||||
title: Strings.MODAL_UNPROXIED_PLUGIN_HEADER,
|
||||
content: Strings.MODAL_UNPROXIED_PLUGIN_DESC,
|
||||
confirmText: Strings.INSTALL,
|
||||
onConfirm: () =>
|
||||
installPlugin(input)
|
||||
.then(() => showToast("Installed plugin", getAssetIDByName("Check")))
|
||||
.then(() => showToast(Strings.TOASTS_INSTALLED_PLUGIN, getAssetIDByName("Check")))
|
||||
.catch((x) => showToast(x?.message ?? `${x}`, getAssetIDByName("Small"))),
|
||||
cancelText: "Cancel",
|
||||
cancelText: Strings.CANCEL,
|
||||
}));
|
||||
else return await installPlugin(input);
|
||||
}}
|
||||
|
|
@ -78,18 +79,18 @@ export const getScreens = (youKeys = false): Screen[] => [
|
|||
},
|
||||
{
|
||||
key: formatKey("VendettaThemes", youKeys),
|
||||
title: "Themes",
|
||||
title: Strings.THEMES,
|
||||
icon: "ic_theme_24px",
|
||||
// TODO: bad
|
||||
shouldRender: () => isThemeSupported(),
|
||||
options: {
|
||||
headerRight: () => !settings.safeMode?.enabled && <InstallButton alertTitle="Install Theme" installFunction={installTheme} />,
|
||||
headerRight: () => !settings.safeMode?.enabled && <InstallButton alertTitle={Strings.INSTALL_THEME} installFunction={installTheme} />,
|
||||
},
|
||||
render: Themes,
|
||||
},
|
||||
{
|
||||
key: formatKey("VendettaDeveloper", youKeys),
|
||||
title: "Developer",
|
||||
title: Strings.DEVELOPER,
|
||||
icon: "ic_progress_wrench_24px",
|
||||
shouldRender: () => settings.developerSettings ?? false,
|
||||
render: Developer,
|
||||
|
|
@ -120,8 +121,8 @@ export const getYouData = () => {
|
|||
|
||||
return {
|
||||
getLayout: () => ({
|
||||
title: "Bunny",
|
||||
label: "Bunny",
|
||||
title: "Vendetta",
|
||||
label: Strings.BUNNY,
|
||||
// We can't use our keyMap function here since `settings` is an array not an object
|
||||
settings: getRenderableScreens(true).map(s => s.key)
|
||||
}),
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import settings, { loaderConfig } from "@lib/settings";
|
|||
import AssetBrowser from "@ui/settings/pages/AssetBrowser";
|
||||
import { semanticColors } from "@ui/color";
|
||||
import { getReactDevToolsProp, getReactDevToolsVersion, isLoaderConfigSupported, isReactDevToolsPreloaded } from "@/lib/loader";
|
||||
import { Strings } from "@/lib/i18n";
|
||||
|
||||
const { Stack, TableRow, TableSwitchRow, TableRowGroup, TextInput } = findByProps("TableRow");
|
||||
const { FormText } = Forms;
|
||||
|
|
@ -74,7 +75,7 @@ export default function Developer() {
|
|||
size="md"
|
||||
onChange={(v: string) => loaderConfig.customLoadUrl.url = v}
|
||||
placeholder="http://localhost:4040/vendetta.js"
|
||||
label="Bunny URL"
|
||||
label={Strings.BUNNY_URL}
|
||||
/>} />}
|
||||
{isReactDevToolsPreloaded() && <TableSwitchRow
|
||||
label="Load React DevTools"
|
||||
|
|
@ -112,7 +113,7 @@ export default function Developer() {
|
|||
options: [
|
||||
// @ts-expect-error
|
||||
// Of course, to trigger an error, we need to do something incorrectly. The below will do!
|
||||
{ label: "Bunny", onPress: () => navigation.push("VendettaCustomPage", { render: () => <undefined /> }) },
|
||||
{ label: Strings.BUNNY, onPress: () => navigation.push("VendettaCustomPage", { render: () => <undefined /> }) },
|
||||
{ label: "Discord", isDestructive: true, onPress: () => navigation.push("VendettaCustomPage", { noErrorBoundary: true }) },
|
||||
],
|
||||
})}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import settings from "@lib/settings";
|
|||
import Version from "@ui/settings/components/Version";
|
||||
import { findByProps } from "@lib/metro/filters";
|
||||
import { semanticColors } from "@ui/color";
|
||||
import { Strings } from "@/lib/i18n";
|
||||
|
||||
const { Stack, TableRow, TableSwitchRow, TableRowGroup } = findByProps("TableRow");
|
||||
const debugInfo = getDebugInfo() as any;
|
||||
|
|
@ -24,7 +25,7 @@ export default function General() {
|
|||
|
||||
const versions = [
|
||||
{
|
||||
label: "Bunny",
|
||||
label: Strings.BUNNY,
|
||||
version: debugInfo.vendetta.version,
|
||||
icon: "ic_progress_wrench_24px",
|
||||
},
|
||||
|
|
@ -44,7 +45,7 @@ export default function General() {
|
|||
icon: "mobile",
|
||||
},
|
||||
{
|
||||
label: "Bytecode",
|
||||
label: Strings.BYTECODE,
|
||||
version: debugInfo.hermes.bytecodeVersion,
|
||||
icon: "ic_server_security_24px",
|
||||
},
|
||||
|
|
@ -52,12 +53,12 @@ export default function General() {
|
|||
|
||||
const platformInfo = [
|
||||
{
|
||||
label: "Loader",
|
||||
label: Strings.LOADER,
|
||||
version: debugInfo.vendetta.loader,
|
||||
icon: "ic_download_24px",
|
||||
},
|
||||
{
|
||||
label: "Operating System",
|
||||
label: Strings.OPERATING_SYSTEM,
|
||||
version: `${debugInfo.os.name} ${debugInfo.os.version}`,
|
||||
icon: "ic_cog_24px"
|
||||
},
|
||||
|
|
@ -67,22 +68,22 @@ export default function General() {
|
|||
icon: "pencil"
|
||||
}] : []),
|
||||
{
|
||||
label: "Manufacturer",
|
||||
label: Strings.MANUFACTURER,
|
||||
version: debugInfo.device.manufacturer,
|
||||
icon: "ic_badge_staff"
|
||||
},
|
||||
{
|
||||
label: "Brand",
|
||||
label: Strings.BRAND,
|
||||
version: debugInfo.device.brand,
|
||||
icon: "ic_settings_boost_24px"
|
||||
},
|
||||
{
|
||||
label: "Model",
|
||||
label: Strings.PHONE_MODEL,
|
||||
version: debugInfo.device.model,
|
||||
icon: "ic_phonelink_24px"
|
||||
},
|
||||
{
|
||||
label: RN.Platform.select({ android: "Codename", ios: "Machine ID" })!,
|
||||
label: RN.Platform.select({ android: Strings.CODENAME, ios: Strings.MACHINE_ID })!,
|
||||
version: debugInfo.device.codename,
|
||||
icon: "ic_compose_24px"
|
||||
}
|
||||
|
|
@ -91,34 +92,34 @@ export default function General() {
|
|||
return (
|
||||
<RN.ScrollView style={styles.container}>
|
||||
<Stack style={{ paddingVertical: 24, paddingHorizontal: 12 }} spacing={24}>
|
||||
<TableRowGroup title="Links">
|
||||
<TableRowGroup title={Strings.LINKS}>
|
||||
<TableRow
|
||||
label="Discord Server"
|
||||
label={Strings.DISCORD_SERVER}
|
||||
icon={<TableRow.Icon source={getAssetIDByName("Discord")} />}
|
||||
trailing={TableRow.Arrow}
|
||||
onPress={() => url.openDeeplink(DISCORD_SERVER)}
|
||||
/>
|
||||
<TableRow
|
||||
label="GitHub"
|
||||
label={Strings.GITHUB}
|
||||
icon={<TableRow.Icon source={getAssetIDByName("img_account_sync_github_white")} />}
|
||||
trailing={TableRow.Arrow}
|
||||
onPress={() => url.openURL(GITHUB)}
|
||||
/>
|
||||
</TableRowGroup>
|
||||
<TableRowGroup title="Actions">
|
||||
<TableRowGroup title={Strings.ACTIONS}>
|
||||
<TableRow
|
||||
label="Reload Discord"
|
||||
label={Strings.RELOAD_DISCORD}
|
||||
icon={<TableRow.Icon source={getAssetIDByName("ic_message_retry")} />}
|
||||
onPress={() => RN.NativeModules.BundleUpdaterManager.reload()}
|
||||
/>
|
||||
<TableRow
|
||||
label={settings.safeMode?.enabled ? "Return to Normal Mode" : "Reload in Safe Mode"}
|
||||
subLabel={`This will reload Discord ${settings.safeMode?.enabled ? "normally." : "without loading addons."}`}
|
||||
label={settings.safeMode?.enabled ? Strings.RELOAD_IN_NORMAL_MODE : Strings.RELOAD_IN_SAFE_MODE}
|
||||
subLabel={settings.safeMode?.enabled ? Strings.RELOAD_IN_NORMAL_MODE_DESC : Strings.RELOAD_IN_SAFE_MODE_DESC}
|
||||
icon={<TableRow.Icon source={getAssetIDByName("ic_privacy_24px")} />}
|
||||
onPress={toggleSafeMode}
|
||||
/>
|
||||
<TableSwitchRow
|
||||
label="Developer Settings"
|
||||
label={Strings.DEVELOPER_SETTINGS}
|
||||
icon={<TableRow.Icon source={getAssetIDByName("ic_progress_wrench_24px")} />}
|
||||
value={settings.developerSettings}
|
||||
onValueChange={(v: boolean) => {
|
||||
|
|
@ -126,11 +127,11 @@ export default function General() {
|
|||
}}
|
||||
/>
|
||||
</TableRowGroup>
|
||||
<TableRowGroup title="Info">
|
||||
<Summary noPadding label="Versions" icon="ic_information_filled_24px">
|
||||
<TableRowGroup title={Strings.INFO}>
|
||||
<Summary noPadding label={Strings.VERSIONS} icon="ic_information_filled_24px">
|
||||
{versions.map(v => <Version padding label={v.label} version={v.version} icon={v.icon} />)}
|
||||
</Summary>
|
||||
<Summary noPadding label="Platform" icon="ic_mobile_device">
|
||||
<Summary noPadding label={Strings.PLATFORM} icon="ic_mobile_device">
|
||||
{platformInfo.map(p => <Version padding label={p.label} version={p.version} icon={p.icon} />)}
|
||||
</Summary>
|
||||
</TableRowGroup>
|
||||
|
|
|
|||
|
|
@ -3,16 +3,17 @@ import { BunnyPlugin, installPlugin, plugins } from "@lib/plugins";
|
|||
import settings from "@lib/settings";
|
||||
import AddonPage from "@ui/settings/components/AddonPage";
|
||||
import PluginCard from "@ui/settings/components/PluginCard";
|
||||
import { Strings } from "@/lib/i18n";
|
||||
|
||||
export default function Plugins() {
|
||||
useProxy(settings)
|
||||
|
||||
return (
|
||||
<AddonPage<BunnyPlugin>
|
||||
title="Plugin"
|
||||
title={Strings.PLUGINS}
|
||||
fetchFunction={installPlugin}
|
||||
items={plugins}
|
||||
safeModeMessage="You are in Safe Mode, so plugins cannot be loaded. Disable any misbehaving plugins, then return to Normal Mode from the General settings page."
|
||||
safeModeMessage={Strings.SAFE_MODE_NOTICE_PLUGINS}
|
||||
card={PluginCard}
|
||||
/>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -5,18 +5,19 @@ import settings from "@lib/settings";
|
|||
import AddonPage from "@ui/settings/components/AddonPage";
|
||||
import ThemeCard from "@ui/settings/components/ThemeCard";
|
||||
import { ButtonColors } from "@/lib/types";
|
||||
import { Strings } from "@/lib/i18n";
|
||||
|
||||
export default function Themes() {
|
||||
useProxy(settings);
|
||||
|
||||
return (
|
||||
<AddonPage<Theme>
|
||||
title={"Theme"}
|
||||
title={Strings.THEMES}
|
||||
fetchFunction={installTheme}
|
||||
items={themes}
|
||||
safeModeMessage={`You are in Safe Mode, meaning themes have been temporarily disabled.${settings.safeMode?.currentThemeId ? " If a theme appears to be causing the issue, you can press below to disable it persistently." : ""}`}
|
||||
safeModeMessage={`${Strings.SAFE_MODE_NOTICE_THEMES}${settings.safeMode?.currentThemeId ? ` ${Strings.SAFE_MODE_NOTICE_THEMES_EXTEND}` : ""}`}
|
||||
safeModeExtras={settings.safeMode?.currentThemeId ? <Button
|
||||
text="Disable Theme"
|
||||
text={Strings.DISABLE_THEME}
|
||||
color={ButtonColors.BRAND}
|
||||
size="small"
|
||||
onPress={() => {
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import { findByProps } from "@metro/filters";
|
|||
import { after, before } from "@lib/patcher";
|
||||
import { getRenderableScreens, getScreens, getYouData } from "@ui/settings/data";
|
||||
import { i18n } from "@lib/metro/common";
|
||||
import { Strings } from "@/lib/i18n";
|
||||
|
||||
export default function patchYou() {
|
||||
const patches = new Array<Function>;
|
||||
|
|
@ -44,7 +45,7 @@ function oldYouPatch(patches: Function[]) {
|
|||
ancestorRendererData: data.rendererConfigs[s.key],
|
||||
setting: s.key,
|
||||
title: data.titleConfig[s.key],
|
||||
breadcrumbs: ["Bunny"],
|
||||
breadcrumbs: [Strings.BUNNY],
|
||||
icon: data.rendererConfigs[s.key].icon,
|
||||
})),
|
||||
// .filter can be removed when dropping support for 189.3 and below (unless Discord changes things again)
|
||||
|
|
@ -78,7 +79,7 @@ function newYouPatch(patches: Function[]) {
|
|||
patches.push(before("type", settingsListComponents.SearchableSettingsList, ([{ sections }]) => manipulateSections(sections, data.getLayout())));
|
||||
|
||||
patches.push(after("getSettingListSearchResultItems", gettersModule, (_, ret) => {
|
||||
ret.forEach((s: any) => screens.some(b => b.key === s.setting) && (s.breadcrumbs = ["Bunny"]))
|
||||
ret.forEach((s: any) => screens.some(b => b.key === s.setting) && (s.breadcrumbs = [Strings.BUNNY]))
|
||||
}));
|
||||
|
||||
const oldRendererConfig = settingConstantsModule.SETTING_RENDERER_CONFIG;
|
||||
|
|
@ -94,7 +95,7 @@ function newYouPatch(patches: Function[]) {
|
|||
const isLabel = (i: any, name: string) => i?.label === name || i?.title === name;
|
||||
|
||||
function manipulateSections(sections: any[], layout: any) {
|
||||
if (!Array.isArray(sections) || sections.find((i: any) => isLabel(i, "Bunny"))) return;
|
||||
if (!Array.isArray(sections) || sections.find((i: any) => isLabel(i, Strings.BUNNY))) return;
|
||||
|
||||
// Add our settings
|
||||
const accountSettingsIndex = sections.findIndex((i: any) => isLabel(i, i18n.Messages.ACCOUNT_SETTINGS));
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue