Files
GitHawk/Classes/Notifications/NotificationClient.swift
Ryan Nystrom 29f7f67ff8 View notifications by repository (#1291)
* get subscriptions api

* stub subscription ctrl

* use icon bar items, inject inbox type

* wire up repo list

* push notif, show all for repos

* clean path

* fix spinner bar item bugs

* call correct mark-read apis

* alert titles

* sort repos by alpha

* unify show-all callsite

* nits
2017-12-19 10:11:59 -05:00

173 lines
5.6 KiB
Swift

//
// NotificationClient.swift
// Freetime
//
// Created by Ryan Nystrom on 6/30/17.
// Copyright © 2017 Ryan Nystrom. All rights reserved.
//
import Foundation
final class NotificationClient {
struct NotificationRepository {
let owner: String
let name: String
}
private var openedNotificationIDs = Set<String>()
let githubClient: GithubClient
init(githubClient: GithubClient) {
self.githubClient = githubClient
}
// Public API
static private let openOnReadKey = "com.freetime.NotificationClient.read-on-open"
static func readOnOpen() -> Bool {
return UserDefaults.standard.bool(forKey: openOnReadKey)
}
static func setReadOnOpen(open: Bool) {
UserDefaults.standard.set(open, forKey: openOnReadKey)
}
// https://developer.github.com/v3/activity/notifications/#list-your-notifications
func fetchNotifications(
repo: NotificationRepository? = nil,
all: Bool = false,
participating: Bool = false,
since: Date? = nil,
page: Int = 1,
before: Date? = nil,
width: CGFloat,
completion: @escaping (Result<([NotificationViewModel], Int?)>) -> Void
) {
var parameters: [String: Any] = [
"all": all ? "true" : "false",
"participating": participating ? "true" : "false",
"page": page,
"per_page": "50"
]
if let since = since {
parameters["since"] = GithubAPIDateFormatter().string(from: since)
}
if let before = before {
parameters["before"] = GithubAPIDateFormatter().string(from: before)
}
let cache = githubClient.cache
githubClient.request(GithubClient.Request(
path: path(repo: repo),
method: .get,
parameters: parameters,
headers: nil
) { response, nextPage in
if let notifications = (response.value as? [[String:Any]])?.flatMap({ NotificationResponse(json: $0) }) {
let viewModels = CreateViewModels(containerWidth: width, notifications: notifications)
cache.set(values: viewModels)
completion(.success((viewModels, nextPage?.next)))
} else {
completion(.error(response.error))
}
})
}
func markAllNotifications(completion: @escaping (Bool) -> Void) {
markNotifications(repo: nil, completion: completion)
}
func markRepoNotifications(repo: NotificationRepository, completion: @escaping (Bool) -> Void) {
markNotifications(repo: repo, completion: completion)
}
func notificationOpened(id: String) -> Bool {
return openedNotificationIDs.contains(id)
}
func markNotificationRead(id: String, isOpen: Bool) {
let oldModel = githubClient.cache.get(id: id) as NotificationViewModel?
if isOpen {
openedNotificationIDs.insert(id)
} else {
// optimistically set the model to read
// if the request fails, replace this model w/ the old one.
if let old = oldModel {
githubClient.cache.set(value: NotificationViewModel(
id: old.id,
title: old.title,
type: old.type,
date: old.date,
read: true,
owner: old.owner,
repo: old.repo,
identifier: old.identifier
))
}
}
githubClient.request(GithubClient.Request(
path: "notifications/threads/\(id)",
method: .patch) { [weak self] response, _ in
// https://developer.github.com/v3/activity/notifications/#mark-a-thread-as-read
if response.response?.statusCode != 205 {
if isOpen {
self?.openedNotificationIDs.remove(id)
} else if let old = oldModel {
self?.githubClient.cache.set(value: old)
}
}
})
}
func fetchWatchedRepositories(completion: @escaping (Result<[Repository]>) -> Void) {
guard let viewer = githubClient.userSession?.username else {
completion(.error(nil))
return
}
githubClient.request(GithubClient.Request(
path: "users/\(viewer)/subscriptions"
) { response, _ in
// https://developer.github.com/v3/activity/watching/#list-repositories-being-watched
if let jsonArr = response.value as? [ [String: Any] ] {
var repos = [Repository]()
for json in jsonArr {
if let repo = Repository(json: json) {
repos.append(repo)
}
}
completion(.success(repos.sorted { $0.name < $1.name }))
} else {
completion(.error(response.error))
}
})
}
// MARK: Private API
func path(repo: NotificationRepository?) -> String {
if let repo = repo {
return "repos/\(repo.owner)/\(repo.name)/notifications"
} else {
return "notifications"
}
}
func markNotifications(repo: NotificationRepository? = nil, completion: @escaping (Bool) -> Void) {
githubClient.request(GithubClient.Request(
path: path(repo: repo),
method: .put
) { response, _ in
let success = response.response?.statusCode == 205
completion(success)
})
}
}