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
c5368fe8
Unverified
Commit
c5368fe8
authored
Jan 21, 2023
by
boojack
Committed by
GitHub
Jan 21, 2023
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
chore: update resource dialog style (#982)
parent
0aaf1537
Changes
19
Show whitespace changes
Inline
Side-by-side
Showing
19 changed files
with
139 additions
and
94 deletions
+139
-94
.dockerignore
.dockerignore
+0
-1
resource.go
api/resource.go
+10
-8
resource.go
server/resource.go
+29
-1
resource.go
store/resource.go
+19
-12
CreateTagDialog.tsx
web/src/components/CreateTagDialog.tsx
+1
-0
BaseDialog.tsx
web/src/components/Dialog/BaseDialog.tsx
+2
-2
MemoEditor.tsx
web/src/components/MemoEditor.tsx
+20
-2
ResourcesDialog.tsx
web/src/components/ResourcesDialog.tsx
+15
-44
Sidebar.tsx
web/src/components/Sidebar.tsx
+4
-0
UserBanner.tsx
web/src/components/UserBanner.tsx
+1
-12
api.ts
web/src/helpers/api.ts
+6
-2
home.less
web/src/less/home.less
+1
-1
resources-dialog.less
web/src/less/resources-dialog.less
+2
-2
en.json
web/src/locales/en.json
+1
-1
zh.json
web/src/locales/zh.json
+1
-1
resource.ts
web/src/store/module/resource.ts
+13
-5
index.ts
web/src/theme/index.ts
+5
-0
resource.d.ts
web/src/types/modules/resource.d.ts
+6
-0
resource.ts
web/src/utils/resource.ts
+3
-0
No files found.
.dockerignore
View file @
c5368fe8
web/node_modules
web/node_modules
web/yarn.lock
api/resource.go
View file @
c5368fe8
...
@@ -11,6 +11,7 @@ type Resource struct {
...
@@ -11,6 +11,7 @@ type Resource struct {
// Domain specific fields
// Domain specific fields
Filename
string
`json:"filename"`
Filename
string
`json:"filename"`
Blob
[]
byte
`json:"-"`
Blob
[]
byte
`json:"-"`
ExternalLink
string
`json:"externalLink"`
Type
string
`json:"type"`
Type
string
`json:"type"`
Size
int64
`json:"size"`
Size
int64
`json:"size"`
...
@@ -24,9 +25,10 @@ type ResourceCreate struct {
...
@@ -24,9 +25,10 @@ type ResourceCreate struct {
// Domain specific fields
// Domain specific fields
Filename
string
`json:"filename"`
Filename
string
`json:"filename"`
Blob
[]
byte
`json:"blob"`
Blob
[]
byte
`json:"-"`
Type
string
`json:"type"`
ExternalLink
string
`json:"externalLink"`
Size
int64
`json:"size"`
Type
string
`json:"-"`
Size
int64
`json:"-"`
}
}
type
ResourceFind
struct
{
type
ResourceFind
struct
{
...
...
server/resource.go
View file @
c5368fe8
...
@@ -21,7 +21,7 @@ import (
...
@@ -21,7 +21,7 @@ import (
const
(
const
(
// The max file size is 32MB.
// The max file size is 32MB.
maxFileSize
=
(
32
*
8
)
<<
20
maxFileSize
=
32
<<
20
)
)
func
(
s
*
Server
)
registerResourceRoutes
(
g
*
echo
.
Group
)
{
func
(
s
*
Server
)
registerResourceRoutes
(
g
*
echo
.
Group
)
{
...
@@ -32,6 +32,34 @@ func (s *Server) registerResourceRoutes(g *echo.Group) {
...
@@ -32,6 +32,34 @@ func (s *Server) registerResourceRoutes(g *echo.Group) {
return
echo
.
NewHTTPError
(
http
.
StatusUnauthorized
,
"Missing user in session"
)
return
echo
.
NewHTTPError
(
http
.
StatusUnauthorized
,
"Missing user in session"
)
}
}
resourceCreate
:=
&
api
.
ResourceCreate
{}
if
err
:=
json
.
NewDecoder
(
c
.
Request
()
.
Body
)
.
Decode
(
resourceCreate
);
err
!=
nil
{
return
echo
.
NewHTTPError
(
http
.
StatusBadRequest
,
"Malformatted post resource request"
)
.
SetInternal
(
err
)
}
resourceCreate
.
CreatorID
=
userID
resource
,
err
:=
s
.
Store
.
CreateResource
(
ctx
,
resourceCreate
)
if
err
!=
nil
{
return
echo
.
NewHTTPError
(
http
.
StatusInternalServerError
,
"Failed to create resource"
)
.
SetInternal
(
err
)
}
if
err
:=
s
.
createResourceCreateActivity
(
c
,
resource
);
err
!=
nil
{
return
echo
.
NewHTTPError
(
http
.
StatusInternalServerError
,
"Failed to create activity"
)
.
SetInternal
(
err
)
}
c
.
Response
()
.
Header
()
.
Set
(
echo
.
HeaderContentType
,
echo
.
MIMEApplicationJSONCharsetUTF8
)
if
err
:=
json
.
NewEncoder
(
c
.
Response
()
.
Writer
)
.
Encode
(
composeResponse
(
resource
));
err
!=
nil
{
return
echo
.
NewHTTPError
(
http
.
StatusInternalServerError
,
"Failed to encode resource response"
)
.
SetInternal
(
err
)
}
return
nil
})
g
.
POST
(
"/resource/blob"
,
func
(
c
echo
.
Context
)
error
{
ctx
:=
c
.
Request
()
.
Context
()
userID
,
ok
:=
c
.
Get
(
getUserIDContextKey
())
.
(
int
)
if
!
ok
{
return
echo
.
NewHTTPError
(
http
.
StatusUnauthorized
,
"Missing user in session"
)
}
if
err
:=
c
.
Request
()
.
ParseMultipartForm
(
maxFileSize
);
err
!=
nil
{
if
err
:=
c
.
Request
()
.
ParseMultipartForm
(
maxFileSize
);
err
!=
nil
{
return
echo
.
NewHTTPError
(
http
.
StatusBadRequest
,
"Upload file overload max size"
)
.
SetInternal
(
err
)
return
echo
.
NewHTTPError
(
http
.
StatusBadRequest
,
"Upload file overload max size"
)
.
SetInternal
(
err
)
}
}
...
...
store/resource.go
View file @
c5368fe8
...
@@ -24,6 +24,7 @@ type resourceRaw struct {
...
@@ -24,6 +24,7 @@ type resourceRaw struct {
// Domain specific fields
// Domain specific fields
Filename
string
Filename
string
Blob
[]
byte
Blob
[]
byte
ExternalLink
string
Type
string
Type
string
Size
int64
Size
int64
}
}
...
@@ -40,6 +41,7 @@ func (raw *resourceRaw) toResource() *api.Resource {
...
@@ -40,6 +41,7 @@ func (raw *resourceRaw) toResource() *api.Resource {
// Domain specific fields
// Domain specific fields
Filename
:
raw
.
Filename
,
Filename
:
raw
.
Filename
,
Blob
:
raw
.
Blob
,
Blob
:
raw
.
Blob
,
ExternalLink
:
raw
.
ExternalLink
,
Type
:
raw
.
Type
,
Type
:
raw
.
Type
,
Size
:
raw
.
Size
,
Size
:
raw
.
Size
,
}
}
...
@@ -215,18 +217,20 @@ func createResource(ctx context.Context, tx *sql.Tx, create *api.ResourceCreate)
...
@@ -215,18 +217,20 @@ func createResource(ctx context.Context, tx *sql.Tx, create *api.ResourceCreate)
INSERT INTO resource (
INSERT INTO resource (
filename,
filename,
blob,
blob,
external_link,
type,
type,
size,
size,
creator_id
creator_id
)
)
VALUES (?, ?, ?, ?, ?)
VALUES (?, ?, ?, ?, ?
, ?
)
RETURNING id, filename, blob, type, size, creator_id, created_ts, updated_ts
RETURNING id, filename, blob,
external_link,
type, size, creator_id, created_ts, updated_ts
`
`
var
resourceRaw
resourceRaw
var
resourceRaw
resourceRaw
if
err
:=
tx
.
QueryRowContext
(
ctx
,
query
,
create
.
Filename
,
create
.
Blob
,
create
.
Type
,
create
.
Size
,
create
.
CreatorID
)
.
Scan
(
if
err
:=
tx
.
QueryRowContext
(
ctx
,
query
,
create
.
Filename
,
create
.
Blob
,
create
.
ExternalLink
,
create
.
Type
,
create
.
Size
,
create
.
CreatorID
)
.
Scan
(
&
resourceRaw
.
ID
,
&
resourceRaw
.
ID
,
&
resourceRaw
.
Filename
,
&
resourceRaw
.
Filename
,
&
resourceRaw
.
Blob
,
&
resourceRaw
.
Blob
,
&
resourceRaw
.
ExternalLink
,
&
resourceRaw
.
Type
,
&
resourceRaw
.
Type
,
&
resourceRaw
.
Size
,
&
resourceRaw
.
Size
,
&
resourceRaw
.
CreatorID
,
&
resourceRaw
.
CreatorID
,
...
@@ -255,13 +259,14 @@ func patchResource(ctx context.Context, tx *sql.Tx, patch *api.ResourcePatch) (*
...
@@ -255,13 +259,14 @@ func patchResource(ctx context.Context, tx *sql.Tx, patch *api.ResourcePatch) (*
UPDATE resource
UPDATE resource
SET `
+
strings
.
Join
(
set
,
", "
)
+
`
SET `
+
strings
.
Join
(
set
,
", "
)
+
`
WHERE id = ?
WHERE id = ?
RETURNING id, filename, blob, type, size, creator_id, created_ts, updated_ts
RETURNING id, filename, blob,
external_link,
type, size, creator_id, created_ts, updated_ts
`
`
var
resourceRaw
resourceRaw
var
resourceRaw
resourceRaw
if
err
:=
tx
.
QueryRowContext
(
ctx
,
query
,
args
...
)
.
Scan
(
if
err
:=
tx
.
QueryRowContext
(
ctx
,
query
,
args
...
)
.
Scan
(
&
resourceRaw
.
ID
,
&
resourceRaw
.
ID
,
&
resourceRaw
.
Filename
,
&
resourceRaw
.
Filename
,
&
resourceRaw
.
Blob
,
&
resourceRaw
.
Blob
,
&
resourceRaw
.
ExternalLink
,
&
resourceRaw
.
Type
,
&
resourceRaw
.
Type
,
&
resourceRaw
.
Size
,
&
resourceRaw
.
Size
,
&
resourceRaw
.
CreatorID
,
&
resourceRaw
.
CreatorID
,
...
@@ -295,6 +300,7 @@ func findResourceList(ctx context.Context, tx *sql.Tx, find *api.ResourceFind) (
...
@@ -295,6 +300,7 @@ func findResourceList(ctx context.Context, tx *sql.Tx, find *api.ResourceFind) (
id,
id,
filename,
filename,
blob,
blob,
external_link,
type,
type,
size,
size,
creator_id,
creator_id,
...
@@ -317,6 +323,7 @@ func findResourceList(ctx context.Context, tx *sql.Tx, find *api.ResourceFind) (
...
@@ -317,6 +323,7 @@ func findResourceList(ctx context.Context, tx *sql.Tx, find *api.ResourceFind) (
&
resourceRaw
.
ID
,
&
resourceRaw
.
ID
,
&
resourceRaw
.
Filename
,
&
resourceRaw
.
Filename
,
&
resourceRaw
.
Blob
,
&
resourceRaw
.
Blob
,
&
resourceRaw
.
ExternalLink
,
&
resourceRaw
.
Type
,
&
resourceRaw
.
Type
,
&
resourceRaw
.
Size
,
&
resourceRaw
.
Size
,
&
resourceRaw
.
CreatorID
,
&
resourceRaw
.
CreatorID
,
...
...
web/src/components/CreateTagDialog.tsx
View file @
c5368fe8
...
@@ -90,6 +90,7 @@ const CreateTagDialog: React.FC<Props> = (props: Props) => {
...
@@ -90,6 +90,7 @@ const CreateTagDialog: React.FC<Props> = (props: Props) => {
<
div
className=
"dialog-content-container !w-80"
>
<
div
className=
"dialog-content-container !w-80"
>
<
Input
<
Input
className=
"mb-2"
className=
"mb-2"
size=
"md"
placeholder=
"TAG_NAME"
placeholder=
"TAG_NAME"
value=
{
tagName
}
value=
{
tagName
}
onChange=
{
handleTagNameChanged
}
onChange=
{
handleTagNameChanged
}
...
...
web/src/components/Dialog/BaseDialog.tsx
View file @
c5368fe8
...
@@ -9,8 +9,8 @@ import theme from "../../theme";
...
@@ -9,8 +9,8 @@ import theme from "../../theme";
import
"../../less/base-dialog.less"
;
import
"../../less/base-dialog.less"
;
interface
DialogConfig
{
interface
DialogConfig
{
className
:
string
;
dialogName
:
string
;
dialogName
:
string
;
className
?:
string
;
clickSpaceDestroy
?:
boolean
;
clickSpaceDestroy
?:
boolean
;
}
}
...
@@ -55,7 +55,7 @@ const BaseDialog: React.FC<Props> = (props: Props) => {
...
@@ -55,7 +55,7 @@ const BaseDialog: React.FC<Props> = (props: Props) => {
};
};
return
(
return
(
<
div
className=
{
`dialog-wrapper ${className}`
}
onMouseDown=
{
handleSpaceClicked
}
>
<
div
className=
{
`dialog-wrapper ${className
?? ""
}`
}
onMouseDown=
{
handleSpaceClicked
}
>
<
div
ref=
{
dialogContainerRef
}
className=
"dialog-container"
onMouseDown=
{
(
e
)
=>
e
.
stopPropagation
()
}
>
<
div
ref=
{
dialogContainerRef
}
className=
"dialog-container"
onMouseDown=
{
(
e
)
=>
e
.
stopPropagation
()
}
>
{
children
}
{
children
}
</
div
>
</
div
>
...
...
web/src/components/MemoEditor.tsx
View file @
c5368fe8
...
@@ -37,6 +37,7 @@ const setEditingMemoVisibilityCache = (visibility: Visibility) => {
...
@@ -37,6 +37,7 @@ const setEditingMemoVisibilityCache = (visibility: Visibility) => {
interface
State
{
interface
State
{
fullscreen
:
boolean
;
fullscreen
:
boolean
;
isUploadingResource
:
boolean
;
isUploadingResource
:
boolean
;
isRequesting
:
boolean
;
}
}
const
MemoEditor
=
()
=>
{
const
MemoEditor
=
()
=>
{
...
@@ -51,6 +52,7 @@ const MemoEditor = () => {
...
@@ -51,6 +52,7 @@ const MemoEditor = () => {
const
[
state
,
setState
]
=
useState
<
State
>
({
const
[
state
,
setState
]
=
useState
<
State
>
({
isUploadingResource
:
false
,
isUploadingResource
:
false
,
fullscreen
:
false
,
fullscreen
:
false
,
isRequesting
:
false
,
});
});
const
[
allowSave
,
setAllowSave
]
=
useState
<
boolean
>
(
false
);
const
[
allowSave
,
setAllowSave
]
=
useState
<
boolean
>
(
false
);
const
editorState
=
editorStore
.
state
;
const
editorState
=
editorStore
.
state
;
...
@@ -280,7 +282,7 @@ const MemoEditor = () => {
...
@@ -280,7 +282,7 @@ const MemoEditor = () => {
let
resource
=
undefined
;
let
resource
=
undefined
;
try
{
try
{
resource
=
await
resourceStore
.
upload
(
file
);
resource
=
await
resourceStore
.
createResourceWithBlob
(
file
);
}
catch
(
error
:
any
)
{
}
catch
(
error
:
any
)
{
console
.
error
(
error
);
console
.
error
(
error
);
toastHelper
.
error
(
error
.
response
.
data
.
message
);
toastHelper
.
error
(
error
.
response
.
data
.
message
);
...
@@ -305,6 +307,16 @@ const MemoEditor = () => {
...
@@ -305,6 +307,16 @@ const MemoEditor = () => {
},
[
editorState
.
editMemoId
]);
},
[
editorState
.
editMemoId
]);
const
handleSaveBtnClick
=
async
()
=>
{
const
handleSaveBtnClick
=
async
()
=>
{
if
(
state
.
isRequesting
)
{
return
;
}
setState
((
state
)
=>
{
return
{
...
state
,
isRequesting
:
true
,
};
});
const
content
=
editorRef
.
current
?.
getContent
()
??
""
;
const
content
=
editorRef
.
current
?.
getContent
()
??
""
;
try
{
try
{
const
{
editMemoId
}
=
editorStore
.
getState
();
const
{
editMemoId
}
=
editorStore
.
getState
();
...
@@ -332,6 +344,12 @@ const MemoEditor = () => {
...
@@ -332,6 +344,12 @@ const MemoEditor = () => {
console
.
error
(
error
);
console
.
error
(
error
);
toastHelper
.
error
(
error
.
response
.
data
.
message
);
toastHelper
.
error
(
error
.
response
.
data
.
message
);
}
}
setState
((
state
)
=>
{
return
{
...
state
,
isRequesting
:
false
,
};
});
// Upsert tag with the content.
// Upsert tag with the content.
const
matchedNodes
=
getMatchedNodes
(
content
);
const
matchedNodes
=
getMatchedNodes
(
content
);
...
@@ -561,7 +579,7 @@ const MemoEditor = () => {
...
@@ -561,7 +579,7 @@ const MemoEditor = () => {
</
button
>
</
button
>
<
button
<
button
className=
"action-btn confirm-btn"
className=
"action-btn confirm-btn"
disabled=
{
!
(
allowSave
||
editorState
.
resourceList
.
length
>
0
)
||
state
.
isUploadingResource
}
disabled=
{
!
(
allowSave
||
editorState
.
resourceList
.
length
>
0
)
||
state
.
isUploadingResource
||
state
.
isRequesting
}
onClick=
{
handleSaveBtnClick
}
onClick=
{
handleSaveBtnClick
}
>
>
{
t
(
"editor.save"
)
}
{
t
(
"editor.save"
)
}
...
...
web/src/components/ResourcesDialog.tsx
View file @
c5368fe8
import
{
Tooltip
}
from
"@mui/joy"
;
import
{
Button
}
from
"@mui/joy"
;
import
copy
from
"copy-to-clipboard"
;
import
copy
from
"copy-to-clipboard"
;
import
{
useEffect
,
useState
}
from
"react"
;
import
{
useEffect
}
from
"react"
;
import
{
useTranslation
}
from
"react-i18next"
;
import
{
useTranslation
}
from
"react-i18next"
;
import
useLoading
from
"../hooks/useLoading"
;
import
useLoading
from
"../hooks/useLoading"
;
import
{
useResourceStore
}
from
"../store/module"
;
import
{
useResourceStore
}
from
"../store/module"
;
...
@@ -16,19 +16,12 @@ import "../less/resources-dialog.less";
...
@@ -16,19 +16,12 @@ import "../less/resources-dialog.less";
type
Props
=
DialogProps
;
type
Props
=
DialogProps
;
interface
State
{
isUploadingResource
:
boolean
;
}
const
ResourcesDialog
:
React
.
FC
<
Props
>
=
(
props
:
Props
)
=>
{
const
ResourcesDialog
:
React
.
FC
<
Props
>
=
(
props
:
Props
)
=>
{
const
{
destroy
}
=
props
;
const
{
destroy
}
=
props
;
const
{
t
}
=
useTranslation
();
const
{
t
}
=
useTranslation
();
const
loadingState
=
useLoading
();
const
loadingState
=
useLoading
();
const
resourceStore
=
useResourceStore
();
const
resourceStore
=
useResourceStore
();
const
resources
=
resourceStore
.
state
.
resources
;
const
resources
=
resourceStore
.
state
.
resources
;
const
[
state
,
setState
]
=
useState
<
State
>
({
isUploadingResource
:
false
,
});
useEffect
(()
=>
{
useEffect
(()
=>
{
resourceStore
resourceStore
...
@@ -43,10 +36,6 @@ const ResourcesDialog: React.FC<Props> = (props: Props) => {
...
@@ -43,10 +36,6 @@ const ResourcesDialog: React.FC<Props> = (props: Props) => {
},
[]);
},
[]);
const
handleUploadFileBtnClick
=
async
()
=>
{
const
handleUploadFileBtnClick
=
async
()
=>
{
if
(
state
.
isUploadingResource
)
{
return
;
}
const
inputEl
=
document
.
createElement
(
"input"
);
const
inputEl
=
document
.
createElement
(
"input"
);
inputEl
.
style
.
position
=
"fixed"
;
inputEl
.
style
.
position
=
"fixed"
;
inputEl
.
style
.
top
=
"-100vh"
;
inputEl
.
style
.
top
=
"-100vh"
;
...
@@ -60,22 +49,12 @@ const ResourcesDialog: React.FC<Props> = (props: Props) => {
...
@@ -60,22 +49,12 @@ const ResourcesDialog: React.FC<Props> = (props: Props) => {
return
;
return
;
}
}
setState
({
...
state
,
isUploadingResource
:
true
,
});
for
(
const
file
of
inputEl
.
files
)
{
for
(
const
file
of
inputEl
.
files
)
{
try
{
try
{
await
resourceStore
.
upload
(
file
);
await
resourceStore
.
createResourceWithBlob
(
file
);
}
catch
(
error
:
any
)
{
}
catch
(
error
:
any
)
{
console
.
error
(
error
);
console
.
error
(
error
);
toastHelper
.
error
(
error
.
response
.
data
.
message
);
toastHelper
.
error
(
error
.
response
.
data
.
message
);
}
finally
{
setState
({
...
state
,
isUploadingResource
:
false
,
});
}
}
}
}
...
@@ -158,18 +137,16 @@ const ResourcesDialog: React.FC<Props> = (props: Props) => {
...
@@ -158,18 +137,16 @@ const ResourcesDialog: React.FC<Props> = (props: Props) => {
</
button
>
</
button
>
</
div
>
</
div
>
<
div
className=
"dialog-content-container"
>
<
div
className=
"dialog-content-container"
>
<
div
className=
"action-buttons-container"
>
<
div
className=
"w-full flex flex-row justify-between items-center"
>
<
div
className=
"buttons-wrapper"
>
<
div
className=
"flex flex-row justify-start items-center space-x-2"
>
<
div
className=
"upload-resource-btn"
onClick=
{
()
=>
handleUploadFileBtnClick
()
}
>
<
Button
onClick=
{
()
=>
handleUploadFileBtnClick
()
}
startDecorator=
{
<
Icon
.
Plus
className=
"w-5 h-auto"
/>
}
>
<
Icon
.
File
className=
"icon-img"
/>
{
t
(
"common.create"
)
}
<
span
>
{
t
(
"resources.upload"
)
}
</
span
>
</
Button
>
</
div
>
</
div
>
<
div
className=
"buttons-wrapper"
>
<
div
className=
"delete-unused-resource-btn"
onClick=
{
handleDeleteUnusedResourcesBtnClick
}
>
<
Icon
.
Trash2
className=
"icon-img"
/>
<
span
>
{
t
(
"resources.clear-unused-resources"
)
}
</
span
>
</
div
>
</
div
>
<
div
className=
"flex flex-row justify-end items-center"
>
<
Button
color=
"danger"
onClick=
{
handleDeleteUnusedResourcesBtnClick
}
startDecorator=
{
<
Icon
.
Trash2
className=
"w-4 h-auto"
/>
}
>
<
span
>
{
t
(
"resources.clear"
)
}
</
span
>
</
Button
>
</
div
>
</
div
>
</
div
>
</
div
>
{
loadingState
.
isLoading
?
(
{
loadingState
.
isLoading
?
(
...
@@ -189,9 +166,9 @@ const ResourcesDialog: React.FC<Props> = (props: Props) => {
...
@@ -189,9 +166,9 @@ const ResourcesDialog: React.FC<Props> = (props: Props) => {
resources
.
map
((
resource
)
=>
(
resources
.
map
((
resource
)
=>
(
<
div
key=
{
resource
.
id
}
className=
"resource-container"
>
<
div
key=
{
resource
.
id
}
className=
"resource-container"
>
<
span
className=
"field-text id-text"
>
{
resource
.
id
}
</
span
>
<
span
className=
"field-text id-text"
>
{
resource
.
id
}
</
span
>
<
Tooltip
title=
{
resource
.
filename
}
>
<
span
className=
"field-text name-text"
onClick=
{
()
=>
handleRenameBtnClick
(
resource
)
}
>
<
span
className=
"field-text name-text"
>
{
resource
.
filename
}
</
span
>
{
resource
.
filename
}
</
Tooltip
>
</
span
>
<
div
className=
"buttons-container"
>
<
div
className=
"buttons-container"
>
<
Dropdown
<
Dropdown
actionsClassName=
"!w-28"
actionsClassName=
"!w-28"
...
@@ -203,12 +180,6 @@ const ResourcesDialog: React.FC<Props> = (props: Props) => {
...
@@ -203,12 +180,6 @@ const ResourcesDialog: React.FC<Props> = (props: Props) => {
>
>
{
t
(
"resources.preview"
)
}
{
t
(
"resources.preview"
)
}
</
button
>
</
button
>
<
button
className=
"w-full text-left text-sm leading-6 py-1 px-3 cursor-pointer rounded hover:bg-gray-100 dark:hover:bg-zinc-600"
onClick=
{
()
=>
handleRenameBtnClick
(
resource
)
}
>
{
t
(
"resources.rename"
)
}
</
button
>
<
button
<
button
className=
"w-full text-left text-sm leading-6 py-1 px-3 cursor-pointer rounded hover:bg-gray-100 dark:hover:bg-zinc-600"
className=
"w-full text-left text-sm leading-6 py-1 px-3 cursor-pointer rounded hover:bg-gray-100 dark:hover:bg-zinc-600"
onClick=
{
()
=>
handleCopyResourceLinkBtnClick
(
resource
)
}
onClick=
{
()
=>
handleCopyResourceLinkBtnClick
(
resource
)
}
...
...
web/src/components/Sidebar.tsx
View file @
c5368fe8
...
@@ -4,6 +4,7 @@ import { Link } from "react-router-dom";
...
@@ -4,6 +4,7 @@ import { Link } from "react-router-dom";
import
{
useTranslation
}
from
"react-i18next"
;
import
{
useTranslation
}
from
"react-i18next"
;
import
{
useLocationStore
,
useUserStore
}
from
"../store/module"
;
import
{
useLocationStore
,
useUserStore
}
from
"../store/module"
;
import
showDailyReviewDialog
from
"./DailyReviewDialog"
;
import
showDailyReviewDialog
from
"./DailyReviewDialog"
;
import
showResourcesDialog
from
"./ResourcesDialog"
;
import
showSettingDialog
from
"./SettingDialog"
;
import
showSettingDialog
from
"./SettingDialog"
;
import
UserBanner
from
"./UserBanner"
;
import
UserBanner
from
"./UserBanner"
;
import
UsageHeatMap
from
"./UsageHeatMap"
;
import
UsageHeatMap
from
"./UsageHeatMap"
;
...
@@ -38,6 +39,9 @@ const Sidebar = () => {
...
@@ -38,6 +39,9 @@ const Sidebar = () => {
<
Link
to=
"/explore"
className=
"btn action-btn"
>
<
Link
to=
"/explore"
className=
"btn action-btn"
>
<
span
className=
"icon"
>
🏂
</
span
>
{
t
(
"common.explore"
)
}
<
span
className=
"icon"
>
🏂
</
span
>
{
t
(
"common.explore"
)
}
</
Link
>
</
Link
>
<
button
className=
"btn action-btn"
onClick=
{
()
=>
showResourcesDialog
()
}
>
<
span
className=
"icon"
>
🗂️
</
span
>
{
t
(
"sidebar.resources"
)
}
</
button
>
{
!
userStore
.
isVisitorMode
()
&&
(
{
!
userStore
.
isVisitorMode
()
&&
(
<>
<>
<
button
className=
"btn action-btn"
onClick=
{
handleSettingBtnClick
}
>
<
button
className=
"btn action-btn"
onClick=
{
handleSettingBtnClick
}
>
...
...
web/src/components/UserBanner.tsx
View file @
c5368fe8
...
@@ -5,7 +5,6 @@ import { getMemoStats } from "../helpers/api";
...
@@ -5,7 +5,6 @@ import { getMemoStats } from "../helpers/api";
import
*
as
utils
from
"../helpers/utils"
;
import
*
as
utils
from
"../helpers/utils"
;
import
Icon
from
"./Icon"
;
import
Icon
from
"./Icon"
;
import
Dropdown
from
"./common/Dropdown"
;
import
Dropdown
from
"./common/Dropdown"
;
import
showResourcesDialog
from
"./ResourcesDialog"
;
import
showArchivedMemoDialog
from
"./ArchivedMemoDialog"
;
import
showArchivedMemoDialog
from
"./ArchivedMemoDialog"
;
import
showAboutSiteDialog
from
"./AboutSiteDialog"
;
import
showAboutSiteDialog
from
"./AboutSiteDialog"
;
import
"../less/user-banner.less"
;
import
"../less/user-banner.less"
;
...
@@ -51,10 +50,6 @@ const UserBanner = () => {
...
@@ -51,10 +50,6 @@ const UserBanner = () => {
locationStore
.
clearQuery
();
locationStore
.
clearQuery
();
},
[]);
},
[]);
const
handleResourcesBtnClick
=
()
=>
{
showResourcesDialog
();
};
const
handleArchivedBtnClick
=
()
=>
{
const
handleArchivedBtnClick
=
()
=>
{
showArchivedMemoDialog
();
showArchivedMemoDialog
();
};
};
...
@@ -82,17 +77,11 @@ const UserBanner = () => {
...
@@ -82,17 +77,11 @@ const UserBanner = () => {
<>
<>
{
!
userStore
.
isVisitorMode
()
&&
(
{
!
userStore
.
isVisitorMode
()
&&
(
<>
<>
<
button
className=
"w-full px-3 whitespace-nowrap text-left leading-10 cursor-pointer rounded dark:text-gray-200 hover:bg-gray-100 dark:hover:bg-zinc-800"
onClick=
{
handleResourcesBtnClick
}
>
<
span
className=
"mr-1"
>
🌄
</
span
>
{
t
(
"sidebar.resources"
)
}
</
button
>
<
button
<
button
className=
"w-full px-3 whitespace-nowrap text-left leading-10 cursor-pointer rounded dark:text-gray-200 hover:bg-gray-100 dark:hover:bg-zinc-800"
className=
"w-full px-3 whitespace-nowrap text-left leading-10 cursor-pointer rounded dark:text-gray-200 hover:bg-gray-100 dark:hover:bg-zinc-800"
onClick=
{
handleArchivedBtnClick
}
onClick=
{
handleArchivedBtnClick
}
>
>
<
span
className=
"mr-1"
>
🗂
</
span
>
{
t
(
"sidebar.archived"
)
}
<
span
className=
"mr-1"
>
🗃️
</
span
>
{
t
(
"sidebar.archived"
)
}
</
button
>
</
button
>
</>
</>
)
}
)
}
...
...
web/src/helpers/api.ts
View file @
c5368fe8
...
@@ -153,8 +153,12 @@ export function getResourceList() {
...
@@ -153,8 +153,12 @@ export function getResourceList() {
return
axios
.
get
<
ResponseObject
<
Resource
[]
>>
(
"/api/resource"
);
return
axios
.
get
<
ResponseObject
<
Resource
[]
>>
(
"/api/resource"
);
}
}
export
function
uploadFile
(
formData
:
FormData
)
{
export
function
createResource
(
resourceCreate
:
ResourceCreate
)
{
return
axios
.
post
<
ResponseObject
<
Resource
>>
(
"/api/resource"
,
formData
);
return
axios
.
post
<
ResponseObject
<
Resource
>>
(
"/api/resource"
,
resourceCreate
);
}
export
function
createResourceWithBlob
(
formData
:
FormData
)
{
return
axios
.
post
<
ResponseObject
<
Resource
>>
(
"/api/resource/blob"
,
formData
);
}
}
export
function
deleteResourceById
(
id
:
ResourceId
)
{
export
function
deleteResourceById
(
id
:
ResourceId
)
{
...
...
web/src/less/home.less
View file @
c5368fe8
...
@@ -9,7 +9,7 @@
...
@@ -9,7 +9,7 @@
@apply relative w-full h-auto mx-auto flex flex-row justify-start sm:justify-center items-start;
@apply relative w-full h-auto mx-auto flex flex-row justify-start sm:justify-center items-start;
> .sidebar-wrapper {
> .sidebar-wrapper {
@apply flex-shrink-0 ml-calc;
@apply flex-shrink-0
h-full
ml-calc;
}
}
> .memos-wrapper {
> .memos-wrapper {
...
...
web/src/less/resources-dialog.less
View file @
c5368fe8
...
@@ -62,11 +62,11 @@
...
@@ -62,11 +62,11 @@
@apply w-full truncate text-base pr-2 last:pr-0;
@apply w-full truncate text-base pr-2 last:pr-0;
&.id-text {
&.id-text {
@apply col-span-
2
;
@apply col-span-
1
;
}
}
&.name-text {
&.name-text {
@apply col-span-
4
;
@apply col-span-
5
;
}
}
}
}
}
}
...
...
web/src/locales/en.json
View file @
c5368fe8
...
@@ -75,7 +75,7 @@
...
@@ -75,7 +75,7 @@
"warning-text"
:
"Are you sure to delete this resource? THIS ACTION IS IRREVERSIABLE❗"
,
"warning-text"
:
"Are you sure to delete this resource? THIS ACTION IS IRREVERSIABLE❗"
,
"linked-amount"
:
"Linked memo amount"
,
"linked-amount"
:
"Linked memo amount"
,
"rename"
:
"Rename"
,
"rename"
:
"Rename"
,
"clear
-unused-resources"
:
"Clear unused resources
"
,
"clear
"
:
"Clear
"
,
"warning-text-unused"
:
"Are you sure to delete these unused resource? THIS ACTION IS IRREVERSIABLE❗"
,
"warning-text-unused"
:
"Are you sure to delete these unused resource? THIS ACTION IS IRREVERSIABLE❗"
,
"no-unused-resources"
:
"No unused resources"
,
"no-unused-resources"
:
"No unused resources"
,
"name"
:
"Name"
"name"
:
"Name"
...
...
web/src/locales/zh.json
View file @
c5368fe8
...
@@ -75,7 +75,7 @@
...
@@ -75,7 +75,7 @@
"warning-text"
:
"确定删除这个资源么?此操作不可逆❗"
,
"warning-text"
:
"确定删除这个资源么?此操作不可逆❗"
,
"linked-amount"
:
"链接的 Memo 数量"
,
"linked-amount"
:
"链接的 Memo 数量"
,
"rename"
:
"重命名"
,
"rename"
:
"重命名"
,
"clear
-unused-resources"
:
"清除无用资源
"
,
"clear
"
:
"清除
"
,
"warning-text-unused"
:
"确定删除这些无用资源么?此操作不可逆❗"
,
"warning-text-unused"
:
"确定删除这些无用资源么?此操作不可逆❗"
,
"no-unused-resources"
:
"无可删除的资源"
,
"no-unused-resources"
:
"无可删除的资源"
,
"name"
:
"资源名称"
"name"
:
"资源名称"
...
...
web/src/store/module/resource.ts
View file @
c5368fe8
...
@@ -2,6 +2,8 @@ import store, { useAppSelector } from "../";
...
@@ -2,6 +2,8 @@ import store, { useAppSelector } from "../";
import
{
patchResource
,
setResources
,
deleteResource
}
from
"../reducer/resource"
;
import
{
patchResource
,
setResources
,
deleteResource
}
from
"../reducer/resource"
;
import
*
as
api
from
"../../helpers/api"
;
import
*
as
api
from
"../../helpers/api"
;
const
MAX_FILE_SIZE
=
32
<<
20
;
const
convertResponseModelResource
=
(
resource
:
Resource
):
Resource
=>
{
const
convertResponseModelResource
=
(
resource
:
Resource
):
Resource
=>
{
return
{
return
{
...
resource
,
...
resource
,
...
@@ -24,16 +26,22 @@ export const useResourceStore = () => {
...
@@ -24,16 +26,22 @@ export const useResourceStore = () => {
store
.
dispatch
(
setResources
(
resourceList
));
store
.
dispatch
(
setResources
(
resourceList
));
return
resourceList
;
return
resourceList
;
},
},
async
upload
(
file
:
File
):
Promise
<
Resource
>
{
async
createResource
(
resourceCreate
:
ResourceCreate
):
Promise
<
Resource
>
{
const
{
data
}
=
(
await
api
.
createResource
(
resourceCreate
)).
data
;
const
resource
=
convertResponseModelResource
(
data
);
const
resourceList
=
state
.
resources
;
store
.
dispatch
(
setResources
([
resource
,
...
resourceList
]));
return
resource
;
},
async
createResourceWithBlob
(
file
:
File
):
Promise
<
Resource
>
{
const
{
name
:
filename
,
size
}
=
file
;
const
{
name
:
filename
,
size
}
=
file
;
if
(
size
>
MAX_FILE_SIZE
)
{
if
(
size
>
64
<<
20
)
{
return
Promise
.
reject
(
"overload max size: 32MB"
);
return
Promise
.
reject
(
"overload max size: 8MB"
);
}
}
const
formData
=
new
FormData
();
const
formData
=
new
FormData
();
formData
.
append
(
"file"
,
file
,
filename
);
formData
.
append
(
"file"
,
file
,
filename
);
const
{
data
}
=
(
await
api
.
uploadFile
(
formData
)).
data
;
const
{
data
}
=
(
await
api
.
createResourceWithBlob
(
formData
)).
data
;
const
resource
=
convertResponseModelResource
(
data
);
const
resource
=
convertResponseModelResource
(
data
);
const
resourceList
=
state
.
resources
;
const
resourceList
=
state
.
resources
;
store
.
dispatch
(
setResources
([
resource
,
...
resourceList
]));
store
.
dispatch
(
setResources
([
resource
,
...
resourceList
]));
...
...
web/src/theme/index.ts
View file @
c5368fe8
...
@@ -7,6 +7,11 @@ const theme = extendTheme({
...
@@ -7,6 +7,11 @@ const theme = extendTheme({
size
:
"sm"
,
size
:
"sm"
,
},
},
},
},
JoyInput
:
{
defaultProps
:
{
size
:
"sm"
,
},
},
JoySelect
:
{
JoySelect
:
{
defaultProps
:
{
defaultProps
:
{
size
:
"sm"
,
size
:
"sm"
,
...
...
web/src/types/modules/resource.d.ts
View file @
c5368fe8
...
@@ -7,12 +7,18 @@ interface Resource {
...
@@ -7,12 +7,18 @@ interface Resource {
updatedTs
:
TimeStamp
;
updatedTs
:
TimeStamp
;
filename
:
string
;
filename
:
string
;
externalLink
:
string
;
type
:
string
;
type
:
string
;
size
:
string
;
size
:
string
;
linkedMemoAmount
:
number
;
linkedMemoAmount
:
number
;
}
}
interface
ResourceCreate
{
filename
:
string
;
externalLink
:
string
;
}
interface
ResourcePatch
{
interface
ResourcePatch
{
id
:
ResourceId
;
id
:
ResourceId
;
filename
?:
string
;
filename
?:
string
;
...
...
web/src/utils/resource.ts
View file @
c5368fe8
export
const
getResourceUrl
=
(
resource
:
Resource
,
withOrigin
=
true
)
=>
{
export
const
getResourceUrl
=
(
resource
:
Resource
,
withOrigin
=
true
)
=>
{
if
(
resource
.
externalLink
)
{
return
resource
.
externalLink
;
}
return
`
${
withOrigin
?
window
.
location
.
origin
:
""
}
/o/r/
${
resource
.
id
}
/
${
encodeURI
(
resource
.
filename
)}
`
;
return
`
${
withOrigin
?
window
.
location
.
origin
:
""
}
/o/r/
${
resource
.
id
}
/
${
encodeURI
(
resource
.
filename
)}
`
;
};
};
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