mirror of
https://github.com/usememos/memos.git
synced 2025-06-05 22:09:59 +02:00
refactor(frontend): use auth service
This commit is contained in:
@ -6,7 +6,6 @@ import storage from "./helpers/storage";
|
|||||||
import { getSystemColorScheme } from "./helpers/utils";
|
import { getSystemColorScheme } from "./helpers/utils";
|
||||||
import useNavigateTo from "./hooks/useNavigateTo";
|
import useNavigateTo from "./hooks/useNavigateTo";
|
||||||
import Loading from "./pages/Loading";
|
import Loading from "./pages/Loading";
|
||||||
import store from "./store";
|
|
||||||
import { useGlobalStore } from "./store/module";
|
import { useGlobalStore } from "./store/module";
|
||||||
import { useUserV1Store } from "./store/v1";
|
import { useUserV1Store } from "./store/v1";
|
||||||
|
|
||||||
@ -28,9 +27,11 @@ const App = () => {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const initialState = async () => {
|
const initialState = async () => {
|
||||||
const { user } = store.getState().user;
|
try {
|
||||||
if (user) {
|
await userV1Store.fetchCurrentUser();
|
||||||
await userV1Store.getOrFetchUserByUsername(user.username);
|
console.log("here");
|
||||||
|
} catch (error) {
|
||||||
|
// Skip.
|
||||||
}
|
}
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
};
|
};
|
||||||
|
@ -12,7 +12,7 @@ interface Props extends DialogProps {
|
|||||||
const ChangeMemberPasswordDialog: React.FC<Props> = (props: Props) => {
|
const ChangeMemberPasswordDialog: React.FC<Props> = (props: Props) => {
|
||||||
const { user, destroy } = props;
|
const { user, destroy } = props;
|
||||||
const t = useTranslate();
|
const t = useTranslate();
|
||||||
const userStore = useUserV1Store();
|
const userV1Store = useUserV1Store();
|
||||||
const [newPassword, setNewPassword] = useState("");
|
const [newPassword, setNewPassword] = useState("");
|
||||||
const [newPasswordAgain, setNewPasswordAgain] = useState("");
|
const [newPasswordAgain, setNewPasswordAgain] = useState("");
|
||||||
|
|
||||||
@ -47,7 +47,7 @@ const ChangeMemberPasswordDialog: React.FC<Props> = (props: Props) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await userStore.updateUser(
|
await userV1Store.updateUser(
|
||||||
{
|
{
|
||||||
name: `${UserNamePrefix}${user.username}`,
|
name: `${UserNamePrefix}${user.username}`,
|
||||||
password: newPassword,
|
password: newPassword,
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { toast } from "react-hot-toast";
|
import { toast } from "react-hot-toast";
|
||||||
import { useGlobalStore, useUserStore } from "@/store/module";
|
import useCurrentUser from "@/hooks/useCurrentUser";
|
||||||
import { useUserV1Store, UserNamePrefix } from "@/store/v1";
|
import { useGlobalStore } from "@/store/module";
|
||||||
|
import { useUserV1Store } from "@/store/v1";
|
||||||
import { useTranslate } from "@/utils/i18n";
|
import { useTranslate } from "@/utils/i18n";
|
||||||
import { generateDialog } from "./Dialog";
|
import { generateDialog } from "./Dialog";
|
||||||
import Icon from "./Icon";
|
import Icon from "./Icon";
|
||||||
@ -10,7 +11,7 @@ type Props = DialogProps;
|
|||||||
|
|
||||||
const ChangePasswordDialog: React.FC<Props> = ({ destroy }: Props) => {
|
const ChangePasswordDialog: React.FC<Props> = ({ destroy }: Props) => {
|
||||||
const t = useTranslate();
|
const t = useTranslate();
|
||||||
const userStore = useUserStore();
|
const currentUser = useCurrentUser();
|
||||||
const userV1Store = useUserV1Store();
|
const userV1Store = useUserV1Store();
|
||||||
const globalStore = useGlobalStore();
|
const globalStore = useGlobalStore();
|
||||||
const profile = globalStore.state.systemStatus.profile;
|
const profile = globalStore.state.systemStatus.profile;
|
||||||
@ -18,7 +19,7 @@ const ChangePasswordDialog: React.FC<Props> = ({ destroy }: Props) => {
|
|||||||
const [newPasswordAgain, setNewPasswordAgain] = useState("");
|
const [newPasswordAgain, setNewPasswordAgain] = useState("");
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (profile.mode === "demo" && userStore.state.user?.id === userStore.state.host?.id) {
|
if (profile.mode === "demo") {
|
||||||
toast.error("Demo mode does not support this operation.");
|
toast.error("Demo mode does not support this operation.");
|
||||||
destroy();
|
destroy();
|
||||||
}
|
}
|
||||||
@ -51,10 +52,9 @@ const ChangePasswordDialog: React.FC<Props> = ({ destroy }: Props) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const user = userStore.getState().user as User;
|
|
||||||
await userV1Store.updateUser(
|
await userV1Store.updateUser(
|
||||||
{
|
{
|
||||||
name: `${UserNamePrefix}${user.username}`,
|
name: currentUser.name,
|
||||||
password: newPassword,
|
password: newPassword,
|
||||||
},
|
},
|
||||||
["password"]
|
["password"]
|
||||||
|
@ -7,7 +7,7 @@ import { UNKNOWN_ID } from "@/helpers/consts";
|
|||||||
import { getRelativeTimeString } from "@/helpers/datetime";
|
import { getRelativeTimeString } from "@/helpers/datetime";
|
||||||
import useCurrentUser from "@/hooks/useCurrentUser";
|
import useCurrentUser from "@/hooks/useCurrentUser";
|
||||||
import useNavigateTo from "@/hooks/useNavigateTo";
|
import useNavigateTo from "@/hooks/useNavigateTo";
|
||||||
import { useFilterStore, useMemoStore, useUserStore } from "@/store/module";
|
import { useFilterStore, useMemoStore } from "@/store/module";
|
||||||
import { useUserV1Store, extractUsernameFromName } from "@/store/v1";
|
import { useUserV1Store, extractUsernameFromName } from "@/store/v1";
|
||||||
import { useTranslate } from "@/utils/i18n";
|
import { useTranslate } from "@/utils/i18n";
|
||||||
import showChangeMemoCreatedTsDialog from "./ChangeMemoCreatedTsDialog";
|
import showChangeMemoCreatedTsDialog from "./ChangeMemoCreatedTsDialog";
|
||||||
@ -36,7 +36,6 @@ const Memo: React.FC<Props> = (props: Props) => {
|
|||||||
const navigateTo = useNavigateTo();
|
const navigateTo = useNavigateTo();
|
||||||
const { i18n } = useTranslation();
|
const { i18n } = useTranslation();
|
||||||
const filterStore = useFilterStore();
|
const filterStore = useFilterStore();
|
||||||
const userStore = useUserStore();
|
|
||||||
const memoStore = useMemoStore();
|
const memoStore = useMemoStore();
|
||||||
const userV1Store = useUserV1Store();
|
const userV1Store = useUserV1Store();
|
||||||
const user = useCurrentUser();
|
const user = useCurrentUser();
|
||||||
@ -211,11 +210,6 @@ const Memo: React.FC<Props> = (props: Props) => {
|
|||||||
if (readonly) {
|
if (readonly) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const loginUser = userStore.state.user;
|
|
||||||
if (loginUser && !loginUser.localSetting.enableDoubleClickEditing) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const targetEl = e.target as HTMLElement;
|
const targetEl = e.target as HTMLElement;
|
||||||
|
|
||||||
if (targetEl.className === "tag-span") {
|
if (targetEl.className === "tag-span") {
|
||||||
|
@ -8,9 +8,10 @@ import { TAB_SPACE_WIDTH, UNKNOWN_ID, VISIBILITY_SELECTOR_ITEMS } from "@/helper
|
|||||||
import { clearContentQueryParam } from "@/helpers/utils";
|
import { clearContentQueryParam } from "@/helpers/utils";
|
||||||
import useCurrentUser from "@/hooks/useCurrentUser";
|
import useCurrentUser from "@/hooks/useCurrentUser";
|
||||||
import { getMatchedNodes } from "@/labs/marked";
|
import { getMatchedNodes } from "@/labs/marked";
|
||||||
import { useFilterStore, useGlobalStore, useMemoStore, useResourceStore, useTagStore, useUserStore } from "@/store/module";
|
import { useFilterStore, useGlobalStore, useMemoStore, useResourceStore, useTagStore } from "@/store/module";
|
||||||
|
import { useUserV1Store } from "@/store/v1";
|
||||||
import { Resource } from "@/types/proto/api/v2/resource_service";
|
import { Resource } from "@/types/proto/api/v2/resource_service";
|
||||||
import { User_Role } from "@/types/proto/api/v2/user_service";
|
import { UserSetting, User_Role } from "@/types/proto/api/v2/user_service";
|
||||||
import { useTranslate } from "@/utils/i18n";
|
import { useTranslate } from "@/utils/i18n";
|
||||||
import showCreateMemoRelationDialog from "../CreateMemoRelationDialog";
|
import showCreateMemoRelationDialog from "../CreateMemoRelationDialog";
|
||||||
import showCreateResourceDialog from "../CreateResourceDialog";
|
import showCreateResourceDialog from "../CreateResourceDialog";
|
||||||
@ -50,7 +51,7 @@ const MemoEditor = (props: Props) => {
|
|||||||
const {
|
const {
|
||||||
state: { systemStatus },
|
state: { systemStatus },
|
||||||
} = useGlobalStore();
|
} = useGlobalStore();
|
||||||
const userStore = useUserStore();
|
const userV1Store = useUserV1Store();
|
||||||
const filterStore = useFilterStore();
|
const filterStore = useFilterStore();
|
||||||
const memoStore = useMemoStore();
|
const memoStore = useMemoStore();
|
||||||
const tagStore = useTagStore();
|
const tagStore = useTagStore();
|
||||||
@ -66,8 +67,7 @@ const MemoEditor = (props: Props) => {
|
|||||||
const [hasContent, setHasContent] = useState<boolean>(false);
|
const [hasContent, setHasContent] = useState<boolean>(false);
|
||||||
const [isInIME, setIsInIME] = useState(false);
|
const [isInIME, setIsInIME] = useState(false);
|
||||||
const editorRef = useRef<EditorRefActions>(null);
|
const editorRef = useRef<EditorRefActions>(null);
|
||||||
const user = userStore.state.user as User;
|
const userSetting = userV1Store.userSetting as UserSetting;
|
||||||
const setting = user.setting;
|
|
||||||
const referenceRelations = memoId
|
const referenceRelations = memoId
|
||||||
? state.relationList.filter(
|
? state.relationList.filter(
|
||||||
(relation) => relation.memoId === memoId && relation.relatedMemoId !== memoId && relation.type === "REFERENCE"
|
(relation) => relation.memoId === memoId && relation.relatedMemoId !== memoId && relation.type === "REFERENCE"
|
||||||
@ -80,15 +80,15 @@ const MemoEditor = (props: Props) => {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let visibility = setting.memoVisibility;
|
let visibility = userSetting.memoVisibility;
|
||||||
if (systemStatus.disablePublicMemos && visibility === "PUBLIC") {
|
if (systemStatus.disablePublicMemos && visibility === "PUBLIC") {
|
||||||
visibility = "PRIVATE";
|
visibility = "PRIVATE";
|
||||||
}
|
}
|
||||||
setState((prevState) => ({
|
setState((prevState) => ({
|
||||||
...prevState,
|
...prevState,
|
||||||
memoVisibility: visibility,
|
memoVisibility: visibility as Visibility,
|
||||||
}));
|
}));
|
||||||
}, [setting.memoVisibility, systemStatus.disablePublicMemos]);
|
}, [userSetting.memoVisibility, systemStatus.disablePublicMemos]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (memoId) {
|
if (memoId) {
|
||||||
|
@ -3,8 +3,8 @@ import React, { useEffect, useState } from "react";
|
|||||||
import { toast } from "react-hot-toast";
|
import { toast } from "react-hot-toast";
|
||||||
import { userServiceClient } from "@/grpcweb";
|
import { userServiceClient } from "@/grpcweb";
|
||||||
import * as api from "@/helpers/api";
|
import * as api from "@/helpers/api";
|
||||||
import { useUserStore } from "@/store/module";
|
import useCurrentUser from "@/hooks/useCurrentUser";
|
||||||
import { UserNamePrefix } from "@/store/v1";
|
import { UserNamePrefix, useUserV1Store } from "@/store/v1";
|
||||||
import { RowStatus } from "@/types/proto/api/v2/common";
|
import { RowStatus } from "@/types/proto/api/v2/common";
|
||||||
import { User_Role } from "@/types/proto/api/v2/user_service";
|
import { User_Role } from "@/types/proto/api/v2/user_service";
|
||||||
import { useTranslate } from "@/utils/i18n";
|
import { useTranslate } from "@/utils/i18n";
|
||||||
@ -19,8 +19,8 @@ interface State {
|
|||||||
|
|
||||||
const MemberSection = () => {
|
const MemberSection = () => {
|
||||||
const t = useTranslate();
|
const t = useTranslate();
|
||||||
const userStore = useUserStore();
|
const currentUser = useCurrentUser();
|
||||||
const currentUser = userStore.state.user;
|
const userV1Store = useUserV1Store();
|
||||||
const [state, setState] = useState<State>({
|
const [state, setState] = useState<State>({
|
||||||
createUserUsername: "",
|
createUserUsername: "",
|
||||||
createUserPassword: "",
|
createUserPassword: "",
|
||||||
@ -115,7 +115,7 @@ const MemberSection = () => {
|
|||||||
style: "danger",
|
style: "danger",
|
||||||
dialogName: "delete-user-dialog",
|
dialogName: "delete-user-dialog",
|
||||||
onConfirm: async () => {
|
onConfirm: async () => {
|
||||||
await userStore.deleteUser(`${UserNamePrefix}${user.username}`);
|
await userV1Store.deleteUser(`${UserNamePrefix}${user.username}`);
|
||||||
fetchUserList();
|
fetchUserList();
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
import { Button, Divider, Input, Option, Select, Switch } from "@mui/joy";
|
import { Button, Divider, Input, Option, Select } from "@mui/joy";
|
||||||
import React, { useState } from "react";
|
import { useState } from "react";
|
||||||
import { toast } from "react-hot-toast";
|
import { toast } from "react-hot-toast";
|
||||||
import { VISIBILITY_SELECTOR_ITEMS } from "@/helpers/consts";
|
import { VISIBILITY_SELECTOR_ITEMS } from "@/helpers/consts";
|
||||||
import { useGlobalStore, useUserStore } from "@/store/module";
|
import { useGlobalStore } from "@/store/module";
|
||||||
|
import { useUserV1Store } from "@/store/v1";
|
||||||
|
import { UserSetting } from "@/types/proto/api/v2/user_service";
|
||||||
import { useTranslate } from "@/utils/i18n";
|
import { useTranslate } from "@/utils/i18n";
|
||||||
import AppearanceSelect from "../AppearanceSelect";
|
import AppearanceSelect from "../AppearanceSelect";
|
||||||
import LearnMore from "../LearnMore";
|
import LearnMore from "../LearnMore";
|
||||||
@ -14,32 +16,48 @@ import "@/less/settings/preferences-section.less";
|
|||||||
const PreferencesSection = () => {
|
const PreferencesSection = () => {
|
||||||
const t = useTranslate();
|
const t = useTranslate();
|
||||||
const globalStore = useGlobalStore();
|
const globalStore = useGlobalStore();
|
||||||
const userStore = useUserStore();
|
const userV1Store = useUserV1Store();
|
||||||
const { appearance, locale } = globalStore.state;
|
const { appearance, locale } = globalStore.state;
|
||||||
const { setting, localSetting } = userStore.state.user as User;
|
const setting = userV1Store.userSetting as UserSetting;
|
||||||
const [telegramUserId, setTelegramUserId] = useState<string>(setting.telegramUserId);
|
const [telegramUserId, setTelegramUserId] = useState<string>(setting.telegramUserId);
|
||||||
|
|
||||||
const handleLocaleSelectChange = async (locale: Locale) => {
|
const handleLocaleSelectChange = async (locale: Locale) => {
|
||||||
await userStore.upsertUserSetting("locale", locale);
|
await userV1Store.updateUserSetting(
|
||||||
|
{
|
||||||
|
locale,
|
||||||
|
},
|
||||||
|
["locale"]
|
||||||
|
);
|
||||||
globalStore.setLocale(locale);
|
globalStore.setLocale(locale);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleAppearanceSelectChange = async (appearance: Appearance) => {
|
const handleAppearanceSelectChange = async (appearance: Appearance) => {
|
||||||
await userStore.upsertUserSetting("appearance", appearance);
|
await userV1Store.updateUserSetting(
|
||||||
|
{
|
||||||
|
appearance,
|
||||||
|
},
|
||||||
|
["appearance"]
|
||||||
|
);
|
||||||
globalStore.setAppearance(appearance);
|
globalStore.setAppearance(appearance);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleDefaultMemoVisibilityChanged = async (value: string) => {
|
const handleDefaultMemoVisibilityChanged = async (value: string) => {
|
||||||
await userStore.upsertUserSetting("memo-visibility", value);
|
await userV1Store.updateUserSetting(
|
||||||
};
|
{
|
||||||
|
memoVisibility: value,
|
||||||
const handleDoubleClickEnabledChanged = (event: React.ChangeEvent<HTMLInputElement>) => {
|
},
|
||||||
userStore.upsertLocalSetting({ ...localSetting, enableDoubleClickEditing: event.target.checked });
|
["memo_visibility"]
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSaveTelegramUserId = async () => {
|
const handleSaveTelegramUserId = async () => {
|
||||||
try {
|
try {
|
||||||
await userStore.upsertUserSetting("telegram-user-id", telegramUserId);
|
await userV1Store.updateUserSetting(
|
||||||
|
{
|
||||||
|
telegramUserId: telegramUserId,
|
||||||
|
},
|
||||||
|
["telegram_user_id"]
|
||||||
|
);
|
||||||
toast.success(t("message.update-succeed"));
|
toast.success(t("message.update-succeed"));
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
@ -68,7 +86,7 @@ const PreferencesSection = () => {
|
|||||||
<Select
|
<Select
|
||||||
className="!min-w-fit"
|
className="!min-w-fit"
|
||||||
value={setting.memoVisibility}
|
value={setting.memoVisibility}
|
||||||
startDecorator={<VisibilityIcon visibility={setting.memoVisibility} />}
|
startDecorator={<VisibilityIcon visibility={setting.memoVisibility as Visibility} />}
|
||||||
onChange={(_, visibility) => {
|
onChange={(_, visibility) => {
|
||||||
if (visibility) {
|
if (visibility) {
|
||||||
handleDefaultMemoVisibilityChanged(visibility);
|
handleDefaultMemoVisibilityChanged(visibility);
|
||||||
@ -83,11 +101,6 @@ const PreferencesSection = () => {
|
|||||||
</Select>
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<label className="form-label selector">
|
|
||||||
<span className="text-sm break-keep">{t("setting.preference-section.enable-double-click")}</span>
|
|
||||||
<Switch className="ml-2" checked={localSetting.enableDoubleClickEditing} onChange={handleDoubleClickEnabledChanged} />
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<Divider className="!mt-3 !my-4" />
|
<Divider className="!mt-3 !my-4" />
|
||||||
|
|
||||||
<div className="mb-2 w-full flex flex-row justify-between items-center">
|
<div className="mb-2 w-full flex flex-row justify-between items-center">
|
||||||
|
@ -2,8 +2,8 @@ import { isEqual } from "lodash-es";
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { toast } from "react-hot-toast";
|
import { toast } from "react-hot-toast";
|
||||||
import { convertFileToBase64 } from "@/helpers/utils";
|
import { convertFileToBase64 } from "@/helpers/utils";
|
||||||
import { useUserStore } from "@/store/module";
|
import useCurrentUser from "@/hooks/useCurrentUser";
|
||||||
import { UserNamePrefix } from "@/store/v1";
|
import { UserNamePrefix, useUserV1Store } from "@/store/v1";
|
||||||
import { User as UserPb } from "@/types/proto/api/v2/user_service";
|
import { User as UserPb } from "@/types/proto/api/v2/user_service";
|
||||||
import { useTranslate } from "@/utils/i18n";
|
import { useTranslate } from "@/utils/i18n";
|
||||||
import { generateDialog } from "./Dialog";
|
import { generateDialog } from "./Dialog";
|
||||||
@ -21,13 +21,13 @@ interface State {
|
|||||||
|
|
||||||
const UpdateAccountDialog: React.FC<Props> = ({ destroy }: Props) => {
|
const UpdateAccountDialog: React.FC<Props> = ({ destroy }: Props) => {
|
||||||
const t = useTranslate();
|
const t = useTranslate();
|
||||||
const userStore = useUserStore();
|
const currentUser = useCurrentUser();
|
||||||
const user = userStore.state.user as User;
|
const userV1Store = useUserV1Store();
|
||||||
const [state, setState] = useState<State>({
|
const [state, setState] = useState<State>({
|
||||||
avatarUrl: user.avatarUrl,
|
avatarUrl: currentUser.avatarUrl,
|
||||||
username: user.username,
|
username: currentUser.name.replace(UserNamePrefix, ""),
|
||||||
nickname: user.nickname,
|
nickname: currentUser.nickname,
|
||||||
email: user.email,
|
email: currentUser.email,
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -95,24 +95,23 @@ const UpdateAccountDialog: React.FC<Props> = ({ destroy }: Props) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const user = userStore.getState().user as User;
|
|
||||||
const updateMask = [];
|
const updateMask = [];
|
||||||
if (!isEqual(user.avatarUrl, state.avatarUrl)) {
|
if (!isEqual(currentUser.avatarUrl, state.avatarUrl)) {
|
||||||
updateMask.push("avatar_url");
|
updateMask.push("avatar_url");
|
||||||
}
|
}
|
||||||
if (!isEqual(user.nickname, state.nickname)) {
|
if (!isEqual(currentUser.nickname, state.nickname)) {
|
||||||
updateMask.push("nickname");
|
updateMask.push("nickname");
|
||||||
}
|
}
|
||||||
if (!isEqual(user.username, state.username)) {
|
if (!isEqual(currentUser.name.replace(UserNamePrefix, ""), state.username)) {
|
||||||
updateMask.push("username");
|
updateMask.push("username");
|
||||||
}
|
}
|
||||||
if (!isEqual(user.email, state.email)) {
|
if (!isEqual(currentUser.email, state.email)) {
|
||||||
updateMask.push("email");
|
updateMask.push("email");
|
||||||
}
|
}
|
||||||
await userStore.patchUser(
|
await userV1Store.updateUser(
|
||||||
UserPb.fromPartial({
|
UserPb.fromPartial({
|
||||||
name: `${UserNamePrefix}${state.username}`,
|
name: `${UserNamePrefix}${state.username}`,
|
||||||
id: user.id,
|
id: currentUser.id,
|
||||||
nickname: state.nickname,
|
nickname: state.nickname,
|
||||||
email: state.email,
|
email: state.email,
|
||||||
avatarUrl: state.avatarUrl,
|
avatarUrl: state.avatarUrl,
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
|
import * as api from "@/helpers/api";
|
||||||
import useCurrentUser from "@/hooks/useCurrentUser";
|
import useCurrentUser from "@/hooks/useCurrentUser";
|
||||||
import useNavigateTo from "@/hooks/useNavigateTo";
|
import useNavigateTo from "@/hooks/useNavigateTo";
|
||||||
import { useGlobalStore, useUserStore } from "@/store/module";
|
import { useGlobalStore } from "@/store/module";
|
||||||
import { extractUsernameFromName } from "@/store/v1";
|
import { extractUsernameFromName } from "@/store/v1";
|
||||||
import { User_Role } from "@/types/proto/api/v2/user_service";
|
import { User_Role } from "@/types/proto/api/v2/user_service";
|
||||||
import { useTranslate } from "@/utils/i18n";
|
import { useTranslate } from "@/utils/i18n";
|
||||||
@ -13,7 +14,6 @@ const UserBanner = () => {
|
|||||||
const t = useTranslate();
|
const t = useTranslate();
|
||||||
const navigateTo = useNavigateTo();
|
const navigateTo = useNavigateTo();
|
||||||
const globalStore = useGlobalStore();
|
const globalStore = useGlobalStore();
|
||||||
const userStore = useUserStore();
|
|
||||||
const { systemStatus } = globalStore.state;
|
const { systemStatus } = globalStore.state;
|
||||||
const user = useCurrentUser();
|
const user = useCurrentUser();
|
||||||
const title = user ? user.nickname : systemStatus.customizedProfile.name || "memos";
|
const title = user ? user.nickname : systemStatus.customizedProfile.name || "memos";
|
||||||
@ -27,7 +27,8 @@ const UserBanner = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleSignOutBtnClick = async () => {
|
const handleSignOutBtnClick = async () => {
|
||||||
await userStore.doSignOut();
|
await api.signout();
|
||||||
|
localStorage.removeItem("userId");
|
||||||
window.location.href = "/auth";
|
window.location.href = "/auth";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,19 +1,9 @@
|
|||||||
import { useEffect } from "react";
|
|
||||||
import { useUserStore } from "@/store/module";
|
|
||||||
import { useUserV1Store } from "@/store/v1";
|
import { useUserV1Store } from "@/store/v1";
|
||||||
|
import { User } from "@/types/proto/api/v2/user_service";
|
||||||
|
|
||||||
const useCurrentUser = () => {
|
const useCurrentUser = () => {
|
||||||
const userStore = useUserStore();
|
|
||||||
const userV1Store = useUserV1Store();
|
const userV1Store = useUserV1Store();
|
||||||
const currentUsername = userStore.state.user?.username;
|
return userV1Store.currentUser as User;
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (currentUsername) {
|
|
||||||
userV1Store.getOrFetchUserByUsername(currentUsername);
|
|
||||||
}
|
|
||||||
}, [currentUsername]);
|
|
||||||
|
|
||||||
return userV1Store.getUserByUsername(currentUsername || "");
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default useCurrentUser;
|
export default useCurrentUser;
|
||||||
|
@ -6,7 +6,6 @@ import Icon from "@/components/Icon";
|
|||||||
import * as api from "@/helpers/api";
|
import * as api from "@/helpers/api";
|
||||||
import { absolutifyLink } from "@/helpers/utils";
|
import { absolutifyLink } from "@/helpers/utils";
|
||||||
import useNavigateTo from "@/hooks/useNavigateTo";
|
import useNavigateTo from "@/hooks/useNavigateTo";
|
||||||
import { useUserStore } from "@/store/module";
|
|
||||||
import { useUserV1Store } from "@/store/v1";
|
import { useUserV1Store } from "@/store/v1";
|
||||||
import { useTranslate } from "@/utils/i18n";
|
import { useTranslate } from "@/utils/i18n";
|
||||||
|
|
||||||
@ -19,7 +18,6 @@ const AuthCallback = () => {
|
|||||||
const t = useTranslate();
|
const t = useTranslate();
|
||||||
const navigateTo = useNavigateTo();
|
const navigateTo = useNavigateTo();
|
||||||
const [searchParams] = useSearchParams();
|
const [searchParams] = useSearchParams();
|
||||||
const userStore = useUserStore();
|
|
||||||
const userV1Store = useUserV1Store();
|
const userV1Store = useUserV1Store();
|
||||||
const [state, setState] = useState<State>({
|
const [state, setState] = useState<State>({
|
||||||
loading: true,
|
loading: true,
|
||||||
@ -42,9 +40,7 @@ const AuthCallback = () => {
|
|||||||
errorMessage: "",
|
errorMessage: "",
|
||||||
});
|
});
|
||||||
if (user) {
|
if (user) {
|
||||||
userStore.setCurrentUser(user);
|
await userV1Store.fetchCurrentUser();
|
||||||
await userStore.fetchCurrentUser();
|
|
||||||
await userV1Store.getOrFetchUserByUsername(user.username);
|
|
||||||
navigateTo("/");
|
navigateTo("/");
|
||||||
} else {
|
} else {
|
||||||
toast.error(t("message.login-failed"));
|
toast.error(t("message.login-failed"));
|
||||||
|
@ -8,7 +8,7 @@ import * as api from "@/helpers/api";
|
|||||||
import { absolutifyLink } from "@/helpers/utils";
|
import { absolutifyLink } from "@/helpers/utils";
|
||||||
import useLoading from "@/hooks/useLoading";
|
import useLoading from "@/hooks/useLoading";
|
||||||
import useNavigateTo from "@/hooks/useNavigateTo";
|
import useNavigateTo from "@/hooks/useNavigateTo";
|
||||||
import { useGlobalStore, useUserStore } from "@/store/module";
|
import { useGlobalStore } from "@/store/module";
|
||||||
import { useUserV1Store } from "@/store/v1";
|
import { useUserV1Store } from "@/store/v1";
|
||||||
import { useTranslate } from "@/utils/i18n";
|
import { useTranslate } from "@/utils/i18n";
|
||||||
|
|
||||||
@ -16,7 +16,6 @@ const SignIn = () => {
|
|||||||
const t = useTranslate();
|
const t = useTranslate();
|
||||||
const navigateTo = useNavigateTo();
|
const navigateTo = useNavigateTo();
|
||||||
const globalStore = useGlobalStore();
|
const globalStore = useGlobalStore();
|
||||||
const userStore = useUserStore();
|
|
||||||
const userV1Store = useUserV1Store();
|
const userV1Store = useUserV1Store();
|
||||||
const actionBtnLoadingState = useLoading(false);
|
const actionBtnLoadingState = useLoading(false);
|
||||||
const { appearance, locale, systemStatus } = globalStore.state;
|
const { appearance, locale, systemStatus } = globalStore.state;
|
||||||
@ -78,9 +77,7 @@ const SignIn = () => {
|
|||||||
actionBtnLoadingState.setLoading();
|
actionBtnLoadingState.setLoading();
|
||||||
const { data: user } = await api.signin(username, password, remember);
|
const { data: user } = await api.signin(username, password, remember);
|
||||||
if (user) {
|
if (user) {
|
||||||
userStore.setCurrentUser(user);
|
await userV1Store.fetchCurrentUser();
|
||||||
await userStore.fetchCurrentUser();
|
|
||||||
await userV1Store.getOrFetchUserByUsername(user.username);
|
|
||||||
navigateTo("/");
|
navigateTo("/");
|
||||||
} else {
|
} else {
|
||||||
toast.error(t("message.login-failed"));
|
toast.error(t("message.login-failed"));
|
||||||
|
@ -7,7 +7,7 @@ import LocaleSelect from "@/components/LocaleSelect";
|
|||||||
import * as api from "@/helpers/api";
|
import * as api from "@/helpers/api";
|
||||||
import useLoading from "@/hooks/useLoading";
|
import useLoading from "@/hooks/useLoading";
|
||||||
import useNavigateTo from "@/hooks/useNavigateTo";
|
import useNavigateTo from "@/hooks/useNavigateTo";
|
||||||
import { useGlobalStore, useUserStore } from "@/store/module";
|
import { useGlobalStore } from "@/store/module";
|
||||||
import { useUserV1Store } from "@/store/v1";
|
import { useUserV1Store } from "@/store/v1";
|
||||||
import { useTranslate } from "@/utils/i18n";
|
import { useTranslate } from "@/utils/i18n";
|
||||||
|
|
||||||
@ -15,7 +15,6 @@ const SignUp = () => {
|
|||||||
const t = useTranslate();
|
const t = useTranslate();
|
||||||
const navigateTo = useNavigateTo();
|
const navigateTo = useNavigateTo();
|
||||||
const globalStore = useGlobalStore();
|
const globalStore = useGlobalStore();
|
||||||
const userStore = useUserStore();
|
|
||||||
const userV1Store = useUserV1Store();
|
const userV1Store = useUserV1Store();
|
||||||
const actionBtnLoadingState = useLoading(false);
|
const actionBtnLoadingState = useLoading(false);
|
||||||
const { appearance, locale, systemStatus } = globalStore.state;
|
const { appearance, locale, systemStatus } = globalStore.state;
|
||||||
@ -58,9 +57,7 @@ const SignUp = () => {
|
|||||||
actionBtnLoadingState.setLoading();
|
actionBtnLoadingState.setLoading();
|
||||||
const { data: user } = await api.signup(username, password);
|
const { data: user } = await api.signup(username, password);
|
||||||
if (user) {
|
if (user) {
|
||||||
userStore.setCurrentUser(user);
|
await userV1Store.fetchCurrentUser();
|
||||||
await userStore.fetchCurrentUser();
|
|
||||||
await userV1Store.getOrFetchUserByUsername(user.username);
|
|
||||||
navigateTo("/");
|
navigateTo("/");
|
||||||
} else {
|
} else {
|
||||||
toast.error(t("message.signup-failed"));
|
toast.error(t("message.signup-failed"));
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { lazy } from "react";
|
import { lazy } from "react";
|
||||||
import { createBrowserRouter, redirect } from "react-router-dom";
|
import { createBrowserRouter } from "react-router-dom";
|
||||||
import App from "@/App";
|
import App from "@/App";
|
||||||
import { initialGlobalState, initialUserState } from "@/store/module";
|
import { initialGlobalState } from "@/store/module";
|
||||||
|
|
||||||
const Root = lazy(() => import("@/layouts/Root"));
|
const Root = lazy(() => import("@/layouts/Root"));
|
||||||
const SignIn = lazy(() => import("@/pages/SignIn"));
|
const SignIn = lazy(() => import("@/pages/SignIn"));
|
||||||
@ -28,20 +28,6 @@ const initialGlobalStateLoader = async () => {
|
|||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
const initialUserStateLoader = async (redirectWhenNotFound = true) => {
|
|
||||||
let user = undefined;
|
|
||||||
try {
|
|
||||||
user = await initialUserState();
|
|
||||||
} catch (error) {
|
|
||||||
// do nothing.
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!user && redirectWhenNotFound) {
|
|
||||||
return redirect("/explore");
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
|
|
||||||
const router = createBrowserRouter([
|
const router = createBrowserRouter([
|
||||||
{
|
{
|
||||||
path: "/",
|
path: "/",
|
||||||
@ -67,54 +53,44 @@ const router = createBrowserRouter([
|
|||||||
{
|
{
|
||||||
path: "",
|
path: "",
|
||||||
element: <Home />,
|
element: <Home />,
|
||||||
loader: () => initialUserStateLoader(),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "review",
|
path: "review",
|
||||||
element: <DailyReview />,
|
element: <DailyReview />,
|
||||||
loader: () => initialUserStateLoader(),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "resources",
|
path: "resources",
|
||||||
element: <Resources />,
|
element: <Resources />,
|
||||||
loader: () => initialUserStateLoader(),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "inbox",
|
path: "inbox",
|
||||||
element: <Inboxes />,
|
element: <Inboxes />,
|
||||||
loader: () => initialUserStateLoader(),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "archived",
|
path: "archived",
|
||||||
element: <Archived />,
|
element: <Archived />,
|
||||||
loader: () => initialUserStateLoader(),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "setting",
|
path: "setting",
|
||||||
element: <Setting />,
|
element: <Setting />,
|
||||||
loader: () => initialUserStateLoader(),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "explore",
|
path: "explore",
|
||||||
element: <Explore />,
|
element: <Explore />,
|
||||||
loader: () => initialUserStateLoader(false),
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/m/:memoId",
|
path: "/m/:memoId",
|
||||||
element: <MemoDetail />,
|
element: <MemoDetail />,
|
||||||
loader: () => initialUserStateLoader(false),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/m/:memoId/embed",
|
path: "/m/:memoId/embed",
|
||||||
element: <EmbedMemo />,
|
element: <EmbedMemo />,
|
||||||
loader: () => initialUserStateLoader(false),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/u/:username",
|
path: "/u/:username",
|
||||||
element: <UserProfile />,
|
element: <UserProfile />,
|
||||||
loader: () => initialUserStateLoader(false),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "*",
|
path: "*",
|
||||||
|
@ -6,12 +6,10 @@ import globalReducer from "./reducer/global";
|
|||||||
import memoReducer from "./reducer/memo";
|
import memoReducer from "./reducer/memo";
|
||||||
import resourceReducer from "./reducer/resource";
|
import resourceReducer from "./reducer/resource";
|
||||||
import tagReducer from "./reducer/tag";
|
import tagReducer from "./reducer/tag";
|
||||||
import userReducer from "./reducer/user";
|
|
||||||
|
|
||||||
const store = configureStore({
|
const store = configureStore({
|
||||||
reducer: {
|
reducer: {
|
||||||
global: globalReducer,
|
global: globalReducer,
|
||||||
user: userReducer,
|
|
||||||
memo: memoReducer,
|
memo: memoReducer,
|
||||||
tag: tagReducer,
|
tag: tagReducer,
|
||||||
filter: filterReducer,
|
filter: filterReducer,
|
||||||
|
@ -3,5 +3,4 @@ export * from "./filter";
|
|||||||
export * from "./memo";
|
export * from "./memo";
|
||||||
export * from "./tag";
|
export * from "./tag";
|
||||||
export * from "./resource";
|
export * from "./resource";
|
||||||
export * from "./user";
|
|
||||||
export * from "./dialog";
|
export * from "./dialog";
|
||||||
|
@ -1,132 +0,0 @@
|
|||||||
import { camelCase } from "lodash-es";
|
|
||||||
import { authServiceClient, userServiceClient } from "@/grpcweb";
|
|
||||||
import * as api from "@/helpers/api";
|
|
||||||
import storage from "@/helpers/storage";
|
|
||||||
import { getSystemColorScheme } from "@/helpers/utils";
|
|
||||||
import { User as UserPb } from "@/types/proto/api/v2/user_service";
|
|
||||||
import store, { useAppSelector } from "..";
|
|
||||||
import { setAppearance, setLocale } from "../reducer/global";
|
|
||||||
import { patchUser, setHost, setUser } from "../reducer/user";
|
|
||||||
|
|
||||||
const defaultSetting: Setting = {
|
|
||||||
locale: "en",
|
|
||||||
appearance: getSystemColorScheme(),
|
|
||||||
memoVisibility: "PRIVATE",
|
|
||||||
telegramUserId: "",
|
|
||||||
};
|
|
||||||
|
|
||||||
const defaultLocalSetting: LocalSetting = {
|
|
||||||
enableDoubleClickEditing: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
export const convertResponseModelUser = (user: User): User => {
|
|
||||||
// user default 'Basic Setting' should follow server's setting
|
|
||||||
// 'Basic Setting' fields: locale, appearance
|
|
||||||
const { systemStatus } = store.getState().global;
|
|
||||||
const { locale, appearance } = systemStatus.customizedProfile;
|
|
||||||
const systemSetting = { locale, appearance };
|
|
||||||
|
|
||||||
const setting: Setting = {
|
|
||||||
...defaultSetting,
|
|
||||||
...systemSetting,
|
|
||||||
};
|
|
||||||
const { localSetting: storageLocalSetting } = storage.get(["localSetting"]);
|
|
||||||
const localSetting: LocalSetting = {
|
|
||||||
...defaultLocalSetting,
|
|
||||||
...storageLocalSetting,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (user.userSettingList) {
|
|
||||||
for (const userSetting of user.userSettingList) {
|
|
||||||
(setting as any)[camelCase(userSetting.key)] = JSON.parse(userSetting.value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
...user,
|
|
||||||
setting,
|
|
||||||
localSetting,
|
|
||||||
createdTs: user.createdTs * 1000,
|
|
||||||
updatedTs: user.updatedTs * 1000,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export const initialUserState = async () => {
|
|
||||||
const { systemStatus } = store.getState().global;
|
|
||||||
|
|
||||||
if (systemStatus.host) {
|
|
||||||
store.dispatch(setHost(convertResponseModelUser(systemStatus.host)));
|
|
||||||
}
|
|
||||||
|
|
||||||
const user = await fetchCurrentUser();
|
|
||||||
if (user) {
|
|
||||||
if (user.setting.locale) {
|
|
||||||
store.dispatch(setLocale(user.setting.locale));
|
|
||||||
}
|
|
||||||
if (user.setting.appearance) {
|
|
||||||
store.dispatch(setAppearance(user.setting.appearance));
|
|
||||||
}
|
|
||||||
return user;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const doSignOut = async () => {
|
|
||||||
await api.signout();
|
|
||||||
localStorage.removeItem("userId");
|
|
||||||
};
|
|
||||||
|
|
||||||
const fetchCurrentUser = async () => {
|
|
||||||
const userId = localStorage.getItem("userId");
|
|
||||||
if (userId) {
|
|
||||||
const { user } = await authServiceClient.getAuthStatus({});
|
|
||||||
if (!user) {
|
|
||||||
localStorage.removeItem("userId");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const { data } = await api.getUserById(Number(userId));
|
|
||||||
const userMessage = convertResponseModelUser(data);
|
|
||||||
if (userMessage) {
|
|
||||||
store.dispatch(setUser(userMessage));
|
|
||||||
return userMessage;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useUserStore = () => {
|
|
||||||
const state = useAppSelector((state) => state.user);
|
|
||||||
|
|
||||||
return {
|
|
||||||
state,
|
|
||||||
getState: () => {
|
|
||||||
return store.getState().user;
|
|
||||||
},
|
|
||||||
doSignOut,
|
|
||||||
fetchCurrentUser,
|
|
||||||
setCurrentUser: async (user: User) => {
|
|
||||||
localStorage.setItem("userId", String(user.id));
|
|
||||||
},
|
|
||||||
upsertUserSetting: async (key: string, value: any) => {
|
|
||||||
await api.upsertUserSetting({
|
|
||||||
key: key as any,
|
|
||||||
value: JSON.stringify(value),
|
|
||||||
});
|
|
||||||
await fetchCurrentUser();
|
|
||||||
},
|
|
||||||
upsertLocalSetting: async (localSetting: LocalSetting) => {
|
|
||||||
storage.set({ localSetting });
|
|
||||||
store.dispatch(patchUser({ localSetting }));
|
|
||||||
},
|
|
||||||
patchUser: async (user: UserPb, updateMask: string[]): Promise<void> => {
|
|
||||||
await userServiceClient.updateUser({ user, updateMask });
|
|
||||||
// If the user is the current user and the username is changed, reload the page.
|
|
||||||
if (user.id === store.getState().user.user?.id) {
|
|
||||||
window.location.reload();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
deleteUser: async (name: string) => {
|
|
||||||
await userServiceClient.deleteUser({
|
|
||||||
name,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
};
|
|
||||||
};
|
|
@ -1,40 +0,0 @@
|
|||||||
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
|
|
||||||
|
|
||||||
interface State {
|
|
||||||
// host is the user who hist the system
|
|
||||||
host?: User;
|
|
||||||
// user is the user who is currently logged in
|
|
||||||
user?: User;
|
|
||||||
}
|
|
||||||
|
|
||||||
const userSlice = createSlice({
|
|
||||||
name: "user",
|
|
||||||
initialState: {} as State,
|
|
||||||
reducers: {
|
|
||||||
setHost: (state, action: PayloadAction<User | undefined>) => {
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
host: action.payload,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
setUser: (state, action: PayloadAction<User | undefined>) => {
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
user: action.payload,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
patchUser: (state, action: PayloadAction<Partial<User>>) => {
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
user: {
|
|
||||||
...state.user,
|
|
||||||
...action.payload,
|
|
||||||
} as User,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
export const { setHost, setUser, patchUser } = userSlice.actions;
|
|
||||||
|
|
||||||
export default userSlice.reducer;
|
|
@ -1,15 +1,29 @@
|
|||||||
import { create } from "zustand";
|
import { create } from "zustand";
|
||||||
import { userServiceClient } from "@/grpcweb";
|
import { authServiceClient, userServiceClient } from "@/grpcweb";
|
||||||
import { User } from "@/types/proto/api/v2/user_service";
|
import { User, UserSetting } from "@/types/proto/api/v2/user_service";
|
||||||
import { UserNamePrefix, extractUsernameFromName } from "./resourceName";
|
import { UserNamePrefix, extractUsernameFromName } from "./resourceName";
|
||||||
|
|
||||||
interface UserV1Store {
|
interface UserV1Store {
|
||||||
userMapByUsername: Record<string, User>;
|
userMapByUsername: Record<string, User>;
|
||||||
|
currentUser?: User;
|
||||||
|
userSetting?: UserSetting;
|
||||||
getOrFetchUserByUsername: (username: string) => Promise<User>;
|
getOrFetchUserByUsername: (username: string) => Promise<User>;
|
||||||
getUserByUsername: (username: string) => User;
|
getUserByUsername: (username: string) => User;
|
||||||
updateUser: (user: Partial<User>, updateMask: string[]) => Promise<User>;
|
updateUser: (user: Partial<User>, updateMask: string[]) => Promise<User>;
|
||||||
|
deleteUser: (name: string) => Promise<void>;
|
||||||
|
fetchCurrentUser: () => Promise<User>;
|
||||||
|
setCurrentUser: (user: User) => void;
|
||||||
|
updateUserSetting: (userSetting: Partial<UserSetting>, updateMark: string[]) => Promise<UserSetting>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getDefaultUserSetting = () => {
|
||||||
|
return UserSetting.fromPartial({
|
||||||
|
locale: "en",
|
||||||
|
appearance: "auto",
|
||||||
|
memoVisibility: "PRIVATE",
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
// Request cache is used to prevent multiple requests.
|
// Request cache is used to prevent multiple requests.
|
||||||
const requestCache = new Map<string, Promise<any>>();
|
const requestCache = new Map<string, Promise<any>>();
|
||||||
|
|
||||||
@ -57,4 +71,38 @@ export const useUserV1Store = create<UserV1Store>()((set, get) => ({
|
|||||||
set(userMap);
|
set(userMap);
|
||||||
return updatedUser;
|
return updatedUser;
|
||||||
},
|
},
|
||||||
|
deleteUser: async (name: string) => {
|
||||||
|
await userServiceClient.deleteUser({
|
||||||
|
name,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
fetchCurrentUser: async () => {
|
||||||
|
const { user } = await authServiceClient.getAuthStatus({});
|
||||||
|
if (!user) {
|
||||||
|
throw new Error("User not found");
|
||||||
|
}
|
||||||
|
set({ currentUser: user });
|
||||||
|
const { setting } = await userServiceClient.getUserSetting({});
|
||||||
|
set({
|
||||||
|
userSetting: UserSetting.fromPartial({
|
||||||
|
...getDefaultUserSetting(),
|
||||||
|
...setting,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
return user;
|
||||||
|
},
|
||||||
|
setCurrentUser: (user: User) => {
|
||||||
|
set({ currentUser: user });
|
||||||
|
},
|
||||||
|
updateUserSetting: async (userSetting: Partial<UserSetting>, updateMask: string[]) => {
|
||||||
|
const { setting: updatedUserSetting } = await userServiceClient.updateUserSetting({
|
||||||
|
setting: userSetting,
|
||||||
|
updateMask: updateMask,
|
||||||
|
});
|
||||||
|
if (!updatedUserSetting) {
|
||||||
|
throw new Error("User setting not found");
|
||||||
|
}
|
||||||
|
set({ userSetting: updatedUserSetting });
|
||||||
|
return updatedUserSetting;
|
||||||
|
},
|
||||||
}));
|
}));
|
||||||
|
Reference in New Issue
Block a user