mirror of
https://github.com/usememos/memos.git
synced 2025-06-05 22:09:59 +02:00
chore: clean duplicated requests
This commit is contained in:
@ -5,8 +5,8 @@ import Icon from "./Icon";
|
|||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
value: Locale;
|
value: Locale;
|
||||||
onChange: (locale: Locale) => void;
|
|
||||||
className?: string;
|
className?: string;
|
||||||
|
onChange: (locale: Locale) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const LocaleSelect: FC<Props> = (props: Props) => {
|
const LocaleSelect: FC<Props> = (props: Props) => {
|
||||||
|
@ -229,7 +229,7 @@ const Memo: React.FC<Props> = (props: Props) => {
|
|||||||
<>
|
<>
|
||||||
<Link className="flex flex-row justify-start items-center" to={`/u/${memo.creatorUsername}`}>
|
<Link className="flex flex-row justify-start items-center" to={`/u/${memo.creatorUsername}`}>
|
||||||
<UserAvatar className="!w-5 !h-auto mr-1" avatarUrl={creator.avatarUrl} />
|
<UserAvatar className="!w-5 !h-auto mr-1" avatarUrl={creator.avatarUrl} />
|
||||||
<span className="text-sm text-gray-600 max-w-[8em] truncate dark:text-zinc-300">{creator.nickname}</span>
|
<span className="text-sm text-gray-600 max-w-[8em] truncate dark:text-gray-400">{creator.nickname}</span>
|
||||||
</Link>
|
</Link>
|
||||||
<Icon.Dot className="w-4 h-auto text-gray-400 dark:text-zinc-400" />
|
<Icon.Dot className="w-4 h-auto text-gray-400 dark:text-zinc-400" />
|
||||||
</>
|
</>
|
||||||
|
@ -15,7 +15,8 @@ const MemoList: React.FC = () => {
|
|||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
const filterStore = useFilterStore();
|
const filterStore = useFilterStore();
|
||||||
const filter = filterStore.state;
|
const filter = filterStore.state;
|
||||||
const { memos, isFetching } = memoStore.state;
|
const { memos } = memoStore.state;
|
||||||
|
const [isFetching, setIsFetching] = useState<boolean>(true);
|
||||||
const [isComplete, setIsComplete] = useState<boolean>(false);
|
const [isComplete, setIsComplete] = useState<boolean>(false);
|
||||||
|
|
||||||
const currentUsername = userStore.getCurrentUsername();
|
const currentUsername = userStore.getCurrentUsername();
|
||||||
@ -82,6 +83,7 @@ const MemoList: React.FC = () => {
|
|||||||
} else {
|
} else {
|
||||||
setIsComplete(false);
|
setIsComplete(false);
|
||||||
}
|
}
|
||||||
|
setIsFetching(false);
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
@ -122,12 +124,14 @@ const MemoList: React.FC = () => {
|
|||||||
|
|
||||||
const handleFetchMoreClick = async () => {
|
const handleFetchMoreClick = async () => {
|
||||||
try {
|
try {
|
||||||
|
setIsFetching(true);
|
||||||
const fetchedMemos = await memoStore.fetchMemos(DEFAULT_MEMO_LIMIT, memos.length);
|
const fetchedMemos = await memoStore.fetchMemos(DEFAULT_MEMO_LIMIT, memos.length);
|
||||||
if (fetchedMemos.length < DEFAULT_MEMO_LIMIT) {
|
if (fetchedMemos.length < DEFAULT_MEMO_LIMIT) {
|
||||||
setIsComplete(true);
|
setIsComplete(true);
|
||||||
} else {
|
} else {
|
||||||
setIsComplete(false);
|
setIsComplete(false);
|
||||||
}
|
}
|
||||||
|
setIsFetching(false);
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
toast.error(error.response.data.message);
|
toast.error(error.response.data.message);
|
||||||
|
@ -136,17 +136,17 @@ const PreferencesSection = () => {
|
|||||||
<div className="inline-block min-w-full align-middle">
|
<div className="inline-block min-w-full align-middle">
|
||||||
<table className="min-w-full divide-y divide-gray-300">
|
<table className="min-w-full divide-y divide-gray-300">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr className="text-sm font-semibold text-left text-gray-900 dark:text-gray-300">
|
||||||
<th scope="col" className="py-2 pl-4 pr-3 text-left text-sm font-semibold text-gray-900">
|
<th scope="col" className="py-2 pl-4 pr-3">
|
||||||
ID
|
ID
|
||||||
</th>
|
</th>
|
||||||
<th scope="col" className="px-3 py-2 text-left text-sm font-semibold text-gray-900">
|
<th scope="col" className="px-3 py-2">
|
||||||
{t("common.username")}
|
{t("common.username")}
|
||||||
</th>
|
</th>
|
||||||
<th scope="col" className="px-3 py-2 text-left text-sm font-semibold text-gray-900">
|
<th scope="col" className="px-3 py-2">
|
||||||
{t("common.nickname")}
|
{t("common.nickname")}
|
||||||
</th>
|
</th>
|
||||||
<th scope="col" className="px-3 py-2 text-left text-sm font-semibold text-gray-900">
|
<th scope="col" className="px-3 py-2">
|
||||||
{t("common.email")}
|
{t("common.email")}
|
||||||
</th>
|
</th>
|
||||||
<th scope="col" className="relative py-2 pl-3 pr-4"></th>
|
<th scope="col" className="relative py-2 pl-3 pr-4"></th>
|
||||||
@ -155,13 +155,13 @@ const PreferencesSection = () => {
|
|||||||
<tbody className="divide-y divide-gray-200">
|
<tbody className="divide-y divide-gray-200">
|
||||||
{userList.map((user) => (
|
{userList.map((user) => (
|
||||||
<tr key={user.id}>
|
<tr key={user.id}>
|
||||||
<td className="whitespace-nowrap py-2 pl-4 pr-3 text-sm text-gray-900">{user.id}</td>
|
<td className="whitespace-nowrap py-2 pl-4 pr-3 text-sm text-gray-900 dark:text-gray-300">{user.id}</td>
|
||||||
<td className="whitespace-nowrap px-3 py-2 text-sm text-gray-500">
|
<td className="whitespace-nowrap px-3 py-2 text-sm text-gray-500 dark:text-gray-300">
|
||||||
{user.username}
|
{user.username}
|
||||||
<span className="ml-1 italic">{user.rowStatus === "ARCHIVED" && "(Archived)"}</span>
|
<span className="ml-1 italic">{user.rowStatus === "ARCHIVED" && "(Archived)"}</span>
|
||||||
</td>
|
</td>
|
||||||
<td className="whitespace-nowrap px-3 py-2 text-sm text-gray-500">{user.nickname}</td>
|
<td className="whitespace-nowrap px-3 py-2 text-sm text-gray-500 dark:text-gray-300">{user.nickname}</td>
|
||||||
<td className="whitespace-nowrap px-3 py-2 text-sm text-gray-500">{user.email}</td>
|
<td className="whitespace-nowrap px-3 py-2 text-sm text-gray-500 dark:text-gray-300">{user.email}</td>
|
||||||
<td className="relative whitespace-nowrap py-2 pl-3 pr-4 text-right text-sm font-medium flex justify-end">
|
<td className="relative whitespace-nowrap py-2 pl-3 pr-4 text-right text-sm font-medium flex justify-end">
|
||||||
{currentUser?.id === user.id ? (
|
{currentUser?.id === user.id ? (
|
||||||
<span>{t("common.yourself")}</span>
|
<span>{t("common.yourself")}</span>
|
||||||
|
@ -3,6 +3,7 @@ import { getMemoStats } from "@/helpers/api";
|
|||||||
import { DAILY_TIMESTAMP } from "@/helpers/consts";
|
import { DAILY_TIMESTAMP } from "@/helpers/consts";
|
||||||
import { getDateStampByDate, getDateString, getTimeStampByDate } from "@/helpers/datetime";
|
import { getDateStampByDate, getDateString, getTimeStampByDate } from "@/helpers/datetime";
|
||||||
import * as utils from "@/helpers/utils";
|
import * as utils from "@/helpers/utils";
|
||||||
|
import { useUserV1Store } from "@/store/v1";
|
||||||
import { useTranslate } from "@/utils/i18n";
|
import { useTranslate } from "@/utils/i18n";
|
||||||
import { useFilterStore, useMemoStore, useUserStore } from "../store/module";
|
import { useFilterStore, useMemoStore, useUserStore } from "../store/module";
|
||||||
import "@/less/usage-heat-map.less";
|
import "@/less/usage-heat-map.less";
|
||||||
@ -32,6 +33,7 @@ const UsageHeatMap = () => {
|
|||||||
const t = useTranslate();
|
const t = useTranslate();
|
||||||
const filterStore = useFilterStore();
|
const filterStore = useFilterStore();
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
|
const userV1Store = useUserV1Store();
|
||||||
const memoStore = useMemoStore();
|
const memoStore = useMemoStore();
|
||||||
const todayTimeStamp = getDateStampByDate(Date.now());
|
const todayTimeStamp = getDateStampByDate(Date.now());
|
||||||
const todayDay = new Date(todayTimeStamp).getDay() + 1;
|
const todayDay = new Date(todayTimeStamp).getDay() + 1;
|
||||||
@ -47,7 +49,7 @@ const UsageHeatMap = () => {
|
|||||||
const currentUsername = userStore.getCurrentUsername();
|
const currentUsername = userStore.getCurrentUsername();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
userStore.getUserByUsername(currentUsername).then((user) => {
|
userV1Store.getOrFetchUserByUsername(currentUsername).then((user) => {
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -56,6 +58,10 @@ const UsageHeatMap = () => {
|
|||||||
}, [currentUsername]);
|
}, [currentUsername]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
if (memos.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
getMemoStats(currentUsername)
|
getMemoStats(currentUsername)
|
||||||
.then(({ data }) => {
|
.then(({ data }) => {
|
||||||
setMemoAmount(data.length);
|
setMemoAmount(data.length);
|
||||||
|
@ -9,7 +9,11 @@ const UserAvatar = (props: Props) => {
|
|||||||
const { avatarUrl, className } = props;
|
const { avatarUrl, className } = props;
|
||||||
return (
|
return (
|
||||||
<div className={classNames(`w-8 h-auto overflow-clip rounded-full`, className)}>
|
<div className={classNames(`w-8 h-auto overflow-clip rounded-full`, className)}>
|
||||||
<img className="w-full h-auto rounded-full min-w-full min-h-full object-cover" src={avatarUrl || "/logo.webp"} alt="" />
|
<img
|
||||||
|
className="w-full h-auto rounded-full min-w-full min-h-full object-cover dark:opacity-80"
|
||||||
|
src={avatarUrl || "/logo.webp"}
|
||||||
|
alt=""
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,9 +1,6 @@
|
|||||||
html,
|
html,
|
||||||
body {
|
body {
|
||||||
@apply text-base w-full h-full overflow-hidden dark:bg-zinc-800;
|
@apply text-base w-full h-full overflow-hidden dark:bg-zinc-800;
|
||||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "PingFang SC", "Noto Sans",
|
|
||||||
"Noto Sans CJK SC", "Microsoft YaHei UI", "Microsoft YaHei", "WenQuanYi Micro Hei", "Apple Color Emoji", "Segoe UI Emoji",
|
|
||||||
"Segoe UI Symbol", "Noto Color Emoji", sans-serif;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#root {
|
#root {
|
||||||
|
@ -143,7 +143,7 @@ const Auth = () => {
|
|||||||
className="w-full"
|
className="w-full"
|
||||||
size="lg"
|
size="lg"
|
||||||
type="text"
|
type="text"
|
||||||
disabled={actionBtnLoadingState.isLoading}
|
readOnly={actionBtnLoadingState.isLoading}
|
||||||
placeholder={t("common.username")}
|
placeholder={t("common.username")}
|
||||||
value={username}
|
value={username}
|
||||||
onChange={handleUsernameInputChanged}
|
onChange={handleUsernameInputChanged}
|
||||||
@ -153,7 +153,7 @@ const Auth = () => {
|
|||||||
className="w-full"
|
className="w-full"
|
||||||
size="lg"
|
size="lg"
|
||||||
type="password"
|
type="password"
|
||||||
disabled={actionBtnLoadingState.isLoading}
|
readOnly={actionBtnLoadingState.isLoading}
|
||||||
placeholder={t("common.password")}
|
placeholder={t("common.password")}
|
||||||
value={password}
|
value={password}
|
||||||
onChange={handlePasswordInputChanged}
|
onChange={handlePasswordInputChanged}
|
||||||
|
@ -6,17 +6,19 @@ import MemoFilter from "@/components/MemoFilter";
|
|||||||
import MemoList from "@/components/MemoList";
|
import MemoList from "@/components/MemoList";
|
||||||
import MobileHeader from "@/components/MobileHeader";
|
import MobileHeader from "@/components/MobileHeader";
|
||||||
import { useGlobalStore, useUserStore } from "@/store/module";
|
import { useGlobalStore, useUserStore } from "@/store/module";
|
||||||
|
import { useUserV1Store } from "@/store/v1";
|
||||||
import { useTranslate } from "@/utils/i18n";
|
import { useTranslate } from "@/utils/i18n";
|
||||||
|
|
||||||
const Home = () => {
|
const Home = () => {
|
||||||
const t = useTranslate();
|
const t = useTranslate();
|
||||||
const globalStore = useGlobalStore();
|
const globalStore = useGlobalStore();
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
|
const userV1Store = useUserV1Store();
|
||||||
const user = userStore.state.user;
|
const user = userStore.state.user;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const currentUsername = userStore.getCurrentUsername();
|
const currentUsername = userStore.getCurrentUsername();
|
||||||
userStore.getUserByUsername(currentUsername).catch((error) => {
|
userV1Store.getOrFetchUserByUsername(currentUsername).catch((error) => {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
toast.error(t("message.user-not-found"));
|
toast.error(t("message.user-not-found"));
|
||||||
});
|
});
|
||||||
|
@ -5,12 +5,13 @@ import FloatingNavButton from "@/components/FloatingNavButton";
|
|||||||
import Memo from "@/components/Memo";
|
import Memo from "@/components/Memo";
|
||||||
import UserAvatar from "@/components/UserAvatar";
|
import UserAvatar from "@/components/UserAvatar";
|
||||||
import useLoading from "@/hooks/useLoading";
|
import useLoading from "@/hooks/useLoading";
|
||||||
import { useMemoStore, useUserStore } from "@/store/module";
|
import { useMemoStore } from "@/store/module";
|
||||||
|
import { useUserV1Store } from "@/store/v1";
|
||||||
|
|
||||||
const MemoDetail = () => {
|
const MemoDetail = () => {
|
||||||
const params = useParams();
|
const params = useParams();
|
||||||
const memoStore = useMemoStore();
|
const memoStore = useMemoStore();
|
||||||
const userStore = useUserStore();
|
const userV1Store = useUserV1Store();
|
||||||
const loadingState = useLoading();
|
const loadingState = useLoading();
|
||||||
const [user, setUser] = useState<User>();
|
const [user, setUser] = useState<User>();
|
||||||
const memoId = Number(params.memoId);
|
const memoId = Number(params.memoId);
|
||||||
@ -21,7 +22,7 @@ const MemoDetail = () => {
|
|||||||
memoStore
|
memoStore
|
||||||
.fetchMemoById(memoId)
|
.fetchMemoById(memoId)
|
||||||
.then(async (memo) => {
|
.then(async (memo) => {
|
||||||
const user = await userStore.getUserByUsername(memo.creatorUsername);
|
const user = await userV1Store.getOrFetchUserByUsername(memo.creatorUsername);
|
||||||
setUser(user);
|
setUser(user);
|
||||||
loadingState.setFinish();
|
loadingState.setFinish();
|
||||||
})
|
})
|
||||||
|
@ -6,18 +6,20 @@ import MemoList from "@/components/MemoList";
|
|||||||
import UserAvatar from "@/components/UserAvatar";
|
import UserAvatar from "@/components/UserAvatar";
|
||||||
import useLoading from "@/hooks/useLoading";
|
import useLoading from "@/hooks/useLoading";
|
||||||
import { useUserStore } from "@/store/module";
|
import { useUserStore } from "@/store/module";
|
||||||
|
import { useUserV1Store } from "@/store/v1";
|
||||||
import { useTranslate } from "@/utils/i18n";
|
import { useTranslate } from "@/utils/i18n";
|
||||||
|
|
||||||
const UserProfile = () => {
|
const UserProfile = () => {
|
||||||
const t = useTranslate();
|
const t = useTranslate();
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
|
const userV1Store = useUserV1Store();
|
||||||
const loadingState = useLoading();
|
const loadingState = useLoading();
|
||||||
const [user, setUser] = useState<User>();
|
const [user, setUser] = useState<User>();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const currentUsername = userStore.getCurrentUsername();
|
const currentUsername = userStore.getCurrentUsername();
|
||||||
userStore
|
userV1Store
|
||||||
.getUserByUsername(currentUsername)
|
.getOrFetchUserByUsername(currentUsername)
|
||||||
.then((user) => {
|
.then((user) => {
|
||||||
setUser(user);
|
setUser(user);
|
||||||
loadingState.setFinish();
|
loadingState.setFinish();
|
||||||
|
@ -2,7 +2,7 @@ import { omit } from "lodash-es";
|
|||||||
import * as api from "@/helpers/api";
|
import * as api from "@/helpers/api";
|
||||||
import { DEFAULT_MEMO_LIMIT } from "@/helpers/consts";
|
import { DEFAULT_MEMO_LIMIT } from "@/helpers/consts";
|
||||||
import store, { useAppSelector } from "../";
|
import store, { useAppSelector } from "../";
|
||||||
import { createMemo, deleteMemo, patchMemo, setIsFetching, upsertMemos } from "../reducer/memo";
|
import { createMemo, deleteMemo, patchMemo, upsertMemos } from "../reducer/memo";
|
||||||
import { useMemoCacheStore } from "../v1";
|
import { useMemoCacheStore } from "../v1";
|
||||||
import { useUserStore } from "./";
|
import { useUserStore } from "./";
|
||||||
|
|
||||||
@ -34,7 +34,6 @@ export const useMemoStore = () => {
|
|||||||
return store.getState().memo;
|
return store.getState().memo;
|
||||||
},
|
},
|
||||||
fetchMemos: async (limit = DEFAULT_MEMO_LIMIT, offset = 0) => {
|
fetchMemos: async (limit = DEFAULT_MEMO_LIMIT, offset = 0) => {
|
||||||
store.dispatch(setIsFetching(true));
|
|
||||||
const memoFind: MemoFind = {
|
const memoFind: MemoFind = {
|
||||||
rowStatus: "NORMAL",
|
rowStatus: "NORMAL",
|
||||||
limit,
|
limit,
|
||||||
@ -46,26 +45,20 @@ export const useMemoStore = () => {
|
|||||||
const { data } = await api.getMemoList(memoFind);
|
const { data } = await api.getMemoList(memoFind);
|
||||||
const fetchedMemos = data.map((m) => convertResponseModelMemo(m));
|
const fetchedMemos = data.map((m) => convertResponseModelMemo(m));
|
||||||
store.dispatch(upsertMemos(fetchedMemos));
|
store.dispatch(upsertMemos(fetchedMemos));
|
||||||
store.dispatch(setIsFetching(false));
|
|
||||||
|
|
||||||
for (const m of fetchedMemos) {
|
for (const m of fetchedMemos) {
|
||||||
memoCacheStore.setMemoCache(m);
|
memoCacheStore.setMemoCache(m);
|
||||||
}
|
}
|
||||||
|
|
||||||
return fetchedMemos;
|
return fetchedMemos;
|
||||||
},
|
},
|
||||||
fetchAllMemos: async (limit = DEFAULT_MEMO_LIMIT, offset?: number) => {
|
fetchAllMemos: async (limit = DEFAULT_MEMO_LIMIT, offset?: number) => {
|
||||||
store.dispatch(setIsFetching(true));
|
|
||||||
const memoFind: MemoFind = {
|
const memoFind: MemoFind = {
|
||||||
rowStatus: "NORMAL",
|
rowStatus: "NORMAL",
|
||||||
limit,
|
limit,
|
||||||
offset,
|
offset,
|
||||||
};
|
};
|
||||||
|
|
||||||
const { data } = await api.getAllMemos(memoFind);
|
const { data } = await api.getAllMemos(memoFind);
|
||||||
const fetchedMemos = data.map((m) => convertResponseModelMemo(m));
|
const fetchedMemos = data.map((m) => convertResponseModelMemo(m));
|
||||||
store.dispatch(upsertMemos(fetchedMemos));
|
store.dispatch(upsertMemos(fetchedMemos));
|
||||||
store.dispatch(setIsFetching(false));
|
|
||||||
|
|
||||||
for (const m of fetchedMemos) {
|
for (const m of fetchedMemos) {
|
||||||
memoCacheStore.setMemoCache(m);
|
memoCacheStore.setMemoCache(m);
|
||||||
|
@ -5,7 +5,7 @@ import storage from "@/helpers/storage";
|
|||||||
import { getSystemColorScheme } from "@/helpers/utils";
|
import { getSystemColorScheme } from "@/helpers/utils";
|
||||||
import store, { useAppSelector } from "..";
|
import store, { useAppSelector } from "..";
|
||||||
import { setAppearance, setLocale } from "../reducer/global";
|
import { setAppearance, setLocale } from "../reducer/global";
|
||||||
import { patchUser, setHost, setUser, setUserById } from "../reducer/user";
|
import { patchUser, setHost, setUser } from "../reducer/user";
|
||||||
|
|
||||||
const defaultSetting: Setting = {
|
const defaultSetting: Setting = {
|
||||||
locale: "en",
|
locale: "en",
|
||||||
@ -118,16 +118,6 @@ export const useUserStore = () => {
|
|||||||
return state.user?.username || UNKNOWN_USERNAME;
|
return state.user?.username || UNKNOWN_USERNAME;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
getUserByUsername: async (username: string) => {
|
|
||||||
const { data } = await api.getUserByUsername(username);
|
|
||||||
if (data) {
|
|
||||||
const user = convertResponseModelUser(data);
|
|
||||||
store.dispatch(setUserById(user));
|
|
||||||
return user;
|
|
||||||
} else {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
upsertUserSetting: async (key: string, value: any) => {
|
upsertUserSetting: async (key: string, value: any) => {
|
||||||
await api.upsertUserSetting({
|
await api.upsertUserSetting({
|
||||||
key: key as any,
|
key: key as any,
|
||||||
|
@ -3,15 +3,12 @@ import { uniqBy } from "lodash-es";
|
|||||||
|
|
||||||
interface State {
|
interface State {
|
||||||
memos: Memo[];
|
memos: Memo[];
|
||||||
isFetching: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const memoSlice = createSlice({
|
const memoSlice = createSlice({
|
||||||
name: "memo",
|
name: "memo",
|
||||||
initialState: {
|
initialState: {
|
||||||
memos: [],
|
memos: [],
|
||||||
// isFetching flag should starts with true.
|
|
||||||
isFetching: true,
|
|
||||||
} as State,
|
} as State,
|
||||||
reducers: {
|
reducers: {
|
||||||
upsertMemos: (state, action: PayloadAction<Memo[]>) => {
|
upsertMemos: (state, action: PayloadAction<Memo[]>) => {
|
||||||
@ -51,15 +48,9 @@ const memoSlice = createSlice({
|
|||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
setIsFetching: (state, action: PayloadAction<boolean>) => {
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
isFetching: action.payload,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export const { upsertMemos, createMemo, patchMemo, deleteMemo, setIsFetching } = memoSlice.actions;
|
export const { upsertMemos, createMemo, patchMemo, deleteMemo } = memoSlice.actions;
|
||||||
|
|
||||||
export default memoSlice.reducer;
|
export default memoSlice.reducer;
|
||||||
|
@ -1,19 +1,15 @@
|
|||||||
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
|
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
|
||||||
import { cloneDeep } from "lodash-es";
|
|
||||||
|
|
||||||
interface State {
|
interface State {
|
||||||
// host is the user who hist the system
|
// host is the user who hist the system
|
||||||
host?: User;
|
host?: User;
|
||||||
// user is the user who is currently logged in
|
// user is the user who is currently logged in
|
||||||
user?: User;
|
user?: User;
|
||||||
userById: { [key: UserId]: User };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const userSlice = createSlice({
|
const userSlice = createSlice({
|
||||||
name: "user",
|
name: "user",
|
||||||
initialState: {
|
initialState: {} as State,
|
||||||
userById: {},
|
|
||||||
} as State,
|
|
||||||
reducers: {
|
reducers: {
|
||||||
setHost: (state, action: PayloadAction<User | undefined>) => {
|
setHost: (state, action: PayloadAction<User | undefined>) => {
|
||||||
return {
|
return {
|
||||||
@ -27,14 +23,6 @@ const userSlice = createSlice({
|
|||||||
user: action.payload,
|
user: action.payload,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
setUserById: (state, action: PayloadAction<User>) => {
|
|
||||||
const userById = cloneDeep(state.userById);
|
|
||||||
userById[action.payload.id] = action.payload;
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
userById: userById,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
patchUser: (state, action: PayloadAction<Partial<User>>) => {
|
patchUser: (state, action: PayloadAction<Partial<User>>) => {
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
@ -47,6 +35,6 @@ const userSlice = createSlice({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export const { setHost, setUser, setUserById, patchUser } = userSlice.actions;
|
export const { setHost, setUser, patchUser } = userSlice.actions;
|
||||||
|
|
||||||
export default userSlice.reducer;
|
export default userSlice.reducer;
|
||||||
|
@ -8,6 +8,9 @@ interface UserV1Store {
|
|||||||
getUserByUsername: (username: string) => User;
|
getUserByUsername: (username: string) => User;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Request cache is used to prevent multiple requests.
|
||||||
|
const requestCache = new Map<string, Promise<any>>();
|
||||||
|
|
||||||
const useUserV1Store = create<UserV1Store>()((set, get) => ({
|
const useUserV1Store = create<UserV1Store>()((set, get) => ({
|
||||||
userMapByUsername: {},
|
userMapByUsername: {},
|
||||||
getOrFetchUserByUsername: async (username: string) => {
|
getOrFetchUserByUsername: async (username: string) => {
|
||||||
@ -15,8 +18,14 @@ const useUserV1Store = create<UserV1Store>()((set, get) => ({
|
|||||||
if (userMap[username]) {
|
if (userMap[username]) {
|
||||||
return userMap[username] as User;
|
return userMap[username] as User;
|
||||||
}
|
}
|
||||||
|
if (requestCache.has(username)) {
|
||||||
|
return await requestCache.get(username);
|
||||||
|
}
|
||||||
|
|
||||||
const { data } = await api.getUserByUsername(username);
|
const promise = api.getUserByUsername(username);
|
||||||
|
requestCache.set(username, promise);
|
||||||
|
const { data } = await promise;
|
||||||
|
requestCache.delete(username);
|
||||||
const user = convertResponseModelUser(data);
|
const user = convertResponseModelUser(data);
|
||||||
userMap[username] = user;
|
userMap[username] = user;
|
||||||
set(userMap);
|
set(userMap);
|
||||||
|
Reference in New Issue
Block a user