Skip to content

Commit

Permalink
Merge pull request #55 from mozilla-services/remove-device
Browse files Browse the repository at this point in the history
Remove device
  • Loading branch information
corbyziesman committed Nov 8, 2019
2 parents bf720b9 + 717a560 commit d25d83f
Show file tree
Hide file tree
Showing 29 changed files with 337 additions and 120 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,7 @@
<dict>
<key>IDEWorkspaceSharedSettings_AutocreateContextsIfNeeded</key>
<false/>
<key>PreviewsEnabled</key>
<false/>
</dict>
</plist>
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,22 @@
//

import UIKit
import RxSwift
import RxCocoa

class DeviceDataSourceAndDelegate: NSObject {
private var devices: [Device]
private var tableView: UITableView
var removeDeviceEvent = PublishSubject<String>()
private let disposeBag = DisposeBag()
private var user: User? { return DependencyFactory.sharedFactory.accountManager.user }
private var devices: [Device] { return user?.deviceList ?? [] }

private var canAddDevice: Bool {
return devices.count < 5
private var headerHeight: CGFloat {
guard let user = user, user.hasTooManyDevices else { return 0 }
return DeviceLimitReachedView.height
}

private var sectionHeight: CGFloat {
return canAddDevice ? 0 : DeviceLimitReachedView.height
}

init(devices: [Device], tableView: UITableView) {
self.devices = devices
init(tableView: UITableView) {
self.tableView = tableView
super.init()

Expand All @@ -32,17 +33,28 @@ class DeviceDataSourceAndDelegate: NSObject {

let nib = UINib.init(nibName: String(describing: DeviceManagementCell.self), bundle: Bundle.main)
tableView.register(nib, forCellReuseIdentifier: String(describing: DeviceManagementCell.self))

self.removeDeviceEvent.subscribe { event in
if let deviceKey = event.element {
DependencyFactory.sharedFactory.accountManager.removeDevice(with: deviceKey) { _ in
DispatchQueue.main.async {
tableView.reloadData()
}
}
}
}.disposed(by: disposeBag)
}
}

// MARK: - UITableViewDelegate
extension DeviceDataSourceAndDelegate: UITableViewDelegate {
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
return canAddDevice ? nil : tableView.dequeueReusableHeaderFooterView(withIdentifier: String(describing: DeviceLimitReachedView.self))
guard let user = user, user.hasTooManyDevices else { return nil }
return tableView.dequeueReusableHeaderFooterView(withIdentifier: String(describing: DeviceLimitReachedView.self))
}

func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return canAddDevice ? 0 : sectionHeight
return headerHeight
}
}

Expand All @@ -56,7 +68,7 @@ extension DeviceDataSourceAndDelegate: UITableViewDataSource {
guard let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: DeviceManagementCell.self), for: indexPath) as? DeviceManagementCell else {
return UITableViewCell(frame: .zero)
}
cell.setup(with: devices[indexPath.row])
cell.setup(with: devices[indexPath.row], event: removeDeviceEvent)

return cell
}
Expand Down
16 changes: 11 additions & 5 deletions FirefoxPrivateNetworkVPN/Networking/Codable Models/Device.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,20 @@

import Foundation

struct Device: UserDefaulting {
static var userDefaultsKey = "currentDevice"
struct Device: Codable, UserDefaulting {

static let userDefaultsKey = "currentDevice"
let name: String
let publicKey: String
let ipv4Address: String
let ipv6Address: String
let createdAtDate: Date

private let createdAtDateString: String
var isBeingRemoved: Bool = false

var isCurrentDevice: Bool {
return self == Device.fetchFromUserDefaults()
}

init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
Expand Down Expand Up @@ -47,8 +51,10 @@ struct Device: UserDefaulting {
case ipv6Address = "ipv6_address"
case createAtDate = "created_at"
}
}

var isCurrentDevice: Bool {
return publicKey == Device.fetchFromUserDefaults()?.publicKey
extension Device: Equatable {
static func == (lhs: Device, rhs: Device) -> Bool {
return lhs.publicKey == rhs.publicKey
}
}
39 changes: 37 additions & 2 deletions FirefoxPrivateNetworkVPN/Networking/Codable Models/User.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,23 @@ struct User: Codable {
let displayName: String
let avatarURL: URL?
let vpnSubscription: Subscription
let devices: [Device]
let maxDevices: Int

private var devices: [Device]
private let avatarUrlString: String

var deviceList: [Device] {
get {
return devices
}
set {
devices = newValue.sorted { return $0.isCurrentDevice && !$1.isCurrentDevice }
}
}

var hasTooManyDevices: Bool {
return devices.count > maxDevices
}

init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)

Expand All @@ -30,6 +42,29 @@ struct User: Codable {

let subscriptionsContainer = try container.nestedContainer(keyedBy: CodingKeys.self, forKey: .subscriptions)
vpnSubscription = try subscriptionsContainer.decode(Subscription.self, forKey: .vpn)

deviceList = devices
}

mutating func deviceIsBeingRemoved(with key: String) {
for (index, each) in deviceList.enumerated() where each.publicKey == key {
deviceList[index].isBeingRemoved = true
return
}
}

mutating func deviceFailedRemoval(with key: String) {
for (index, each) in deviceList.enumerated() where each.publicKey == key {
deviceList[index].isBeingRemoved = false
return
}
}

mutating func removeDevice(with key: String) {
for (index, each) in deviceList.enumerated() where each.publicKey == key {
deviceList.remove(at: index)
return
}
}

func encode(to encoder: Encoder) throws {
Expand Down
37 changes: 25 additions & 12 deletions FirefoxPrivateNetworkVPN/Networking/GuardianAPI.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,6 @@ class GuardianAPI: NetworkRequesting {
static func verify(urlString: String, completion: @escaping (Result<VerifyResponse, Error>) -> Void) {
let urlRequest = GuardianURLRequestBuilder.urlRequest(fullUrlString: urlString, type: .GET)
NetworkLayer.fireURLRequest(with: urlRequest) { result in
if case let .failure(error) = result {
print(error)
}
completion(result.flatMap { $0.convert(to: VerifyResponse.self) })
}
}
Expand All @@ -42,32 +39,48 @@ class GuardianAPI: NetworkRequesting {
}
}

static func addDevice(with token: String, body: [String: Any], completion: @escaping (Result<Device, Error>) -> Void) {
static func addDevice(with token: String, body: [String: Any], completion: @escaping (Result<Device, GuardianAPIError>) -> Void) {
guard let data = try? JSONSerialization.data(withJSONObject: body) else {
completion(Result { throw GuardianFailReason.couldNotCreateBody })
completion(.failure(.couldNotCreateBody))
return
}

let urlRequest = GuardianURLRequestBuilder.urlRequest(request: .addDevice, type: .POST, httpHeaderParams: headers(with: token), body: data)

NetworkLayer.fireURLRequest(with: urlRequest) { result in
completion(result.flatMap { $0.convert(to: Device.self) })
}
NetworkLayer.fire(urlRequest: urlRequest, dataHandler: { result in
switch result {
case .success(let data):
if let device = try? data.convert(to: Device.self).get() {
completion(.success(device))
return
}
case .failure(let error):
print(error)
completion(.failure(error))
// completion(result.flatMap { try $0.convert(to: Device.self) })
}
})
}

static func removeDevice(with deviceKey: String, completion: @escaping (Result<Data, Error>) -> Void) {
static func removeDevice(with token: String, deviceKey: String, completion: @escaping (Result<Void, Error>) -> Void) {
guard let encodedKey = deviceKey.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed) else {
completion(Result { throw GuardianFailReason.emptyToken })
return
}

let urlRequest = GuardianURLRequestBuilder.urlRequest(request: .removeDevice(encodedKey), type: .DELETE)
let urlRequest = GuardianURLRequestBuilder.urlRequest(request: .removeDevice(encodedKey), type: .DELETE, httpHeaderParams: headers(with: token))

NetworkLayer.fireURLRequest(with: urlRequest, completion: completion)
NetworkLayer.fire(urlRequest: urlRequest, errorHandler: completion)
}

private static func headers(with token: String) -> [String: String] {
return ["Authorization": "Bearer \(token)",
"Content-Type": "application/json"]
}
}

enum GuardianAPIError: Error {
case addDeviceFailure(Data)
case couldNotCreateBody
case other(Error)
case errorWithData(Error, Data?)
}
35 changes: 35 additions & 0 deletions FirefoxPrivateNetworkVPN/Networking/NetworkLayer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,42 @@ class NetworkLayer {
completion(.failure(GuardianFailReason.no200))
}
}
dataTask.resume()
}

static func fire(urlRequest: URLRequest, dataHandler: @escaping (Result<Data, GuardianAPIError>) -> Void) {
let defaultSession = URLSession(configuration: .default)
defaultSession.configuration.timeoutIntervalForRequest = 120

let dataTask = defaultSession.dataTask(with: urlRequest) { data, response, error in
if let error = error {
dataHandler(.failure(GuardianAPIError.errorWithData(error, data)))
} else if let data = data, let response = response as? HTTPURLResponse,
response.statusCode == 200 || response.statusCode == 201 {
dataHandler(.success(data))
} else {
dataHandler(.failure(.other(GuardianFailReason.no200)))
}
}
dataTask.resume()
}

static func fire(urlRequest: URLRequest, errorHandler: @escaping (Result<Void, Error>) -> Void) {
let defaultSession = URLSession(configuration: .default)
defaultSession.configuration.timeoutIntervalForRequest = 120

let dataTask = defaultSession.dataTask(with: urlRequest) { _, response, error in
if let error = error {
errorHandler(.failure(error))
} else if let response = response as? HTTPURLResponse,
response.statusCode == 204 || response.statusCode == 200 || response.statusCode == 201 {
errorHandler(.success(()))
} else {
errorHandler(.failure(GuardianFailReason.no200))
}
}
dataTask.resume()
}
}

extension Data: Error { }
1 change: 1 addition & 0 deletions FirefoxPrivateNetworkVPN/Protocols/AccountManaging.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,5 @@ protocol AccountManaging {
func finishSetupFromVerify(completion: @escaping (Result<Void, Error>) -> Void)
func startHeartbeat()
func countryCodeForCity(_ city: String) -> String?
func removeDevice(with deviceKey: String, completion: @escaping (Result<Void, Error>) -> Void)
}
4 changes: 2 additions & 2 deletions FirefoxPrivateNetworkVPN/Protocols/NetworkRequesting.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,6 @@ protocol NetworkRequesting {
static func accountInfo(token: String, completion: @escaping (Result<User, Error>) -> Void)
static func verify(urlString: String, completion: @escaping (Result<VerifyResponse, Error>) -> Void)
static func availableServers(with token: String, completion: @escaping (Result<[VPNCountry], Error>) -> Void)
static func addDevice(with token: String, body: [String: Any], completion: @escaping (Result<Device, Error>) -> Void)
static func removeDevice(with deviceKey: String, completion: @escaping (Result<Data, Error>) -> Void)
static func addDevice(with token: String, body: [String: Any], completion: @escaping (Result<Device, GuardianAPIError>) -> Void)
static func removeDevice(with token: String, deviceKey: String, completion: @escaping (Result<Void, Error>) -> Void)
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"author" : "xcode"
},
"properties" : {
"template-rendering-intent" : "template",
"preserves-vector-representation" : true
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"author" : "xcode"
},
"properties" : {
"template-rendering-intent" : "template",
"preserves-vector-representation" : true
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"author" : "xcode"
},
"properties" : {
"template-rendering-intent" : "template",
"preserves-vector-representation" : true
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"author" : "xcode"
},
"properties" : {
"template-rendering-intent" : "template",
"preserves-vector-representation" : true
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"author" : "xcode"
},
"properties" : {
"template-rendering-intent" : "template",
"preserves-vector-representation" : true
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"author" : "xcode"
},
"properties" : {
"template-rendering-intent" : "template",
"preserves-vector-representation" : true
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"author" : "xcode"
},
"properties" : {
"template-rendering-intent" : "template",
"preserves-vector-representation" : true
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"author" : "xcode"
},
"properties" : {
"template-rendering-intent" : "template",
"preserves-vector-representation" : true
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"author" : "xcode"
},
"properties" : {
"template-rendering-intent" : "template",
"preserves-vector-representation" : true
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"author" : "xcode"
},
"properties" : {
"template-rendering-intent" : "template",
"preserves-vector-representation" : true
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"author" : "xcode"
},
"properties" : {
"template-rendering-intent" : "template",
"preserves-vector-representation" : true
}
}
Loading

0 comments on commit d25d83f

Please sign in to comment.