This repository has been archived by the owner on Sep 20, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 384
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
New inbox header to select inbox type (all, mentioned, repo, etc) (#2524
) * update contextmenu * setup menu item on inbox * add subscription request * wip networking * add dashboard cell, controller, and model * clean up notification parsing * repo selection working * feature working * update menu to fix centering and revert icon position * adjust ui
- Loading branch information
Showing
35 changed files
with
6,008 additions
and
5,118 deletions.
There are no files selected for viewing
32 changes: 32 additions & 0 deletions
32
Classes/Notifications/Filter/GithubClient+InboxFilterController.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
// | ||
// GithubClient+InboxFilterController.swift | ||
// Freetime | ||
// | ||
// Created by Ryan Nystrom on 12/2/18. | ||
// Copyright © 2018 Ryan Nystrom. All rights reserved. | ||
// | ||
|
||
import Foundation | ||
import GitHubAPI | ||
|
||
extension GithubClient: InboxFilterControllerClient { | ||
|
||
func fetchSubscriptions(completion: @escaping (Result<[RepositoryDetails]>) -> Void) { | ||
guard let username = userSession?.username else { | ||
completion(.error(nil)) | ||
return | ||
} | ||
client.send(V3SubscriptionsRequest(username: username)) { result in | ||
switch result { | ||
case .failure(let error): | ||
completion(.error(error)) | ||
case .success(let data): | ||
let repos = data.data.map { | ||
RepositoryDetails(owner: $0.owner.login, name: $0.name) | ||
} | ||
completion(.success(repos)) | ||
} | ||
} | ||
} | ||
|
||
} |
102 changes: 102 additions & 0 deletions
102
Classes/Notifications/Filter/InboxFilterController.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
// | ||
// InboxFilterController.swift | ||
// Freetime | ||
// | ||
// Created by Ryan Nystrom on 12/2/18. | ||
// Copyright © 2018 Ryan Nystrom. All rights reserved. | ||
// | ||
|
||
import Foundation | ||
import ContextMenu | ||
|
||
protocol InboxFilterControllerClient { | ||
func fetchSubscriptions(completion: @escaping (Result<[RepositoryDetails]>) -> Void) | ||
} | ||
|
||
protocol InboxFilterControllerListener: class { | ||
func didUpdateSelectedFilter(for controller: InboxFilterController) | ||
} | ||
|
||
final class InboxFilterController { | ||
|
||
let client: InboxFilterControllerClient | ||
let announcer = Announcer<InboxFilterControllerListener>() | ||
|
||
private static let filters = [ | ||
InboxFilterModel(type: .unread), | ||
InboxFilterModel(type: .all), | ||
InboxFilterModel(type: .mentioned), | ||
InboxFilterModel(type: .assigned), | ||
InboxFilterModel(type: .created) | ||
] | ||
|
||
private(set) var selected: InboxFilterModel = InboxFilterController.filters[0] { | ||
didSet { | ||
announcer.enumerate { $0.didUpdateSelectedFilter(for: self) } | ||
} | ||
} | ||
private var fetchedFilters = [InboxFilterModel]() | ||
|
||
init(client: InboxFilterControllerClient) { | ||
self.client = client | ||
} | ||
|
||
func update(selection: InboxFilterModel) { | ||
selected = selection | ||
} | ||
|
||
private func selected(model: InboxFilterModel) { | ||
selected = model | ||
} | ||
|
||
private func showRepos(from viewController: UIViewController?) { | ||
guard let viewController = viewController else { return } | ||
ContextMenu.shared.show( | ||
sourceViewController: viewController, | ||
viewController: InboxFilterReposViewController(inboxFilterController: self), | ||
options: ContextMenu.Options( | ||
containerStyle: ContextMenu.ContainerStyle( | ||
backgroundColor: Styles.Colors.menuBackgroundColor.color | ||
) | ||
) | ||
) | ||
} | ||
|
||
func showMenu(from viewController: UIViewController) { | ||
var items: [ContrastContextMenuItem] = InboxFilterController.filters.map { model in | ||
ContrastContextMenuItem( | ||
title: model.type.title, | ||
iconName: model.type.iconName, | ||
iconColor: Styles.Colors.Blue.medium.color, | ||
separator: false, | ||
action: { [weak self] menu in | ||
menu.dismiss(animated: trueUnlessReduceMotionEnabled) | ||
self?.selected(model: model) | ||
}) | ||
} | ||
items.append(ContrastContextMenuItem( | ||
title: NSLocalizedString("Repos", comment: ""), | ||
iconName: "repo", | ||
iconColor: Styles.Colors.Blue.medium.color, | ||
separator: true, | ||
action: { [weak self, weak viewController] menu in | ||
menu.dismiss(animated: trueUnlessReduceMotionEnabled) | ||
self?.showRepos(from: viewController) | ||
})) | ||
|
||
ContextMenu.shared.show( | ||
sourceViewController: viewController, | ||
viewController: ContrastContextMenu(items: items), | ||
options: ContextMenu.Options( | ||
containerStyle: ContextMenu.ContainerStyle( | ||
backgroundColor: Styles.Colors.menuBackgroundColor.color | ||
), | ||
menuStyle: .minimal, | ||
hapticsStyle: .medium, | ||
position: .centerX | ||
), | ||
sourceView: viewController.navigationItem.titleView | ||
) | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
// | ||
// InboxFilterModel.swift | ||
// Freetime | ||
// | ||
// Created by Ryan Nystrom on 12/2/18. | ||
// Copyright © 2018 Ryan Nystrom. All rights reserved. | ||
// | ||
|
||
import Foundation | ||
import IGListKit | ||
|
||
struct InboxFilterModel: ListSwiftDiffable { | ||
|
||
enum FilterType: Equatable { | ||
case unread | ||
case all | ||
case assigned | ||
case created | ||
case mentioned | ||
case repo(owner: String, name: String) | ||
|
||
var title: String { | ||
switch self { | ||
case .unread: return Constants.Strings.inbox | ||
case .all: return NSLocalizedString("All", comment: "") | ||
case .assigned: return NSLocalizedString("Assigned", comment: "") | ||
case .created: return NSLocalizedString("Created", comment: "") | ||
case .mentioned: return NSLocalizedString("Mentioned", comment: "") | ||
case let .repo(_, name): return name | ||
} | ||
} | ||
|
||
var subtitle: String? { | ||
switch self { | ||
case .unread, .all, .assigned, .created, .mentioned: return nil | ||
case let .repo(owner, _): return owner | ||
} | ||
} | ||
|
||
var iconName: String? { | ||
switch self { | ||
case .unread: return "inbox" | ||
case .all: return "bell" | ||
case .assigned: return "person" | ||
case .created: return "plus" | ||
case .mentioned: return "mention" | ||
case .repo: return nil | ||
} | ||
} | ||
|
||
// MARK: Equatable | ||
|
||
static func == (lhs: FilterType, rhs: FilterType) -> Bool { | ||
return lhs.title == rhs.title | ||
&& lhs.subtitle == rhs.subtitle | ||
} | ||
|
||
} | ||
|
||
let type: FilterType | ||
|
||
// MARK: ListSwiftDiffable | ||
|
||
var identifier: String { | ||
return "\(type.title).\(type.subtitle ?? "")" | ||
} | ||
|
||
func isEqual(to value: ListSwiftDiffable) -> Bool { | ||
return value is InboxFilterModel | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
// | ||
// InboxFilterRepoCell.swift | ||
// Freetime | ||
// | ||
// Created by Ryan Nystrom on 12/2/18. | ||
// Copyright © 2018 Ryan Nystrom. All rights reserved. | ||
// | ||
|
||
import UIKit | ||
import SnapKit | ||
|
||
final class InboxFilterRepoCell: SelectableCell { | ||
|
||
private let label = UILabel() | ||
|
||
override init(frame: CGRect) { | ||
super.init(frame: frame) | ||
|
||
accessibilityIdentifier = "inbox-filter-repo-cell" | ||
|
||
label.adjustsFontSizeToFitWidth = true | ||
label.minimumScaleFactor = 0.7 | ||
contentView.addSubview(label) | ||
|
||
label.snp.makeConstraints { make in | ||
make.centerY.equalToSuperview() | ||
make.left.equalTo(Styles.Sizes.gutter) | ||
make.right.lessThanOrEqualTo(-Styles.Sizes.gutter) | ||
} | ||
|
||
addBorder(.bottom, left: Styles.Sizes.gutter) | ||
} | ||
|
||
required init?(coder aDecoder: NSCoder) { | ||
fatalError("init(coder:) has not been implemented") | ||
} | ||
|
||
func configure(owner: String, name: String) { | ||
let text = NSMutableAttributedString(string: "\(owner)/", attributes: [ | ||
.foregroundColor: Styles.Colors.Gray.light.color, | ||
.font: Styles.Text.body.preferredFont | ||
]) | ||
text.append(NSAttributedString(string: "\(name)", attributes: [ | ||
.foregroundColor: UIColor.white, | ||
.font: Styles.Text.bodyBold.preferredFont | ||
])) | ||
label.attributedText = text | ||
} | ||
|
||
} |
45 changes: 45 additions & 0 deletions
45
Classes/Notifications/Filter/InboxFilterRepoSectionController.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
// | ||
// InboxFilterRepoSectionController.swift | ||
// Freetime | ||
// | ||
// Created by Ryan Nystrom on 12/2/18. | ||
// Copyright © 2018 Ryan Nystrom. All rights reserved. | ||
// | ||
|
||
import IGListKit | ||
|
||
final class InboxFilterRepoSectionController: ListSwiftSectionController<RepositoryDetails> { | ||
|
||
private let inboxFilterController: InboxFilterController | ||
|
||
init(inboxFilterController: InboxFilterController) { | ||
self.inboxFilterController = inboxFilterController | ||
super.init() | ||
} | ||
|
||
override func createBinders(from value: RepositoryDetails) -> [ListBinder] { | ||
return [ | ||
binder( | ||
value, | ||
cellType: ListCellType.class(InboxFilterRepoCell.self), | ||
size: { | ||
return $0.collection.cellSize(with: Styles.Sizes.tableCellHeight) | ||
}, configure: { | ||
$0.configure( | ||
owner: $1.value.owner, | ||
name: $1.value.name | ||
) | ||
}, didSelect: { [weak self] context in | ||
self?.didSelect(value: context.value) | ||
}) | ||
] | ||
} | ||
|
||
private func didSelect(value: RepositoryDetails) { | ||
viewController?.dismiss(animated: trueUnlessReduceMotionEnabled) | ||
inboxFilterController.update(selection: InboxFilterModel( | ||
type: .repo(owner: value.owner, name: value.name)) | ||
) | ||
} | ||
|
||
} |
62 changes: 62 additions & 0 deletions
62
Classes/Notifications/Filter/InboxFilterReposViewController.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
// | ||
// InboxFilterReposViewController.swift | ||
// Freetime | ||
// | ||
// Created by Ryan Nystrom on 12/2/18. | ||
// Copyright © 2018 Ryan Nystrom. All rights reserved. | ||
// | ||
|
||
import UIKit | ||
import IGListKit | ||
import Squawk | ||
|
||
final class InboxFilterReposViewController: BaseListViewController<String>, | ||
BaseListViewControllerDataSource { | ||
|
||
private let inboxFilterController: InboxFilterController | ||
private var repos = [RepositoryDetails]() | ||
|
||
init(inboxFilterController: InboxFilterController) { | ||
self.inboxFilterController = inboxFilterController | ||
super.init(emptyErrorMessage: NSLocalizedString("Error loading repos", comment: "")) | ||
dataSource = self | ||
title = NSLocalizedString("Watched Repos", comment: "") | ||
preferredContentSize = Styles.Sizes.contextMenuSize | ||
feed.collectionView.backgroundColor = Styles.Colors.menuBackgroundColor.color | ||
feed.setLoadingSpinnerColor(to: .white) | ||
} | ||
|
||
required init?(coder aDecoder: NSCoder) { | ||
fatalError("init(coder:) has not been implemented") | ||
} | ||
|
||
override func viewDidLoad() { | ||
super.viewDidLoad() | ||
navigationController?.navigationBar.titleTextAttributes = [ | ||
.foregroundColor: UIColor.white | ||
] | ||
} | ||
|
||
override func fetch(page: String?) { | ||
inboxFilterController.client.fetchSubscriptions { [weak self] result in | ||
switch result { | ||
case .error(let error): | ||
Squawk.show(error: error) | ||
case .success(let repos): | ||
self?.repos = repos | ||
self?.update() | ||
} | ||
} | ||
} | ||
|
||
// MARK: BaseListViewControllerDataSource | ||
|
||
func models(adapter: ListSwiftAdapter) -> [ListSwiftPair] { | ||
return repos.map { [inboxFilterController] model in | ||
ListSwiftPair.pair(model, { | ||
InboxFilterRepoSectionController(inboxFilterController: inboxFilterController) | ||
}) | ||
} | ||
} | ||
|
||
} |
Oops, something went wrong.