diff --git a/server/router/api/v1/attachment_service.go b/server/router/api/v1/attachment_service.go index ab5149237..a322b2a8b 100644 --- a/server/router/api/v1/attachment_service.go +++ b/server/router/api/v1/attachment_service.go @@ -153,6 +153,11 @@ func (s *APIV1Service) ListAttachments(ctx context.Context, request *v1pb.ListAt Offset: &offset, } + if request.Filter != "" { + filter := strings.TrimSpace(request.Filter) + findAttachment.FilenameSearch = &filter + } + attachments, err := s.Store.ListAttachments(ctx, findAttachment) if err != nil { return nil, status.Errorf(codes.Internal, "failed to list attachments: %v", err) diff --git a/web/src/pages/Attachments.tsx b/web/src/pages/Attachments.tsx index be334a7de..6b6edb039 100644 --- a/web/src/pages/Attachments.tsx +++ b/web/src/pages/Attachments.tsx @@ -42,12 +42,6 @@ const groupAttachmentsByDate = (attachments: Attachment[]): Map { - if (!searchQuery.trim()) return attachments; - const query = searchQuery.toLowerCase(); - return attachments.filter((attachment) => attachment.filename.toLowerCase().includes(query)); -}; - interface AttachmentItemProps { attachment: Attachment; } @@ -80,20 +74,18 @@ const Attachments = observer(() => { const [isLoadingMore, setIsLoadingMore] = useState(false); // Memoized computed values - const filteredAttachments = useMemo(() => filterAttachments(attachments, searchQuery), [attachments, searchQuery]); - - const usedAttachments = useMemo(() => filteredAttachments.filter((attachment) => attachment.memo), [filteredAttachments]); - - const unusedAttachments = useMemo(() => filteredAttachments.filter((attachment) => !attachment.memo), [filteredAttachments]); - + const usedAttachments = useMemo(() => attachments.filter((attachment) => attachment.memo), [attachments]); + const unusedAttachments = useMemo(() => attachments.filter((attachment) => !attachment.memo), [attachments]); const groupedAttachments = useMemo(() => groupAttachmentsByDate(usedAttachments), [usedAttachments]); - // Fetch initial attachments + // Fetch attachments with search filter useEffect(() => { - const fetchInitialAttachments = async () => { + const fetchAttachments = async () => { + loadingState.setLoading(); try { const { attachments: fetchedAttachments, nextPageToken } = await attachmentServiceClient.listAttachments({ pageSize: PAGE_SIZE, + filter: searchQuery.trim(), }); setAttachments(fetchedAttachments); setNextPageToken(nextPageToken ?? ""); @@ -105,9 +97,8 @@ const Attachments = observer(() => { } }; - fetchInitialAttachments(); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); + fetchAttachments(); + }, [searchQuery]); // Load more attachments with pagination const handleLoadMore = useCallback(async () => { @@ -118,6 +109,7 @@ const Attachments = observer(() => { const { attachments: fetchedAttachments, nextPageToken: newPageToken } = await attachmentServiceClient.listAttachments({ pageSize: PAGE_SIZE, pageToken: nextPageToken, + filter: searchQuery.trim(), }); setAttachments((prev) => [...prev, ...fetchedAttachments]); setNextPageToken(newPageToken ?? ""); @@ -127,7 +119,7 @@ const Attachments = observer(() => { } finally { setIsLoadingMore(false); } - }, [nextPageToken, isLoadingMore]); + }, [searchQuery, nextPageToken, isLoadingMore]); // Refetch all attachments from the beginning const handleRefetch = useCallback(async () => { @@ -135,6 +127,7 @@ const Attachments = observer(() => { loadingState.setLoading(); const { attachments: fetchedAttachments, nextPageToken } = await attachmentServiceClient.listAttachments({ pageSize: PAGE_SIZE, + filter: searchQuery.trim(), }); setAttachments(fetchedAttachments); setNextPageToken(nextPageToken ?? ""); @@ -144,7 +137,7 @@ const Attachments = observer(() => { loadingState.setError(); toast.error("Failed to refresh attachments. Please try again."); } - }, [loadingState]); + }, [searchQuery, loadingState]); // Delete all unused attachments const handleDeleteUnusedAttachments = useCallback(async () => { @@ -188,7 +181,7 @@ const Attachments = observer(() => { ) : ( <> - {filteredAttachments.length === 0 ? ( + {attachments.length === 0 ? (

{t("message.no-data")}