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

Commit

Permalink
New inbox header to select inbox type (all, mentioned, repo, etc) (#2524
Browse files Browse the repository at this point in the history
)

* 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
rnystrom authored Dec 16, 2018
1 parent d16fc0c commit 7381566
Show file tree
Hide file tree
Showing 35 changed files with 6,008 additions and 5,118 deletions.
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 Classes/Notifications/Filter/InboxFilterController.swift
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
)
}

}
72 changes: 72 additions & 0 deletions Classes/Notifications/Filter/InboxFilterModel.swift
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
}

}
50 changes: 50 additions & 0 deletions Classes/Notifications/Filter/InboxFilterRepoCell.swift
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
}

}
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 Classes/Notifications/Filter/InboxFilterReposViewController.swift
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)
})
}
}

}
Loading

0 comments on commit 7381566

Please sign in to comment.