Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 62 additions & 22 deletions components/object/list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,17 @@ import { normalizeDateToIso } from "@/lib/safe-date"
import { buildBucketPath } from "@/lib/bucket-path"
import {
createObjectListScope,
resolveObjectListAutoSearchState,
resolveObjectListDisplayState,
resolveObjectListLoadButtonMode,
shouldApplyObjectListResponse,
shouldResetObjectListPagination,
} from "@/lib/object-list-state"
import { OBJECT_LIST_DEFAULT_PAGE_SIZE, resolveObjectListPageSize } from "@/lib/object-list-pagination"
import {
OBJECT_LIST_AUTO_SEARCH_DEBOUNCE_MS,
OBJECT_LIST_DEFAULT_PAGE_SIZE,
resolveObjectListPageSize,
} from "@/lib/object-list-pagination"
import {
resolveBucketVersioningState,
shouldForceDeleteObjects,
Expand Down Expand Up @@ -136,6 +142,7 @@ export function ObjectList({
const tasks = useTasks()

const [searchTerm, setSearchTerm] = React.useState("")
const [autoSearchStopped, setAutoSearchStopped] = React.useState(false)
const [showDeleted, setShowDeleted] = useLocalStorage("object-list-show-deleted", false)
const [loading, setLoading] = React.useState(false)
const [listError, setListError] = React.useState<ObjectListError | null>(null)
Expand Down Expand Up @@ -387,23 +394,24 @@ export function ObjectList({
return item.Key !== prefix
})
}, [data, searchTerm, prefix, displayKey])
const hasSearchTerm = Boolean(searchTerm.trim())
const hasMore = Boolean(nextToken)
const { isAutoSearching, canResumeSearch } = resolveObjectListAutoSearchState(
hasSearchTerm,
hasMore,
autoSearchStopped,
)
const displayState = resolveObjectListDisplayState({
searchTerm,
filteredCount: filteredData.length,
loadedCount: data.length,
hasMore: Boolean(nextToken),
hasMore: hasMore && !autoSearchStopped,
loading,
})
const filteredEmptyState = displayState === "filtered-partial" || displayState === "filtered-empty"
const emptyTitle = filteredEmptyState
? t(displayState === "filtered-partial" ? "No matches in loaded objects" : "No matching objects")
: t("No Objects")
const filteredEmptyState = displayState === "filtered-empty"
const emptyTitle = filteredEmptyState ? t("No matching objects") : t("No Objects")
const emptyDescription = filteredEmptyState
? t(
displayState === "filtered-partial"
? "More objects have not been searched yet."
: "No loaded objects match this filter.",
)
? t("No loaded objects match this filter.")
: t("Upload files or create folders to populate this bucket.")

const downloadFile = React.useCallback(
Expand Down Expand Up @@ -765,6 +773,24 @@ export function ObjectList({
void fetchObjects({ token: nextToken, append: true })
}, [fetchObjects, nextToken])

React.useEffect(() => {
setAutoSearchStopped(false)
}, [searchTerm])

React.useEffect(() => {
const term = searchTerm.trim()
if (!term) return
if (autoSearchStopped) return
if (!nextToken || loading) return

const timer = window.setTimeout(() => {
if (searchTerm.trim() !== term || autoSearchStopped || loadingRef.current || !nextToken) return
loadNextBatch()
}, OBJECT_LIST_AUTO_SEARCH_DEBOUNCE_MS)

return () => window.clearTimeout(timer)
}, [searchTerm, nextToken, loadNextBatch, loading, autoSearchStopped])

React.useEffect(() => {
const node = loadMoreRef.current
if (!node || !nextToken || typeof IntersectionObserver === "undefined") return
Expand All @@ -785,6 +811,22 @@ export function ObjectList({
}
}, [loadNextBatch, nextToken])

const loadButtonMode = resolveObjectListLoadButtonMode({ isAutoSearching, canResumeSearch, loading, hasMore })
const loadButtonLabel = {
stop: t("Stop search"),
resume: t("Resume search"),
loading: t("Loading more objects"),
load: t("Load next objects"),
done: t("All objects loaded"),
}[loadButtonMode]
const loadButtonAction =
loadButtonMode === "stop"
? () => setAutoSearchStopped(true)
: loadButtonMode === "resume"
? () => setAutoSearchStopped(false)
: loadNextBatch
const loadButtonDisabled = loadButtonMode === "loading" || loadButtonMode === "done"

return (
<div className="space-y-6">
<PageHeader
Expand Down Expand Up @@ -824,14 +866,12 @@ export function ObjectList({
<Button
type="button"
variant="outline"
onClick={loadNextBatch}
aria-disabled={!nextToken || loading}
onClick={loadButtonAction}
aria-disabled={loadButtonDisabled}
className="aria-disabled:pointer-events-none aria-disabled:opacity-50"
>
{loading ? <Spinner className="size-4" aria-hidden /> : null}
<span>
{loading ? t("Loading more objects") : nextToken ? t("Load next objects") : t("All objects loaded")}
</span>
{loading || isAutoSearching ? <Spinner className="size-4" aria-hidden /> : null}
<span>{loadButtonLabel}</span>
</Button>
) : null}
<Button variant="outline" onClick={() => (onRefresh ? onRefresh() : resetAndFetchObjects())}>
Expand All @@ -845,7 +885,7 @@ export function ObjectList({
<SearchInput
value={searchTerm}
onChange={setSearchTerm}
placeholder={t("Filter loaded objects")}
placeholder={t("Search objects")}
clearable
className="lg:max-w-sm"
/>
Expand Down Expand Up @@ -898,7 +938,7 @@ export function ObjectList({
<span className="text-destructive">
{t("Failed to load more objects. Loaded objects remain available.")}
</span>
) : loading && nextToken ? (
) : (loading || isAutoSearching) && nextToken ? (
<span className="inline-flex items-center gap-2">
<Spinner className="size-4" />
{t("Loading more objects")}
Expand All @@ -910,11 +950,11 @@ export function ObjectList({
type="button"
variant="outline"
size="sm"
onClick={loadNextBatch}
aria-disabled={!nextToken || loading}
onClick={loadMoreError ? loadNextBatch : loadButtonAction}
aria-disabled={!loadMoreError && loadButtonDisabled}
className="aria-disabled:pointer-events-none aria-disabled:opacity-50"
>
{loadMoreError ? t("Retry") : nextToken ? t("Load next objects") : t("All objects loaded")}
{loadMoreError ? t("Retry") : loadButtonLabel}
</Button>
</div>
) : null}
Expand Down
6 changes: 3 additions & 3 deletions i18n/locales/ar-MA.json
Original file line number Diff line number Diff line change
Expand Up @@ -699,9 +699,7 @@
"No OIDC providers configured yet.": "لا توجد موفّرات OIDC مُعدّة بعد.",
"No Objects": "لا توجد كائنات",
"No loaded objects match this filter.": "لا تتطابق أي كائنات محملة مع عامل التصفية هذا.",
"No matches in loaded objects": "لا توجد نتائج في الكائنات المحملة",
"No matching objects": "لا توجد كائنات مطابقة",
"More objects have not been searched yet.": "لا تزال هناك كائنات لم يتم البحث فيها.",
"No Policies": "لا توجد سياسات",
"No Secret Configured": "لم يتم إعداد سر",
"No Selection": "لا يوجد تحديد",
Expand Down Expand Up @@ -1526,11 +1524,13 @@
"Rename Failed": "فشلت إعادة التسمية",
"Rename Object": "إعادة تسمية الكائن",
"Renaming object": "جارٍ إعادة تسمية الكائن",
"Filter loaded objects": "Filter loaded objects",
"Search objects": "Filter loaded objects",
"Loaded {count} objects": "Loaded {count} objects",
"Filtering and sorting apply to loaded objects": "Filtering and sorting apply to loaded objects",
"Loaded objects only": "الكائنات المحملة فقط",
"Load next objects": "تحميل الكائنات التالية",
"Stop search": "Stop search",
"Resume search": "Resume search",
"All objects loaded": "تم تحميل جميع الكائنات",
"Failed to load more objects. Loaded objects remain available.": "فشل تحميل المزيد من الكائنات. تظل الكائنات المحملة متاحة.",
"Retry": "إعادة المحاولة",
Expand Down
6 changes: 3 additions & 3 deletions i18n/locales/de-DE.json
Original file line number Diff line number Diff line change
Expand Up @@ -699,9 +699,7 @@
"No OIDC providers configured yet.": "Es sind noch keine OIDC-Anbieter konfiguriert.",
"No Objects": "Keine Objekte",
"No loaded objects match this filter.": "Keine geladenen Objekte entsprechen diesem Filter.",
"No matches in loaded objects": "Keine Treffer in geladenen Objekten",
"No matching objects": "Keine passenden Objekte",
"More objects have not been searched yet.": "Weitere Objekte wurden noch nicht durchsucht.",
"No Policies": "Keine Richtlinien",
"No Secret Configured": "Kein Secret konfiguriert",
"No Selection": "Keine Auswahl",
Expand Down Expand Up @@ -1526,11 +1524,13 @@
"Rename Failed": "Umbenennen fehlgeschlagen",
"Rename Object": "Objekt umbenennen",
"Renaming object": "Objekt wird umbenannt",
"Filter loaded objects": "Filter loaded objects",
"Search objects": "Filter loaded objects",
"Loaded {count} objects": "Loaded {count} objects",
"Filtering and sorting apply to loaded objects": "Filtering and sorting apply to loaded objects",
"Loaded objects only": "Nur geladene Objekte",
"Load next objects": "Nächste Objekte laden",
"Stop search": "Stop search",
"Resume search": "Resume search",
"All objects loaded": "Alle Objekte geladen",
"Failed to load more objects. Loaded objects remain available.": "Weitere Objekte konnten nicht geladen werden. Geladene Objekte bleiben verfügbar.",
"Retry": "Erneut versuchen",
Expand Down
6 changes: 3 additions & 3 deletions i18n/locales/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -698,9 +698,7 @@
"No OIDC providers configured yet.": "No OIDC providers configured yet.",
"No Objects": "No Objects",
"No loaded objects match this filter.": "No loaded objects match this filter.",
"No matches in loaded objects": "No matches in loaded objects",
"No matching objects": "No matching objects",
"More objects have not been searched yet.": "More objects have not been searched yet.",
"No Policies": "No Policies",
"No Secret Configured": "No Secret Configured",
"No Selection": "No Selection",
Expand Down Expand Up @@ -1526,11 +1524,13 @@
"Rename Failed": "Rename Failed",
"Rename Object": "Rename Object",
"Renaming object": "Renaming object",
"Filter loaded objects": "Filter loaded objects",
"Search objects": "Search objects",
"Loaded {count} objects": "Loaded {count} objects",
"Filtering and sorting apply to loaded objects": "Filtering and sorting apply to loaded objects",
"Loaded objects only": "Loaded objects only",
"Load next objects": "Load next objects",
"Stop search": "Stop search",
"Resume search": "Resume search",
"All objects loaded": "All objects loaded",
"Failed to load more objects. Loaded objects remain available.": "Failed to load more objects. Loaded objects remain available.",
"Retry": "Retry",
Expand Down
6 changes: 3 additions & 3 deletions i18n/locales/es-ES.json
Original file line number Diff line number Diff line change
Expand Up @@ -699,9 +699,7 @@
"No OIDC providers configured yet.": "Aún no hay proveedores OIDC configurados.",
"No Objects": "Sin Objetos",
"No loaded objects match this filter.": "Ningún objeto cargado coincide con este filtro.",
"No matches in loaded objects": "Sin coincidencias en los objetos cargados",
"No matching objects": "No hay objetos coincidentes",
"More objects have not been searched yet.": "Aún quedan objetos por buscar.",
"No Policies": "Sin Políticas",
"No Secret Configured": "No hay secreto configurado",
"No Selection": "Sin Selección",
Expand Down Expand Up @@ -1526,11 +1524,13 @@
"Rename Failed": "Error al renombrar",
"Rename Object": "Renombrar objeto",
"Renaming object": "Renombrando objeto",
"Filter loaded objects": "Filter loaded objects",
"Search objects": "Filter loaded objects",
"Loaded {count} objects": "Loaded {count} objects",
"Filtering and sorting apply to loaded objects": "Filtering and sorting apply to loaded objects",
"Loaded objects only": "Solo objetos cargados",
"Load next objects": "Cargar los siguientes objetos",
"Stop search": "Stop search",
"Resume search": "Resume search",
"All objects loaded": "Todos los objetos cargados",
"Failed to load more objects. Loaded objects remain available.": "No se pudieron cargar más objetos. Los objetos cargados siguen disponibles.",
"Retry": "Reintentar",
Expand Down
6 changes: 3 additions & 3 deletions i18n/locales/fr-FR.json
Original file line number Diff line number Diff line change
Expand Up @@ -699,9 +699,7 @@
"No OIDC providers configured yet.": "Aucun fournisseur OIDC n'est encore configuré.",
"No Objects": "Aucun objet",
"No loaded objects match this filter.": "Aucun objet chargé ne correspond à ce filtre.",
"No matches in loaded objects": "Aucune correspondance dans les objets chargés",
"No matching objects": "Aucun objet correspondant",
"More objects have not been searched yet.": "D'autres objets n'ont pas encore été recherchés.",
"No Policies": "Aucune politique",
"No Secret Configured": "Aucun secret configuré",
"No Selection": "Aucune sélection",
Expand Down Expand Up @@ -1526,11 +1524,13 @@
"Rename Failed": "Échec du renommage",
"Rename Object": "Renommer l’objet",
"Renaming object": "Renommage de l’objet",
"Filter loaded objects": "Filter loaded objects",
"Search objects": "Filter loaded objects",
"Loaded {count} objects": "Loaded {count} objects",
"Filtering and sorting apply to loaded objects": "Filtering and sorting apply to loaded objects",
"Loaded objects only": "Objets chargés uniquement",
"Load next objects": "Charger les objets suivants",
"Stop search": "Stop search",
"Resume search": "Resume search",
"All objects loaded": "Tous les objets sont chargés",
"Failed to load more objects. Loaded objects remain available.": "Impossible de charger d’autres objets. Les objets chargés restent disponibles.",
"Retry": "Réessayer",
Expand Down
6 changes: 3 additions & 3 deletions i18n/locales/id-ID.json
Original file line number Diff line number Diff line change
Expand Up @@ -699,9 +699,7 @@
"No OIDC providers configured yet.": "Belum ada penyedia OIDC yang dikonfigurasi.",
"No Objects": "Tidak Ada Objek",
"No loaded objects match this filter.": "Tidak ada objek yang dimuat yang cocok dengan filter ini.",
"No matches in loaded objects": "Tidak ada kecocokan dalam objek yang dimuat",
"No matching objects": "Tidak ada objek yang cocok",
"More objects have not been searched yet.": "Masih ada objek yang belum dicari.",
"No Policies": "Tidak Ada Kebijakan",
"No Secret Configured": "Belum Ada Secret yang Dikonfigurasi",
"No Selection": "Tidak Ada Pilihan",
Expand Down Expand Up @@ -1526,11 +1524,13 @@
"Rename Failed": "Gagal mengganti nama",
"Rename Object": "Ganti nama objek",
"Renaming object": "Mengganti nama objek",
"Filter loaded objects": "Filter loaded objects",
"Search objects": "Filter loaded objects",
"Loaded {count} objects": "Loaded {count} objects",
"Filtering and sorting apply to loaded objects": "Filtering and sorting apply to loaded objects",
"Loaded objects only": "Hanya objek yang dimuat",
"Load next objects": "Muat objek berikutnya",
"Stop search": "Stop search",
"Resume search": "Resume search",
"All objects loaded": "Semua objek telah dimuat",
"Failed to load more objects. Loaded objects remain available.": "Gagal memuat lebih banyak objek. Objek yang telah dimuat tetap tersedia.",
"Retry": "Coba lagi",
Expand Down
6 changes: 3 additions & 3 deletions i18n/locales/it-IT.json
Original file line number Diff line number Diff line change
Expand Up @@ -699,9 +699,7 @@
"No OIDC providers configured yet.": "Non è ancora configurato alcun provider OIDC.",
"No Objects": "Nessun oggetto",
"No loaded objects match this filter.": "Nessun oggetto caricato corrisponde a questo filtro.",
"No matches in loaded objects": "Nessuna corrispondenza negli oggetti caricati",
"No matching objects": "Nessun oggetto corrispondente",
"More objects have not been searched yet.": "Altri oggetti non sono ancora stati cercati.",
"No Policies": "Nessuna politica",
"No Secret Configured": "Nessun segreto configurato",
"No Selection": "Nessuna selezione",
Expand Down Expand Up @@ -1526,11 +1524,13 @@
"Rename Failed": "Rinomina non riuscita",
"Rename Object": "Rinomina oggetto",
"Renaming object": "Rinomina oggetto in corso",
"Filter loaded objects": "Filter loaded objects",
"Search objects": "Filter loaded objects",
"Loaded {count} objects": "Loaded {count} objects",
"Filtering and sorting apply to loaded objects": "Filtering and sorting apply to loaded objects",
"Loaded objects only": "Solo oggetti caricati",
"Load next objects": "Carica gli oggetti successivi",
"Stop search": "Stop search",
"Resume search": "Resume search",
"All objects loaded": "Tutti gli oggetti sono stati caricati",
"Failed to load more objects. Loaded objects remain available.": "Impossibile caricare altri oggetti. Gli oggetti caricati restano disponibili.",
"Retry": "Riprova",
Expand Down
6 changes: 3 additions & 3 deletions i18n/locales/ja-JP.json
Original file line number Diff line number Diff line change
Expand Up @@ -699,9 +699,7 @@
"No OIDC providers configured yet.": "まだ OIDC プロバイダーは設定されていません。",
"No Objects": "オブジェクトなし",
"No loaded objects match this filter.": "読み込まれたオブジェクトはこのフィルターに一致しません。",
"No matches in loaded objects": "読み込み済みオブジェクトに一致なし",
"No matching objects": "一致するオブジェクトなし",
"More objects have not been searched yet.": "まだ検索されていないオブジェクトがあります。",
"No Policies": "ポリシーなし",
"No Secret Configured": "シークレット未設定",
"No Selection": "選択なし",
Expand Down Expand Up @@ -1526,11 +1524,13 @@
"Rename Failed": "名前の変更に失敗しました",
"Rename Object": "オブジェクト名を変更",
"Renaming object": "オブジェクト名を変更中",
"Filter loaded objects": "Filter loaded objects",
"Search objects": "Filter loaded objects",
"Loaded {count} objects": "Loaded {count} objects",
"Filtering and sorting apply to loaded objects": "Filtering and sorting apply to loaded objects",
"Loaded objects only": "読み込み済みオブジェクトのみ",
"Load next objects": "次のオブジェクトを読み込む",
"Stop search": "Stop search",
"Resume search": "Resume search",
"All objects loaded": "すべてのオブジェクトを読み込みました",
"Failed to load more objects. Loaded objects remain available.": "追加のオブジェクトを読み込めませんでした。読み込み済みのオブジェクトは引き続き利用できます。",
"Retry": "再試行",
Expand Down
6 changes: 3 additions & 3 deletions i18n/locales/ko-KR.json
Original file line number Diff line number Diff line change
Expand Up @@ -699,9 +699,7 @@
"No OIDC providers configured yet.": "아직 구성된 OIDC 공급자가 없습니다.",
"No Objects": "객체 없음",
"No loaded objects match this filter.": "로드된 객체가 이 필터와 일치하지 않습니다.",
"No matches in loaded objects": "로드된 객체에서 일치 항목 없음",
"No matching objects": "일치하는 객체 없음",
"More objects have not been searched yet.": "아직 검색되지 않은 객체가 더 있습니다.",
"No Policies": "정책 없음",
"No Secret Configured": "구성된 시크릿 없음",
"No Selection": "선택 없음",
Expand Down Expand Up @@ -1526,11 +1524,13 @@
"Rename Failed": "이름 변경 실패",
"Rename Object": "객체 이름 변경",
"Renaming object": "객체 이름 변경 중",
"Filter loaded objects": "Filter loaded objects",
"Search objects": "Filter loaded objects",
"Loaded {count} objects": "Loaded {count} objects",
"Filtering and sorting apply to loaded objects": "Filtering and sorting apply to loaded objects",
"Loaded objects only": "로드된 객체만",
"Load next objects": "다음 객체 로드",
"Stop search": "Stop search",
"Resume search": "Resume search",
"All objects loaded": "모든 객체 로드됨",
"Failed to load more objects. Loaded objects remain available.": "추가 객체를 로드하지 못했습니다. 로드된 객체는 계속 사용할 수 있습니다.",
"Retry": "다시 시도",
Expand Down
Loading