Commit 558828a21c
Unsigned
Layout: unified · split
Hutch.xcodeproj/project.pbxproj +8 −8
| @@ -515,7 +515,7 @@ | ||
| 515 | 515 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; |
| 516 | 516 | CODE_SIGN_ENTITLEMENTS = Hutch/Hutch.entitlements; |
| 517 | 517 | CODE_SIGN_STYLE = Automatic; |
| 518 | CURRENT_PROJECT_VERSION = 68; | |
| 518 | CURRENT_PROJECT_VERSION = 69; | |
| 519 | 519 | DEVELOPMENT_TEAM = ZCNAX3VL9D; |
| 520 | 520 | ENABLE_PREVIEWS = YES; |
| 521 | 521 | GENERATE_INFOPLIST_FILE = YES; |
| @@ -532,7 +532,7 @@ | ||
| 532 | 532 | "$(inherited)", |
| 533 | 533 | "@executable_path/Frameworks", |
| 534 | 534 | ); |
| 535 | MARKETING_VERSION = 3.1.0; | |
| 535 | MARKETING_VERSION = 3.1.1; | |
| 536 | 536 | PRODUCT_BUNDLE_IDENTIFIER = net.cleberg.Hutch; |
| 537 | 537 | PRODUCT_NAME = "$(TARGET_NAME)"; |
| 538 | 538 | STRING_CATALOG_GENERATE_SYMBOLS = YES; |
| @@ -552,7 +552,7 @@ | ||
| 552 | 552 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; |
| 553 | 553 | CODE_SIGN_ENTITLEMENTS = Hutch/Hutch.entitlements; |
| 554 | 554 | CODE_SIGN_STYLE = Automatic; |
| 555 | CURRENT_PROJECT_VERSION = 68; | |
| 555 | CURRENT_PROJECT_VERSION = 69; | |
| 556 | 556 | DEVELOPMENT_TEAM = ZCNAX3VL9D; |
| 557 | 557 | ENABLE_PREVIEWS = YES; |
| 558 | 558 | GENERATE_INFOPLIST_FILE = YES; |
| @@ -569,7 +569,7 @@ | ||
| 569 | 569 | "$(inherited)", |
| 570 | 570 | "@executable_path/Frameworks", |
| 571 | 571 | ); |
| 572 | MARKETING_VERSION = 3.1.0; | |
| 572 | MARKETING_VERSION = 3.1.1; | |
| 573 | 573 | PRODUCT_BUNDLE_IDENTIFIER = net.cleberg.Hutch; |
| 574 | 574 | PRODUCT_NAME = "$(TARGET_NAME)"; |
| 575 | 575 | STRING_CATALOG_GENERATE_SYMBOLS = YES; |
| @@ -632,7 +632,7 @@ | ||
| 632 | 632 | APPLICATION_EXTENSION_API_ONLY = YES; |
| 633 | 633 | CODE_SIGN_ENTITLEMENTS = HutchWidgetExtension/HutchWidgetExtension.entitlements; |
| 634 | 634 | CODE_SIGN_STYLE = Automatic; |
| 635 | CURRENT_PROJECT_VERSION = 68; | |
| 635 | CURRENT_PROJECT_VERSION = 69; | |
| 636 | 636 | DEVELOPMENT_TEAM = ZCNAX3VL9D; |
| 637 | 637 | GENERATE_INFOPLIST_FILE = NO; |
| 638 | 638 | INFOPLIST_FILE = HutchWidgetExtension/Info.plist; |
| @@ -642,7 +642,7 @@ | ||
| 642 | 642 | "@executable_path/Frameworks", |
| 643 | 643 | "@executable_path/../../Frameworks", |
| 644 | 644 | ); |
| 645 | MARKETING_VERSION = 3.1.0; | |
| 645 | MARKETING_VERSION = 3.1.1; | |
| 646 | 646 | PRODUCT_BUNDLE_IDENTIFIER = net.cleberg.Hutch.HutchWidgetExtension; |
| 647 | 647 | PRODUCT_NAME = "$(TARGET_NAME)"; |
| 648 | 648 | SKIP_INSTALL = YES; |
| @@ -661,7 +661,7 @@ | ||
| 661 | 661 | APPLICATION_EXTENSION_API_ONLY = YES; |
| 662 | 662 | CODE_SIGN_ENTITLEMENTS = HutchWidgetExtension/HutchWidgetExtension.entitlements; |
| 663 | 663 | CODE_SIGN_STYLE = Automatic; |
| 664 | CURRENT_PROJECT_VERSION = 68; | |
| 664 | CURRENT_PROJECT_VERSION = 69; | |
| 665 | 665 | DEVELOPMENT_TEAM = ZCNAX3VL9D; |
| 666 | 666 | GENERATE_INFOPLIST_FILE = NO; |
| 667 | 667 | INFOPLIST_FILE = HutchWidgetExtension/Info.plist; |
| @@ -671,7 +671,7 @@ | ||
| 671 | 671 | "@executable_path/Frameworks", |
| 672 | 672 | "@executable_path/../../Frameworks", |
| 673 | 673 | ); |
| 674 | MARKETING_VERSION = 3.1.0; | |
| 674 | MARKETING_VERSION = 3.1.1; | |
| 675 | 675 | PRODUCT_BUNDLE_IDENTIFIER = net.cleberg.Hutch.HutchWidgetExtension; |
| 676 | 676 | PRODUCT_NAME = "$(TARGET_NAME)"; |
| 677 | 677 | SKIP_INSTALL = YES; |
Hutch/Views/Tickets/TicketListViewModel.swift +26 −5
| @@ -299,13 +299,34 @@ final class TicketListViewModel { | ||
| 299 | 299 | hasMore = true |
| 300 | 300 | |
| 301 | 301 | do { |
| 302 | let page = try await fetchPage(cursor: nil) | |
| 303 | tickets = page.results | |
| 304 | cursor = page.cursor | |
| 305 | hasMore = page.cursor != nil | |
| 302 | // todo.sr.ht exposes `tickets(cursor:)` only (see Docs/API/todo.json) — no server-side | |
| 303 | // status filter. The Open tab filters client-side, so we paginate until the cursor is | |
| 304 | // exhausted; otherwise older open tickets never appear in the first page (25 items). | |
| 305 | var accumulated: [TicketSummary] = [] | |
| 306 | var nextCursor: String? | |
| 307 | ||
| 308 | repeat { | |
| 309 | let page = try await fetchPage(cursor: nextCursor) | |
| 310 | ||
| 311 | let existingIDs = Set(accumulated.map(\.id)) | |
| 312 | let newTickets = page.results.filter { !existingIDs.contains($0.id) } | |
| 313 | ||
| 314 | if newTickets.isEmpty && !page.results.isEmpty { | |
| 315 | break | |
| 316 | } | |
| 317 | ||
| 318 | accumulated.append(contentsOf: newTickets) | |
| 319 | nextCursor = page.cursor | |
| 320 | } while nextCursor != nil | |
| 321 | ||
| 322 | tickets = accumulated | |
| 323 | cursor = nextCursor | |
| 324 | hasMore = nextCursor != nil | |
| 306 | 325 | reconcileSelectionWithLoadedTickets() |
| 307 | 326 | } catch { |
| 308 | self.error = error.userFacingMessage | |
| 327 | if !Task.isCancelled { | |
| 328 | self.error = error.userFacingMessage | |
| 329 | } | |
| 309 | 330 | } |
| 310 | 331 | |
| 311 | 332 | isLoading = false |