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
ded4da07
Commit
ded4da07
authored
Mar 13, 2024
by
Steven
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: use @github/relative-time-element to display time
parent
e7951491
Changes
11
Show whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
64 additions
and
158 deletions
+64
-158
package.json
web/package.json
+1
-0
pnpm-lock.yaml
web/pnpm-lock.yaml
+7
-0
BetaBadge.tsx
web/src/components/BetaBadge.tsx
+0
-22
MemoActionMenu.tsx
web/src/components/MemoActionMenu.tsx
+13
-6
EmbeddedMemo.tsx
...c/components/MemoContent/EmbeddedContent/EmbeddedMemo.tsx
+3
-2
index.tsx
web/src/components/MemoContent/index.tsx
+1
-1
MemoView.tsx
web/src/components/MemoView.tsx
+27
-40
datetime.ts
web/src/helpers/datetime.ts
+0
-84
main.tsx
web/src/main.tsx
+1
-0
Archived.tsx
web/src/pages/Archived.tsx
+3
-2
MemoDetail.tsx
web/src/pages/MemoDetail.tsx
+8
-1
No files found.
web/package.json
View file @
ded4da07
...
@@ -11,6 +11,7 @@
...
@@ -11,6 +11,7 @@
"dependencies"
:
{
"dependencies"
:
{
"@emotion/react"
:
"^11.11.4"
,
"@emotion/react"
:
"^11.11.4"
,
"@emotion/styled"
:
"^11.11.0"
,
"@emotion/styled"
:
"^11.11.0"
,
"@github/relative-time-element"
:
"^4.3.1"
,
"@matejmazur/react-katex"
:
"^3.1.3"
,
"@matejmazur/react-katex"
:
"^3.1.3"
,
"@mui/joy"
:
"5.0.0-beta.30"
,
"@mui/joy"
:
"5.0.0-beta.30"
,
"@reduxjs/toolkit"
:
"^1.9.7"
,
"@reduxjs/toolkit"
:
"^1.9.7"
,
...
...
web/pnpm-lock.yaml
View file @
ded4da07
...
@@ -14,6 +14,9 @@ dependencies:
...
@@ -14,6 +14,9 @@ dependencies:
'
@emotion/styled'
:
'
@emotion/styled'
:
specifier
:
^11.11.0
specifier
:
^11.11.0
version
:
11.11.0(@emotion/react@11.11.4)(@types/react@18.2.63)(react@18.2.0)
version
:
11.11.0(@emotion/react@11.11.4)(@types/react@18.2.63)(react@18.2.0)
'
@github/relative-time-element'
:
specifier
:
^4.3.1
version
:
4.3.1
'
@matejmazur/react-katex'
:
'
@matejmazur/react-katex'
:
specifier
:
^3.1.3
specifier
:
^3.1.3
version
:
3.1.3(katex@0.16.9)(react@18.2.0)
version
:
3.1.3(katex@0.16.9)(react@18.2.0)
...
@@ -905,6 +908,10 @@ packages:
...
@@ -905,6 +908,10 @@ packages:
resolution
:
{
integrity
:
sha512-9TANp6GPoMtYzQdt54kfAyMmz1+osLlXdg2ENroU7zzrtflTLrrC/lgrIfaSe+Wu0b89GKccT7vxXA0MoAIO+Q==
}
resolution
:
{
integrity
:
sha512-9TANp6GPoMtYzQdt54kfAyMmz1+osLlXdg2ENroU7zzrtflTLrrC/lgrIfaSe+Wu0b89GKccT7vxXA0MoAIO+Q==
}
dev
:
false
dev
:
false
/@github/relative-time-element@4.3.1
:
resolution
:
{
integrity
:
sha512-zL79nlhZVCg7x2Pf/HT5MB0mowmErE71VXpF10/3Wy8dQwkninNO1M9aOizh2wKC5LkSpDXqNYjDZwbH0/bcSg==
}
dev
:
false
/@humanwhocodes/config-array@0.11.14
:
/@humanwhocodes/config-array@0.11.14
:
resolution
:
{
integrity
:
sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==
}
resolution
:
{
integrity
:
sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==
}
engines
:
{
node
:
'
>=10.10.0'
}
engines
:
{
node
:
'
>=10.10.0'
}
...
...
web/src/components/BetaBadge.tsx
deleted
100644 → 0
View file @
e7951491
import
{
useTranslate
}
from
"@/utils/i18n"
;
interface
Props
{
className
?:
string
;
}
const
BetaBadge
:
React
.
FC
<
Props
>
=
(
props
:
Props
)
=>
{
const
{
className
}
=
props
;
const
t
=
useTranslate
();
return
(
<
span
className=
{
`mx-1 px-1 leading-5 text-xs border font-normal dark:border-zinc-600 rounded-full text-gray-500 dark:text-gray-400 ${
className ?? ""
}`
}
>
{
t
(
"common.beta"
)
}
</
span
>
);
};
export
default
BetaBadge
;
web/src/components/MemoActionMenu.tsx
View file @
ded4da07
import
{
Dropdown
,
Menu
,
MenuButton
,
MenuItem
}
from
"@mui/joy"
;
import
{
Dropdown
,
Menu
,
MenuButton
,
MenuItem
}
from
"@mui/joy"
;
import
classNames
from
"classnames"
;
import
classNames
from
"classnames"
;
import
toast
from
"react-hot-toast"
;
import
toast
from
"react-hot-toast"
;
import
{
useLocation
}
from
"react-router-dom"
;
import
Icon
from
"@/components/Icon"
;
import
Icon
from
"@/components/Icon"
;
import
useNavigateTo
from
"@/hooks/useNavigateTo"
;
import
{
useMemoStore
}
from
"@/store/v1"
;
import
{
useMemoStore
}
from
"@/store/v1"
;
import
{
RowStatus
}
from
"@/types/proto/api/v2/common"
;
import
{
RowStatus
}
from
"@/types/proto/api/v2/common"
;
import
{
Memo
}
from
"@/types/proto/api/v2/memo_service"
;
import
{
Memo
}
from
"@/types/proto/api/v2/memo_service"
;
...
@@ -14,14 +16,15 @@ interface Props {
...
@@ -14,14 +16,15 @@ interface Props {
memo
:
Memo
;
memo
:
Memo
;
className
?:
string
;
className
?:
string
;
hiddenActions
?:
(
"edit"
|
"archive"
|
"delete"
|
"share"
|
"pin"
)[];
hiddenActions
?:
(
"edit"
|
"archive"
|
"delete"
|
"share"
|
"pin"
)[];
onArchived
?:
()
=>
void
;
onDeleted
?:
()
=>
void
;
}
}
const
MemoActionMenu
=
(
props
:
Props
)
=>
{
const
MemoActionMenu
=
(
props
:
Props
)
=>
{
const
{
memo
,
hiddenActions
}
=
props
;
const
{
memo
,
hiddenActions
}
=
props
;
const
t
=
useTranslate
();
const
t
=
useTranslate
();
const
location
=
useLocation
();
const
navigateTo
=
useNavigateTo
();
const
memoStore
=
useMemoStore
();
const
memoStore
=
useMemoStore
();
const
isInMemoDetailPage
=
location
.
pathname
.
startsWith
(
`/m/
${
memo
.
name
}
`
);
const
handleTogglePinMemoBtnClick
=
async
()
=>
{
const
handleTogglePinMemoBtnClick
=
async
()
=>
{
try
{
try
{
...
@@ -66,9 +69,12 @@ const MemoActionMenu = (props: Props) => {
...
@@ -66,9 +69,12 @@ const MemoActionMenu = (props: Props) => {
}
catch
(
error
:
any
)
{
}
catch
(
error
:
any
)
{
console
.
error
(
error
);
console
.
error
(
error
);
toast
.
error
(
error
.
response
.
data
.
message
);
toast
.
error
(
error
.
response
.
data
.
message
);
return
;
}
}
if
(
props
.
onArchived
)
{
props
.
onArchived
();
toast
.
success
(
"Archived successfully"
);
if
(
isInMemoDetailPage
)
{
navigateTo
(
"/archived"
);
}
}
};
};
...
@@ -80,8 +86,9 @@ const MemoActionMenu = (props: Props) => {
...
@@ -80,8 +86,9 @@ const MemoActionMenu = (props: Props) => {
dialogName
:
"delete-memo-dialog"
,
dialogName
:
"delete-memo-dialog"
,
onConfirm
:
async
()
=>
{
onConfirm
:
async
()
=>
{
await
memoStore
.
deleteMemo
(
memo
.
id
);
await
memoStore
.
deleteMemo
(
memo
.
id
);
if
(
props
.
onDeleted
)
{
toast
.
success
(
"Deleted successfully"
);
props
.
onDeleted
();
if
(
isInMemoDetailPage
)
{
navigateTo
(
"/"
);
}
}
},
},
});
});
...
...
web/src/components/MemoContent/EmbeddedContent/EmbeddedMemo.tsx
View file @
ded4da07
...
@@ -2,7 +2,6 @@ import { useContext, useEffect } from "react";
...
@@ -2,7 +2,6 @@ import { useContext, useEffect } from "react";
import
{
Link
}
from
"react-router-dom"
;
import
{
Link
}
from
"react-router-dom"
;
import
Icon
from
"@/components/Icon"
;
import
Icon
from
"@/components/Icon"
;
import
MemoResourceListView
from
"@/components/MemoResourceListView"
;
import
MemoResourceListView
from
"@/components/MemoResourceListView"
;
import
{
getDateTimeString
}
from
"@/helpers/datetime"
;
import
useLoading
from
"@/hooks/useLoading"
;
import
useLoading
from
"@/hooks/useLoading"
;
import
{
useMemoStore
}
from
"@/store/v1"
;
import
{
useMemoStore
}
from
"@/store/v1"
;
import
MemoContent
from
".."
;
import
MemoContent
from
".."
;
...
@@ -51,7 +50,9 @@ const EmbeddedMemo = ({ resourceId, params: paramsStr }: Props) => {
...
@@ -51,7 +50,9 @@ const EmbeddedMemo = ({ resourceId, params: paramsStr }: Props) => {
return
(
return
(
<
div
className=
"relative flex flex-col justify-start items-start w-full p-4 pt-3 !my-2 bg-white dark:bg-zinc-800 rounded-lg border border-gray-200 dark:border-zinc-700 hover:shadow"
>
<
div
className=
"relative flex flex-col justify-start items-start w-full p-4 pt-3 !my-2 bg-white dark:bg-zinc-800 rounded-lg border border-gray-200 dark:border-zinc-700 hover:shadow"
>
<
div
className=
"w-full mb-1 flex flex-row justify-between items-center"
>
<
div
className=
"w-full mb-1 flex flex-row justify-between items-center"
>
<
span
className=
"text-sm text-gray-400 select-none"
>
{
getDateTimeString
(
memo
.
displayTime
)
}
</
span
>
<
div
className=
"text-sm leading-6 text-gray-400 select-none"
>
<
relative
-
time
datetime=
{
memo
.
displayTime
?.
toISOString
()
}
tense=
"past"
></
relative
-
time
>
</
div
>
<
Link
className=
"hover:opacity-80"
to=
{
`/m/${memo.name}`
}
unstable_viewTransition
>
<
Link
className=
"hover:opacity-80"
to=
{
`/m/${memo.name}`
}
unstable_viewTransition
>
<
Icon
.
ArrowUpRight
className=
"w-5 h-auto opacity-80 text-gray-400"
/>
<
Icon
.
ArrowUpRight
className=
"w-5 h-auto opacity-80 text-gray-400"
/>
</
Link
>
</
Link
>
...
...
web/src/components/MemoContent/index.tsx
View file @
ded4da07
...
@@ -74,7 +74,7 @@ const MemoContent: React.FC<Props> = (props: Props) => {
...
@@ -74,7 +74,7 @@ const MemoContent: React.FC<Props> = (props: Props) => {
<
div
<
div
ref=
{
memoContentContainerRef
}
ref=
{
memoContentContainerRef
}
className=
{
classNames
(
className=
{
classNames
(
"w-full max-w-full word-break text-base leading-
6
space-y-1 whitespace-pre-wrap"
,
"w-full max-w-full word-break text-base leading-
7
space-y-1 whitespace-pre-wrap"
,
showCompactMode
&&
"line-clamp-6"
,
showCompactMode
&&
"line-clamp-6"
,
)
}
)
}
onClick=
{
handleMemoContentClick
}
onClick=
{
handleMemoContentClick
}
...
...
web/src/components/MemoView.tsx
View file @
ded4da07
import
{
Tooltip
}
from
"@mui/joy"
;
import
{
Tooltip
}
from
"@mui/joy"
;
import
classNames
from
"classnames"
;
import
classNames
from
"classnames"
;
import
{
memo
,
useCallback
,
useEffect
,
useRef
,
useState
}
from
"react"
;
import
{
memo
,
useCallback
,
useEffect
,
useRef
,
useState
}
from
"react"
;
import
{
useTranslation
}
from
"react-i18next"
;
import
{
Link
,
useLocation
}
from
"react-router-dom"
;
import
{
Link
}
from
"react-router-dom"
;
import
{
getRelativeTimeString
,
getTimeStampByDate
}
from
"@/helpers/datetime"
;
import
useCurrentUser
from
"@/hooks/useCurrentUser"
;
import
useCurrentUser
from
"@/hooks/useCurrentUser"
;
import
useNavigateTo
from
"@/hooks/useNavigateTo"
;
import
useNavigateTo
from
"@/hooks/useNavigateTo"
;
import
{
useUserStore
,
extractUsernameFromName
}
from
"@/store/v1"
;
import
{
useUserStore
,
extractUsernameFromName
}
from
"@/store/v1"
;
...
@@ -25,6 +23,7 @@ import VisibilityIcon from "./VisibilityIcon";
...
@@ -25,6 +23,7 @@ import VisibilityIcon from "./VisibilityIcon";
interface
Props
{
interface
Props
{
memo
:
Memo
;
memo
:
Memo
;
compact
?:
boolean
;
showVisibility
?:
boolean
;
showVisibility
?:
boolean
;
showPinned
?:
boolean
;
showPinned
?:
boolean
;
className
?:
string
;
className
?:
string
;
...
@@ -33,12 +32,11 @@ interface Props {
...
@@ -33,12 +32,11 @@ interface Props {
const
MemoView
:
React
.
FC
<
Props
>
=
(
props
:
Props
)
=>
{
const
MemoView
:
React
.
FC
<
Props
>
=
(
props
:
Props
)
=>
{
const
{
memo
,
className
}
=
props
;
const
{
memo
,
className
}
=
props
;
const
t
=
useTranslate
();
const
t
=
useTranslate
();
const
location
=
useLocation
();
const
navigateTo
=
useNavigateTo
();
const
navigateTo
=
useNavigateTo
();
const
{
i18n
}
=
useTranslation
();
const
currentUser
=
useCurrentUser
();
const
currentUser
=
useCurrentUser
();
const
userStore
=
useUserStore
();
const
userStore
=
useUserStore
();
const
user
=
useCurrentUser
();
const
user
=
useCurrentUser
();
const
[
displayTime
,
setDisplayTime
]
=
useState
<
string
>
(
getRelativeTimeString
(
getTimeStampByDate
(
memo
.
displayTime
)));
const
[
creator
,
setCreator
]
=
useState
(
userStore
.
getUserByUsername
(
extractUsernameFromName
(
memo
.
creator
)));
const
[
creator
,
setCreator
]
=
useState
(
userStore
.
getUserByUsername
(
extractUsernameFromName
(
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
);
...
@@ -46,6 +44,7 @@ const MemoView: React.FC<Props> = (props: Props) => {
...
@@ -46,6 +44,7 @@ const MemoView: React.FC<Props> = (props: Props) => {
(
relation
)
=>
relation
.
type
===
MemoRelation_Type
.
COMMENT
&&
relation
.
relatedMemoId
===
memo
.
id
,
(
relation
)
=>
relation
.
type
===
MemoRelation_Type
.
COMMENT
&&
relation
.
relatedMemoId
===
memo
.
id
,
).
length
;
).
length
;
const
readonly
=
memo
.
creator
!==
user
?.
name
;
const
readonly
=
memo
.
creator
!==
user
?.
name
;
const
isInMemoDetailPage
=
location
.
pathname
.
startsWith
(
`/m/
${
memo
.
name
}
`
);
// Initial related data: creator.
// Initial related data: creator.
useEffect
(()
=>
{
useEffect
(()
=>
{
...
@@ -55,20 +54,6 @@ const MemoView: React.FC<Props> = (props: Props) => {
...
@@ -55,20 +54,6 @@ const MemoView: React.FC<Props> = (props: Props) => {
})();
})();
},
[]);
},
[]);
// Update display time string.
useEffect
(()
=>
{
let
intervalFlag
:
any
=
-
1
;
if
(
Date
.
now
()
-
getTimeStampByDate
(
memo
.
displayTime
)
<
1000
*
60
*
60
*
24
)
{
intervalFlag
=
setInterval
(()
=>
{
setDisplayTime
(
getRelativeTimeString
(
getTimeStampByDate
(
memo
.
displayTime
)));
},
1000
*
1
);
}
return
()
=>
{
clearInterval
(
intervalFlag
);
};
},
[
i18n
.
language
]);
const
handleGotoMemoDetailPage
=
(
event
:
React
.
MouseEvent
<
HTMLDivElement
>
)
=>
{
const
handleGotoMemoDetailPage
=
(
event
:
React
.
MouseEvent
<
HTMLDivElement
>
)
=>
{
if
(
event
.
altKey
)
{
if
(
event
.
altKey
)
{
showChangeMemoCreatedTsDialog
(
memo
.
id
);
showChangeMemoCreatedTsDialog
(
memo
.
id
);
...
@@ -124,8 +109,8 @@ const MemoView: React.FC<Props> = (props: Props) => {
...
@@ -124,8 +109,8 @@ 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-
1
"
>
<
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-
1
"
>
<
div
className=
"w-auto invisible group-hover:visible flex flex-row justify-between items-center gap-
2
"
>
{
props
.
showVisibility
&&
memo
.
visibility
!==
Visibility
.
PRIVATE
&&
(
{
props
.
showVisibility
&&
memo
.
visibility
!==
Visibility
.
PRIVATE
&&
(
<
Tooltip
title=
{
t
(
`memo.visibility.${convertVisibilityToString(memo.visibility).toLowerCase()}`
as
any
)
}
placement=
"top"
>
<
Tooltip
title=
{
t
(
`memo.visibility.${convertVisibilityToString(memo.visibility).toLowerCase()}`
as
any
)
}
placement=
"top"
>
<
span
className=
"flex justify-center items-center hover:opacity-70"
>
<
span
className=
"flex justify-center items-center hover:opacity-70"
>
...
@@ -133,8 +118,9 @@ const MemoView: React.FC<Props> = (props: Props) => {
...
@@ -133,8 +118,9 @@ const MemoView: React.FC<Props> = (props: Props) => {
</
span
>
</
span
>
</
Tooltip
>
</
Tooltip
>
)
}
)
}
{
currentUser
&&
<
ReactionSelector
className=
"border-none"
memo=
{
memo
}
/>
}
{
currentUser
&&
<
ReactionSelector
className=
"border-none
w-auto h-auto
"
memo=
{
memo
}
/>
}
</
div
>
</
div
>
{
!
isInMemoDetailPage
&&
(
<
Link
<
Link
className=
{
classNames
(
className=
{
classNames
(
"flex flex-row justify-start items-center hover:opacity-70"
,
"flex flex-row justify-start items-center hover:opacity-70"
,
...
@@ -146,12 +132,13 @@ const MemoView: React.FC<Props> = (props: Props) => {
...
@@ -146,12 +132,13 @@ const MemoView: React.FC<Props> = (props: Props) => {
<
Icon
.
MessageCircleMore
className=
"w-4 h-4 mx-auto text-gray-500 dark:text-gray-400"
/>
<
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
>
}
{
commentAmount
>
0
&&
<
span
className=
"text-xs text-gray-500 dark:text-gray-400"
>
{
commentAmount
}
</
span
>
}
</
Link
>
</
Link
>
)
}
{
props
.
showPinned
&&
memo
.
pinned
&&
(
{
props
.
showPinned
&&
memo
.
pinned
&&
(
<
Tooltip
title=
{
"Pinned"
}
placement=
"top"
>
<
Tooltip
title=
{
"Pinned"
}
placement=
"top"
>
<
Icon
.
Bookmark
className=
"
ml-1
w-4 h-auto text-amber-500"
/>
<
Icon
.
Bookmark
className=
"w-4 h-auto text-amber-500"
/>
</
Tooltip
>
</
Tooltip
>
)
}
)
}
{
!
readonly
&&
<
MemoActionMenu
memo=
{
memo
}
hiddenActions=
{
props
.
showPinned
?
[]
:
[
"pin"
]
}
/>
}
{
!
readonly
&&
<
MemoActionMenu
className=
"-ml-1"
memo=
{
memo
}
hiddenActions=
{
props
.
showPinned
?
[]
:
[
"pin"
]
}
/>
}
</
div
>
</
div
>
</
div
>
</
div
>
<
MemoContent
<
MemoContent
...
@@ -160,13 +147,13 @@ const MemoView: React.FC<Props> = (props: Props) => {
...
@@ -160,13 +147,13 @@ const MemoView: React.FC<Props> = (props: Props) => {
content=
{
memo
.
content
}
content=
{
memo
.
content
}
readonly=
{
readonly
}
readonly=
{
readonly
}
onClick=
{
handleMemoContentClick
}
onClick=
{
handleMemoContentClick
}
compact=
{
true
}
compact=
{
props
.
compact
??
true
}
/>
/>
<
MemoResourceListView
resources=
{
memo
.
resources
}
/>
<
MemoResourceListView
resources=
{
memo
.
resources
}
/>
<
div
className=
"w-full flex flex-row justify-between items-center
mt-1
"
>
<
div
className=
"w-full flex flex-row justify-between items-center"
>
<
span
className=
"text-sm leading-6 text-gray-400 select-none"
onClick=
{
handleGotoMemoDetailPage
}
>
<
div
className=
"text-sm leading-6 text-gray-400 select-none"
>
{
displayTime
}
<
relative
-
time
datetime=
{
memo
.
displayTime
?.
toISOString
()
}
tense=
"past"
onClick=
{
handleGotoMemoDetailPage
}
></
relative
-
time
>
</
span
>
</
div
>
</
div
>
</
div
>
<
MemoRelationListView
memo=
{
memo
}
relations=
{
referencedMemos
}
/>
<
MemoRelationListView
memo=
{
memo
}
relations=
{
referencedMemos
}
/>
<
MemoReactionistView
memo=
{
memo
}
reactions=
{
memo
.
reactions
}
/>
<
MemoReactionistView
memo=
{
memo
}
reactions=
{
memo
.
reactions
}
/>
...
...
web/src/helpers/datetime.ts
View file @
ded4da07
...
@@ -4,13 +4,6 @@ export function getTimeStampByDate(t: Date | number | string | any): number {
...
@@ -4,13 +4,6 @@ export function getTimeStampByDate(t: Date | number | string | any): number {
return
new
Date
(
t
).
getTime
();
return
new
Date
(
t
).
getTime
();
}
}
export
function
getDateStampByDate
(
t
?:
Date
|
number
|
string
):
number
{
const
tsFromDate
=
getTimeStampByDate
(
t
?
t
:
Date
.
now
());
const
d
=
new
Date
(
tsFromDate
);
return
new
Date
(
d
.
getFullYear
(),
d
.
getMonth
(),
d
.
getDate
()).
getTime
();
}
/**
/**
* Get a time string to provided time.
* Get a time string to provided time.
*
*
...
@@ -57,56 +50,6 @@ export function getDateTimeString(t?: Date | number | string | any, locale = i18
...
@@ -57,56 +50,6 @@ export function getDateTimeString(t?: Date | number | string | any, locale = i18
}
}
}
}
/**
* Get a localized relative time string to provided time.
*
* Possible outputs for "long" format and "en" locale:
* - "x seconds ago"
* - "x minutes ago"
* - "x hours ago"
* - "yesterday"
* - "x days ago"
* - "x weeks ago"
* - "x months ago"
* - "last year"
* - "x years ago"
*/
export
const
getRelativeTimeString
=
(
time
:
number
,
locale
=
i18n
.
language
,
formatStyle
:
"long"
|
"short"
|
"narrow"
=
"long"
):
string
=>
{
const
pastTimeMillis
=
Date
.
now
()
-
time
;
const
secMillis
=
1000
;
const
minMillis
=
secMillis
*
60
;
const
hourMillis
=
minMillis
*
60
;
const
dayMillis
=
hourMillis
*
24
;
// Show full date if more than 1 day ago.
if
(
pastTimeMillis
>=
dayMillis
)
{
return
getDateTimeString
(
time
,
locale
);
}
// numeric: "auto" provides "yesterday" for 1 day ago, "always" provides "1 day ago"
const
formatOpts
=
{
style
:
formatStyle
,
numeric
:
"auto"
}
as
Intl
.
RelativeTimeFormatOptions
;
const
relTime
=
new
Intl
.
RelativeTimeFormat
(
locale
,
formatOpts
);
if
(
pastTimeMillis
<
minMillis
)
{
return
relTime
.
format
(
-
Math
.
round
(
pastTimeMillis
/
secMillis
),
"second"
);
}
if
(
pastTimeMillis
<
hourMillis
)
{
return
relTime
.
format
(
-
Math
.
round
(
pastTimeMillis
/
minMillis
),
"minute"
);
}
if
(
pastTimeMillis
<
dayMillis
)
{
return
relTime
.
format
(
-
Math
.
round
(
pastTimeMillis
/
hourMillis
),
"hour"
);
}
if
(
pastTimeMillis
<
dayMillis
*
7
)
{
return
relTime
.
format
(
-
Math
.
round
(
pastTimeMillis
/
dayMillis
),
"day"
);
}
if
(
pastTimeMillis
<
dayMillis
*
30
)
{
return
relTime
.
format
(
-
Math
.
round
(
pastTimeMillis
/
(
dayMillis
*
7
)),
"week"
);
}
if
(
pastTimeMillis
<
dayMillis
*
365
)
{
return
relTime
.
format
(
-
Math
.
round
(
pastTimeMillis
/
(
dayMillis
*
30
)),
"month"
);
}
return
relTime
.
format
(
-
Math
.
round
(
pastTimeMillis
/
(
dayMillis
*
365
)),
"year"
);
};
/**
/**
* This returns the normalized date string of the provided date.
* This returns the normalized date string of the provided date.
* Format is always `YYYY-MM-DDT00:00`.
* Format is always `YYYY-MM-DDT00:00`.
...
@@ -143,33 +86,6 @@ export function getNormalizedDateString(t?: Date | number | string): string {
...
@@ -143,33 +86,6 @@ export function getNormalizedDateString(t?: Date | number | string): string {
return
`
${
yyyy
}
-
${
MM
}
-
${
dd
}
`
;
return
`
${
yyyy
}
-
${
MM
}
-
${
dd
}
`
;
}
}
/**
* This returns the Unix timestamp (the number of **seconds** since the Unix Epoch) of the provided date.
*
* If no date is provided, the current date is used.
* ```
* getUnixTime("2019-01-25 00:00") // 1548381600
* ```
* This value is floored to the nearest second, and does not include a milliseconds component.
*/
export
function
getUnixTime
(
t
?:
Date
|
number
|
string
):
number
{
const
date
=
new
Date
(
t
?
t
:
Date
.
now
());
return
Math
.
floor
(
date
.
getTime
()
/
1000
);
}
/**
* Checks if the provided date or timestamp is in the future.
*
* If no date is provided, the current date is used.
*
* @param t - Date or timestamp to check.
* @returns `true` if the date is in the future, `false` otherwise.
*/
export
function
isFutureDate
(
t
?:
Date
|
number
|
string
):
boolean
{
const
timestamp
=
getTimeStampByDate
(
t
?
t
:
Date
.
now
());
return
timestamp
>
Date
.
now
();
}
/**
/**
* Calculates a new Date object by adjusting the provided date, timestamp, or date string
* Calculates a new Date object by adjusting the provided date, timestamp, or date string
* based on the current timezone offset.
* based on the current timezone offset.
...
...
web/src/main.tsx
View file @
ded4da07
import
"@github/relative-time-element"
;
import
{
CssVarsProvider
}
from
"@mui/joy"
;
import
{
CssVarsProvider
}
from
"@mui/joy"
;
import
{
createRoot
}
from
"react-dom/client"
;
import
{
createRoot
}
from
"react-dom/client"
;
import
{
Toaster
}
from
"react-hot-toast"
;
import
{
Toaster
}
from
"react-hot-toast"
;
...
...
web/src/pages/Archived.tsx
View file @
ded4da07
...
@@ -11,7 +11,6 @@ import MobileHeader from "@/components/MobileHeader";
...
@@ -11,7 +11,6 @@ import MobileHeader from "@/components/MobileHeader";
import
SearchBar
from
"@/components/SearchBar"
;
import
SearchBar
from
"@/components/SearchBar"
;
import
{
DEFAULT_LIST_MEMOS_PAGE_SIZE
}
from
"@/helpers/consts"
;
import
{
DEFAULT_LIST_MEMOS_PAGE_SIZE
}
from
"@/helpers/consts"
;
import
{
getTimeStampByDate
}
from
"@/helpers/datetime"
;
import
{
getTimeStampByDate
}
from
"@/helpers/datetime"
;
import
{
getDateTimeString
}
from
"@/helpers/datetime"
;
import
useCurrentUser
from
"@/hooks/useCurrentUser"
;
import
useCurrentUser
from
"@/hooks/useCurrentUser"
;
import
useFilterWithUrlParams
from
"@/hooks/useFilterWithUrlParams"
;
import
useFilterWithUrlParams
from
"@/hooks/useFilterWithUrlParams"
;
import
{
useMemoList
,
useMemoStore
}
from
"@/store/v1"
;
import
{
useMemoList
,
useMemoStore
}
from
"@/store/v1"
;
...
@@ -105,7 +104,9 @@ const Archived = () => {
...
@@ -105,7 +104,9 @@ const Archived = () => {
>
>
<
div
className=
"w-full mb-1 flex flex-row justify-between items-center"
>
<
div
className=
"w-full mb-1 flex flex-row justify-between items-center"
>
<
div
className=
"w-full max-w-[calc(100%-20px)] flex flex-row justify-start items-center mr-1"
>
<
div
className=
"w-full max-w-[calc(100%-20px)] flex flex-row justify-start items-center mr-1"
>
<
span
className=
"text-sm text-gray-400 select-none"
>
{
getDateTimeString
(
memo
.
displayTime
)
}
</
span
>
<
div
className=
"text-sm leading-6 text-gray-400 select-none"
>
<
relative
-
time
datetime=
{
memo
.
displayTime
?.
toISOString
()
}
tense=
"past"
></
relative
-
time
>
</
div
>
</
div
>
</
div
>
<
div
className=
"flex flex-row justify-end items-center gap-x-2"
>
<
div
className=
"flex flex-row justify-end items-center gap-x-2"
>
<
Tooltip
title=
{
t
(
"common.restore"
)
}
placement=
"top"
>
<
Tooltip
title=
{
t
(
"common.restore"
)
}
placement=
"top"
>
...
...
web/src/pages/MemoDetail.tsx
View file @
ded4da07
...
@@ -81,7 +81,14 @@ const MemoDetail = () => {
...
@@ -81,7 +81,14 @@ const MemoDetail = () => {
</
Link
>
</
Link
>
</
div
>
</
div
>
)
}
)
}
<
MemoView
key=
{
`${memo.id}-${memo.displayTime}`
}
className=
"shadow hover:shadow-xl transition-all"
memo=
{
memo
}
/>
<
MemoView
key=
{
`${memo.id}-${memo.displayTime}`
}
className=
"shadow hover:shadow-xl transition-all"
memo=
{
memo
}
compact=
{
false
}
showVisibility
showPinned
/>
<
div
className=
"pt-8 pb-16 w-full"
>
<
div
className=
"pt-8 pb-16 w-full"
>
<
h2
id=
"comments"
className=
"sr-only"
>
<
h2
id=
"comments"
className=
"sr-only"
>
Comments
Comments
...
...
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