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
615cec30
Unverified
Commit
615cec30
authored
May 15, 2022
by
STEVEN
Committed by
GitHub
May 15, 2022
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: import data from json (#53)
parent
af4b6114
Changes
12
Hide whitespace changes
Inline
Side-by-side
Showing
12 changed files
with
90 additions
and
61 deletions
+90
-61
memo.go
api/memo.go
+2
-0
memo.go
store/memo.go
+11
-5
AboutSiteDialog.tsx
web/src/components/AboutSiteDialog.tsx
+2
-3
ChangePasswordDialog.tsx
web/src/components/ChangePasswordDialog.tsx
+2
-4
MyAccountSection.tsx
web/src/components/MyAccountSection.tsx
+4
-8
PreferencesSection.tsx
web/src/components/PreferencesSection.tsx
+42
-1
api.ts
web/src/helpers/api.ts
+13
-4
change-password-dialog.less
web/src/less/change-password-dialog.less
+0
-8
menu-btns-popup.less
web/src/less/menu-btns-popup.less
+1
-1
my-account-section.less
web/src/less/my-account-section.less
+3
-21
setting-dialog.less
web/src/less/setting-dialog.less
+2
-2
memoService.ts
web/src/services/memoService.ts
+8
-4
No files found.
api/memo.go
View file @
615cec30
...
...
@@ -16,6 +16,8 @@ type Memo struct {
type
MemoCreate
struct
{
// Standard fields
CreatorID
int
// Used to import memos with clearly created ts.
CreatedTs
*
int64
`json:"createdTs"`
// Domain specific fields
Content
string
`json:"content"`
...
...
store/memo.go
View file @
615cec30
...
...
@@ -65,16 +65,22 @@ func (s *MemoService) DeleteMemo(delete *api.MemoDelete) error {
}
func
createMemo
(
db
*
DB
,
create
*
api
.
MemoCreate
)
(
*
api
.
Memo
,
error
)
{
set
:=
[]
string
{
"creator_id"
,
"content"
}
placeholder
:=
[]
string
{
"?"
,
"?"
}
args
:=
[]
interface
{}{
create
.
CreatorID
,
create
.
Content
}
if
v
:=
create
.
CreatedTs
;
v
!=
nil
{
set
,
placeholder
,
args
=
append
(
set
,
"created_ts"
),
append
(
placeholder
,
"?"
),
append
(
args
,
*
v
)
}
row
,
err
:=
db
.
Db
.
Query
(
`
INSERT INTO memo (
creator_id,
content
`
+
strings
.
Join
(
set
,
", "
)
+
`
)
VALUES (
?, ?
)
VALUES (
`
+
strings
.
Join
(
placeholder
,
","
)
+
`
)
RETURNING id, creator_id, created_ts, updated_ts, content, row_status
`
,
create
.
CreatorID
,
create
.
Content
,
args
...
,
)
if
err
!=
nil
{
return
nil
,
FormatError
(
err
)
...
...
web/src/components/AboutSiteDialog.tsx
View file @
615cec30
...
...
@@ -35,13 +35,12 @@ const AboutSiteDialog: React.FC<Props> = ({ destroy }: Props) => {
</
div
>
<
div
className=
"dialog-content-container"
>
<
p
>
Memos is an open source,
self-hosted alternative to
<
a
href=
"https://flomoapp.com"
>
flomo
</
a
>
.
Memos is an open source,
quickly self-hosted alternative
<
a
href=
"https://flomoapp.com"
>
flomo
</
a
>
.
</
p
>
<
p
>
Built with `Golang` and `React`.
</
p
>
<
br
/>
<
p
>
🏗
<
a
href=
"https://github.com/justmemos/memos"
>
This project
</
a
>
is working in progress, and very pleasure to your
{
" "
}
<
a
href=
"https://github.com/justmemos/memos/issues"
>
issue
s
</
a
>
.
<
a
href=
"https://github.com/justmemos/memos/issues"
>
PR
s
</
a
>
.
</
p
>
<
p
className=
"updated-time-text"
>
Last updated on
<
span
className=
"pre-text"
>
{
lastUpdatedAt
}
</
span
>
🎉
...
...
web/src/components/ChangePasswordDialog.tsx
View file @
615cec30
...
...
@@ -73,12 +73,10 @@ const ChangePasswordDialog: React.FC<Props> = ({ destroy }: Props) => {
</
div
>
<
div
className=
"dialog-content-container"
>
<
label
className=
"form-label input-form-label"
>
<
span
className=
{
"normal-text "
+
(
newPassword
===
""
?
""
:
"not-null"
)
}
>
New passworld
</
span
>
<
input
type=
"password"
value=
{
newPassword
}
onChange=
{
handleNewPasswordChanged
}
/>
<
input
type=
"password"
placeholder=
"New passworld"
value=
{
newPassword
}
onChange=
{
handleNewPasswordChanged
}
/>
</
label
>
<
label
className=
"form-label input-form-label"
>
<
span
className=
{
"normal-text "
+
(
newPasswordAgain
===
""
?
""
:
"not-null"
)
}
>
Repeat the new password
</
span
>
<
input
type=
"password"
value=
{
newPasswordAgain
}
onChange=
{
handleNewPasswordAgainChanged
}
/>
<
input
type=
"password"
placeholder=
"Repeat the new password"
value=
{
newPasswordAgain
}
onChange=
{
handleNewPasswordAgainChanged
}
/>
</
label
>
<
div
className=
"btns-container"
>
<
span
className=
"btn cancel-btn"
onClick=
{
handleCloseBtnClick
}
>
...
...
web/src/components/MyAccountSection.tsx
View file @
615cec30
...
...
@@ -66,17 +66,13 @@ const MyAccountSection: React.FC<Props> = () => {
<
div
className=
"section-container account-section-container"
>
<
p
className=
"title-text"
>
Account Information
</
p
>
<
label
className=
"form-label"
>
<
span
className=
"normal-text"
>
ID
:
</
span
>
<
span
className=
"normal-text"
>
{
user
.
id
}
</
span
>
<
span
className=
"normal-text"
>
Email
:
</
span
>
<
span
className=
"normal-text"
>
{
user
.
email
}
</
span
>
</
label
>
<
label
className=
"form-label"
>
<
span
className=
"normal-text"
>
Created at:
</
span
>
<
span
className=
"normal-text"
>
{
utils
.
getDateString
(
user
.
createdAt
)
}
</
span
>
</
label
>
<
label
className=
"form-label"
>
<
span
className=
"normal-text"
>
Email:
</
span
>
<
span
className=
"normal-text"
>
{
user
.
email
}
</
span
>
</
label
>
<
label
className=
"form-label input-form-label username-label"
>
<
span
className=
"normal-text"
>
Username:
</
span
>
<
input
type=
"text"
value=
{
username
}
onChange=
{
handleUsernameChanged
}
/>
...
...
@@ -97,12 +93,12 @@ const MyAccountSection: React.FC<Props> = () => {
<
label
className=
"form-label password-label"
>
<
span
className=
"normal-text"
>
Password:
</
span
>
<
span
className=
"btn"
onClick=
{
handleChangePasswordBtnClick
}
>
Change
I
t
Change
i
t
</
span
>
</
label
>
</
div
>
<
div
className=
"section-container openapi-section-container"
>
<
p
className=
"title-text"
>
Open API
(Experimental feature)
</
p
>
<
p
className=
"title-text"
>
Open API
</
p
>
<
p
className=
"value-text"
>
{
openAPIRoute
}
</
p
>
<
span
className=
"reset-btn"
onClick=
{
handleResetOpenIdBtnClick
}
>
Reset API
...
...
web/src/components/PreferencesSection.tsx
View file @
615cec30
import
{
useContext
}
from
"react"
;
import
appContext
from
"../stores/appContext"
;
import
{
globalStateService
,
memoService
}
from
"../services"
;
import
utils
from
"../helpers/utils"
;
import
{
formatMemoContent
}
from
"./Memo"
;
import
toastHelper
from
"./Toast"
;
import
"../less/preferences-section.less"
;
interface
Props
{}
...
...
@@ -41,13 +43,49 @@ const PreferencesSection: React.FC<Props> = () => {
const
jsonStr
=
JSON
.
stringify
(
formatedMemos
);
const
element
=
document
.
createElement
(
"a"
);
element
.
setAttribute
(
"href"
,
"data:text/json;charset=utf-8,"
+
encodeURIComponent
(
jsonStr
));
element
.
setAttribute
(
"download"
,
"data.json"
);
element
.
setAttribute
(
"download"
,
`memos-
${
utils
.
getDateTimeString
(
Date
.
now
())}
.json`
);
element
.
style
.
display
=
"none"
;
document
.
body
.
appendChild
(
element
);
element
.
click
();
document
.
body
.
removeChild
(
element
);
};
const
handleImportBtnClick
=
async
()
=>
{
const
fileInputEl
=
document
.
createElement
(
"input"
);
fileInputEl
.
type
=
"file"
;
fileInputEl
.
accept
=
"application/JSON"
;
fileInputEl
.
onchange
=
()
=>
{
if
(
fileInputEl
.
files
?.
length
&&
fileInputEl
.
files
.
length
>
0
)
{
const
reader
=
new
FileReader
();
reader
.
readAsText
(
fileInputEl
.
files
[
0
]);
reader
.
onload
=
async
(
event
)
=>
{
const
memoList
=
JSON
.
parse
(
event
.
target
?.
result
as
string
)
as
Model
.
Memo
[];
if
(
!
Array
.
isArray
(
memoList
))
{
toastHelper
.
error
(
"Unexpected data type."
);
}
let
succeedAmount
=
0
;
for
(
const
memo
of
memoList
)
{
const
content
=
memo
.
content
||
""
;
const
createdAt
=
memo
.
createdAt
||
utils
.
getDateTimeString
(
Date
.
now
());
try
{
await
memoService
.
importMemo
(
content
,
createdAt
);
succeedAmount
++
;
}
catch
(
error
)
{
// do nth
}
}
await
memoService
.
fetchAllMemos
();
toastHelper
.
success
(
`
${
succeedAmount
}
memos successfully imported.`
);
};
}
};
fileInputEl
.
click
();
};
return
(
<>
<
div
className=
"section-container preferences-section-container"
>
...
...
@@ -75,6 +113,9 @@ const PreferencesSection: React.FC<Props> = () => {
<
button
className=
"px-2 py-1 border rounded text-base hover:opacity-80"
onClick=
{
handleExportBtnClick
}
>
Export data as JSON
</
button
>
<
button
className=
"ml-2 px-2 py-1 border rounded text-base hover:opacity-80"
onClick=
{
handleImportBtnClick
}
>
Import from JSON
</
button
>
</
div
>
</
div
>
</>
...
...
web/src/helpers/api.ts
View file @
615cec30
import
utils
from
"./utils"
;
type
ResponseObject
<
T
>
=
{
data
:
T
;
error
?:
string
;
...
...
@@ -133,13 +135,20 @@ namespace api {
});
}
export
function
createMemo
(
content
:
string
)
{
export
function
createMemo
(
content
:
string
,
createdAt
?:
string
)
{
const
data
:
any
=
{
content
,
};
if
(
createdAt
)
{
const
createdTms
=
utils
.
getTimeStampByDate
(
createdAt
);
data
.
createdTs
=
Math
.
floor
(
createdTms
/
1000
);
}
return
request
<
Model
.
Memo
>
({
method
:
"POST"
,
url
:
"/api/memo"
,
data
:
{
content
,
},
data
:
data
,
});
}
...
...
web/src/less/change-password-dialog.less
View file @
615cec30
...
...
@@ -15,14 +15,6 @@
.flex(column, flex-start, flex-start);
@apply relative w-full leading-relaxed;
> .normal-text {
@apply absolute left-2 py-px pl-1 shrink-0 text-sm text-gray-400 leading-10 transition-all cursor-text;
&.not-null {
@apply top-1 bg-white text-xs py-0 px-1 rounded-xl;
}
}
&.input-form-label {
@apply py-3 pb-1;
...
...
web/src/less/menu-btns-popup.less
View file @
615cec30
...
...
@@ -10,7 +10,7 @@
> .btn {
.flex(row, flex-start, center);
@apply w-full py-2 px-3 text-
sm
rounded text-left;
@apply w-full py-2 px-3 text-
base
rounded text-left;
> .icon {
@apply block w-6 text-center mr-2 text-base;
...
...
web/src/less/my-account-section.less
View file @
615cec30
...
...
@@ -55,12 +55,7 @@
&.password-label {
> .btn {
color: @text-blue;
cursor: pointer;
&:hover {
opacity: 0.8;
}
@apply text-blue-600 ml-1 cursor-pointer hover:opacity-80;
}
}
}
...
...
@@ -78,20 +73,7 @@
}
> .reset-btn {
margin-top: 4px;
padding: 4px 8px;
background-color: @bg-red;
border: 1px solid red;
color: red;
border-radius: 4px;
line-height: 1.6;
cursor: pointer;
user-select: none;
font-size: 12px;
&:hover {
opacity: 0.8;
}
@apply mt-2 py-1 px-2 bg-red-50 border border-red-500 text-red-600 rounded leading-4 cursor-pointer text-xs select-none hover:opacity-80;
}
> .usage-guide-container {
...
...
@@ -99,7 +81,7 @@
@apply mt-2 w-full;
> .title-text {
line-height: 2
;
@apply my-2 text-sm
;
}
> pre {
...
...
web/src/less/setting-dialog.less
View file @
615cec30
...
...
@@ -23,7 +23,7 @@
@apply w-40 h-full shrink-0 rounded-l-lg p-4 bg-gray-100 flex flex-col justify-start items-start;
> .section-item {
@apply text-
sm left-6 mt-2
cursor-pointer hover:opacity-80;
@apply text-
base left-6 mt-2 mb-1
cursor-pointer hover:opacity-80;
&.selected {
@apply font-bold hover:opacity-100;
...
...
@@ -48,7 +48,7 @@
@apply w-full text-sm mb-2;
> .normal-text {
@apply shrink-0;
@apply shrink-0
select-text
;
}
}
}
...
...
web/src/services/memoService.ts
View file @
615cec30
...
...
@@ -123,13 +123,13 @@ class MemoService {
return
memos
.
filter
((
m
)
=>
m
.
content
.
includes
(
memoId
));
}
public
async
createMemo
(
tex
t
:
string
):
Promise
<
Model
.
Memo
>
{
const
memo
=
await
api
.
createMemo
(
tex
t
);
public
async
createMemo
(
conten
t
:
string
):
Promise
<
Model
.
Memo
>
{
const
memo
=
await
api
.
createMemo
(
conten
t
);
return
this
.
convertResponseModelMemo
(
memo
);
}
public
async
updateMemo
(
memoId
:
string
,
tex
t
:
string
):
Promise
<
Model
.
Memo
>
{
const
memo
=
await
api
.
updateMemo
(
memoId
,
tex
t
);
public
async
updateMemo
(
memoId
:
string
,
conten
t
:
string
):
Promise
<
Model
.
Memo
>
{
const
memo
=
await
api
.
updateMemo
(
memoId
,
conten
t
);
return
this
.
convertResponseModelMemo
(
memo
);
}
...
...
@@ -141,6 +141,10 @@ class MemoService {
await
api
.
unpinMemo
(
memoId
);
}
public
async
importMemo
(
content
:
string
,
createdAt
:
string
)
{
await
api
.
createMemo
(
content
,
createdAt
);
}
private
convertResponseModelMemo
(
memo
:
Model
.
Memo
):
Model
.
Memo
{
return
{
...
memo
,
...
...
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