mirror of
https://github.com/usememos/memos.git
synced 2024-12-26 12:52:27 +03:00
refactor: user v1 store (#2047)
This commit is contained in:
parent
f5793c142c
commit
a6a1898c41
@ -20,7 +20,7 @@
|
|||||||
"i18next": "^21.9.2",
|
"i18next": "^21.9.2",
|
||||||
"i18next-browser-languagedetector": "^7.0.1",
|
"i18next-browser-languagedetector": "^7.0.1",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
"lucide-react": "^0.105.0",
|
"lucide-react": "^0.263.0",
|
||||||
"qrcode.react": "^3.1.0",
|
"qrcode.react": "^3.1.0",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
|
@ -42,8 +42,8 @@ dependencies:
|
|||||||
specifier: ^4.17.21
|
specifier: ^4.17.21
|
||||||
version: 4.17.21
|
version: 4.17.21
|
||||||
lucide-react:
|
lucide-react:
|
||||||
specifier: ^0.105.0
|
specifier: ^0.263.0
|
||||||
version: 0.105.0(react@18.2.0)
|
version: 0.263.0(react@18.2.0)
|
||||||
qrcode.react:
|
qrcode.react:
|
||||||
specifier: ^3.1.0
|
specifier: ^3.1.0
|
||||||
version: 3.1.0(react@18.2.0)
|
version: 3.1.0(react@18.2.0)
|
||||||
@ -2428,8 +2428,8 @@ packages:
|
|||||||
dependencies:
|
dependencies:
|
||||||
yallist: 4.0.0
|
yallist: 4.0.0
|
||||||
|
|
||||||
/lucide-react@0.105.0(react@18.2.0):
|
/lucide-react@0.263.0(react@18.2.0):
|
||||||
resolution: {integrity: sha512-iHaIkd4Wq6aNIVrFMXt3If8E/+2lnJd4WlCyntoJNIzZ8nWhdSSHWpsw7XM4rlw2319LZ2t4WLdnM8Z0ECDTOQ==}
|
resolution: {integrity: sha512-F+rHswbbI1xuDZ/OzofiJZJVlBPOIYVVST705cPdRLImJ5aOJNXYaFBPNo3qdUV0iEG/4nZeiUtLSHO2qU2ISw==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
react: ^16.5.1 || ^17.0.0 || ^18.0.0
|
react: ^16.5.1 || ^17.0.0 || ^18.0.0
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -8,7 +8,7 @@ import { Link } from "react-router-dom";
|
|||||||
import { useFilterStore, useMemoStore, useUserStore } from "@/store/module";
|
import { useFilterStore, useMemoStore, useUserStore } from "@/store/module";
|
||||||
import { UNKNOWN_ID } from "@/helpers/consts";
|
import { UNKNOWN_ID } from "@/helpers/consts";
|
||||||
import { getRelativeTimeString } from "@/helpers/datetime";
|
import { getRelativeTimeString } from "@/helpers/datetime";
|
||||||
import { useMemoCacheStore } from "@/store/zustand";
|
import { useMemoCacheStore, useUserV1Store } from "@/store/v1";
|
||||||
import { showCommonDialog } from "./Dialog/CommonDialog";
|
import { showCommonDialog } from "./Dialog/CommonDialog";
|
||||||
import Icon from "./Icon";
|
import Icon from "./Icon";
|
||||||
import MemoContent from "./MemoContent";
|
import MemoContent from "./MemoContent";
|
||||||
@ -18,6 +18,7 @@ import showShareMemo from "./ShareMemoDialog";
|
|||||||
import showPreviewImageDialog from "./PreviewImageDialog";
|
import showPreviewImageDialog from "./PreviewImageDialog";
|
||||||
import showChangeMemoCreatedTsDialog from "./ChangeMemoCreatedTsDialog";
|
import showChangeMemoCreatedTsDialog from "./ChangeMemoCreatedTsDialog";
|
||||||
import showMemoEditorDialog from "./MemoEditor/MemoEditorDialog";
|
import showMemoEditorDialog from "./MemoEditor/MemoEditorDialog";
|
||||||
|
import UserAvatar from "./UserAvatar";
|
||||||
import "@/less/memo.less";
|
import "@/less/memo.less";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@ -36,11 +37,19 @@ const Memo: React.FC<Props> = (props: Props) => {
|
|||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
const memoStore = useMemoStore();
|
const memoStore = useMemoStore();
|
||||||
const memoCacheStore = useMemoCacheStore();
|
const memoCacheStore = useMemoCacheStore();
|
||||||
|
const userV1Store = useUserV1Store();
|
||||||
const [createdTimeStr, setCreatedTimeStr] = useState<string>(getRelativeTimeString(memo.displayTs));
|
const [createdTimeStr, setCreatedTimeStr] = useState<string>(getRelativeTimeString(memo.displayTs));
|
||||||
const [relatedMemoList, setRelatedMemoList] = useState<Memo[]>([]);
|
const [relatedMemoList, setRelatedMemoList] = useState<Memo[]>([]);
|
||||||
const memoContainerRef = useRef<HTMLDivElement>(null);
|
const memoContainerRef = useRef<HTMLDivElement>(null);
|
||||||
const readonly = userStore.isVisitorMode() || userStore.getCurrentUsername() !== memo.creatorUsername;
|
const readonly = userStore.isVisitorMode() || userStore.getCurrentUsername() !== memo.creatorUsername;
|
||||||
|
const creator = userV1Store.getUserByUsername(memo.creatorUsername);
|
||||||
|
|
||||||
|
// Prepare memo creator.
|
||||||
|
useEffect(() => {
|
||||||
|
userV1Store.getOrFetchUserByUsername(memo.creatorUsername);
|
||||||
|
}, [memo.creatorUsername]);
|
||||||
|
|
||||||
|
// Prepare related memos.
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
Promise.allSettled(memo.relationList.map((memoRelation) => memoCacheStore.getOrFetchMemoById(memoRelation.relatedMemoId))).then(
|
Promise.allSettled(memo.relationList.map((memoRelation) => memoCacheStore.getOrFetchMemoById(memoRelation.relatedMemoId))).then(
|
||||||
(results) => {
|
(results) => {
|
||||||
@ -217,14 +226,18 @@ const Memo: React.FC<Props> = (props: Props) => {
|
|||||||
<div className={`memo-wrapper ${"memos-" + memo.id} ${memo.pinned && !readonly ? "pinned" : ""}`} ref={memoContainerRef}>
|
<div className={`memo-wrapper ${"memos-" + memo.id} ${memo.pinned && !readonly ? "pinned" : ""}`} ref={memoContainerRef}>
|
||||||
<div className="memo-top-wrapper">
|
<div className="memo-top-wrapper">
|
||||||
<div className="status-text-container">
|
<div className="status-text-container">
|
||||||
|
{showCreator && creator && (
|
||||||
|
<>
|
||||||
|
<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 dark:text-zinc-300">{creator.nickname}</span>
|
||||||
|
</Link>
|
||||||
|
<Icon.Dot className="w-4 h-auto text-gray-400 dark:text-zinc-400" />
|
||||||
|
</>
|
||||||
|
)}
|
||||||
<Link className="time-text" to={`/m/${memo.id}`} onClick={handleMemoCreatedTimeClick}>
|
<Link className="time-text" to={`/m/${memo.id}`} onClick={handleMemoCreatedTimeClick}>
|
||||||
{createdTimeStr}
|
{createdTimeStr}
|
||||||
</Link>
|
</Link>
|
||||||
{showCreator && (
|
|
||||||
<Link className="name-text" to={`/u/${memo.creatorUsername}`}>
|
|
||||||
@{memo.creatorName}
|
|
||||||
</Link>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
<div className="btns-container space-x-2">
|
<div className="btns-container space-x-2">
|
||||||
{showVisibility && memo.visibility !== "PRIVATE" && (
|
{showVisibility && memo.visibility !== "PRIVATE" && (
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Message } from "@/store/zustand/message";
|
import { Message } from "@/store/v1/message";
|
||||||
import { marked } from "@/labs/marked";
|
import { marked } from "@/labs/marked";
|
||||||
import Icon from "@/components/Icon";
|
import Icon from "@/components/Icon";
|
||||||
import Dropdown from "../kit/Dropdown";
|
import Dropdown from "../kit/Dropdown";
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Conversation } from "@/store/zustand/conversation";
|
import { Conversation } from "@/store/v1/conversation";
|
||||||
import Icon from "@/components/Icon";
|
import Icon from "@/components/Icon";
|
||||||
|
|
||||||
interface ConversationTabProps {
|
interface ConversationTabProps {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { useMemoCacheStore } from "@/store/zustand";
|
import { useMemoCacheStore } from "@/store/v1";
|
||||||
import Icon from "../Icon";
|
import Icon from "../Icon";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { useMemoCacheStore } from "@/store/zustand";
|
import { useMemoCacheStore } from "@/store/v1";
|
||||||
import Icon from "./Icon";
|
import Icon from "./Icon";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import classNames from "classnames";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
avatarUrl?: string;
|
avatarUrl?: string;
|
||||||
className?: string;
|
className?: string;
|
||||||
@ -6,7 +8,7 @@ interface Props {
|
|||||||
const UserAvatar = (props: Props) => {
|
const UserAvatar = (props: Props) => {
|
||||||
const { avatarUrl, className } = props;
|
const { avatarUrl, className } = props;
|
||||||
return (
|
return (
|
||||||
<div className={`${className ?? ""} w-8 h-8 overflow-clip`}>
|
<div className={classNames(`w-8 h-8 overflow-clip`, 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" src={avatarUrl || "/logo.webp"} alt="" />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import { fetchEventSource } from "@microsoft/fetch-event-source";
|
|
||||||
import { Message } from "@/store/zustand/message";
|
|
||||||
|
|
||||||
export function getSystemStatus() {
|
export function getSystemStatus() {
|
||||||
return axios.get<SystemStatus>("/api/v1/status");
|
return axios.get<SystemStatus>("/api/v1/status");
|
||||||
@ -139,32 +137,11 @@ export function unpinMemo(memoId: MemoId) {
|
|||||||
export function deleteMemo(memoId: MemoId) {
|
export function deleteMemo(memoId: MemoId) {
|
||||||
return axios.delete(`/api/v1/memo/${memoId}`);
|
return axios.delete(`/api/v1/memo/${memoId}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function checkOpenAIEnabled() {
|
export function checkOpenAIEnabled() {
|
||||||
return axios.get<boolean>(`/api/openai/enabled`);
|
return axios.get<boolean>(`/api/openai/enabled`);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function chatStreaming(messageList: Array<Message>, onmessage: any, onclose: any) {
|
|
||||||
await fetchEventSource("/api/v1/openai/chat-streaming", {
|
|
||||||
method: "POST",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
},
|
|
||||||
body: JSON.stringify(messageList),
|
|
||||||
async onopen() {
|
|
||||||
// to do nth
|
|
||||||
},
|
|
||||||
onmessage(event: any) {
|
|
||||||
onmessage(event);
|
|
||||||
},
|
|
||||||
onclose() {
|
|
||||||
onclose();
|
|
||||||
},
|
|
||||||
onerror(error: any) {
|
|
||||||
console.log("error", error);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getShortcutList(shortcutFind?: ShortcutFind) {
|
export function getShortcutList(shortcutFind?: ShortcutFind) {
|
||||||
const queryList = [];
|
const queryList = [];
|
||||||
if (shortcutFind?.creatorUsername) {
|
if (shortcutFind?.creatorUsername) {
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
import { Button, Stack } from "@mui/joy";
|
import { Button, Stack } from "@mui/joy";
|
||||||
|
import { fetchEventSource } from "@microsoft/fetch-event-source";
|
||||||
import { head } from "lodash-es";
|
import { head } from "lodash-es";
|
||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import { toast } from "react-hot-toast";
|
import { toast } from "react-hot-toast";
|
||||||
import { useTranslate } from "@/utils/i18n";
|
import { useTranslate } from "@/utils/i18n";
|
||||||
import * as api from "@/helpers/api";
|
import * as api from "@/helpers/api";
|
||||||
import useLoading from "@/hooks/useLoading";
|
import useLoading from "@/hooks/useLoading";
|
||||||
import { useMessageStore } from "@/store/zustand/message";
|
import { Message, useMessageStore } from "@/store/v1/message";
|
||||||
import { Conversation, useConversationStore } from "@/store/zustand/conversation";
|
import { Conversation, useConversationStore } from "@/store/v1/conversation";
|
||||||
import Icon from "@/components/Icon";
|
import Icon from "@/components/Icon";
|
||||||
import { generateUUID } from "@/utils/uuid";
|
import { generateUUID } from "@/utils/uuid";
|
||||||
import MobileHeader from "@/components/MobileHeader";
|
import MobileHeader from "@/components/MobileHeader";
|
||||||
@ -15,6 +16,28 @@ import ChatInput from "@/components/MemoChat/ChatInput";
|
|||||||
import ConversationTab from "@/components/MemoChat/ConversationTab";
|
import ConversationTab from "@/components/MemoChat/ConversationTab";
|
||||||
import Empty from "@/components/Empty";
|
import Empty from "@/components/Empty";
|
||||||
|
|
||||||
|
const chatStreaming = async (messageList: Array<Message>, onmessage: any, onclose: any) => {
|
||||||
|
await fetchEventSource("/api/v1/openai/chat-streaming", {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify(messageList),
|
||||||
|
async onopen() {
|
||||||
|
// to do nth
|
||||||
|
},
|
||||||
|
onmessage(event: any) {
|
||||||
|
onmessage(event);
|
||||||
|
},
|
||||||
|
onclose() {
|
||||||
|
onclose();
|
||||||
|
},
|
||||||
|
onerror(error: any) {
|
||||||
|
console.log("error", error);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const MemoChat = () => {
|
const MemoChat = () => {
|
||||||
const t = useTranslate();
|
const t = useTranslate();
|
||||||
const fetchingState = useLoading(false);
|
const fetchingState = useLoading(false);
|
||||||
@ -91,7 +114,7 @@ const MemoChat = () => {
|
|||||||
|
|
||||||
const fetchChatStreaming = async (messageId: string) => {
|
const fetchChatStreaming = async (messageId: string) => {
|
||||||
const messageList = messageStore.getState().messageList;
|
const messageList = messageStore.getState().messageList;
|
||||||
await api.chatStreaming(
|
await chatStreaming(
|
||||||
messageList,
|
messageList,
|
||||||
async (event: any) => {
|
async (event: any) => {
|
||||||
messageStore.updateMessage(messageId, event.data);
|
messageStore.updateMessage(messageId, event.data);
|
||||||
|
@ -4,7 +4,7 @@ import { DEFAULT_MEMO_LIMIT } from "@/helpers/consts";
|
|||||||
import { useUserStore } from "./";
|
import { useUserStore } from "./";
|
||||||
import store, { useAppSelector } from "../";
|
import store, { useAppSelector } from "../";
|
||||||
import { createMemo, deleteMemo, patchMemo, setIsFetching, upsertMemos } from "../reducer/memo";
|
import { createMemo, deleteMemo, patchMemo, setIsFetching, upsertMemos } from "../reducer/memo";
|
||||||
import { useMemoCacheStore } from "../zustand/memo";
|
import { useMemoCacheStore } from "../v1";
|
||||||
|
|
||||||
export const convertResponseModelMemo = (memo: Memo): Memo => {
|
export const convertResponseModelMemo = (memo: Memo): Memo => {
|
||||||
return {
|
return {
|
||||||
|
4
web/src/store/v1/index.ts
Normal file
4
web/src/store/v1/index.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
import useMemoCacheStore from "./memo";
|
||||||
|
import useUserV1Store from "./user";
|
||||||
|
|
||||||
|
export { useUserV1Store, useMemoCacheStore };
|
@ -3,7 +3,7 @@ import { combine } from "zustand/middleware";
|
|||||||
import * as api from "@/helpers/api";
|
import * as api from "@/helpers/api";
|
||||||
import { convertResponseModelMemo } from "../module";
|
import { convertResponseModelMemo } from "../module";
|
||||||
|
|
||||||
export const useMemoCacheStore = create(
|
const useMemoCacheStore = create(
|
||||||
combine({ memoById: new Map<MemoId, Memo>() }, (set, get) => ({
|
combine({ memoById: new Map<MemoId, Memo>() }, (set, get) => ({
|
||||||
getState: () => get(),
|
getState: () => get(),
|
||||||
getOrFetchMemoById: async (memoId: MemoId) => {
|
getOrFetchMemoById: async (memoId: MemoId) => {
|
||||||
@ -39,3 +39,5 @@ export const useMemoCacheStore = create(
|
|||||||
},
|
},
|
||||||
}))
|
}))
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export default useMemoCacheStore;
|
31
web/src/store/v1/user.ts
Normal file
31
web/src/store/v1/user.ts
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
import { create } from "zustand";
|
||||||
|
import * as api from "@/helpers/api";
|
||||||
|
import { convertResponseModelUser } from "../module";
|
||||||
|
|
||||||
|
interface UserV1Store {
|
||||||
|
userMapByUsername: Record<string, User>;
|
||||||
|
getOrFetchUserByUsername: (username: string) => Promise<User>;
|
||||||
|
getUserByUsername: (username: string) => User;
|
||||||
|
}
|
||||||
|
|
||||||
|
const useUserV1Store = create<UserV1Store>()((set, get) => ({
|
||||||
|
userMapByUsername: {},
|
||||||
|
getOrFetchUserByUsername: async (username: string) => {
|
||||||
|
const userMap = get().userMapByUsername;
|
||||||
|
if (userMap[username]) {
|
||||||
|
return userMap[username] as User;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { data } = await api.getUserByUsername(username);
|
||||||
|
const user = convertResponseModelUser(data);
|
||||||
|
userMap[username] = user;
|
||||||
|
set(userMap);
|
||||||
|
return user;
|
||||||
|
},
|
||||||
|
getUserByUsername: (username: string) => {
|
||||||
|
const userMap = get().userMapByUsername;
|
||||||
|
return userMap[username] as User;
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
export default useUserV1Store;
|
@ -1 +0,0 @@
|
|||||||
export { useMemoCacheStore } from "./memo";
|
|
Loading…
Reference in New Issue
Block a user