- {t("account:title")}
+ {t("account:title")}
["t"];
@@ -14,39 +17,6 @@ export type SectionProps = {
export const WEBSITE_ACCOUNT = "https://vrchat.com/home/profile";
-export function Section({
- title,
- description,
- icon,
- danger,
- children,
-}: {
- title: string;
- description?: ReactNode;
- icon?: ReactNode;
- danger?: boolean;
- children: ReactNode;
-}) {
- return (
-
-
-
- {icon ? {icon} : null}
- {title}
-
- {description ?
{description}
: null}
-
- {children}
-
- );
-}
-
export function useAction() {
const { t } = useI18n();
const [busy, setBusy] = useState(false);
@@ -80,22 +50,6 @@ export function useAction() {
};
}
-export function Notice({ error, ok }: { error?: string | null; ok?: string | null }) {
- if (error)
- return (
-
- {error}
-
- );
- if (ok)
- return (
-
- {ok}
-
- );
- return null;
-}
-
export function ToggleRow({
label,
hint,
diff --git a/src/renderer/src/features/account/useAccountSettings.ts b/src/renderer/src/features/account/useAccountSettings.ts
index 0afa9f4..9fba324 100644
--- a/src/renderer/src/features/account/useAccountSettings.ts
+++ b/src/renderer/src/features/account/useAccountSettings.ts
@@ -1,43 +1,36 @@
-import { useCallback, useEffect, useState } from "react";
+import { useCallback, useState } from "react";
import type { AccountSettings } from "../../../../shared/types/settings";
-import { api, errorMessage } from "../../lib/api";
+import { api } from "../../lib/api";
+import { useAsync, type Async } from "../../lib/useAsync";
import { useAuth } from "../auth/AuthContext";
-type State =
- | { status: "loading" }
- | { status: "error"; message: string }
- | { status: "ready"; settings: AccountSettings };
-
export function useAccountSettings(): {
- state: State;
+ state: Async;
reload: () => void;
set: (s: AccountSettings) => void;
} {
const { status } = useAuth();
const activeId = status.state === "authenticated" ? status.user.id : null;
- const [state, setState] = useState({ status: "loading" });
+
+ const [nonce, setNonce] = useState(0);
+ const [override, setOverride] = useState(null);
+
+ const fetched = useAsync(
+ () => api.settings.get(),
+ [activeId, nonce],
+ "Failed to load settings.",
+ );
+
+ const state: Async = override
+ ? { status: "ready", data: override }
+ : fetched;
const reload = useCallback(() => {
- setState({ status: "loading" });
- api.settings
- .get()
- .then((settings) => setState({ status: "ready", settings }))
- .catch((err) =>
- setState({
- status: "error",
- message: errorMessage(err, "Failed to load settings."),
- }),
- );
+ setOverride(null);
+ setNonce((n) => n + 1);
}, []);
- useEffect(() => {
- reload();
- }, [reload, activeId]);
-
- const set = useCallback(
- (settings: AccountSettings) => setState({ status: "ready", settings }),
- [],
- );
+ const set = useCallback((settings: AccountSettings) => setOverride(settings), []);
return { state, reload, set };
}
diff --git a/src/renderer/src/features/enhancements/EnhancementsView.tsx b/src/renderer/src/features/enhancements/EnhancementsView.tsx
index e61c114..8b8bfe8 100644
--- a/src/renderer/src/features/enhancements/EnhancementsView.tsx
+++ b/src/renderer/src/features/enhancements/EnhancementsView.tsx
@@ -8,7 +8,7 @@ import type {
} from "../../../../shared/types/enhancements";
import { api, errorMessage } from "../../lib/api";
import { useI18n } from "../../lib/i18n";
-import { Badge, Banner, Loader, Toggle } from "../../components/ui";
+import { Badge, Banner, Loader, PAGE_TITLE, Toggle } from "../../components/ui";
const SHELL = "mx-auto flex w-full max-w-[760px] flex-col gap-[18px] px-12 pb-16 pt-10";
@@ -62,7 +62,7 @@ export function EnhancementsView() {
return (
diff --git a/src/renderer/src/features/profile/MyWorldsView.tsx b/src/renderer/src/features/profile/MyWorldsView.tsx
index db72a9a..055f58d 100644
--- a/src/renderer/src/features/profile/MyWorldsView.tsx
+++ b/src/renderer/src/features/profile/MyWorldsView.tsx
@@ -1,5 +1,5 @@
import { useState } from "react";
-import { Banner, Loader, Tabs } from "../../components/ui";
+import { Banner, Loader, PAGE_TITLE, Tabs } from "../../components/ui";
import { useT } from "../../lib/i18n";
import { useProfile } from "./useProfile";
import { WorldsSection, FavoriteWorldsSection, WorldSearch } from "./WorldsSection";
@@ -26,7 +26,7 @@ export function MyWorldsView() {
return (
- {t("nav:worlds")}
+ {t("nav:worlds")}
- {t("search:title")}
+ {t("search:title")}
- {t("settings:title")}
+ {t("settings:title")}
{t("settings:subtitle")}
@@ -84,31 +91,6 @@ export function SettingsView() {
);
}
-function Section({
- title,
- description,
- icon,
- children,
-}: {
- title: string;
- description?: ReactNode;
- icon?: ReactNode;
- children: ReactNode;
-}) {
- return (
-
-
-
- {icon ? {icon} : null}
- {title}
-
- {description ?
{description}
: null}
-
- {children}
-
- );
-}
-
const SCHEME_OPTIONS: { mode: SchemeMode; labelKey: string; hintKey: string; icon: ReactNode }[] = [
{
mode: "auto",
@@ -378,12 +360,6 @@ function GameSection() {
);
}
-function Notice({ error, ok }: { error?: string | null; ok?: string | null }) {
- if (error) return {error}
;
- if (ok) return {ok}
;
- return null;
-}
-
const REGION_OPTIONS: PreferredRegion[] = ["auto", "us", "use", "eu", "jp"];
function RegionSection() {