Skip to content
This repository has been archived by the owner on Sep 20, 2023. It is now read-only.

feat: add repository labels pagination #2904

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
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
62 changes: 62 additions & 0 deletions Classes/Labels/GitHubClient+RepositoryLabels.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
//
// GitHubClient+RepositoryLabels.swift
// Freetime
//
// Created by Quentin Dreyer on 05/03/2020.
// Copyright © 2020 Ryan Nystrom. All rights reserved.
//

import GitHubAPI
import Apollo

private extension FetchRepositoryLabelsQuery.Data {

func labels() -> [RepositoryLabel] {
var labels: [RepositoryLabel] = []
repository?.labels.map { nodes in
nodes.nodes.map { node in
labels += node.compactMap {
guard let label = $0 else { return nil }
return RepositoryLabel(color: label.color, name: label.name)
}
}
}
return labels
}

func nextPageToken() -> String? {
guard repository?.labels?.pageInfo.hasNextPage == true else { return nil }
return repository?.labels?.pageInfo.endCursor
}

}

extension GithubClient {

struct RepositoryLabelsPayload {
let labels: [RepositoryLabel]
let nextPage: String?
}

func fetchRepositoryLabels(owner: String,
repo: String,
nextPage: String?,
completion: @escaping (Result<RepositoryLabelsPayload>) -> Void
) {
let query = FetchRepositoryLabelsQuery(owner: owner, repo: repo, after: nextPage)
client.query(query, result: { $0 }, completion: { result in

switch result {
case .failure(let error):
completion(.error(error))

case .success(let data):
let payload = RepositoryLabelsPayload(
labels: data.labels(),
nextPage: data.nextPageToken()
)
completion(.success(payload))
}
})
}
}
28 changes: 15 additions & 13 deletions Classes/Labels/LabelsViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@ LabelSectionControllerDelegate {

private let selectedLabels: Set<RepositoryLabel>
private var labels = [RepositoryLabel]()
private let owner: String
private let repo: String
private let client: GithubClient
private let request: RepositoryLabelsQuery

init(
selected: [RepositoryLabel],
Expand All @@ -27,7 +28,8 @@ LabelSectionControllerDelegate {
) {
self.selectedLabels = Set(selected)
self.client = client
self.request = RepositoryLabelsQuery(owner: owner, repo: repo)
self.owner = owner
self.repo = repo
super.init(emptyErrorMessage: NSLocalizedString("No labels found", comment: ""))
preferredContentSize = Styles.Sizes.contextMenuSize
title = Constants.Strings.labels
Expand Down Expand Up @@ -87,20 +89,20 @@ LabelSectionControllerDelegate {
// MARK: Overrides

override func fetch(page: String?) {
client.client.query(request, result: { data in
data.repository?.labels?.nodes
}, completion: { [weak self] result in
client.fetchRepositoryLabels(
owner: owner,
repo: repo,
nextPage: page as String?
) { [weak self] result in
guard let strongSelf = self else { return }
switch result {
case .success(let nodes):
self?.labels = nodes.compactMap {
guard let node = $0 else { return nil }
return RepositoryLabel(color: node.color, name: node.name)
}.sorted { $0.name < $1.name }
self?.update(animated: true)
case .failure(let error):
case .success(let payload):
self?.labels = payload.labels.sorted { $0.name < $1.name }
strongSelf.update(page: payload.nextPage, animated: true)
case .error(let error):
Squawk.show(error: error)
}
})
}
}

// MARK: BaseListViewControllerDataSource
Expand Down
4 changes: 4 additions & 0 deletions Freetime.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -459,6 +459,7 @@
29FE635F21AE2E2F00A07A86 /* RepositoryLoadingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29FE635E21AE2E2F00A07A86 /* RepositoryLoadingViewController.swift */; };
29FE636121AE2E7900A07A86 /* RepositoryErrorViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29FE636021AE2E7900A07A86 /* RepositoryErrorViewController.swift */; };
29FF85A51EE1EA7A007B8762 /* ReactionContent+ReactionType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29FF85A41EE1EA7A007B8762 /* ReactionContent+ReactionType.swift */; };
2CDD97C22411B61C0016D5CF /* GitHubClient+RepositoryLabels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CDD97C12411B61C0016D5CF /* GitHubClient+RepositoryLabels.swift */; };
3E79A2FF1F8A7DA700E1126B /* ShortcutHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3E79A2FE1F8A7DA700E1126B /* ShortcutHandler.swift */; };
4920F1A81F72E27200131E9D /* UIViewController+UserActivity.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4920F1A71F72E27200131E9D /* UIViewController+UserActivity.swift */; };
49AF91B1204B416500DFF325 /* MergeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49AF91B0204B416500DFF325 /* MergeTests.swift */; };
Expand Down Expand Up @@ -1060,6 +1061,7 @@
29FE635E21AE2E2F00A07A86 /* RepositoryLoadingViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RepositoryLoadingViewController.swift; sourceTree = "<group>"; };
29FE636021AE2E7900A07A86 /* RepositoryErrorViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RepositoryErrorViewController.swift; sourceTree = "<group>"; };
29FF85A41EE1EA7A007B8762 /* ReactionContent+ReactionType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ReactionContent+ReactionType.swift"; sourceTree = "<group>"; };
2CDD97C12411B61C0016D5CF /* GitHubClient+RepositoryLabels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "GitHubClient+RepositoryLabels.swift"; sourceTree = "<group>"; };
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>"; };
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>"; };
3E79A2FE1F8A7DA700E1126B /* ShortcutHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShortcutHandler.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -2154,6 +2156,7 @@
2924C18A20D5B3A100FCFCFF /* LabelMenuCell.swift */,
29C8F9B4208C081D0075931C /* LabelSectionController.swift */,
2924C18C20D5B3DD00FCFCFF /* LabelsViewController.swift */,
2CDD97C12411B61C0016D5CF /* GitHubClient+RepositoryLabels.swift */,
);
path = Labels;
sourceTree = "<group>";
Expand Down Expand Up @@ -3263,6 +3266,7 @@
031E0241220B433C00A329F1 /* UIImage+Color.swift in Sources */,
29999734203135E100995FFD /* IssueMergeContextCell.swift in Sources */,
29EDFE821F661562005BCCEB /* RepositoryReadmeModel.swift in Sources */,
2CDD97C22411B61C0016D5CF /* GitHubClient+RepositoryLabels.swift in Sources */,
29EDFE841F661776005BCCEB /* RepositoryReadmeSectionController.swift in Sources */,
29136BDF200A7A75007317BE /* UIScrollView+LeftRightSafeInset.swift in Sources */,
298C7E2621D7F56600DD2A60 /* SettingsAccountCell.swift in Sources */,
Expand Down
72 changes: 67 additions & 5 deletions gql/API.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1389,20 +1389,22 @@ public final class AddReactionMutation: GraphQLMutation {
}
}

public final class RepositoryLabelsQuery: GraphQLQuery {
public final class FetchRepositoryLabelsQuery: GraphQLQuery {
public let operationDefinition =
"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}"
"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}"

public var owner: String
public var repo: String
public var after: String?

public init(owner: String, repo: String) {
public init(owner: String, repo: String, after: String? = nil) {
self.owner = owner
self.repo = repo
self.after = after
}

public var variables: GraphQLMap? {
return ["owner": owner, "repo": repo]
return ["owner": owner, "repo": repo, "after": after]
}

public struct Data: GraphQLSelectionSet {
Expand Down Expand Up @@ -1437,7 +1439,7 @@ public final class RepositoryLabelsQuery: GraphQLQuery {

public static let selections: [GraphQLSelection] = [
GraphQLField("__typename", type: .nonNull(.scalar(String.self))),
GraphQLField("labels", arguments: ["first": 100], type: .object(Label.selections)),
GraphQLField("labels", arguments: ["first": 100, "after": GraphQLVariable("after")], type: .object(Label.selections)),
]

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

public private(set) var resultMap: ResultMap
Expand Down Expand Up @@ -1506,6 +1509,16 @@ public final class RepositoryLabelsQuery: GraphQLQuery {
}
}

/// Information to aid in pagination.
public var pageInfo: PageInfo {
get {
return PageInfo(unsafeResultMap: resultMap["pageInfo"]! as! ResultMap)
}
set {
resultMap.updateValue(newValue.resultMap, forKey: "pageInfo")
}
}

public struct Node: GraphQLSelectionSet {
public static let possibleTypes = ["Label"]

Expand Down Expand Up @@ -1555,6 +1568,55 @@ public final class RepositoryLabelsQuery: GraphQLQuery {
}
}
}

public struct PageInfo: GraphQLSelectionSet {
public static let possibleTypes = ["PageInfo"]

public static let selections: [GraphQLSelection] = [
GraphQLField("__typename", type: .nonNull(.scalar(String.self))),
GraphQLField("hasNextPage", type: .nonNull(.scalar(Bool.self))),
GraphQLField("endCursor", type: .scalar(String.self)),
]

public private(set) var resultMap: ResultMap

public init(unsafeResultMap: ResultMap) {
self.resultMap = unsafeResultMap
}

public init(hasNextPage: Bool, endCursor: String? = nil) {
self.init(unsafeResultMap: ["__typename": "PageInfo", "hasNextPage": hasNextPage, "endCursor": endCursor])
}

public var __typename: String {
get {
return resultMap["__typename"]! as! String
}
set {
resultMap.updateValue(newValue, forKey: "__typename")
}
}

/// When paginating forwards, are there more items?
public var hasNextPage: Bool {
get {
return resultMap["hasNextPage"]! as! Bool
}
set {
resultMap.updateValue(newValue, forKey: "hasNextPage")
}
}

/// When paginating forwards, the cursor to continue.
public var endCursor: String? {
get {
return resultMap["endCursor"] as? String
}
set {
resultMap.updateValue(newValue, forKey: "endCursor")
}
}
}
}
}
}
Expand Down
10 changes: 7 additions & 3 deletions gql/RepositoryLabels.graphql
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
query RepositoryLabels($owner: String!, $repo: String!) {
query fetchRepositoryLabels($owner: String!, $repo: String!, $after: String) {
repository(owner: $owner, name: $repo) {
labels(first:100) {
labels(first:100, after: $after) {
nodes {
name
color
}
}
pageInfo {
hasNextPage
endCursor
}
}
}
}