fix: temporarily lock tasker dark theme
This commit is contained in:
parent
3f86b73d5c
commit
6e74964853
|
|
@ -10,7 +10,7 @@ import { Links, Meta, Outlet, Scripts } from "react-router";
|
|||
import type { LinksFunction } from "react-router";
|
||||
import { ThemeProvider } from "next-themes";
|
||||
// plane imports
|
||||
import { SITE_DESCRIPTION, SITE_NAME } from "@plane/constants";
|
||||
import { NODEDC_FORCED_THEME, NODEDC_THEME_LOCK_ENABLED, SITE_DESCRIPTION, SITE_NAME, THEMES } from "@plane/constants";
|
||||
import { cn } from "@plane/utils";
|
||||
// types
|
||||
// assets
|
||||
|
|
@ -24,9 +24,12 @@ import { NodeDCSessionSync } from "@/components/auth-screens/nodedc-session-sync
|
|||
import { CustomErrorComponent } from "./error";
|
||||
import { AppProvider } from "./provider";
|
||||
// fonts
|
||||
// oxlint-disable-next-line import/no-unassigned-import
|
||||
import "@fontsource-variable/inter";
|
||||
import interVariableWoff2 from "@fontsource-variable/inter/files/inter-latin-wght-normal.woff2?url";
|
||||
// oxlint-disable-next-line import/no-unassigned-import
|
||||
import "@fontsource/material-symbols-rounded";
|
||||
// oxlint-disable-next-line import/no-unassigned-import
|
||||
import "@fontsource/ibm-plex-mono";
|
||||
|
||||
const APP_TITLE = "NODE.DC | Self-hosted task management workspace.";
|
||||
|
|
@ -110,15 +113,20 @@ export function Layout({ children }: { children: ReactNode }) {
|
|||
const isSessionRecorderEnabled = parseInt(process.env.VITE_ENABLE_SESSION_RECORDER || "0");
|
||||
|
||||
return (
|
||||
<html lang="en" suppressHydrationWarning style={designConfigStyle}>
|
||||
<html
|
||||
lang="en"
|
||||
data-theme={NODEDC_THEME_LOCK_ENABLED ? NODEDC_FORCED_THEME : undefined}
|
||||
suppressHydrationWarning
|
||||
style={designConfigStyle}
|
||||
>
|
||||
<head>
|
||||
<meta charSet="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#fff" />
|
||||
<meta name="theme-color" content={NODEDC_THEME_LOCK_ENABLED ? "#111113" : "#fff"} />
|
||||
{/* Meta info for PWA */}
|
||||
<meta name="application-name" content="NODE.DC" />
|
||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="default" />
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content={NODEDC_THEME_LOCK_ENABLED ? "black" : "default"} />
|
||||
<meta name="apple-mobile-web-app-title" content={SITE_NAME} />
|
||||
<meta name="format-detection" content="telephone=no" />
|
||||
<meta name="mobile-web-app-capable" content="yes" />
|
||||
|
|
@ -128,7 +136,14 @@ export function Layout({ children }: { children: ReactNode }) {
|
|||
<body suppressHydrationWarning>
|
||||
<div id="context-menu-portal" />
|
||||
<div id="editor-portal" />
|
||||
<ThemeProvider themes={["light", "dark", "light-contrast", "dark-contrast", "custom"]} defaultTheme="system">
|
||||
<ThemeProvider
|
||||
attribute="data-theme"
|
||||
themes={THEMES}
|
||||
defaultTheme={NODEDC_THEME_LOCK_ENABLED ? NODEDC_FORCED_THEME : "system"}
|
||||
forcedTheme={NODEDC_THEME_LOCK_ENABLED ? NODEDC_FORCED_THEME : undefined}
|
||||
enableSystem={!NODEDC_THEME_LOCK_ENABLED}
|
||||
disableTransitionOnChange
|
||||
>
|
||||
{children}
|
||||
</ThemeProvider>
|
||||
<Scripts />
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import { observer } from "mobx-react";
|
|||
import { useTheme } from "next-themes";
|
||||
// plane imports
|
||||
import type { I_THEME_OPTION } from "@plane/constants";
|
||||
import { THEME_OPTIONS } from "@plane/constants";
|
||||
import { NODEDC_FORCED_THEME, NODEDC_THEME_LOCK_ENABLED, THEME_OPTIONS } from "@plane/constants";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { setPromiseToast } from "@plane/propel/toast";
|
||||
import { applyCustomTheme } from "@plane/utils";
|
||||
|
|
@ -35,18 +35,20 @@ export const ThemeSwitcher = observer(function ThemeSwitcher(props: {
|
|||
const { t } = useTranslation();
|
||||
// derived values
|
||||
const currentTheme = useMemo(() => {
|
||||
const userThemeOption = THEME_OPTIONS.find((t) => t.value === userProfile?.theme?.theme);
|
||||
return userThemeOption || null;
|
||||
const themeValue = NODEDC_THEME_LOCK_ENABLED ? NODEDC_FORCED_THEME : userProfile?.theme?.theme;
|
||||
return THEME_OPTIONS.find((themeOption) => themeOption.value === themeValue) || null;
|
||||
}, [userProfile?.theme?.theme]);
|
||||
|
||||
const handleThemeChange = useCallback(
|
||||
async (themeOption: I_THEME_OPTION) => {
|
||||
try {
|
||||
setTheme(themeOption.value);
|
||||
const nextTheme = NODEDC_THEME_LOCK_ENABLED ? NODEDC_FORCED_THEME : themeOption.value;
|
||||
setTheme(nextTheme);
|
||||
|
||||
// If switching to custom theme and user has saved custom colors, apply them immediately
|
||||
if (
|
||||
themeOption.value === "custom" &&
|
||||
!NODEDC_THEME_LOCK_ENABLED &&
|
||||
nextTheme === "custom" &&
|
||||
userProfile?.theme?.primary &&
|
||||
userProfile?.theme?.background &&
|
||||
userProfile?.theme?.darkPalette !== undefined
|
||||
|
|
@ -58,7 +60,7 @@ export const ThemeSwitcher = observer(function ThemeSwitcher(props: {
|
|||
);
|
||||
}
|
||||
|
||||
const updatePromise = updateUserTheme({ theme: themeOption.value });
|
||||
const updatePromise = updateUserTheme({ theme: nextTheme });
|
||||
setPromiseToast(updatePromise, {
|
||||
loading: "Updating theme...",
|
||||
success: {
|
||||
|
|
@ -96,7 +98,7 @@ export const ThemeSwitcher = observer(function ThemeSwitcher(props: {
|
|||
/>
|
||||
}
|
||||
/>
|
||||
{userProfile.theme?.theme === "custom" && <CustomThemeSelector />}
|
||||
{!NODEDC_THEME_LOCK_ENABLED && userProfile.theme?.theme === "custom" && <CustomThemeSelector />}
|
||||
</>
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import { observer } from "mobx-react";
|
|||
import { useTheme } from "next-themes";
|
||||
// plane imports
|
||||
import type { I_THEME_OPTION } from "@plane/constants";
|
||||
import { THEME_OPTIONS } from "@plane/constants";
|
||||
import { NODEDC_FORCED_THEME, NODEDC_THEME_LOCK_ENABLED, THEME_OPTIONS } from "@plane/constants";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { setPromiseToast } from "@plane/propel/toast";
|
||||
// components
|
||||
|
|
@ -34,15 +34,16 @@ export const ThemeSwitcher = observer(function ThemeSwitcher(props: {
|
|||
const { t } = useTranslation();
|
||||
// derived values
|
||||
const currentTheme = useMemo(() => {
|
||||
const userThemeOption = THEME_OPTIONS.find((t) => t.value === userProfile?.theme?.theme);
|
||||
return userThemeOption || null;
|
||||
const themeValue = NODEDC_THEME_LOCK_ENABLED ? NODEDC_FORCED_THEME : userProfile?.theme?.theme;
|
||||
return THEME_OPTIONS.find((themeOption) => themeOption.value === themeValue) || null;
|
||||
}, [userProfile?.theme?.theme]);
|
||||
|
||||
const handleThemeChange = useCallback(
|
||||
(themeOption: I_THEME_OPTION) => {
|
||||
try {
|
||||
setTheme(themeOption.value);
|
||||
const updatePromise = updateUserTheme({ theme: themeOption.value });
|
||||
const nextTheme = NODEDC_THEME_LOCK_ENABLED ? NODEDC_FORCED_THEME : themeOption.value;
|
||||
setTheme(nextTheme);
|
||||
const updatePromise = updateUserTheme({ theme: nextTheme });
|
||||
setPromiseToast(updatePromise, {
|
||||
loading: "Updating theme...",
|
||||
success: {
|
||||
|
|
@ -58,7 +59,7 @@ export const ThemeSwitcher = observer(function ThemeSwitcher(props: {
|
|||
console.error("Error updating theme:", error);
|
||||
}
|
||||
},
|
||||
[updateUserTheme]
|
||||
[setTheme, updateUserTheme]
|
||||
);
|
||||
|
||||
if (!userProfile) return null;
|
||||
|
|
@ -70,7 +71,7 @@ export const ThemeSwitcher = observer(function ThemeSwitcher(props: {
|
|||
description={t(props.option.description)}
|
||||
control={<ThemeSwitch value={currentTheme} onChange={handleThemeChange} />}
|
||||
/>
|
||||
{userProfile.theme?.theme === "custom" && <CustomThemeSelector />}
|
||||
{!NODEDC_THEME_LOCK_ENABLED && userProfile.theme?.theme === "custom" && <CustomThemeSelector />}
|
||||
</>
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
// plane imports
|
||||
import type { I_THEME_OPTION } from "@plane/constants";
|
||||
import { THEME_OPTIONS } from "@plane/constants";
|
||||
import { NODEDC_ACTIVE_THEME_OPTIONS } from "@plane/constants";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
// constants
|
||||
import { SelectionDropdown } from "@/components/common/selection-dropdown";
|
||||
|
|
@ -17,12 +17,7 @@ type Props = {
|
|||
onChange: (value: I_THEME_OPTION) => void;
|
||||
};
|
||||
|
||||
export function ThemeSwitch(props: Props) {
|
||||
const { value, onChange } = props;
|
||||
// translation
|
||||
const { t } = useTranslation();
|
||||
|
||||
const renderThemeSwatch = (themeOption: I_THEME_OPTION) => (
|
||||
const renderThemeSwatch = (themeOption: I_THEME_OPTION) => (
|
||||
<div
|
||||
className="relative flex h-4 w-4 rotate-45 transform items-center justify-center rounded-full border-1"
|
||||
style={{
|
||||
|
|
@ -43,7 +38,12 @@ export function ThemeSwitch(props: Props) {
|
|||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
);
|
||||
|
||||
export function ThemeSwitch(props: Props) {
|
||||
const { value, onChange } = props;
|
||||
// translation
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<SelectionDropdown
|
||||
|
|
@ -59,7 +59,7 @@ export function ThemeSwitch(props: Props) {
|
|||
)
|
||||
}
|
||||
menuButtonWrapperClassName="flex w-full items-center justify-between rounded-full border border-subtle-1 px-3 py-2 text-13"
|
||||
options={THEME_OPTIONS.map((themeOption) => ({
|
||||
options={NODEDC_ACTIVE_THEME_OPTIONS.map((themeOption) => ({
|
||||
key: themeOption.value,
|
||||
isChecked: value?.value === themeOption.value,
|
||||
onClick: () => onChange(themeOption),
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import React, { useState } from "react";
|
|||
import { useTheme } from "next-themes";
|
||||
import { ArrowRightLeft } from "lucide-react";
|
||||
import { Dialog, Transition } from "@headlessui/react";
|
||||
import { NODEDC_FORCED_THEME, NODEDC_THEME_LOCK_ENABLED } from "@plane/constants";
|
||||
// ui
|
||||
import { Button } from "@plane/propel/button";
|
||||
import { TOAST_TYPE, setToast } from "@plane/propel/toast";
|
||||
|
|
@ -42,9 +43,10 @@ export function SwitchAccountModal(props: Props) {
|
|||
|
||||
await signOut()
|
||||
.then(() => {
|
||||
setTheme("system");
|
||||
setTheme(NODEDC_THEME_LOCK_ENABLED ? NODEDC_FORCED_THEME : "system");
|
||||
router.push("/");
|
||||
handleClose();
|
||||
return undefined;
|
||||
})
|
||||
.catch(() =>
|
||||
setToast({
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import { useCallback } from "react";
|
|||
import { useTheme } from "next-themes";
|
||||
import { Calendar, Earth, Languages, Palette } from "lucide-react";
|
||||
// plane imports
|
||||
import { NODEDC_FORCED_THEME, NODEDC_THEME_LOCK_ENABLED } from "@plane/constants";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { setToast, TOAST_TYPE } from "@plane/propel/toast";
|
||||
import type { EStartOfTheWeek, TUserProfile } from "@plane/types";
|
||||
|
|
@ -29,8 +30,9 @@ export const usePowerKPreferencesCommands = (): TPowerKCommandConfig[] => {
|
|||
|
||||
const handleUpdateTheme = useCallback(
|
||||
async (newTheme: string) => {
|
||||
setTheme(newTheme);
|
||||
return updateUserTheme({ theme: newTheme })
|
||||
const nextTheme = NODEDC_THEME_LOCK_ENABLED ? NODEDC_FORCED_THEME : newTheme;
|
||||
setTheme(nextTheme);
|
||||
return updateUserTheme({ theme: nextTheme })
|
||||
.then(() => {
|
||||
setToast({
|
||||
type: TOAST_TYPE.SUCCESS,
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import React, { useEffect, useState } from "react";
|
|||
import { Command } from "cmdk";
|
||||
import { observer } from "mobx-react";
|
||||
// plane imports
|
||||
import { THEME_OPTIONS } from "@plane/constants";
|
||||
import { NODEDC_ACTIVE_THEME_OPTIONS } from "@plane/constants";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
// local imports
|
||||
import { PowerKModalCommandItem } from "../../modal/command-item";
|
||||
|
|
@ -33,7 +33,7 @@ export const PowerKPreferencesThemesMenu = observer(function PowerKPreferencesTh
|
|||
|
||||
return (
|
||||
<Command.Group>
|
||||
{THEME_OPTIONS.map((theme) => (
|
||||
{NODEDC_ACTIVE_THEME_OPTIONS.map((theme) => (
|
||||
<PowerKModalCommandItem key={theme.value} onSelect={() => onSelect(theme.value)} label={t(theme.i18n_label)} />
|
||||
))}
|
||||
</Command.Group>
|
||||
|
|
|
|||
|
|
@ -137,7 +137,10 @@ export const CodexAgentApiSettingsContent = observer(function CodexAgentApiSetti
|
|||
})
|
||||
)
|
||||
);
|
||||
const workspaceProjectGroups = projectAccessPayload?.workspaces ?? [];
|
||||
const workspaceProjectGroups = useMemo(
|
||||
() => projectAccessPayload?.workspaces ?? [],
|
||||
[projectAccessPayload?.workspaces]
|
||||
);
|
||||
const projectOptions = useMemo(() => flattenProjectAccessOptions(workspaceProjectGroups), [workspaceProjectGroups]);
|
||||
const effectiveSelectedProjectKey = selectedProjectKey || projectOptions[0]?.key || "";
|
||||
const setupCards = useMemo(
|
||||
|
|
@ -735,6 +738,10 @@ function CodexConnectionGuide(props: TCodexConnectionGuideProps) {
|
|||
В строке <code>Authorization</code> замените только <code>ВАШ_УНИКАЛЬНЫЙ_ТОКЕН</code> на значение из{" "}
|
||||
<code>Agent token</code>. <code>Bearer</code> и <code>ndcag_</code> оставьте как есть.
|
||||
</p>
|
||||
<p className="mt-3">
|
||||
Не используйте <code>bearer_token_env_var</code>, если переменная окружения не добавлена в macOS launchd:
|
||||
Codex Desktop не увидит обычный <code>export</code> из shell-профиля.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="nodedc-settings-input min-h-36 px-4 py-4 text-13 leading-5 text-secondary">
|
||||
|
|
@ -743,6 +750,10 @@ function CodexConnectionGuide(props: TCodexConnectionGuideProps) {
|
|||
Сохраните <code>config.toml</code> и перезапустите локальный Codex. После старта попросите Codex проверить
|
||||
доступные проекты через <code>tasker_list_projects</code>.
|
||||
</p>
|
||||
<p className="mt-3">
|
||||
Если токен или endpoint временно недоступны, чат Codex все равно откроется, но MCP tools появятся только
|
||||
после исправления подключения.
|
||||
</p>
|
||||
<p className="mt-3">Правила работы и grants Codex получит сам через MCP по токену.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -775,13 +786,13 @@ function buildCodexConfigSnippet(endpoint: string): string {
|
|||
return `[mcp_servers.${CODEX_MCP_SERVER_NAME}]
|
||||
url = "${endpoint}"
|
||||
enabled = true
|
||||
required = true
|
||||
required = false
|
||||
startup_timeout_sec = 20
|
||||
tool_timeout_sec = 60
|
||||
|
||||
[mcp_servers.${CODEX_MCP_SERVER_NAME}.headers]
|
||||
Authorization = "Bearer ndcag_ВАШ_УНИКАЛЬНЫЙ_ТОКЕН"
|
||||
Accept = "application/json, text/event-stream"
|
||||
Accept = "application/json"
|
||||
"MCP-Protocol-Version" = "2025-06-18"`;
|
||||
}
|
||||
|
||||
|
|
@ -995,6 +1006,7 @@ function mergeTokens(primaryTokens: TCodexAgentToken[], secondaryTokens: TCodexA
|
|||
const tokensById = new Map<string, TCodexAgentToken>();
|
||||
for (const token of primaryTokens) tokensById.set(token.id, token);
|
||||
for (const token of secondaryTokens) tokensById.set(token.id, tokensById.get(token.id) ?? token);
|
||||
// oxlint-disable-next-line unicorn/no-array-sort
|
||||
return Array.from(tokensById.values()).sort(
|
||||
(leftToken, rightToken) => new Date(rightToken.created_at).getTime() - new Date(leftToken.created_at).getTime()
|
||||
);
|
||||
|
|
@ -1072,13 +1084,16 @@ function buildProjectGrantPayload(grantKeys: string[]): { workspace_slug: string
|
|||
}
|
||||
|
||||
function getGrantedProjectKeys(grants: TCodexAgentGrant[]): string[] {
|
||||
return [
|
||||
const grantedProjectKeys = [
|
||||
...new Set(
|
||||
grants
|
||||
.filter((grant) => grant.project_id)
|
||||
.map((grant) => buildProjectGrantKey(grant.workspace_slug, String(grant.project_id)))
|
||||
),
|
||||
].sort();
|
||||
];
|
||||
|
||||
// oxlint-disable-next-line unicorn/no-array-sort
|
||||
return grantedProjectKeys.sort();
|
||||
}
|
||||
|
||||
type TMergeAgentGrantsOptions = {
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import { useEffect, useRef } from "react";
|
|||
import { observer } from "mobx-react";
|
||||
import { useParams } from "next/navigation";
|
||||
import { useTheme } from "next-themes";
|
||||
import { NODEDC_FORCED_THEME, NODEDC_THEME_LOCK_ENABLED } from "@plane/constants";
|
||||
import type { TLanguage } from "@plane/i18n";
|
||||
import { DEFAULT_LANGUAGE, useTranslation } from "@plane/i18n";
|
||||
// helpers
|
||||
|
|
@ -57,16 +58,13 @@ function StoreWrapper(props: TStoreWrapper) {
|
|||
*/
|
||||
useEffect(() => {
|
||||
const localValue = localStorage && localStorage.getItem("app_sidebar_collapsed");
|
||||
const localBoolValue = localValue ? (localValue === "true" ? true : false) : false;
|
||||
const localBoolValue = localValue === "true";
|
||||
if (localValue && sidebarCollapsed === undefined) toggleSidebar(localBoolValue);
|
||||
}, [sidebarCollapsed, setTheme, toggleSidebar]);
|
||||
|
||||
/**
|
||||
* Effect 1: Initial theme sync from server (one-time only)
|
||||
*
|
||||
* This effect runs ONCE per user session to load theme from server.
|
||||
* After initial load, all theme changes are localStorage-driven (next-themes).
|
||||
* This prevents a feedback loop where server updates trigger UI updates in a cycle.
|
||||
* Temporary NODE.DC theme lock: keep the operational dark palette active
|
||||
* until the light theme gets a full pass.
|
||||
*/
|
||||
useEffect(() => {
|
||||
const userId = userProfile?.id;
|
||||
|
|
@ -79,6 +77,16 @@ function StoreWrapper(props: TStoreWrapper) {
|
|||
currentUserIdRef.current = userId;
|
||||
}
|
||||
|
||||
if (NODEDC_THEME_LOCK_ENABLED) {
|
||||
if (!userProfile?.id || hasInitializedThemeRef.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
setTheme(NODEDC_FORCED_THEME);
|
||||
hasInitializedThemeRef.current = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// Only initialize theme from server on FIRST load for this user
|
||||
if (!userProfile?.theme?.theme || hasInitializedThemeRef.current) {
|
||||
return; // Skip if already initialized or no profile data
|
||||
|
|
@ -89,7 +97,7 @@ function StoreWrapper(props: TStoreWrapper) {
|
|||
|
||||
// Mark as initialized - prevents future syncs from server
|
||||
hasInitializedThemeRef.current = true;
|
||||
}, [userProfile?.theme?.theme, setTheme]);
|
||||
}, [userProfile?.id, userProfile?.theme?.theme, setTheme]);
|
||||
|
||||
/**
|
||||
* Effect 2: Custom theme CSS application (runs on every change)
|
||||
|
|
@ -98,12 +106,21 @@ function StoreWrapper(props: TStoreWrapper) {
|
|||
* the theme changes. It runs independently of the initial sync effect.
|
||||
*/
|
||||
useEffect(() => {
|
||||
if (!userProfile?.theme?.theme) return;
|
||||
if (NODEDC_THEME_LOCK_ENABLED) {
|
||||
clearCustomTheme();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!userProfile?.theme?.theme) {
|
||||
return;
|
||||
}
|
||||
|
||||
const currentTheme = userProfile?.theme?.theme;
|
||||
const previousTheme = previousThemeRef.current;
|
||||
const themeData = userProfile?.theme;
|
||||
|
||||
if (!currentTheme) return;
|
||||
|
||||
// Apply custom theme if current theme is custom
|
||||
if (currentTheme === "custom" && themeData.primary && themeData.background && themeData.darkPalette !== undefined) {
|
||||
applyCustomTheme(themeData.primary, themeData.background, themeData.darkPalette ? "dark" : "light");
|
||||
|
|
@ -116,7 +133,7 @@ function StoreWrapper(props: TStoreWrapper) {
|
|||
|
||||
// Update previous theme for next comparison
|
||||
previousThemeRef.current = currentTheme;
|
||||
}, [userProfile?.theme]);
|
||||
}, [userProfile?.id, userProfile?.theme]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!userProfile?.id) return;
|
||||
|
|
@ -138,7 +155,7 @@ function StoreWrapper(props: TStoreWrapper) {
|
|||
useEffect(() => {
|
||||
if (!userProfile?.id) return;
|
||||
changeLanguage((userProfile?.language as TLanguage) || DEFAULT_LANGUAGE);
|
||||
}, [userProfile?.language, changeLanguage]);
|
||||
}, [userProfile?.id, userProfile?.language, changeLanguage]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!params) return;
|
||||
|
|
|
|||
|
|
@ -4,7 +4,9 @@
|
|||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
export const THEMES = ["light", "dark", "light-contrast", "dark-contrast", "custom"];
|
||||
export const NODEDC_FORCED_THEME = "dark";
|
||||
export const NODEDC_THEME_LOCK_ENABLED = true;
|
||||
export const THEMES = ["light", NODEDC_FORCED_THEME, "light-contrast", "dark-contrast", "custom"];
|
||||
|
||||
export interface I_THEME_OPTION {
|
||||
key: string;
|
||||
|
|
@ -43,7 +45,7 @@ export const THEME_OPTIONS: I_THEME_OPTION[] = [
|
|||
},
|
||||
{
|
||||
key: "dark",
|
||||
value: "dark",
|
||||
value: NODEDC_FORCED_THEME,
|
||||
i18n_label: "Dark",
|
||||
type: "dark",
|
||||
icon: {
|
||||
|
|
@ -86,3 +88,7 @@ export const THEME_OPTIONS: I_THEME_OPTION[] = [
|
|||
},
|
||||
},
|
||||
];
|
||||
|
||||
export const NODEDC_ACTIVE_THEME_OPTIONS = NODEDC_THEME_LOCK_ENABLED
|
||||
? THEME_OPTIONS.filter((themeOption) => themeOption.value === NODEDC_FORCED_THEME)
|
||||
: THEME_OPTIONS;
|
||||
|
|
|
|||
Loading…
Reference in New Issue