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
590b6260
Commit
590b6260
authored
May 01, 2024
by
Steven
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
chore: update upload resources button
parent
832ad92b
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
99 additions
and
218 deletions
+99
-218
CreateResourceDialog.tsx
web/src/components/CreateResourceDialog.tsx
+0
-202
UploadResourceButton.tsx
...mponents/MemoEditor/ActionButton/UploadResourceButton.tsx
+84
-0
index.tsx
web/src/components/MemoEditor/index.tsx
+10
-16
context.ts
web/src/components/MemoEditor/types/context.ts
+5
-0
No files found.
web/src/components/CreateResourceDialog.tsx
deleted
100644 → 0
View file @
832ad92b
import
{
Button
,
IconButton
,
List
,
ListItem
,
Typography
}
from
"@mui/joy"
;
import
React
,
{
useRef
,
useState
}
from
"react"
;
import
{
toast
}
from
"react-hot-toast"
;
import
{
useResourceStore
}
from
"@/store/v1"
;
import
{
Resource
}
from
"@/types/proto/api/v1/resource_service"
;
import
{
useTranslate
}
from
"@/utils/i18n"
;
import
{
generateDialog
}
from
"./Dialog"
;
import
Icon
from
"./Icon"
;
interface
Props
extends
DialogProps
{
onCancel
?:
()
=>
void
;
onConfirm
?:
(
resourceList
:
Resource
[])
=>
void
;
}
interface
State
{
uploadingFlag
:
boolean
;
}
const
CreateResourceDialog
:
React
.
FC
<
Props
>
=
(
props
:
Props
)
=>
{
const
t
=
useTranslate
();
const
{
destroy
,
onCancel
,
onConfirm
}
=
props
;
const
resourceStore
=
useResourceStore
();
const
[
state
,
setState
]
=
useState
<
State
>
({
uploadingFlag
:
false
,
});
const
[
fileList
,
setFileList
]
=
useState
<
File
[]
>
([]);
const
fileInputRef
=
useRef
<
HTMLInputElement
>
(
null
);
const
handleReorderFileList
=
(
fileName
:
string
,
direction
:
"up"
|
"down"
)
=>
{
const
fileIndex
=
fileList
.
findIndex
((
file
)
=>
file
.
name
===
fileName
);
if
(
fileIndex
===
-
1
)
{
return
;
}
const
newFileList
=
[...
fileList
];
if
(
direction
===
"up"
)
{
if
(
fileIndex
===
0
)
{
return
;
}
const
temp
=
newFileList
[
fileIndex
-
1
];
newFileList
[
fileIndex
-
1
]
=
newFileList
[
fileIndex
];
newFileList
[
fileIndex
]
=
temp
;
}
else
if
(
direction
===
"down"
)
{
if
(
fileIndex
===
fileList
.
length
-
1
)
{
return
;
}
const
temp
=
newFileList
[
fileIndex
+
1
];
newFileList
[
fileIndex
+
1
]
=
newFileList
[
fileIndex
];
newFileList
[
fileIndex
]
=
temp
;
}
setFileList
(
newFileList
);
};
const
handleCloseDialog
=
()
=>
{
if
(
onCancel
)
{
onCancel
();
}
destroy
();
};
const
handleFileInputChange
=
async
()
=>
{
if
(
!
fileInputRef
.
current
||
!
fileInputRef
.
current
.
files
)
{
return
;
}
const
files
:
File
[]
=
[];
for
(
const
file
of
fileInputRef
.
current
.
files
)
{
files
.
push
(
file
);
}
setFileList
(
files
);
};
const
allowConfirmAction
=
()
=>
{
if
(
!
fileInputRef
.
current
||
!
fileInputRef
.
current
.
files
||
fileInputRef
.
current
.
files
.
length
===
0
)
{
return
false
;
}
return
true
;
};
const
handleConfirmBtnClick
=
async
()
=>
{
if
(
state
.
uploadingFlag
)
{
return
;
}
setState
((
state
)
=>
{
return
{
...
state
,
uploadingFlag
:
true
,
};
});
const
createdResourceList
:
Resource
[]
=
[];
try
{
if
(
!
fileInputRef
.
current
||
!
fileInputRef
.
current
.
files
)
{
return
;
}
const
filesOnInput
=
Array
.
from
(
fileInputRef
.
current
.
files
);
for
(
const
file
of
fileList
)
{
const
fileOnInput
=
filesOnInput
.
find
((
fileOnInput
)
=>
fileOnInput
.
name
===
file
.
name
);
if
(
!
fileOnInput
)
{
continue
;
}
const
{
name
:
filename
,
size
,
type
}
=
file
;
const
buffer
=
new
Uint8Array
(
await
file
.
arrayBuffer
());
const
resource
=
await
resourceStore
.
createResource
({
resource
:
Resource
.
fromPartial
({
filename
,
size
,
type
,
content
:
buffer
,
}),
});
createdResourceList
.
push
(
resource
);
}
}
catch
(
error
:
any
)
{
console
.
error
(
error
);
toast
.
error
(
error
.
details
);
}
if
(
onConfirm
)
{
onConfirm
(
createdResourceList
);
}
destroy
();
};
return
(
<>
<
div
className=
"dialog-header-container"
>
<
p
className=
"title-text"
>
{
t
(
"resource.create-dialog.title"
)
}
</
p
>
<
IconButton
size=
"sm"
onClick=
{
handleCloseDialog
}
>
<
Icon
.
X
className=
"w-5 h-auto"
/>
</
IconButton
>
</
div
>
<
div
className=
"dialog-content-container !w-80"
>
<
div
className=
"w-full relative bg-blue-50 dark:bg-zinc-900 rounded-md border-dashed border-2 dark:border-zinc-700 flex flex-row justify-center items-center py-8 hover:opacity-90"
>
<
label
htmlFor=
"files"
className=
"p-2 px-4 text-sm text-white cursor-pointer bg-blue-500 block rounded hover:opacity-80"
>
{
t
(
"resource.create-dialog.local-file.choose"
)
}
</
label
>
<
input
className=
"absolute inset-0 w-full h-full opacity-0"
ref=
{
fileInputRef
}
onChange=
{
handleFileInputChange
}
type=
"file"
id=
"files"
multiple=
{
true
}
accept=
"*"
/>
</
div
>
<
List
size=
"sm"
sx=
{
{
width
:
"100%"
}
}
>
{
fileList
.
map
((
file
,
index
)
=>
(
<
ListItem
key=
{
file
.
name
}
className=
"flex justify-between"
>
<
Typography
noWrap
>
{
file
.
name
}
</
Typography
>
<
div
className=
"flex gap-1"
>
<
button
onClick=
{
()
=>
{
handleReorderFileList
(
file
.
name
,
"up"
);
}
}
disabled=
{
index
===
0
}
className=
"disabled:opacity-50"
>
<
Icon
.
ArrowUp
className=
"w-4 h-4"
/>
</
button
>
<
button
onClick=
{
()
=>
{
handleReorderFileList
(
file
.
name
,
"down"
);
}
}
disabled=
{
index
===
fileList
.
length
-
1
}
className=
"disabled:opacity-50"
>
<
Icon
.
ArrowDown
className=
"w-4 h-4"
/>
</
button
>
</
div
>
</
ListItem
>
))
}
</
List
>
<
div
className=
"mt-2 w-full flex flex-row justify-end items-center space-x-1"
>
<
Button
variant=
"plain"
color=
"neutral"
onClick=
{
handleCloseDialog
}
>
{
t
(
"common.cancel"
)
}
</
Button
>
<
Button
onClick=
{
handleConfirmBtnClick
}
loading=
{
state
.
uploadingFlag
}
disabled=
{
!
allowConfirmAction
()
}
>
{
t
(
"common.create"
)
}
</
Button
>
</
div
>
</
div
>
</>
);
};
function
showCreateResourceDialog
(
props
:
Omit
<
Props
,
"destroy"
>
)
{
generateDialog
<
Props
>
(
{
dialogName
:
"create-resource-dialog"
,
},
CreateResourceDialog
,
props
,
);
}
export
default
showCreateResourceDialog
;
web/src/components/MemoEditor/ActionButton/UploadResourceButton.tsx
0 → 100644
View file @
590b6260
import
{
IconButton
}
from
"@mui/joy"
;
import
{
useContext
,
useRef
,
useState
}
from
"react"
;
import
toast
from
"react-hot-toast"
;
import
Icon
from
"@/components/Icon"
;
import
{
useResourceStore
}
from
"@/store/v1"
;
import
{
Resource
}
from
"@/types/proto/api/v1/resource_service"
;
import
{
MemoEditorContext
}
from
"../types"
;
interface
State
{
uploadingFlag
:
boolean
;
}
const
UploadResourceButton
=
()
=>
{
const
context
=
useContext
(
MemoEditorContext
);
const
resourceStore
=
useResourceStore
();
const
[
state
,
setState
]
=
useState
<
State
>
({
uploadingFlag
:
false
,
});
const
fileInputRef
=
useRef
<
HTMLInputElement
>
(
null
);
const
handleFileInputChange
=
async
()
=>
{
if
(
!
fileInputRef
.
current
||
!
fileInputRef
.
current
.
files
||
fileInputRef
.
current
.
files
.
length
===
0
)
{
return
;
}
if
(
state
.
uploadingFlag
)
{
return
;
}
setState
((
state
)
=>
{
return
{
...
state
,
uploadingFlag
:
true
,
};
});
const
createdResourceList
:
Resource
[]
=
[];
try
{
if
(
!
fileInputRef
.
current
||
!
fileInputRef
.
current
.
files
)
{
return
;
}
const
filesOnInput
=
Array
.
from
(
fileInputRef
.
current
.
files
);
for
(
const
file
of
fileInputRef
.
current
.
files
)
{
const
fileOnInput
=
filesOnInput
.
find
((
fileOnInput
)
=>
fileOnInput
.
name
===
file
.
name
);
if
(
!
fileOnInput
)
{
continue
;
}
const
{
name
:
filename
,
size
,
type
}
=
file
;
const
buffer
=
new
Uint8Array
(
await
file
.
arrayBuffer
());
const
resource
=
await
resourceStore
.
createResource
({
resource
:
Resource
.
fromPartial
({
filename
,
size
,
type
,
content
:
buffer
,
}),
});
createdResourceList
.
push
(
resource
);
}
}
catch
(
error
:
any
)
{
console
.
error
(
error
);
toast
.
error
(
error
.
details
);
}
context
.
setResourceList
([...
context
.
resourceList
,
...
createdResourceList
]);
};
return
(
<
IconButton
size=
"sm"
>
<
Icon
.
Image
className=
"w-5 h-5 mx-auto"
/>
<
input
className=
"absolute inset-0 w-full h-full opacity-0 cursor-pointer"
ref=
{
fileInputRef
}
disabled=
{
state
.
uploadingFlag
}
onChange=
{
handleFileInputChange
}
type=
"file"
id=
"files"
multiple=
{
true
}
accept=
"*"
/>
</
IconButton
>
);
};
export
default
UploadResourceButton
;
web/src/components/MemoEditor/index.tsx
View file @
590b6260
import
{
Select
,
Option
,
Button
,
IconButton
,
Divider
}
from
"@mui/joy"
;
import
{
Select
,
Option
,
Button
,
Divider
}
from
"@mui/joy"
;
import
React
,
{
useEffect
,
useMemo
,
useRef
,
useState
}
from
"react"
;
import
React
,
{
useEffect
,
useMemo
,
useRef
,
useState
}
from
"react"
;
import
{
toast
}
from
"react-hot-toast"
;
import
{
toast
}
from
"react-hot-toast"
;
import
{
useTranslation
}
from
"react-i18next"
;
import
{
useTranslation
}
from
"react-i18next"
;
...
@@ -17,12 +17,12 @@ import { WorkspaceSettingKey } from "@/types/proto/store/workspace_setting";
...
@@ -17,12 +17,12 @@ import { WorkspaceSettingKey } from "@/types/proto/store/workspace_setting";
import
{
useTranslate
}
from
"@/utils/i18n"
;
import
{
useTranslate
}
from
"@/utils/i18n"
;
import
{
convertVisibilityFromString
,
convertVisibilityToString
}
from
"@/utils/memo"
;
import
{
convertVisibilityFromString
,
convertVisibilityToString
}
from
"@/utils/memo"
;
import
{
extractTagsFromContent
}
from
"@/utils/tag"
;
import
{
extractTagsFromContent
}
from
"@/utils/tag"
;
import
showCreateResourceDialog
from
"../CreateResourceDialog"
;
import
Icon
from
"../Icon"
;
import
Icon
from
"../Icon"
;
import
VisibilityIcon
from
"../VisibilityIcon"
;
import
VisibilityIcon
from
"../VisibilityIcon"
;
import
AddMemoRelationButton
from
"./ActionButton/AddMemoRelationButton"
;
import
AddMemoRelationButton
from
"./ActionButton/AddMemoRelationButton"
;
import
MarkdownMenu
from
"./ActionButton/MarkdownMenu"
;
import
MarkdownMenu
from
"./ActionButton/MarkdownMenu"
;
import
TagSelector
from
"./ActionButton/TagSelector"
;
import
TagSelector
from
"./ActionButton/TagSelector"
;
import
UploadResourceButton
from
"./ActionButton/UploadResourceButton"
;
import
Editor
,
{
EditorRefActions
}
from
"./Editor"
;
import
Editor
,
{
EditorRefActions
}
from
"./Editor"
;
import
RelationListView
from
"./RelationListView"
;
import
RelationListView
from
"./RelationListView"
;
import
ResourceListView
from
"./ResourceListView"
;
import
ResourceListView
from
"./ResourceListView"
;
...
@@ -176,17 +176,6 @@ const MemoEditor = (props: Props) => {
...
@@ -176,17 +176,6 @@ const MemoEditor = (props: Props) => {
}));
}));
};
};
const
handleUploadFileBtnClick
=
()
=>
{
showCreateResourceDialog
({
onConfirm
:
(
resourceList
)
=>
{
setState
((
prevState
)
=>
({
...
prevState
,
resourceList
:
[...
prevState
.
resourceList
,
...
resourceList
],
}));
},
});
};
const
handleSetResourceList
=
(
resourceList
:
Resource
[])
=>
{
const
handleSetResourceList
=
(
resourceList
:
Resource
[])
=>
{
setState
((
prevState
)
=>
({
setState
((
prevState
)
=>
({
...
prevState
,
...
prevState
,
...
@@ -397,7 +386,14 @@ const MemoEditor = (props: Props) => {
...
@@ -397,7 +386,14 @@ const MemoEditor = (props: Props) => {
return
(
return
(
<
MemoEditorContext
.
Provider
<
MemoEditorContext
.
Provider
value=
{
{
value=
{
{
resourceList
:
state
.
resourceList
,
relationList
:
state
.
relationList
,
relationList
:
state
.
relationList
,
setResourceList
:
(
resourceList
:
Resource
[])
=>
{
setState
((
prevState
)
=>
({
...
prevState
,
resourceList
,
}));
},
setRelationList
:
(
relationList
:
MemoRelation
[])
=>
{
setRelationList
:
(
relationList
:
MemoRelation
[])
=>
{
setState
((
prevState
)
=>
({
setState
((
prevState
)
=>
({
...
prevState
,
...
prevState
,
...
@@ -425,9 +421,7 @@ const MemoEditor = (props: Props) => {
...
@@ -425,9 +421,7 @@ const MemoEditor = (props: Props) => {
<
div
className=
"flex flex-row justify-start items-center opacity-80"
>
<
div
className=
"flex flex-row justify-start items-center opacity-80"
>
<
TagSelector
editorRef=
{
editorRef
}
/>
<
TagSelector
editorRef=
{
editorRef
}
/>
<
MarkdownMenu
editorRef=
{
editorRef
}
/>
<
MarkdownMenu
editorRef=
{
editorRef
}
/>
<
IconButton
size=
"sm"
onClick=
{
handleUploadFileBtnClick
}
>
<
UploadResourceButton
/>
<
Icon
.
Image
className=
"w-5 h-5 mx-auto"
/>
</
IconButton
>
<
AddMemoRelationButton
editorRef=
{
editorRef
}
/>
<
AddMemoRelationButton
editorRef=
{
editorRef
}
/>
</
div
>
</
div
>
</
div
>
</
div
>
...
...
web/src/components/MemoEditor/types/context.ts
View file @
590b6260
import
{
createContext
}
from
"react"
;
import
{
createContext
}
from
"react"
;
import
{
MemoRelation
}
from
"@/types/proto/api/v1/memo_relation_service"
;
import
{
MemoRelation
}
from
"@/types/proto/api/v1/memo_relation_service"
;
import
{
Resource
}
from
"@/types/proto/api/v1/resource_service"
;
interface
Context
{
interface
Context
{
resourceList
:
Resource
[];
relationList
:
MemoRelation
[];
relationList
:
MemoRelation
[];
setResourceList
:
(
resourceList
:
Resource
[])
=>
void
;
setRelationList
:
(
relationList
:
MemoRelation
[])
=>
void
;
setRelationList
:
(
relationList
:
MemoRelation
[])
=>
void
;
memoName
?:
string
;
memoName
?:
string
;
}
}
export
const
MemoEditorContext
=
createContext
<
Context
>
({
export
const
MemoEditorContext
=
createContext
<
Context
>
({
resourceList
:
[],
relationList
:
[],
relationList
:
[],
setResourceList
:
()
=>
{},
setRelationList
:
()
=>
{},
setRelationList
:
()
=>
{},
});
});
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