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
da1ccabd
Commit
da1ccabd
authored
Mar 20, 2022
by
email
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: use dialog instead of page
parent
2b5ee783
Changes
15
Show whitespace changes
Inline
Side-by-side
Showing
15 changed files
with
190 additions
and
260 deletions
+190
-260
MemoTrashDialog.tsx
web/src/components/MemoTrashDialog.tsx
+79
-0
MenuBtnsPopup.tsx
web/src/components/MenuBtnsPopup.tsx
+5
-3
SettingDialog.tsx
web/src/components/SettingDialog.tsx
+45
-0
ShortcutList.tsx
web/src/components/ShortcutList.tsx
+1
-4
TagList.tsx
web/src/components/TagList.tsx
+1
-1
UsageHeatMap.tsx
web/src/components/UsageHeatMap.tsx
+1
-1
memo-trash-dialog.less
web/src/less/memo-trash-dialog.less
+23
-0
memo-trash.less
web/src/less/memo-trash.less
+0
-39
setting-dialog.less
web/src/less/setting-dialog.less
+33
-0
setting.less
web/src/less/setting.less
+0
-47
Setting.tsx
web/src/pages/Setting.tsx
+0
-30
Trash.tsx
web/src/pages/Trash.tsx
+0
-129
homeRouter.tsx
web/src/routers/homeRouter.tsx
+0
-4
locationService.ts
web/src/services/locationService.ts
+1
-1
location.d.ts
web/src/types/location.d.ts
+1
-1
No files found.
web/src/components/MemoTrashDialog.tsx
0 → 100644
View file @
da1ccabd
import
{
useCallback
,
useEffect
,
useState
}
from
"react"
;
import
useLoading
from
"../hooks/useLoading"
;
import
{
locationService
,
memoService
}
from
"../services"
;
import
{
showDialog
}
from
"./Dialog"
;
import
toastHelper
from
"./Toast"
;
import
DeletedMemo
from
"./DeletedMemo"
;
import
"../less/memo-trash-dialog.less"
;
interface
Props
extends
DialogProps
{}
const
MemoTrashDialog
:
React
.
FC
<
Props
>
=
(
props
:
Props
)
=>
{
const
{
destroy
}
=
props
;
const
loadingState
=
useLoading
();
const
[
deletedMemos
,
setDeletedMemos
]
=
useState
<
Model
.
Memo
[]
>
([]);
useEffect
(()
=>
{
memoService
.
fetchAllMemos
();
memoService
.
fetchDeletedMemos
()
.
then
((
result
)
=>
{
if
(
result
!==
false
)
{
setDeletedMemos
(
result
);
}
})
.
catch
((
error
)
=>
{
toastHelper
.
error
(
"Failed to fetch deleted memos: "
,
error
);
})
.
finally
(()
=>
{
loadingState
.
setFinish
();
});
locationService
.
clearQuery
();
},
[]);
const
handleDeletedMemoAction
=
useCallback
((
memoId
:
string
)
=>
{
setDeletedMemos
((
deletedMemos
)
=>
deletedMemos
.
filter
((
memo
)
=>
memo
.
id
!==
memoId
));
},
[]);
return
(
<>
<
div
className=
"dialog-header-container"
>
<
p
className=
"title-text"
>
<
span
className=
"icon-text"
>
🗑️
</
span
>
Trash Bin
</
p
>
<
button
className=
"btn close-btn"
onClick=
{
destroy
}
>
<
img
className=
"icon-img"
src=
"/icons/close.svg"
/>
</
button
>
</
div
>
<
div
className=
"dialog-content-container"
>
{
loadingState
.
isLoading
?
(
<
div
className=
"tip-text-container"
>
<
p
className=
"tip-text"
>
fetching data...
</
p
>
</
div
>
)
:
deletedMemos
.
length
===
0
?
(
<
div
className=
"tip-text-container"
>
<
p
className=
"tip-text"
>
Here is No Zettels.
</
p
>
</
div
>
)
:
(
<
div
className=
"deleted-memos-container"
>
{
deletedMemos
.
map
((
memo
)
=>
(
<
DeletedMemo
key=
{
`${memo.id}-${memo.updatedAt}`
}
memo=
{
memo
}
handleDeletedMemoAction=
{
handleDeletedMemoAction
}
/>
))
}
</
div
>
)
}
</
div
>
</>
);
};
export
default
function
showMemoTrashDialog
():
void
{
showDialog
(
{
className
:
"memo-trash-dialog"
,
useAppContext
:
true
,
},
MemoTrashDialog
,
{}
);
}
web/src/components/MenuBtnsPopup.tsx
View file @
da1ccabd
import
{
useEffect
,
useRef
}
from
"react"
;
import
{
useEffect
,
useRef
}
from
"react"
;
import
{
locationService
,
userService
}
from
"../services"
;
import
{
locationService
,
userService
}
from
"../services"
;
import
showAboutSiteDialog
from
"./AboutSiteDialog"
;
import
showAboutSiteDialog
from
"./AboutSiteDialog"
;
import
showSettingDialog
from
"./SettingDialog"
;
import
showMemoTrashDialog
from
"./MemoTrashDialog"
;
import
"../less/menu-btns-popup.less"
;
import
"../less/menu-btns-popup.less"
;
interface
Props
{
interface
Props
{
...
@@ -29,11 +31,11 @@ const MenuBtnsPopup: React.FC<Props> = (props: Props) => {
...
@@ -29,11 +31,11 @@ const MenuBtnsPopup: React.FC<Props> = (props: Props) => {
},
[
shownStatus
]);
},
[
shownStatus
]);
const
handleMyAccountBtnClick
=
()
=>
{
const
handleMyAccountBtnClick
=
()
=>
{
locationService
.
pushHistory
(
"/setting"
);
showSettingDialog
(
);
};
};
const
handleMemosTrashBtnClick
=
()
=>
{
const
handleMemosTrashBtnClick
=
()
=>
{
locationService
.
pushHistory
(
"/trash"
);
showMemoTrashDialog
(
);
};
};
const
handleAboutBtnClick
=
()
=>
{
const
handleAboutBtnClick
=
()
=>
{
...
@@ -49,7 +51,7 @@ const MenuBtnsPopup: React.FC<Props> = (props: Props) => {
...
@@ -49,7 +51,7 @@ const MenuBtnsPopup: React.FC<Props> = (props: Props) => {
return
(
return
(
<
div
className=
{
`menu-btns-popup ${shownStatus ? "" : "hidden"}`
}
ref=
{
popupElRef
}
>
<
div
className=
{
`menu-btns-popup ${shownStatus ? "" : "hidden"}`
}
ref=
{
popupElRef
}
>
<
button
className=
"btn action-btn"
onClick=
{
handleMyAccountBtnClick
}
>
<
button
className=
"btn action-btn"
onClick=
{
handleMyAccountBtnClick
}
>
<
span
className=
"icon"
>
👤
</
span
>
Setting
s
<
span
className=
"icon"
>
👤
</
span
>
Setting
</
button
>
</
button
>
<
button
className=
"btn action-btn"
onClick=
{
handleMemosTrashBtnClick
}
>
<
button
className=
"btn action-btn"
onClick=
{
handleMemosTrashBtnClick
}
>
<
span
className=
"icon"
>
🗑️
</
span
>
Recycle Bin
<
span
className=
"icon"
>
🗑️
</
span
>
Recycle Bin
...
...
web/src/components/SettingDialog.tsx
0 → 100644
View file @
da1ccabd
import
{
useEffect
}
from
"react"
;
import
{
memoService
}
from
"../services"
;
import
{
showDialog
}
from
"./Dialog"
;
import
MyAccountSection
from
"./MyAccountSection"
;
import
PreferencesSection
from
"./PreferencesSection"
;
import
"../less/setting-dialog.less"
;
interface
Props
extends
DialogProps
{}
const
SettingDialog
:
React
.
FC
<
Props
>
=
(
props
:
Props
)
=>
{
const
{
destroy
}
=
props
;
useEffect
(()
=>
{
memoService
.
fetchAllMemos
();
},
[]);
return
(
<>
<
div
className=
"dialog-header-container"
>
<
p
className=
"title-text"
>
<
span
className=
"icon-text"
>
👤
</
span
>
Setting
</
p
>
<
button
className=
"btn close-btn"
onClick=
{
destroy
}
>
<
img
className=
"icon-img"
src=
"/icons/close.svg"
/>
</
button
>
</
div
>
<
div
className=
"dialog-content-container"
>
<
MyAccountSection
/>
<
PreferencesSection
/>
</
div
>
</>
);
};
export
default
function
showSettingDialog
():
void
{
showDialog
(
{
className
:
"setting-dialog"
,
useAppContext
:
true
,
},
SettingDialog
,
{}
);
}
web/src/components/ShortcutList.tsx
View file @
da1ccabd
...
@@ -67,14 +67,11 @@ const ShortcutContainer: React.FC<ShortcutContainerProps> = (props: ShortcutCont
...
@@ -67,14 +67,11 @@ const ShortcutContainer: React.FC<ShortcutContainerProps> = (props: ShortcutCont
const
{
shortcut
,
isActive
}
=
props
;
const
{
shortcut
,
isActive
}
=
props
;
const
[
showConfirmDeleteBtn
,
toggleConfirmDeleteBtn
]
=
useToggle
(
false
);
const
[
showConfirmDeleteBtn
,
toggleConfirmDeleteBtn
]
=
useToggle
(
false
);
console
.
log
(
props
);
const
handleShortcutClick
=
()
=>
{
const
handleShortcutClick
=
()
=>
{
console
.
log
(
"here"
);
if
(
isActive
)
{
if
(
isActive
)
{
locationService
.
setMemoShortcut
(
""
);
locationService
.
setMemoShortcut
(
""
);
}
else
{
}
else
{
if
(
!
[
"/"
,
"/trash"
].
includes
(
locationService
.
getState
().
pathname
))
{
if
(
!
[
"/"
].
includes
(
locationService
.
getState
().
pathname
))
{
locationService
.
setPathname
(
"/"
);
locationService
.
setPathname
(
"/"
);
}
}
locationService
.
setMemoShortcut
(
shortcut
.
id
);
locationService
.
setMemoShortcut
(
shortcut
.
id
);
...
...
web/src/components/TagList.tsx
View file @
da1ccabd
...
@@ -101,7 +101,7 @@ const TagItemContainer: React.FC<TagItemContainerProps> = (props: TagItemContain
...
@@ -101,7 +101,7 @@ const TagItemContainer: React.FC<TagItemContainerProps> = (props: TagItemContain
locationService
.
setTagQuery
(
""
);
locationService
.
setTagQuery
(
""
);
}
else
{
}
else
{
utils
.
copyTextToClipboard
(
`#
${
tag
.
text
}
`
);
utils
.
copyTextToClipboard
(
`#
${
tag
.
text
}
`
);
if
(
!
[
"/"
,
"/trash"
].
includes
(
locationService
.
getState
().
pathname
))
{
if
(
!
[
"/"
].
includes
(
locationService
.
getState
().
pathname
))
{
locationService
.
setPathname
(
"/"
);
locationService
.
setPathname
(
"/"
);
}
}
locationService
.
setTagQuery
(
tag
.
text
);
locationService
.
setTagQuery
(
tag
.
text
);
...
...
web/src/components/UsageHeatMap.tsx
View file @
da1ccabd
...
@@ -76,7 +76,7 @@ const UsageHeatMap: React.FC<Props> = () => {
...
@@ -76,7 +76,7 @@ const UsageHeatMap: React.FC<Props> = () => {
locationService
.
setFromAndToQuery
(
0
,
0
);
locationService
.
setFromAndToQuery
(
0
,
0
);
setCurrentStat
(
null
);
setCurrentStat
(
null
);
}
else
if
(
item
.
count
>
0
)
{
}
else
if
(
item
.
count
>
0
)
{
if
(
!
[
"/"
,
"/trash"
].
includes
(
locationService
.
getState
().
pathname
))
{
if
(
!
[
"/"
].
includes
(
locationService
.
getState
().
pathname
))
{
locationService
.
setPathname
(
"/"
);
locationService
.
setPathname
(
"/"
);
}
}
locationService
.
setFromAndToQuery
(
item
.
timestamp
,
item
.
timestamp
+
DAILY_TIMESTAMP
);
locationService
.
setFromAndToQuery
(
item
.
timestamp
,
item
.
timestamp
+
DAILY_TIMESTAMP
);
...
...
web/src/less/memo-trash-dialog.less
0 → 100644
View file @
da1ccabd
@import "./mixin.less";
@import "./memos-header.less";
.memo-trash-dialog {
> .dialog-container {
@apply w-128 max-w-full mb-8;
> .dialog-content-container {
.flex(column, flex-start, flex-start);
@apply w-full overflow-y-scroll;
> .tip-text-container {
@apply w-full h-32;
.flex(column, center, center);
}
> .deleted-memos-container {
.flex(column, flex-start, flex-start);
@apply w-full;
}
}
}
}
web/src/less/memo-trash.less
deleted
100644 → 0
View file @
2b5ee783
@import "./mixin.less";
@import "./memos-header.less";
.memo-trash-wrapper {
@apply px-8;
.flex(column, flex-start, flex-start);
width: 100%;
height: 100%;
flex-grow: 1;
overflow-y: scroll;
.hide-scroll-bar();
> .section-header-container {
width: 100%;
height: 40px;
margin-bottom: 0;
> .title-text {
font-weight: bold;
font-size: 18px;
color: @text-black;
}
}
> .tip-text-container {
width: 100%;
height: 128px;
.flex(column, center, center);
}
> .deleted-memos-container {
.flex(column, flex-start, flex-start);
flex-grow: 1;
width: 100%;
overflow-y: scroll;
padding-bottom: 64px;
.hide-scroll-bar();
}
}
web/src/less/setting-dialog.less
0 → 100644
View file @
da1ccabd
@import "./mixin.less";
@import "./memos-header.less";
.setting-dialog {
> .dialog-container {
@apply w-3/5 max-w-full mb-8;
> .dialog-content-container {
.flex(column, flex-start, flex-start);
@apply w-full overflow-y-scroll;
.hide-scroll-bar();
> .section-container {
.flex(column, flex-start, flex-start);
@apply w-full my-2;
> .title-text {
@apply text-base font-bold mb-2;
color: @text-black;
}
> .form-label {
.flex(row, flex-start, center);
@apply w-full text-sm mb-2;
> .normal-text {
@apply shrink-0;
}
}
}
}
}
}
web/src/less/setting.less
deleted
100644 → 0
View file @
2b5ee783
@import "./mixin.less";
@import "./memos-header.less";
.preference-wrapper {
.flex(column, flex-start, flex-start);
@apply w-full h-full grow overflow-y-scroll px-8;
.hide-scroll-bar();
> .section-header-container {
@apply w-full h-10 mb-0;
> .title-text {
@apply font-bold text-lg;
color: @text-black;
}
}
> .tip-text-container {
.flex(column, center, center);
@apply w-full h-32;
}
> .sections-wrapper {
.flex(column, flex-start, flex-start);
@apply grow w-full overflow-y-scroll pb-16;
.hide-scroll-bar();
> .section-container {
.flex(column, flex-start, flex-start);
@apply w-full bg-white my-2 mx-0 p-4 pb-2 rounded-lg;
> .title-text {
@apply text-base font-bold mb-2;
color: @text-black;
}
> .form-label {
.flex(row, flex-start, center);
@apply w-full text-sm mb-2;
> .normal-text {
@apply shrink-0;
}
}
}
}
}
web/src/pages/Setting.tsx
deleted
100644 → 0
View file @
2b5ee783
import
{
useEffect
}
from
"react"
;
import
{
memoService
}
from
"../services"
;
import
MyAccountSection
from
"../components/MyAccountSection"
;
import
PreferencesSection
from
"../components/PreferencesSection"
;
import
"../less/setting.less"
;
interface
Props
{}
const
Setting
:
React
.
FC
<
Props
>
=
()
=>
{
useEffect
(()
=>
{
memoService
.
fetchAllMemos
();
},
[]);
return
(
<
div
className=
"preference-wrapper"
>
<
div
className=
"section-header-container"
>
<
div
className=
"title-text"
>
<
span
className=
"normal-text"
>
Settings
</
span
>
</
div
>
</
div
>
<
div
className=
"sections-wrapper"
>
<
MyAccountSection
/>
<
PreferencesSection
/>
</
div
>
</
div
>
);
};
export
default
Setting
;
web/src/pages/Trash.tsx
deleted
100644 → 0
View file @
2b5ee783
import
{
useCallback
,
useContext
,
useEffect
,
useState
}
from
"react"
;
import
appContext
from
"../stores/appContext"
;
import
useLoading
from
"../hooks/useLoading"
;
import
{
locationService
,
memoService
,
shortcutService
}
from
"../services"
;
import
{
IMAGE_URL_REG
,
LINK_REG
,
MEMO_LINK_REG
,
TAG_REG
}
from
"../helpers/consts"
;
import
utils
from
"../helpers/utils"
;
import
{
checkShouldShowMemoWithFilters
}
from
"../helpers/filter"
;
import
toastHelper
from
"../components/Toast"
;
import
DeletedMemo
from
"../components/DeletedMemo"
;
import
MemoFilter
from
"../components/MemoFilter"
;
import
"../less/memo-trash.less"
;
interface
Props
{}
const
Trash
:
React
.
FC
<
Props
>
=
()
=>
{
const
{
locationState
:
{
query
},
}
=
useContext
(
appContext
);
const
loadingState
=
useLoading
();
const
[
deletedMemos
,
setDeletedMemos
]
=
useState
<
Model
.
Memo
[]
>
([]);
const
{
tag
:
tagQuery
,
duration
,
type
:
memoType
,
text
:
textQuery
,
shortcutId
}
=
query
;
const
queryFilter
=
shortcutService
.
getShortcutById
(
shortcutId
);
const
showMemoFilter
=
Boolean
(
tagQuery
||
(
duration
&&
duration
.
from
<
duration
.
to
)
||
memoType
||
textQuery
||
queryFilter
);
const
shownMemos
=
showMemoFilter
||
queryFilter
?
deletedMemos
.
filter
((
memo
)
=
>
{
let
shouldShow
=
true
;
if
(
queryFilter
)
{
const
filters
=
JSON
.
parse
(
queryFilter
.
payload
)
as
Filter
[];
if
(
Array
.
isArray
(
filters
))
{
shouldShow
=
checkShouldShowMemoWithFilters
(
memo
,
filters
);
}
}
if
(
tagQuery
)
{
const
tagsSet
=
new
Set
<
string
>
();
for
(
const
t
of
Array
.
from
(
memo
.
content
.
match
(
TAG_REG
)
??
[]))
{
const
tag
=
t
.
replace
(
TAG_REG
,
"$1"
).
trim
();
const
items
=
tag
.
split
(
"/"
);
let
temp
=
""
;
for
(
const
i
of
items
)
{
temp
+=
i
;
tagsSet
.
add
(
temp
);
temp
+=
"/"
;
}
}
if
(
!
tagsSet
.
has
(
tagQuery
))
{
shouldShow
=
false
;
}
}
if
(
duration
&&
duration
.
from
<
duration
.
to
&&
(
utils
.
getTimeStampByDate
(
memo
.
createdAt
)
<
duration
.
from
||
utils
.
getTimeStampByDate
(
memo
.
createdAt
)
>
duration.to)
)
{
shouldShow
=
false
;
}
if (memoType)
{
if
(
memoType
===
"NOT_TAGGED"
&&
memo
.
content
.
match
(
TAG_REG
)
!==
null
)
{
shouldShow
=
false
;
}
else
if
(
memoType
===
"LINKED"
&&
memo
.
content
.
match
(
LINK_REG
)
===
null
)
{
shouldShow
=
false
;
}
else
if
(
memoType
===
"IMAGED"
&&
memo
.
content
.
match
(
IMAGE_URL_REG
)
===
null
)
{
shouldShow
=
false
;
}
else
if
(
memoType
===
"CONNECTED"
&&
memo
.
content
.
match
(
MEMO_LINK_REG
)
===
null
)
{
shouldShow
=
false
;
}
}
if (textQuery
&&
!memo.content.includes(textQuery))
{
shouldShow
=
false
;
}
return shouldShow;
}
)
: deletedMemos;
useEffect(() =
>
{
memoService
.
fetchAllMemos
();
memoService
.
fetchDeletedMemos
()
.
then
((
result
)
=>
{
if
(
result
!==
false
)
{
setDeletedMemos
(
result
);
}
})
.
catch
((
error
)
=>
{
toastHelper
.
error
(
"Failed to fetch deleted memos: "
,
error
);
})
.
finally
(()
=>
{
loadingState
.
setFinish
();
});
locationService
.
clearQuery
();
}
, []);
const handleDeletedMemoAction = useCallback((memoId: string) =
>
{
setDeletedMemos
((
deletedMemos
)
=>
deletedMemos
.
filter
((
memo
)
=>
memo
.
id
!==
memoId
));
}
, []);
return (
<
div
className=
"memo-trash-wrapper"
>
<
div
className=
"section-header-container"
>
<
div
className=
"title-text"
>
<
span
className=
"normal-text"
>
Recycle Bin
</
span
>
</
div
>
</
div
>
<
MemoFilter
/>
{
loadingState
.
isLoading
?
(
<
div
className=
"tip-text-container"
>
<
p
className=
"tip-text"
>
fetching data...
</
p
>
</
div
>
)
:
deletedMemos
.
length
===
0
?
(
<
div
className=
"tip-text-container"
>
<
p
className=
"tip-text"
>
Here is No Zettels.
</
p
>
</
div
>
)
:
(
<
div
className=
"deleted-memos-container"
>
{
shownMemos
.
map
((
memo
)
=>
(
<
DeletedMemo
key=
{
`${memo.id}-${memo.updatedAt}`
}
memo=
{
memo
}
handleDeletedMemoAction=
{
handleDeletedMemoAction
}
/>
))
}
</
div
>
)
}
</
div
>
);
};
export default Trash;
web/src/routers/homeRouter.tsx
View file @
da1ccabd
import
Memos
from
"../pages/Memos"
;
import
Memos
from
"../pages/Memos"
;
import
Trash
from
"../pages/Trash"
;
import
Setting
from
"../pages/Setting"
;
const
homeRouter
=
{
const
homeRouter
=
{
"/trash"
:
<
Trash
/>
,
"/setting"
:
<
Setting
/>
,
"*"
:
<
Memos
/>
,
"*"
:
<
Memos
/>
,
};
};
...
...
web/src/services/locationService.ts
View file @
da1ccabd
...
@@ -185,7 +185,7 @@ class LocationService {
...
@@ -185,7 +185,7 @@ class LocationService {
};
};
public
getValidPathname
=
(
pathname
:
string
):
AppRouter
=>
{
public
getValidPathname
=
(
pathname
:
string
):
AppRouter
=>
{
if
([
"/"
,
"/signin"
,
"/trash"
,
"/setting"
].
includes
(
pathname
))
{
if
([
"/"
,
"/signin"
].
includes
(
pathname
))
{
return
pathname
as
AppRouter
;
return
pathname
as
AppRouter
;
}
else
{
}
else
{
return
"/"
;
return
"/"
;
...
...
web/src/types/location.d.ts
View file @
da1ccabd
...
@@ -11,7 +11,7 @@ interface Query {
...
@@ -11,7 +11,7 @@ interface Query {
shortcutId
:
string
;
shortcutId
:
string
;
}
}
type
AppRouter
=
"/"
|
"/signin"
|
"/trash"
|
"/setting"
;
type
AppRouter
=
"/"
|
"/signin"
;
interface
AppLocation
{
interface
AppLocation
{
pathname
:
AppRouter
;
pathname
:
AppRouter
;
...
...
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