Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -94,10 +94,15 @@ final class DataBrowserViewModel {
underlying: nil
)
isLoading = false
isPageLoading = false
return
}

if isInitial || legacyRows.isEmpty { isLoading = true }
if isInitial || legacyRows.isEmpty {
isLoading = true
} else {
isPageLoading = true
}
loadError = nil

do {
Expand All @@ -119,6 +124,7 @@ final class DataBrowserViewModel {
if case .error(let err) = phase {
loadError = err
isLoading = false
isPageLoading = false
return
}

Expand All @@ -139,12 +145,14 @@ final class DataBrowserViewModel {
}

isLoading = false
isPageLoading = false
} catch {
loadError = ErrorClassifier.classify(
error,
context: ErrorContext(operation: "loadData", databaseType: databaseType, host: host)
)
isLoading = false
isPageLoading = false
}
}

Expand Down Expand Up @@ -246,9 +254,7 @@ final class DataBrowserViewModel {
}

private func navigatePage() async {
isPageLoading = true
await load()
isPageLoading = false
}

// MARK: - Sort / Filter / Search
Expand Down
5 changes: 4 additions & 1 deletion TableProMobile/TableProMobile/Views/DataBrowserView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ struct DataBrowserView: View {
]
}
.toolbar { topToolbar }
.toolbar(rows.isEmpty && !viewModel.hasActiveSearch && !viewModel.hasActiveFilters ? .hidden : .visible, for: .bottomBar)
.toolbar(rows.isEmpty && !viewModel.hasActiveSearch && !viewModel.hasActiveFilters && !viewModel.isPageLoading ? .hidden : .visible, for: .bottomBar)
.toolbar { paginationToolbar }
.task {
viewModel.attach(session: session, table: table, databaseType: connection.type, host: connection.host)
Expand Down Expand Up @@ -199,6 +199,9 @@ struct DataBrowserView: View {
.frame(maxWidth: .infinity, maxHeight: .infinity)
} else if let appError = viewModel.loadError {
ErrorView(error: appError) { await viewModel.load() }
} else if rows.isEmpty, viewModel.isPageLoading {
ProgressView()
.frame(maxWidth: .infinity, maxHeight: .infinity)
} else if rows.isEmpty, viewModel.hasActiveSearch {
ContentUnavailableView.search(text: viewModel.activeSearchText)
} else if rows.isEmpty {
Expand Down
26 changes: 26 additions & 0 deletions TableProMobile/TableProMobileTests/DataBrowserViewModelTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,32 @@ struct DataBrowserViewModelTests {
#expect(vm.activeSearchText == "")
}

@Test("clearSearch with existing rows replaces them without leaving loading flags stuck")
func clearSearchReplacesRowsCleanly() async {
let driver = MockDatabaseDriver()
driver.scriptedColumns = makeColumns()
driver.scriptedExecuteResults = [
.success(QueryResult(columns: makeColumns(), rows: [["1", "Alice"]], rowsAffected: 0, executionTime: 0)),
.success(QueryResult(columns: [], rows: [["1"]], rowsAffected: 0, executionTime: 0))
]
let vm = DataBrowserViewModel()
vm.attach(session: makeSession(driver: driver), table: TableInfo(name: "users"), databaseType: .mysql, host: "localhost")
await vm.load(isInitial: true)
#expect(vm.legacyRows.count == 1)
#expect(vm.isLoading == false)
#expect(vm.isPageLoading == false)

driver.scriptedExecuteResults = [
.success(QueryResult(columns: makeColumns(), rows: [["1", "Alice"], ["2", "Bob"]], rowsAffected: 0, executionTime: 0)),
.success(QueryResult(columns: [], rows: [["2"]], rowsAffected: 0, executionTime: 0))
]
await vm.clearSearch()

#expect(vm.isLoading == false)
#expect(vm.isPageLoading == false)
#expect(vm.legacyRows.count == 2)
}

@Test("pagination prev/next clamps at boundaries")
func paginationClamps() async {
let driver = MockDatabaseDriver()
Expand Down
Loading