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
57f51d1c
Commit
57f51d1c
authored
Aug 25, 2022
by
Steven
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: allow updating memo `createdTs`
parent
9f3f7307
Changes
9
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
172 additions
and
21 deletions
+172
-21
memo.go
api/memo.go
+3
-4
memo.go
store/memo.go
+6
-7
ChangeMemoCreatedTsDialog.tsx
web/src/components/ChangeMemoCreatedTsDialog.tsx
+99
-0
MemoCardDialog.tsx
web/src/components/MemoCardDialog.tsx
+12
-2
MemoList.tsx
web/src/components/MemoList.tsx
+1
-1
utils.ts
web/src/helpers/utils.ts
+0
-4
change-memo-created-ts-dialog.less
web/src/less/change-memo-created-ts-dialog.less
+47
-0
change-password-dialog.less
web/src/less/change-password-dialog.less
+1
-1
memo.d.ts
web/src/types/modules/memo.d.ts
+3
-2
No files found.
api/memo.go
View file @
57f51d1c
...
...
@@ -42,18 +42,17 @@ type Memo struct {
type
MemoCreate
struct
{
// Standard fields
CreatorID
int
// Used to import memos with a clearly created ts.
CreatedTs
*
int64
`json:"createdTs"`
// Domain specific fields
Visibility
Visibility
Content
string
`json:"content"`
Visibility
Visibility
`json:"visibility"`
Content
string
`json:"content"`
}
type
MemoPatch
struct
{
ID
int
// Standard fields
CreatedTs
*
int64
`json:"createdTs"`
RowStatus
*
RowStatus
`json:"rowStatus"`
// Domain specific fields
...
...
store/memo.go
View file @
57f51d1c
...
...
@@ -202,12 +202,8 @@ func (s *Store) DeleteMemo(ctx context.Context, delete *api.MemoDelete) error {
func
createMemoRaw
(
ctx
context
.
Context
,
tx
*
sql
.
Tx
,
create
*
api
.
MemoCreate
)
(
*
memoRaw
,
error
)
{
set
:=
[]
string
{
"creator_id"
,
"content"
,
"visibility"
}
placeholder
:=
[]
string
{
"?"
,
"?"
,
"?"
}
args
:=
[]
interface
{}{
create
.
CreatorID
,
create
.
Content
,
create
.
Visibility
}
if
v
:=
create
.
CreatedTs
;
v
!=
nil
{
set
,
placeholder
,
args
=
append
(
set
,
"created_ts"
),
append
(
placeholder
,
"?"
),
append
(
args
,
*
v
)
}
placeholder
:=
[]
string
{
"?"
,
"?"
,
"?"
}
query
:=
`
INSERT INTO memo (
...
...
@@ -235,12 +231,15 @@ func createMemoRaw(ctx context.Context, tx *sql.Tx, create *api.MemoCreate) (*me
func
patchMemoRaw
(
ctx
context
.
Context
,
tx
*
sql
.
Tx
,
patch
*
api
.
MemoPatch
)
(
*
memoRaw
,
error
)
{
set
,
args
:=
[]
string
{},
[]
interface
{}{}
if
v
:=
patch
.
C
ontent
;
v
!=
nil
{
set
,
args
=
append
(
set
,
"c
ontent
= ?"
),
append
(
args
,
*
v
)
if
v
:=
patch
.
C
reatedTs
;
v
!=
nil
{
set
,
args
=
append
(
set
,
"c
reated_ts
= ?"
),
append
(
args
,
*
v
)
}
if
v
:=
patch
.
RowStatus
;
v
!=
nil
{
set
,
args
=
append
(
set
,
"row_status = ?"
),
append
(
args
,
*
v
)
}
if
v
:=
patch
.
Content
;
v
!=
nil
{
set
,
args
=
append
(
set
,
"content = ?"
),
append
(
args
,
*
v
)
}
if
v
:=
patch
.
Visibility
;
v
!=
nil
{
set
,
args
=
append
(
set
,
"visibility = ?"
),
append
(
args
,
*
v
)
}
...
...
web/src/components/ChangeMemoCreatedTsDialog.tsx
0 → 100644
View file @
57f51d1c
import
{
useEffect
,
useState
}
from
"react"
;
import
dayjs
from
"dayjs"
;
import
useI18n
from
"../hooks/useI18n"
;
import
{
memoService
}
from
"../services"
;
import
Icon
from
"./Icon"
;
import
{
generateDialog
}
from
"./Dialog"
;
import
toastHelper
from
"./Toast"
;
import
"../less/change-memo-created-ts-dialog.less"
;
interface
Props
extends
DialogProps
{
memoId
:
MemoId
;
}
const
ChangeMemoCreatedTsDialog
:
React
.
FC
<
Props
>
=
(
props
:
Props
)
=>
{
const
{
t
}
=
useI18n
();
const
{
destroy
,
memoId
}
=
props
;
const
[
createdAt
,
setCreatedAt
]
=
useState
(
""
);
const
maxDatetimeValue
=
dayjs
().
format
(
"YYYY-MM-DDTHH:mm"
);
useEffect
(()
=>
{
const
memo
=
memoService
.
getMemoById
(
memoId
);
if
(
memo
)
{
const
datetime
=
dayjs
(
memo
.
createdTs
).
format
(
"YYYY-MM-DDTHH:mm"
);
setCreatedAt
(
datetime
);
}
else
{
toastHelper
.
error
(
"Memo not found."
);
destroy
();
}
},
[]);
const
handleCloseBtnClick
=
()
=>
{
destroy
();
};
const
handleDatetimeInputChange
=
(
e
:
React
.
ChangeEvent
<
HTMLInputElement
>
)
=>
{
const
datetime
=
e
.
target
.
value
as
string
;
setCreatedAt
(
datetime
);
};
const
handleSaveBtnClick
=
async
()
=>
{
const
nowTs
=
dayjs
().
unix
();
const
createdTs
=
dayjs
(
createdAt
).
unix
();
if
(
createdTs
>
nowTs
)
{
toastHelper
.
error
(
"Invalid created datetime."
);
return
;
}
try
{
await
memoService
.
patchMemo
({
id
:
memoId
,
createdTs
,
});
toastHelper
.
info
(
"Memo created datetime changed."
);
handleCloseBtnClick
();
}
catch
(
error
:
any
)
{
console
.
error
(
error
);
toastHelper
.
error
(
error
.
response
.
data
.
message
);
}
};
return
(
<>
<
div
className=
"dialog-header-container"
>
<
p
className=
"title-text"
>
Change memo created time
</
p
>
<
button
className=
"btn close-btn"
onClick=
{
handleCloseBtnClick
}
>
<
Icon
.
X
/>
</
button
>
</
div
>
<
div
className=
"dialog-content-container"
>
<
label
className=
"form-label input-form-label"
>
<
input
type=
"datetime-local"
value=
{
createdAt
}
max=
{
maxDatetimeValue
}
onChange=
{
handleDatetimeInputChange
}
/>
</
label
>
<
div
className=
"btns-container"
>
<
span
className=
"btn cancel-btn"
onClick=
{
handleCloseBtnClick
}
>
{
t
(
"common.cancel"
)
}
</
span
>
<
span
className=
"btn confirm-btn"
onClick=
{
handleSaveBtnClick
}
>
{
t
(
"common.save"
)
}
</
span
>
</
div
>
</
div
>
</>
);
};
function
showChangeMemoCreatedTsDialog
(
memoId
:
MemoId
)
{
generateDialog
(
{
className
:
"change-memo-created-ts-dialog"
,
},
ChangeMemoCreatedTsDialog
,
{
memoId
,
}
);
}
export
default
showChangeMemoCreatedTsDialog
;
web/src/components/MemoCardDialog.tsx
View file @
57f51d1c
import
{
useState
,
useEffect
,
useCallback
}
from
"react"
;
import
{
editorStateService
,
memoService
,
userService
}
from
"../services"
;
import
{
useAppSelector
}
from
"../store"
;
import
{
IMAGE_URL_REG
,
MEMO_LINK_REG
,
UNKNOWN_ID
,
VISIBILITY_SELECTOR_ITEMS
}
from
"../helpers/consts"
;
import
*
as
utils
from
"../helpers/utils"
;
import
{
formatMemoContent
,
parseHtmlToRawText
}
from
"../helpers/marked"
;
...
...
@@ -9,6 +10,7 @@ import { generateDialog } from "./Dialog";
import
Image
from
"./Image"
;
import
Icon
from
"./Icon"
;
import
Selector
from
"./common/Selector"
;
import
showChangeMemoCreatedTsDialog
from
"./ChangeMemoCreatedTsDialog"
;
import
"../less/memo-card-dialog.less"
;
interface
LinkedMemo
extends
Memo
{
...
...
@@ -21,6 +23,7 @@ interface Props extends DialogProps {
}
const
MemoCardDialog
:
React
.
FC
<
Props
>
=
(
props
:
Props
)
=>
{
const
memos
=
useAppSelector
((
state
)
=>
state
.
memo
.
memos
);
const
[
memo
,
setMemo
]
=
useState
<
Memo
>
({
...
props
.
memo
,
});
...
...
@@ -69,7 +72,12 @@ const MemoCardDialog: React.FC<Props> = (props: Props) => {
};
fetchLinkedMemos
();
},
[
memo
.
id
]);
setMemo
(
memoService
.
getMemoById
(
memo
.
id
)
as
Memo
);
},
[
memos
,
memo
.
id
]);
const
handleMemoCreatedAtClick
=
()
=>
{
showChangeMemoCreatedTsDialog
(
memo
.
id
);
};
const
handleMemoContentClick
=
useCallback
(
async
(
e
:
React
.
MouseEvent
)
=>
{
const
targetEl
=
e
.
target
as
HTMLElement
;
...
...
@@ -136,7 +144,9 @@ const MemoCardDialog: React.FC<Props> = (props: Props) => {
</
Only
>
<
div
className=
"memo-card-container"
>
<
div
className=
"header-container"
>
<
p
className=
"time-text"
>
{
utils
.
getDateTimeString
(
memo
.
createdTs
)
}
</
p
>
<
p
className=
"time-text"
onClick=
{
handleMemoCreatedAtClick
}
>
{
utils
.
getDateTimeString
(
memo
.
createdTs
)
}
</
p
>
<
div
className=
"btns-container"
>
<
Only
when=
{
!
userService
.
isVisitorMode
()
}
>
<>
...
...
web/src/components/MemoList.tsx
View file @
57f51d1c
...
...
@@ -97,7 +97,7 @@ const MemoList: React.FC<Props> = () => {
return (
<
div
className=
{
`memo-list-container ${isFetching ? "" : "completed"}`
}
ref=
{
wrapperElement
}
>
{
sortedMemos
.
map
((
memo
)
=>
(
<
Memo
key=
{
`${memo.id}-${memo.updatedTs}`
}
memo=
{
memo
}
/>
<
Memo
key=
{
`${memo.id}-${memo.
createdTs}-${memo.
updatedTs}`
}
memo=
{
memo
}
/>
))
}
<
div
className=
"status-text-container"
>
<
p
className=
"status-text"
>
...
...
web/src/helpers/utils.ts
View file @
57f51d1c
...
...
@@ -44,10 +44,6 @@ export function getDateString(t: Date | number | string): string {
return
`
${
year
}
/
${
month
}
/
${
date
}
`
;
}
export
function
getDataStringWithTs
(
ts
:
number
):
string
{
return
getDateTimeString
(
ts
*
1000
);
}
export
function
getTimeString
(
t
:
Date
|
number
|
string
):
string
{
const
d
=
new
Date
(
getTimeStampByDate
(
t
));
...
...
web/src/less/change-memo-created-ts-dialog.less
0 → 100644
View file @
57f51d1c
@import "./mixin.less";
.change-memo-created-ts-dialog {
> .dialog-container {
@apply w-72;
> .dialog-content-container {
.flex(column, flex-start, flex-start);
> .tip-text {
@apply bg-gray-400 text-xs p-2 rounded-lg;
}
> .form-label {
@apply flex flex-col justify-start items-start relative w-full leading-relaxed;
&.input-form-label {
@apply py-3 pb-1;
> input {
@apply w-full p-2 text-sm leading-6 rounded border border-gray-400 bg-transparent;
}
}
}
> .btns-container {
@apply flex flex-row justify-end items-center mt-2 w-full;
> .btn {
@apply text-sm px-4 py-2 rounded ml-2 bg-gray-400;
&:hover {
@apply opacity-80;
}
&.confirm-btn {
@apply bg-green-600 text-white shadow-inner;
}
&.cancel-btn {
background-color: unset;
}
}
}
}
}
}
web/src/less/change-password-dialog.less
View file @
57f51d1c
...
...
@@ -29,7 +29,7 @@
@apply mt-2 w-full;
> .btn {
@apply text-sm px-4 py-2 rounded m
r
-2 bg-gray-400;
@apply text-sm px-4 py-2 rounded m
l
-2 bg-gray-400;
&:hover {
@apply opacity-80;
...
...
web/src/types/modules/memo.d.ts
View file @
57f51d1c
...
...
@@ -17,13 +17,14 @@ interface Memo {
interface
MemoCreate
{
content
:
string
;
createdTs
?:
TimeStamp
;
visibility
?:
Visibility
;
}
interface
MemoPatch
{
id
:
MemoId
;
c
ontent
?:
string
;
c
reatedTs
?:
TimeStamp
;
rowStatus
?:
RowStatus
;
content
?:
string
;
visibility
?:
Visibility
;
}
...
...
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