Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
C
canifa_note
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Vũ Hoàng Anh
canifa_note
Commits
e2fd7920
Commit
e2fd7920
authored
Jul 17, 2024
by
Steven
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: implement inline memo editor
parent
956f2183
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
96 additions
and
53 deletions
+96
-53
MemoActionMenu.tsx
web/src/components/MemoActionMenu.tsx
+7
-0
index.tsx
web/src/components/MemoEditor/index.tsx
+7
-1
MemoView.tsx
web/src/components/MemoView.tsx
+66
-45
MemoDetail.tsx
web/src/pages/MemoDetail.tsx
+16
-7
No files found.
web/src/components/MemoActionMenu.tsx
View file @
e2fd7920
...
@@ -15,6 +15,7 @@ interface Props {
...
@@ -15,6 +15,7 @@ interface Props {
memo
:
Memo
;
memo
:
Memo
;
className
?:
string
;
className
?:
string
;
hiddenActions
?:
(
"edit"
|
"archive"
|
"delete"
|
"share"
|
"pin"
)[];
hiddenActions
?:
(
"edit"
|
"archive"
|
"delete"
|
"share"
|
"pin"
)[];
onEdit
?:
()
=>
void
;
}
}
const
MemoActionMenu
=
(
props
:
Props
)
=>
{
const
MemoActionMenu
=
(
props
:
Props
)
=>
{
...
@@ -50,6 +51,12 @@ const MemoActionMenu = (props: Props) => {
...
@@ -50,6 +51,12 @@ const MemoActionMenu = (props: Props) => {
};
};
const
handleEditMemoClick
=
()
=>
{
const
handleEditMemoClick
=
()
=>
{
if
(
props
.
onEdit
)
{
props
.
onEdit
();
return
;
}
// TODO: remove me later.
showMemoEditorDialog
({
showMemoEditorDialog
({
memoName
:
memo
.
name
,
memoName
:
memo
.
name
,
cacheKey
:
`
${
memo
.
name
}
-
${
memo
.
updateTime
}
`
,
cacheKey
:
`
${
memo
.
name
}
-
${
memo
.
updateTime
}
`
,
...
...
web/src/components/MemoEditor/index.tsx
View file @
e2fd7920
...
@@ -38,6 +38,7 @@ export interface Props {
...
@@ -38,6 +38,7 @@ export interface Props {
autoFocus
?:
boolean
;
autoFocus
?:
boolean
;
memoPatchRef
?:
React
.
MutableRefObject
<
Partial
<
Memo
>>
;
memoPatchRef
?:
React
.
MutableRefObject
<
Partial
<
Memo
>>
;
onConfirm
?:
(
memoName
:
string
)
=>
void
;
onConfirm
?:
(
memoName
:
string
)
=>
void
;
onCancel
?:
()
=>
void
;
}
}
interface
State
{
interface
State
{
...
@@ -439,7 +440,12 @@ const MemoEditor = (props: Props) => {
...
@@ -439,7 +440,12 @@ const MemoEditor = (props: Props) => {
))
}
))
}
</
Select
>
</
Select
>
</
div
>
</
div
>
<
div
className=
"shrink-0 flex flex-row justify-end items-center"
>
<
div
className=
"shrink-0 flex flex-row justify-end items-center gap-2"
>
{
props
.
onCancel
&&
(
<
Button
className=
"!font-normal"
color=
"neutral"
variant=
"plain"
loading=
{
state
.
isRequesting
}
onClick=
{
props
.
onCancel
}
>
{
t
(
"common.cancel"
)
}
</
Button
>
)
}
<
Button
<
Button
className=
"!font-normal"
className=
"!font-normal"
disabled=
{
!
allowSave
}
disabled=
{
!
allowSave
}
...
...
web/src/components/MemoView.tsx
View file @
e2fd7920
...
@@ -14,7 +14,7 @@ import { convertVisibilityToString } from "@/utils/memo";
...
@@ -14,7 +14,7 @@ import { convertVisibilityToString } from "@/utils/memo";
import
Icon
from
"./Icon"
;
import
Icon
from
"./Icon"
;
import
MemoActionMenu
from
"./MemoActionMenu"
;
import
MemoActionMenu
from
"./MemoActionMenu"
;
import
MemoContent
from
"./MemoContent"
;
import
MemoContent
from
"./MemoContent"
;
import
showMemoEditorDialog
from
"./MemoEditor/MemoEditorDialog
"
;
import
MemoEditor
from
"./MemoEditor
"
;
import
MemoReactionistView
from
"./MemoReactionListView"
;
import
MemoReactionistView
from
"./MemoReactionListView"
;
import
MemoRelationListView
from
"./MemoRelationListView"
;
import
MemoRelationListView
from
"./MemoRelationListView"
;
import
MemoResourceListView
from
"./MemoResourceListView"
;
import
MemoResourceListView
from
"./MemoResourceListView"
;
...
@@ -45,6 +45,7 @@ const MemoView: React.FC<Props> = (props: Props) => {
...
@@ -45,6 +45,7 @@ const MemoView: React.FC<Props> = (props: Props) => {
const
workspaceMemoRelatedSetting
=
const
workspaceMemoRelatedSetting
=
workspaceSettingStore
.
getWorkspaceSettingByKey
(
WorkspaceSettingKey
.
MEMO_RELATED
).
memoRelatedSetting
||
workspaceSettingStore
.
getWorkspaceSettingByKey
(
WorkspaceSettingKey
.
MEMO_RELATED
).
memoRelatedSetting
||
WorkspaceMemoRelatedSetting
.
fromPartial
({});
WorkspaceMemoRelatedSetting
.
fromPartial
({});
const
[
showEditor
,
setShowEditor
]
=
useState
<
boolean
>
(
false
);
const
[
creator
,
setCreator
]
=
useState
(
userStore
.
getUserByName
(
memo
.
creator
));
const
[
creator
,
setCreator
]
=
useState
(
userStore
.
getUserByName
(
memo
.
creator
));
const
memoContainerRef
=
useRef
<
HTMLDivElement
>
(
null
);
const
memoContainerRef
=
useRef
<
HTMLDivElement
>
(
null
);
const
referencedMemos
=
memo
.
relations
.
filter
((
relation
)
=>
relation
.
type
===
MemoRelation_Type
.
REFERENCE
);
const
referencedMemos
=
memo
.
relations
.
filter
((
relation
)
=>
relation
.
type
===
MemoRelation_Type
.
REFERENCE
);
...
@@ -85,10 +86,7 @@ const MemoView: React.FC<Props> = (props: Props) => {
...
@@ -85,10 +86,7 @@ const MemoView: React.FC<Props> = (props: Props) => {
if
(
workspaceMemoRelatedSetting
.
enableDoubleClickEdit
)
{
if
(
workspaceMemoRelatedSetting
.
enableDoubleClickEdit
)
{
e
.
preventDefault
();
e
.
preventDefault
();
showMemoEditorDialog
({
setShowEditor
(
true
);
memoName
:
memo
.
name
,
cacheKey
:
`
${
memo
.
name
}
-
${
memo
.
updateTime
}
`
,
});
}
}
},
[]);
},
[]);
...
@@ -137,50 +135,73 @@ const MemoView: React.FC<Props> = (props: Props) => {
...
@@ -137,50 +135,73 @@ const MemoView: React.FC<Props> = (props: Props) => {
</
div
>
</
div
>
)
}
)
}
</
div
>
</
div
>
<
div
className=
"flex flex-row justify-end items-center select-none shrink-0 gap-2"
>
{
!
showEditor
&&
(
<
div
className=
"w-auto invisible group-hover:visible flex flex-row justify-between items-center gap-2"
>
<
div
className=
"flex flex-row justify-end items-center select-none shrink-0 gap-2"
>
{
props
.
showVisibility
&&
memo
.
visibility
!==
Visibility
.
PRIVATE
&&
(
<
div
className=
"w-auto invisible group-hover:visible flex flex-row justify-between items-center gap-2"
>
<
Tooltip
title=
{
t
(
`memo.visibility.${convertVisibilityToString(memo.visibility).toLowerCase()}`
as
any
)
}
placement=
"top"
>
{
props
.
showVisibility
&&
memo
.
visibility
!==
Visibility
.
PRIVATE
&&
(
<
span
className=
"flex justify-center items-center hover:opacity-70"
>
<
Tooltip
title=
{
t
(
`memo.visibility.${convertVisibilityToString(memo.visibility).toLowerCase()}`
as
any
)
}
placement=
"top"
>
<
VisibilityIcon
visibility=
{
memo
.
visibility
}
/>
<
span
className=
"flex justify-center items-center hover:opacity-70"
>
</
span
>
<
VisibilityIcon
visibility=
{
memo
.
visibility
}
/>
</
span
>
</
Tooltip
>
)
}
{
currentUser
&&
<
ReactionSelector
className=
"border-none w-auto h-auto"
memo=
{
memo
}
/>
}
</
div
>
{
!
isInMemoDetailPage
&&
(
<
Link
className=
{
clsx
(
"flex flex-row justify-start items-center hover:opacity-70"
,
commentAmount
===
0
&&
"invisible group-hover:visible"
,
)
}
to=
{
`/m/${memo.uid}#comments`
}
unstable_viewTransition
>
<
Icon
.
MessageCircleMore
className=
"w-4 h-4 mx-auto text-gray-500 dark:text-gray-400"
/>
{
commentAmount
>
0
&&
<
span
className=
"text-xs text-gray-500 dark:text-gray-400"
>
{
commentAmount
}
</
span
>
}
</
Link
>
)
}
{
props
.
showPinned
&&
memo
.
pinned
&&
(
<
Tooltip
title=
{
t
(
"common.pinned"
)
}
placement=
"top"
>
<
Icon
.
Bookmark
className=
"w-4 h-auto text-amber-500"
/>
</
Tooltip
>
</
Tooltip
>
)
}
)
}
{
currentUser
&&
<
ReactionSelector
className=
"border-none w-auto h-auto"
memo=
{
memo
}
/>
}
{
!
readonly
&&
(
<
MemoActionMenu
className=
"-ml-1"
memo=
{
memo
}
hiddenActions=
{
props
.
showPinned
?
[]
:
[
"pin"
]
}
onEdit=
{
()
=>
setShowEditor
(
true
)
}
/>
)
}
</
div
>
</
div
>
{
!
isInMemoDetailPage
&&
(
)
}
<
Link
className=
{
clsx
(
"flex flex-row justify-start items-center hover:opacity-70"
,
commentAmount
===
0
&&
"invisible group-hover:visible"
,
)
}
to=
{
`/m/${memo.uid}#comments`
}
unstable_viewTransition
>
<
Icon
.
MessageCircleMore
className=
"w-4 h-4 mx-auto text-gray-500 dark:text-gray-400"
/>
{
commentAmount
>
0
&&
<
span
className=
"text-xs text-gray-500 dark:text-gray-400"
>
{
commentAmount
}
</
span
>
}
</
Link
>
)
}
{
props
.
showPinned
&&
memo
.
pinned
&&
(
<
Tooltip
title=
{
t
(
"common.pinned"
)
}
placement=
"top"
>
<
Icon
.
Bookmark
className=
"w-4 h-auto text-amber-500"
/>
</
Tooltip
>
)
}
{
!
readonly
&&
<
MemoActionMenu
className=
"-ml-1"
memo=
{
memo
}
hiddenActions=
{
props
.
showPinned
?
[]
:
[
"pin"
]
}
/>
}
</
div
>
</
div
>
</
div
>
<
MemoContent
key=
{
`${memo.name}-${memo.updateTime}`
}
{
showEditor
?
(
memoName=
{
memo
.
name
}
<
MemoEditor
nodes=
{
memo
.
nodes
}
autoFocus
readonly=
{
readonly
}
className=
"border-none !p-0 -mb-2"
onClick=
{
handleMemoContentClick
}
cacheKey=
{
`inline-memo-editor-${memo.name}`
}
onDoubleClick=
{
handleMemoContentDoubleClick
}
memoName=
{
memo
.
name
}
compact=
{
props
.
compact
&&
workspaceMemoRelatedSetting
.
enableAutoCompact
}
onConfirm=
{
()
=>
setShowEditor
(
false
)
}
/>
onCancel=
{
()
=>
setShowEditor
(
false
)
}
<
MemoResourceListView
resources=
{
memo
.
resources
}
/>
/>
<
MemoRelationListView
memo=
{
memo
}
relations=
{
referencedMemos
}
/>
)
:
(
<
MemoReactionistView
memo=
{
memo
}
reactions=
{
memo
.
reactions
}
/>
<>
<
MemoContent
key=
{
`${memo.name}-${memo.updateTime}`
}
memoName=
{
memo
.
name
}
nodes=
{
memo
.
nodes
}
readonly=
{
readonly
}
onClick=
{
handleMemoContentClick
}
onDoubleClick=
{
handleMemoContentDoubleClick
}
compact=
{
props
.
compact
&&
workspaceMemoRelatedSetting
.
enableAutoCompact
}
/>
<
MemoResourceListView
resources=
{
memo
.
resources
}
/>
<
MemoRelationListView
memo=
{
memo
}
relations=
{
referencedMemos
}
/>
<
MemoReactionistView
memo=
{
memo
}
reactions=
{
memo
.
reactions
}
/>
</>
)
}
</
div
>
</
div
>
);
);
};
};
...
...
web/src/pages/MemoDetail.tsx
View file @
e2fd7920
...
@@ -6,7 +6,7 @@ import { toast } from "react-hot-toast";
...
@@ -6,7 +6,7 @@ import { toast } from "react-hot-toast";
import
{
Link
,
useParams
}
from
"react-router-dom"
;
import
{
Link
,
useParams
}
from
"react-router-dom"
;
import
Icon
from
"@/components/Icon"
;
import
Icon
from
"@/components/Icon"
;
import
{
MemoDetailSidebar
,
MemoDetailSidebarDrawer
}
from
"@/components/MemoDetailSidebar"
;
import
{
MemoDetailSidebar
,
MemoDetailSidebarDrawer
}
from
"@/components/MemoDetailSidebar"
;
import
showMemoEditorDialog
from
"@/components/MemoEditor/MemoEditorDialog
"
;
import
MemoEditor
from
"@/components/MemoEditor
"
;
import
MemoView
from
"@/components/MemoView"
;
import
MemoView
from
"@/components/MemoView"
;
import
MobileHeader
from
"@/components/MobileHeader"
;
import
MobileHeader
from
"@/components/MobileHeader"
;
import
useCurrentUser
from
"@/hooks/useCurrentUser"
;
import
useCurrentUser
from
"@/hooks/useCurrentUser"
;
...
@@ -27,6 +27,7 @@ const MemoDetail = () => {
...
@@ -27,6 +27,7 @@ const MemoDetail = () => {
const
uid
=
params
.
uid
;
const
uid
=
params
.
uid
;
const
memo
=
memoStore
.
getMemoByUid
(
uid
||
""
);
const
memo
=
memoStore
.
getMemoByUid
(
uid
||
""
);
const
[
parentMemo
,
setParentMemo
]
=
useState
<
Memo
|
undefined
>
(
undefined
);
const
[
parentMemo
,
setParentMemo
]
=
useState
<
Memo
|
undefined
>
(
undefined
);
const
[
showCommentEditor
,
setShowCommentEditor
]
=
useState
(
false
);
const
commentRelations
=
const
commentRelations
=
memo
?.
relations
.
filter
((
relation
)
=>
relation
.
relatedMemo
===
memo
.
name
&&
relation
.
type
===
MemoRelation_Type
.
COMMENT
)
||
[];
memo
?.
relations
.
filter
((
relation
)
=>
relation
.
relatedMemo
===
memo
.
name
&&
relation
.
type
===
MemoRelation_Type
.
COMMENT
)
||
[];
const
comments
=
commentRelations
.
map
((
relation
)
=>
memoStore
.
getMemoByName
(
relation
.
memo
)).
filter
((
memo
)
=>
memo
)
as
any
as
Memo
[];
const
comments
=
commentRelations
.
map
((
relation
)
=>
memoStore
.
getMemoByName
(
relation
.
memo
)).
filter
((
memo
)
=>
memo
)
as
any
as
Memo
[];
...
@@ -66,17 +67,13 @@ const MemoDetail = () => {
...
@@ -66,17 +67,13 @@ const MemoDetail = () => {
}
}
const
handleShowCommentEditor
=
()
=>
{
const
handleShowCommentEditor
=
()
=>
{
showMemoEditorDialog
({
setShowCommentEditor
(
true
);
placeholder
:
t
(
"editor.add-your-comment-here"
),
parentMemoName
:
memo
.
name
,
onConfirm
:
handleCommentCreated
,
cacheKey
:
`
${
memo
.
name
}
-
${
memo
.
updateTime
}
-comment`
,
});
};
};
const
handleCommentCreated
=
async
(
memoCommentName
:
string
)
=>
{
const
handleCommentCreated
=
async
(
memoCommentName
:
string
)
=>
{
await
memoStore
.
getOrFetchMemoByName
(
memoCommentName
);
await
memoStore
.
getOrFetchMemoByName
(
memoCommentName
);
await
memoStore
.
getOrFetchMemoByName
(
memo
.
name
,
{
skipCache
:
true
});
await
memoStore
.
getOrFetchMemoByName
(
memo
.
name
,
{
skipCache
:
true
});
setShowCommentEditor
(
false
);
};
};
return
(
return
(
...
@@ -145,6 +142,18 @@ const MemoDetail = () => {
...
@@ -145,6 +142,18 @@ const MemoDetail = () => {
</>
</>
)
}
)
}
</
div
>
</
div
>
{
showCommentEditor
&&
(
<
div
className=
"w-full"
>
<
MemoEditor
cacheKey=
{
`${memo.name}-${memo.updateTime}-comment`
}
placeholder=
{
t
(
"editor.add-your-comment-here"
)
}
parentMemoName=
{
memo
.
name
}
autoFocus
onConfirm=
{
handleCommentCreated
}
onCancel=
{
()
=>
setShowCommentEditor
(
false
)
}
/>
</
div
>
)
}
</
div
>
</
div
>
</
div
>
</
div
>
{
md
&&
(
{
md
&&
(
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment