mirror of
https://github.com/usememos/memos.git
synced 2025-06-05 22:09:59 +02:00
chore: update user setting appearance (#654)
This commit is contained in:
@ -10,6 +10,8 @@ type UserSettingKey string
|
|||||||
const (
|
const (
|
||||||
// UserSettingLocaleKey is the key type for user locale.
|
// UserSettingLocaleKey is the key type for user locale.
|
||||||
UserSettingLocaleKey UserSettingKey = "locale"
|
UserSettingLocaleKey UserSettingKey = "locale"
|
||||||
|
// UserSettingAppearanceKey is the key type for user appearance.
|
||||||
|
UserSettingAppearanceKey UserSettingKey = "appearance"
|
||||||
// UserSettingMemoVisibilityKey is the key type for user preference memo default visibility.
|
// UserSettingMemoVisibilityKey is the key type for user preference memo default visibility.
|
||||||
UserSettingMemoVisibilityKey UserSettingKey = "memoVisibility"
|
UserSettingMemoVisibilityKey UserSettingKey = "memoVisibility"
|
||||||
// UserSettingMemoDisplayTsOptionKey is the key type for memo display ts option.
|
// UserSettingMemoDisplayTsOptionKey is the key type for memo display ts option.
|
||||||
@ -21,6 +23,8 @@ func (key UserSettingKey) String() string {
|
|||||||
switch key {
|
switch key {
|
||||||
case UserSettingLocaleKey:
|
case UserSettingLocaleKey:
|
||||||
return "locale"
|
return "locale"
|
||||||
|
case UserSettingAppearanceKey:
|
||||||
|
return "appearance"
|
||||||
case UserSettingMemoVisibilityKey:
|
case UserSettingMemoVisibilityKey:
|
||||||
return "memoVisibility"
|
return "memoVisibility"
|
||||||
case UserSettingMemoDisplayTsOptionKey:
|
case UserSettingMemoDisplayTsOptionKey:
|
||||||
@ -31,8 +35,8 @@ func (key UserSettingKey) String() string {
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
UserSettingLocaleValue = []string{"en", "zh", "vi", "fr"}
|
UserSettingLocaleValue = []string{"en", "zh", "vi", "fr"}
|
||||||
|
UserSettingAppearanceValue = []string{"light", "dark"}
|
||||||
UserSettingMemoVisibilityValue = []Visibility{Private, Protected, Public}
|
UserSettingMemoVisibilityValue = []Visibility{Private, Protected, Public}
|
||||||
UserSettingEditorFontStyleValue = []string{"normal", "mono"}
|
|
||||||
UserSettingMemoDisplayTsOptionKeyValue = []string{"created_ts", "updated_ts"}
|
UserSettingMemoDisplayTsOptionKeyValue = []string{"created_ts", "updated_ts"}
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -67,6 +71,23 @@ func (upsert UserSettingUpsert) Validate() error {
|
|||||||
if invalid {
|
if invalid {
|
||||||
return fmt.Errorf("invalid user setting locale value")
|
return fmt.Errorf("invalid user setting locale value")
|
||||||
}
|
}
|
||||||
|
} else if upsert.Key == UserSettingAppearanceKey {
|
||||||
|
appearanceValue := "light"
|
||||||
|
err := json.Unmarshal([]byte(upsert.Value), &appearanceValue)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to unmarshal user setting appearance value")
|
||||||
|
}
|
||||||
|
|
||||||
|
invalid := true
|
||||||
|
for _, value := range UserSettingAppearanceValue {
|
||||||
|
if appearanceValue == value {
|
||||||
|
invalid = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if invalid {
|
||||||
|
return fmt.Errorf("invalid user setting appearance value")
|
||||||
|
}
|
||||||
} else if upsert.Key == UserSettingMemoVisibilityKey {
|
} else if upsert.Key == UserSettingMemoVisibilityKey {
|
||||||
memoVisibilityValue := Private
|
memoVisibilityValue := Private
|
||||||
err := json.Unmarshal([]byte(upsert.Value), &memoVisibilityValue)
|
err := json.Unmarshal([]byte(upsert.Value), &memoVisibilityValue)
|
||||||
|
@ -6,12 +6,12 @@ import { useAppSelector } from "./store";
|
|||||||
import Loading from "./pages/Loading";
|
import Loading from "./pages/Loading";
|
||||||
import router from "./router";
|
import router from "./router";
|
||||||
import * as storage from "./helpers/storage";
|
import * as storage from "./helpers/storage";
|
||||||
import useAppearance from "./hooks/useAppearance";
|
import { useColorScheme } from "@mui/joy";
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
const { i18n } = useTranslation();
|
const { i18n } = useTranslation();
|
||||||
const { locale, systemStatus } = useAppSelector((state) => state.global);
|
const { appearance, locale, systemStatus } = useAppSelector((state) => state.global);
|
||||||
useAppearance();
|
const { setMode } = useColorScheme();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
locationService.updateStateWithLocation();
|
locationService.updateStateWithLocation();
|
||||||
@ -42,6 +42,19 @@ function App() {
|
|||||||
});
|
});
|
||||||
}, [locale]);
|
}, [locale]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const root = document.documentElement;
|
||||||
|
if (appearance === "light") {
|
||||||
|
root.classList.remove("dark");
|
||||||
|
} else if (appearance === "dark") {
|
||||||
|
root.classList.add("dark");
|
||||||
|
}
|
||||||
|
setMode(appearance);
|
||||||
|
storage.set({
|
||||||
|
appearance: appearance,
|
||||||
|
});
|
||||||
|
}, [appearance]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Suspense fallback={<Loading />}>
|
<Suspense fallback={<Loading />}>
|
||||||
<RouterProvider router={router} />
|
<RouterProvider router={router} />
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
import { Option, Select } from "@mui/joy";
|
import { Option, Select } from "@mui/joy";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { globalService } from "../services";
|
import { globalService, userService } from "../services";
|
||||||
import { useAppSelector } from "../store";
|
import { useAppSelector } from "../store";
|
||||||
import Icon from "./Icon";
|
import Icon from "./Icon";
|
||||||
|
|
||||||
const appearanceList = ["system", "light", "dark"];
|
const appearanceList = ["light", "dark"];
|
||||||
|
|
||||||
const AppearanceSelect = () => {
|
const AppearanceSelect = () => {
|
||||||
|
const user = useAppSelector((state) => state.user.user);
|
||||||
const appearance = useAppSelector((state) => state.global.appearance);
|
const appearance = useAppSelector((state) => state.global.appearance);
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
@ -16,12 +17,13 @@ const AppearanceSelect = () => {
|
|||||||
return <Icon.Sun className={className} />;
|
return <Icon.Sun className={className} />;
|
||||||
} else if (apperance === "dark") {
|
} else if (apperance === "dark") {
|
||||||
return <Icon.Moon className={className} />;
|
return <Icon.Moon className={className} />;
|
||||||
} else {
|
|
||||||
return <Icon.Smile className={className} />;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSelectChange = (appearance: Appearance) => {
|
const handleSelectChange = async (appearance: Appearance) => {
|
||||||
|
if (user) {
|
||||||
|
await userService.upsertUserSetting("appearance", appearance);
|
||||||
|
}
|
||||||
globalService.setAppearance(appearance);
|
globalService.setAppearance(appearance);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -43,7 +43,6 @@ const DailyReviewDialog: React.FC<Props> = (props: Props) => {
|
|||||||
toggleShowDatePicker(false);
|
toggleShowDatePicker(false);
|
||||||
|
|
||||||
toImage(memosElRef.current, {
|
toImage(memosElRef.current, {
|
||||||
backgroundColor: "#ffffff",
|
|
||||||
pixelRatio: window.devicePixelRatio * 2,
|
pixelRatio: window.devicePixelRatio * 2,
|
||||||
})
|
})
|
||||||
.then((url) => {
|
.then((url) => {
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
|
import { Select, Switch, Option } from "@mui/joy";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import Switch from "@mui/joy/Switch";
|
|
||||||
import { globalService, userService } from "../../services";
|
import { globalService, userService } from "../../services";
|
||||||
import { useAppSelector } from "../../store";
|
import { useAppSelector } from "../../store";
|
||||||
import { VISIBILITY_SELECTOR_ITEMS, MEMO_DISPLAY_TS_OPTION_SELECTOR_ITEMS } from "../../helpers/consts";
|
import { VISIBILITY_SELECTOR_ITEMS, MEMO_DISPLAY_TS_OPTION_SELECTOR_ITEMS } from "../../helpers/consts";
|
||||||
import Selector from "../common/Selector";
|
import Icon from "../Icon";
|
||||||
import AppearanceSelect from "../AppearanceSelect";
|
import AppearanceSelect from "../AppearanceSelect";
|
||||||
import "../../less/settings/preferences-section.less";
|
import "../../less/settings/preferences-section.less";
|
||||||
|
|
||||||
@ -63,32 +63,65 @@ const PreferencesSection = () => {
|
|||||||
return (
|
return (
|
||||||
<div className="section-container preferences-section-container">
|
<div className="section-container preferences-section-container">
|
||||||
<p className="title-text">{t("common.basic")}</p>
|
<p className="title-text">{t("common.basic")}</p>
|
||||||
<label className="form-label selector">
|
<div className="form-label selector">
|
||||||
<span className="normal-text">{t("common.language")}</span>
|
<span className="normal-text">{t("common.language")}</span>
|
||||||
<Selector className="ml-2 w-32" value={setting.locale} dataSource={localeSelectorItems} handleValueChanged={handleLocaleChanged} />
|
<Select
|
||||||
</label>
|
className="!min-w-[10rem] w-auto text-sm"
|
||||||
<label className="form-label selector">
|
value={setting.locale}
|
||||||
|
onChange={(_, locale) => {
|
||||||
|
if (locale) {
|
||||||
|
handleLocaleChanged(locale);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
startDecorator={<Icon.Globe className="w-4 h-auto" />}
|
||||||
|
>
|
||||||
|
{localeSelectorItems.map((item) => (
|
||||||
|
<Option key={item.value} value={item.value} className="whitespace-nowrap">
|
||||||
|
{item.text}
|
||||||
|
</Option>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
</div>
|
||||||
|
<div className="form-label selector">
|
||||||
<span className="normal-text">Theme</span>
|
<span className="normal-text">Theme</span>
|
||||||
<AppearanceSelect />
|
<AppearanceSelect />
|
||||||
</label>
|
</div>
|
||||||
<p className="title-text">{t("setting.preference")}</p>
|
<p className="title-text">{t("setting.preference")}</p>
|
||||||
<label className="form-label selector">
|
<div className="form-label selector">
|
||||||
<span className="normal-text">{t("setting.preference-section.default-memo-visibility")}</span>
|
<span className="normal-text">{t("setting.preference-section.default-memo-visibility")}</span>
|
||||||
<Selector
|
<Select
|
||||||
className="ml-2 w-32"
|
className="!min-w-[10rem] w-auto text-sm"
|
||||||
value={setting.memoVisibility}
|
value={setting.memoVisibility}
|
||||||
dataSource={visibilitySelectorItems}
|
onChange={(_, visibility) => {
|
||||||
handleValueChanged={handleDefaultMemoVisibilityChanged}
|
if (visibility) {
|
||||||
/>
|
handleDefaultMemoVisibilityChanged(visibility);
|
||||||
</label>
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{visibilitySelectorItems.map((item) => (
|
||||||
|
<Option key={item.value} value={item.value} className="whitespace-nowrap">
|
||||||
|
{item.text}
|
||||||
|
</Option>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
</div>
|
||||||
<label className="form-label selector">
|
<label className="form-label selector">
|
||||||
<span className="normal-text">{t("setting.preference-section.default-memo-sort-option")}</span>
|
<span className="normal-text">{t("setting.preference-section.default-memo-sort-option")}</span>
|
||||||
<Selector
|
<Select
|
||||||
className="ml-2 w-32"
|
className="!min-w-[10rem] w-auto text-sm"
|
||||||
value={setting.memoDisplayTsOption}
|
value={setting.memoDisplayTsOption}
|
||||||
dataSource={memoDisplayTsOptionSelectorItems}
|
onChange={(_, value) => {
|
||||||
handleValueChanged={handleMemoDisplayTsOptionChanged}
|
if (value) {
|
||||||
/>
|
handleMemoDisplayTsOptionChanged(value);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{memoDisplayTsOptionSelectorItems.map((item) => (
|
||||||
|
<Option key={item.value} value={item.value} className="whitespace-nowrap">
|
||||||
|
{item.text}
|
||||||
|
</Option>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
</label>
|
</label>
|
||||||
<label className="form-label selector">
|
<label className="form-label selector">
|
||||||
<span className="normal-text">{t("setting.preference-section.enable-folding-memo")}</span>
|
<span className="normal-text">{t("setting.preference-section.enable-folding-memo")}</span>
|
||||||
|
@ -14,7 +14,6 @@ import toastHelper from "./Toast";
|
|||||||
import MemoContent from "./MemoContent";
|
import MemoContent from "./MemoContent";
|
||||||
import MemoResources from "./MemoResources";
|
import MemoResources from "./MemoResources";
|
||||||
import Selector from "./common/Selector";
|
import Selector from "./common/Selector";
|
||||||
import useAppearance from "../hooks/useAppearance";
|
|
||||||
import "../less/share-memo-image-dialog.less";
|
import "../less/share-memo-image-dialog.less";
|
||||||
|
|
||||||
interface Props extends DialogProps {
|
interface Props extends DialogProps {
|
||||||
@ -36,7 +35,6 @@ const ShareMemoImageDialog: React.FC<Props> = (props: Props) => {
|
|||||||
shortcutImgUrl: "",
|
shortcutImgUrl: "",
|
||||||
memoVisibility: propsMemo.visibility,
|
memoVisibility: propsMemo.visibility,
|
||||||
});
|
});
|
||||||
const [appearance] = useAppearance();
|
|
||||||
const loadingState = useLoading();
|
const loadingState = useLoading();
|
||||||
const memoElRef = useRef<HTMLDivElement>(null);
|
const memoElRef = useRef<HTMLDivElement>(null);
|
||||||
const memo = {
|
const memo = {
|
||||||
@ -72,7 +70,6 @@ const ShareMemoImageDialog: React.FC<Props> = (props: Props) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
toImage(memoElRef.current, {
|
toImage(memoElRef.current, {
|
||||||
backgroundColor: appearance === "light" ? "#f4f4f5" : "#27272a",
|
|
||||||
pixelRatio: window.devicePixelRatio * 2,
|
pixelRatio: window.devicePixelRatio * 2,
|
||||||
})
|
})
|
||||||
.then((url) => {
|
.then((url) => {
|
||||||
@ -147,7 +144,7 @@ const ShareMemoImageDialog: React.FC<Props> = (props: Props) => {
|
|||||||
<div className="userinfo-container">
|
<div className="userinfo-container">
|
||||||
<span className="name-text">{user.nickname || user.username}</span>
|
<span className="name-text">{user.nickname || user.username}</span>
|
||||||
<span className="usage-text">
|
<span className="usage-text">
|
||||||
{createdDays} DAYS / {state.memoAmount} MEMOS
|
{state.memoAmount} MEMOS / {createdDays} DAYS
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<img className="logo-img" src="/logo.webp" alt="" />
|
<img className="logo-img" src="/logo.webp" alt="" />
|
||||||
|
@ -10,6 +10,8 @@ interface StorageData {
|
|||||||
editingMemoVisibilityCache: Visibility;
|
editingMemoVisibilityCache: Visibility;
|
||||||
// locale
|
// locale
|
||||||
locale: Locale;
|
locale: Locale;
|
||||||
|
// appearance
|
||||||
|
appearance: Appearance;
|
||||||
// local setting
|
// local setting
|
||||||
localSetting: LocalSetting;
|
localSetting: LocalSetting;
|
||||||
// skipped version
|
// skipped version
|
||||||
|
@ -140,3 +140,11 @@ export function absolutifyLink(rel: string): string {
|
|||||||
anchor.setAttribute("href", rel);
|
anchor.setAttribute("href", rel);
|
||||||
return anchor.href;
|
return anchor.href;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getSystemColorScheme() {
|
||||||
|
if (window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches) {
|
||||||
|
return "dark";
|
||||||
|
} else {
|
||||||
|
return "light";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,65 +0,0 @@
|
|||||||
import { useEffect } from "react";
|
|
||||||
import { useColorScheme } from "@mui/joy/styles";
|
|
||||||
import { useAppSelector } from "../store";
|
|
||||||
import { globalService } from "../services";
|
|
||||||
|
|
||||||
const getSystemColorScheme = () => {
|
|
||||||
if (window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches) {
|
|
||||||
return "dark";
|
|
||||||
} else {
|
|
||||||
return "light";
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const useAppearance = () => {
|
|
||||||
const user = useAppSelector((state) => state.user.user);
|
|
||||||
const appearance = useAppSelector((state) => state.global.appearance);
|
|
||||||
const { mode, setMode } = useColorScheme();
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (user) {
|
|
||||||
globalService.setAppearance(user.setting.appearance);
|
|
||||||
}
|
|
||||||
}, [user]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
let mode = appearance;
|
|
||||||
if (appearance === "system") {
|
|
||||||
mode = getSystemColorScheme();
|
|
||||||
}
|
|
||||||
setMode(mode);
|
|
||||||
}, [appearance]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const colorSchemeChangeHandler = (event: MediaQueryListEvent) => {
|
|
||||||
const newColorScheme = event.matches ? "dark" : "light";
|
|
||||||
if (globalService.getState().appearance === "system") {
|
|
||||||
setMode(newColorScheme);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if (appearance !== "system") {
|
|
||||||
window.matchMedia("(prefers-color-scheme: dark)").removeEventListener("change", colorSchemeChangeHandler);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", colorSchemeChangeHandler);
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
window.matchMedia("(prefers-color-scheme: dark)").removeEventListener("change", colorSchemeChangeHandler);
|
|
||||||
};
|
|
||||||
}, [appearance]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const root = document.documentElement;
|
|
||||||
if (mode === "dark") {
|
|
||||||
root.classList.add("dark");
|
|
||||||
} else if (mode === "light") {
|
|
||||||
root.classList.remove("dark");
|
|
||||||
}
|
|
||||||
}, [mode]);
|
|
||||||
|
|
||||||
return [appearance, globalService.setAppearance] as const;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default useAppearance;
|
|
@ -1,21 +0,0 @@
|
|||||||
import { useState, useEffect } from "react";
|
|
||||||
|
|
||||||
const useMediaQuery = (query: string) => {
|
|
||||||
const [matches, setMatches] = useState(false);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const media = window.matchMedia(query);
|
|
||||||
if (media.matches !== matches) {
|
|
||||||
setMatches(media.matches);
|
|
||||||
}
|
|
||||||
const listener = () => {
|
|
||||||
setMatches(media.matches);
|
|
||||||
};
|
|
||||||
media.addEventListener("change", listener);
|
|
||||||
return () => media.removeEventListener("change", listener);
|
|
||||||
}, [query, matches]);
|
|
||||||
|
|
||||||
return matches;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default useMediaQuery;
|
|
@ -1,13 +0,0 @@
|
|||||||
// A custom hook that builds on useLocation to parse
|
|
||||||
|
|
||||||
import React from "react";
|
|
||||||
import { useLocation } from "react-router-dom";
|
|
||||||
|
|
||||||
// the query string for you.
|
|
||||||
const useQuery = () => {
|
|
||||||
const { search } = useLocation();
|
|
||||||
|
|
||||||
return React.useMemo(() => new URLSearchParams(search), [search]);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default useQuery;
|
|
@ -2,7 +2,7 @@
|
|||||||
@apply p-0 sm:py-16;
|
@apply p-0 sm:py-16;
|
||||||
|
|
||||||
> .dialog-container {
|
> .dialog-container {
|
||||||
@apply w-full sm:w-112 max-w-full grow sm:grow-0 p-0 rounded-none sm:rounded-lg;
|
@apply w-full sm:w-112 max-w-full grow sm:grow-0 p-0 pb-4 rounded-none sm:rounded-lg;
|
||||||
|
|
||||||
> .dialog-header-container {
|
> .dialog-header-container {
|
||||||
@apply relative flex flex-row justify-between items-center w-full p-6 pb-0 mb-0;
|
@apply relative flex flex-row justify-between items-center w-full p-6 pb-0 mb-0;
|
||||||
@ -33,7 +33,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
> .dialog-content-container {
|
> .dialog-content-container {
|
||||||
@apply w-full h-auto flex flex-col justify-start items-start p-6 pb-0;
|
@apply w-full h-auto flex flex-col justify-start items-start p-6 pb-0 bg-white dark:bg-zinc-800;
|
||||||
|
|
||||||
> .date-card-container {
|
> .date-card-container {
|
||||||
@apply flex flex-col justify-center items-center m-auto pb-6 select-none;
|
@apply flex flex-col justify-center items-center m-auto pb-6 select-none;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
.share-memo-image-dialog {
|
.share-memo-image-dialog {
|
||||||
> .dialog-container {
|
> .dialog-container {
|
||||||
@apply w-96 p-0 bg-zinc-100;
|
@apply w-96 p-0 bg-white dark:bg-zinc-800;
|
||||||
|
|
||||||
> .dialog-header-container {
|
> .dialog-header-container {
|
||||||
@apply py-2 pt-4 px-4 pl-6 mb-0 rounded-t-lg;
|
@apply py-2 pt-4 px-4 pl-6 mb-0 rounded-t-lg;
|
||||||
@ -35,7 +35,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
> .memo-container {
|
> .memo-container {
|
||||||
@apply w-96 max-w-full h-auto select-none relative flex flex-col justify-start items-start;
|
@apply w-96 max-w-full h-auto select-none relative flex flex-col justify-start items-start bg-white dark:bg-zinc-800;
|
||||||
|
|
||||||
> .memo-shortcut-img {
|
> .memo-shortcut-img {
|
||||||
@apply absolute top-0 left-0 w-full h-auto z-10;
|
@apply absolute top-0 left-0 w-full h-auto z-10;
|
||||||
@ -50,7 +50,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
> .images-container {
|
> .images-container {
|
||||||
@apply w-full h-auto flex flex-col justify-start items-start px-6 pb-2 bg-white;
|
@apply w-full h-auto flex flex-col justify-start items-start px-6 pb-2;
|
||||||
|
|
||||||
> img {
|
> img {
|
||||||
@apply w-full h-auto mb-2 rounded;
|
@apply w-full h-auto mb-2 rounded;
|
||||||
@ -58,30 +58,22 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
> .watermark-container {
|
> .watermark-container {
|
||||||
@apply flex flex-row justify-between items-center w-full dark:bg-zinc-900 py-2 px-6;
|
@apply flex flex-row justify-between items-center w-full bg-gray-100 dark:bg-zinc-700 py-2 px-6;
|
||||||
|
|
||||||
> .normal-text {
|
|
||||||
@apply w-full flex flex-row justify-start items-center text-sm leading-6 text-gray-500;
|
|
||||||
|
|
||||||
> .name-text {
|
|
||||||
@apply text-black;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
> .userinfo-container {
|
> .userinfo-container {
|
||||||
@apply w-64 flex flex-col justify-center items-start;
|
@apply w-64 flex flex-col justify-center items-start;
|
||||||
|
|
||||||
> .name-text {
|
> .name-text {
|
||||||
@apply text-lg truncate font-medium text-gray-600;
|
@apply text-sm truncate font-bold text-gray-600 dark:text-gray-300;
|
||||||
}
|
}
|
||||||
|
|
||||||
> .usage-text {
|
> .usage-text {
|
||||||
@apply -mt-1 text-sm text-gray-400;
|
@apply text-xs text-gray-400;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
> .logo-img {
|
> .logo-img {
|
||||||
@apply h-12 w-auto;
|
@apply h-10 w-auto;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,7 @@ const globalService = {
|
|||||||
initialState: async () => {
|
initialState: async () => {
|
||||||
const defaultGlobalState = {
|
const defaultGlobalState = {
|
||||||
locale: "en" as Locale,
|
locale: "en" as Locale,
|
||||||
appearance: "system" as Appearance,
|
appearance: "light" as Appearance,
|
||||||
systemStatus: {
|
systemStatus: {
|
||||||
allowSignUp: false,
|
allowSignUp: false,
|
||||||
additionalStyle: "",
|
additionalStyle: "",
|
||||||
@ -19,10 +19,13 @@ const globalService = {
|
|||||||
} as SystemStatus,
|
} as SystemStatus,
|
||||||
};
|
};
|
||||||
|
|
||||||
const { locale: storageLocale } = storage.get(["locale"]);
|
const { locale: storageLocale, appearance: storageAppearance } = storage.get(["locale", "appearance"]);
|
||||||
if (storageLocale) {
|
if (storageLocale) {
|
||||||
defaultGlobalState.locale = storageLocale;
|
defaultGlobalState.locale = storageLocale;
|
||||||
}
|
}
|
||||||
|
if (storageAppearance) {
|
||||||
|
defaultGlobalState.appearance = storageAppearance;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { data } = (await api.getSystemStatus()).data;
|
const { data } = (await api.getSystemStatus()).data;
|
||||||
|
@ -3,12 +3,12 @@ import * as api from "../helpers/api";
|
|||||||
import * as storage from "../helpers/storage";
|
import * as storage from "../helpers/storage";
|
||||||
import { UNKNOWN_ID } from "../helpers/consts";
|
import { UNKNOWN_ID } from "../helpers/consts";
|
||||||
import store from "../store";
|
import store from "../store";
|
||||||
import { setLocale } from "../store/modules/global";
|
|
||||||
import { setUser, patchUser, setHost, setOwner } from "../store/modules/user";
|
import { setUser, patchUser, setHost, setOwner } from "../store/modules/user";
|
||||||
|
import { getSystemColorScheme } from "../helpers/utils";
|
||||||
|
|
||||||
const defaultSetting: Setting = {
|
const defaultSetting: Setting = {
|
||||||
locale: "en",
|
locale: "en",
|
||||||
appearance: "system",
|
appearance: getSystemColorScheme(),
|
||||||
memoVisibility: "PRIVATE",
|
memoVisibility: "PRIVATE",
|
||||||
memoDisplayTsOption: "created_ts",
|
memoDisplayTsOption: "created_ts",
|
||||||
};
|
};
|
||||||
@ -61,11 +61,15 @@ const userService = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const { data: user } = (await api.getMyselfUser()).data;
|
const { data } = (await api.getMyselfUser()).data;
|
||||||
if (user) {
|
if (data) {
|
||||||
store.dispatch(setUser(convertResponseModelUser(user)));
|
const user = convertResponseModelUser(data);
|
||||||
|
store.dispatch(setUser(user));
|
||||||
if (user.setting.locale) {
|
if (user.setting.locale) {
|
||||||
store.dispatch(setLocale(user.setting.locale));
|
globalService.setLocale(user.setting.locale);
|
||||||
|
}
|
||||||
|
if (user.setting.appearance) {
|
||||||
|
globalService.setAppearance(user.setting.appearance);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -10,7 +10,7 @@ const globalSlice = createSlice({
|
|||||||
name: "global",
|
name: "global",
|
||||||
initialState: {
|
initialState: {
|
||||||
locale: "en",
|
locale: "en",
|
||||||
appearance: "system",
|
appearance: "light",
|
||||||
systemStatus: {
|
systemStatus: {
|
||||||
host: undefined,
|
host: undefined,
|
||||||
profile: {
|
profile: {
|
||||||
|
2
web/src/types/modules/setting.d.ts
vendored
2
web/src/types/modules/setting.d.ts
vendored
@ -1,4 +1,4 @@
|
|||||||
type Appearance = "light" | "dark" | "system";
|
type Appearance = "light" | "dark";
|
||||||
|
|
||||||
interface Setting {
|
interface Setting {
|
||||||
locale: Locale;
|
locale: Locale;
|
||||||
|
Reference in New Issue
Block a user