mirror of
https://github.com/zhigang1992/GitHawk.git
synced 2026-04-15 12:10:33 +08:00
273 lines
9.5 KiB
Swift
273 lines
9.5 KiB
Swift
//
|
|
// RepositoryViewController.swift
|
|
// Freetime
|
|
//
|
|
// Created by Sherlock, James on 29/07/2017.
|
|
// Copyright © 2017 Ryan Nystrom. All rights reserved.
|
|
//
|
|
|
|
import UIKit
|
|
import IGListKit
|
|
import TUSafariActivity
|
|
import SafariServices
|
|
|
|
struct RepositoryDetails {
|
|
let owner: String
|
|
let name: String
|
|
let hasIssuesEnabled: Bool
|
|
}
|
|
|
|
class RepositoryViewController: UIViewController,
|
|
FeedDelegate,
|
|
ListAdapterDataSource,
|
|
SegmentedControlSectionControllerDelegate,
|
|
LoadMoreSectionControllerDelegate,
|
|
PrimaryViewController {
|
|
|
|
private let repo: RepositoryDetails
|
|
private let client: RepositoryClient
|
|
private lazy var feed: Feed = { Feed(viewController: self, delegate: self) }()
|
|
|
|
private let noReadmeResultsKey = "noReadmeResultsKey" as ListDiffable
|
|
private let noIssuesResultsKey = "noIssuesResultsKey" as ListDiffable
|
|
private let noPullRequestsResultsKey = "noPullRequestsResultsKey" as ListDiffable
|
|
|
|
private let dataSource: RepositoryDataSource
|
|
|
|
init(client: GithubClient, repo: RepositoryDetails) {
|
|
self.repo = repo
|
|
self.client = RepositoryClient(githubClient: client, owner: repo.owner, name: repo.name)
|
|
self.dataSource = RepositoryDataSource(hasIssuesEnabled: repo.hasIssuesEnabled)
|
|
super.init(nibName: nil, bundle: nil)
|
|
}
|
|
|
|
required init?(coder aDecoder: NSCoder) {
|
|
fatalError("init(coder:) has not been implemented")
|
|
}
|
|
|
|
override func viewDidLoad() {
|
|
super.viewDidLoad()
|
|
|
|
navigationItem.backBarButtonItem = UIBarButtonItem(title: " ", style: .plain, target: nil, action: nil)
|
|
|
|
feed.viewDidLoad()
|
|
feed.adapter.dataSource = self
|
|
title = "\(repo.owner)/\(repo.name)"
|
|
|
|
let rightItem = UIBarButtonItem(
|
|
image: UIImage(named: "bullets-hollow"),
|
|
style: .plain,
|
|
target: self,
|
|
action: #selector(IssuesViewController.onMore(sender:))
|
|
)
|
|
rightItem.accessibilityLabel = NSLocalizedString("More options", comment: "")
|
|
navigationItem.rightBarButtonItem = rightItem
|
|
}
|
|
|
|
override func viewWillAppear(_ animated: Bool) {
|
|
super.viewWillAppear(animated)
|
|
rz_smoothlyDeselectRows(collectionView: feed.collectionView)
|
|
}
|
|
|
|
override func viewWillLayoutSubviews() {
|
|
super.viewWillLayoutSubviews()
|
|
feed.viewWillLayoutSubviews(view: view)
|
|
}
|
|
|
|
// MARK: Private API
|
|
|
|
var repoUrl: URL {
|
|
return URL(string: "https://github.com/\(repo.owner)/\(repo.name)")!
|
|
}
|
|
|
|
var repoOwnerUrl: URL {
|
|
return URL(string: "https://github.com/\(repo.owner)")!
|
|
}
|
|
|
|
func shareAction(sender: UIBarButtonItem) -> UIAlertAction {
|
|
return UIAlertAction(title: NSLocalizedString("Send To", comment: ""), style: .default) { [weak self] _ in
|
|
guard let strongSelf = self else { return }
|
|
let safariActivity = TUSafariActivity()
|
|
let controller = UIActivityViewController(activityItems: [strongSelf.repoUrl], applicationActivities: [safariActivity])
|
|
controller.popoverPresentationController?.barButtonItem = sender
|
|
strongSelf.present(controller, animated: true)
|
|
}
|
|
}
|
|
|
|
func safariAction() -> UIAlertAction {
|
|
return UIAlertAction(title: NSLocalizedString("Open in Safari", comment: ""), style: .default) { [weak self] _ in
|
|
guard let strongSelf = self else { return }
|
|
let controller = SFSafariViewController(url: strongSelf.repoUrl)
|
|
strongSelf.present(controller, animated: true)
|
|
}
|
|
}
|
|
|
|
func viewOwnerAction() -> UIAlertAction {
|
|
return UIAlertAction(title: NSLocalizedString("View Owner's Profile", comment: ""), style: .default) { [weak self] _ in
|
|
guard let strongSelf = self else { return }
|
|
let controller = SFSafariViewController(url: strongSelf.repoOwnerUrl)
|
|
strongSelf.present(controller, animated: true)
|
|
}
|
|
|
|
}
|
|
|
|
func onMore(sender: UIBarButtonItem) {
|
|
let alert = UIAlertController()
|
|
|
|
alert.addAction(shareAction(sender: sender))
|
|
alert.addAction(safariAction())
|
|
alert.addAction(viewOwnerAction())
|
|
alert.popoverPresentationController?.barButtonItem = sender
|
|
present(alert, animated: true)
|
|
}
|
|
|
|
// MARK: Data Loading/Paging
|
|
|
|
private func update(dismissRefresh: Bool, animated: Bool) {
|
|
feed.finishLoading(dismissRefresh: dismissRefresh, animated: animated)
|
|
}
|
|
|
|
func reload() {
|
|
client.fetchReadme { result in
|
|
switch result {
|
|
case .error: break
|
|
case .success(let readme):
|
|
self.dataSource.setReadme(
|
|
readme,
|
|
width: self.view.bounds.width,
|
|
owner: self.repo.owner,
|
|
repo: self.repo.name,
|
|
completion: {
|
|
self.update(dismissRefresh: true, animated: true)
|
|
})
|
|
}
|
|
}
|
|
|
|
client.load(containerWidth: view.bounds.width) { result in
|
|
switch result {
|
|
case .error: break
|
|
case .success(let payload):
|
|
self.dataSource.reset(
|
|
issues: payload.issues.models,
|
|
issuesNextPage: payload.issues.nextPage,
|
|
pullRequests: payload.pullRequests.models,
|
|
pullRequestsNextPage: payload.pullRequests.nextPage
|
|
)
|
|
self.update(dismissRefresh: true, animated: true)
|
|
}
|
|
}
|
|
}
|
|
|
|
func loadNextPage() {
|
|
let width = view.bounds.width
|
|
|
|
switch dataSource.state {
|
|
case .readme: return
|
|
case .issues:
|
|
client.loadMoreIssues(nextPage: dataSource.issuesNextPage, containerWidth: width, completion: { result in
|
|
switch result {
|
|
case .error: break
|
|
case .success(let payload):
|
|
self.dataSource.appendIssues(issues: payload.models, page: payload.nextPage)
|
|
self.update(dismissRefresh: true, animated: false)
|
|
}
|
|
})
|
|
case .pullRequests:
|
|
client.loadMorePullRequests(nextPage: dataSource.pullRequestsNextPage, containerWidth: width, completion: { result in
|
|
switch result {
|
|
case .error: break
|
|
case .success(let payload):
|
|
self.dataSource.appendPullRequests(pullRequests: payload.models, page: payload.nextPage)
|
|
self.update(dismissRefresh: true, animated: false)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// MARK: FeedDelegate
|
|
|
|
func loadFromNetwork(feed: Feed) {
|
|
reload()
|
|
}
|
|
|
|
func loadNextPage(feed: Feed) -> Bool {
|
|
return false
|
|
}
|
|
|
|
// MARK: ListAdapterDataSource
|
|
|
|
func objects(for listAdapter: ListAdapter) -> [ListDiffable] {
|
|
var builder = [ListDiffable]()
|
|
builder.append(dataSource.selection)
|
|
|
|
let models = dataSource.selectionModels
|
|
builder += models
|
|
|
|
if models.count == 0, feed.status == .idle {
|
|
switch dataSource.state {
|
|
case .readme: builder.append(noReadmeResultsKey)
|
|
case .issues: builder.append(noIssuesResultsKey)
|
|
case .pullRequests: builder.append(noPullRequestsResultsKey)
|
|
}
|
|
}
|
|
|
|
return builder
|
|
}
|
|
|
|
func listAdapter(_ listAdapter: ListAdapter, sectionControllerFor object: Any) -> ListSectionController {
|
|
guard let object = object as? ListDiffable else { fatalError("Object does not conform to ListDiffable") }
|
|
|
|
// 28 is the default height of UISegmentedControl
|
|
let controlHeight = 28 + 2*Styles.Sizes.rowSpacing
|
|
|
|
if object === noReadmeResultsKey {
|
|
return RepositoryEmptyResultsSectionController(
|
|
topInset: controlHeight,
|
|
topLayoutGuide: topLayoutGuide,
|
|
bottomLayoutGuide: bottomLayoutGuide,
|
|
type: .readme
|
|
)
|
|
} else if object === noIssuesResultsKey {
|
|
return RepositoryEmptyResultsSectionController(
|
|
topInset: controlHeight,
|
|
topLayoutGuide: topLayoutGuide,
|
|
bottomLayoutGuide: bottomLayoutGuide,
|
|
type: .issues
|
|
)
|
|
} else if object === noPullRequestsResultsKey {
|
|
return RepositoryEmptyResultsSectionController(
|
|
topInset: controlHeight,
|
|
topLayoutGuide: topLayoutGuide,
|
|
bottomLayoutGuide: bottomLayoutGuide,
|
|
type: .pullRequests
|
|
)
|
|
} else if object === dataSource.selection {
|
|
return SegmentedControlSectionController(delegate: self, height: controlHeight)
|
|
} else if object === dataSource.loadMore {
|
|
return LoadMoreSectionController(delegate: self)
|
|
} else if object is RepositoryIssueSummaryModel {
|
|
return RepositorySummarySectionController(client: client.githubClient, repo: repo)
|
|
} else if object is RepositoryReadmeModel {
|
|
return RepositoryReadmeSectionController()
|
|
}
|
|
|
|
fatalError("Could not find section controller for object")
|
|
}
|
|
|
|
func emptyView(for listAdapter: ListAdapter) -> UIView? {
|
|
return nil
|
|
}
|
|
|
|
// MARK: SegmentedControlSectionControllerDelegate
|
|
|
|
func didChangeSelection(sectionController: SegmentedControlSectionController, model: SegmentedControlModel) {
|
|
update(dismissRefresh: false, animated: false)
|
|
}
|
|
|
|
// MARK: LoadMoreSectionControllerDelegate
|
|
|
|
func didSelect(sectionController: LoadMoreSectionController) {
|
|
loadNextPage()
|
|
}
|
|
}
|