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
d4e54f34
Commit
d4e54f34
authored
Sep 23, 2023
by
Steven
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: update memo detail page
parent
08a81e79
Changes
7
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
149 additions
and
131 deletions
+149
-131
FloatingNavButton.tsx
web/src/components/FloatingNavButton.tsx
+3
-3
Memo.tsx
web/src/components/Memo.tsx
+11
-15
ShareMemoDialog.tsx
web/src/components/ShareMemoDialog.tsx
+27
-93
UserBanner.tsx
web/src/components/UserBanner.tsx
+3
-3
index.ts
web/src/hooks/index.ts
+2
-0
useNavigateTo.ts
web/src/hooks/useNavigateTo.ts
+20
-0
MemoDetail.tsx
web/src/pages/MemoDetail.tsx
+83
-17
No files found.
web/src/components/FloatingNavButton.tsx
View file @
d4e54f34
import
{
Dropdown
,
IconButton
,
Menu
,
MenuButton
}
from
"@mui/joy"
;
import
{
useNavigate
}
from
"react-router-dom
"
;
import
useNavigateTo
from
"@/hooks/useNavigateTo
"
;
import
{
useTranslate
}
from
"@/utils/i18n"
;
import
Icon
from
"./Icon"
;
const
FloatingNavButton
=
()
=>
{
const
navigate
=
useNavigate
();
const
navigate
To
=
useNavigateTo
();
const
t
=
useTranslate
();
return
(
...
...
@@ -23,7 +23,7 @@ const FloatingNavButton = () => {
<
Menu
placement=
"top-end"
>
<
button
className=
"w-full text-left text-sm whitespace-nowrap leading-6 py-1 px-3 cursor-pointer hover:bg-gray-100 dark:hover:bg-zinc-600"
onClick=
{
()
=>
navigate
(
"/"
)
}
onClick=
{
()
=>
navigate
To
(
"/"
)
}
>
{
t
(
"router.back-to-home"
)
}
</
button
>
...
...
web/src/components/Memo.tsx
View file @
d4e54f34
...
...
@@ -6,6 +6,7 @@ import { Link } from "react-router-dom";
import
{
UNKNOWN_ID
}
from
"@/helpers/consts"
;
import
{
getRelativeTimeString
}
from
"@/helpers/datetime"
;
import
useCurrentUser
from
"@/hooks/useCurrentUser"
;
import
useNavigateTo
from
"@/hooks/useNavigateTo"
;
import
{
useFilterStore
,
useMemoStore
,
useUserStore
}
from
"@/store/module"
;
import
{
useUserV1Store
}
from
"@/store/v1"
;
import
{
useTranslate
}
from
"@/utils/i18n"
;
...
...
@@ -17,7 +18,6 @@ import showMemoEditorDialog from "./MemoEditor/MemoEditorDialog";
import
MemoRelationListView
from
"./MemoRelationListView"
;
import
MemoResourceListView
from
"./MemoResourceListView"
;
import
showPreviewImageDialog
from
"./PreviewImageDialog"
;
import
showShareMemo
from
"./ShareMemoDialog"
;
import
UserAvatar
from
"./UserAvatar"
;
import
"@/less/memo.less"
;
...
...
@@ -30,6 +30,7 @@ interface Props {
const
Memo
:
React
.
FC
<
Props
>
=
(
props
:
Props
)
=>
{
const
{
memo
,
lazyRendering
}
=
props
;
const
t
=
useTranslate
();
const
navigateTo
=
useNavigateTo
();
const
{
i18n
}
=
useTranslation
();
const
filterStore
=
useFilterStore
();
const
userStore
=
useUserStore
();
...
...
@@ -89,6 +90,14 @@ const Memo: React.FC<Props> = (props: Props) => {
return
<
div
className=
{
`memo-wrapper min-h-[128px] ${"memos-" + memo.id}`
}
ref=
{
memoContainerRef
}
></
div
>;
}
const
handleGotoMemoDetailPage
=
(
event
:
React
.
MouseEvent
<
HTMLDivElement
>
)
=>
{
if
(
event
.
altKey
)
{
showChangeMemoCreatedTsDialog
(
memo
.
id
);
}
else
{
navigateTo
(
`/m/
${
memo
.
id
}
`
);
}
};
const
handleTogglePinMemoBtnClick
=
async
()
=>
{
try
{
if
(
memo
.
pinned
)
{
...
...
@@ -143,10 +152,6 @@ const Memo: React.FC<Props> = (props: Props) => {
});
};
const
handleGenerateMemoImageBtnClick
=
()
=>
{
showShareMemo
(
memo
);
};
const
handleMemoContentClick
=
async
(
e
:
React
.
MouseEvent
)
=>
{
const
targetEl
=
e
.
target
as
HTMLElement
;
...
...
@@ -217,11 +222,6 @@ const Memo: React.FC<Props> = (props: Props) => {
handleEditMemoClick
();
};
const
handleMemoCreatedTimeClick
=
(
e
:
React
.
MouseEvent
)
=>
{
e
.
preventDefault
();
showChangeMemoCreatedTsDialog
(
memo
.
id
);
};
return
(
<>
<
div
className=
{
`memo-wrapper ${"memos-" + memo.id} ${memo.pinned && !readonly ? "pinned" : ""}`
}
ref=
{
memoContainerRef
}
>
...
...
@@ -236,7 +236,7 @@ const Memo: React.FC<Props> = (props: Props) => {
<
Icon
.
Dot
className=
"w-4 h-auto text-gray-400 dark:text-zinc-400"
/>
</>
)
}
<
span
className=
"text-sm text-gray-400 select-none"
on
DoubleClick=
{
handleMemoCreatedTimeClick
}
>
<
span
className=
"text-sm text-gray-400 select-none"
on
Click=
{
handleGotoMemoDetailPage
}
>
{
displayTime
}
</
span
>
</
div
>
...
...
@@ -256,10 +256,6 @@ const Memo: React.FC<Props> = (props: Props) => {
<
Icon
.
Edit3
className=
"w-4 h-auto mr-2"
/>
{
t
(
"common.edit"
)
}
</
span
>
<
span
className=
"btn"
onClick=
{
handleGenerateMemoImageBtnClick
}
>
<
Icon
.
Share
className=
"w-4 h-auto mr-2"
/>
{
t
(
"common.share"
)
}
</
span
>
<
span
className=
"btn"
onClick=
{
handleMarkMemoClick
}
>
<
Icon
.
Link
className=
"w-4 h-auto mr-2"
/>
{
t
(
"common.mark"
)
}
...
...
web/src/components/ShareMemoDialog.tsx
View file @
d4e54f34
import
{
Option
,
Select
}
from
"@mui/joy"
;
import
{
Button
}
from
"@mui/joy"
;
import
copy
from
"copy-to-clipboard"
;
import
{
toLower
}
from
"lodash-es"
;
import
{
QRCodeSVG
}
from
"qrcode.react"
;
import
React
,
{
useEffect
,
useRef
,
useState
}
from
"react"
;
import
React
,
{
useEffect
,
useRef
}
from
"react"
;
import
{
toast
}
from
"react-hot-toast"
;
import
{
getMemoStats
}
from
"@/helpers/api"
;
import
{
VISIBILITY_SELECTOR_ITEMS
}
from
"@/helpers/consts"
;
import
{
getDateTimeString
,
getTimeStampByDate
}
from
"@/helpers/datetime"
;
import
{
getDateTimeString
}
from
"@/helpers/datetime"
;
import
useLoading
from
"@/hooks/useLoading"
;
import
toImage
from
"@/labs/html2image"
;
import
{
use
MemoStore
,
useUserStore
}
from
"@/store/module
"
;
import
{
use
UserV1Store
}
from
"@/store/v1
"
;
import
{
useTranslate
}
from
"@/utils/i18n"
;
import
{
generateDialog
}
from
"./Dialog"
;
import
showEmbedMemoDialog
from
"./EmbedMemoDialog"
;
import
Icon
from
"./Icon"
;
import
MemoContent
from
"./MemoContent"
;
import
MemoResourceListView
from
"./MemoResourceListView"
;
import
UserAvatar
from
"./UserAvatar"
;
import
"@/less/share-memo-dialog.less"
;
interface
Props
extends
DialogProps
{
memo
:
Memo
;
}
interface
State
{
memoAmount
:
number
;
memoVisibility
:
Visibility
;
showQRCode
:
boolean
;
}
const
ShareMemoDialog
:
React
.
FC
<
Props
>
=
(
props
:
Props
)
=>
{
const
{
memo
:
propsMemo
,
destroy
}
=
props
;
const
t
=
useTranslate
();
const
userStore
=
useUserStore
();
const
memoStore
=
useMemoStore
();
const
user
=
userStore
.
state
.
user
as
User
;
const
[
state
,
setState
]
=
useState
<
State
>
({
memoAmount
:
0
,
memoVisibility
:
propsMemo
.
visibility
,
showQRCode
:
true
,
});
const
createLoadingState
=
useLoading
(
false
);
const
userV1Store
=
useUserV1Store
();
const
downloadingImageState
=
useLoading
(
false
);
const
loadingState
=
useLoading
();
const
memoElRef
=
useRef
<
HTMLDivElement
>
(
null
);
const
memo
=
{
...
propsMemo
,
displayTsStr
:
getDateTimeString
(
propsMemo
.
displayTs
),
};
const
createdDays
=
Math
.
ceil
((
Date
.
now
()
-
getTimeStampByDate
(
user
.
createdTs
))
/
1000
/
3600
/
24
);
const
user
=
userV1Store
.
getUserByUsername
(
memo
.
creatorUsername
);
useEffect
(()
=>
{
getMemoStats
(
user
.
username
)
.
then
(({
data
})
=>
{
setPartialState
({
memoAmount
:
data
.
length
,
});
loadingState
.
setFinish
();
})
.
catch
((
error
)
=>
{
console
.
error
(
error
);
});
(
async
()
=>
{
await
userV1Store
.
getOrFetchUserByUsername
(
memo
.
creatorUsername
);
loadingState
.
setFinish
();
})();
},
[]);
const
setPartialState
=
(
partialState
:
Partial
<
State
>
)
=>
{
setState
({
...
state
,
...
partialState
,
});
};
const
handleCloseBtnClick
=
()
=>
{
destroy
();
};
const
handleDownloadBtnClick
=
()
=>
{
const
handleDownload
Image
BtnClick
=
()
=>
{
if
(
!
memoElRef
.
current
)
{
return
;
}
createLoadingState
.
setLoading
();
downloadingImageState
.
setLoading
();
toImage
(
memoElRef
.
current
,
{
pixelRatio
:
window
.
devicePixelRatio
*
2
,
})
...
...
@@ -88,7 +59,7 @@ const ShareMemoDialog: React.FC<Props> = (props: Props) => {
a
.
download
=
`memos-
${
getDateTimeString
(
Date
.
now
())}
.png`
;
a
.
click
();
createLoading
State
.
setFinish
();
downloadingImage
State
.
setFinish
();
})
.
catch
((
err
)
=>
{
console
.
error
(
err
);
...
...
@@ -104,23 +75,9 @@ const ShareMemoDialog: React.FC<Props> = (props: Props) => {
toast
.
success
(
t
(
"message.succeed-copy-link"
));
};
const
memoVisibilityOptionSelectorItems
=
VISIBILITY_SELECTOR_ITEMS
.
map
((
item
)
=>
{
return
{
value
:
item
.
value
,
text
:
t
(
`memo.visibility.
${
toLower
(
item
.
value
)
as
Lowercase
<
typeof
item
.
value
>
}
`
),
};
});
const
handleMemoVisibilityOptionChanged
=
async
(
value
:
string
)
=>
{
const
visibilityValue
=
value
as
Visibility
;
setPartialState
({
memoVisibility
:
visibilityValue
,
});
await
memoStore
.
patchMemo
({
id
:
memo
.
id
,
visibility
:
visibilityValue
,
});
};
if
(
loadingState
.
isLoading
)
{
return
null
;
}
return
(
<>
...
...
@@ -131,41 +88,23 @@ const ShareMemoDialog: React.FC<Props> = (props: Props) => {
</
button
>
</
div
>
<
div
className=
"dialog-content-container w-full flex flex-col justify-start items-start relative"
>
<
div
className=
"px-4 pb-3 w-full flex flex-row justify-start items-center"
>
<
span
className=
"text-sm mr-2"
>
{
t
(
"common.visibility"
)
}
:
</
span
>
<
Select
className=
"!min-w-[10rem] w-auto text-sm"
value=
{
state
.
memoVisibility
}
onChange=
{
(
_
,
visibility
)
=>
{
if
(
visibility
)
{
handleMemoVisibilityOptionChanged
(
visibility
);
}
}
}
>
{
memoVisibilityOptionSelectorItems
.
map
((
item
)
=>
(
<
Option
key=
{
item
.
value
}
value=
{
item
.
value
}
className=
"whitespace-nowrap"
>
{
item
.
text
}
</
Option
>
))
}
</
Select
>
</
div
>
<
div
className=
"px-4 pb-3 w-full flex flex-row justify-start items-center space-x-2"
>
<
button
disabled=
{
createLoadingState
.
isLoading
}
className=
"btn-normal h-8"
onClick=
{
handleDownload
BtnClick
}
>
{
createLoading
State
.
isLoading
?
(
<
Button
color=
"neutral"
variant=
"outlined"
disabled=
{
downloadingImageState
.
isLoading
}
onClick=
{
handleDownloadImage
BtnClick
}
>
{
downloadingImage
State
.
isLoading
?
(
<
Icon
.
Loader
className=
"w-4 h-auto mr-1 animate-spin"
/>
)
:
(
<
Icon
.
Download
className=
"w-4 h-auto mr-1"
/>
)
}
{
t
(
"common.image"
)
}
</
b
utton
>
<
button
className=
"btn-normal h-8
"
onClick=
{
handleShowEmbedMemoDialog
}
>
</
B
utton
>
<
Button
color=
"neutral"
variant=
"outlined
"
onClick=
{
handleShowEmbedMemoDialog
}
>
<
Icon
.
Code
className=
"w-4 h-auto mr-1"
/>
{
t
(
"memo.embed"
)
}
</
b
utton
>
<
button
className=
"btn-normal h-8
"
onClick=
{
handleCopyLinkBtnClick
}
>
</
B
utton
>
<
Button
color=
"neutral"
variant=
"outlined
"
onClick=
{
handleCopyLinkBtnClick
}
>
<
Icon
.
Link
className=
"w-4 h-auto mr-1"
/>
{
t
(
"common.link"
)
}
</
b
utton
>
</
B
utton
>
</
div
>
<
div
className=
"w-full rounded-lg border-t overflow-clip"
>
<
div
...
...
@@ -178,18 +117,13 @@ const ShareMemoDialog: React.FC<Props> = (props: Props) => {
<
MemoResourceListView
className=
"!grid-cols-2"
resourceList=
{
memo
.
resourceList
}
/>
</
div
>
<
div
className=
"flex flex-row justify-between items-center w-full bg-gray-100 dark:bg-zinc-700 py-4 px-6"
>
<
div
className=
"mr-2"
>
<
img
className=
"h-10 w-auto rounded-lg"
src=
{
`${user.avatarUrl || "/logo.webp"}`
}
alt=
""
/>
</
div
>
<
UserAvatar
className=
"mr-2"
avatarUrl=
{
user
.
avatarUrl
}
/>
<
div
className=
"w-auto grow truncate flex mr-2 flex-col justify-center items-start"
>
<
span
className=
"w-full text-sm truncate font-bold text-gray-600 dark:text-gray-300"
>
{
user
.
nickname
||
user
.
username
}
</
span
>
<
span
className=
"text-xs text-gray-400"
>
{
state
.
memoAmount
}
MEMOS /
{
createdDays
}
DAYS
</
span
>
</
div
>
<
QRCodeSVG
value=
{
`${window.location.origin}/m/${memo.id}`
}
size=
{
40
}
size=
{
28
}
bgColor=
{
"#F3F4F6"
}
fgColor=
{
"#4B5563"
}
includeMargin=
{
false
}
...
...
web/src/components/UserBanner.tsx
View file @
d4e54f34
import
{
useNavigate
}
from
"react-router-dom"
;
import
useCurrentUser
from
"@/hooks/useCurrentUser"
;
import
useNavigateTo
from
"@/hooks/useNavigateTo"
;
import
{
useGlobalStore
,
useUserStore
}
from
"@/store/module"
;
import
{
User_Role
}
from
"@/types/proto/api/v2/user_service"
;
import
{
useTranslate
}
from
"@/utils/i18n"
;
...
...
@@ -10,7 +10,7 @@ import Dropdown from "./kit/Dropdown";
const
UserBanner
=
()
=>
{
const
t
=
useTranslate
();
const
navigate
=
useNavigate
();
const
navigate
To
=
useNavigateTo
();
const
globalStore
=
useGlobalStore
();
const
userStore
=
useUserStore
();
const
{
systemStatus
}
=
globalStore
.
state
;
...
...
@@ -18,7 +18,7 @@ const UserBanner = () => {
const
title
=
user
?
user
.
nickname
:
systemStatus
.
customizedProfile
.
name
||
"memos"
;
const
handleMyAccountClick
=
()
=>
{
navigate
(
`/u/
${
encodeURIComponent
(
user
.
username
)}
`
);
navigate
To
(
`/u/
${
encodeURIComponent
(
user
.
username
)}
`
);
};
const
handleAboutBtnClick
=
()
=>
{
...
...
web/src/hooks/index.ts
View file @
d4e54f34
export
*
from
"./useLoading"
;
export
*
from
"./useCurrentUser"
;
export
*
from
"./useNavigateTo"
;
web/src/hooks/useNavigateTo.ts
0 → 100644
View file @
d4e54f34
import
{
useNavigate
}
from
"react-router-dom"
;
const
useNavigateTo
=
()
=>
{
const
navigateTo
=
useNavigate
();
const
navigateToWithViewTransition
=
(
to
:
string
)
=>
{
const
document
=
window
.
document
as
any
;
if
(
!
document
.
startViewTransition
)
{
navigateTo
(
to
);
}
else
{
document
.
startViewTransition
(()
=>
{
navigateTo
(
to
);
});
}
};
return
navigateToWithViewTransition
;
};
export
default
useNavigateTo
;
web/src/pages/MemoDetail.tsx
View file @
d4e54f34
import
{
Divider
,
Select
,
Tooltip
,
Option
,
IconButton
}
from
"@mui/joy"
;
import
copy
from
"copy-to-clipboard"
;
import
{
toLower
}
from
"lodash-es"
;
import
{
useEffect
,
useState
}
from
"react"
;
import
{
toast
}
from
"react-hot-toast"
;
import
{
useParams
}
from
"react-router-dom"
;
import
FloatingNavButton
from
"@/components/FloatingNavButton"
;
import
Memo
from
"@/components/Memo"
;
import
Icon
from
"@/components/Icon"
;
import
MemoContent
from
"@/components/MemoContent"
;
import
MemoRelationListView
from
"@/components/MemoRelationListView"
;
import
MemoResourceListView
from
"@/components/MemoResourceListView"
;
import
showShareMemoDialog
from
"@/components/ShareMemoDialog"
;
import
UserAvatar
from
"@/components/UserAvatar"
;
import
useLoading
from
"@/hooks/useLoading"
;
import
{
VISIBILITY_SELECTOR_ITEMS
}
from
"@/helpers/consts"
;
import
{
getDateTimeString
}
from
"@/helpers/datetime"
;
import
useNavigateTo
from
"@/hooks/useNavigateTo"
;
import
{
useMemoStore
}
from
"@/store/module"
;
import
{
useUserV1Store
}
from
"@/store/v1"
;
import
{
User
}
from
"@/types/proto/api/v2/user_service"
;
import
{
useTranslate
}
from
"@/utils/i18n"
;
const
MemoDetail
=
()
=>
{
const
params
=
useParams
();
const
navigateTo
=
useNavigateTo
();
const
t
=
useTranslate
();
const
memoStore
=
useMemoStore
();
const
userV1Store
=
useUserV1Store
();
const
loadingState
=
useLoading
();
const
[
user
,
setUser
]
=
useState
<
User
>
();
const
memoId
=
Number
(
params
.
memoId
);
const
memo
=
memoStore
.
state
.
memos
.
find
((
memo
)
=>
memo
.
id
===
memoId
);
...
...
@@ -25,18 +36,43 @@ const MemoDetail = () => {
.
then
(
async
(
memo
)
=>
{
const
user
=
await
userV1Store
.
getOrFetchUserByUsername
(
memo
.
creatorUsername
);
setUser
(
user
);
loadingState
.
setFinish
();
})
.
catch
((
error
)
=>
{
console
.
error
(
error
);
toast
.
error
(
error
.
response
.
data
.
message
);
});
}
else
{
navigateTo
(
"/404"
);
}
},
[
memoId
]);
if
(
!
memo
)
{
return
null
;
}
const
memoVisibilityOptionSelectorItems
=
VISIBILITY_SELECTOR_ITEMS
.
map
((
item
)
=>
{
return
{
value
:
item
.
value
,
text
:
t
(
`memo.visibility.
${
toLower
(
item
.
value
)
as
Lowercase
<
typeof
item
.
value
>
}
`
),
};
});
const
handleMemoVisibilityOptionChanged
=
async
(
value
:
string
)
=>
{
const
visibilityValue
=
value
as
Visibility
;
await
memoStore
.
patchMemo
({
id
:
memo
.
id
,
visibility
:
visibilityValue
,
});
};
const
handleCopyLinkBtnClick
=
()
=>
{
copy
(
`
${
window
.
location
.
origin
}
/m/
${
memo
.
id
}
`
);
toast
.
success
(
t
(
"message.succeed-copy-link"
));
};
return
(
<>
<
section
className=
"relative top-0 w-full min-h-full overflow-x-hidden bg-
zinc-100
dark:bg-zinc-800"
>
<
section
className=
"relative top-0 w-full min-h-full overflow-x-hidden bg-
white
dark:bg-zinc-800"
>
<
div
className=
"relative w-full min-h-full mx-auto flex flex-col justify-start items-center pb-6"
>
<
div
className=
"w-full flex flex-col justify-start items-center py-8"
>
<
UserAvatar
className=
"!w-20 h-auto mb-4 drop-shadow"
avatarUrl=
{
user
?.
avatarUrl
}
/>
...
...
@@ -44,18 +80,48 @@ const MemoDetail = () => {
<
p
className=
"text-2xl font-bold text-gray-700 dark:text-gray-300"
>
{
user
?.
nickname
}
</
p
>
</
div
>
</
div
>
{
!
loadingState
.
isLoading
&&
(
memo
?
(
<>
<
div
className=
"relative flex-grow max-w-2xl w-full min-h-full flex flex-col justify-start items-start px-4"
>
<
Memo
memo=
{
memo
}
/>
</
div
>
</>
)
:
(
<>
<
p
>
Not found
</
p
>
</>
))
}
<
div
className=
"relative flex-grow max-w-2xl w-full min-h-full flex flex-col justify-start items-start px-4"
>
<
MemoContent
content=
{
memo
.
content
}
/>
<
MemoResourceListView
resourceList=
{
memo
.
resourceList
}
/>
<
MemoRelationListView
relationList=
{
memo
.
relationList
}
/>
<
Divider
className=
"!my-6"
/>
<
div
className=
"w-full flex flex-col sm:flex-row justify-start sm:justify-between sm:items-center gap-2"
>
<
div
className=
"flex flex-row justify-start items-center"
>
<
span
className=
"text-sm text-gray-500 dark:text-gray-400"
>
{
getDateTimeString
(
memo
.
displayTs
)
}
</
span
>
<
span
className=
"mx-1 font-mono opacity-80 text-gray-400"
>
·
</
span
>
<
Tooltip
title=
{
"The identifier of memo"
}
placement=
"top"
>
<
span
className=
"text-sm text-gray-500 dark:text-gray-400"
>
#
{
memo
.
id
}
</
span
>
</
Tooltip
>
<
span
className=
"mx-1 font-mono opacity-80 text-gray-400"
>
·
</
span
>
<
Tooltip
title=
{
"The visibility of memo"
}
placement=
"top"
>
<
Select
className=
"w-auto text-sm"
variant=
"plain"
value=
{
memo
.
visibility
}
onChange=
{
(
_
,
visibility
)
=>
{
if
(
visibility
)
{
handleMemoVisibilityOptionChanged
(
visibility
);
}
}
}
>
{
memoVisibilityOptionSelectorItems
.
map
((
item
)
=>
(
<
Option
key=
{
item
.
value
}
value=
{
item
.
value
}
className=
"whitespace-nowrap"
>
{
item
.
text
}
</
Option
>
))
}
</
Select
>
</
Tooltip
>
</
div
>
<
div
className=
"flex flex-row sm:justify-end items-center"
>
<
IconButton
size=
"sm"
onClick=
{
handleCopyLinkBtnClick
}
>
<
Icon
.
Link
className=
"w-4 h-auto text-gray-600"
/>
</
IconButton
>
<
IconButton
size=
"sm"
onClick=
{
()
=>
showShareMemoDialog
(
memo
)
}
>
<
Icon
.
Share
className=
"w-4 h-auto text-gray-600"
/>
</
IconButton
>
</
div
>
</
div
>
</
div
>
</
div
>
</
section
>
...
...
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