mirror of
https://github.com/usememos/memos.git
synced 2024-12-25 12:23:09 +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-browser-languagedetector": "^7.0.1",
|
||||
"lodash-es": "^4.17.21",
|
||||
"lucide-react": "^0.105.0",
|
||||
"lucide-react": "^0.263.0",
|
||||
"qrcode.react": "^3.1.0",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
|
@ -42,8 +42,8 @@ dependencies:
|
||||
specifier: ^4.17.21
|
||||
version: 4.17.21
|
||||
lucide-react:
|
||||
specifier: ^0.105.0
|
||||
version: 0.105.0(react@18.2.0)
|
||||
specifier: ^0.263.0
|
||||
version: 0.263.0(react@18.2.0)
|
||||
qrcode.react:
|
||||
specifier: ^3.1.0
|
||||
version: 3.1.0(react@18.2.0)
|
||||
@ -2428,8 +2428,8 @@ packages:
|
||||
dependencies:
|
||||
yallist: 4.0.0
|
||||
|
||||
/lucide-react@0.105.0(react@18.2.0):
|
||||
resolution: {integrity: sha512-iHaIkd4Wq6aNIVrFMXt3If8E/+2lnJd4WlCyntoJNIzZ8nWhdSSHWpsw7XM4rlw2319LZ2t4WLdnM8Z0ECDTOQ==}
|
||||
/lucide-react@0.263.0(react@18.2.0):
|
||||
resolution: {integrity: sha512-F+rHswbbI1xuDZ/OzofiJZJVlBPOIYVVST705cPdRLImJ5aOJNXYaFBPNo3qdUV0iEG/4nZeiUtLSHO2qU2ISw==}
|
||||
peerDependencies:
|
||||
react: ^16.5.1 || ^17.0.0 || ^18.0.0
|
||||
dependencies:
|
||||
|
@ -8,7 +8,7 @@ import { Link } from "react-router-dom";
|
||||
import { useFilterStore, useMemoStore, useUserStore } from "@/store/module";
|
||||
import { UNKNOWN_ID } from "@/helpers/consts";
|
||||
import { getRelativeTimeString } from "@/helpers/datetime";
|
||||
import { useMemoCacheStore } from "@/store/zustand";
|
||||
import { useMemoCacheStore, useUserV1Store } from "@/store/v1";
|
||||
import { showCommonDialog } from "./Dialog/CommonDialog";
|
||||
import Icon from "./Icon";
|
||||
import MemoContent from "./MemoContent";
|
||||
@ -18,6 +18,7 @@ import showShareMemo from "./ShareMemoDialog";
|
||||
import showPreviewImageDialog from "./PreviewImageDialog";
|
||||
import showChangeMemoCreatedTsDialog from "./ChangeMemoCreatedTsDialog";
|
||||
import showMemoEditorDialog from "./MemoEditor/MemoEditorDialog";
|
||||
import UserAvatar from "./UserAvatar";
|
||||
import "@/less/memo.less";
|
||||
|
||||
interface Props {
|
||||
@ -36,11 +37,19 @@ const Memo: React.FC<Props> = (props: Props) => {
|
||||
const userStore = useUserStore();
|
||||
const memoStore = useMemoStore();
|
||||
const memoCacheStore = useMemoCacheStore();
|
||||
const userV1Store = useUserV1Store();
|
||||
const [createdTimeStr, setCreatedTimeStr] = useState<string>(getRelativeTimeString(memo.displayTs));
|
||||
const [relatedMemoList, setRelatedMemoList] = useState<Memo[]>([]);
|
||||
const memoContainerRef = useRef<HTMLDivElement>(null);
|
||||
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(() => {
|
||||
Promise.allSettled(memo.relationList.map((memoRelation) => memoCacheStore.getOrFetchMemoById(memoRelation.relatedMemoId))).then(
|
||||
(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-top-wrapper">
|
||||
<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}>
|
||||
{createdTimeStr}
|
||||
</Link>
|
||||
{showCreator && (
|
||||
<Link className="name-text" to={`/u/${memo.creatorUsername}`}>
|
||||
@{memo.creatorName}
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
<div className="btns-container space-x-2">
|
||||
{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 Icon from "@/components/Icon";
|
||||
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";
|
||||
|
||||
interface ConversationTabProps {
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { useMemoCacheStore } from "@/store/zustand";
|
||||
import { useMemoCacheStore } from "@/store/v1";
|
||||
import Icon from "../Icon";
|
||||
|
||||
interface Props {
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { useMemoCacheStore } from "@/store/zustand";
|
||||
import { useMemoCacheStore } from "@/store/v1";
|
||||
import Icon from "./Icon";
|
||||
|
||||
interface Props {
|
||||
|
@ -1,3 +1,5 @@
|
||||
import classNames from "classnames";
|
||||
|
||||
interface Props {
|
||||
avatarUrl?: string;
|
||||
className?: string;
|
||||
@ -6,7 +8,7 @@ interface Props {
|
||||
const UserAvatar = (props: Props) => {
|
||||
const { avatarUrl, className } = props;
|
||||
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="" />
|
||||
</div>
|
||||
);
|
||||
|
@ -1,6 +1,4 @@
|
||||
import axios from "axios";
|
||||
import { fetchEventSource } from "@microsoft/fetch-event-source";
|
||||
import { Message } from "@/store/zustand/message";
|
||||
|
||||
export function getSystemStatus() {
|
||||
return axios.get<SystemStatus>("/api/v1/status");
|
||||
@ -139,32 +137,11 @@ export function unpinMemo(memoId: MemoId) {
|
||||
export function deleteMemo(memoId: MemoId) {
|
||||
return axios.delete(`/api/v1/memo/${memoId}`);
|
||||
}
|
||||
|
||||
export function checkOpenAIEnabled() {
|
||||
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) {
|
||||
const queryList = [];
|
||||
if (shortcutFind?.creatorUsername) {
|
||||
|
@ -1,12 +1,13 @@
|
||||
import { Button, Stack } from "@mui/joy";
|
||||
import { fetchEventSource } from "@microsoft/fetch-event-source";
|
||||
import { head } from "lodash-es";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { toast } from "react-hot-toast";
|
||||
import { useTranslate } from "@/utils/i18n";
|
||||
import * as api from "@/helpers/api";
|
||||
import useLoading from "@/hooks/useLoading";
|
||||
import { useMessageStore } from "@/store/zustand/message";
|
||||
import { Conversation, useConversationStore } from "@/store/zustand/conversation";
|
||||
import { Message, useMessageStore } from "@/store/v1/message";
|
||||
import { Conversation, useConversationStore } from "@/store/v1/conversation";
|
||||
import Icon from "@/components/Icon";
|
||||
import { generateUUID } from "@/utils/uuid";
|
||||
import MobileHeader from "@/components/MobileHeader";
|
||||
@ -15,6 +16,28 @@ import ChatInput from "@/components/MemoChat/ChatInput";
|
||||
import ConversationTab from "@/components/MemoChat/ConversationTab";
|
||||
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 t = useTranslate();
|
||||
const fetchingState = useLoading(false);
|
||||
@ -91,7 +114,7 @@ const MemoChat = () => {
|
||||
|
||||
const fetchChatStreaming = async (messageId: string) => {
|
||||
const messageList = messageStore.getState().messageList;
|
||||
await api.chatStreaming(
|
||||
await chatStreaming(
|
||||
messageList,
|
||||
async (event: any) => {
|
||||
messageStore.updateMessage(messageId, event.data);
|
||||
|
@ -4,7 +4,7 @@ import { DEFAULT_MEMO_LIMIT } from "@/helpers/consts";
|
||||
import { useUserStore } from "./";
|
||||
import store, { useAppSelector } from "../";
|
||||
import { createMemo, deleteMemo, patchMemo, setIsFetching, upsertMemos } from "../reducer/memo";
|
||||
import { useMemoCacheStore } from "../zustand/memo";
|
||||
import { useMemoCacheStore } from "../v1";
|
||||
|
||||
export const convertResponseModelMemo = (memo: Memo): Memo => {
|
||||
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 { convertResponseModelMemo } from "../module";
|
||||
|
||||
export const useMemoCacheStore = create(
|
||||
const useMemoCacheStore = create(
|
||||
combine({ memoById: new Map<MemoId, Memo>() }, (set, get) => ({
|
||||
getState: () => get(),
|
||||
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