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
b68d6e26
Unverified
Commit
b68d6e26
authored
Oct 21, 2022
by
boojack
Committed by
GitHub
Oct 21, 2022
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: update memo sort option setting (#326)
feat: add memo display ts
parent
0b34b142
Changes
12
Hide whitespace changes
Inline
Side-by-side
Showing
12 changed files
with
58 additions
and
39 deletions
+58
-39
memo.go
api/memo.go
+3
-1
user_setting.go
api/user_setting.go
+3
-3
memo.go
server/memo.go
+12
-1
memo.go
store/memo.go
+24
-0
Memo.tsx
web/src/components/Memo.tsx
+4
-9
MemoEditor.tsx
web/src/components/MemoEditor.tsx
+0
-1
MemoList.tsx
web/src/components/MemoList.tsx
+6
-7
locationService.ts
web/src/services/locationService.ts
+1
-6
memoService.ts
web/src/services/memoService.ts
+1
-0
location.ts
web/src/store/modules/location.ts
+1
-9
memo.ts
web/src/store/modules/memo.ts
+2
-2
memo.d.ts
web/src/types/modules/memo.d.ts
+1
-0
No files found.
api/memo.go
View file @
b68d6e26
...
...
@@ -37,6 +37,7 @@ type Memo struct {
Content
string
`json:"content"`
Visibility
Visibility
`json:"visibility"`
Pinned
bool
`json:"pinned"`
DisplayTs
int64
`json:"displayTs"`
// Related fields
Creator
*
User
`json:"creator"`
...
...
@@ -59,7 +60,8 @@ type MemoPatch struct {
ID
int
// Standard fields
CreatedTs
*
int64
`json:"createdTs"`
CreatedTs
*
int64
`json:"createdTs"`
UpdatedTs
*
int64
RowStatus
*
RowStatus
`json:"rowStatus"`
// Domain specific fields
...
...
api/user_setting.go
View file @
b68d6e26
...
...
@@ -16,7 +16,7 @@ const (
UserSettingEditorFontStyleKey
UserSettingKey
=
"editorFontStyle"
// UserSettingEditorFontStyleKey is the key type for mobile editor style.
UserSettingMobileEditorStyleKey
UserSettingKey
=
"mobileEditorStyle"
// UserSettingMemoSortOptionKey is the key type for
sort time
option.
// UserSettingMemoSortOptionKey is the key type for
memo sort
option.
UserSettingMemoSortOptionKey
UserSettingKey
=
"memoSortOption"
)
...
...
@@ -42,7 +42,7 @@ var (
UserSettingMemoVisibilityValue
=
[]
Visibility
{
Privite
,
Protected
,
Public
}
UserSettingEditorFontStyleValue
=
[]
string
{
"normal"
,
"mono"
}
UserSettingMobileEditorStyleValue
=
[]
string
{
"normal"
,
"float"
}
UserSetting
SortTime
OptionKeyValue
=
[]
string
{
"created_ts"
,
"updated_ts"
}
UserSetting
MemoSort
OptionKeyValue
=
[]
string
{
"created_ts"
,
"updated_ts"
}
)
type
UserSetting
struct
{
...
...
@@ -135,7 +135,7 @@ func (upsert UserSettingUpsert) Validate() error {
}
invalid
:=
true
for
_
,
value
:=
range
UserSetting
SortTime
OptionKeyValue
{
for
_
,
value
:=
range
UserSetting
MemoSort
OptionKeyValue
{
if
memoSortOption
==
value
{
invalid
=
false
break
...
...
server/memo.go
View file @
b68d6e26
...
...
@@ -4,6 +4,7 @@ import (
"encoding/json"
"fmt"
"net/http"
"sort"
"strconv"
"strings"
"time"
...
...
@@ -101,8 +102,10 @@ func (s *Server) registerMemoRoutes(g *echo.Group) {
return
echo
.
NewHTTPError
(
http
.
StatusInternalServerError
,
"Failed to find memo"
)
.
SetInternal
(
err
)
}
currentTs
:=
time
.
Now
()
.
Unix
()
memoPatch
:=
&
api
.
MemoPatch
{
ID
:
memoID
,
ID
:
memoID
,
UpdatedTs
:
&
currentTs
,
}
if
err
:=
json
.
NewDecoder
(
c
.
Request
()
.
Body
)
.
Decode
(
memoPatch
);
err
!=
nil
{
return
echo
.
NewHTTPError
(
http
.
StatusBadRequest
,
"Malformatted patch memo request"
)
.
SetInternal
(
err
)
...
...
@@ -175,6 +178,10 @@ func (s *Server) registerMemoRoutes(g *echo.Group) {
return
echo
.
NewHTTPError
(
http
.
StatusInternalServerError
,
"Failed to fetch memo list"
)
.
SetInternal
(
err
)
}
sort
.
Slice
(
list
,
func
(
i
,
j
int
)
bool
{
return
list
[
i
]
.
DisplayTs
>
list
[
j
]
.
DisplayTs
})
c
.
Response
()
.
Header
()
.
Set
(
echo
.
HeaderContentType
,
echo
.
MIMEApplicationJSONCharsetUTF8
)
if
err
:=
json
.
NewEncoder
(
c
.
Response
()
.
Writer
)
.
Encode
(
composeResponse
(
list
));
err
!=
nil
{
return
echo
.
NewHTTPError
(
http
.
StatusInternalServerError
,
"Failed to encode memo list response"
)
.
SetInternal
(
err
)
...
...
@@ -227,6 +234,10 @@ func (s *Server) registerMemoRoutes(g *echo.Group) {
return
echo
.
NewHTTPError
(
http
.
StatusInternalServerError
,
"Failed to fetch all memo list"
)
.
SetInternal
(
err
)
}
sort
.
Slice
(
list
,
func
(
i
,
j
int
)
bool
{
return
list
[
i
]
.
DisplayTs
>
list
[
j
]
.
DisplayTs
})
c
.
Response
()
.
Header
()
.
Set
(
echo
.
HeaderContentType
,
echo
.
MIMEApplicationJSONCharsetUTF8
)
if
err
:=
json
.
NewEncoder
(
c
.
Response
()
.
Writer
)
.
Encode
(
composeResponse
(
list
));
err
!=
nil
{
return
echo
.
NewHTTPError
(
http
.
StatusInternalServerError
,
"Failed to encode all memo list response"
)
.
SetInternal
(
err
)
...
...
store/memo.go
View file @
b68d6e26
...
...
@@ -3,6 +3,7 @@ package store
import
(
"context"
"database/sql"
"encoding/json"
"fmt"
"strings"
...
...
@@ -41,6 +42,7 @@ func (raw *memoRaw) toMemo() *api.Memo {
// Domain specific fields
Content
:
raw
.
Content
,
Visibility
:
raw
.
Visibility
,
DisplayTs
:
raw
.
CreatedTs
,
}
}
...
...
@@ -62,6 +64,25 @@ func (s *Store) ComposeMemo(ctx context.Context, memo *api.Memo) (*api.Memo, err
return
nil
,
err
}
memoSortOptionKey
:=
api
.
UserSettingMemoSortOptionKey
memoSortOption
,
err
:=
s
.
FindUserSetting
(
ctx
,
&
api
.
UserSettingFind
{
UserID
:
memo
.
CreatorID
,
Key
:
&
memoSortOptionKey
,
})
if
err
!=
nil
{
return
nil
,
err
}
memoSortOptionValue
:=
"created_ts"
if
memoSortOption
!=
nil
{
err
=
json
.
Unmarshal
([]
byte
(
memoSortOption
.
Value
),
&
memoSortOptionValue
)
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"failed to unmarshal user setting memo sort option value"
)
}
}
if
memoSortOptionValue
==
"updated_ts"
{
memo
.
DisplayTs
=
memo
.
UpdatedTs
}
return
memo
,
nil
}
...
...
@@ -244,6 +265,9 @@ func patchMemoRaw(ctx context.Context, tx *sql.Tx, patch *api.MemoPatch) (*memoR
if
v
:=
patch
.
CreatedTs
;
v
!=
nil
{
set
,
args
=
append
(
set
,
"created_ts = ?"
),
append
(
args
,
*
v
)
}
if
v
:=
patch
.
UpdatedTs
;
v
!=
nil
{
set
,
args
=
append
(
set
,
"updated_ts = ?"
),
append
(
args
,
*
v
)
}
if
v
:=
patch
.
RowStatus
;
v
!=
nil
{
set
,
args
=
append
(
set
,
"row_status = ?"
),
append
(
args
,
*
v
)
}
...
...
web/src/components/Memo.tsx
View file @
b68d6e26
...
...
@@ -7,7 +7,6 @@ import { useNavigate } from "react-router-dom";
import
"dayjs/locale/zh"
;
import
{
UNKNOWN_ID
}
from
"../helpers/consts"
;
import
{
editorStateService
,
locationService
,
memoService
,
userService
}
from
"../services"
;
import
{
useAppSelector
}
from
"../store"
;
import
Icon
from
"./Icon"
;
import
toastHelper
from
"./Toast"
;
import
MemoContent
from
"./MemoContent"
;
...
...
@@ -33,20 +32,17 @@ export const getFormatedMemoTimeStr = (time: number, locale = "en"): string => {
const
Memo
:
React
.
FC
<
Props
>
=
(
props
:
Props
)
=>
{
const
memo
=
props
.
memo
;
const
user
=
useAppSelector
((
state
)
=>
state
.
user
.
user
);
const
{
t
,
i18n
}
=
useTranslation
();
const
navigate
=
useNavigate
();
const
[
createdAtStr
,
setCreatedAtStr
]
=
useState
<
string
>
(
getFormatedMemoTimeStr
(
memo
.
createdTs
,
i18n
.
language
));
const
[
updatedAtStr
,
setUpdatedAtStr
]
=
useState
<
string
>
(
getFormatedMemoTimeStr
(
memo
.
updatedTs
,
i18n
.
language
));
const
[
displayTimeStr
,
setDisplayTimeStr
]
=
useState
<
string
>
(
getFormatedMemoTimeStr
(
memo
.
displayTs
,
i18n
.
language
));
const
memoContainerRef
=
useRef
<
HTMLDivElement
>
(
null
);
const
isVisitorMode
=
userService
.
isVisitorMode
();
useEffect
(()
=>
{
let
intervalFlag
:
any
=
-
1
;
if
(
Date
.
now
()
-
memo
.
created
Ts
<
1000
*
60
*
60
*
24
)
{
if
(
Date
.
now
()
-
memo
.
display
Ts
<
1000
*
60
*
60
*
24
)
{
intervalFlag
=
setInterval
(()
=>
{
setCreatedAtStr
(
getFormatedMemoTimeStr
(
memo
.
createdTs
,
i18n
.
language
));
setUpdatedAtStr
(
getFormatedMemoTimeStr
(
memo
.
updatedTs
,
i18n
.
language
));
setDisplayTimeStr
(
getFormatedMemoTimeStr
(
memo
.
displayTs
,
i18n
.
language
));
},
1000
*
1
);
}
...
...
@@ -186,13 +182,12 @@ const Memo: React.FC<Props> = (props: Props) => {
editorStateService
.
setEditMemoWithId
(
memo
.
id
);
};
const
timeStr
=
user
?.
setting
.
memoSortOption
===
"created_ts"
?
createdAtStr
:
`
${
t
(
"common.update-on"
)}
${
updatedAtStr
}
`
;
return
(
<
div
className=
{
`memo-wrapper ${"memos-" + memo.id} ${memo.pinned ? "pinned" : ""}`
}
ref=
{
memoContainerRef
}
>
<
div
className=
"memo-top-wrapper"
>
<
div
className=
"status-text-container"
>
<
span
className=
"time-text"
onClick=
{
handleShowMemoStoryDialog
}
>
{
t
imeStr
}
{
displayT
imeStr
}
</
span
>
{
memo
.
visibility
!==
"PRIVATE"
&&
!
isVisitorMode
&&
(
<
span
className=
{
`status-text ${memo.visibility.toLocaleLowerCase()}`
}
>
{
memo
.
visibility
}
</
span
>
...
...
web/src/components/MemoEditor.tsx
View file @
b68d6e26
...
...
@@ -174,7 +174,6 @@ const MemoEditor: React.FC = () => {
});
locationService
.
clearQuery
();
}
locationService
.
setUpdatedFlag
();
}
catch
(
error
:
any
)
{
console
.
error
(
error
);
toastHelper
.
error
(
error
.
response
.
data
.
message
);
...
...
web/src/components/MemoList.tsx
View file @
b68d6e26
...
...
@@ -12,8 +12,7 @@ import "../less/memo-list.less";
const
MemoList
=
()
=>
{
const
{
t
}
=
useTranslation
();
const
query
=
useAppSelector
((
state
)
=>
state
.
location
.
query
);
const
updatedTime
=
useAppSelector
((
state
)
=>
state
.
location
.
updatedTime
);
const
user
=
useAppSelector
((
state
)
=>
state
.
user
.
user
);
const
memoSortOption
=
useAppSelector
((
state
)
=>
state
.
user
.
user
?.
setting
.
memoSortOption
);
const
{
memos
,
isFetching
}
=
useAppSelector
((
state
)
=>
state
.
memo
);
const
{
tag
:
tagQuery
,
duration
,
type
:
memoType
,
text
:
textQuery
,
shortcutId
}
=
query
??
{};
...
...
@@ -72,8 +71,8 @@ const MemoList = () => {
const pinnedMemos = shownMemos.filter((m) =
>
m.pinned);
const unpinnedMemos = shownMemos.filter((m) =
>
!m.pinned);
const memoSorting = (m
1: Memo, m2
: Memo) =
>
{
return
user
?.
setting
.
memoSortOption
===
"created_ts"
?
m2
.
createdTs
-
m1
.
createdTs
:
m2
.
updatedTs
-
m1
.
updated
Ts
;
const memoSorting = (m
i: Memo, mj
: Memo) =
>
{
return
mj
.
displayTs
-
mi
.
display
Ts
;
}
;
pinnedMemos.sort(memoSorting);
unpinnedMemos.sort(memoSorting);
...
...
@@ -89,19 +88,19 @@ const MemoList = () => {
console
.
error
(
error
);
toastHelper
.
error
(
error
.
response
.
data
.
message
);
});
}
, [
updatedTime
]);
}
, [
memoSortOption
]);
useEffect(() =
>
{
const
pageWrapper
=
document
.
body
.
querySelector
(
".page-wrapper"
);
if
(
pageWrapper
)
{
pageWrapper
.
scrollTo
(
0
,
0
);
}
}
, [query
, updatedTime
]);
}
, [query]);
return (
<
div
className=
"memo-list-container"
>
{
sortedMemos
.
map
((
memo
)
=>
(
<
Memo
key=
{
`${memo.id}-${memo.
createdTs}-${memo.updated
Ts}`
}
memo=
{
memo
}
/>
<
Memo
key=
{
`${memo.id}-${memo.
display
Ts}`
}
memo=
{
memo
}
/>
))
}
{
isFetching
?
(
<
div
className=
"status-text-container fetching-tip"
>
...
...
web/src/services/locationService.ts
View file @
b68d6e26
import
{
stringify
}
from
"qs"
;
import
store
from
"../store"
;
import
{
setQuery
,
setPathname
,
setUpdatedTime
,
Query
,
updateStateWithLocation
}
from
"../store/modules/location"
;
import
{
getTimeStampByDate
}
from
"../helpers/utils"
;
import
{
setQuery
,
setPathname
,
Query
,
updateStateWithLocation
}
from
"../store/modules/location"
;
const
updateLocationUrl
=
(
method
:
"replace"
|
"push"
=
"replace"
)
=>
{
const
{
query
,
pathname
,
hash
}
=
store
.
getState
().
location
;
...
...
@@ -113,10 +112,6 @@ const locationService = {
);
updateLocationUrl
();
},
setUpdatedFlag
:
()
=>
{
store
.
dispatch
(
setUpdatedTime
(
getTimeStampByDate
(
new
Date
()).
toString
()));
},
};
export
default
locationService
;
web/src/services/memoService.ts
View file @
b68d6e26
...
...
@@ -8,6 +8,7 @@ const convertResponseModelMemo = (memo: Memo): Memo => {
...
memo
,
createdTs
:
memo
.
createdTs
*
1000
,
updatedTs
:
memo
.
updatedTs
*
1000
,
displayTs
:
memo
.
displayTs
*
1000
,
};
};
...
...
web/src/store/modules/location.ts
View file @
b68d6e26
...
...
@@ -17,7 +17,6 @@ interface State {
pathname
:
string
;
hash
:
string
;
query
:
Query
;
updatedTime
:
string
;
}
const
getValidPathname
=
(
pathname
:
string
):
string
=>
{
...
...
@@ -36,7 +35,6 @@ const getStateFromLocation = () => {
pathname
:
getValidPathname
(
pathname
),
hash
:
hash
,
query
:
{},
updatedTime
:
""
,
};
if
(
search
!==
""
)
{
...
...
@@ -88,15 +86,9 @@ const locationSlice = createSlice({
},
};
},
setUpdatedTime
:
(
state
,
action
:
PayloadAction
<
string
>
)
=>
{
return
{
...
state
,
updatedTime
:
action
.
payload
,
};
},
},
});
export
const
{
setPathname
,
setQuery
,
setUpdatedTime
,
updateStateWithLocation
}
=
locationSlice
.
actions
;
export
const
{
setPathname
,
setQuery
,
updateStateWithLocation
}
=
locationSlice
.
actions
;
export
default
locationSlice
.
reducer
;
web/src/store/modules/memo.ts
View file @
b68d6e26
...
...
@@ -18,13 +18,13 @@ const memoSlice = createSlice({
setMemos
:
(
state
,
action
:
PayloadAction
<
Memo
[]
>
)
=>
{
return
{
...
state
,
memos
:
action
.
payload
.
filter
((
m
)
=>
m
.
rowStatus
===
"NORMAL"
)
.
sort
((
a
,
b
)
=>
b
.
createdTs
-
a
.
createdTs
)
,
memos
:
action
.
payload
.
filter
((
m
)
=>
m
.
rowStatus
===
"NORMAL"
),
};
},
createMemo
:
(
state
,
action
:
PayloadAction
<
Memo
>
)
=>
{
return
{
...
state
,
memos
:
state
.
memos
.
concat
(
action
.
payload
)
.
sort
((
a
,
b
)
=>
b
.
createdTs
-
a
.
createdTs
)
,
memos
:
state
.
memos
.
concat
(
action
.
payload
),
};
},
patchMemo
:
(
state
,
action
:
PayloadAction
<
Partial
<
Memo
>>
)
=>
{
...
...
web/src/types/modules/memo.d.ts
View file @
b68d6e26
...
...
@@ -13,6 +13,7 @@ interface Memo {
content
:
string
;
visibility
:
Visibility
;
pinned
:
boolean
;
displayTs
:
TimeStamp
;
creator
:
User
;
resourceList
:
Resource
[];
...
...
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