mirror of
https://github.com/usememos/memos.git
synced 2024-12-18 16:41:44 +03:00
chore: clean duplicated requests
This commit is contained in:
parent
ca336af4fa
commit
866937787c
@ -5,8 +5,8 @@ import Icon from "./Icon";
|
||||
|
||||
interface Props {
|
||||
value: Locale;
|
||||
onChange: (locale: Locale) => void;
|
||||
className?: string;
|
||||
onChange: (locale: Locale) => void;
|
||||
}
|
||||
|
||||
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}`}>
|
||||
<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>
|
||||
<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 filterStore = useFilterStore();
|
||||
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 currentUsername = userStore.getCurrentUsername();
|
||||
@ -82,6 +83,7 @@ const MemoList: React.FC = () => {
|
||||
} else {
|
||||
setIsComplete(false);
|
||||
}
|
||||
setIsFetching(false);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
@ -122,12 +124,14 @@ const MemoList: React.FC = () => {
|
||||
|
||||
const handleFetchMoreClick = async () => {
|
||||
try {
|
||||
setIsFetching(true);
|
||||
const fetchedMemos = await memoStore.fetchMemos(DEFAULT_MEMO_LIMIT, memos.length);
|
||||
if (fetchedMemos.length < DEFAULT_MEMO_LIMIT) {
|
||||
setIsComplete(true);
|
||||
} else {
|
||||
setIsComplete(false);
|
||||
}
|
||||
setIsFetching(false);
|
||||
} catch (error: any) {
|
||||
console.error(error);
|
||||
toast.error(error.response.data.message);
|
||||
|
@ -136,17 +136,17 @@ const PreferencesSection = () => {
|
||||
<div className="inline-block min-w-full align-middle">
|
||||
<table className="min-w-full divide-y divide-gray-300">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" className="py-2 pl-4 pr-3 text-left text-sm font-semibold text-gray-900">
|
||||
<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">
|
||||
ID
|
||||
</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")}
|
||||
</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")}
|
||||
</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")}
|
||||
</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">
|
||||
{userList.map((user) => (
|
||||
<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 px-3 py-2 text-sm text-gray-500">
|
||||
<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 dark:text-gray-300">
|
||||
{user.username}
|
||||
<span className="ml-1 italic">{user.rowStatus === "ARCHIVED" && "(Archived)"}</span>
|
||||
</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">{user.email}</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 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">
|
||||
{currentUser?.id === user.id ? (
|
||||
<span>{t("common.yourself")}</span>
|
||||
|
@ -3,6 +3,7 @@ import { getMemoStats } from "@/helpers/api";
|
||||
import { DAILY_TIMESTAMP } from "@/helpers/consts";
|
||||
import { getDateStampByDate, getDateString, getTimeStampByDate } from "@/helpers/datetime";
|
||||
import * as utils from "@/helpers/utils";
|
||||
import { useUserV1Store } from "@/store/v1";
|
||||
import { useTranslate } from "@/utils/i18n";
|
||||
import { useFilterStore, useMemoStore, useUserStore } from "../store/module";
|
||||
import "@/less/usage-heat-map.less";
|
||||
@ -32,6 +33,7 @@ const UsageHeatMap = () => {
|
||||
const t = useTranslate();
|
||||
const filterStore = useFilterStore();
|
||||
const userStore = useUserStore();
|
||||
const userV1Store = useUserV1Store();
|
||||
const memoStore = useMemoStore();
|
||||
const todayTimeStamp = getDateStampByDate(Date.now());
|
||||
const todayDay = new Date(todayTimeStamp).getDay() + 1;
|
||||
@ -47,7 +49,7 @@ const UsageHeatMap = () => {
|
||||
const currentUsername = userStore.getCurrentUsername();
|
||||
|
||||
useEffect(() => {
|
||||
userStore.getUserByUsername(currentUsername).then((user) => {
|
||||
userV1Store.getOrFetchUserByUsername(currentUsername).then((user) => {
|
||||
if (!user) {
|
||||
return;
|
||||
}
|
||||
@ -56,6 +58,10 @@ const UsageHeatMap = () => {
|
||||
}, [currentUsername]);
|
||||
|
||||
useEffect(() => {
|
||||
if (memos.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
getMemoStats(currentUsername)
|
||||
.then(({ data }) => {
|
||||
setMemoAmount(data.length);
|
||||
|
@ -9,7 +9,11 @@ const UserAvatar = (props: Props) => {
|
||||
const { avatarUrl, className } = props;
|
||||
return (
|
||||
<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>
|
||||
);
|
||||
};
|
||||
|
@ -1,9 +1,6 @@
|
||||
html,
|
||||
body {
|
||||
@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 {
|
||||
|
@ -143,7 +143,7 @@ const Auth = () => {
|
||||
className="w-full"
|
||||
size="lg"
|
||||
type="text"
|
||||
disabled={actionBtnLoadingState.isLoading}
|
||||
readOnly={actionBtnLoadingState.isLoading}
|
||||
placeholder={t("common.username")}
|
||||
value={username}
|
||||
onChange={handleUsernameInputChanged}
|
||||
@ -153,7 +153,7 @@ const Auth = () => {
|
||||
className="w-full"
|
||||
size="lg"
|
||||
type="password"
|
||||
disabled={actionBtnLoadingState.isLoading}
|
||||
readOnly={actionBtnLoadingState.isLoading}
|
||||
placeholder={t("common.password")}
|
||||
value={password}
|
||||
onChange={handlePasswordInputChanged}
|
||||
|
@ -6,17 +6,19 @@ import MemoFilter from "@/components/MemoFilter";
|
||||
import MemoList from "@/components/MemoList";
|
||||
import MobileHeader from "@/components/MobileHeader";
|
||||
import { useGlobalStore, useUserStore } from "@/store/module";
|
||||
import { useUserV1Store } from "@/store/v1";
|
||||
import { useTranslate } from "@/utils/i18n";
|
||||
|
||||
const Home = () => {
|
||||
const t = useTranslate();
|
||||
const globalStore = useGlobalStore();
|
||||
const userStore = useUserStore();
|
||||
const userV1Store = useUserV1Store();
|
||||
const user = userStore.state.user;
|
||||
|
||||
useEffect(() => {
|
||||
const currentUsername = userStore.getCurrentUsername();
|
||||
userStore.getUserByUsername(currentUsername).catch((error) => {
|
||||
userV1Store.getOrFetchUserByUsername(currentUsername).catch((error) => {
|
||||
console.error(error);
|
||||
toast.error(t("message.user-not-found"));
|
||||
});
|
||||
|
@ -5,12 +5,13 @@ import FloatingNavButton from "@/components/FloatingNavButton";
|
||||
import Memo from "@/components/Memo";
|
||||
import UserAvatar from "@/components/UserAvatar";
|
||||
import useLoading from "@/hooks/useLoading";
|
||||
import { useMemoStore, useUserStore } from "@/store/module";
|
||||
import { useMemoStore } from "@/store/module";
|
||||
import { useUserV1Store } from "@/store/v1";
|
||||
|
||||
const MemoDetail = () => {
|
||||
const params = useParams();
|
||||
const memoStore = useMemoStore();
|
||||
const userStore = useUserStore();
|
||||
const userV1Store = useUserV1Store();
|
||||
const loadingState = useLoading();
|
||||
const [user, setUser] = useState<User>();
|
||||
const memoId = Number(params.memoId);
|
||||
@ -21,7 +22,7 @@ const MemoDetail = () => {
|
||||
memoStore
|
||||
.fetchMemoById(memoId)
|
||||
.then(async (memo) => {
|
||||
const user = await userStore.getUserByUsername(memo.creatorUsername);
|
||||
const user = await userV1Store.getOrFetchUserByUsername(memo.creatorUsername);
|
||||
setUser(user);
|
||||
loadingState.setFinish();
|
||||
})
|
||||
|
@ -6,18 +6,20 @@ import MemoList from "@/components/MemoList";
|
||||
import UserAvatar from "@/components/UserAvatar";
|
||||
import useLoading from "@/hooks/useLoading";
|
||||
import { useUserStore } from "@/store/module";
|
||||
import { useUserV1Store } from "@/store/v1";
|
||||
import { useTranslate } from "@/utils/i18n";
|
||||
|
||||
const UserProfile = () => {
|
||||
const t = useTranslate();
|
||||
const userStore = useUserStore();
|
||||
const userV1Store = useUserV1Store();
|
||||
const loadingState = useLoading();
|
||||
const [user, setUser] = useState<User>();
|
||||
|
||||
useEffect(() => {
|
||||
const currentUsername = userStore.getCurrentUsername();
|
||||
userStore
|
||||
.getUserByUsername(currentUsername)
|
||||
userV1Store
|
||||
.getOrFetchUserByUsername(currentUsername)
|
||||
.then((user) => {
|
||||
setUser(user);
|
||||
loadingState.setFinish();
|
||||
|
@ -2,7 +2,7 @@ import { omit } from "lodash-es";
|
||||
import * as api from "@/helpers/api";
|
||||
import { DEFAULT_MEMO_LIMIT } from "@/helpers/consts";
|
||||
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 { useUserStore } from "./";
|
||||
|
||||
@ -34,7 +34,6 @@ export const useMemoStore = () => {
|
||||
return store.getState().memo;
|
||||
},
|
||||
fetchMemos: async (limit = DEFAULT_MEMO_LIMIT, offset = 0) => {
|
||||
store.dispatch(setIsFetching(true));
|
||||
const memoFind: MemoFind = {
|
||||
rowStatus: "NORMAL",
|
||||
limit,
|
||||
@ -46,26 +45,20 @@ export const useMemoStore = () => {
|
||||
const { data } = await api.getMemoList(memoFind);
|
||||
const fetchedMemos = data.map((m) => convertResponseModelMemo(m));
|
||||
store.dispatch(upsertMemos(fetchedMemos));
|
||||
store.dispatch(setIsFetching(false));
|
||||
|
||||
for (const m of fetchedMemos) {
|
||||
memoCacheStore.setMemoCache(m);
|
||||
}
|
||||
|
||||
return fetchedMemos;
|
||||
},
|
||||
fetchAllMemos: async (limit = DEFAULT_MEMO_LIMIT, offset?: number) => {
|
||||
store.dispatch(setIsFetching(true));
|
||||
const memoFind: MemoFind = {
|
||||
rowStatus: "NORMAL",
|
||||
limit,
|
||||
offset,
|
||||
};
|
||||
|
||||
const { data } = await api.getAllMemos(memoFind);
|
||||
const fetchedMemos = data.map((m) => convertResponseModelMemo(m));
|
||||
store.dispatch(upsertMemos(fetchedMemos));
|
||||
store.dispatch(setIsFetching(false));
|
||||
|
||||
for (const m of fetchedMemos) {
|
||||
memoCacheStore.setMemoCache(m);
|
||||
|
@ -5,7 +5,7 @@ import storage from "@/helpers/storage";
|
||||
import { getSystemColorScheme } from "@/helpers/utils";
|
||||
import store, { useAppSelector } from "..";
|
||||
import { setAppearance, setLocale } from "../reducer/global";
|
||||
import { patchUser, setHost, setUser, setUserById } from "../reducer/user";
|
||||
import { patchUser, setHost, setUser } from "../reducer/user";
|
||||
|
||||
const defaultSetting: Setting = {
|
||||
locale: "en",
|
||||
@ -118,16 +118,6 @@ export const useUserStore = () => {
|
||||
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) => {
|
||||
await api.upsertUserSetting({
|
||||
key: key as any,
|
||||
|
@ -3,15 +3,12 @@ import { uniqBy } from "lodash-es";
|
||||
|
||||
interface State {
|
||||
memos: Memo[];
|
||||
isFetching: boolean;
|
||||
}
|
||||
|
||||
const memoSlice = createSlice({
|
||||
name: "memo",
|
||||
initialState: {
|
||||
memos: [],
|
||||
// isFetching flag should starts with true.
|
||||
isFetching: true,
|
||||
} as State,
|
||||
reducers: {
|
||||
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;
|
||||
|
@ -1,19 +1,15 @@
|
||||
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
|
||||
import { cloneDeep } from "lodash-es";
|
||||
|
||||
interface State {
|
||||
// host is the user who hist the system
|
||||
host?: User;
|
||||
// user is the user who is currently logged in
|
||||
user?: User;
|
||||
userById: { [key: UserId]: User };
|
||||
}
|
||||
|
||||
const userSlice = createSlice({
|
||||
name: "user",
|
||||
initialState: {
|
||||
userById: {},
|
||||
} as State,
|
||||
initialState: {} as State,
|
||||
reducers: {
|
||||
setHost: (state, action: PayloadAction<User | undefined>) => {
|
||||
return {
|
||||
@ -27,14 +23,6 @@ const userSlice = createSlice({
|
||||
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>>) => {
|
||||
return {
|
||||
...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;
|
||||
|
@ -8,6 +8,9 @@ interface UserV1Store {
|
||||
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) => ({
|
||||
userMapByUsername: {},
|
||||
getOrFetchUserByUsername: async (username: string) => {
|
||||
@ -15,8 +18,14 @@ const useUserV1Store = create<UserV1Store>()((set, get) => ({
|
||||
if (userMap[username]) {
|
||||
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);
|
||||
userMap[username] = user;
|
||||
set(userMap);
|
||||
|
Loading…
Reference in New Issue
Block a user