mirror of
https://github.com/usememos/memos.git
synced 2025-06-05 22:09:59 +02:00
chore: update detail styles (#1964)
This commit is contained in:
@ -14,6 +14,7 @@
|
|||||||
"@mui/joy": "^5.0.0-alpha.75",
|
"@mui/joy": "^5.0.0-alpha.75",
|
||||||
"@reduxjs/toolkit": "^1.8.1",
|
"@reduxjs/toolkit": "^1.8.1",
|
||||||
"axios": "^0.27.2",
|
"axios": "^0.27.2",
|
||||||
|
"classnames": "^2.3.2",
|
||||||
"copy-to-clipboard": "^3.3.2",
|
"copy-to-clipboard": "^3.3.2",
|
||||||
"highlight.js": "^11.6.0",
|
"highlight.js": "^11.6.0",
|
||||||
"i18next": "^21.9.2",
|
"i18next": "^21.9.2",
|
||||||
|
9
web/pnpm-lock.yaml
generated
9
web/pnpm-lock.yaml
generated
@ -1,4 +1,4 @@
|
|||||||
lockfileVersion: '6.0'
|
lockfileVersion: '6.1'
|
||||||
|
|
||||||
settings:
|
settings:
|
||||||
autoInstallPeers: true
|
autoInstallPeers: true
|
||||||
@ -23,6 +23,9 @@ dependencies:
|
|||||||
axios:
|
axios:
|
||||||
specifier: ^0.27.2
|
specifier: ^0.27.2
|
||||||
version: 0.27.2
|
version: 0.27.2
|
||||||
|
classnames:
|
||||||
|
specifier: ^2.3.2
|
||||||
|
version: 2.3.2
|
||||||
copy-to-clipboard:
|
copy-to-clipboard:
|
||||||
specifier: ^3.3.2
|
specifier: ^3.3.2
|
||||||
version: 3.3.2
|
version: 3.3.2
|
||||||
@ -1346,6 +1349,10 @@ packages:
|
|||||||
fsevents: 2.3.2
|
fsevents: 2.3.2
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/classnames@2.3.2:
|
||||||
|
resolution: {integrity: sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/clsx@1.2.1:
|
/clsx@1.2.1:
|
||||||
resolution: {integrity: sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==}
|
resolution: {integrity: sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
|
import classNames from "classnames";
|
||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
import { NavLink, useLocation } from "react-router-dom";
|
import { NavLink, useLocation } from "react-router-dom";
|
||||||
import { useTranslate } from "@/utils/i18n";
|
|
||||||
import { useLayoutStore, useUserStore } from "@/store/module";
|
import { useLayoutStore, useUserStore } from "@/store/module";
|
||||||
|
import { useTranslate } from "@/utils/i18n";
|
||||||
import { resolution } from "@/utils/layout";
|
import { resolution } from "@/utils/layout";
|
||||||
import Icon from "./Icon";
|
import Icon from "./Icon";
|
||||||
import UserBanner from "./UserBanner";
|
import UserBanner from "./UserBanner";
|
||||||
@ -53,9 +54,10 @@ const Header = () => {
|
|||||||
to="/"
|
to="/"
|
||||||
id="header-home"
|
id="header-home"
|
||||||
className={({ isActive }) =>
|
className={({ isActive }) =>
|
||||||
`${
|
classNames(
|
||||||
isActive && "bg-white dark:bg-zinc-700 border-gray-200 dark:border-zinc-600"
|
"px-4 pr-5 py-2 rounded-full border flex flex-row items-center text-lg text-gray-800 dark:text-gray-300 hover:bg-white hover:border-gray-200 dark:hover:border-zinc-600 dark:hover:bg-zinc-700",
|
||||||
} px-4 pr-5 py-2 rounded-full border border-transparent flex flex-row items-center text-lg text-gray-800 dark:text-gray-300 hover:bg-white hover:border-gray-200 dark:hover:border-zinc-600 dark:hover:bg-zinc-700`
|
isActive ? "bg-white dark:bg-zinc-700 border-gray-200 dark:border-zinc-600" : "border-transparent"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<>
|
<>
|
||||||
@ -66,9 +68,10 @@ const Header = () => {
|
|||||||
to="/review"
|
to="/review"
|
||||||
id="header-review"
|
id="header-review"
|
||||||
className={({ isActive }) =>
|
className={({ isActive }) =>
|
||||||
`${
|
classNames(
|
||||||
isActive && "bg-white dark:bg-zinc-700 border-gray-200 dark:border-zinc-600"
|
"px-4 pr-5 py-2 rounded-full border flex flex-row items-center text-lg text-gray-800 dark:text-gray-300 hover:bg-white hover:border-gray-200 dark:hover:border-zinc-600 dark:hover:bg-zinc-700",
|
||||||
} px-4 pr-5 py-2 rounded-full border border-transparent flex flex-row items-center text-lg text-gray-800 dark:text-gray-300 hover:bg-white hover:border-gray-200 dark:hover:border-zinc-600 dark:hover:bg-zinc-700`
|
isActive ? "bg-white dark:bg-zinc-700 border-gray-200 dark:border-zinc-600" : "border-transparent"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<>
|
<>
|
||||||
@ -79,9 +82,10 @@ const Header = () => {
|
|||||||
to="/resources"
|
to="/resources"
|
||||||
id="header-resources"
|
id="header-resources"
|
||||||
className={({ isActive }) =>
|
className={({ isActive }) =>
|
||||||
`${
|
classNames(
|
||||||
isActive && "bg-white dark:bg-zinc-700 border-gray-200 dark:border-zinc-600"
|
"px-4 pr-5 py-2 rounded-full border flex flex-row items-center text-lg text-gray-800 dark:text-gray-300 hover:bg-white hover:border-gray-200 dark:hover:border-zinc-600 dark:hover:bg-zinc-700",
|
||||||
} px-4 pr-5 py-2 rounded-full border border-transparent flex flex-row items-center text-lg text-gray-800 dark:text-gray-300 hover:bg-white hover:border-gray-200 dark:hover:border-zinc-600 dark:hover:bg-zinc-700`
|
isActive ? "bg-white dark:bg-zinc-700 border-gray-200 dark:border-zinc-600" : "border-transparent"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<>
|
<>
|
||||||
@ -94,9 +98,10 @@ const Header = () => {
|
|||||||
to="/explore"
|
to="/explore"
|
||||||
id="header-explore"
|
id="header-explore"
|
||||||
className={({ isActive }) =>
|
className={({ isActive }) =>
|
||||||
`${
|
classNames(
|
||||||
isActive && "bg-white dark:bg-zinc-700 border-gray-200 dark:border-zinc-600"
|
"px-4 pr-5 py-2 rounded-full border flex flex-row items-center text-lg text-gray-800 dark:text-gray-300 hover:bg-white hover:border-gray-200 dark:hover:border-zinc-600 dark:hover:bg-zinc-700",
|
||||||
} px-4 pr-5 py-2 rounded-full border border-transparent flex flex-row items-center text-lg text-gray-800 dark:text-gray-300 hover:bg-white hover:border-gray-200 dark:hover:border-zinc-600 dark:hover:bg-zinc-700`
|
isActive ? "bg-white dark:bg-zinc-700 border-gray-200 dark:border-zinc-600" : "border-transparent"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<>
|
<>
|
||||||
@ -110,9 +115,10 @@ const Header = () => {
|
|||||||
to="/memo-chat"
|
to="/memo-chat"
|
||||||
id="header-memo-chat"
|
id="header-memo-chat"
|
||||||
className={({ isActive }) =>
|
className={({ isActive }) =>
|
||||||
`${
|
classNames(
|
||||||
isActive && "bg-white dark:bg-zinc-700 shadow"
|
"px-4 pr-5 py-2 rounded-full border flex flex-row items-center text-lg text-gray-800 dark:text-gray-300 hover:bg-white hover:border-gray-200 dark:hover:border-zinc-600 dark:hover:bg-zinc-700",
|
||||||
} px-4 pr-5 py-2 rounded-full flex flex-row items-center text-lg text-gray-800 dark:text-gray-300 hover:bg-white hover:shadow dark:hover:bg-zinc-700`
|
isActive ? "bg-white dark:bg-zinc-700 border-gray-200 dark:border-zinc-600" : "border-transparent"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<>
|
<>
|
||||||
@ -123,9 +129,10 @@ const Header = () => {
|
|||||||
to="/archived"
|
to="/archived"
|
||||||
id="header-archived"
|
id="header-archived"
|
||||||
className={({ isActive }) =>
|
className={({ isActive }) =>
|
||||||
`${
|
classNames(
|
||||||
isActive && "bg-white dark:bg-zinc-700 border-gray-200 dark:border-zinc-600"
|
"px-4 pr-5 py-2 rounded-full border flex flex-row items-center text-lg text-gray-800 dark:text-gray-300 hover:bg-white hover:border-gray-200 dark:hover:border-zinc-600 dark:hover:bg-zinc-700",
|
||||||
} px-4 pr-5 py-2 rounded-full border border-transparent flex flex-row items-center text-lg text-gray-800 dark:text-gray-300 hover:bg-white hover:border-gray-200 dark:hover:border-zinc-600 dark:hover:bg-zinc-700`
|
isActive ? "bg-white dark:bg-zinc-700 border-gray-200 dark:border-zinc-600" : "border-transparent"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<>
|
<>
|
||||||
@ -136,9 +143,10 @@ const Header = () => {
|
|||||||
to="/setting"
|
to="/setting"
|
||||||
id="header-setting"
|
id="header-setting"
|
||||||
className={({ isActive }) =>
|
className={({ isActive }) =>
|
||||||
`${
|
classNames(
|
||||||
isActive && "bg-white dark:bg-zinc-700 border-gray-200 dark:border-zinc-600"
|
"px-4 pr-5 py-2 rounded-full border flex flex-row items-center text-lg text-gray-800 dark:text-gray-300 hover:bg-white hover:border-gray-200 dark:hover:border-zinc-600 dark:hover:bg-zinc-700",
|
||||||
} px-4 pr-5 py-2 rounded-full border border-transparent flex flex-row items-center text-lg text-gray-800 dark:text-gray-300 hover:bg-white hover:border-gray-200 dark:hover:border-zinc-600 dark:hover:bg-zinc-700`
|
isActive ? "bg-white dark:bg-zinc-700 border-gray-200 dark:border-zinc-600" : "border-transparent"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<>
|
<>
|
||||||
@ -154,9 +162,10 @@ const Header = () => {
|
|||||||
to="/auth"
|
to="/auth"
|
||||||
id="header-auth"
|
id="header-auth"
|
||||||
className={({ isActive }) =>
|
className={({ isActive }) =>
|
||||||
`${
|
classNames(
|
||||||
isActive && "bg-white dark:bg-zinc-700 border-gray-200 dark:border-zinc-600"
|
"px-4 pr-5 py-2 rounded-full border flex flex-row items-center text-lg text-gray-800 dark:text-gray-300 hover:bg-white hover:border-gray-200 dark:hover:border-zinc-600 dark:hover:bg-zinc-700",
|
||||||
} px-4 pr-5 py-2 rounded-full border border-transparent flex flex-row items-center text-lg text-gray-800 dark:text-gray-300 hover:bg-white hover:border-gray-200 dark:hover:border-zinc-600 dark:hover:bg-zinc-700`
|
isActive ? "bg-white dark:bg-zinc-700 border-gray-200 dark:border-zinc-600" : "border-transparent"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<>
|
<>
|
||||||
|
@ -2,20 +2,15 @@ import Icon from "@/components/Icon";
|
|||||||
import Textarea from "@mui/joy/Textarea/Textarea";
|
import Textarea from "@mui/joy/Textarea/Textarea";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
interface MemosChatInputProps {
|
interface Props {
|
||||||
question: string;
|
question: string;
|
||||||
handleQuestionTextareaChange: any;
|
handleQuestionTextareaChange: any;
|
||||||
setIsInIME: any;
|
setIsInIME: any;
|
||||||
handleKeyDown: any;
|
handleKeyDown: any;
|
||||||
handleSendQuestionButtonClick: any;
|
handleSendQuestionButtonClick: any;
|
||||||
}
|
}
|
||||||
const MemosChatInput = ({
|
|
||||||
question,
|
const ChatInput = ({ question, handleQuestionTextareaChange, setIsInIME, handleKeyDown, handleSendQuestionButtonClick }: Props) => {
|
||||||
handleQuestionTextareaChange,
|
|
||||||
setIsInIME,
|
|
||||||
handleKeyDown,
|
|
||||||
handleSendQuestionButtonClick,
|
|
||||||
}: MemosChatInputProps) => {
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -39,4 +34,4 @@ const MemosChatInput = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default MemosChatInput;
|
export default ChatInput;
|
@ -7,7 +7,7 @@ interface MessageProps {
|
|||||||
message: Message;
|
message: Message;
|
||||||
}
|
}
|
||||||
|
|
||||||
const MemosChatMessage = ({ index, message }: MessageProps) => {
|
const ChatMessage = ({ index, message }: MessageProps) => {
|
||||||
return (
|
return (
|
||||||
<div key={index} className="w-full flex flex-col justify-start items-start space-y-2">
|
<div key={index} className="w-full flex flex-col justify-start items-start space-y-2">
|
||||||
{message.role === "user" ? (
|
{message.role === "user" ? (
|
||||||
@ -28,4 +28,4 @@ const MemosChatMessage = ({ index, message }: MessageProps) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default MemosChatMessage;
|
export default ChatMessage;
|
@ -1,3 +1,4 @@
|
|||||||
|
import { Button } from "@mui/joy";
|
||||||
import { isNumber, last, uniq } from "lodash-es";
|
import { isNumber, last, uniq } from "lodash-es";
|
||||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||||
import { toast } from "react-hot-toast";
|
import { toast } from "react-hot-toast";
|
||||||
@ -421,9 +422,9 @@ const MemoEditor = (props: Props) => {
|
|||||||
<div className="editor-footer-container">
|
<div className="editor-footer-container">
|
||||||
<MemoVisibilitySelector value={state.memoVisibility} onChange={handleMemoVisibilityChange} />
|
<MemoVisibilitySelector value={state.memoVisibility} onChange={handleMemoVisibilityChange} />
|
||||||
<div className="buttons-container">
|
<div className="buttons-container">
|
||||||
<button className="action-btn confirm-btn" disabled={!allowSave} onClick={handleSaveBtnClick}>
|
<Button disabled={!allowSave} onClick={handleSaveBtnClick}>
|
||||||
{t("editor.save")}
|
{t("editor.save")}
|
||||||
</button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -76,7 +76,7 @@ const MemoList = () => {
|
|||||||
return shouldShow;
|
return shouldShow;
|
||||||
})
|
})
|
||||||
: memos
|
: memos
|
||||||
).filter((memo) => memo.creatorId === currentUserId);
|
).filter((memo) => memo.creatorId === currentUserId && memo.rowStatus === "NORMAL");
|
||||||
|
|
||||||
const pinnedMemos = shownMemos.filter((m) => m.pinned);
|
const pinnedMemos = shownMemos.filter((m) => m.pinned);
|
||||||
const unpinnedMemos = shownMemos.filter((m) => !m.pinned);
|
const unpinnedMemos = shownMemos.filter((m) => !m.pinned);
|
||||||
|
@ -326,6 +326,14 @@
|
|||||||
"and": "And",
|
"and": "And",
|
||||||
"or": "Or"
|
"or": "Or"
|
||||||
},
|
},
|
||||||
|
"amount-text": {
|
||||||
|
"memo_one": "MEMO",
|
||||||
|
"memo_other": "MEMOS",
|
||||||
|
"tag_one": "TAG",
|
||||||
|
"tag_other": "TAGS",
|
||||||
|
"day_one": "TAG",
|
||||||
|
"day_other": "TAGE"
|
||||||
|
},
|
||||||
"message": {
|
"message": {
|
||||||
"no-data": "Maybe no data was found, maybe it should be another option.",
|
"no-data": "Maybe no data was found, maybe it should be another option.",
|
||||||
"memos-ready": "all memos are ready 🎉",
|
"memos-ready": "all memos are ready 🎉",
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Button, Divider } from "@mui/joy";
|
import { Button, Divider, Input } from "@mui/joy";
|
||||||
import { useEffect, useState } from "react";
|
import { 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";
|
||||||
@ -131,71 +131,44 @@ const Auth = () => {
|
|||||||
<div className="flex flex-row justify-center items-center w-full h-full dark:bg-zinc-800">
|
<div className="flex flex-row justify-center items-center w-full h-full dark:bg-zinc-800">
|
||||||
<div className="w-80 max-w-full h-full py-4 flex flex-col justify-start items-center">
|
<div className="w-80 max-w-full h-full py-4 flex flex-col justify-start items-center">
|
||||||
<div className="w-full py-4 grow flex flex-col justify-center items-center">
|
<div className="w-full py-4 grow flex flex-col justify-center items-center">
|
||||||
<div className="flex flex-col justify-start items-start w-full mb-4">
|
<div className="w-full flex flex-col justify-center items-center mb-2">
|
||||||
<div className="w-full flex flex-row justify-start items-center mb-2">
|
<img className="h-20 w-auto rounded-full shadow mr-1" src={systemStatus.customizedProfile.logoUrl} alt="" />
|
||||||
<img className="h-12 w-auto rounded-lg mr-1" src={systemStatus.customizedProfile.logoUrl} alt="" />
|
<p className="text-3xl text-black opacity-80 dark:text-gray-200">{systemStatus.customizedProfile.name}</p>
|
||||||
<p className="text-6xl tracking-wide text-black opacity-80 dark:text-gray-200">{systemStatus.customizedProfile.name}</p>
|
|
||||||
</div>
|
|
||||||
<p className="text-sm text-gray-700 dark:text-gray-300">
|
|
||||||
{systemStatus.customizedProfile.description || t("common.memos-slogan")}
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
<form className="w-full" onSubmit={handleFormSubmit}>
|
<form className="w-full mt-4" onSubmit={handleFormSubmit}>
|
||||||
<div className={`flex flex-col justify-start items-start w-full ${actionBtnLoadingState.isLoading && "opacity-80"}`}>
|
<div className="flex flex-col justify-start items-start w-full gap-4">
|
||||||
<div className="flex flex-col justify-start items-start relative w-full text-base mt-2 py-2">
|
<Input
|
||||||
<span
|
className="w-full"
|
||||||
className={`absolute top-3 left-3 px-1 leading-10 shrink-0 text-base cursor-text text-gray-400 transition-all select-none pointer-events-none ${
|
size="lg"
|
||||||
username ? "!text-sm !top-0 !z-10 !leading-4 bg-white dark:bg-zinc-800 rounded" : ""
|
type="text"
|
||||||
}`}
|
placeholder={t("common.username")}
|
||||||
>
|
value={username}
|
||||||
{t("common.username")}
|
onChange={handleUsernameInputChanged}
|
||||||
</span>
|
required
|
||||||
<input
|
/>
|
||||||
className="input-text w-full py-3 px-3 text-base rounded-lg dark:bg-zinc-800"
|
<Input
|
||||||
type="text"
|
className="w-full"
|
||||||
value={username}
|
size="lg"
|
||||||
onChange={handleUsernameInputChanged}
|
type="password"
|
||||||
required
|
placeholder={t("common.password")}
|
||||||
/>
|
value={password}
|
||||||
</div>
|
onChange={handlePasswordInputChanged}
|
||||||
<div className="flex flex-col justify-start items-start relative w-full text-base mt-2 py-2">
|
required
|
||||||
<span
|
/>
|
||||||
className={`absolute top-3 left-3 px-1 leading-10 shrink-0 text-base cursor-text text-gray-400 transition-all select-none pointer-events-none ${
|
|
||||||
password ? "!text-sm !top-0 !z-10 !leading-4 bg-white dark:bg-zinc-800 rounded" : ""
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
{t("common.password")}
|
|
||||||
</span>
|
|
||||||
<input
|
|
||||||
className="input-text w-full py-3 px-3 text-base rounded-lg dark:bg-zinc-800"
|
|
||||||
type="password"
|
|
||||||
value={password}
|
|
||||||
onChange={handlePasswordInputChanged}
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-row justify-end items-center w-full mt-2">
|
<div className="flex flex-row justify-end items-center w-full mt-6">
|
||||||
{actionBtnLoadingState.isLoading && <Icon.Loader className="w-4 h-auto mr-2 animate-spin dark:text-gray-300" />}
|
{actionBtnLoadingState.isLoading && <Icon.Loader className="w-4 h-auto mr-2 animate-spin dark:text-gray-300" />}
|
||||||
{systemStatus?.allowSignUp && (
|
{systemStatus?.allowSignUp && (
|
||||||
<>
|
<>
|
||||||
<button
|
<Button variant={"plain"} loading={actionBtnLoadingState.isLoading} onClick={handleSignUpButtonClick}>
|
||||||
type="button"
|
|
||||||
className={`btn-text ${actionBtnLoadingState.isLoading ? "cursor-wait opacity-80" : ""}`}
|
|
||||||
onClick={handleSignUpButtonClick}
|
|
||||||
>
|
|
||||||
{t("common.sign-up")}
|
{t("common.sign-up")}
|
||||||
</button>
|
</Button>
|
||||||
<span className="mr-2 font-mono text-gray-200">/</span>
|
<span className="mr-2 font-mono text-gray-200">/</span>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
<button
|
<Button type="submit" loading={actionBtnLoadingState.isLoading} onClick={handleSignInButtonClick}>
|
||||||
type="submit"
|
|
||||||
className={`btn-primary ${actionBtnLoadingState.isLoading ? "cursor-wait opacity-80" : ""}`}
|
|
||||||
onClick={handleSignInButtonClick}
|
|
||||||
>
|
|
||||||
{t("common.sign-in")}
|
{t("common.sign-in")}
|
||||||
</button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
{identityProviderList.length > 0 && (
|
{identityProviderList.length > 0 && (
|
||||||
|
@ -11,19 +11,13 @@ import Memo from "@/components/Memo";
|
|||||||
import MobileHeader from "@/components/MobileHeader";
|
import MobileHeader from "@/components/MobileHeader";
|
||||||
import Empty from "@/components/Empty";
|
import Empty from "@/components/Empty";
|
||||||
|
|
||||||
interface State {
|
|
||||||
memos: Memo[];
|
|
||||||
}
|
|
||||||
|
|
||||||
const Explore = () => {
|
const Explore = () => {
|
||||||
const t = useTranslate();
|
const t = useTranslate();
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const filterStore = useFilterStore();
|
const filterStore = useFilterStore();
|
||||||
const memoStore = useMemoStore();
|
const memoStore = useMemoStore();
|
||||||
const filter = filterStore.state;
|
const filter = filterStore.state;
|
||||||
const [state, setState] = useState<State>({
|
const memos = memoStore.state.memos;
|
||||||
memos: [],
|
|
||||||
});
|
|
||||||
const [isComplete, setIsComplete] = useState<boolean>(false);
|
const [isComplete, setIsComplete] = useState<boolean>(false);
|
||||||
const loadingState = useLoading();
|
const loadingState = useLoading();
|
||||||
|
|
||||||
@ -32,9 +26,6 @@ const Explore = () => {
|
|||||||
if (memos.length < DEFAULT_MEMO_LIMIT) {
|
if (memos.length < DEFAULT_MEMO_LIMIT) {
|
||||||
setIsComplete(true);
|
setIsComplete(true);
|
||||||
}
|
}
|
||||||
setState({
|
|
||||||
memos,
|
|
||||||
});
|
|
||||||
loadingState.setFinish();
|
loadingState.setFinish();
|
||||||
});
|
});
|
||||||
}, [location]);
|
}, [location]);
|
||||||
@ -43,7 +34,7 @@ const Explore = () => {
|
|||||||
const showMemoFilter = Boolean(tagQuery || textQuery);
|
const showMemoFilter = Boolean(tagQuery || textQuery);
|
||||||
|
|
||||||
const shownMemos = showMemoFilter
|
const shownMemos = showMemoFilter
|
||||||
? state.memos.filter((memo) => {
|
? memos.filter((memo) => {
|
||||||
let shouldShow = true;
|
let shouldShow = true;
|
||||||
|
|
||||||
if (tagQuery) {
|
if (tagQuery) {
|
||||||
@ -64,21 +55,17 @@ const Explore = () => {
|
|||||||
}
|
}
|
||||||
return shouldShow;
|
return shouldShow;
|
||||||
})
|
})
|
||||||
: state.memos;
|
: memos;
|
||||||
|
|
||||||
const sortedMemos = shownMemos.filter((m) => m.rowStatus === "NORMAL");
|
|
||||||
|
|
||||||
|
const sortedMemos = shownMemos.filter((m) => m.rowStatus === "NORMAL" && m.visibility !== "PRIVATE");
|
||||||
const handleFetchMoreClick = async () => {
|
const handleFetchMoreClick = async () => {
|
||||||
try {
|
try {
|
||||||
const fetchedMemos = await memoStore.fetchAllMemos(DEFAULT_MEMO_LIMIT, state.memos.length);
|
const fetchedMemos = await memoStore.fetchAllMemos(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);
|
||||||
}
|
}
|
||||||
setState({
|
|
||||||
memos: state.memos.concat(fetchedMemos),
|
|
||||||
});
|
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
toast.error(error.response.data.message);
|
toast.error(error.response.data.message);
|
||||||
@ -95,7 +82,7 @@ const Explore = () => {
|
|||||||
return <Memo key={`${memo.id}-${memo.displayTs}`} memo={memo} showCreator />;
|
return <Memo key={`${memo.id}-${memo.displayTs}`} memo={memo} showCreator />;
|
||||||
})}
|
})}
|
||||||
{isComplete ? (
|
{isComplete ? (
|
||||||
state.memos.length === 0 && (
|
memos.length === 0 && (
|
||||||
<div className="w-full mt-16 mb-8 flex flex-col justify-center items-center italic">
|
<div className="w-full mt-16 mb-8 flex flex-col justify-center items-center italic">
|
||||||
<Empty />
|
<Empty />
|
||||||
<p className="mt-4 text-gray-600 dark:text-gray-400">{t("message.no-data")}</p>
|
<p className="mt-4 text-gray-600 dark:text-gray-400">{t("message.no-data")}</p>
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { Button, Stack } from "@mui/joy";
|
import { Button, Stack } from "@mui/joy";
|
||||||
|
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 { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
@ -9,27 +10,23 @@ import { Conversation, useConversationStore } from "@/store/zustand/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";
|
||||||
import MemosChatMessage from "@/components/MemosChat/MemosChatMessage";
|
import ChatMessage from "@/components/MemoChat/ChatMessage";
|
||||||
import MemosChatInput from "@/components/MemosChat/MemosChatInput";
|
import ChatInput from "@/components/MemoChat/ChatInput";
|
||||||
import head from "lodash-es/head";
|
import ConversationTab from "@/components/MemoChat/ConversationTab";
|
||||||
import ConversationTab from "@/components/MemosChat/ConversationTab";
|
|
||||||
import Empty from "@/components/Empty";
|
import Empty from "@/components/Empty";
|
||||||
|
|
||||||
const MemosChat = () => {
|
const MemoChat = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const fetchingState = useLoading(false);
|
const fetchingState = useLoading(false);
|
||||||
const [isEnabled, setIsEnabled] = useState<boolean>(true);
|
const [isEnabled, setIsEnabled] = useState<boolean>(true);
|
||||||
const [isInIME, setIsInIME] = useState(false);
|
const [isInIME, setIsInIME] = useState(false);
|
||||||
const [question, setQuestion] = useState<string>("");
|
const [question, setQuestion] = useState<string>("");
|
||||||
|
|
||||||
const conversationStore = useConversationStore();
|
const conversationStore = useConversationStore();
|
||||||
const conversationList = conversationStore.conversationList;
|
const conversationList = conversationStore.conversationList;
|
||||||
|
|
||||||
const [selectedConversationId, setSelectedConversationId] = useState<string>(head(conversationList)?.messageStorageId || "");
|
const [selectedConversationId, setSelectedConversationId] = useState<string>(head(conversationList)?.messageStorageId || "");
|
||||||
const messageStore = useMessageStore(selectedConversationId)();
|
const messageStore = useMessageStore(selectedConversationId)();
|
||||||
const messageList = messageStore.messageList;
|
const messageList = messageStore.messageList;
|
||||||
|
// The state didn't show in component, just for trigger re-render
|
||||||
// the state didn't show in component, just for trigger re-render
|
|
||||||
const [message, setMessage] = useState<string>("");
|
const [message, setMessage] = useState<string>("");
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -170,7 +167,7 @@ const MemosChat = () => {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{messageList.map((message, index) => (
|
{messageList.map((message, index) => (
|
||||||
<MemosChatMessage key={index} message={message} index={index} />
|
<ChatMessage key={index} message={message} index={index} />
|
||||||
))}
|
))}
|
||||||
</Stack>
|
</Stack>
|
||||||
{fetchingState.isLoading && (
|
{fetchingState.isLoading && (
|
||||||
@ -185,7 +182,7 @@ const MemosChat = () => {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<MemosChatInput
|
<ChatInput
|
||||||
question={question}
|
question={question}
|
||||||
handleQuestionTextareaChange={handleQuestionTextareaChange}
|
handleQuestionTextareaChange={handleQuestionTextareaChange}
|
||||||
setIsInIME={setIsInIME}
|
setIsInIME={setIsInIME}
|
||||||
@ -198,4 +195,4 @@ const MemosChat = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default MemosChat;
|
export default MemoChat;
|
@ -1,40 +1,28 @@
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect } 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 { Link, useLocation, useParams } from "react-router-dom";
|
import { Link, useLocation, useParams } from "react-router-dom";
|
||||||
import { UNKNOWN_ID } from "@/helpers/consts";
|
|
||||||
import { useGlobalStore, useMemoStore } from "@/store/module";
|
import { useGlobalStore, useMemoStore } from "@/store/module";
|
||||||
import useLoading from "@/hooks/useLoading";
|
import useLoading from "@/hooks/useLoading";
|
||||||
import Icon from "@/components/Icon";
|
import Icon from "@/components/Icon";
|
||||||
import Memo from "@/components/Memo";
|
import Memo from "@/components/Memo";
|
||||||
|
|
||||||
interface State {
|
|
||||||
memo: Memo;
|
|
||||||
}
|
|
||||||
|
|
||||||
const MemoDetail = () => {
|
const MemoDetail = () => {
|
||||||
const t = useTranslate();
|
const t = useTranslate();
|
||||||
const params = useParams();
|
const params = useParams();
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const globalStore = useGlobalStore();
|
const globalStore = useGlobalStore();
|
||||||
const memoStore = useMemoStore();
|
const memoStore = useMemoStore();
|
||||||
const [state, setState] = useState<State>({
|
|
||||||
memo: {
|
|
||||||
id: UNKNOWN_ID,
|
|
||||||
} as Memo,
|
|
||||||
});
|
|
||||||
const loadingState = useLoading();
|
const loadingState = useLoading();
|
||||||
const customizedProfile = globalStore.state.systemStatus.customizedProfile;
|
const customizedProfile = globalStore.state.systemStatus.customizedProfile;
|
||||||
|
const memoId = Number(params.memoId);
|
||||||
|
const memo = memoStore.state.memos.find((memo) => memo.id === memoId);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const memoId = Number(params.memoId);
|
|
||||||
if (memoId && !isNaN(memoId)) {
|
if (memoId && !isNaN(memoId)) {
|
||||||
memoStore
|
memoStore
|
||||||
.fetchMemoById(memoId)
|
.fetchMemoById(memoId)
|
||||||
.then((memo) => {
|
.then(() => {
|
||||||
setState({
|
|
||||||
memo,
|
|
||||||
});
|
|
||||||
loadingState.setFinish();
|
loadingState.setFinish();
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
@ -53,21 +41,26 @@ const MemoDetail = () => {
|
|||||||
<p className="detail-name text-4xl tracking-wide text-black dark:text-white">{customizedProfile.name}</p>
|
<p className="detail-name text-4xl tracking-wide text-black dark:text-white">{customizedProfile.name}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{!loadingState.isLoading && (
|
{!loadingState.isLoading &&
|
||||||
<>
|
(memo ? (
|
||||||
<main className="relative flex-grow max-w-2xl w-full min-h-full flex flex-col justify-start items-start px-4">
|
<>
|
||||||
<Memo memo={state.memo} showCreator showRelatedMemos />
|
<main className="relative flex-grow max-w-2xl w-full min-h-full flex flex-col justify-start items-start px-4">
|
||||||
</main>
|
<Memo memo={memo} showCreator showRelatedMemos />
|
||||||
<div className="mt-4 w-full flex flex-row justify-center items-center gap-2">
|
</main>
|
||||||
<Link
|
<div className="mt-4 w-full flex flex-row justify-center items-center gap-2">
|
||||||
to="/"
|
<Link
|
||||||
className="flex flex-row justify-center items-center text-gray-600 dark:text-gray-300 text-sm px-3 hover:opacity-80 hover:underline"
|
to="/"
|
||||||
>
|
className="flex flex-row justify-center items-center text-gray-600 dark:text-gray-300 text-sm px-3 hover:opacity-80 hover:underline"
|
||||||
<Icon.Home className="w-4 h-auto mr-1 -mt-0.5" /> {t("router.back-to-home")}
|
>
|
||||||
</Link>
|
<Icon.Home className="w-4 h-auto mr-1 -mt-0.5" /> {t("router.back-to-home")}
|
||||||
</div>
|
</Link>
|
||||||
</>
|
</div>
|
||||||
)}
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<p>Not found</p>
|
||||||
|
</>
|
||||||
|
))}
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
|
@ -16,7 +16,7 @@ const Home = lazy(() => import("@/pages/Home"));
|
|||||||
const MemoDetail = lazy(() => import("@/pages/MemoDetail"));
|
const MemoDetail = lazy(() => import("@/pages/MemoDetail"));
|
||||||
const EmbedMemo = lazy(() => import("@/pages/EmbedMemo"));
|
const EmbedMemo = lazy(() => import("@/pages/EmbedMemo"));
|
||||||
const NotFound = lazy(() => import("@/pages/NotFound"));
|
const NotFound = lazy(() => import("@/pages/NotFound"));
|
||||||
const MemosChat = lazy(() => import("@/pages/MemosChat"));
|
const MemoChat = lazy(() => import("@/pages/MemoChat"));
|
||||||
|
|
||||||
const initialGlobalStateLoader = (() => {
|
const initialGlobalStateLoader = (() => {
|
||||||
let done = false;
|
let done = false;
|
||||||
@ -150,7 +150,7 @@ const router = createBrowserRouter([
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "memo-chat",
|
path: "memo-chat",
|
||||||
element: <MemosChat />,
|
element: <MemoChat />,
|
||||||
loader: async () => {
|
loader: async () => {
|
||||||
await initialGlobalStateLoader();
|
await initialGlobalStateLoader();
|
||||||
|
|
||||||
|
@ -23,6 +23,7 @@ export const useMemoStore = () => {
|
|||||||
const fetchMemoById = async (memoId: MemoId) => {
|
const fetchMemoById = async (memoId: MemoId) => {
|
||||||
const { data } = await api.getMemoById(memoId);
|
const { data } = await api.getMemoById(memoId);
|
||||||
const memo = convertResponseModelMemo(data);
|
const memo = convertResponseModelMemo(data);
|
||||||
|
store.dispatch(upsertMemos([memo]));
|
||||||
|
|
||||||
return memo;
|
return memo;
|
||||||
};
|
};
|
||||||
@ -62,6 +63,7 @@ export const useMemoStore = () => {
|
|||||||
|
|
||||||
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));
|
||||||
|
|
||||||
for (const m of fetchedMemos) {
|
for (const m of fetchedMemos) {
|
||||||
memoCacheStore.setMemoCache(m);
|
memoCacheStore.setMemoCache(m);
|
||||||
|
Reference in New Issue
Block a user