支持多服务器

This commit is contained in:
Fin 2022-04-07 16:04:34 +08:00
parent 51ac6c01e4
commit 60d5474acc
30 changed files with 704 additions and 3 deletions

View File

@ -14,6 +14,8 @@
060481EE250F404500BC9799 /* SoundsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 060481ED250F404500BC9799 /* SoundsViewController.swift */; };
060481F0250F51CA00BC9799 /* SoundCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 060481EF250F51CA00BC9799 /* SoundCell.swift */; };
0604F7DF20620D4900B32F09 /* ServerManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0604F7DE20620D4900B32F09 /* ServerManager.swift */; };
06172FDA27F6DAEF002333A4 /* ServerListTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06172FD927F6DAEF002333A4 /* ServerListTableViewCell.swift */; };
06172FDC27F6DB06002333A4 /* ServerListTableViewCellViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06172FDB27F6DB06002333A4 /* ServerListTableViewCellViewModel.swift */; };
062B98C3251B2762004562E7 /* BKButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 062B98C2251B2762004562E7 /* BKButton.swift */; };
062B98C8251B27AE004562E7 /* UINavigationItem+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 062B98C7251B27AE004562E7 /* UINavigationItem+Extension.swift */; };
0632050F250B6DD4001561EC /* gotosleep.caf in Resources */ = {isa = PBXBuildFile; fileRef = 063204F0250B6DD1001561EC /* gotosleep.caf */; };
@ -88,6 +90,8 @@
06802E5320ECC40C00767047 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0661A549204FDA4100965E4E /* Assets.xcassets */; };
06840DBB272298FB001B3193 /* BKColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06840DBA272298FB001B3193 /* BKColor.swift */; };
06885EB6247FB9880004A303 /* MessageSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06885EB5247FB9880004A303 /* MessageSettingsViewController.swift */; };
068EC15827ED99C900D5D11E /* ServerListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 068EC15727ED99C900D5D11E /* ServerListViewController.swift */; };
068EC15A27ED99E700D5D11E /* ServerListViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 068EC15927ED99E700D5D11E /* ServerListViewModel.swift */; };
068F66B3247BD84C00DAD25A /* MessageListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 068F66B2247BD84C00DAD25A /* MessageListViewController.swift */; };
06AE3118266F4E2E00B39FBB /* GroupFilterViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06AE3117266F4E2E00B39FBB /* GroupFilterViewController.swift */; };
06AE311A266F4E6600B39FBB /* GroupFilterViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06AE3119266F4E6600B39FBB /* GroupFilterViewModel.swift */; };
@ -174,6 +178,8 @@
060481ED250F404500BC9799 /* SoundsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SoundsViewController.swift; sourceTree = "<group>"; };
060481EF250F51CA00BC9799 /* SoundCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SoundCell.swift; sourceTree = "<group>"; };
0604F7DE20620D4900B32F09 /* ServerManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerManager.swift; sourceTree = "<group>"; };
06172FD927F6DAEF002333A4 /* ServerListTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerListTableViewCell.swift; sourceTree = "<group>"; };
06172FDB27F6DB06002333A4 /* ServerListTableViewCellViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerListTableViewCellViewModel.swift; sourceTree = "<group>"; };
062B98C2251B2762004562E7 /* BKButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BKButton.swift; sourceTree = "<group>"; };
062B98C7251B27AE004562E7 /* UINavigationItem+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UINavigationItem+Extension.swift"; sourceTree = "<group>"; };
063204F0250B6DD1001561EC /* gotosleep.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = gotosleep.caf; sourceTree = "<group>"; };
@ -252,6 +258,8 @@
0683487220510FB20024B6DA /* UserNotificationsUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UserNotificationsUI.framework; path = System/Library/Frameworks/UserNotificationsUI.framework; sourceTree = SDKROOT; };
06840DBA272298FB001B3193 /* BKColor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BKColor.swift; sourceTree = "<group>"; };
06885EB5247FB9880004A303 /* MessageSettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageSettingsViewController.swift; sourceTree = "<group>"; };
068EC15727ED99C900D5D11E /* ServerListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerListViewController.swift; sourceTree = "<group>"; };
068EC15927ED99E700D5D11E /* ServerListViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerListViewModel.swift; sourceTree = "<group>"; };
068F66B2247BD84C00DAD25A /* MessageListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageListViewController.swift; sourceTree = "<group>"; };
06AE3117266F4E2E00B39FBB /* GroupFilterViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupFilterViewController.swift; sourceTree = "<group>"; };
06AE3119266F4E6600B39FBB /* GroupFilterViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupFilterViewModel.swift; sourceTree = "<group>"; };
@ -356,6 +364,8 @@
06AE3117266F4E2E00B39FBB /* GroupFilterViewController.swift */,
06AE3119266F4E6600B39FBB /* GroupFilterViewModel.swift */,
06F11E7627D9D5FB00F00298 /* QRScannerViewController.swift */,
068EC15727ED99C900D5D11E /* ServerListViewController.swift */,
068EC15927ED99E700D5D11E /* ServerListViewModel.swift */,
);
path = Controller;
sourceTree = "<group>";
@ -383,6 +393,8 @@
06C2CF242685BDB80034B127 /* SpacerCell.swift */,
0642B55927EB13F100453D91 /* MutableTextCell.swift */,
0642B55B27EB149900453D91 /* MutableTextCellViewModel.swift */,
06172FD927F6DAEF002333A4 /* ServerListTableViewCell.swift */,
06172FDB27F6DB06002333A4 /* ServerListTableViewCellViewModel.swift */,
);
path = View;
sourceTree = "<group>";
@ -941,8 +953,11 @@
0603706D20E23EC000F4CA05 /* BarkSFSafariViewController.swift in Sources */,
06AE311A266F4E6600B39FBB /* GroupFilterViewModel.swift in Sources */,
06C5953124811392006B98F3 /* ArchiveSettingCell.swift in Sources */,
068EC15A27ED99E700D5D11E /* ServerListViewModel.swift in Sources */,
06172FDC27F6DB06002333A4 /* ServerListTableViewCellViewModel.swift in Sources */,
065BE4502563D939002A8CA4 /* SoundCellViewModel.swift in Sources */,
06B1158F247BB1FB006D91FB /* Message.swift in Sources */,
06172FDA27F6DAEF002333A4 /* ServerListTableViewCell.swift in Sources */,
06C595362481160F006B98F3 /* BKLabel.swift in Sources */,
0637FA7820E0926D00E80174 /* BarkTargetType.swift in Sources */,
064CABA6256BE9510018155C /* PreviewModel.swift in Sources */,
@ -969,6 +984,7 @@
06885EB6247FB9880004A303 /* MessageSettingsViewController.swift in Sources */,
06F11E7727D9D5FB00F00298 /* QRScannerViewController.swift in Sources */,
06C5952D2480E3F8006B98F3 /* LabelCell.swift in Sources */,
068EC15827ED99C900D5D11E /* ServerListViewController.swift in Sources */,
0637FA7C20E0930E00E80174 /* BarkApi.swift in Sources */,
06C2CF252685BDB80034B127 /* SpacerCell.swift in Sources */,
06BBB8BC2567B3AD0076F63E /* ArchiveSettingCellViewModel.swift in Sources */,

View File

@ -0,0 +1,26 @@
{
"images" : [
{
"filename" : "baseline_keyboard_arrow_down_black_24pt_1x.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "baseline_keyboard_arrow_down_black_24pt_2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "baseline_keyboard_arrow_down_black_24pt_3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"template-rendering-intent" : "template"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 126 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 186 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 243 B

View File

@ -0,0 +1,26 @@
{
"images" : [
{
"filename" : "baseline_remove_black_20pt_1x.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "baseline_remove_black_20pt_2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "baseline_remove_black_20pt_3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"template-rendering-intent" : "template"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 111 B

View File

@ -0,0 +1,26 @@
{
"images" : [
{
"filename" : "baseline_wifi_black_20pt_1x.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "baseline_wifi_black_20pt_2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "baseline_wifi_black_20pt_3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"template-rendering-intent" : "template"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 263 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 455 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 636 B

View File

@ -0,0 +1,26 @@
{
"images" : [
{
"filename" : "baseline_wifi_off_black_20pt_1x.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "baseline_wifi_off_black_20pt_2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "baseline_wifi_off_black_20pt_3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"template-rendering-intent" : "template"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 304 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 523 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 726 B

View File

@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "offline@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

View File

@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "online@3X.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

View File

@ -105,3 +105,17 @@ badge = "Modify a Notification Badge";
badgeNotice = "The notification badge can be set with a number.";
deviceTokenInfo = "Token for APNs, click to copy to clipboard.";
serverList = "Server List";
copyAddressAndKey = "Copy address and key";
resetKey = "Reset or Restore Key";
resetKeyDesc = "Click confirm to reset, enter the old Key to restore";
resetKeyPlaceholder = "Do not enter or enter the old Key";
confirm = "Confirm";
deleteServer = "Delete server";
confirmDeleteServer = "Sure to delete the server?";
deleteFailed = "Cannot be deleted, the server must keep at least one";
deletedSuccessfully = "Deleted successfully";
resetFailed = "Reset failed";
resetFailed2 = "Failed to reset, did not get DeviceToken";

View File

@ -108,3 +108,17 @@ badge = "设置角标";
badgeNotice = "可以为推送设置角标,角标可以是任意数字。";
deviceTokenInfo = "用于 APNs 推送的 Token请勿泄露点击可复制到剪切板。";
serverList = "服务器列表";
copyAddressAndKey = "复制地址和Key";
resetKey = "重置或还原Key";
resetKeyDesc = "重置直接点确定还原请先输入旧的Key";
resetKeyPlaceholder = "不输入或输入旧的Key";
confirm = "确定";
deleteServer = "删除服务器";
confirmDeleteServer = "确定删除服务器吗?";
deleteFailed = "不能删除,服务器至少需保留一个";
deletedSuccessfully = "删除成功";
resetFailed = "重置失败";
resetFailed2 = "重置失败,没有获取到 DeviceToken";

View File

@ -81,6 +81,12 @@ class ServerManager: NSObject {
saveServers()
}
func updateServerKey(server: Server) {
let foundServer = self.servers.first{ $0.id == server.id }
foundServer?.key = server.key
saveServers()
}
/// server server ``, `server`
func removeServer(server: Server) {
self.servers.removeAll { $0.id == server.id }
@ -88,6 +94,8 @@ class ServerManager: NSObject {
self.servers.append(
Server(id: UUID().uuidString, address: defaultServer, key: "")
)
}
if self.currentServer.id == server.id {
self.setCurrentServer(serverId: self.servers[0].id)
}
saveServers()
@ -140,7 +148,9 @@ class ServerManager: NSObject {
.merge(apis)
.subscribe { result in
// server
result.0.key = result.1
if result.2 == .ok {
result.0.key = result.1
}
result.0.state = result.2
// server

View File

@ -51,7 +51,7 @@ class HomeViewController: BaseViewController<HomeViewModel> {
navigationItem.setBarButtonItems(items: [
UIBarButtonItem(customView: newButton),
// UIBarButtonItem(customView: serversButton),
UIBarButtonItem(customView: serversButton),
], position: .right)
self.view.addSubview(self.tableView)
@ -98,6 +98,7 @@ class HomeViewController: BaseViewController<HomeViewModel> {
let output = viewModel.transform(
input: HomeViewModel.Input(
addCustomServerTap: newButton.rx.tap.asDriver(),
serverListTap: serversButton.rx.tap.asDriver(),
viewDidAppear: self.rx.methodInvoked(#selector(viewDidAppear(_:)))
.map { _ in () }
.asDriver(onErrorDriveWith: .empty()),
@ -132,6 +133,11 @@ class HomeViewController: BaseViewController<HomeViewModel> {
self?.pushViewModel(viewModel: viewModel)
})
.disposed(by: rx.disposeBag)
output.present
.drive(onNext: { [weak self] viewModel in
self?.presentViewModel(viewModel: viewModel)
})
.disposed(by: rx.disposeBag)
// ping clienState
output.clienStateChanged
@ -201,4 +207,13 @@ class HomeViewController: BaseViewController<HomeViewModel> {
self.navigationController?.pushViewController(viewController, animated: true)
}
}
func presentViewModel(viewModel: ViewModel) {
if let viewModel = viewModel as? ServerListViewModel {
let controller = BarkSnackbarController(
rootViewController: BarkNavigationController(
rootViewController: ServerListViewController(viewModel: viewModel)))
self.navigationController?.present(controller, animated: true, completion: nil)
}
}
}

View File

@ -16,6 +16,7 @@ import UserNotifications
class HomeViewModel: ViewModel, ViewModelType {
struct Input {
let addCustomServerTap: Driver<Void>
let serverListTap: Driver<Void>
let viewDidAppear: Driver<Void>
let start: Driver<Void>
let clientState: Driver<Client.ClienState>
@ -26,6 +27,7 @@ class HomeViewModel: ViewModel, ViewModelType {
struct Output {
let previews: Driver<[SectionModel<String, PreviewCardCellViewModel>]>
let push: Driver<ViewModel>
let present: Driver<ViewModel>
let title: Driver<String>
let clienStateChanged: Driver<Client.ClienState>
let tableViewHidden: Driver<Bool>
@ -178,10 +180,24 @@ class HomeViewModel: ViewModel, ViewModelType {
}
})
.disposed(by: rx.disposeBag)
let serverList = input.serverListTap.map { ServerListViewModel() as ViewModel }
//
serverList
.flatMapLatest { model -> Driver<Server> in
(model as! ServerListViewModel).currentServerChanged.asDriver(onErrorDriveWith: .empty())
}
.map { server -> String in
(try? server.address.asURL().host) ?? ""
}
.drive(title)
.disposed(by: rx.disposeBag)
return Output(
previews: Driver.just([sectionModel]),
push: Driver<ViewModel>.merge(customServer, noticeTap),
present: serverList.asDriver(),
title: title.asDriver(),
clienStateChanged: clienState.asDriver(onErrorDriveWith: .empty()),
tableViewHidden: tableViewHidden,

View File

@ -0,0 +1,165 @@
//
// ServerListViewController.swift
// Bark
//
// Created by huangfeng on 2022/3/25.
// Copyright © 2022 Fin. All rights reserved.
//
import Material
import RxCocoa
import RxDataSources
import RxSwift
import UIKit
enum ServerActionType {
case copy
case reset(key: String?)
case delete
}
func == (lhs: ServerActionType, rhs: ServerActionType) -> Bool {
switch (lhs, rhs) {
case (.copy, .copy),
(.delete, .delete):
return true
case let (.reset(a), .reset(b)):
return a == b
default:
return false
}
}
class ServerListViewController: BaseViewController<ServerListViewModel> {
let closeButton: BKButton = {
let closeButton = BKButton()
closeButton.setImage(UIImage(named: "baseline_keyboard_arrow_down_black_24pt")?.withRenderingMode(.alwaysTemplate), for: .normal)
closeButton.frame = CGRect(x: 0, y: 0, width: 40, height: 40)
closeButton.hitTestSlop = UIEdgeInsets(top: -10, left: -10, bottom: -10, right: -10)
closeButton.tintColor = BKColor.grey.darken4
return closeButton
}()
let tableView: UITableView = {
let tableView = UITableView()
tableView.separatorStyle = .none
tableView.backgroundColor = BKColor.background.primary
tableView.register(ServerListTableViewCell.self, forCellReuseIdentifier: "\(ServerListTableViewCell.self)")
tableView.contentInset = UIEdgeInsets(top: 20, left: 0, bottom: 0, right: 0)
return tableView
}()
override func makeUI() {
self.title = NSLocalizedString("serverList")
navigationItem.setRightBarButtonItem(item: UIBarButtonItem(customView: closeButton))
self.view.addSubview(tableView)
tableView.snp.makeConstraints { make in
make.edges.equalToSuperview()
}
closeButton.rx.tap.subscribe {[weak self] in
self?.dismiss(animated: true, completion: nil)
} onError: { _ in
}.disposed(by: rx.disposeBag)
}
override func bindViewModel() {
let action = getServerAction()
// server
let copyServer = action.filter { $0.1 == ServerActionType.copy }
.map { $0.0 }.asDriver(onErrorDriveWith: .empty())
// server
let deleteServer = action.filter { $0.1 == ServerActionType.delete }
.map { $0.0 }.asDriver(onErrorDriveWith: .empty())
// server key
let resetServer = action.compactMap { r -> (Server, String?)? in
if case let ServerActionType.reset(key) = r.1 {
return (r.0, key)
}
return nil
}.asDriver(onErrorDriveWith: .empty())
let output = viewModel.transform(input: ServerListViewModel.Input(
copyServer: copyServer,
deleteServer: deleteServer,
resetServer: resetServer
))
let dataSource = RxTableViewSectionedReloadDataSource<SectionModel<String, ServerListTableViewCellViewModel>> { _, tableView, _, item -> UITableViewCell in
if let cell = tableView.dequeueReusableCell(withIdentifier: "\(ServerListTableViewCell.self)") as? ServerListTableViewCell {
cell.bindViewModel(model: item)
return cell
}
return UITableViewCell()
}
// TableView
output.servers
.drive(self.tableView.rx.items(dataSource: dataSource))
.disposed(by: rx.disposeBag)
//
output.copy
.drive(onNext: { [weak self] text in
UIPasteboard.general.string = text
self?.showSnackbar(text: NSLocalizedString("Copy"))
})
.disposed(by: rx.disposeBag)
//
output.showSnackbar
.drive(onNext: { [weak self] text in
self?.showSnackbar(text: text)
})
.disposed(by: rx.disposeBag)
}
func getServerAction() -> Driver<(Server, ServerActionType)> {
return tableView.rx
.modelSelected(ServerListTableViewCellViewModel.self)
.flatMapLatest { viewModel -> PublishRelay<(Server, ServerActionType)> in
let relay = PublishRelay<(Server, ServerActionType)>()
let alertController = UIAlertController(title: nil, message: "\(viewModel.address.value)", preferredStyle: .actionSheet)
alertController.addAction(UIAlertAction(title: NSLocalizedString("copyAddressAndKey"), style: .default, handler: { _ in
relay.accept((viewModel.server, .copy))
}))
alertController.addAction(UIAlertAction(title: NSLocalizedString("resetKey"), style: .default, handler: { _ in
let alertController = UIAlertController(title: NSLocalizedString("resetKey"), message: NSLocalizedString("resetKeyDesc"), preferredStyle: .alert)
alertController.addTextField { textField in
textField.placeholder = NSLocalizedString("resetKeyPlaceholder")
}
alertController.addAction(UIAlertAction(title: NSLocalizedString("confirm"), style: .default, handler: { _ in
relay.accept((viewModel.server, .reset(key: alertController.textFields?.first?.text)))
}))
alertController.addAction(UIAlertAction(title: NSLocalizedString("Cancel"), style: .cancel, handler: nil))
self.navigationController?.present(alertController, animated: true, completion: nil)
}))
alertController.addAction(UIAlertAction(title: NSLocalizedString("deleteServer"), style: .destructive, handler: { _ in
let alertController = UIAlertController(title: nil, message: NSLocalizedString("confirmDeleteServer"), preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: NSLocalizedString("confirm"), style: .destructive, handler: { _ in
relay.accept((viewModel.server, .delete))
}))
alertController.addAction(UIAlertAction(title: NSLocalizedString("Cancel"), style: .cancel, handler: nil))
self.navigationController?.present(alertController, animated: true, completion: nil)
}))
alertController.addAction(UIAlertAction(title: NSLocalizedString("Cancel"), style: .cancel, handler: nil))
self.navigationController?.present(alertController, animated: true, completion: nil)
return relay
}.asDriver(onErrorDriveWith: .empty())
}
}

View File

@ -0,0 +1,158 @@
//
// ServerListViewModel.swift
// Bark
//
// Created by huangfeng on 2022/3/25.
// Copyright © 2022 Fin. All rights reserved.
//
import Differentiator
import Foundation
import Moya
import RxCocoa
import RxSwift
import SwiftyJSON
class ServerListViewModel: ViewModel, ViewModelType {
struct Input {
let copyServer: Driver<Server>
let deleteServer: Driver<Server>
let resetServer: Driver<(Server, String?)>
}
struct Output {
let servers: Driver<[SectionModel<String, ServerListTableViewCellViewModel>]>
let showSnackbar: Driver<String>
let copy: Driver<String>
}
let currentServerChanged = PublishRelay<Server>()
func transform(input: Input) -> Output {
//
let showSnackbar = PublishRelay<String>()
// Server
let copy = input.copyServer.map { server -> String in
"\(server.address)/\(server.key)/"
}
//
let deleteCheck = input.deleteServer.map { server -> Server? in
if ServerManager.shared.servers.count > 1 {
return server
}
return nil
}.asObservable().share()
//
deleteCheck.filter { $0 == nil }
.map { _ in NSLocalizedString("deleteFailed") }
.bind(to: showSnackbar)
.disposed(by: rx.disposeBag)
// deviceToken
let delete = deleteCheck.compactMap { $0 }
.flatMapLatest { server -> Observable<Result<JSON, ApiError>>in
if server.key.count > 0 {
return BarkApi.provider
.request(.register(address: server.address, key: server.key, devicetoken: "deleted"))
.filterResponseError()
}
return Observable.just(Result<JSON, ApiError>.success(JSON()))
}
//
// withLatestFrom delete ,
// input.deleteServer server
let serverDeleted = delete.withLatestFrom(input.deleteServer)
.map { server in
ServerManager.shared.removeServer(server: server)
}
//
serverDeleted.map { NSLocalizedString("deletedSuccessfully") }
.bind(to: showSnackbar)
.disposed(by: rx.disposeBag)
// DeviceToken
let resetServer = input.resetServer
.map { ($0.0, $0.1, Client.shared.deviceToken.value) }
.asObservable()
//
resetServer.filter { ($0.2?.count ?? 0) <= 0 }
.map { _ in NSLocalizedString("resetFailed2") }
.bind(to: showSnackbar)
.disposed(by: rx.disposeBag)
//
let serverReseted = resetServer.filter { ($0.2?.count ?? 0) > 0 }
.flatMapLatest { r -> Observable<Result<JSON, ApiError>> in
let server = r.0
let newKey = r.1
let deviceToken = r.2!
return BarkApi.provider
.request(.register(address: server.address, key: newKey, devicetoken: deviceToken))
.filterResponseError()
}
.map { result -> String? in
switch result {
case .success(let json):
return json["data", "key"].rawString()
case .failure:
return nil
}
}.share()
//
let serverResetSuccess = serverReseted.compactMap { $0 }.withLatestFrom(input.resetServer) { newKey, r in
let server = r.0
server.key = newKey
ServerManager.shared.updateServerKey(server: server)
}
//
serverReseted.filter { $0 == nil }
.map { _ in NSLocalizedString("resetFailed") }
.bind(to: showSnackbar)
.disposed(by: rx.disposeBag)
//
let servers = Observable
.merge(
Observable.just(()),
serverDeleted,
serverResetSuccess
)
.map {
[SectionModel(
model: "servers",
items: ServerManager.shared.servers.map { ServerListTableViewCellViewModel(server: $0) }
)]
}.asDriver(onErrorDriveWith: .empty())
//
let serverChanged = Observable.merge(serverDeleted, serverResetSuccess)
.share()
serverChanged.map {
ServerManager.shared.currentServer
}
.bind(to: self.currentServerChanged)
.disposed(by: rx.disposeBag)
// Client.shared.state
serverChanged.map {
ServerManager.shared.currentServer.state
}
.bind(to: Client.shared.state)
.disposed(by: rx.disposeBag)
return Output(
servers: servers,
showSnackbar: showSnackbar.asDriver(onErrorDriveWith: .empty()),
copy: copy
)
}
}

View File

@ -0,0 +1,117 @@
//
// ServerListTableViewCell.swift
// Bark
//
// Created by huangfeng on 2022/4/1.
// Copyright © 2022 Fin. All rights reserved.
//
import Material
import UIKit
class ServerListTableViewCell: BaseTableViewCell<ServerListTableViewCellViewModel> {
let backgroundPanel: UIView = {
let view = UIView()
view.layer.cornerRadius = 3
view.clipsToBounds = true
view.backgroundColor = BKColor.background.secondary
view.layer.cornerRadius = 25
view.clipsToBounds = true
view.layer.borderColor = BKColor.grey.lighten3.cgColor
view.layer.borderWidth = 1
return view
}()
let addressLabel: UILabel = {
let label = UILabel()
label.font = RobotoFont.medium(with: 14)
label.textColor = BKColor.grey.darken4
label.numberOfLines = 0
return label
}()
let keyLabel: UILabel = {
let label = UILabel()
label.font = RobotoFont.regular(with: 12)
label.textColor = BKColor.grey.darken4
label.numberOfLines = 0
return label
}()
let stateImageView: UIImageView = {
let imageView = UIImageView()
imageView.contentMode = .scaleAspectFit
imageView.layer.cornerRadius = 15
imageView.clipsToBounds = true
return imageView
}()
var state: Bool = false {
didSet {
if state {
stateImageView.image = UIImage(named: "online")
}
else {
stateImageView.image = UIImage(named: "offline")
}
}
}
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
self.selectionStyle = .none
self.backgroundColor = BKColor.background.primary
addSubview(backgroundPanel)
addSubview(stateImageView)
addSubview(addressLabel)
addSubview(keyLabel)
backgroundPanel.snp.makeConstraints { make in
make.left.equalToSuperview().offset(18)
make.right.equalToSuperview().offset(-18)
make.top.equalToSuperview().offset(5)
make.bottom.equalToSuperview().offset(-5)
make.height.equalTo(50)
}
stateImageView.snp.makeConstraints { make in
make.centerY.equalTo(backgroundPanel)
make.left.equalTo(backgroundPanel).offset(13)
make.width.height.equalTo(30)
}
addressLabel.snp.makeConstraints { make in
make.left.equalTo(stateImageView.snp.right).offset(8)
make.top.equalTo(backgroundPanel).offset(8)
make.right.equalTo(backgroundPanel).offset(-8)
}
keyLabel.snp.makeConstraints { make in
make.top.equalTo(addressLabel.snp.bottom).offset(1)
make.left.right.equalTo(addressLabel)
}
}
@available(*, unavailable)
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func bindViewModel(model: ServerListTableViewCellViewModel) {
super.bindViewModel(model: model)
model.address
.bind(to: addressLabel.rx.text)
.disposed(by: rx.reuseBag)
model.key
.bind(to: keyLabel.rx.text)
.disposed(by: rx.reuseBag)
model.state
.subscribe { state in
self.state = state
} onError: { _ in }
.disposed(by: rx.reuseBag)
}
}

View File

@ -0,0 +1,30 @@
//
// ServerListTableViewCellViewModel.swift
// Bark
//
// Created by huangfeng on 2022/4/1.
// Copyright © 2022 Fin. All rights reserved.
//
import RxRelay
import UIKit
class ServerListTableViewCellViewModel: ViewModel {
let server: Server
let address: BehaviorRelay<String>
let key: BehaviorRelay<String>
let state: BehaviorRelay<Bool>
init(server: Server) {
self.server = server
self.address = BehaviorRelay<String>(value: {
URL(string: server.address)?.host ?? "Invalid Server"
}())
self.key = BehaviorRelay<String>(value: !server.key.isEmpty ? server.key : "none")
self.state = BehaviorRelay<Bool>(value: server.state == .ok)
super.init()
}
}