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
c3135961
Commit
c3135961
authored
Jul 22, 2024
by
Steven
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: update memo editor
parent
d2727e68
Changes
9
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
227 additions
and
327 deletions
+227
-327
ActivityCalendar.tsx
web/src/components/ActivityCalendar.tsx
+25
-18
MemoActionMenu.tsx
web/src/components/MemoActionMenu.tsx
+1
-8
MemoEditorDialog.tsx
web/src/components/MemoEditor/MemoEditorDialog.tsx
+0
-103
index.tsx
web/src/components/MemoEditor/index.tsx
+44
-29
MemoRelationListView.tsx
web/src/components/MemoRelationListView.tsx
+5
-3
MemoView.tsx
web/src/components/MemoView.tsx
+71
-71
UserStatisticsView.tsx
web/src/components/UserStatisticsView.tsx
+81
-81
index.ts
web/src/hooks/index.ts
+0
-1
useDateTime.ts
web/src/hooks/useDateTime.ts
+0
-13
No files found.
web/src/components/ActivityCalendar.tsx
View file @
c3135961
...
...
@@ -18,11 +18,11 @@ const getCellAdditionalStyles = (count: number, maxCount: number) => {
const
ratio
=
count
/
maxCount
;
if
(
ratio
>
0.7
)
{
return
"bg-teal-
6
00 text-gray-100 dark:opacity-80"
;
return
"bg-teal-
7
00 text-gray-100 dark:opacity-80"
;
}
else
if
(
ratio
>
0.4
)
{
return
"bg-teal-
4
00 text-gray-100 dark:opacity-80"
;
return
"bg-teal-
6
00 text-gray-100 dark:opacity-80"
;
}
else
{
return
"bg-teal-
300 text-gray-100 dark:opacity-8
0"
;
return
"bg-teal-
500 text-gray-100 dark:opacity-7
0"
;
}
};
...
...
@@ -58,29 +58,36 @@ const ActivityCalendar = (props: Props) => {
const
tooltipText
=
count
?
t
(
"memo.count-memos-in-date"
,
{
count
:
count
,
date
:
date
})
:
date
;
const
isSelected
=
new
Date
(
props
.
selectedDate
).
toDateString
()
===
new
Date
(
date
).
toDateString
();
return
day
?
(
<
Tooltip
className=
"shrink-0"
key=
{
`${date}-${index}`
}
title=
{
tooltipText
}
placement=
"top"
arrow
>
count
>
0
?
(
<
Tooltip
className=
"shrink-0"
key=
{
`${date}-${index}`
}
title=
{
tooltipText
}
placement=
"top"
arrow
>
<
div
className=
{
clsx
(
"w-6 h-6 text-xs rounded-xl flex justify-center items-center border cursor-default"
,
getCellAdditionalStyles
(
count
,
maxCount
),
isToday
&&
"border-zinc-400 dark:border-zinc-300"
,
isSelected
&&
"font-bold border-zinc-400 dark:border-zinc-300"
,
!
isToday
&&
!
isSelected
&&
"border-transparent"
,
)
}
onClick=
{
()
=>
count
&&
onClick
&&
onClick
(
new
Date
(
date
).
toDateString
())
}
>
{
day
}
</
div
>
</
Tooltip
>
)
:
(
<
div
key=
{
`${date}-${index}`
}
className=
{
clsx
(
"w-6 h-6 text-xs rounded-xl flex justify-center items-center border"
,
getCellAdditionalStyles
(
count
,
maxCount
),
isToday
&&
"border-gray-600 dark:border-zinc-300"
,
isSelected
&&
"font-bold border-gray-600 dark:border-zinc-300"
,
"w-6 h-6 text-xs rounded-xl flex justify-center items-center border cursor-default"
,
"bg-gray-100 text-gray-400 dark:bg-zinc-800 dark:text-gray-500"
,
isToday
&&
"border-zinc-400 dark:border-zinc-500"
,
!
isToday
&&
!
isSelected
&&
"border-transparent"
,
count
>
0
?
"cursor-pointer"
:
"cursor-default"
,
)
}
onClick=
{
()
=>
count
&&
onClick
&&
onClick
(
new
Date
(
date
).
toDateString
())
}
>
{
day
}
</
div
>
</
Tooltip
>
)
)
:
(
<
div
key=
{
`${date}-${index}`
}
className=
{
clsx
(
"shrink-0 opacity-30 w-6 h-6 rounded-xl flex justify-center items-center border border-transparent"
,
getCellAdditionalStyles
(
count
,
maxCount
),
)
}
></
div
>
<
div
key=
{
`${date}-${index}`
}
className=
{
clsx
(
"shrink-0 w-6 h-6 opacity-0"
,
getCellAdditionalStyles
(
count
,
maxCount
))
}
></
div
>
);
})
}
</
div
>
...
...
web/src/components/MemoActionMenu.tsx
View file @
c3135961
...
...
@@ -9,7 +9,6 @@ import { useMemoStore } from "@/store/v1";
import
{
RowStatus
}
from
"@/types/proto/api/v1/common"
;
import
{
Memo
}
from
"@/types/proto/api/v1/memo_service"
;
import
{
useTranslate
}
from
"@/utils/i18n"
;
import
showMemoEditorDialog
from
"./MemoEditor/MemoEditorDialog"
;
interface
Props
{
memo
:
Memo
;
...
...
@@ -55,12 +54,6 @@ const MemoActionMenu = (props: Props) => {
props
.
onEdit
();
return
;
}
// TODO: remove me later.
showMemoEditorDialog
({
memoName
:
memo
.
name
,
cacheKey
:
`
${
memo
.
name
}
-
${
memo
.
updateTime
}
`
,
});
};
const
handleToggleMemoStatusClick
=
async
()
=>
{
...
...
@@ -125,7 +118,7 @@ const MemoActionMenu = (props: Props) => {
{
memo
.
pinned
?
t
(
"common.unpin"
)
:
t
(
"common.pin"
)
}
</
MenuItem
>
)
}
{
!
hiddenActions
?.
includes
(
"edit"
)
&&
(
{
!
hiddenActions
?.
includes
(
"edit"
)
&&
props
.
onEdit
&&
(
<
MenuItem
onClick=
{
handleEditMemoClick
}
>
<
Icon
.
Edit3
className=
"w-4 h-auto"
/>
{
t
(
"common.edit"
)
}
...
...
web/src/components/MemoEditor/MemoEditorDialog.tsx
deleted
100644 → 0
View file @
d2727e68
import
{
IconButton
}
from
"@mui/joy"
;
import
clsx
from
"clsx"
;
import
{
useEffect
,
useRef
}
from
"react"
;
import
useCurrentUser
from
"@/hooks/useCurrentUser"
;
import
useDateTime
from
"@/hooks/useDateTime"
;
import
{
useMemoStore
,
useTagStore
}
from
"@/store/v1"
;
import
{
Memo
}
from
"@/types/proto/api/v1/memo_service"
;
import
MemoEditor
,
{
Props
as
MemoEditorProps
}
from
"."
;
import
{
generateDialog
}
from
"../Dialog"
;
import
Icon
from
"../Icon"
;
interface
Props
extends
DialogProps
,
MemoEditorProps
{}
const
MemoEditorDialog
:
React
.
FC
<
Props
>
=
({
memoName
,
parentMemoName
,
placeholder
,
cacheKey
,
relationList
,
onConfirm
,
destroy
,
}:
Props
)
=>
{
const
tagStore
=
useTagStore
();
const
memoStore
=
useMemoStore
();
const
{
setDateTime
,
displayDateTime
,
datePickerDateTime
}
=
useDateTime
(
memoStore
.
getMemoByName
(
memoName
||
""
)?.
displayTime
);
const
memoPatchRef
=
useRef
<
Partial
<
Memo
>>
({
displayTime
:
memoStore
.
getMemoByName
(
memoName
||
""
)?.
displayTime
,
});
const
user
=
useCurrentUser
();
useEffect
(()
=>
{
tagStore
.
fetchTags
({
user
},
{
skipCache
:
false
});
},
[]);
const
updateDisplayTime
=
(
displayTime
:
string
)
=>
{
setDateTime
(
displayTime
);
memoPatchRef
.
current
.
displayTime
=
new
Date
(
displayTime
);
};
const
handleCloseBtnClick
=
()
=>
{
destroy
();
};
const
handleConfirm
=
(
memoName
:
string
)
=>
{
handleCloseBtnClick
();
if
(
onConfirm
)
{
onConfirm
(
memoName
);
}
};
return
(
<>
<
div
className=
"w-full flex flex-row justify-between items-center"
>
<
div
className=
{
clsx
(
"flex flex-row justify-start items-center"
,
!
displayDateTime
&&
"mb-2"
)
}
>
{
displayDateTime
?
(
<
div
className=
"relative"
>
<
span
className=
"cursor-pointer text-gray-500 dark:text-gray-400"
>
{
displayDateTime
}
</
span
>
<
input
className=
"inset-0 absolute z-1 opacity-0"
type=
"datetime-local"
value=
{
datePickerDateTime
}
onFocus=
{
(
e
:
any
)
=>
e
.
target
.
showPicker
()
}
onChange=
{
(
e
)
=>
updateDisplayTime
(
e
.
target
.
value
)
}
/>
</
div
>
)
:
(
<>
<
img
className=
"w-6 h-auto rounded-full shadow"
src=
{
"/full-logo.webp"
}
alt=
""
/>
<
p
className=
"ml-1 text-lg opacity-80 dark:text-gray-300"
>
Memos
</
p
>
</>
)
}
</
div
>
<
IconButton
size=
"sm"
onClick=
{
handleCloseBtnClick
}
>
<
Icon
.
X
className=
"w-5 h-auto"
/>
</
IconButton
>
</
div
>
<
div
className=
"flex flex-col justify-start items-start max-w-full w-[40rem]"
>
<
MemoEditor
className=
"border-none !p-0 -mb-2"
cacheKey=
{
`memo-editor-${cacheKey || memoName}`
}
memoName=
{
memoName
}
parentMemoName=
{
parentMemoName
}
placeholder=
{
placeholder
}
relationList=
{
relationList
}
memoPatchRef=
{
memoPatchRef
}
onConfirm=
{
handleConfirm
}
autoFocus
/>
</
div
>
</>
);
};
export
default
function
showMemoEditorDialog
(
props
:
Partial
<
Props
>
=
{}):
void
{
generateDialog
(
{
className
:
"memo-editor-dialog"
,
dialogName
:
"memo-editor-dialog"
,
},
MemoEditorDialog
,
props
,
);
}
web/src/components/MemoEditor/index.tsx
View file @
c3135961
import
{
Select
,
Option
,
Button
,
Divider
}
from
"@mui/joy"
;
import
{
isEqual
}
from
"lodash-es"
;
import
React
,
{
useEffect
,
useMemo
,
useRef
,
useState
}
from
"react"
;
import
{
toast
}
from
"react-hot-toast"
;
import
{
useTranslation
}
from
"react-i18next"
;
...
...
@@ -6,6 +7,7 @@ import useLocalStorage from "react-use/lib/useLocalStorage";
import
{
memoServiceClient
}
from
"@/grpcweb"
;
import
{
TAB_SPACE_WIDTH
}
from
"@/helpers/consts"
;
import
{
isValidUrl
}
from
"@/helpers/utils"
;
import
useAsyncEffect
from
"@/hooks/useAsyncEffect"
;
import
useCurrentUser
from
"@/hooks/useCurrentUser"
;
import
{
useMemoStore
,
useResourceStore
,
useUserStore
,
useWorkspaceSettingStore
}
from
"@/store/v1"
;
import
{
MemoRelation
,
MemoRelation_Type
}
from
"@/types/proto/api/v1/memo_relation_service"
;
...
...
@@ -32,11 +34,11 @@ export interface Props {
className
?:
string
;
cacheKey
?:
string
;
placeholder
?:
string
;
// The name of the memo to be edited.
memoName
?:
string
;
// The name of the parent memo if the memo is a comment.
parentMemoName
?:
string
;
relationList
?:
MemoRelation
[];
autoFocus
?:
boolean
;
memoPatchRef
?:
React
.
MutableRefObject
<
Partial
<
Memo
>>
;
onConfirm
?:
(
memoName
:
string
)
=>
void
;
onCancel
?:
()
=>
void
;
}
...
...
@@ -62,11 +64,12 @@ const MemoEditor = (props: Props) => {
const
[
state
,
setState
]
=
useState
<
State
>
({
memoVisibility
:
Visibility
.
PRIVATE
,
resourceList
:
[],
relationList
:
props
.
relationList
??
[],
relationList
:
[],
isUploadingResource
:
false
,
isRequesting
:
false
,
isComposing
:
false
,
});
const
[
displayTime
,
setDisplayTime
]
=
useState
<
Date
|
undefined
>
();
const
[
hasContent
,
setHasContent
]
=
useState
<
boolean
>
(
false
);
const
editorRef
=
useRef
<
EditorRefActions
>
(
null
);
const
userSetting
=
userStore
.
userSetting
as
UserSetting
;
...
...
@@ -102,22 +105,24 @@ const MemoEditor = (props: Props) => {
}));
},
[
userSetting
.
memoVisibility
,
workspaceMemoRelatedSetting
.
disallowPublicVisibility
]);
useEffect
(()
=>
{
if
(
memoName
)
{
memoStore
.
getOrFetchMemoByName
(
memoName
).
then
((
memo
)
=>
{
if
(
memo
)
{
handleEditorFocus
();
setState
((
prevState
)
=>
({
...
prevState
,
memoVisibility
:
memo
.
visibility
,
resourceList
:
memo
.
resources
,
relationList
:
memo
.
relations
,
}));
if
(
!
contentCache
)
{
editorRef
.
current
?.
setContent
(
memo
.
content
??
""
);
}
}
});
useAsyncEffect
(
async
()
=>
{
if
(
!
memoName
)
{
return
;
}
const
memo
=
await
memoStore
.
getOrFetchMemoByName
(
memoName
);
if
(
memo
)
{
handleEditorFocus
();
setState
((
prevState
)
=>
({
...
prevState
,
memoVisibility
:
memo
.
visibility
,
resourceList
:
memo
.
resources
,
relationList
:
memo
.
relations
,
}));
setDisplayTime
(
memo
.
displayTime
);
if
(
!
contentCache
)
{
editorRef
.
current
?.
setContent
(
memo
.
content
??
""
);
}
}
},
[
memoName
]);
...
...
@@ -289,18 +294,16 @@ const MemoEditor = (props: Props) => {
const
prevMemo
=
await
memoStore
.
getOrFetchMemoByName
(
memoName
);
if
(
prevMemo
)
{
const
updateMask
=
[
"content"
,
"visibility"
];
if
(
props
.
memoPatchRef
?.
current
?.
displayTime
)
{
const
memoPatch
:
Partial
<
Memo
>
=
{
name
:
prevMemo
.
name
,
content
,
visibility
:
state
.
memoVisibility
,
};
if
(
!
isEqual
(
displayTime
,
prevMemo
.
displayTime
))
{
updateMask
.
push
(
"display_time"
);
memoPatch
.
displayTime
=
displayTime
;
}
const
memo
=
await
memoStore
.
updateMemo
(
{
name
:
prevMemo
.
name
,
content
,
visibility
:
state
.
memoVisibility
,
...
props
.
memoPatchRef
?.
current
,
},
updateMask
,
);
const
memo
=
await
memoStore
.
updateMemo
(
memoPatch
,
updateMask
);
await
memoServiceClient
.
setMemoResources
({
name
:
memo
.
name
,
resources
:
state
.
resourceList
,
...
...
@@ -409,6 +412,18 @@ const MemoEditor = (props: Props) => {
onCompositionStart=
{
handleCompositionStart
}
onCompositionEnd=
{
handleCompositionEnd
}
>
{
memoName
&&
displayTime
&&
(
<
div
className=
"relative text-sm"
>
<
span
className=
"cursor-pointer text-gray-400 dark:text-gray-500"
>
{
displayTime
.
toLocaleString
()
}
</
span
>
<
input
className=
"inset-0 absolute z-1 opacity-0"
type=
"datetime-local"
value=
{
displayTime
.
toLocaleString
()
}
onFocus=
{
(
e
:
any
)
=>
e
.
target
.
showPicker
()
}
onChange=
{
(
e
)
=>
setDisplayTime
(
new
Date
(
e
.
target
.
value
))
}
/>
</
div
>
)
}
<
Editor
ref=
{
editorRef
}
{
...
editorConfig
}
/>
<
ResourceListView
resourceList=
{
state
.
resourceList
}
setResourceList=
{
handleSetResourceList
}
/>
<
RelationListView
relationList=
{
referenceRelations
}
setRelationList=
{
handleSetRelationList
}
/>
...
...
web/src/components/MemoRelationListView.tsx
View file @
c3135961
...
...
@@ -44,7 +44,7 @@ const MemoRelationListView = (props: Props) => {
}
return
(
<
div
className=
"relative flex flex-col justify-start items-start w-full px-2 pt-2 pb-1 bg-zinc-50 dark:bg-zinc-900 rounded-lg border border-gray-200 dark:border-zinc-700"
>
<
div
className=
"relative flex flex-col justify-start items-start w-full px-2 pt-2 pb-1
.5
bg-zinc-50 dark:bg-zinc-900 rounded-lg border border-gray-200 dark:border-zinc-700"
>
<
div
className=
"w-full flex flex-row justify-start items-center mb-1 gap-3 opacity-60"
>
{
referencingMemoList
.
length
>
0
&&
(
<
button
...
...
@@ -56,6 +56,7 @@ const MemoRelationListView = (props: Props) => {
>
<
Icon
.
Link
className=
"w-3 h-auto shrink-0 opacity-70"
/>
<
span
>
Referencing
</
span
>
<
span
className=
"opacity-80"
>
(
{
referencingMemoList
.
length
}
)
</
span
>
</
button
>
)
}
{
referencedMemoList
.
length
>
0
&&
(
...
...
@@ -68,6 +69,7 @@ const MemoRelationListView = (props: Props) => {
>
<
Icon
.
Milestone
className=
"w-3 h-auto shrink-0 opacity-70"
/>
<
span
>
Referenced by
</
span
>
<
span
className=
"opacity-80"
>
(
{
referencedMemoList
.
length
}
)
</
span
>
</
button
>
)
}
</
div
>
...
...
@@ -81,7 +83,7 @@ const MemoRelationListView = (props: Props) => {
to=
{
`/m/${memo.uid}`
}
unstable_viewTransition
>
<
Icon
.
Dot
className=
"shrink-0
-ml-1 opacity-6
0"
/>
<
Icon
.
Dot
className=
"shrink-0
w-4 h-auto opacity-4
0"
/>
<
span
className=
"truncate"
>
{
memo
.
snippet
}
</
span
>
</
Link
>
);
...
...
@@ -98,7 +100,7 @@ const MemoRelationListView = (props: Props) => {
to=
{
`/m/${memo.uid}`
}
unstable_viewTransition
>
<
Icon
.
Dot
className=
"shrink-0
-ml-1 opacity-6
0"
/>
<
Icon
.
Dot
className=
"shrink-0
w-4 h-auto opacity-4
0"
/>
<
span
className=
"truncate"
>
{
memo
.
snippet
}
</
span
>
</
Link
>
);
...
...
web/src/components/MemoView.tsx
View file @
c3135961
...
...
@@ -106,77 +106,6 @@ const MemoView: React.FC<Props> = (props: Props) => {
)
}
ref=
{
memoContainerRef
}
>
<
div
className=
"w-full flex flex-row justify-between items-center gap-2"
>
<
div
className=
"w-auto max-w-[calc(100%-8rem)] grow flex flex-row justify-start items-center"
>
{
props
.
showCreator
&&
creator
?
(
<
div
className=
"w-full flex flex-row justify-start items-center"
>
<
Link
className=
"w-auto hover:opacity-80"
to=
{
`/u/${encodeURIComponent(creator.username)}`
}
unstable_viewTransition
>
<
UserAvatar
className=
"mr-2 shrink-0"
avatarUrl=
{
creator
.
avatarUrl
}
/>
</
Link
>
<
div
className=
"w-full flex flex-col justify-center items-start"
>
<
Link
className=
"w-full block leading-tight hover:opacity-80 truncate text-gray-600 dark:text-gray-400"
to=
{
`/u/${encodeURIComponent(creator.username)}`
}
unstable_viewTransition
>
{
creator
.
nickname
||
creator
.
username
}
</
Link
>
<
div
className=
"w-auto -mt-0.5 text-xs leading-tight text-gray-400 dark:text-gray-500 select-none"
onClick=
{
handleGotoMemoDetailPage
}
>
{
displayTime
}
</
div
>
</
div
>
</
div
>
)
:
(
<
div
className=
"w-full text-sm leading-tight text-gray-400 dark:text-gray-500 select-none"
onClick=
{
handleGotoMemoDetailPage
}
>
{
displayTime
}
</
div
>
)
}
</
div
>
{
!
showEditor
&&
(
<
div
className=
"flex flex-row justify-end items-center select-none shrink-0 gap-2"
>
<
div
className=
"w-auto invisible group-hover:visible flex flex-row justify-between items-center gap-2"
>
{
props
.
showVisibility
&&
memo
.
visibility
!==
Visibility
.
PRIVATE
&&
(
<
Tooltip
title=
{
t
(
`memo.visibility.${convertVisibilityToString(memo.visibility).toLowerCase()}`
as
any
)
}
placement=
"top"
>
<
span
className=
"flex justify-center items-center hover:opacity-70"
>
<
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
>
)
}
{
!
readonly
&&
(
<
MemoActionMenu
className=
"-ml-1"
memo=
{
memo
}
hiddenActions=
{
props
.
showPinned
?
[]
:
[
"pin"
]
}
onEdit=
{
()
=>
setShowEditor
(
true
)
}
/>
)
}
</
div
>
)
}
</
div
>
{
showEditor
?
(
<
MemoEditor
autoFocus
...
...
@@ -188,6 +117,77 @@ const MemoView: React.FC<Props> = (props: Props) => {
/>
)
:
(
<>
<
div
className=
"w-full flex flex-row justify-between items-center gap-2"
>
<
div
className=
"w-auto max-w-[calc(100%-8rem)] grow flex flex-row justify-start items-center"
>
{
props
.
showCreator
&&
creator
?
(
<
div
className=
"w-full flex flex-row justify-start items-center"
>
<
Link
className=
"w-auto hover:opacity-80"
to=
{
`/u/${encodeURIComponent(creator.username)}`
}
unstable_viewTransition
>
<
UserAvatar
className=
"mr-2 shrink-0"
avatarUrl=
{
creator
.
avatarUrl
}
/>
</
Link
>
<
div
className=
"w-full flex flex-col justify-center items-start"
>
<
Link
className=
"w-full block leading-tight hover:opacity-80 truncate text-gray-600 dark:text-gray-400"
to=
{
`/u/${encodeURIComponent(creator.username)}`
}
unstable_viewTransition
>
{
creator
.
nickname
||
creator
.
username
}
</
Link
>
<
div
className=
"w-auto -mt-0.5 text-xs leading-tight text-gray-400 dark:text-gray-500 select-none"
onClick=
{
handleGotoMemoDetailPage
}
>
{
displayTime
}
</
div
>
</
div
>
</
div
>
)
:
(
<
div
className=
"w-full text-sm leading-tight text-gray-400 dark:text-gray-500 select-none"
onClick=
{
handleGotoMemoDetailPage
}
>
{
displayTime
}
</
div
>
)
}
</
div
>
<
div
className=
"flex flex-row justify-end items-center select-none shrink-0 gap-2"
>
<
div
className=
"w-auto invisible group-hover:visible flex flex-row justify-between items-center gap-2"
>
{
props
.
showVisibility
&&
memo
.
visibility
!==
Visibility
.
PRIVATE
&&
(
<
Tooltip
title=
{
t
(
`memo.visibility.${convertVisibilityToString(memo.visibility).toLowerCase()}`
as
any
)
}
placement=
"top"
>
<
span
className=
"flex justify-center items-center hover:opacity-70"
>
<
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
>
)
}
{
!
readonly
&&
(
<
MemoActionMenu
className=
"-ml-1"
memo=
{
memo
}
hiddenActions=
{
props
.
showPinned
?
[]
:
[
"pin"
]
}
onEdit=
{
()
=>
setShowEditor
(
true
)
}
/>
)
}
</
div
>
</
div
>
<
MemoContent
key=
{
`${memo.name}-${memo.updateTime}`
}
memoName=
{
memo
.
name
}
...
...
web/src/components/UserStatisticsView.tsx
View file @
c3135961
This diff is collapsed.
Click to expand it.
web/src/hooks/index.ts
View file @
c3135961
...
...
@@ -4,4 +4,3 @@ export * from "./useNavigateTo";
export
*
from
"./useAsyncEffect"
;
export
*
from
"./useFilterWithUrlParams"
;
export
*
from
"./useResponsiveWidth"
;
export
*
from
"./useDateTime"
;
web/src/hooks/useDateTime.ts
deleted
100644 → 0
View file @
d2727e68
import
{
useState
}
from
"react"
;
const
useDateTime
=
(
initalState
?:
Date
)
=>
{
const
[
dateTime
,
setDateTimeInternal
]
=
useState
<
Date
|
undefined
>
(
initalState
&&
new
Date
(
initalState
));
return
{
setDateTime
:
(
dateTimeString
:
string
)
=>
setDateTimeInternal
(
new
Date
(
dateTimeString
)),
displayDateTime
:
dateTime
&&
dateTime
.
toLocaleString
(),
datePickerDateTime
:
dateTime
&&
new
Date
(
dateTime
.
getTime
()
-
dateTime
.
getTimezoneOffset
()
*
60000
).
toISOString
().
split
(
"."
)[
0
],
};
};
export
default
useDateTime
;
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