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
a533ba02
Unverified
Commit
a533ba02
authored
Nov 21, 2025
by
gitkeniwo
Committed by
GitHub
Nov 21, 2025
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
fix: add load more button and pagination to attachments page (#5258)
parent
edfbd6b0
Changes
1
Show whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
98 additions
and
55 deletions
+98
-55
Attachments.tsx
web/src/pages/Attachments.tsx
+98
-55
No files found.
web/src/pages/Attachments.tsx
View file @
a533ba02
...
@@ -3,16 +3,17 @@ import { includes } from "lodash-es";
...
@@ -3,16 +3,17 @@ import { includes } from "lodash-es";
import
{
PaperclipIcon
,
SearchIcon
}
from
"lucide-react"
;
import
{
PaperclipIcon
,
SearchIcon
}
from
"lucide-react"
;
import
{
observer
}
from
"mobx-react-lite"
;
import
{
observer
}
from
"mobx-react-lite"
;
import
{
useEffect
,
useState
}
from
"react"
;
import
{
useEffect
,
useState
}
from
"react"
;
import
{
toast
}
from
"react-hot-toast"
;
import
AttachmentIcon
from
"@/components/AttachmentIcon"
;
import
AttachmentIcon
from
"@/components/AttachmentIcon"
;
import
Empty
from
"@/components/Empty"
;
import
Empty
from
"@/components/Empty"
;
import
MobileHeader
from
"@/components/MobileHeader"
;
import
MobileHeader
from
"@/components/MobileHeader"
;
import
{
Button
}
from
"@/components/ui/button"
;
import
{
Input
}
from
"@/components/ui/input"
;
import
{
Input
}
from
"@/components/ui/input"
;
import
{
Separator
}
from
"@/components/ui/separator"
;
import
{
Separator
}
from
"@/components/ui/separator"
;
import
{
attachmentServiceClient
}
from
"@/grpcweb"
;
import
{
attachmentServiceClient
}
from
"@/grpcweb"
;
import
useLoading
from
"@/hooks/useLoading"
;
import
useLoading
from
"@/hooks/useLoading"
;
import
useResponsiveWidth
from
"@/hooks/useResponsiveWidth"
;
import
useResponsiveWidth
from
"@/hooks/useResponsiveWidth"
;
import
i18n
from
"@/i18n"
;
import
i18n
from
"@/i18n"
;
import
{
memoStore
}
from
"@/store"
;
import
{
Attachment
}
from
"@/types/proto/api/v1/attachment_service"
;
import
{
Attachment
}
from
"@/types/proto/api/v1/attachment_service"
;
import
{
useTranslate
}
from
"@/utils/i18n"
;
import
{
useTranslate
}
from
"@/utils/i18n"
;
...
@@ -42,18 +43,51 @@ const Attachments = observer(() => {
...
@@ -42,18 +43,51 @@ const Attachments = observer(() => {
searchQuery
:
""
,
searchQuery
:
""
,
});
});
const
[
attachments
,
setAttachments
]
=
useState
<
Attachment
[]
>
([]);
const
[
attachments
,
setAttachments
]
=
useState
<
Attachment
[]
>
([]);
const
[
nextPageToken
,
setNextPageToken
]
=
useState
(
""
);
const
[
isLoadingMore
,
setIsLoadingMore
]
=
useState
(
false
);
const
filteredAttachments
=
attachments
.
filter
((
attachment
)
=>
includes
(
attachment
.
filename
,
state
.
searchQuery
));
const
filteredAttachments
=
attachments
.
filter
((
attachment
)
=>
includes
(
attachment
.
filename
,
state
.
searchQuery
));
const
groupedAttachments
=
groupAttachmentsByDate
(
filteredAttachments
.
filter
((
attachment
)
=>
attachment
.
memo
));
const
groupedAttachments
=
groupAttachmentsByDate
(
filteredAttachments
.
filter
((
attachment
)
=>
attachment
.
memo
));
const
unusedAttachments
=
filteredAttachments
.
filter
((
attachment
)
=>
!
attachment
.
memo
);
const
unusedAttachments
=
filteredAttachments
.
filter
((
attachment
)
=>
!
attachment
.
memo
);
useEffect
(()
=>
{
useEffect
(()
=>
{
attachmentServiceClient
.
listAttachments
({}).
then
(({
attachments
}
)
=>
{
const
fetchInitialAttachments
=
async
(
)
=>
{
setAttachments
(
attachments
);
try
{
loadingState
.
setFinish
();
const
{
attachments
:
fetchedAttachments
,
nextPageToken
}
=
await
attachmentServiceClient
.
listAttachments
({
Promise
.
all
(
attachments
.
map
((
attachment
)
=>
(
attachment
.
memo
?
memoStore
.
getOrFetchMemoByName
(
attachment
.
memo
)
:
null
)));
pageSize
:
50
,
});
});
setAttachments
(
fetchedAttachments
);
setNextPageToken
(
nextPageToken
??
""
);
}
catch
(
error
)
{
console
.
error
(
"Failed to fetch attachments:"
,
error
);
toast
.
error
(
"Failed to load attachments. Please try again."
);
}
finally
{
loadingState
.
setFinish
();
}
};
fetchInitialAttachments
();
},
[]);
},
[]);
const
handleLoadMore
=
async
()
=>
{
if
(
!
nextPageToken
||
isLoadingMore
)
{
return
;
}
setIsLoadingMore
(
true
);
try
{
const
{
attachments
:
fetchedAttachments
,
nextPageToken
:
newPageToken
}
=
await
attachmentServiceClient
.
listAttachments
({
pageSize
:
50
,
pageToken
:
nextPageToken
,
});
setAttachments
((
prevAttachments
)
=>
[...
prevAttachments
,
...
fetchedAttachments
]);
setNextPageToken
(
newPageToken
??
""
);
}
catch
(
error
)
{
console
.
error
(
"Failed to load more attachments:"
,
error
);
toast
.
error
(
"Failed to load more attachments. Please try again."
);
}
finally
{
setIsLoadingMore
(
false
);
}
};
return
(
return
(
<
section
className=
"@container w-full max-w-5xl min-h-full flex flex-col justify-start items-center sm:pt-3 md:pt-6 pb-8"
>
<
section
className=
"@container w-full max-w-5xl min-h-full flex flex-col justify-start items-center sm:pt-3 md:pt-6 pb-8"
>
{
!
md
&&
<
MobileHeader
/>
}
{
!
md
&&
<
MobileHeader
/>
}
...
@@ -89,6 +123,7 @@ const Attachments = observer(() => {
...
@@ -89,6 +123,7 @@ const Attachments = observer(() => {
<
p
className=
"mt-4 text-muted-foreground"
>
{
t
(
"message.no-data"
)
}
</
p
>
<
p
className=
"mt-4 text-muted-foreground"
>
{
t
(
"message.no-data"
)
}
</
p
>
</
div
>
</
div
>
)
:
(
)
:
(
<>
<
div
className=
{
"w-full h-auto px-2 flex flex-col justify-start items-start gap-y-8"
}
>
<
div
className=
{
"w-full h-auto px-2 flex flex-col justify-start items-start gap-y-8"
}
>
{
Array
.
from
(
groupedAttachments
.
entries
()).
map
(([
monthStr
,
attachments
])
=>
{
{
Array
.
from
(
groupedAttachments
.
entries
()).
map
(([
monthStr
,
attachments
])
=>
{
return
(
return
(
...
@@ -144,6 +179,14 @@ const Attachments = observer(() => {
...
@@ -144,6 +179,14 @@ const Attachments = observer(() => {
</>
</>
)
}
)
}
</
div
>
</
div
>
{
nextPageToken
&&
(
<
div
className=
"w-full flex flex-row justify-center items-center mt-4"
>
<
Button
variant=
"outline"
size=
"sm"
onClick=
{
handleLoadMore
}
disabled=
{
isLoadingMore
}
>
{
isLoadingMore
?
t
(
"resource.fetching-data"
)
:
t
(
"memo.load-more"
)
}
</
Button
>
</
div
>
)
}
</>
)
}
)
}
</>
</>
)
}
)
}
...
...
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