Skip to content

Commit b9579e2

Browse files
committed
feat: add repository labels pagination
fix GitHawkApp#2902
1 parent 18fef6c commit b9579e2

File tree

5 files changed

+155
-21
lines changed

5 files changed

+155
-21
lines changed

Diff for: Classes/Labels/GitHubClient+RepositoryLabels.swift

+62
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
//
2+
// GitHubClient+RepositoryLabels.swift
3+
// Freetime
4+
//
5+
// Created by Quentin Dreyer on 05/03/2020.
6+
// Copyright © 2020 Ryan Nystrom. All rights reserved.
7+
//
8+
9+
import GitHubAPI
10+
import Apollo
11+
12+
private extension FetchRepositoryLabelsQuery.Data {
13+
14+
func labels() -> [RepositoryLabel] {
15+
var labels: [RepositoryLabel] = []
16+
repository?.labels.map { nodes in
17+
nodes.nodes.map { node in
18+
labels += node.compactMap {
19+
guard let label = $0 else { return nil }
20+
return RepositoryLabel(color: label.color, name: label.name)
21+
}
22+
}
23+
}
24+
return labels
25+
}
26+
27+
func nextPageToken() -> String? {
28+
guard repository?.labels?.pageInfo.hasNextPage == true else { return nil }
29+
return repository?.labels?.pageInfo.endCursor
30+
}
31+
32+
}
33+
34+
extension GithubClient {
35+
36+
struct RepositoryLabelsPayload {
37+
let labels: [RepositoryLabel]
38+
let nextPage: String?
39+
}
40+
41+
func fetchRepositoryLabels(owner: String,
42+
repo: String,
43+
nextPage: String?,
44+
completion: @escaping (Result<RepositoryLabelsPayload>) -> Void
45+
) {
46+
let query = FetchRepositoryLabelsQuery(owner: owner, repo: repo, after: nextPage)
47+
client.query(query, result: { $0 }, completion: { result in
48+
49+
switch result {
50+
case .failure(let error):
51+
completion(.error(error))
52+
53+
case .success(let data):
54+
let payload = RepositoryLabelsPayload(
55+
labels: data.labels(),
56+
nextPage: data.nextPageToken()
57+
)
58+
completion(.success(payload))
59+
}
60+
})
61+
}
62+
}

Diff for: Classes/Labels/LabelsViewController.swift

+15-13
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,9 @@ LabelSectionControllerDelegate {
1616

1717
private let selectedLabels: Set<RepositoryLabel>
1818
private var labels = [RepositoryLabel]()
19+
private let owner: String
20+
private let repo: String
1921
private let client: GithubClient
20-
private let request: RepositoryLabelsQuery
2122

2223
init(
2324
selected: [RepositoryLabel],
@@ -27,7 +28,8 @@ LabelSectionControllerDelegate {
2728
) {
2829
self.selectedLabels = Set(selected)
2930
self.client = client
30-
self.request = RepositoryLabelsQuery(owner: owner, repo: repo)
31+
self.owner = owner
32+
self.repo = repo
3133
super.init(emptyErrorMessage: NSLocalizedString("No labels found", comment: ""))
3234
preferredContentSize = Styles.Sizes.contextMenuSize
3335
title = Constants.Strings.labels
@@ -87,20 +89,20 @@ LabelSectionControllerDelegate {
8789
// MARK: Overrides
8890

8991
override func fetch(page: String?) {
90-
client.client.query(request, result: { data in
91-
data.repository?.labels?.nodes
92-
}, completion: { [weak self] result in
92+
client.fetchRepositoryLabels(
93+
owner: owner,
94+
repo: repo,
95+
nextPage: page as String?
96+
) { [weak self] result in
97+
guard let strongSelf = self else { return }
9398
switch result {
94-
case .success(let nodes):
95-
self?.labels = nodes.compactMap {
96-
guard let node = $0 else { return nil }
97-
return RepositoryLabel(color: node.color, name: node.name)
98-
}.sorted { $0.name < $1.name }
99-
self?.update(animated: true)
100-
case .failure(let error):
99+
case .success(let payload):
100+
self?.labels = payload.labels.sorted { $0.name < $1.name }
101+
strongSelf.update(page: payload.nextPage, animated: true)
102+
case .error(let error):
101103
Squawk.show(error: error)
102104
}
103-
})
105+
}
104106
}
105107

106108
// MARK: BaseListViewControllerDataSource

Diff for: Freetime.xcodeproj/project.pbxproj

+4
Original file line numberDiff line numberDiff line change
@@ -459,6 +459,7 @@
459459
29FE635F21AE2E2F00A07A86 /* RepositoryLoadingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29FE635E21AE2E2F00A07A86 /* RepositoryLoadingViewController.swift */; };
460460
29FE636121AE2E7900A07A86 /* RepositoryErrorViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29FE636021AE2E7900A07A86 /* RepositoryErrorViewController.swift */; };
461461
29FF85A51EE1EA7A007B8762 /* ReactionContent+ReactionType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29FF85A41EE1EA7A007B8762 /* ReactionContent+ReactionType.swift */; };
462+
2CDD97C22411B61C0016D5CF /* GitHubClient+RepositoryLabels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CDD97C12411B61C0016D5CF /* GitHubClient+RepositoryLabels.swift */; };
462463
3E79A2FF1F8A7DA700E1126B /* ShortcutHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3E79A2FE1F8A7DA700E1126B /* ShortcutHandler.swift */; };
463464
4920F1A81F72E27200131E9D /* UIViewController+UserActivity.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4920F1A71F72E27200131E9D /* UIViewController+UserActivity.swift */; };
464465
49AF91B1204B416500DFF325 /* MergeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49AF91B0204B416500DFF325 /* MergeTests.swift */; };
@@ -1060,6 +1061,7 @@
10601061
29FE635E21AE2E2F00A07A86 /* RepositoryLoadingViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RepositoryLoadingViewController.swift; sourceTree = "<group>"; };
10611062
29FE636021AE2E7900A07A86 /* RepositoryErrorViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RepositoryErrorViewController.swift; sourceTree = "<group>"; };
10621063
29FF85A41EE1EA7A007B8762 /* ReactionContent+ReactionType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ReactionContent+ReactionType.swift"; sourceTree = "<group>"; };
1064+
2CDD97C12411B61C0016D5CF /* GitHubClient+RepositoryLabels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "GitHubClient+RepositoryLabels.swift"; sourceTree = "<group>"; };
10631065
36115D494E8C3B4F39AC8CD9 /* Pods-Freetime.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Freetime.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Freetime/Pods-Freetime.debug.xcconfig"; sourceTree = "<group>"; };
10641066
3E106824819769E0A6665A79 /* Pods-FreetimeWatch.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-FreetimeWatch.release.xcconfig"; path = "Pods/Target Support Files/Pods-FreetimeWatch/Pods-FreetimeWatch.release.xcconfig"; sourceTree = "<group>"; };
10651067
3E79A2FE1F8A7DA700E1126B /* ShortcutHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShortcutHandler.swift; sourceTree = "<group>"; };
@@ -2154,6 +2156,7 @@
21542156
2924C18A20D5B3A100FCFCFF /* LabelMenuCell.swift */,
21552157
29C8F9B4208C081D0075931C /* LabelSectionController.swift */,
21562158
2924C18C20D5B3DD00FCFCFF /* LabelsViewController.swift */,
2159+
2CDD97C12411B61C0016D5CF /* GitHubClient+RepositoryLabels.swift */,
21572160
);
21582161
path = Labels;
21592162
sourceTree = "<group>";
@@ -3263,6 +3266,7 @@
32633266
031E0241220B433C00A329F1 /* UIImage+Color.swift in Sources */,
32643267
29999734203135E100995FFD /* IssueMergeContextCell.swift in Sources */,
32653268
29EDFE821F661562005BCCEB /* RepositoryReadmeModel.swift in Sources */,
3269+
2CDD97C22411B61C0016D5CF /* GitHubClient+RepositoryLabels.swift in Sources */,
32663270
29EDFE841F661776005BCCEB /* RepositoryReadmeSectionController.swift in Sources */,
32673271
29136BDF200A7A75007317BE /* UIScrollView+LeftRightSafeInset.swift in Sources */,
32683272
298C7E2621D7F56600DD2A60 /* SettingsAccountCell.swift in Sources */,

Diff for: gql/API.swift

+67-5
Original file line numberDiff line numberDiff line change
@@ -1389,20 +1389,22 @@ public final class AddReactionMutation: GraphQLMutation {
13891389
}
13901390
}
13911391

1392-
public final class RepositoryLabelsQuery: GraphQLQuery {
1392+
public final class FetchRepositoryLabelsQuery: GraphQLQuery {
13931393
public let operationDefinition =
1394-
"query RepositoryLabels($owner: String!, $repo: String!) {\n repository(owner: $owner, name: $repo) {\n __typename\n labels(first: 100) {\n __typename\n nodes {\n __typename\n name\n color\n }\n }\n }\n}"
1394+
"query fetchRepositoryLabels($owner: String!, $repo: String!, $after: String) {\n repository(owner: $owner, name: $repo) {\n __typename\n labels(first: 100, after: $after) {\n __typename\n nodes {\n __typename\n name\n color\n }\n pageInfo {\n __typename\n hasNextPage\n endCursor\n }\n }\n }\n}"
13951395

13961396
public var owner: String
13971397
public var repo: String
1398+
public var after: String?
13981399

1399-
public init(owner: String, repo: String) {
1400+
public init(owner: String, repo: String, after: String? = nil) {
14001401
self.owner = owner
14011402
self.repo = repo
1403+
self.after = after
14021404
}
14031405

14041406
public var variables: GraphQLMap? {
1405-
return ["owner": owner, "repo": repo]
1407+
return ["owner": owner, "repo": repo, "after": after]
14061408
}
14071409

14081410
public struct Data: GraphQLSelectionSet {
@@ -1437,7 +1439,7 @@ public final class RepositoryLabelsQuery: GraphQLQuery {
14371439

14381440
public static let selections: [GraphQLSelection] = [
14391441
GraphQLField("__typename", type: .nonNull(.scalar(String.self))),
1440-
GraphQLField("labels", arguments: ["first": 100], type: .object(Label.selections)),
1442+
GraphQLField("labels", arguments: ["first": 100, "after": GraphQLVariable("after")], type: .object(Label.selections)),
14411443
]
14421444

14431445
public private(set) var resultMap: ResultMap
@@ -1475,6 +1477,7 @@ public final class RepositoryLabelsQuery: GraphQLQuery {
14751477
public static let selections: [GraphQLSelection] = [
14761478
GraphQLField("__typename", type: .nonNull(.scalar(String.self))),
14771479
GraphQLField("nodes", type: .list(.object(Node.selections))),
1480+
GraphQLField("pageInfo", type: .nonNull(.object(PageInfo.selections))),
14781481
]
14791482

14801483
public private(set) var resultMap: ResultMap
@@ -1506,6 +1509,16 @@ public final class RepositoryLabelsQuery: GraphQLQuery {
15061509
}
15071510
}
15081511

1512+
/// Information to aid in pagination.
1513+
public var pageInfo: PageInfo {
1514+
get {
1515+
return PageInfo(unsafeResultMap: resultMap["pageInfo"]! as! ResultMap)
1516+
}
1517+
set {
1518+
resultMap.updateValue(newValue.resultMap, forKey: "pageInfo")
1519+
}
1520+
}
1521+
15091522
public struct Node: GraphQLSelectionSet {
15101523
public static let possibleTypes = ["Label"]
15111524

@@ -1555,6 +1568,55 @@ public final class RepositoryLabelsQuery: GraphQLQuery {
15551568
}
15561569
}
15571570
}
1571+
1572+
public struct PageInfo: GraphQLSelectionSet {
1573+
public static let possibleTypes = ["PageInfo"]
1574+
1575+
public static let selections: [GraphQLSelection] = [
1576+
GraphQLField("__typename", type: .nonNull(.scalar(String.self))),
1577+
GraphQLField("hasNextPage", type: .nonNull(.scalar(Bool.self))),
1578+
GraphQLField("endCursor", type: .scalar(String.self)),
1579+
]
1580+
1581+
public private(set) var resultMap: ResultMap
1582+
1583+
public init(unsafeResultMap: ResultMap) {
1584+
self.resultMap = unsafeResultMap
1585+
}
1586+
1587+
public init(hasNextPage: Bool, endCursor: String? = nil) {
1588+
self.init(unsafeResultMap: ["__typename": "PageInfo", "hasNextPage": hasNextPage, "endCursor": endCursor])
1589+
}
1590+
1591+
public var __typename: String {
1592+
get {
1593+
return resultMap["__typename"]! as! String
1594+
}
1595+
set {
1596+
resultMap.updateValue(newValue, forKey: "__typename")
1597+
}
1598+
}
1599+
1600+
/// When paginating forwards, are there more items?
1601+
public var hasNextPage: Bool {
1602+
get {
1603+
return resultMap["hasNextPage"]! as! Bool
1604+
}
1605+
set {
1606+
resultMap.updateValue(newValue, forKey: "hasNextPage")
1607+
}
1608+
}
1609+
1610+
/// When paginating forwards, the cursor to continue.
1611+
public var endCursor: String? {
1612+
get {
1613+
return resultMap["endCursor"] as? String
1614+
}
1615+
set {
1616+
resultMap.updateValue(newValue, forKey: "endCursor")
1617+
}
1618+
}
1619+
}
15581620
}
15591621
}
15601622
}

Diff for: gql/RepositoryLabels.graphql

+7-3
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
1-
query RepositoryLabels($owner: String!, $repo: String!) {
1+
query fetchRepositoryLabels($owner: String!, $repo: String!, $after: String) {
22
repository(owner: $owner, name: $repo) {
3-
labels(first:100) {
3+
labels(first:100, after: $after) {
44
nodes {
55
name
66
color
77
}
8-
}
8+
pageInfo {
9+
hasNextPage
10+
endCursor
11+
}
12+
}
913
}
1014
}

0 commit comments

Comments
 (0)