格式化代码

This commit is contained in:
Fin 2021-10-12 16:02:34 +08:00
parent 7be222b370
commit 13a11ebcf4
55 changed files with 827 additions and 857 deletions

View File

@ -6,15 +6,14 @@
// Copyright © 2018 Fin. All rights reserved. // Copyright © 2018 Fin. All rights reserved.
// //
import UIKit
import Material
import UserNotifications
import RealmSwift
import IceCream import IceCream
import Material
import RealmSwift
import UIKit
import UserNotifications
@UIApplicationMain @UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate{ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate {
var window: UIWindow? var window: UIWindow?
var syncEngine: SyncEngine? var syncEngine: SyncEngine?
func setupRealm() { func setupRealm() {
@ -23,18 +22,19 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
let config = Realm.Configuration( let config = Realm.Configuration(
fileURL: fileUrl, fileURL: fileUrl,
schemaVersion: 13, schemaVersion: 13,
migrationBlock: { migration, oldSchemaVersion in migrationBlock: { _, oldSchemaVersion in
// We havent migrated anything yet, so oldSchemaVersion == 0 // We havent migrated anything yet, so oldSchemaVersion == 0
if (oldSchemaVersion < 1) { if oldSchemaVersion < 1 {
// Nothing to do! // Nothing to do!
// Realm will automatically detect new properties and removed properties // Realm will automatically detect new properties and removed properties
// And will update the schema on disk automatically // And will update the schema on disk automatically
} }
}) }
)
// Tell Realm to use this new configuration object for the default Realm // Tell Realm to use this new configuration object for the default Realm
Realm.Configuration.defaultConfiguration = config Realm.Configuration.defaultConfiguration = config
//iCloud // iCloud
syncEngine = SyncEngine(objects: [ syncEngine = SyncEngine(objects: [
SyncObject(type: Message.self) SyncObject(type: Message.self)
], databaseScope: .private) ], databaseScope: .private)
@ -46,7 +46,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
} }
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Realm() // Realm()
setupRealm() setupRealm()
self.window = UIWindow(frame: UIScreen.main.bounds) self.window = UIWindow(frame: UIScreen.main.bounds)
@ -62,15 +62,15 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
) )
tabBarController.viewControllers = [ tabBarController.viewControllers = [
BarkNavigationController(rootViewController:HomeViewController(viewModel: HomeViewModel())), BarkNavigationController(rootViewController: HomeViewController(viewModel: HomeViewModel())),
BarkNavigationController(rootViewController:MessageListViewController(viewModel: MessageListViewModel())), BarkNavigationController(rootViewController: MessageListViewController(viewModel: MessageListViewModel())),
BarkNavigationController(rootViewController:MessageSettingsViewController(viewModel: MessageSettingsViewModel())), BarkNavigationController(rootViewController: MessageSettingsViewController(viewModel: MessageSettingsViewModel()))
] ]
let tabBarItems = [UITabBarItem(title: NSLocalizedString("service"), image: UIImage(named: "baseline_gite_black_24pt"), tag: 0), let tabBarItems = [UITabBarItem(title: NSLocalizedString("service"), image: UIImage(named: "baseline_gite_black_24pt"), tag: 0),
UITabBarItem(title: NSLocalizedString("historyMessage"), image: Icon.history, tag: 1), UITabBarItem(title: NSLocalizedString("historyMessage"), image: Icon.history, tag: 1),
UITabBarItem(title: NSLocalizedString("settings"), image: UIImage(named: "baseline_manage_accounts_black_24pt"), tag: 2)] UITabBarItem(title: NSLocalizedString("settings"), image: UIImage(named: "baseline_manage_accounts_black_24pt"), tag: 2)]
for (index , viewController) in tabBarController.viewControllers!.enumerated() { for (index, viewController) in tabBarController.viewControllers!.enumerated() {
viewController.tabBarItem = tabBarItems[index] viewController.tabBarItem = tabBarItems[index]
} }
@ -83,7 +83,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
], intentIdentifiers: [], options: .customDismissAction) ], intentIdentifiers: [], options: .customDismissAction)
]) ])
UNUserNotificationCenter.current().getNotificationSettings { (settings) in UNUserNotificationCenter.current().getNotificationSettings { settings in
dispatch_sync_safely_main_queue { dispatch_sync_safely_main_queue {
if settings.authorizationStatus == .authorized { if settings.authorizationStatus == .authorized {
Client.shared.registerForRemoteNotifications() Client.shared.registerForRemoteNotifications()
@ -91,7 +91,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
} }
} }
// //
let bar = UINavigationBar.appearance(whenContainedInInstancesOf: [BarkNavigationController.self]) let bar = UINavigationBar.appearance(whenContainedInInstancesOf: [BarkNavigationController.self])
bar.backIndicatorImage = UIImage(named: "back") bar.backIndicatorImage = UIImage(named: "back")
bar.backIndicatorTransitionMaskImage = UIImage(named: "back") bar.backIndicatorTransitionMaskImage = UIImage(named: "back")
@ -108,51 +108,52 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
let deviceTokenString = deviceToken.map { String(format: "%02.2hhx", $0) }.joined() let deviceTokenString = deviceToken.map { String(format: "%02.2hhx", $0) }.joined()
Settings[.deviceToken] = deviceTokenString Settings[.deviceToken] = deviceTokenString
// //
Client.shared.bindDeviceToken() Client.shared.bindDeviceToken()
} }
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) { func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
notificatonHandler(userInfo: notification.request.content.userInfo) notificatonHandler(userInfo: notification.request.content.userInfo)
} }
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) { func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
notificatonHandler(userInfo: response.notification.request.content.userInfo) notificatonHandler(userInfo: response.notification.request.content.userInfo)
} }
private func notificatonHandler(userInfo:[AnyHashable:Any]){
private func notificatonHandler(userInfo: [AnyHashable: Any]) {
let navigationController = Client.shared.currentNavigationController let navigationController = Client.shared.currentNavigationController
func presentController(){ func presentController() {
let alert = (userInfo["aps"] as? [String:Any])?["alert"] as? [String:Any] let alert = (userInfo["aps"] as? [String: Any])?["alert"] as? [String: Any]
let title = alert?["title"] as? String let title = alert?["title"] as? String
let body = alert?["body"] as? String let body = alert?["body"] as? String
let url:URL? = { let url: URL? = {
if let url = userInfo["url"] as? String { if let url = userInfo["url"] as? String {
return URL(string: url) return URL(string: url)
} }
return nil return nil
}() }()
//URL // URL
if let url = url { if let url = url {
if ["http","https"].contains(url.scheme?.lowercased() ?? ""){ if ["http", "https"].contains(url.scheme?.lowercased() ?? "") {
navigationController?.present(BarkSFSafariViewController(url: url), animated: true, completion: nil) navigationController?.present(BarkSFSafariViewController(url: url), animated: true, completion: nil)
} }
else{ else {
UIApplication.shared.open(url, options: [:], completionHandler: nil) UIApplication.shared.open(url, options: [:], completionHandler: nil)
} }
return return
} }
let alertController = UIAlertController(title: title, message: body, preferredStyle: .alert) let alertController = UIAlertController(title: title, message: body, preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: "复制内容", style: .default, handler: { (_) in alertController.addAction(UIAlertAction(title: "复制内容", style: .default, handler: { _ in
if let copy = userInfo["copy"] as? String { if let copy = userInfo["copy"] as? String {
UIPasteboard.general.string = copy UIPasteboard.general.string = copy
} }
else{ else {
UIPasteboard.general.string = body UIPasteboard.general.string = body
} }
})) }))
alertController.addAction(UIAlertAction(title: "更多操作", style: .default, handler: { (_) in alertController.addAction(UIAlertAction(title: "更多操作", style: .default, handler: { _ in
var shareContent = "" var shareContent = ""
if let title = title { if let title = title {
shareContent += "\(title)\n" shareContent += "\(title)\n"
@ -160,15 +161,15 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
if let body = body { if let body = body {
shareContent += "\(body)\n" shareContent += "\(body)\n"
} }
for (key,value) in userInfo { for (key, value) in userInfo {
if ["aps","title","body","url"].contains((key as? String) ?? "") { if ["aps", "title", "body", "url"].contains((key as? String) ?? "") {
continue continue
} }
shareContent += "\(key): \(value) \n" shareContent += "\(key): \(value) \n"
} }
var items:[Any] = [] var items: [Any] = []
items.append(shareContent) items.append(shareContent)
if let url = url{ if let url = url {
items.append(url) items.append(url)
} }
let controller = UIApplication.shared.keyWindow?.rootViewController let controller = UIApplication.shared.keyWindow?.rootViewController
@ -186,7 +187,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
presentController() presentController()
} }
} }
else{ else {
presentController() presentController()
} }
} }
@ -202,7 +203,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
} }
func applicationWillEnterForeground(_ application: UIApplication) { func applicationWillEnterForeground(_ application: UIApplication) {
if (Client.shared.key?.count ?? 0) <= 0{ if (Client.shared.key?.count ?? 0) <= 0 {
Client.shared.bindDeviceToken() Client.shared.bindDeviceToken()
} }
} }
@ -214,7 +215,4 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
func applicationWillTerminate(_ application: UIApplication) { func applicationWillTerminate(_ application: UIApplication) {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
} }
} }

View File

@ -9,7 +9,6 @@
import XCTest import XCTest
class BarkTests: XCTestCase { class BarkTests: XCTestCase {
override func setUpWithError() throws { override func setUpWithError() throws {
// Put setup code here. This method is called before the invocation of each test method in the class. // Put setup code here. This method is called before the invocation of each test method in the class.
} }
@ -29,5 +28,4 @@ class BarkTests: XCTestCase {
// Put the code you want to measure the time of here. // Put the code you want to measure the time of here.
} }
} }
} }

View File

@ -10,17 +10,17 @@ import UIKit
class ArchiveSettingManager: NSObject { class ArchiveSettingManager: NSObject {
static let shared = ArchiveSettingManager() static let shared = ArchiveSettingManager()
let defaults = UserDefaults.init(suiteName: "group.bark") let defaults = UserDefaults(suiteName: "group.bark")
var isArchive: Bool { var isArchive: Bool {
get { get {
return defaults?.value(forKey: "isArchive") as? Bool ?? true return defaults?.value(forKey: "isArchive") as? Bool ?? true
} }
set{ set {
defaults?.set(newValue, forKey: "isArchive") defaults?.set(newValue, forKey: "isArchive")
} }
} }
private override init(){
override private init() {
super.init() super.init()
} }
} }

View File

@ -6,10 +6,10 @@
// Copyright © 2018 Fin. All rights reserved. // Copyright © 2018 Fin. All rights reserved.
// //
import UIKit
import DefaultsKit import DefaultsKit
import UIKit
enum BarkSettingKey:String { enum BarkSettingKey: String {
/// key /// key
case key = "me.fin.bark.key" case key = "me.fin.bark.key"
case servers = "me.fin.bark.servers" case servers = "me.fin.bark.servers"
@ -20,13 +20,11 @@ enum BarkSettingKey:String {
case selectedViewControllerIndex = "me.fin.bark.selectedViewControllerIndex" case selectedViewControllerIndex = "me.fin.bark.selectedViewControllerIndex"
} }
class BarkSettings{ class BarkSettings {
static let shared = BarkSettings() static let shared = BarkSettings()
private init(){ private init() {}
} subscript(key: String) -> String? {
subscript(key:String) -> String? {
get { get {
let storeKey = Key<String>(key) let storeKey = Key<String>(key)
return Defaults.shared.get(for: storeKey) return Defaults.shared.get(for: storeKey)
@ -42,7 +40,7 @@ class BarkSettings{
} }
} }
subscript(key:BarkSettingKey) -> String? { subscript(key: BarkSettingKey) -> String? {
get { get {
return self[key.rawValue] return self[key.rawValue]
} }
@ -51,7 +49,7 @@ class BarkSettings{
} }
} }
subscript<T : Codable>(key:String) -> T? { subscript<T: Codable>(key: String) -> T? {
get { get {
let storeKey = Key<T>(key) let storeKey = Key<T>(key)
return Defaults.shared.get(for: storeKey) return Defaults.shared.get(for: storeKey)
@ -66,7 +64,8 @@ class BarkSettings{
} }
} }
} }
subscript<T : Codable>(key:BarkSettingKey) -> T? {
subscript<T: Codable>(key: BarkSettingKey) -> T? {
get { get {
return self[key.rawValue] return self[key.rawValue]
} }

View File

@ -6,31 +6,29 @@
// Copyright © 2018 Fin. All rights reserved. // Copyright © 2018 Fin. All rights reserved.
// //
import RxCocoa
import RxSwift
import UIKit import UIKit
import UserNotifications import UserNotifications
import RxSwift
import RxCocoa
class Client: NSObject { class Client: NSObject {
static let shared = Client() static let shared = Client()
private override init() { override private init() {
super.init() super.init()
} }
var currentNavigationController:UINavigationController? {
get { var currentNavigationController: UINavigationController? {
let controller = UIApplication.shared.delegate?.window??.rootViewController as? BarkSnackbarController let controller = UIApplication.shared.delegate?.window??.rootViewController as? BarkSnackbarController
let nav = (controller?.rootViewController as? UITabBarController)?.selectedViewController as? UINavigationController let nav = (controller?.rootViewController as? UITabBarController)?.selectedViewController as? UINavigationController
return nav return nav
} }
}
var currentTabBarController:StateStorageTabBarController? { var currentTabBarController: StateStorageTabBarController? {
get {
let controller = UIApplication.shared.delegate?.window??.rootViewController as? BarkSnackbarController let controller = UIApplication.shared.delegate?.window??.rootViewController as? BarkSnackbarController
return controller?.rootViewController as? StateStorageTabBarController return controller?.rootViewController as? StateStorageTabBarController
} }
}
let appVersion:String = { let appVersion: String = {
var version = "0.0.0" var version = "0.0.0"
if let infoDict = Bundle.main.infoDictionary { if let infoDict = Bundle.main.infoDictionary {
if let appVersion = infoDict["CFBundleVersion"] as? String { if let appVersion = infoDict["CFBundleVersion"] as? String {
@ -40,10 +38,10 @@ class Client: NSObject {
return version return version
}() }()
private var _key:String? private var _key: String?
var key:String? { var key: String? {
get { get {
if _key == nil, let aKey = Settings[.key]{ if _key == nil, let aKey = Settings[.key] {
_key = aKey _key = aKey
} }
return _key return _key
@ -62,9 +60,9 @@ class Client: NSObject {
var state = BehaviorRelay<ClienState>(value: .ok) var state = BehaviorRelay<ClienState>(value: .ok)
var dispose:Disposable? var dispose: Disposable?
func bindDeviceToken(){ func bindDeviceToken() {
if let token = Settings[.deviceToken] , token.count > 0{ if let token = Settings[.deviceToken], token.count > 0 {
dispose?.dispose() dispose?.dispose()
dispose = BarkApi.provider dispose = BarkApi.provider
@ -72,14 +70,14 @@ class Client: NSObject {
key: key, key: key,
devicetoken: token)) devicetoken: token))
.filterResponseError() .filterResponseError()
.map { (json) -> ClienState in .map { json -> ClienState in
switch json { switch json {
case .success(let json): case .success(let json):
if let key = json["data","key"].rawString() { if let key = json["data", "key"].rawString() {
Client.shared.key = key Client.shared.key = key
return .ok return .ok
} }
else{ else {
return .serverError return .serverError
} }
case .failure: case .failure:
@ -92,13 +90,13 @@ class Client: NSObject {
func registerForRemoteNotifications() { func registerForRemoteNotifications() {
let center = UNUserNotificationCenter.current() let center = UNUserNotificationCenter.current()
center.requestAuthorization(options: [.alert , .sound , .badge], completionHandler: {(_ granted: Bool, _ error: Error?) -> Void in center.requestAuthorization(options: [.alert, .sound, .badge], completionHandler: { (_ granted: Bool, _: Error?) -> Void in
if granted { if granted {
dispatch_sync_safely_main_queue { dispatch_sync_safely_main_queue {
UIApplication.shared.registerForRemoteNotifications() UIApplication.shared.registerForRemoteNotifications()
} }
} }
else{ else {
print("没有打开推送") print("没有打开推送")
} }
}) })

View File

@ -9,16 +9,15 @@
import UIKit import UIKit
extension Date { extension Date {
func formatString(format:String) -> String { func formatString(format: String) -> String {
let formatter = DateFormatter() let formatter = DateFormatter()
formatter.dateFormat = format formatter.dateFormat = format
return formatter.string(for: self) ?? "" return formatter.string(for: self) ?? ""
} }
func agoFormatString() -> String { func agoFormatString() -> String {
let clendar = NSCalendar(calendarIdentifier: .gregorian) let clendar = NSCalendar(calendarIdentifier: .gregorian)
let cps = clendar?.components([ .hour, .minute, .second, .day, .month, .year], from: self, to: Date(), options: .wrapComponents) let cps = clendar?.components([.hour, .minute, .second, .day, .month, .year], from: self, to: Date(), options: .wrapComponents)
let year = cps!.year! let year = cps!.year!
let month = cps!.month! let month = cps!.month!
@ -49,15 +48,19 @@ extension Date {
var dayBefore: Date { var dayBefore: Date {
return Calendar.current.date(byAdding: .day, value: -1, to: noon)! return Calendar.current.date(byAdding: .day, value: -1, to: noon)!
} }
var dayAfter: Date { var dayAfter: Date {
return Calendar.current.date(byAdding: .day, value: 1, to: noon)! return Calendar.current.date(byAdding: .day, value: 1, to: noon)!
} }
var noon: Date { var noon: Date {
return Calendar.current.date(bySettingHour: 0, minute: 0, second: 0, of: self)! return Calendar.current.date(bySettingHour: 0, minute: 0, second: 0, of: self)!
} }
var month: Int { var month: Int {
return Calendar.current.component(.month, from: self) return Calendar.current.component(.month, from: self)
} }
var isLastDayOfMonth: Bool { var isLastDayOfMonth: Bool {
return dayAfter.month != month return dayAfter.month != month
} }

View File

@ -1,4 +1,4 @@
// //
// Defines.swift // Defines.swift
// Bark // Bark
// //
@ -9,7 +9,7 @@
import UIKit import UIKit
/// 线 /// 线
func dispatch_sync_safely_main_queue(_ block: ()->()) { func dispatch_sync_safely_main_queue(_ block: () -> ()) {
if Thread.isMainThread { if Thread.isMainThread {
block() block()
} else { } else {
@ -20,29 +20,28 @@ func dispatch_sync_safely_main_queue(_ block: ()->()) {
} }
extension UIViewController { extension UIViewController {
func showSnackbar(text:String) { func showSnackbar(text: String) {
self.snackbarController?.snackbar.text = text self.snackbarController?.snackbar.text = text
self.snackbarController?.animate(snackbar: .visible) self.snackbarController?.animate(snackbar: .visible)
self.snackbarController?.animate(snackbar: .hidden, delay: 3) self.snackbarController?.animate(snackbar: .hidden, delay: 3)
} }
} }
func NSLocalizedString( _ key:String ) -> String { func NSLocalizedString(_ key: String) -> String {
return NSLocalizedString(key, comment: "") return NSLocalizedString(key, comment: "")
} }
let kNavigationHeight: CGFloat = { let kNavigationHeight: CGFloat = {
return kSafeAreaInsets.top + 44 kSafeAreaInsets.top + 44
}() }()
let kSafeAreaInsets:UIEdgeInsets = { let kSafeAreaInsets: UIEdgeInsets = {
if #available(iOS 12.0, *){ if #available(iOS 12.0, *) {
return UIWindow().safeAreaInsets return UIWindow().safeAreaInsets
} } else if #available(iOS 11.0, *) {
else if #available(iOS 11.0, *){
let inset = UIWindow().safeAreaInsets let inset = UIWindow().safeAreaInsets
if inset.top > 0 { return inset} if inset.top > 0 { return inset }
//iOS 11 safeAreaInsets.top 0iOS12 便 // iOS 11 safeAreaInsets.top 0iOS12 便
} }
return UIEdgeInsets(top: 20, left: 0, bottom: 0, right: 0) return UIEdgeInsets(top: 20, left: 0, bottom: 0, right: 0)
}() }()

View File

@ -7,14 +7,13 @@
// //
import Foundation import Foundation
import MJRefresh
import RxCocoa import RxCocoa
import RxSwift import RxSwift
import MJRefresh
extension Reactive where Base : MJRefreshComponent { extension Reactive where Base: MJRefreshComponent {
var refresh: ControlEvent<Void> { var refresh: ControlEvent<Void> {
let source = Observable<Void>.create { [weak control = self.base] observer -> Disposable in
let source = Observable<Void>.create {[weak control = self.base] (observer) -> Disposable in
MainScheduler.ensureExecutingOnScheduler() MainScheduler.ensureExecutingOnScheduler()
guard let control = control else { guard let control = control else {
observer.onCompleted() observer.onCompleted()
@ -27,10 +26,8 @@ extension Reactive where Base : MJRefreshComponent {
} }
return ControlEvent(events: source) return ControlEvent(events: source)
} }
} }
enum MJRefreshAction { enum MJRefreshAction {
/// ///
case none case none
@ -48,16 +45,14 @@ enum MJRefreshAction {
case resetNomoreData case resetNomoreData
} }
extension Reactive where Base:UIScrollView { extension Reactive where Base: UIScrollView {
/// ///
var refreshAction:Binder<MJRefreshAction> { var refreshAction: Binder<MJRefreshAction> {
return Binder(base) { target, action in
return Binder(base) { (target, action) in switch action {
switch action{
case .begainRefresh: case .begainRefresh:
//使 UIRefreshControl // 使 UIRefreshControl
if let control = target.refreshControl { if let control = target.refreshControl {
control.beginRefreshing() control.beginRefreshing()
} }
@ -81,11 +76,9 @@ extension Reactive where Base:UIScrollView {
if let footer = target.mj_footer { if let footer = target.mj_footer {
footer.resetNoMoreData() footer.resetNoMoreData()
} }
break
case .none: case .none:
break break
} }
} }
} }
} }

View File

@ -9,8 +9,8 @@
import UIKit import UIKit
enum BarkApi { enum BarkApi {
case ping(baseURL:String?) case ping(baseURL: String?)
case register(key:String? , devicetoken:String) // case register(key: String?, devicetoken: String) //
} }
extension BarkApi: BarkTargetType { extension BarkApi: BarkTargetType {
@ -20,10 +20,11 @@ extension BarkApi: BarkTargetType {
} }
return URL(string: ServerManager.shared.currentAddress)! return URL(string: ServerManager.shared.currentAddress)!
} }
var parameters: [String : Any]? {
var parameters: [String: Any]? {
switch self { switch self {
case let .register(key, devicetoken): case let .register(key, devicetoken):
var params = ["devicetoken":devicetoken] var params = ["devicetoken": devicetoken]
if let key = key { if let key = key {
params["key"] = key params["key"] = key
} }
@ -41,6 +42,4 @@ extension BarkApi: BarkTargetType {
return "/register" return "/register"
} }
} }
} }

View File

@ -6,21 +6,22 @@
// Copyright © 2018 Fin. All rights reserved. // Copyright © 2018 Fin. All rights reserved.
// //
import UIKit
import Moya import Moya
import RxSwift import RxSwift
import UIKit
//Providers // Providers
fileprivate var retainProviders:[String: Any] = [:] private var retainProviders: [String: Any] = [:]
protocol BarkTargetType: TargetType { protocol BarkTargetType: TargetType {
var parameters: [String: Any]? { get } var parameters: [String: Any]? { get }
} }
extension BarkTargetType { extension BarkTargetType {
var headers: [String : String]? { var headers: [String: String]? {
return nil return nil
} }
var baseURL: URL { var baseURL: URL {
return URL(string: ServerManager.shared.currentAddress)! return URL(string: ServerManager.shared.currentAddress)!
} }
@ -42,10 +43,9 @@ extension BarkTargetType {
} }
var requestTaskWithParameters: Task { var requestTaskWithParameters: Task {
get { //
// var defaultParameters: [String: Any] = [:]
var defaultParameters:[String:Any] = [:] //
//
if let parameters = self.parameters { if let parameters = self.parameters {
for (key, value) in parameters { for (key, value) in parameters {
defaultParameters[key] = value defaultParameters[key] = value
@ -53,10 +53,9 @@ extension BarkTargetType {
} }
return Task.requestParameters(parameters: defaultParameters, encoding: parameterEncoding) return Task.requestParameters(parameters: defaultParameters, encoding: parameterEncoding)
} }
}
static var networkActivityPlugin: PluginType { static var networkActivityPlugin: PluginType {
return NetworkActivityPlugin { (change, type) in return NetworkActivityPlugin { change, _ in
switch change { switch change {
case .began: case .began:
dispatch_sync_safely_main_queue { dispatch_sync_safely_main_queue {
@ -71,9 +70,9 @@ extension BarkTargetType {
} }
/// provider /// provider
static var provider: RxSwift.Reactive< MoyaProvider<Self> > { static var provider: RxSwift.Reactive<MoyaProvider<Self>> {
let key = "\(Self.self)" let key = "\(Self.self)"
if let provider = retainProviders[key] as? RxSwift.Reactive< MoyaProvider<Self> > { if let provider = retainProviders[key] as? RxSwift.Reactive<MoyaProvider<Self>> {
return provider return provider
} }
let provider = Self.weakProvider let provider = Self.weakProvider
@ -82,18 +81,18 @@ extension BarkTargetType {
} }
/// Provider 使 /// Provider 使
static var weakProvider: RxSwift.Reactive< MoyaProvider<Self> > { static var weakProvider: RxSwift.Reactive<MoyaProvider<Self>> {
var plugins:[PluginType] = [networkActivityPlugin] var plugins: [PluginType] = [networkActivityPlugin]
#if DEBUG #if DEBUG
plugins.append(LogPlugin()) plugins.append(LogPlugin())
#endif #endif
let provider = MoyaProvider<Self>(plugins:plugins) let provider = MoyaProvider<Self>(plugins: plugins)
return provider.rx return provider.rx
} }
} }
extension RxSwift.Reactive where Base: MoyaProviderType { public extension RxSwift.Reactive where Base: MoyaProviderType {
public func request(_ token: Base.Target, callbackQueue: DispatchQueue? = nil) -> Observable<Response> { func request(_ token: Base.Target, callbackQueue: DispatchQueue? = nil) -> Observable<Response> {
return Single.create { [weak base] single in return Single.create { [weak base] single in
let cancellableToken = base?.request(token, callbackQueue: callbackQueue, progress: nil) { result in let cancellableToken = base?.request(token, callbackQueue: callbackQueue, progress: nil) { result in
switch result { switch result {
@ -111,7 +110,7 @@ extension RxSwift.Reactive where Base: MoyaProviderType {
} }
} }
fileprivate class LogPlugin: PluginType{ private class LogPlugin: PluginType {
func willSend(_ request: RequestType, target: TargetType) { func willSend(_ request: RequestType, target: TargetType) {
print("\n-------------------\n准备请求: \(target.path)") print("\n-------------------\n准备请求: \(target.path)")
print("请求方式: \(target.method.rawValue)") print("请求方式: \(target.method.rawValue)")
@ -119,8 +118,8 @@ fileprivate class LogPlugin: PluginType{
print(params) print(params)
} }
print("\n") print("\n")
} }
func didReceive(_ result: Result<Response, MoyaError>, target: TargetType) { func didReceive(_ result: Result<Response, MoyaError>, target: TargetType) {
print("\n-------------------\n请求结束: \(target.path)") print("\n-------------------\n请求结束: \(target.path)")
if let data = try? result.get().data, let resutl = String(data: data, encoding: String.Encoding.utf8) { if let data = try? result.get().data, let resutl = String(data: data, encoding: String.Encoding.utf8) {

View File

@ -8,13 +8,13 @@
import UIKit import UIKit
import UIKit
import RxSwift
import ObjectMapper
import SwiftyJSON
import Moya import Moya
import ObjectMapper
import RxSwift
import SwiftyJSON
import UIKit
public enum ApiError : Swift.Error { public enum ApiError: Swift.Error {
case Error(info: String) case Error(info: String)
case AccountBanned(info: String) case AccountBanned(info: String)
} }
@ -25,9 +25,9 @@ extension Swift.Error {
return self.localizedDescription return self.localizedDescription
} }
switch err { switch err {
case let .Error(info): case .Error(let info):
return info return info
case let .AccountBanned(info): case .AccountBanned(let info):
return info return info
} }
} }
@ -35,23 +35,23 @@ extension Swift.Error {
extension Observable where Element: Moya.Response { extension Observable where Element: Moya.Response {
/// HTTP /// HTTP
func filterHttpError() -> Observable<Result<Element,ApiError>> { func filterHttpError() -> Observable<Result<Element, ApiError>> {
return return
catchErrorJustReturn(Element(statusCode: 599, data: Data())) catchErrorJustReturn(Element(statusCode: 599, data: Data()))
.map { (response) -> Result<Element,ApiError> in .map { response -> Result<Element, ApiError> in
if (200...209) ~= response.statusCode { if (200 ... 209) ~= response.statusCode {
return .success(response) return .success(response)
} }
else{ else {
return .failure(ApiError.Error(info: "网络错误")) return .failure(ApiError.Error(info: "网络错误"))
} }
} }
} }
/// CODE /// CODE
func filterResponseError() -> Observable<Result<JSON,ApiError>> { func filterResponseError() -> Observable<Result<JSON, ApiError>> {
return filterHttpError() return filterHttpError()
.map{ response -> Result<JSON,ApiError> in .map { response -> Result<JSON, ApiError> in
switch response { switch response {
case .success(let element): case .success(let element):
do { do {
@ -59,11 +59,12 @@ extension Observable where Element: Moya.Response {
if let codeStr = json["code"].rawString(), if let codeStr = json["code"].rawString(),
let code = Int(codeStr), let code = Int(codeStr),
code == 200{ code == 200
{
return .success(json) return .success(json)
} }
else{ else {
var msg:String = "" var msg: String = ""
if json["message"].exists() { if json["message"].exists() {
msg = json["message"].rawString()! msg = json["message"].rawString()!
} }
@ -73,7 +74,7 @@ extension Observable where Element: Moya.Response {
catch { catch {
return .failure(ApiError.Error(info: error.rawString())) return .failure(ApiError.Error(info: error.rawString()))
} }
case .failure(let error) : case .failure(let error):
return .failure(ApiError.Error(info: error.rawString())) return .failure(ApiError.Error(info: error.rawString()))
} }
} }
@ -84,18 +85,18 @@ extension Observable where Element: Moya.Response {
/// - Parameters: /// - Parameters:
/// - typeName: Model Class /// - typeName: Model Class
/// - dataPath: ["data","links"] /// - dataPath: ["data","links"]
func mapResponseToObj<T: Mappable>(_ typeName: T.Type , dataPath:[String] = ["data"] ) -> Observable<Result<T,ApiError>> { func mapResponseToObj<T: Mappable>(_ typeName: T.Type, dataPath: [String] = ["data"]) -> Observable<Result<T, ApiError>> {
return filterResponseError().map{ json in return filterResponseError().map { json in
switch json { switch json {
case .success(let json): case .success(let json):
var rootJson = json var rootJson = json
if dataPath.count > 0{ if dataPath.count > 0 {
rootJson = rootJson[dataPath] rootJson = rootJson[dataPath]
} }
if let model: T = self.resultFromJSON(json: rootJson) { if let model: T = self.resultFromJSON(json: rootJson) {
return .success(model) return .success(model)
} }
else{ else {
return .failure(ApiError.Error(info: "json 转换失败")) return .failure(ApiError.Error(info: "json 转换失败"))
} }
case .failure(let error): case .failure(let error):
@ -105,24 +106,24 @@ extension Observable where Element: Moya.Response {
} }
/// Response JSON Model Array /// Response JSON Model Array
func mapResponseToObjArray<T: Mappable>(_ type: T.Type, dataPath:[String] = ["data"] ) -> Observable<Result<[T],ApiError>> { func mapResponseToObjArray<T: Mappable>(_ type: T.Type, dataPath: [String] = ["data"]) -> Observable<Result<[T], ApiError>> {
return filterResponseError().map{ json in return filterResponseError().map { json in
switch json { switch json {
case .success(let json): case .success(let json):
var rootJson = json; var rootJson = json
if dataPath.count > 0{ if dataPath.count > 0 {
rootJson = rootJson[dataPath] rootJson = rootJson[dataPath]
} }
var result = [T]() var result = [T]()
guard let jsonArray = rootJson.array else{ guard let jsonArray = rootJson.array else {
return .failure(ApiError.Error(info: "Root Json 不是 Array")) return .failure(ApiError.Error(info: "Root Json 不是 Array"))
} }
for json in jsonArray{ for json in jsonArray {
if let jsonModel: T = self.resultFromJSON(json: json) { if let jsonModel: T = self.resultFromJSON(json: json) {
result.append(jsonModel) result.append(jsonModel)
} }
else{ else {
return .failure(ApiError.Error(info: "json 转换失败")) return .failure(ApiError.Error(info: "json 转换失败"))
} }
} }
@ -131,15 +132,15 @@ extension Observable where Element: Moya.Response {
case .failure(let error): case .failure(let error):
return .failure(error) return .failure(error)
} }
} }
} }
private func resultFromJSON<T: Mappable>(jsonString:String) -> T? { private func resultFromJSON<T: Mappable>(jsonString: String) -> T? {
return T(JSONString: jsonString) return T(JSONString: jsonString)
} }
private func resultFromJSON<T: Mappable>(json:JSON) -> T? {
if let str = json.rawString(){ private func resultFromJSON<T: Mappable>(json: JSON) -> T? {
if let str = json.rawString() {
return resultFromJSON(jsonString: str) return resultFromJSON(jsonString: str)
} }
return nil return nil

View File

@ -5,8 +5,8 @@
// Created by Krunoslav Zaher on 12/6/15. // Created by Krunoslav Zaher on 12/6/15.
// Copyright © 2015 Krunoslav Zaher. All rights reserved. // Copyright © 2015 Krunoslav Zaher. All rights reserved.
// //
import RxSwift
import RxCocoa import RxCocoa
import RxSwift
#if os(iOS) #if os(iOS)
import UIKit import UIKit
#elseif os(macOS) #elseif os(macOS)
@ -14,7 +14,7 @@ import AppKit
#endif #endif
// Two way binding operator between control property and relay, that's all it takes. // Two way binding operator between control property and relay, that's all it takes.
infix operator <-> : DefaultPrecedence infix operator <->: DefaultPrecedence
#if os(iOS) #if os(iOS)
func nonMarkedText(_ textInput: UITextInput) -> String? { func nonMarkedText(_ textInput: UITextInput) -> String? {
@ -22,7 +22,8 @@ func nonMarkedText(_ textInput: UITextInput) -> String? {
let end = textInput.endOfDocument let end = textInput.endOfDocument
guard let rangeAll = textInput.textRange(from: start, to: end), guard let rangeAll = textInput.textRange(from: start, to: end),
let text = textInput.text(in: rangeAll) else { let text = textInput.text(in: rangeAll)
else {
return nil return nil
} }
@ -31,7 +32,8 @@ func nonMarkedText(_ textInput: UITextInput) -> String? {
} }
guard let startRange = textInput.textRange(from: start, to: markedTextRange.start), guard let startRange = textInput.textRange(from: start, to: markedTextRange.start),
let endRange = textInput.textRange(from: markedTextRange.end, to: end) else { let endRange = textInput.textRange(from: markedTextRange.end, to: end)
else {
return text return text
} }
@ -42,7 +44,7 @@ func <-> <Base>(textInput: TextInput<Base>, relay: BehaviorRelay<String>) -> Dis
let bindToUIDisposable = relay.bind(to: textInput.text) let bindToUIDisposable = relay.bind(to: textInput.text)
let bindToRelay = textInput.text let bindToRelay = textInput.text
.subscribe(onNext: { [weak base = textInput.base] n in .subscribe(onNext: { [weak base = textInput.base] _ in
guard let base = base else { guard let base = base else {
return return
} }

View File

@ -6,13 +6,13 @@
// Copyright © 2020 Fin. All rights reserved. // Copyright © 2020 Fin. All rights reserved.
// //
import UIKit
import RxSwift
import RxCocoa import RxCocoa
import RxSwift
import UIKit
private var prepareForReuseBag: Int8 = 0 private var prepareForReuseBag: Int8 = 0
@objc public protocol Reusable : class { @objc public protocol Reusable: class {
func prepareForReuse() func prepareForReuse()
} }

View File

@ -12,32 +12,33 @@ let defaultServer = "https://api.day.app"
class ServerManager: NSObject { class ServerManager: NSObject {
static let shared = ServerManager() static let shared = ServerManager()
private override init() { override private init() {
if let servers:Set<String> = Settings[.servers] { if let servers: Set<String> = Settings[.servers] {
self.servers = servers self.servers = servers
} }
if let address = Settings[.currentServer] { if let address = Settings[.currentServer] {
self.currentAddress = address self.currentAddress = address
} }
else{ else {
self.currentAddress = self.servers.first ?? defaultServer self.currentAddress = self.servers.first ?? defaultServer
} }
super.init() super.init()
} }
var servers:Set<String> = [defaultServer] var servers: Set<String> = [defaultServer]
var currentAddress:String { var currentAddress: String {
didSet{ didSet {
Settings[.currentServer] = currentAddress Settings[.currentServer] = currentAddress
} }
} }
func addServer(server:String){ func addServer(server: String) {
self.servers.insert(server) self.servers.insert(server)
Settings[.servers] = self.servers Settings[.servers] = self.servers
} }
func removeServer(server:String){
func removeServer(server: String) {
self.servers.remove(server) self.servers.remove(server)
if self.servers.count <= 0 { if self.servers.count <= 0 {
self.servers.insert(defaultServer) self.servers.insert(defaultServer)
@ -45,6 +46,7 @@ class ServerManager: NSObject {
Settings[.servers] = self.servers Settings[.servers] = self.servers
} }
@available(*, unavailable)
required init?(coder aDecoder: NSCoder) { required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented") fatalError("init(coder:) has not been implemented")
} }

View File

@ -9,14 +9,14 @@
import UIKit import UIKit
extension String { extension String {
//urlurl // urlurl
func urlEncoded() -> String { func urlEncoded() -> String {
let encodeUrlString = self.addingPercentEncoding(withAllowedCharacters: let encodeUrlString = self.addingPercentEncoding(withAllowedCharacters:
.urlQueryAllowed) .urlQueryAllowed)
return encodeUrlString ?? "" return encodeUrlString ?? ""
} }
//urlurl // urlurl
func urlDecoded() -> String { func urlDecoded() -> String {
return self.removingPercentEncoding ?? "" return self.removingPercentEncoding ?? ""
} }

View File

@ -9,10 +9,11 @@
import UIKit import UIKit
extension UIColor { extension UIColor {
convenience public init(r255:CGFloat, g255:CGFloat, b255:CGFloat, a255:CGFloat = 255) { public convenience init(r255: CGFloat, g255: CGFloat, b255: CGFloat, a255: CGFloat = 255) {
self.init(red: r255/255, green: g255/255, blue: b255/255, alpha: a255/255) self.init(red: r255/255, green: g255/255, blue: b255/255, alpha: a255/255)
} }
class func image(color:UIColor, size:CGSize = CGSize(width: 1, height: 1)) -> UIImage{
class func image(color: UIColor, size: CGSize = CGSize(width: 1, height: 1)) -> UIImage {
UIGraphicsBeginImageContext(size) UIGraphicsBeginImageContext(size)
let context = UIGraphicsGetCurrentContext() let context = UIGraphicsGetCurrentContext()
context?.setFillColor(color.cgColor) context?.setFillColor(color.cgColor)
@ -20,7 +21,7 @@ extension UIColor {
let image = UIGraphicsGetImageFromCurrentImageContext() let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext() UIGraphicsEndImageContext()
return image! //contextget~ return image! // contextget~
} }
var image: UIImage { var image: UIImage {

View File

@ -16,4 +16,4 @@ protocol ViewModelType {
func transform(input: Input) -> Output func transform(input: Input) -> Output
} }
class ViewModel:NSObject{ } class ViewModel: NSObject {}

View File

@ -6,11 +6,11 @@
// Copyright © 2018 Fin. All rights reserved. // Copyright © 2018 Fin. All rights reserved.
// //
import UIKit
import Material import Material
import RxSwift import RxSwift
import UIKit
class BarkNavigationController: UINavigationController{ class BarkNavigationController: UINavigationController {
override func viewDidLoad() { override func viewDidLoad() {
super.viewDidLoad() super.viewDidLoad()
self.navigationBar.prefersLargeTitles = true self.navigationBar.prefersLargeTitles = true
@ -18,7 +18,7 @@ class BarkNavigationController: UINavigationController{
} }
class BarkSnackbarController: SnackbarController { class BarkSnackbarController: SnackbarController {
override var childForStatusBarStyle: UIViewController?{ override var childForStatusBarStyle: UIViewController? {
return self.rootViewController return self.rootViewController
} }
} }
@ -31,13 +31,12 @@ enum TabPage: Int {
} }
class StateStorageTabBarController: UITabBarController, UITabBarControllerDelegate { class StateStorageTabBarController: UITabBarController, UITabBarControllerDelegate {
// //
var currentSelectedIndex: Int = 0 var currentSelectedIndex: Int = 0
// tabBarItem // tabBarItem
lazy var tabBarItemDidClick: Observable<TabPage> = { lazy var tabBarItemDidClick: Observable<TabPage> = {
return self.rx.didSelect self.rx.didSelect
.flatMapLatest { _ -> Single<TabPage> in .flatMapLatest { _ -> Single<TabPage> in
let single = Single<TabPage>.create { single in let single = Single<TabPage>.create { single in
if self.currentSelectedIndex == self.selectedIndex { if self.currentSelectedIndex == self.selectedIndex {
@ -56,16 +55,15 @@ class StateStorageTabBarController: UITabBarController, UITabBarControllerDelega
if isFirstAppear { if isFirstAppear {
isFirstAppear = false isFirstAppear = false
//APP // APP
if let index:Int = Settings[.selectedViewControllerIndex] { if let index: Int = Settings[.selectedViewControllerIndex] {
self.selectedIndex = index self.selectedIndex = index
self.currentSelectedIndex = index self.currentSelectedIndex = index
} }
//Index // Index
self.rx.didSelect.subscribe(onNext: {_ in self.rx.didSelect.subscribe(onNext: { _ in
Settings[.selectedViewControllerIndex] = self.selectedIndex Settings[.selectedViewControllerIndex] = self.selectedIndex
}).disposed(by: rx.disposeBag) }).disposed(by: rx.disposeBag)
} }
} }
} }

View File

@ -6,10 +6,9 @@
// Copyright © 2018 Fin. All rights reserved. // Copyright © 2018 Fin. All rights reserved.
// //
import UIKit
import SafariServices import SafariServices
import UIKit
class BarkSFSafariViewController: SFSafariViewController { class BarkSFSafariViewController: SFSafariViewController {
override func viewDidLoad() { override func viewDidLoad() {
super.viewDidLoad() super.viewDidLoad()
@ -21,10 +20,7 @@ class BarkSFSafariViewController: SFSafariViewController {
// Dispose of any resources that can be recreated. // Dispose of any resources that can be recreated.
} }
override var preferredStatusBarStyle: UIStatusBarStyle{ override var preferredStatusBarStyle: UIStatusBarStyle {
get {
return .default return .default
} }
}
} }

View File

@ -6,33 +6,32 @@
// Copyright © 2018 Fin. All rights reserved. // Copyright © 2018 Fin. All rights reserved.
// //
import UIKit
import Material import Material
import UIKit
class BaseViewController: UIViewController { class BaseViewController: UIViewController {
let viewModel: ViewModel
let viewModel:ViewModel init(viewModel: ViewModel) {
init(viewModel:ViewModel) {
self.viewModel = viewModel self.viewModel = viewModel
super.init(nibName: nil, bundle: nil) super.init(nibName: nil, bundle: nil)
self.view.backgroundColor = Color.grey.lighten5 self.view.backgroundColor = Color.grey.lighten5
} }
@available(*, unavailable)
required init?(coder: NSCoder) { required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented") fatalError("init(coder:) has not been implemented")
} }
override var preferredStatusBarStyle: UIStatusBarStyle{ override var preferredStatusBarStyle: UIStatusBarStyle {
get {
return .lightContent return .lightContent
} }
}
override func viewDidLoad() { override func viewDidLoad() {
super.viewDidLoad() super.viewDidLoad()
self.navigationItem.largeTitleDisplayMode = .automatic self.navigationItem.largeTitleDisplayMode = .automatic
makeUI() makeUI()
} }
var isViewModelBinded = false var isViewModelBinded = false
override func viewWillAppear(_ animated: Bool) { override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated) super.viewWillAppear(animated)
@ -42,10 +41,7 @@ class BaseViewController: UIViewController {
} }
} }
func makeUI() { func makeUI() {}
} func bindViewModel() {}
func bindViewModel(){
}
} }

View File

@ -6,16 +6,15 @@
// Copyright © 2021 Fin. All rights reserved. // Copyright © 2021 Fin. All rights reserved.
// //
import UIKit
import Material import Material
import MJRefresh
import RealmSwift import RealmSwift
import RxCocoa import RxCocoa
import RxDataSources import RxDataSources
import MJRefresh
import RxSwift import RxSwift
import UIKit
class GroupFilterViewController: BaseViewController { class GroupFilterViewController: BaseViewController {
let doneButton: BKButton = { let doneButton: BKButton = {
let btn = BKButton() let btn = BKButton()
btn.setTitle(NSLocalizedString("done"), for: .normal) btn.setTitle(NSLocalizedString("done"), for: .normal)
@ -35,7 +34,7 @@ class GroupFilterViewController: BaseViewController {
}() }()
let tableView: UITableView = { let tableView: UITableView = {
let tableView:UITableView = UITableView(frame: CGRect.zero, style: .insetGrouped) let tableView = UITableView(frame: CGRect.zero, style: .insetGrouped)
tableView.separatorStyle = .singleLine tableView.separatorStyle = .singleLine
tableView.separatorColor = Color.grey.lighten3 tableView.separatorColor = Color.grey.lighten3
tableView.backgroundColor = Color.grey.lighten5 tableView.backgroundColor = Color.grey.lighten5
@ -49,9 +48,9 @@ class GroupFilterViewController: BaseViewController {
self.view.addSubview(tableView) self.view.addSubview(tableView)
self.view.addSubview(showAllGroupsButton) self.view.addSubview(showAllGroupsButton)
tableView.snp.makeConstraints { (make) in tableView.snp.makeConstraints { make in
make.top.equalToSuperview() make.top.equalToSuperview()
make.bottom.equalToSuperview().offset( (kSafeAreaInsets.bottom + 40) * -1) make.bottom.equalToSuperview().offset((kSafeAreaInsets.bottom + 40) * -1)
make.left.right.equalToSuperview() make.left.right.equalToSuperview()
} }
showAllGroupsButton.snp.makeConstraints { make in showAllGroupsButton.snp.makeConstraints { make in
@ -62,6 +61,7 @@ class GroupFilterViewController: BaseViewController {
self.tableView.tableHeaderView = UIView(frame: CGRect(x: 0, y: 0, width: self.view.bounds.size.width, height: 20)) self.tableView.tableHeaderView = UIView(frame: CGRect(x: 0, y: 0, width: self.view.bounds.size.width, height: 20))
} }
override func bindViewModel() { override func bindViewModel() {
guard let viewModel = self.viewModel as? GroupFilterViewModel else { guard let viewModel = self.viewModel as? GroupFilterViewModel else {
return return
@ -71,15 +71,15 @@ class GroupFilterViewController: BaseViewController {
input: GroupFilterViewModel.Input( input: GroupFilterViewModel.Input(
showAllGroups: self.showAllGroupsButton.rx showAllGroups: self.showAllGroupsButton.rx
.tap .tap
.compactMap({[weak self] in .compactMap { [weak self] in
guard let strongSelf = self else {return nil} guard let strongSelf = self else { return nil }
return !strongSelf.showAllGroupsButton.isSelected return !strongSelf.showAllGroupsButton.isSelected
}) }
.asDriver(onErrorDriveWith: .empty()), .asDriver(onErrorDriveWith: .empty()),
doneTap: self.doneButton.rx.tap.asDriver() doneTap: self.doneButton.rx.tap.asDriver()
)) ))
let dataSource = RxTableViewSectionedReloadDataSource<SectionModel<String, GroupCellViewModel>> { (source, tableView, indexPath, item) -> UITableViewCell in let dataSource = RxTableViewSectionedReloadDataSource<SectionModel<String, GroupCellViewModel>> { _, tableView, _, item -> UITableViewCell in
guard let cell = tableView.dequeueReusableCell(withIdentifier: "\(GroupTableViewCell.self)") as? GroupTableViewCell else { guard let cell = tableView.dequeueReusableCell(withIdentifier: "\(GroupTableViewCell.self)") as? GroupTableViewCell else {
return UITableViewCell() return UITableViewCell()
} }
@ -95,13 +95,11 @@ class GroupFilterViewController: BaseViewController {
.drive(self.showAllGroupsButton.rx.isSelected) .drive(self.showAllGroupsButton.rx.isSelected)
.disposed(by: rx.disposeBag) .disposed(by: rx.disposeBag)
output.dismiss.drive(onNext: {[weak self] in output.dismiss.drive(onNext: { [weak self] in
self?.dismiss(animated: true, completion: nil) self?.dismiss(animated: true, completion: nil)
}) })
.disposed(by: rx.disposeBag) .disposed(by: rx.disposeBag)
} }
} }
extension GroupFilterViewController: UITableViewDelegate { extension GroupFilterViewController: UITableViewDelegate {}
}

View File

@ -6,89 +6,86 @@
// Copyright © 2021 Fin. All rights reserved. // Copyright © 2021 Fin. All rights reserved.
// //
import UIKit
import RxSwift
import RxDataSources
import RxCocoa
import RealmSwift import RealmSwift
import RxCocoa
import RxDataSources
import RxSwift
import UIKit
struct GroupFilterModel { struct GroupFilterModel {
var name:String? var name: String?
var checked:Bool var checked: Bool
} }
class GroupFilterViewModel: ViewModel,ViewModelType {
let groups:[GroupFilterModel] class GroupFilterViewModel: ViewModel, ViewModelType {
init(groups:[GroupFilterModel]) { let groups: [GroupFilterModel]
init(groups: [GroupFilterModel]) {
self.groups = groups self.groups = groups
} }
struct Input { struct Input {
var showAllGroups:Driver<Bool> var showAllGroups: Driver<Bool>
var doneTap:Driver<Void> var doneTap: Driver<Void>
} }
struct Output { struct Output {
var groups:Driver<[ SectionModel<String, GroupCellViewModel>]> var groups: Driver<[SectionModel<String, GroupCellViewModel>]>
var isShowAllGroups:Driver<Bool> var isShowAllGroups: Driver<Bool>
var dismiss:Driver<Void> var dismiss: Driver<Void>
} }
var done = PublishRelay<[String?]>() var done = PublishRelay<[String?]>()
func transform(input: Input) -> Output { func transform(input: Input) -> Output {
// cellModel // cellModel
let groupCellModels = self.groups.map({ filterModel in let groupCellModels = self.groups.map { filterModel in
return GroupCellViewModel(groupFilterModel: filterModel) GroupCellViewModel(groupFilterModel: filterModel)
}) }
//cell checked // cell checked
input.showAllGroups.drive(onNext: { isShowAllGroups in input.showAllGroups.drive(onNext: { isShowAllGroups in
groupCellModels.forEach { model in groupCellModels.forEach { model in
model.checked.accept(isShowAllGroups) model.checked.accept(isShowAllGroups)
} }
}).disposed(by: rx.disposeBag) }).disposed(by: rx.disposeBag)
//cell checked // cell checked
let checkChanged = Observable.merge(groupCellModels.map { model in let checkChanged = Observable.merge(groupCellModels.map { model in
return model.checked.asObservable() model.checked.asObservable()
}) })
// //
let isShowAllGroups = let isShowAllGroups =
checkChanged checkChanged
.map{_ in .map { _ in
return groupCellModels.filter { viewModel in groupCellModels.filter { viewModel in
return viewModel.checked.value viewModel.checked.value
}.count >= groupCellModels.count }.count >= groupCellModels.count
} }
input.doneTap.map { () -> [String?] in input.doneTap.map { () -> [String?] in
let isShowAllGroups = groupCellModels.filter { viewModel in let isShowAllGroups = groupCellModels.filter { viewModel in
return viewModel.checked.value viewModel.checked.value
}.count >= groupCellModels.count }.count >= groupCellModels.count
if isShowAllGroups { if isShowAllGroups {
return [] return []
} }
return groupCellModels return groupCellModels
.filter { $0.checked.value} .filter { $0.checked.value }
.map { $0.name.value } .map { $0.name.value }
} }
.asObservable() .asObservable()
.bind(to: self.done) .bind(to: self.done)
.disposed(by: rx.disposeBag); .disposed(by: rx.disposeBag)
let dismiss = PublishRelay<Void>() let dismiss = PublishRelay<Void>()
input.doneTap.map{ _ in () } input.doneTap.map { _ in () }
.asObservable() .asObservable()
.bind(to: dismiss) .bind(to: dismiss)
.disposed(by: rx.disposeBag) .disposed(by: rx.disposeBag)
return Output( return Output(
groups: Driver.just([ SectionModel(model: "header", items: groupCellModels) ]), groups: Driver.just([SectionModel(model: "header", items: groupCellModels)]),
isShowAllGroups: isShowAllGroups.asDriver(onErrorDriveWith: .empty()), isShowAllGroups: isShowAllGroups.asDriver(onErrorDriveWith: .empty()),
dismiss: dismiss.asDriver(onErrorDriveWith: .empty()) dismiss: dismiss.asDriver(onErrorDriveWith: .empty())
) )
} }
} }

View File

@ -6,14 +6,13 @@
// Copyright © 2018 Fin. All rights reserved. // Copyright © 2018 Fin. All rights reserved.
// //
import UIKit
import UserNotifications
import Material import Material
import RxCocoa import RxCocoa
import RxDataSources import RxDataSources
import UIKit
import UserNotifications
class HomeViewController: BaseViewController { class HomeViewController: BaseViewController {
let newButton: BKButton = { let newButton: BKButton = {
let btn = BKButton() let btn = BKButton()
btn.setImage(Icon.add, for: .normal) btn.setImage(Icon.add, for: .normal)
@ -24,11 +23,11 @@ class HomeViewController: BaseViewController {
let startButton: FABButton = { let startButton: FABButton = {
let button = FABButton(title: NSLocalizedString("RegisterDevice")) let button = FABButton(title: NSLocalizedString("RegisterDevice"))
button.backgroundColor = Color.white button.backgroundColor = Color.white
button.transition([ .scale(0.75) , .opacity(0)] ) button.transition([.scale(0.75), .opacity(0)])
return button return button
}() }()
let tableView :UITableView = { let tableView: UITableView = {
let tableView = UITableView() let tableView = UITableView()
tableView.separatorStyle = .none tableView.separatorStyle = .none
tableView.backgroundColor = Color.grey.lighten4 tableView.backgroundColor = Color.grey.lighten4
@ -44,12 +43,12 @@ class HomeViewController: BaseViewController {
item: UIBarButtonItem(customView: newButton)) item: UIBarButtonItem(customView: newButton))
self.view.addSubview(self.tableView) self.view.addSubview(self.tableView)
self.tableView.snp.makeConstraints { (make ) in self.tableView.snp.makeConstraints { make in
make.top.right.bottom.left.equalToSuperview() make.top.right.bottom.left.equalToSuperview()
} }
self.view.addSubview(self.startButton) self.view.addSubview(self.startButton)
self.startButton.snp.makeConstraints { (make) in self.startButton.snp.makeConstraints { make in
make.width.height.equalTo(150) make.width.height.equalTo(150)
make.centerX.equalToSuperview() make.centerX.equalToSuperview()
make.centerY.equalToSuperview().offset(-50) make.centerY.equalToSuperview().offset(-50)
@ -57,12 +56,12 @@ class HomeViewController: BaseViewController {
Client.shared.currentTabBarController? Client.shared.currentTabBarController?
.tabBarItemDidClick .tabBarItemDidClick
.filter{ $0 == .service } .filter { $0 == .service }
.subscribe(onNext: {[weak self] index in .subscribe(onNext: { [weak self] _ in
self?.tableView.scrollToRow(at: IndexPath(row: 0, section: 0), at: .top, animated: true) self?.tableView.scrollToRow(at: IndexPath(row: 0, section: 0), at: .top, animated: true)
}).disposed(by: self.rx.disposeBag) }).disposed(by: self.rx.disposeBag)
} }
override func bindViewModel() { override func bindViewModel() {
guard let viewModel = self.viewModel as? HomeViewModel else { guard let viewModel = self.viewModel as? HomeViewModel else {
return return
@ -72,96 +71,95 @@ class HomeViewController: BaseViewController {
input: HomeViewModel.Input( input: HomeViewModel.Input(
addCustomServerTap: newButton.rx.tap.asDriver(), addCustomServerTap: newButton.rx.tap.asDriver(),
viewDidAppear: self.rx.methodInvoked(#selector(viewDidAppear(_:))) viewDidAppear: self.rx.methodInvoked(#selector(viewDidAppear(_:)))
.map{ _ in () } .map { _ in () }
.asDriver(onErrorDriveWith: .empty()), .asDriver(onErrorDriveWith: .empty()),
start: self.startButton.rx.tap.asDriver(), start: self.startButton.rx.tap.asDriver(),
clientState: Client.shared.state.asDriver() clientState: Client.shared.state.asDriver()
) )
) )
let dataSource = RxTableViewSectionedReloadDataSource<SectionModel<String,PreviewCardCellViewModel>> { (source, tableView, indexPath, item) -> UITableViewCell in let dataSource = RxTableViewSectionedReloadDataSource<SectionModel<String, PreviewCardCellViewModel>> { _, tableView, _, item -> UITableViewCell in
if let cell = tableView.dequeueReusableCell(withIdentifier: "\(PreviewCardCell.self)") as? PreviewCardCell{ if let cell = tableView.dequeueReusableCell(withIdentifier: "\(PreviewCardCell.self)") as? PreviewCardCell {
cell.bindViewModel(model: item) cell.bindViewModel(model: item)
return cell return cell
} }
return UITableViewCell() return UITableViewCell()
} }
// //
output.title output.title
.drive(self.navigationItem.rx.title) .drive(self.navigationItem.rx.title)
.disposed(by: rx.disposeBag) .disposed(by: rx.disposeBag)
//TableView // TableView
output.previews output.previews
.drive(self.tableView.rx.items(dataSource: dataSource)) .drive(self.tableView.rx.items(dataSource: dataSource))
.disposed(by: rx.disposeBag) .disposed(by: rx.disposeBag)
// //
output.push output.push
.drive(onNext: {[weak self] viewModel in .drive(onNext: { [weak self] viewModel in
self?.pushViewModel(viewModel: viewModel) self?.pushViewModel(viewModel: viewModel)
}) })
.disposed(by: rx.disposeBag) .disposed(by: rx.disposeBag)
//ping clienState // ping clienState
output.clienStateChanged output.clienStateChanged
.drive(Client.shared.state) .drive(Client.shared.state)
.disposed(by: rx.disposeBag) .disposed(by: rx.disposeBag)
// //
output.tableViewHidden output.tableViewHidden
.map{ !$0 } .map { !$0 }
.drive(self.tableView.rx.isHidden) .drive(self.tableView.rx.isHidden)
.disposed(by: rx.disposeBag) .disposed(by: rx.disposeBag)
output.tableViewHidden output.tableViewHidden
.drive(self.startButton.rx.isHidden) .drive(self.startButton.rx.isHidden)
.disposed(by: rx.disposeBag) .disposed(by: rx.disposeBag)
// //
output.registerForRemoteNotifications.drive(onNext: { output.registerForRemoteNotifications.drive(onNext: {
UIApplication.shared.registerForRemoteNotifications() UIApplication.shared.registerForRemoteNotifications()
}) })
.disposed(by: rx.disposeBag) .disposed(by: rx.disposeBag)
// //
output.showSnackbar output.showSnackbar
.drive(onNext: {[weak self] text in .drive(onNext: { [weak self] text in
self?.showSnackbar(text: text) self?.showSnackbar(text: text)
}) })
.disposed(by: rx.disposeBag) .disposed(by: rx.disposeBag)
//startButton // startButton
output.startButtonEnable output.startButtonEnable
.drive(self.startButton.rx.isEnabled) .drive(self.startButton.rx.isEnabled)
.disposed(by: rx.disposeBag) .disposed(by: rx.disposeBag)
// //
output.copy output.copy
.drive(onNext: {[weak self] text in .drive(onNext: { [weak self] text in
UIPasteboard.general.string = text UIPasteboard.general.string = text
self?.showSnackbar(text: NSLocalizedString("Copy")) self?.showSnackbar(text: NSLocalizedString("Copy"))
}) })
.disposed(by: rx.disposeBag) .disposed(by: rx.disposeBag)
// //
output.preview output.preview
.drive(onNext: { url in .drive(onNext: { url in
UIApplication.shared.open(url, options: [:], completionHandler: nil) UIApplication.shared.open(url, options: [:], completionHandler: nil)
}) })
.disposed(by: rx.disposeBag) .disposed(by: rx.disposeBag)
// TableView // TableView
output.reloadData output.reloadData
.drive(onNext: {[weak self] in .drive(onNext: { [weak self] in
self?.tableView.reloadData() self?.tableView.reloadData()
}) })
.disposed(by: rx.disposeBag) .disposed(by: rx.disposeBag)
} }
func pushViewModel(viewModel:ViewModel) { func pushViewModel(viewModel: ViewModel) {
var viewController:UIViewController? var viewController: UIViewController?
if let viewModel = viewModel as? NewServerViewModel { if let viewModel = viewModel as? NewServerViewModel {
viewController = NewServerViewController(viewModel: viewModel) viewController = NewServerViewController(viewModel: viewModel)
} }
@ -173,5 +171,4 @@ class HomeViewController: BaseViewController {
self.navigationController?.pushViewController(viewController, animated: true) self.navigationController?.pushViewController(viewController, animated: true)
} }
} }
} }

View File

@ -7,9 +7,9 @@
// //
import Foundation import Foundation
import RxSwift
import RxCocoa import RxCocoa
import RxDataSources import RxDataSources
import RxSwift
import SwiftyJSON import SwiftyJSON
import UserNotifications import UserNotifications
@ -20,8 +20,9 @@ class HomeViewModel: ViewModel, ViewModelType {
let start: Driver<Void> let start: Driver<Void>
let clientState: Driver<Client.ClienState> let clientState: Driver<Client.ClienState>
} }
struct Output { struct Output {
let previews: Driver<[SectionModel<String,PreviewCardCellViewModel>]> let previews: Driver<[SectionModel<String, PreviewCardCellViewModel>]>
let push: Driver<ViewModel> let push: Driver<ViewModel>
let title: Driver<String> let title: Driver<String>
let clienStateChanged: Driver<Client.ClienState> let clienStateChanged: Driver<Client.ClienState>
@ -34,20 +35,22 @@ class HomeViewModel: ViewModel, ViewModelType {
let registerForRemoteNotifications: Driver<Void> let registerForRemoteNotifications: Driver<Void>
} }
let previews:[PreviewModel] = { let previews: [PreviewModel] = {
return [ [
PreviewModel( PreviewModel(
body: NSLocalizedString("CustomedNotificationContent"), body: NSLocalizedString("CustomedNotificationContent"),
notice: NSLocalizedString("Notice1")), notice: NSLocalizedString("Notice1")
),
PreviewModel( PreviewModel(
title: NSLocalizedString("CustomedNotificationTitle"), title: NSLocalizedString("CustomedNotificationTitle"),
body: NSLocalizedString("CustomedNotificationContent"), body: NSLocalizedString("CustomedNotificationContent"),
notice: NSLocalizedString("Notice2")), notice: NSLocalizedString("Notice2")
),
PreviewModel( PreviewModel(
body: NSLocalizedString("notificationSound"), body: NSLocalizedString("notificationSound"),
notice: NSLocalizedString("setSounds"), notice: NSLocalizedString("setSounds"),
queryParameter: "sound=minuet", queryParameter: "sound=minuet",
moreInfo:NSLocalizedString("viewAllSounds"), moreInfo: NSLocalizedString("viewAllSounds"),
moreViewModel: SoundsViewModel() moreViewModel: SoundsViewModel()
), ),
PreviewModel( PreviewModel(
@ -87,36 +90,35 @@ class HomeViewModel: ViewModel, ViewModelType {
}() }()
func transform(input: Input) -> Output { func transform(input: Input) -> Output {
let title = BehaviorRelay(value: URL(string: ServerManager.shared.currentAddress)?.host ?? "") let title = BehaviorRelay(value: URL(string: ServerManager.shared.currentAddress)?.host ?? "")
let sectionModel = SectionModel( let sectionModel = SectionModel(
model: "previews", model: "previews",
items: previews.map { PreviewCardCellViewModel(previewModel: $0, clientState: input.clientState) }) items: previews.map { PreviewCardCellViewModel(previewModel: $0, clientState: input.clientState) }
)
//
let customServer = input.addCustomServerTap.map { NewServerViewModel() as ViewModel }
// // title
let customServer = input.addCustomServerTap.map{ NewServerViewModel() as ViewModel }
// title
customServer customServer
.flatMapLatest({ (model) -> Driver<String> in .flatMapLatest { model -> Driver<String> in
return (model as! NewServerViewModel).pop.asDriver(onErrorJustReturn: "") (model as! NewServerViewModel).pop.asDriver(onErrorJustReturn: "")
}) }
.drive(title) .drive(title)
.disposed(by: rx.disposeBag) .disposed(by: rx.disposeBag)
//previewnotice // previewnotice
let noticeTap = Driver.merge(sectionModel.items.map{ $0.noticeTap.asDriver(onErrorDriveWith: .empty()) }) let noticeTap = Driver.merge(sectionModel.items.map { $0.noticeTap.asDriver(onErrorDriveWith: .empty()) })
// //
let clienState = input.viewDidAppear let clienState = input.viewDidAppear
.asObservable().flatMapLatest { _ -> Observable<Result<JSON,ApiError>> in .asObservable().flatMapLatest { _ -> Observable<Result<JSON, ApiError>> in
BarkApi.provider BarkApi.provider
.request(.ping(baseURL: ServerManager.shared.currentAddress)) .request(.ping(baseURL: ServerManager.shared.currentAddress))
.filterResponseError() .filterResponseError()
} }
.map { (response) -> Client.ClienState in .map { response -> Client.ClienState in
switch response { switch response {
case .failure: case .failure:
return .serverError return .serverError
@ -125,40 +127,39 @@ class HomeViewModel: ViewModel, ViewModelType {
} }
} }
//APP // APP
let authorizationStatus = Single<UNAuthorizationStatus>.create { (single) -> Disposable in let authorizationStatus = Single<UNAuthorizationStatus>.create { single -> Disposable in
UNUserNotificationCenter.current().getNotificationSettings { (settings) in UNUserNotificationCenter.current().getNotificationSettings { settings in
single(.success(settings.authorizationStatus)) single(.success(settings.authorizationStatus))
} }
return Disposables.create() return Disposables.create()
} }
.map { $0 == .authorized} .map { $0 == .authorized }
// //
let startRequestAuthorization = Single<Bool>.create { (single) -> Disposable in let startRequestAuthorization = Single<Bool>.create { single -> Disposable in
let center = UNUserNotificationCenter.current() let center = UNUserNotificationCenter.current()
center.requestAuthorization(options: [.alert , .sound , .badge], completionHandler: {(_ granted: Bool, _ error: Error?) -> Void in center.requestAuthorization(options: [.alert, .sound, .badge], completionHandler: { (_ granted: Bool, _: Error?) -> Void in
single(.success(granted)) single(.success(granted))
}) })
return Disposables.create() return Disposables.create()
} }
.asObservable() .asObservable()
// //
let tableViewHidden = authorizationStatus let tableViewHidden = authorizationStatus
.asObservable() .asObservable()
.concat(input.start .concat(input.start
.asObservable() .asObservable()
.flatMapLatest{ startRequestAuthorization }) .flatMapLatest { startRequestAuthorization })
.asDriver(onErrorJustReturn: false) .asDriver(onErrorJustReturn: false)
let showSnackbar = PublishRelay<String>() let showSnackbar = PublishRelay<String>()
// //
tableViewHidden tableViewHidden
.skip(1) .skip(1)
.compactMap { (granted) -> String? in .compactMap { granted -> String? in
if !granted { if !granted {
return NSLocalizedString("AllowNotifications") return NSLocalizedString("AllowNotifications")
} }
@ -168,36 +169,35 @@ class HomeViewModel: ViewModel, ViewModelType {
.bind(to: showSnackbar) .bind(to: showSnackbar)
.disposed(by: rx.disposeBag) .disposed(by: rx.disposeBag)
// viewController // viewController
let registerForRemoteNotifications = tableViewHidden let registerForRemoteNotifications = tableViewHidden
.skip(1) .skip(1)
.filter{ $0 } .filter { $0 }
.map{ _ in () } .map { _ in () }
//client state // client state
input.clientState.drive(onNext: { state in input.clientState.drive(onNext: { state in
switch state { switch state {
case .ok: break; case .ok: break
case .serverError: case .serverError:
showSnackbar.accept(NSLocalizedString("ServerError")) showSnackbar.accept(NSLocalizedString("ServerError"))
default: break; default: break
} }
}) })
.disposed(by: rx.disposeBag) .disposed(by: rx.disposeBag)
return Output( return Output(
previews:Driver.just([sectionModel]), previews: Driver.just([sectionModel]),
push: Driver<ViewModel>.merge(customServer,noticeTap), push: Driver<ViewModel>.merge(customServer, noticeTap),
title: title.asDriver(), title: title.asDriver(),
clienStateChanged: clienState.asDriver(onErrorDriveWith: .empty()), clienStateChanged: clienState.asDriver(onErrorDriveWith: .empty()),
tableViewHidden: tableViewHidden, tableViewHidden: tableViewHidden,
showSnackbar: showSnackbar.asDriver(onErrorDriveWith: .empty()), showSnackbar: showSnackbar.asDriver(onErrorDriveWith: .empty()),
startButtonEnable: Driver.just(true), startButtonEnable: Driver.just(true),
copy: Driver.merge(sectionModel.items.map{ $0.copy.asDriver(onErrorDriveWith: .empty()) }), copy: Driver.merge(sectionModel.items.map { $0.copy.asDriver(onErrorDriveWith: .empty()) }),
preview: Driver.merge(sectionModel.items.map{ $0.preview.asDriver(onErrorDriveWith: .empty()) }), preview: Driver.merge(sectionModel.items.map { $0.preview.asDriver(onErrorDriveWith: .empty()) }),
reloadData: input.clientState.map{ _ in ()}, reloadData: input.clientState.map { _ in () },
registerForRemoteNotifications: registerForRemoteNotifications registerForRemoteNotifications: registerForRemoteNotifications
) )
} }
} }

View File

@ -6,22 +6,21 @@
// Copyright © 2020 Fin. All rights reserved. // Copyright © 2020 Fin. All rights reserved.
// //
import UIKit
import Material import Material
import MJRefresh
import RealmSwift import RealmSwift
import RxCocoa import RxCocoa
import RxDataSources import RxDataSources
import MJRefresh
import RxSwift import RxSwift
import UIKit
enum MessageDeleteType: Int{ enum MessageDeleteType: Int {
case lastHour = 0 case lastHour = 0
case today case today
case todayAndYesterday case todayAndYesterday
case allTime case allTime
var string: String{ var string: String {
get {
return [ return [
NSLocalizedString("lastHour"), NSLocalizedString("lastHour"),
NSLocalizedString("today"), NSLocalizedString("today"),
@ -29,7 +28,6 @@ enum MessageDeleteType: Int{
NSLocalizedString("allTime"), NSLocalizedString("allTime"),
][self.rawValue] ][self.rawValue]
} }
}
} }
class MessageListViewController: BaseViewController { class MessageListViewController: BaseViewController {
@ -64,7 +62,7 @@ class MessageListViewController: BaseViewController {
navigationItem.setBarButtonItems(items: [UIBarButtonItem(customView: deleteButton), UIBarButtonItem(customView: groupButton)], left: false) navigationItem.setBarButtonItems(items: [UIBarButtonItem(customView: deleteButton), UIBarButtonItem(customView: groupButton)], left: false)
self.view.addSubview(tableView) self.view.addSubview(tableView)
tableView.snp.makeConstraints { (make) in tableView.snp.makeConstraints { make in
make.edges.equalToSuperview() make.edges.equalToSuperview()
} }
tableView.rx.setDelegate(self).disposed(by: rx.disposeBag) tableView.rx.setDelegate(self).disposed(by: rx.disposeBag)
@ -74,13 +72,12 @@ class MessageListViewController: BaseViewController {
// tab // tab
Client.shared.currentTabBarController? Client.shared.currentTabBarController?
.tabBarItemDidClick .tabBarItemDidClick
.filter{ $0 == .messageHistory } .filter { $0 == .messageHistory }
.subscribe(onNext: {[weak self] index in .subscribe(onNext: { [weak self] _ in
self?.scrollToTop() self?.scrollToTop()
}).disposed(by: self.rx.disposeBag) }).disposed(by: self.rx.disposeBag)
// APP1
//APP1
var lastAutoRefreshdate = Date() var lastAutoRefreshdate = Date()
NotificationCenter.default.rx NotificationCenter.default.rx
.notification(UIApplication.willEnterForegroundNotification) .notification(UIApplication.willEnterForegroundNotification)
@ -92,11 +89,10 @@ class MessageListViewController: BaseViewController {
} }
return false return false
} }
.subscribe(onNext: {[weak self] _ in .subscribe(onNext: { [weak self] _ in
self?.tableView.refreshControl?.sendActions(for: .valueChanged) self?.tableView.refreshControl?.sendActions(for: .valueChanged)
self?.scrollToTop() self?.scrollToTop()
}).disposed(by: rx.disposeBag) }).disposed(by: rx.disposeBag)
} }
override func bindViewModel() { override func bindViewModel() {
@ -106,10 +102,10 @@ class MessageListViewController: BaseViewController {
let batchDelete = deleteButton.rx let batchDelete = deleteButton.rx
.tap .tap
.flatMapLatest { Void -> PublishRelay<MessageDeleteType> in .flatMapLatest { _ -> PublishRelay<MessageDeleteType> in
let relay = PublishRelay<MessageDeleteType>() let relay = PublishRelay<MessageDeleteType>()
func alert(_ type:MessageDeleteType){ func alert(_ type: MessageDeleteType) {
let alertController = UIAlertController(title: nil, message: "\(NSLocalizedString("clearFrom"))\n\(type.string)", preferredStyle: .alert) let alertController = UIAlertController(title: nil, message: "\(NSLocalizedString("clearFrom"))\n\(type.string)", preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: NSLocalizedString("clear"), style: .destructive, handler: { _ in alertController.addAction(UIAlertAction(title: NSLocalizedString("clear"), style: .destructive, handler: { _ in
relay.accept(type) relay.accept(type)
@ -143,67 +139,65 @@ class MessageListViewController: BaseViewController {
loadMore: tableView.mj_footer!.rx.refresh.asDriver(), loadMore: tableView.mj_footer!.rx.refresh.asDriver(),
itemDelete: tableView.rx.itemDeleted.asDriver(), itemDelete: tableView.rx.itemDeleted.asDriver(),
itemSelected: tableView.rx.modelSelected(MessageTableViewCellViewModel.self).asDriver(), itemSelected: tableView.rx.modelSelected(MessageTableViewCellViewModel.self).asDriver(),
delete:batchDelete.asDriver(onErrorDriveWith: .empty()), delete: batchDelete.asDriver(onErrorDriveWith: .empty()),
groupTap: groupButton.rx.tap.asDriver(), groupTap: groupButton.rx.tap.asDriver(),
searchText: navigationItem.searchController!.searchBar.rx.text.asObservable() searchText: navigationItem.searchController!.searchBar.rx.text.asObservable()))
))
//tableView // tableView
output.refreshAction output.refreshAction
.drive(tableView.rx.refreshAction) .drive(tableView.rx.refreshAction)
.disposed(by: rx.disposeBag) .disposed(by: rx.disposeBag)
//tableView // tableView
let dataSource = RxTableViewSectionedAnimatedDataSource<MessageSection>( let dataSource = RxTableViewSectionedAnimatedDataSource<MessageSection>(
animationConfiguration: AnimationConfiguration( animationConfiguration: AnimationConfiguration(
insertAnimation: .none, insertAnimation: .none,
reloadAnimation: .none, reloadAnimation: .none,
deleteAnimation: .left), deleteAnimation: .left),
configureCell:{ (source, tableView, indexPath, item) -> UITableViewCell in configureCell: { _, tableView, _, item -> UITableViewCell in
guard let cell = tableView.dequeueReusableCell(withIdentifier: "\(MessageTableViewCell.self)") as? MessageTableViewCell else { guard let cell = tableView.dequeueReusableCell(withIdentifier: "\(MessageTableViewCell.self)") as? MessageTableViewCell else {
return UITableViewCell () return UITableViewCell()
} }
cell.bindViewModel(model: item) cell.bindViewModel(model: item)
return cell return cell
}, canEditRowAtIndexPath: { _, _ in }, canEditRowAtIndexPath: { _, _ in
return true true
}) })
output.messages output.messages
.drive(tableView.rx.items(dataSource: dataSource)) .drive(tableView.rx.items(dataSource: dataSource))
.disposed(by: rx.disposeBag) .disposed(by: rx.disposeBag)
//messagealert // messagealert
output.alertMessage.drive(onNext: {[weak self] message in output.alertMessage.drive(onNext: { [weak self] message in
self?.alertMessage(message: message) self?.alertMessage(message: message)
}).disposed(by: rx.disposeBag) }).disposed(by: rx.disposeBag)
//messageURL // messageURL
output.urlTap.drive(onNext: { url in output.urlTap.drive(onNext: { url in
if ["http","https"].contains(url.scheme?.lowercased() ?? ""){ if ["http", "https"].contains(url.scheme?.lowercased() ?? "") {
self.navigationController?.present(BarkSFSafariViewController(url: url), animated: true, completion: nil) self.navigationController?.present(BarkSFSafariViewController(url: url), animated: true, completion: nil)
} }
else{ else {
UIApplication.shared.open(url, options: [:], completionHandler: nil) UIApplication.shared.open(url, options: [:], completionHandler: nil)
} }
}).disposed(by: rx.disposeBag) }).disposed(by: rx.disposeBag)
// //
output.groupFilter output.groupFilter
.drive(onNext: {[weak self] groupModel in .drive(onNext: { [weak self] groupModel in
self?.navigationController?.present(BarkNavigationController(rootViewController: GroupFilterViewController(viewModel: groupModel)), animated: true, completion: nil) self?.navigationController?.present(BarkNavigationController(rootViewController: GroupFilterViewController(viewModel: groupModel)), animated: true, completion: nil)
}).disposed(by: rx.disposeBag) }).disposed(by: rx.disposeBag)
// //
output.title output.title
.drive(self.navigationItem.rx.title).disposed(by: rx.disposeBag) .drive(self.navigationItem.rx.title).disposed(by: rx.disposeBag)
} }
func alertMessage(message:String) { func alertMessage(message: String) {
let alertController = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet) let alertController = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
let copyAction = UIAlertAction(title: NSLocalizedString("Copy2"), style: .default, handler: {[weak self] let copyAction = UIAlertAction(title: NSLocalizedString("Copy2"), style: .default, handler: { [weak self]
(alert: UIAlertAction) -> Void in (_: UIAlertAction) -> Void in
UIPasteboard.general.string = message UIPasteboard.general.string = message
self?.showSnackbar(text: NSLocalizedString("Copy")) self?.showSnackbar(text: NSLocalizedString("Copy"))
}) })
@ -216,7 +210,7 @@ class MessageListViewController: BaseViewController {
self.navigationController?.present(alertController, animated: true, completion: nil) self.navigationController?.present(alertController, animated: true, completion: nil)
} }
private func scrollToTop(){ private func scrollToTop() {
if self.tableView.visibleCells.count > 0 { if self.tableView.visibleCells.count > 0 {
self.tableView.scrollToRow(at: IndexPath(row: 0, section: 0), at: .top, animated: true) self.tableView.scrollToRow(at: IndexPath(row: 0, section: 0), at: .top, animated: true)
} }
@ -225,7 +219,7 @@ class MessageListViewController: BaseViewController {
extension MessageListViewController: UITableViewDelegate { extension MessageListViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? { func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
let action = UIContextualAction(style: .destructive, title: "删除") {[weak self] (action, sourceView, actionPerformed) in let action = UIContextualAction(style: .destructive, title: "删除") { [weak self] _, _, actionPerformed in
self?.tableView.dataSource?.tableView?(self!.tableView, commit: .delete, forRowAt: indexPath) self?.tableView.dataSource?.tableView?(self!.tableView, commit: .delete, forRowAt: indexPath)
actionPerformed(true) actionPerformed(true)
} }
@ -235,14 +229,15 @@ extension MessageListViewController: UITableViewDelegate {
} }
} }
extension MessageListViewController: UISearchControllerDelegate{ extension MessageListViewController: UISearchControllerDelegate {
func scrollViewWillBeginDragging(_ scrollView: UIScrollView) { func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
if self.navigationItem.searchController?.searchBar.isFirstResponder == true{ if self.navigationItem.searchController?.searchBar.isFirstResponder == true {
self.navigationItem.searchController?.searchBar.resignFirstResponder() self.navigationItem.searchController?.searchBar.resignFirstResponder()
} }
} }
func willDismissSearchController(_ searchController: UISearchController) { func willDismissSearchController(_ searchController: UISearchController) {
if !searchController.searchBar.isFirstResponder{ if !searchController.searchBar.isFirstResponder {
/* /*
searchBar searchBar.rx.text searchBar searchBar.rx.text
searchBar.rx.text searchBar.rx.text

View File

@ -7,12 +7,12 @@
// //
import Foundation import Foundation
import RxSwift
import RxDataSources
import RxCocoa
import RealmSwift import RealmSwift
import RxCocoa
import RxDataSources
import RxSwift
class MessageListViewModel: ViewModel,ViewModelType { class MessageListViewModel: ViewModel, ViewModelType {
struct Input { struct Input {
var refresh: Driver<Void> var refresh: Driver<Void>
var loadMore: Driver<Void> var loadMore: Driver<Void>
@ -32,10 +32,10 @@ class MessageListViewModel: ViewModel,ViewModelType {
var title: Driver<String> var title: Driver<String>
} }
private var results:Results<Message>? private var results: Results<Message>?
// //
private func getResults(filterGroups:[String?], searchText:String?) -> Results<Message>? { private func getResults(filterGroups: [String?], searchText: String?) -> Results<Message>? {
if let realm = try? Realm() { if let realm = try? Realm() {
var results = realm.objects(Message.self) var results = realm.objects(Message.self)
.filter("isDeleted != true") .filter("isDeleted != true")
@ -60,7 +60,7 @@ class MessageListViewModel: ViewModel,ViewModelType {
guard endIndex > startIndex else { guard endIndex > startIndex else {
return [] return []
} }
var messages:[Message] = [] var messages: [Message] = []
for i in startIndex ..< endIndex { for i in startIndex ..< endIndex {
messages.append(result[i]) messages.append(result[i])
} }
@ -70,12 +70,11 @@ class MessageListViewModel: ViewModel,ViewModelType {
return [] return []
} }
func transform(input: Input) -> Output { func transform(input: Input) -> Output {
let alertMessage = input.itemSelected.map { (model) -> String in let alertMessage = input.itemSelected.map { model -> String in
let message = model.message let message = model.message
var copyContent:String = "" var copyContent: String = ""
if let title = message.title { if let title = message.title {
copyContent += "\(title)\n" copyContent += "\(title)\n"
} }
@ -97,45 +96,45 @@ class MessageListViewModel: ViewModel,ViewModelType {
let refreshAction = BehaviorRelay<MJRefreshAction>(value: .none) let refreshAction = BehaviorRelay<MJRefreshAction>(value: .none)
// //
let filterGroups: BehaviorRelay<[String?]> = { let filterGroups: BehaviorRelay<[String?]> = {
if let groups:[String?] = Settings["me.fin.filterGroups"] { if let groups: [String?] = Settings["me.fin.filterGroups"] {
return BehaviorRelay<[String?]>(value: groups) return BehaviorRelay<[String?]>(value: groups)
} }
return BehaviorRelay<[String?]>(value: []) return BehaviorRelay<[String?]>(value: [])
}() }()
// Message MessageSection // Message MessageSection
func messagesToMessageSection(messages:[Message]) -> [MessageSection] { func messagesToMessageSection(messages: [Message]) -> [MessageSection] {
let cellViewModels = messages.map({ (message) -> MessageTableViewCellViewModel in let cellViewModels = messages.map { message -> MessageTableViewCellViewModel in
return MessageTableViewCellViewModel(message: message) MessageTableViewCellViewModel(message: message)
}) }
return [MessageSection(header: "model", messages: cellViewModels)] return [MessageSection(header: "model", messages: cellViewModels)]
} }
// //
filterGroups filterGroups
.subscribe(onNext: {filterGroups in .subscribe(onNext: { filterGroups in
if filterGroups.count <= 0 { if filterGroups.count <= 0 {
titleRelay.accept(NSLocalizedString("historyMessage")) titleRelay.accept(NSLocalizedString("historyMessage"))
} }
else{ else {
titleRelay.accept(filterGroups.map { $0 ?? NSLocalizedString("default") }.joined(separator: " , ")) titleRelay.accept(filterGroups.map { $0 ?? NSLocalizedString("default") }.joined(separator: " , "))
} }
}).disposed(by: rx.disposeBag) }).disposed(by: rx.disposeBag)
// //
Observable Observable
.combineLatest(filterGroups, input.searchText) .combineLatest(filterGroups, input.searchText)
.subscribe(onNext: {[weak self] groups, searchText in .subscribe(onNext: { [weak self] groups, searchText in
self?.results = self?.getResults(filterGroups: groups, searchText: searchText) self?.results = self?.getResults(filterGroups: groups, searchText: searchText)
}).disposed(by: rx.disposeBag) }).disposed(by: rx.disposeBag)
// //
Observable Observable
.merge( .merge(
input.refresh.asObservable().map{ () }, input.refresh.asObservable().map { () },
filterGroups.map{ _ in () }, filterGroups.map { _ in () },
input.searchText.asObservable().map{ _ in () } input.searchText.asObservable().map { _ in () }
) )
.subscribe(onNext: {[weak self] in .subscribe(onNext: { [weak self] in
guard let strongSelf = self else { return } guard let strongSelf = self else { return }
strongSelf.page = 0 strongSelf.page = 0
messagesRelay.accept( messagesRelay.accept(
@ -146,27 +145,27 @@ class MessageListViewModel: ViewModel,ViewModelType {
refreshAction.accept(.endRefresh) refreshAction.accept(.endRefresh)
}).disposed(by: rx.disposeBag) }).disposed(by: rx.disposeBag)
// //
input.loadMore.asObservable() input.loadMore.asObservable()
.subscribe(onNext: {[weak self] in .subscribe(onNext: { [weak self] in
guard let strongSelf = self else { return } guard let strongSelf = self else { return }
let messages = strongSelf.getNextPage() let messages = strongSelf.getNextPage()
let cellViewModels = messages.map({ (message) -> MessageTableViewCellViewModel in let cellViewModels = messages.map { message -> MessageTableViewCellViewModel in
return MessageTableViewCellViewModel(message: message) MessageTableViewCellViewModel(message: message)
}) }
refreshAction.accept(.endLoadmore) refreshAction.accept(.endLoadmore)
if var section = messagesRelay.value.first { if var section = messagesRelay.value.first {
section.messages.append(contentsOf: cellViewModels) section.messages.append(contentsOf: cellViewModels)
messagesRelay.accept([section]) messagesRelay.accept([section])
} }
else{ else {
messagesRelay.accept([MessageSection(header: "model", messages: cellViewModels)]) messagesRelay.accept([MessageSection(header: "model", messages: cellViewModels)])
} }
}).disposed(by: rx.disposeBag) }).disposed(by: rx.disposeBag)
//message // message
input.itemDelete.drive(onNext: {[weak self] indexPath in input.itemDelete.drive(onNext: { [weak self] indexPath in
if var section = messagesRelay.value.first { if var section = messagesRelay.value.first {
if let realm = try? Realm() { if let realm = try? Realm() {
try? realm.write { try? realm.write {
@ -180,19 +179,19 @@ class MessageListViewModel: ViewModel,ViewModelType {
}).disposed(by: rx.disposeBag) }).disposed(by: rx.disposeBag)
// cell url // cell url
let urlTap = messagesRelay.flatMapLatest { (section) -> Observable<String> in let urlTap = messagesRelay.flatMapLatest { section -> Observable<String> in
if let section = section.first { if let section = section.first {
let taps = section.messages.compactMap { (model) -> Observable<String> in let taps = section.messages.compactMap { model -> Observable<String> in
return model.urlTap.asObservable() model.urlTap.asObservable()
} }
return Observable.merge(taps) return Observable.merge(taps)
} }
return .empty() return .empty()
} }
.compactMap { URL(string: $0) } //url .compactMap { URL(string: $0) } // url
// //
input.delete.drive(onNext: {[weak self] type in input.delete.drive(onNext: { [weak self] type in
guard let strongSelf = self else { return } guard let strongSelf = self else { return }
var date = Date() var date = Date()
@ -211,7 +210,7 @@ class MessageListViewModel: ViewModel,ViewModelType {
guard let messages = strongSelf.getResults(filterGroups: filterGroups.value, searchText: nil)?.filter("createDate >= %@", date) else { guard let messages = strongSelf.getResults(filterGroups: filterGroups.value, searchText: nil)?.filter("createDate >= %@", date) else {
return return
} }
try? realm.write{ try? realm.write {
for msg in messages { for msg in messages {
msg.isDeleted = true msg.isDeleted = true
} }
@ -223,31 +222,31 @@ class MessageListViewModel: ViewModel,ViewModelType {
}).disposed(by: rx.disposeBag) }).disposed(by: rx.disposeBag)
// //
let groupFilter = input.groupTap.compactMap {() -> GroupFilterViewModel? in let groupFilter = input.groupTap.compactMap { () -> GroupFilterViewModel? in
if let realm = try? Realm() { if let realm = try? Realm() {
let groups = realm.objects(Message.self) let groups = realm.objects(Message.self)
.filter("isDeleted != true") .filter("isDeleted != true")
.distinct(by: ["group"]) .distinct(by: ["group"])
.value(forKeyPath: "group") as? [String?] .value(forKeyPath: "group") as? [String?]
let groupModels = groups?.compactMap({ groupName -> GroupFilterModel in let groupModels = groups?.compactMap { groupName -> GroupFilterModel in
var check = true var check = true
if filterGroups.value.count > 0 { if filterGroups.value.count > 0 {
check = filterGroups.value.contains(groupName) check = filterGroups.value.contains(groupName)
} }
return GroupFilterModel(name: groupName, checked: check) return GroupFilterModel(name: groupName, checked: check)
}) }
if let models = groupModels { if let models = groupModels {
let viewModel = GroupFilterViewModel(groups: models) let viewModel = GroupFilterViewModel(groups: models)
// group // group
viewModel.done.subscribe(onNext: { filterGroups in viewModel.done.subscribe(onNext: { filterGroups in
Settings["me.fin.filterGroups"] = filterGroups Settings["me.fin.filterGroups"] = filterGroups
}).disposed(by: viewModel.rx.disposeBag) }).disposed(by: viewModel.rx.disposeBag)
// //
viewModel.done viewModel.done
.bind(to: filterGroups) .bind(to: filterGroups)
.disposed(by: viewModel.rx.disposeBag) .disposed(by: viewModel.rx.disposeBag)

View File

@ -6,9 +6,9 @@
// Copyright © 2020 Fin. All rights reserved. // Copyright © 2020 Fin. All rights reserved.
// //
import UIKit
import Material import Material
import RxDataSources import RxDataSources
import UIKit
class MessageSettingsViewController: BaseViewController { class MessageSettingsViewController: BaseViewController {
let tableView: UITableView = { let tableView: UITableView = {
let tableView = UITableView() let tableView = UITableView()
@ -22,14 +22,16 @@ class MessageSettingsViewController: BaseViewController {
return tableView return tableView
}() }()
override func makeUI() { override func makeUI() {
self.title = NSLocalizedString("settings") self.title = NSLocalizedString("settings")
self.view.addSubview(tableView) self.view.addSubview(tableView)
tableView.snp.makeConstraints { (make) in tableView.snp.makeConstraints { make in
make.edges.equalToSuperview() make.edges.equalToSuperview()
} }
} }
override func bindViewModel() { override func bindViewModel() {
guard let viewModel = self.viewModel as? MessageSettingsViewModel else { guard let viewModel = self.viewModel as? MessageSettingsViewModel else {
return return
@ -40,9 +42,9 @@ class MessageSettingsViewController: BaseViewController {
) )
) )
let dataSource = RxTableViewSectionedReloadDataSource<SectionModel<String, MessageSettingItem>> { (source, tableView, indexPath, item) -> UITableViewCell in let dataSource = RxTableViewSectionedReloadDataSource<SectionModel<String, MessageSettingItem>> { _, tableView, _, item -> UITableViewCell in
switch item { switch item {
case .label(let text): case let .label(text):
if let cell = tableView.dequeueReusableCell(withIdentifier: "\(LabelCell.self)") as? LabelCell { if let cell = tableView.dequeueReusableCell(withIdentifier: "\(LabelCell.self)") as? LabelCell {
cell.textLabel?.text = text cell.textLabel?.text = text
return cell return cell
@ -51,12 +53,12 @@ class MessageSettingsViewController: BaseViewController {
if let cell = tableView.dequeueReusableCell(withIdentifier: "\(iCloudStatusCell.self)") { if let cell = tableView.dequeueReusableCell(withIdentifier: "\(iCloudStatusCell.self)") {
return cell return cell
} }
case .archiveSetting(let viewModel): case let .archiveSetting(viewModel):
if let cell = tableView.dequeueReusableCell(withIdentifier: "\(ArchiveSettingCell.self)") as? ArchiveSettingCell { if let cell = tableView.dequeueReusableCell(withIdentifier: "\(ArchiveSettingCell.self)") as? ArchiveSettingCell {
cell.bindViewModel(model: viewModel) cell.bindViewModel(model: viewModel)
return cell return cell
} }
case let .detail(title,text,textColor,_): case let .detail(title, text, textColor, _):
if let cell = tableView.dequeueReusableCell(withIdentifier: "\(DetailTextCell.self)") as? DetailTextCell { if let cell = tableView.dequeueReusableCell(withIdentifier: "\(DetailTextCell.self)") as? DetailTextCell {
cell.textLabel?.text = title cell.textLabel?.text = title
cell.detailTextLabel?.text = text cell.detailTextLabel?.text = text
@ -78,10 +80,8 @@ class MessageSettingsViewController: BaseViewController {
.drive(tableView.rx.items(dataSource: dataSource)) .drive(tableView.rx.items(dataSource: dataSource))
.disposed(by: rx.disposeBag) .disposed(by: rx.disposeBag)
output.openUrl.drive {[weak self] url in output.openUrl.drive { [weak self] url in
self?.navigationController?.present(BarkSFSafariViewController(url: url), animated: true, completion: nil) self?.navigationController?.present(BarkSFSafariViewController(url: url), animated: true, completion: nil)
}.disposed(by: rx.disposeBag) }.disposed(by: rx.disposeBag)
} }
} }

View File

@ -7,22 +7,23 @@
// //
import Foundation import Foundation
import RxSwift import Material
import RxCocoa import RxCocoa
import RxDataSources import RxDataSources
import Material import RxSwift
class MessageSettingsViewModel: ViewModel, ViewModelType { class MessageSettingsViewModel: ViewModel, ViewModelType {
struct Input { struct Input {
var itemSelected: Driver<MessageSettingItem> var itemSelected: Driver<MessageSettingItem>
} }
struct Output {
var settings:Driver<[SectionModel<String, MessageSettingItem>]>
var openUrl:Driver<URL>
}
func transform(input: Input) -> Output {
let settings:[MessageSettingItem] = { struct Output {
var settings: Driver<[SectionModel<String, MessageSettingItem>]>
var openUrl: Driver<URL>
}
func transform(input: Input) -> Output {
let settings: [MessageSettingItem] = {
var settings = [MessageSettingItem]() var settings = [MessageSettingItem]()
settings.append(.label(text: "iCloud")) settings.append(.label(text: "iCloud"))
settings.append(.iCloudStatus) settings.append(.iCloudStatus)
@ -32,17 +33,17 @@ class MessageSettingsViewModel: ViewModel, ViewModelType {
settings.append(.label(text: NSLocalizedString("archiveNote"))) settings.append(.label(text: NSLocalizedString("archiveNote")))
if let infoDict = Bundle.main.infoDictionary, if let infoDict = Bundle.main.infoDictionary,
let runId = infoDict["GitHub Run Id"] as? String{ let runId = infoDict["GitHub Run Id"] as? String
{
settings.append(.label(text: NSLocalizedString("buildInfo"))) settings.append(.label(text: NSLocalizedString("buildInfo")))
settings.append(.detail( settings.append(.detail(
title: "Github Run Id", title: "Github Run Id",
text:"\(runId)", text: "\(runId)",
textColor: Color.grey.darken2, textColor: Color.grey.darken2,
url: URL(string: "https://github.com/Finb/Bark/actions/runs/\(runId)"))) url: URL(string: "https://github.com/Finb/Bark/actions/runs/\(runId)")))
settings.append(.label(text: NSLocalizedString("buildDesc"))) settings.append(.label(text: NSLocalizedString("buildDesc")))
} }
settings.append(.label(text: NSLocalizedString("other"))) settings.append(.label(text: NSLocalizedString("other")))
settings.append(.detail( settings.append(.detail(
title: NSLocalizedString("faq"), title: NSLocalizedString("faq"),
@ -66,7 +67,7 @@ class MessageSettingsViewModel: ViewModel, ViewModelType {
return settings return settings
}() }()
settings.compactMap { (item) -> ArchiveSettingCellViewModel? in settings.compactMap { item -> ArchiveSettingCellViewModel? in
if case let MessageSettingItem.archiveSetting(viewModel) = item { if case let MessageSettingItem.archiveSetting(viewModel) = item {
return viewModel return viewModel
} }
@ -74,7 +75,7 @@ class MessageSettingsViewModel: ViewModel, ViewModelType {
} }
.first? .first?
.on .on
.subscribe(onNext: { (on) in .subscribe(onNext: { on in
ArchiveSettingManager.shared.isArchive = on ArchiveSettingManager.shared.isArchive = on
}).disposed(by: rx.disposeBag) }).disposed(by: rx.disposeBag)
@ -85,25 +86,22 @@ class MessageSettingsViewModel: ViewModel, ViewModelType {
return nil return nil
} }
return Output( return Output(
settings: Driver<[SectionModel<String, MessageSettingItem>]> settings: Driver<[SectionModel<String, MessageSettingItem>]>
.just([SectionModel(model: "model", items: settings)]), .just([SectionModel(model: "model", items: settings)]),
openUrl: openUrl openUrl: openUrl)
)
} }
} }
enum MessageSettingItem { enum MessageSettingItem {
// //
case label(text:String) case label(text: String)
// iCloud // iCloud
case iCloudStatus case iCloudStatus
// //
case archiveSetting(viewModel:ArchiveSettingCellViewModel) case archiveSetting(viewModel: ArchiveSettingCellViewModel)
// cell // cell
case detail(title:String?, text:String?, textColor:UIColor?, url:URL?) case detail(title: String?, text: String?, textColor: UIColor?, url: URL?)
// 线 // 线
case spacer(height:CGFloat, color:UIColor?) case spacer(height: CGFloat, color: UIColor?)
} }

View File

@ -6,22 +6,21 @@
// Copyright © 2018 Fin. All rights reserved. // Copyright © 2018 Fin. All rights reserved.
// //
import UIKit
import Material import Material
import SnapKit
import SafariServices
import RxSwift
import RxCocoa import RxCocoa
import RxSwift
import SafariServices
import SnapKit
import UIKit
class NewServerViewController: BaseViewController { class NewServerViewController: BaseViewController {
let addressTextField: TextField = {
let addressTextField : TextField = {
let textField = TextField() let textField = TextField()
textField.keyboardType = .URL textField.keyboardType = .URL
textField.placeholder = NSLocalizedString("ServerAddress") textField.placeholder = NSLocalizedString("ServerAddress")
textField.detail = NSLocalizedString("ServerExample") textField.detail = NSLocalizedString("ServerExample")
textField.transition([ .scale(0.85) , .opacity(0)] ) textField.transition([.scale(0.85), .opacity(0)])
textField.detailLabel.transition([ .scale(0.85) , .opacity(0)] ) textField.detailLabel.transition([.scale(0.85), .opacity(0)])
return textField return textField
}() }()
@ -30,7 +29,7 @@ class NewServerViewController: BaseViewController {
label.text = NSLocalizedString("DeploymentDocuments") label.text = NSLocalizedString("DeploymentDocuments")
label.textColor = Color.blue.base label.textColor = Color.blue.base
label.font = UIFont.systemFont(ofSize: 12) label.font = UIFont.systemFont(ofSize: 12)
label.transition([ .scale(0.85) , .opacity(0), .translate(x: 50)] ) label.transition([.scale(0.85), .opacity(0), .translate(x: 50)])
label.isUserInteractionEnabled = true label.isUserInteractionEnabled = true
label.addGestureRecognizer(UITapGestureRecognizer()) label.addGestureRecognizer(UITapGestureRecognizer())
return label return label
@ -52,35 +51,34 @@ class NewServerViewController: BaseViewController {
.top(kNavigationHeight + 40).left(10).right(10) .top(kNavigationHeight + 40).left(10).right(10)
self.view.addSubview(noticeLabel) self.view.addSubview(noticeLabel)
noticeLabel.snp.makeConstraints { (make) in noticeLabel.snp.makeConstraints { make in
make.top.equalTo(self.addressTextField.snp.bottom).offset(40) make.top.equalTo(self.addressTextField.snp.bottom).offset(40)
make.left.equalTo(self.addressTextField) make.left.equalTo(self.addressTextField)
} }
} }
override func bindViewModel() { override func bindViewModel() {
guard let viewModel = self.viewModel as? NewServerViewModel else { guard let viewModel = self.viewModel as? NewServerViewModel else {
return return
} }
let noticeTap = noticeLabel.gestureRecognizers!.first!.rx let noticeTap = noticeLabel.gestureRecognizers!.first!.rx
.event .event
.map({ (_) -> () in .map { _ -> () in
return () ()
}) }
.asDriver(onErrorJustReturn: ()) .asDriver(onErrorJustReturn: ())
let done = doneButton.rx.tap let done = doneButton.rx.tap
.map({[weak self] in .map { [weak self] in
return self?.addressTextField.text ?? "" self?.addressTextField.text ?? ""
}) }
.asDriver(onErrorDriveWith: .empty()) .asDriver(onErrorDriveWith: .empty())
let viewDidAppear = rx let viewDidAppear = rx
.methodInvoked(#selector(viewDidAppear(_:))) .methodInvoked(#selector(viewDidAppear(_:)))
.map{ _ in () } .map { _ in () }
.asDriver(onErrorDriveWith: .empty()) .asDriver(onErrorDriveWith: .empty())
let output = viewModel.transform( let output = viewModel.transform(
input: NewServerViewModel.Input( input: NewServerViewModel.Input(
noticeClick: noticeTap, noticeClick: noticeTap,
@ -88,36 +86,34 @@ class NewServerViewController: BaseViewController {
viewDidAppear: viewDidAppear viewDidAppear: viewDidAppear
)) ))
// //
output.showKeyboard.drive(onNext: { [weak self] show in output.showKeyboard.drive(onNext: { [weak self] show in
if show { if show {
_ = self?.addressTextField.becomeFirstResponder() _ = self?.addressTextField.becomeFirstResponder()
} }
else{ else {
self?.addressTextField.resignFirstResponder() self?.addressTextField.resignFirstResponder()
} }
}).disposed(by: rx.disposeBag) }).disposed(by: rx.disposeBag)
// //
output.notice.drive(onNext: {[weak self] url in output.notice.drive(onNext: { [weak self] url in
self?.navigationController?.present(BarkSFSafariViewController(url: url), animated: true, completion: nil) self?.navigationController?.present(BarkSFSafariViewController(url: url), animated: true, completion: nil)
}).disposed(by: rx.disposeBag) }).disposed(by: rx.disposeBag)
//URL // URL
output.urlText output.urlText
.drive(self.addressTextField.rx.text) .drive(self.addressTextField.rx.text)
.disposed(by: rx.disposeBag) .disposed(by: rx.disposeBag)
//退 // 退
output.pop.drive(onNext: {[weak self] _ in output.pop.drive(onNext: { [weak self] _ in
self?.navigationController?.popViewController(animated: true) self?.navigationController?.popViewController(animated: true)
}).disposed(by: rx.disposeBag) }).disposed(by: rx.disposeBag)
// //
output.showSnackbar.drive(onNext: {[weak self] text in output.showSnackbar.drive(onNext: { [weak self] text in
self?.showSnackbar(text: text) self?.showSnackbar(text: text)
}).disposed(by: rx.disposeBag) }).disposed(by: rx.disposeBag)
} }
} }

View File

@ -7,14 +7,14 @@
// //
import Foundation import Foundation
import RxSwift
import RxCocoa
import SwiftyJSON
import Moya import Moya
import RxCocoa
import RxSwift
import SwiftyJSON
class NewServerViewModel: ViewModel, ViewModelType { class NewServerViewModel: ViewModel, ViewModelType {
struct Input { struct Input {
var noticeClick:Driver<Void> var noticeClick: Driver<Void>
var done: Driver<String> var done: Driver<String>
var viewDidAppear: Driver<Void> var viewDidAppear: Driver<Void>
} }
@ -27,23 +27,21 @@ class NewServerViewModel: ViewModel, ViewModelType {
var pop: Driver<String> var pop: Driver<String>
} }
private var url: String = "" private var url: String = ""
let pop = PublishRelay<String>() let pop = PublishRelay<String>()
func transform(input: Input) -> Output { func transform(input: Input) -> Output {
let showKeyboard = PublishRelay<Bool>() let showKeyboard = PublishRelay<Bool>()
let urlText = PublishRelay<String>() let urlText = PublishRelay<String>()
let showSnackbar = PublishRelay<String>() let showSnackbar = PublishRelay<String>()
let notice = input.noticeClick let notice = input.noticeClick
.map{ URL(string: "https://day.app/2018/06/bark-server-document/")! } .map { URL(string: "https://day.app/2018/06/bark-server-document/")! }
.asDriver() .asDriver()
input.viewDidAppear input.viewDidAppear
.map{ "https://" } .map { "https://" }
.asObservable() .asObservable()
.take(1) .take(1)
.subscribe(onNext: { text in .subscribe(onNext: { text in
@ -53,7 +51,7 @@ class NewServerViewModel: ViewModel, ViewModelType {
input.done input.done
.asObservable() .asObservable()
.flatMapLatest {[weak self] (url) -> Observable<Result<JSON,ApiError>> in .flatMapLatest { [weak self] url -> Observable<Result<JSON, ApiError>> in
showKeyboard.accept(false) showKeyboard.accept(false)
if let _ = URL(string: url) { if let _ = URL(string: url) {
guard let strongSelf = self else { return .empty() } guard let strongSelf = self else { return .empty() }
@ -67,7 +65,7 @@ class NewServerViewModel: ViewModel, ViewModelType {
return .empty() return .empty()
} }
} }
.subscribe(onNext: {[weak self] response in .subscribe(onNext: { [weak self] response in
guard let strongSelf = self else { return } guard let strongSelf = self else { return }
switch response { switch response {
case .success: case .success:

View File

@ -6,14 +6,14 @@
// Copyright © 2020 Fin. All rights reserved. // Copyright © 2020 Fin. All rights reserved.
// //
import UIKit
import Material
import AVKit import AVKit
import Material
import UIKit
import RxSwift import NSObject_Rx
import RxCocoa import RxCocoa
import RxDataSources import RxDataSources
import NSObject_Rx import RxSwift
class SoundsViewController: BaseViewController { class SoundsViewController: BaseViewController {
let tableView: UITableView = { let tableView: UITableView = {
@ -27,7 +27,7 @@ class SoundsViewController: BaseViewController {
self.title = NSLocalizedString("notificationSound") self.title = NSLocalizedString("notificationSound")
self.view.addSubview(self.tableView) self.view.addSubview(self.tableView)
self.tableView.snp.makeConstraints { (make) in self.tableView.snp.makeConstraints { make in
make.edges.equalToSuperview() make.edges.equalToSuperview()
} }
@ -40,6 +40,7 @@ class SoundsViewController: BaseViewController {
return header return header
}() }()
} }
override func bindViewModel() { override func bindViewModel() {
guard let viewModel = viewModel as? SoundsViewModel else { guard let viewModel = viewModel as? SoundsViewModel else {
return return
@ -50,7 +51,7 @@ class SoundsViewController: BaseViewController {
.modelSelected(SoundCellViewModel.self) .modelSelected(SoundCellViewModel.self)
.asDriver())) .asDriver()))
let dataSource = RxTableViewSectionedReloadDataSource<SectionModel<String,SoundCellViewModel>> { (source, tableView, indexPath, item) -> UITableViewCell in let dataSource = RxTableViewSectionedReloadDataSource<SectionModel<String, SoundCellViewModel>> { _, tableView, _, item -> UITableViewCell in
guard let cell = tableView.dequeueReusableCell(withIdentifier: "\(SoundCell.self)") as? SoundCell else { guard let cell = tableView.dequeueReusableCell(withIdentifier: "\(SoundCell.self)") as? SoundCell else {
return UITableViewCell() return UITableViewCell()
} }
@ -68,7 +69,7 @@ class SoundsViewController: BaseViewController {
}).disposed(by: rx.disposeBag) }).disposed(by: rx.disposeBag)
output.playAction.drive(onNext: { url in output.playAction.drive(onNext: { url in
var soundID:SystemSoundID = 0 var soundID: SystemSoundID = 0
AudioServicesCreateSystemSoundID(url, &soundID) AudioServicesCreateSystemSoundID(url, &soundID)
AudioServicesPlaySystemSoundWithCompletion(soundID) { AudioServicesPlaySystemSoundWithCompletion(soundID) {
AudioServicesDisposeSystemSoundID(soundID) AudioServicesDisposeSystemSoundID(soundID)

View File

@ -6,19 +6,19 @@
// Copyright © 2020 Fin. All rights reserved. // Copyright © 2020 Fin. All rights reserved.
// //
import Foundation
import RxSwift
import RxCocoa
import AVKit import AVKit
import Foundation
import RxCocoa
import RxDataSources import RxDataSources
import RxSwift
class SoundsViewModel:ViewModel,ViewModelType { class SoundsViewModel: ViewModel, ViewModelType {
struct Input { struct Input {
var soundSelected:Driver<SoundCellViewModel> var soundSelected: Driver<SoundCellViewModel>
} }
struct Output { struct Output {
var audios:Observable<[ SectionModel<String, SoundCellViewModel>]> var audios: Observable<[SectionModel<String, SoundCellViewModel>]>
var copyNameAction: Driver<String> var copyNameAction: Driver<String>
var playAction: Driver<CFURL> var playAction: Driver<CFURL>
} }
@ -26,15 +26,15 @@ class SoundsViewModel:ViewModel,ViewModelType {
func transform(input: Input) -> Output { func transform(input: Input) -> Output {
let models = { () -> [AVURLAsset] in let models = { () -> [AVURLAsset] in
var urls = Bundle.main.urls(forResourcesWithExtension: "caf", subdirectory: nil) ?? [] var urls = Bundle.main.urls(forResourcesWithExtension: "caf", subdirectory: nil) ?? []
urls.sort { (u1, u2) -> Bool in urls.sort { u1, u2 -> Bool in
u1.lastPathComponent.localizedStandardCompare(u2.lastPathComponent) == ComparisonResult.orderedAscending u1.lastPathComponent.localizedStandardCompare(u2.lastPathComponent) == ComparisonResult.orderedAscending
} }
let audios = urls.map { (url) -> AVURLAsset in let audios = urls.map { url -> AVURLAsset in
let asset = AVURLAsset(url: url) let asset = AVURLAsset(url: url)
return asset return asset
} }
return audios return audios
}().map { SoundCellViewModel(model: $0 ) } }().map { SoundCellViewModel(model: $0) }
let copyAction = Driver.merge( let copyAction = Driver.merge(
models.map { $0.copyNameAction.asDriver(onErrorDriveWith: .empty()) } models.map { $0.copyNameAction.asDriver(onErrorDriveWith: .empty()) }
@ -43,8 +43,7 @@ class SoundsViewModel:ViewModel,ViewModelType {
return Output( return Output(
audios: Observable.just([SectionModel(model: "model", items: models)]), audios: Observable.just([SectionModel(model: "model", items: models)]),
copyNameAction: copyAction, copyNameAction: copyAction,
playAction: input.soundSelected.map{ $0.model.url as CFURL } playAction: input.soundSelected.map { $0.model.url as CFURL }
) )
} }
} }

View File

@ -18,7 +18,6 @@ import Intents
// "Search for messages in <myApp>" // "Search for messages in <myApp>"
class IntentHandler: INExtension, INSendMessageIntentHandling, INSearchForMessagesIntentHandling, INSetMessageAttributeIntentHandling { class IntentHandler: INExtension, INSendMessageIntentHandling, INSearchForMessagesIntentHandling, INSetMessageAttributeIntentHandling {
override func handler(for intent: INIntent) -> Any { override func handler(for intent: INIntent) -> Any {
// This is the default implementation. If you want different objects to handle different intents, // This is the default implementation. If you want different objects to handle different intents,
// you can override this and return the handler you want for that particular intent. // you can override this and return the handler you want for that particular intent.
@ -31,7 +30,6 @@ class IntentHandler: INExtension, INSendMessageIntentHandling, INSearchForMessag
// Implement resolution methods to provide additional information about your intent (optional). // Implement resolution methods to provide additional information about your intent (optional).
func resolveRecipients(for intent: INSendMessageIntent, with completion: @escaping ([INSendMessageRecipientResolutionResult]) -> Void) { func resolveRecipients(for intent: INSendMessageIntent, with completion: @escaping ([INSendMessageRecipientResolutionResult]) -> Void) {
if let recipients = intent.recipients { if let recipients = intent.recipients {
// If no recipients were provided we'll need to prompt for a value. // If no recipients were provided we'll need to prompt for a value.
if recipients.count == 0 { if recipients.count == 0 {
completion([INSendMessageRecipientResolutionResult.needsValue()]) completion([INSendMessageRecipientResolutionResult.needsValue()])
@ -56,7 +54,6 @@ class IntentHandler: INExtension, INSendMessageIntentHandling, INSearchForMessag
default: default:
break break
} }
} }
completion(resolutionResults) completion(resolutionResults)

View File

@ -6,16 +6,16 @@
// Copyright © 2020 Fin. All rights reserved. // Copyright © 2020 Fin. All rights reserved.
// //
import UIKit
import RealmSwift
import IceCream import IceCream
import RealmSwift
import UIKit
class Message: Object { class Message: Object {
@objc dynamic var id = NSUUID().uuidString @objc dynamic var id = NSUUID().uuidString
@objc dynamic var title:String? @objc dynamic var title: String?
@objc dynamic var body:String? @objc dynamic var body: String?
@objc dynamic var url:String? @objc dynamic var url: String?
@objc dynamic var group:String? @objc dynamic var group: String?
@objc dynamic var createDate:Date? @objc dynamic var createDate: Date?
// true IceCream // true IceCream
@objc dynamic var isDeleted = false @objc dynamic var isDeleted = false
@ -23,8 +23,9 @@ class Message: Object {
override class func primaryKey() -> String? { override class func primaryKey() -> String? {
return "id" return "id"
} }
override class func indexedProperties() -> [String] { override class func indexedProperties() -> [String] {
return ["group","createDate"] return ["group", "createDate"]
} }
} }

View File

@ -10,24 +10,24 @@ import Foundation
import UIKit import UIKit
class PreviewModel: NSObject { class PreviewModel: NSObject {
var title:String? var title: String?
var body:String? var body: String?
var category:String? var category: String?
var notice:String? var notice: String?
var queryParameter:String? var queryParameter: String?
var image:UIImage? var image: UIImage?
var moreInfo:String? var moreInfo: String?
var moreViewModel:ViewModel? var moreViewModel: ViewModel?
init(title:String? = nil, init(title: String? = nil,
body:String? = nil, body: String? = nil,
category:String? = nil, category: String? = nil,
notice:String? = nil, notice: String? = nil,
queryParameter:String? = nil, queryParameter: String? = nil,
image:UIImage? = nil, image: UIImage? = nil,
moreInfo:String? = nil, moreInfo: String? = nil,
moreViewModel:ViewModel? = nil moreViewModel: ViewModel? = nil)
) { {
self.title = title self.title = title
self.body = body self.body = body
self.category = category self.category = category

View File

@ -6,75 +6,74 @@
// Copyright © 2018 Fin. All rights reserved. // Copyright © 2018 Fin. All rights reserved.
// //
import UIKit import Intents
import UserNotifications
import RealmSwift
import Kingfisher import Kingfisher
import MobileCoreServices import MobileCoreServices
import Intents import RealmSwift
import UIKit
import UserNotifications
class NotificationService: UNNotificationServiceExtension { class NotificationService: UNNotificationServiceExtension {
var contentHandler: ((UNNotificationContent) -> ())?
var contentHandler: ((UNNotificationContent) -> Void)? lazy var realm: Realm? = {
lazy var realm:Realm? = {
let groupUrl = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.bark") let groupUrl = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.bark")
let fileUrl = groupUrl?.appendingPathComponent("bark.realm") let fileUrl = groupUrl?.appendingPathComponent("bark.realm")
let config = Realm.Configuration( let config = Realm.Configuration(
fileURL: fileUrl, fileURL: fileUrl,
schemaVersion: 13, schemaVersion: 13,
migrationBlock: { migration, oldSchemaVersion in migrationBlock: { _, oldSchemaVersion in
// We havent migrated anything yet, so oldSchemaVersion == 0 // We havent migrated anything yet, so oldSchemaVersion == 0
if (oldSchemaVersion < 1) { if oldSchemaVersion < 1 {
// Nothing to do! // Nothing to do!
// Realm will automatically detect new properties and removed properties // Realm will automatically detect new properties and removed properties
// And will update the schema on disk automatically // And will update the schema on disk automatically
} }
}) }
)
// Tell Realm to use this new configuration object for the default Realm // Tell Realm to use this new configuration object for the default Realm
Realm.Configuration.defaultConfiguration = config Realm.Configuration.defaultConfiguration = config
return try? Realm() return try? Realm()
}() }()
/// ///
/// - Parameters: /// - Parameters:
/// - userInfo: /// - userInfo:
/// - bestAttemptContentBody: body`` `` /// - bestAttemptContentBody: body`` ``
fileprivate func autoCopy(_ userInfo: [AnyHashable : Any], defaultCopy: String) { fileprivate func autoCopy(_ userInfo: [AnyHashable: Any], defaultCopy: String) {
if userInfo["autocopy"] as? String == "1" if userInfo["autocopy"] as? String == "1"
|| userInfo["automaticallycopy"] as? String == "1"{ || userInfo["automaticallycopy"] as? String == "1"
{
if let copy = userInfo["copy"] as? String { if let copy = userInfo["copy"] as? String {
UIPasteboard.general.string = copy UIPasteboard.general.string = copy
} }
else{ else {
UIPasteboard.general.string = defaultCopy UIPasteboard.general.string = defaultCopy
} }
} }
} }
/// ///
/// - Parameter userInfo: /// - Parameter userInfo:
/// `isarchive` `isarchive` /// `isarchive` `isarchive`
/// `` /// ``
fileprivate func archive(_ userInfo: [AnyHashable : Any]) { fileprivate func archive(_ userInfo: [AnyHashable: Any]) {
var isArchive:Bool? var isArchive: Bool?
if let archive = userInfo["isarchive"] as? String{ if let archive = userInfo["isarchive"] as? String {
isArchive = archive == "1" ? true : false isArchive = archive == "1" ? true : false
} }
if isArchive == nil { if isArchive == nil {
isArchive = ArchiveSettingManager.shared.isArchive isArchive = ArchiveSettingManager.shared.isArchive
} }
let alert = (userInfo["aps"] as? [String:Any])?["alert"] as? [String:Any] let alert = (userInfo["aps"] as? [String: Any])?["alert"] as? [String: Any]
let title = alert?["title"] as? String let title = alert?["title"] as? String
let body = alert?["body"] as? String let body = alert?["body"] as? String
let url = userInfo["url"] as? String let url = userInfo["url"] as? String
let group = userInfo["group"] as? String let group = userInfo["group"] as? String
if (isArchive == true){ if isArchive == true {
try? realm?.write{ try? realm?.write {
let message = Message() let message = Message()
message.title = title message.title = title
message.body = body message.body = body
@ -86,22 +85,21 @@ class NotificationService: UNNotificationServiceExtension {
} }
} }
/// ///
/// - Parameters: /// - Parameters:
/// - userInfo: /// - userInfo:
/// - bestAttemptContent: content /// - bestAttemptContent: content
/// - complection: /// - complection:
fileprivate func downloadImage(_ imageUrl:String, complection: @escaping (_ imageFileUrl:String?) -> () ) { fileprivate func downloadImage(_ imageUrl: String, complection: @escaping (_ imageFileUrl: String?) -> ()) {
guard let groupUrl = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.bark"), guard let groupUrl = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.bark"),
let cache = try? ImageCache(name: "shared",cacheDirectoryURL: groupUrl), let cache = try? ImageCache(name: "shared", cacheDirectoryURL: groupUrl),
let imageResource = URL(string: imageUrl) let imageResource = URL(string: imageUrl)
else { else {
complection(nil) complection(nil)
return return
} }
func downloadFinished(){ func downloadFinished() {
let cacheFileUrl = cache.cachePath(forKey: imageResource.cacheKey) let cacheFileUrl = cache.cachePath(forKey: imageResource.cacheKey)
complection(cacheFileUrl) complection(cacheFileUrl)
} }
@ -119,7 +117,7 @@ class NotificationService: UNNotificationServiceExtension {
return return
} }
// //
cache.storeToDisk(result.originalData, forKey: imageResource.cacheKey, expiration: StorageExpiration.never) { r in cache.storeToDisk(result.originalData, forKey: imageResource.cacheKey, expiration: StorageExpiration.never) { _ in
downloadFinished() downloadFinished()
} }
} }
@ -130,14 +128,15 @@ class NotificationService: UNNotificationServiceExtension {
/// - bestAttemptContent: content /// - bestAttemptContent: content
/// - complection: /// - complection:
fileprivate func setImage(content bestAttemptContent: UNMutableNotificationContent, fileprivate func setImage(content bestAttemptContent: UNMutableNotificationContent,
complection: @escaping (_ content:UNMutableNotificationContent) -> () ) { complection: @escaping (_ content: UNMutableNotificationContent) -> ())
{
let userInfo = bestAttemptContent.userInfo let userInfo = bestAttemptContent.userInfo
guard let imageUrl = userInfo["image"] as? String else { guard let imageUrl = userInfo["image"] as? String else {
complection(bestAttemptContent) complection(bestAttemptContent)
return return
} }
func finished(_ imageFileUrl: String?){ func finished(_ imageFileUrl: String?) {
guard let imageFileUrl = imageFileUrl else { guard let imageFileUrl = imageFileUrl else {
complection(bestAttemptContent) complection(bestAttemptContent)
return return
@ -146,13 +145,15 @@ class NotificationService: UNNotificationServiceExtension {
// 使 // 使
try? FileManager.default.copyItem( try? FileManager.default.copyItem(
at: URL(fileURLWithPath: imageFileUrl), at: URL(fileURLWithPath: imageFileUrl),
to: copyDestUrl) to: copyDestUrl
)
if let attachment = try? UNNotificationAttachment( if let attachment = try? UNNotificationAttachment(
identifier: "image", identifier: "image",
url: copyDestUrl, url: copyDestUrl,
options: [UNNotificationAttachmentOptionsTypeHintKey : kUTTypePNG]){ options: [UNNotificationAttachmentOptionsTypeHintKey: kUTTypePNG]
bestAttemptContent.attachments = [ attachment ] ) {
bestAttemptContent.attachments = [attachment]
} }
complection(bestAttemptContent) complection(bestAttemptContent)
} }
@ -165,7 +166,8 @@ class NotificationService: UNNotificationServiceExtension {
/// - bestAttemptContent: content /// - bestAttemptContent: content
/// - complection: /// - complection:
fileprivate func setIcon(content bestAttemptContent: UNMutableNotificationContent, fileprivate func setIcon(content bestAttemptContent: UNMutableNotificationContent,
complection: @escaping (_ content:UNMutableNotificationContent) -> () ) { complection: @escaping (_ content: UNMutableNotificationContent) -> ())
{
if #available(iOSApplicationExtension 15.0, *) { if #available(iOSApplicationExtension 15.0, *) {
let userInfo = bestAttemptContent.userInfo let userInfo = bestAttemptContent.userInfo
guard let imageUrl = userInfo["icon"] as? String else { guard let imageUrl = userInfo["icon"] as? String else {
@ -173,7 +175,7 @@ class NotificationService: UNNotificationServiceExtension {
return return
} }
func finished(_ imageFileUrl: String?){ func finished(_ imageFileUrl: String?) {
guard let imageFileUrl = imageFileUrl else { guard let imageFileUrl = imageFileUrl else {
complection(bestAttemptContent) complection(bestAttemptContent)
return return
@ -224,7 +226,8 @@ class NotificationService: UNNotificationServiceExtension {
do { do {
let content = try bestAttemptContent.updating(from: intent) as! UNMutableNotificationContent let content = try bestAttemptContent.updating(from: intent) as! UNMutableNotificationContent
complection(content) complection(content)
} catch { }
catch {
// Handle error // Handle error
} }
@ -233,13 +236,12 @@ class NotificationService: UNNotificationServiceExtension {
downloadImage(imageUrl, complection: finished) downloadImage(imageUrl, complection: finished)
} }
else{ else {
complection(bestAttemptContent) complection(bestAttemptContent)
} }
} }
override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) { override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> ()) {
self.contentHandler = contentHandler self.contentHandler = contentHandler
guard let bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent) else { guard let bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent) else {
contentHandler(request.content) contentHandler(request.content)

View File

@ -13,6 +13,7 @@ class ArchiveSettingCell: BaseTableViewCell {
let btn = UISwitch() let btn = UISwitch()
return btn return btn
}() }()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier) super.init(style: style, reuseIdentifier: reuseIdentifier)
self.selectionStyle = .none self.selectionStyle = .none
@ -20,11 +21,13 @@ class ArchiveSettingCell: BaseTableViewCell {
self.textLabel?.text = NSLocalizedString("defaultArchiveSettings") self.textLabel?.text = NSLocalizedString("defaultArchiveSettings")
contentView.addSubview(switchButton) contentView.addSubview(switchButton)
switchButton.snp.makeConstraints { (make) in switchButton.snp.makeConstraints { make in
make.right.equalToSuperview().offset(-16) make.right.equalToSuperview().offset(-16)
make.centerY.equalToSuperview() make.centerY.equalToSuperview()
} }
} }
@available(*, unavailable)
required init?(coder aDecoder: NSCoder) { required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented") fatalError("init(coder:) has not been implemented")
} }
@ -38,4 +41,3 @@ class ArchiveSettingCell: BaseTableViewCell {
.disposed(by: rx.reuseBag) .disposed(by: rx.reuseBag)
} }
} }

View File

@ -10,7 +10,7 @@ import Foundation
import RxCocoa import RxCocoa
class ArchiveSettingCellViewModel: ViewModel { class ArchiveSettingCellViewModel: ViewModel {
var on: BehaviorRelay<Bool> var on: BehaviorRelay<Bool>
init(on:Bool) { init(on: Bool) {
self.on = BehaviorRelay<Bool>(value: on) self.on = BehaviorRelay<Bool>(value: on)
super.init() super.init()
} }

View File

@ -8,21 +8,21 @@
import UIKit import UIKit
protocol AlignmentRectInsetsOverridable:AnyObject { protocol AlignmentRectInsetsOverridable: AnyObject {
var alignmentRectInsetsOverride: UIEdgeInsets? {get set} var alignmentRectInsetsOverride: UIEdgeInsets? { get set }
}
protocol HitTestSlopable:AnyObject {
var hitTestSlop: UIEdgeInsets {get set}
} }
class BKButton: UIButton, HitTestSlopable,AlignmentRectInsetsOverridable { protocol HitTestSlopable: AnyObject {
var hitTestSlop: UIEdgeInsets { get set }
}
var hitTestSlop:UIEdgeInsets = UIEdgeInsets.zero class BKButton: UIButton, HitTestSlopable, AlignmentRectInsetsOverridable {
var hitTestSlop = UIEdgeInsets.zero
override func point(inside point: CGPoint, with event: UIEvent?) -> Bool { override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
if hitTestSlop == UIEdgeInsets.zero { if hitTestSlop == UIEdgeInsets.zero {
return super.point(inside: point, with:event) return super.point(inside: point, with: event)
} }
else{ else {
return self.bounds.inset(by: hitTestSlop).contains(point) return self.bounds.inset(by: hitTestSlop).contains(point)
} }
} }

View File

@ -9,16 +9,14 @@
import UIKit import UIKit
class BKLabel: UILabel { class BKLabel: UILabel {
var hitTestSlop = UIEdgeInsets.zero
var hitTestSlop:UIEdgeInsets = UIEdgeInsets.zero
override func point(inside point: CGPoint, with event: UIEvent?) -> Bool { override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
if hitTestSlop == UIEdgeInsets.zero { if hitTestSlop == UIEdgeInsets.zero {
return super.point(inside: point, with:event) return super.point(inside: point, with: event)
} }
else{ else {
return self.bounds.inset(by: hitTestSlop).contains(point) return self.bounds.inset(by: hitTestSlop).contains(point)
} }
} }
} }

View File

@ -9,8 +9,8 @@
import UIKit import UIKit
class BaseTableViewCell: UITableViewCell { class BaseTableViewCell: UITableViewCell {
var viewModel:ViewModel? var viewModel: ViewModel?
func bindViewModel(model:ViewModel){ func bindViewModel(model: ViewModel) {
self.viewModel = model self.viewModel = model
} }
} }

View File

@ -6,16 +6,16 @@
// Copyright © 2021 Fin. All rights reserved. // Copyright © 2021 Fin. All rights reserved.
// //
import UIKit
import RxSwift
import RxCocoa import RxCocoa
import RxDataSources import RxDataSources
import RxSwift
import UIKit
class GroupCellViewModel:ViewModel { class GroupCellViewModel: ViewModel {
let name = BehaviorRelay<String?>(value: nil) let name = BehaviorRelay<String?>(value: nil)
let checked = BehaviorRelay<Bool>(value: false) let checked = BehaviorRelay<Bool>(value: false)
init(groupFilterModel:GroupFilterModel) { init(groupFilterModel: GroupFilterModel) {
self.name.accept(groupFilterModel.name) self.name.accept(groupFilterModel.name)
self.checked.accept(groupFilterModel.checked) self.checked.accept(groupFilterModel.checked)
} }

View File

@ -6,16 +6,17 @@
// Copyright © 2021 Fin. All rights reserved. // Copyright © 2021 Fin. All rights reserved.
// //
import UIKit
import Material import Material
import UIKit
class GroupTableViewCell: BaseTableViewCell { class GroupTableViewCell: BaseTableViewCell {
let nameLabel:UILabel = { let nameLabel: UILabel = {
let label = UILabel() let label = UILabel()
label.fontSize = 14 label.fontSize = 14
label.textColor = Color.darkText.primary label.textColor = Color.darkText.primary
return label return label
}() }()
let checkButton: BKButton = { let checkButton: BKButton = {
let btn = BKButton() let btn = BKButton()
btn.setImage(UIImage(named: "baseline_radio_button_unchecked_black_24pt"), for: .normal) btn.setImage(UIImage(named: "baseline_radio_button_unchecked_black_24pt"), for: .normal)
@ -37,31 +38,33 @@ class GroupTableViewCell: BaseTableViewCell {
make.left.equalToSuperview().offset(15) make.left.equalToSuperview().offset(15)
make.centerY.equalToSuperview() make.centerY.equalToSuperview()
} }
nameLabel.snp.makeConstraints { (make) in nameLabel.snp.makeConstraints { make in
make.left.equalTo(checkButton.snp.right).offset(15) make.left.equalTo(checkButton.snp.right).offset(15)
make.top.equalToSuperview().offset(15) make.top.equalToSuperview().offset(15)
make.bottom.equalToSuperview().offset(-15) make.bottom.equalToSuperview().offset(-15)
} }
let tap = UITapGestureRecognizer() let tap = UITapGestureRecognizer()
self.contentView.addGestureRecognizer(tap) self.contentView.addGestureRecognizer(tap)
tap.rx.event.subscribe(onNext: {[weak self] _ in tap.rx.event.subscribe(onNext: { [weak self] _ in
(self?.viewModel as? GroupCellViewModel)?.checked.accept(!self!.checkButton.isSelected) (self?.viewModel as? GroupCellViewModel)?.checked.accept(!self!.checkButton.isSelected)
}).disposed(by: rx.disposeBag) }).disposed(by: rx.disposeBag)
} }
@available(*, unavailable)
required init?(coder aDecoder: NSCoder) { required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented") fatalError("init(coder:) has not been implemented")
} }
override func bindViewModel(model:ViewModel){ override func bindViewModel(model: ViewModel) {
super.bindViewModel(model: model) super.bindViewModel(model: model)
guard let viewModel = model as? GroupCellViewModel else { guard let viewModel = model as? GroupCellViewModel else {
return return
} }
viewModel.name viewModel.name
.map({ name in .map { name in
return name ?? NSLocalizedString("default") name ?? NSLocalizedString("default")
}) }
.bind(to: nameLabel.rx.text) .bind(to: nameLabel.rx.text)
.disposed(by: rx.reuseBag) .disposed(by: rx.reuseBag)
@ -70,10 +73,8 @@ class GroupTableViewCell: BaseTableViewCell {
.disposed(by: rx.reuseBag) .disposed(by: rx.reuseBag)
viewModel.checked.subscribe( viewModel.checked.subscribe(
onNext: {[weak self] checked in onNext: { [weak self] checked in
self?.checkButton.tintColor = checked ? Color.lightBlue.darken3 : Color.lightGray self?.checkButton.tintColor = checked ? Color.lightBlue.darken3 : Color.lightGray
}).disposed(by: rx.reuseBag) }).disposed(by: rx.reuseBag)
} }
} }

View File

@ -6,8 +6,8 @@
// Copyright © 2020 Fin. All rights reserved. // Copyright © 2020 Fin. All rights reserved.
// //
import UIKit
import Material import Material
import UIKit
class LabelCell: UITableViewCell { class LabelCell: UITableViewCell {
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
@ -19,6 +19,8 @@ class LabelCell: UITableViewCell {
self.textLabel?.fontSize = 12 self.textLabel?.fontSize = 12
self.textLabel?.numberOfLines = 0 self.textLabel?.numberOfLines = 0
} }
@available(*, unavailable)
required init?(coder aDecoder: NSCoder) { required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented") fatalError("init(coder:) has not been implemented")
} }

View File

@ -6,10 +6,9 @@
// Copyright © 2020 Fin. All rights reserved. // Copyright © 2020 Fin. All rights reserved.
// //
import UIKit
import Material import Material
import UIKit
class MessageTableViewCell: BaseTableViewCell { class MessageTableViewCell: BaseTableViewCell {
let backgroundPanel: UIView = { let backgroundPanel: UIView = {
let view = UIView() let view = UIView()
view.layer.cornerRadius = 3 view.layer.cornerRadius = 3
@ -25,6 +24,7 @@ class MessageTableViewCell: BaseTableViewCell {
label.numberOfLines = 0 label.numberOfLines = 0
return label return label
}() }()
let bodyLabel: UILabel = { let bodyLabel: UILabel = {
let label = UILabel() let label = UILabel()
label.font = RobotoFont.regular(with: 14) label.font = RobotoFont.regular(with: 14)
@ -49,12 +49,14 @@ class MessageTableViewCell: BaseTableViewCell {
label.textColor = Color.darkText.others label.textColor = Color.darkText.others
return label return label
}() }()
let bodyStackView: UIStackView = { let bodyStackView: UIStackView = {
let stackView = UIStackView() let stackView = UIStackView()
stackView.axis = .vertical stackView.axis = .vertical
return stackView return stackView
}() }()
let separatorLine:UIImageView = {
let separatorLine: UIImageView = {
let imageView = UIImageView() let imageView = UIImageView()
imageView.backgroundColor = Color.grey.lighten5 imageView.backgroundColor = Color.grey.lighten5
return imageView return imageView
@ -80,30 +82,32 @@ class MessageTableViewCell: BaseTableViewCell {
layoutView() layoutView()
} }
@available(*, unavailable)
required init?(coder: NSCoder) { required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented") fatalError("init(coder:) has not been implemented")
} }
func layoutView(){ func layoutView() {
bodyStackView.snp.makeConstraints { (make) in bodyStackView.snp.makeConstraints { make in
make.left.top.equalToSuperview().offset(16) make.left.top.equalToSuperview().offset(16)
make.right.equalToSuperview().offset(-16) make.right.equalToSuperview().offset(-16)
} }
titleLabel.snp.remakeConstraints { (make) in titleLabel.snp.remakeConstraints { make in
make.left.equalTo(12) make.left.equalTo(12)
make.right.equalTo(-12) make.right.equalTo(-12)
} }
bodyLabel.snp.remakeConstraints { (make) in bodyLabel.snp.remakeConstraints { make in
make.left.right.equalTo(titleLabel) make.left.right.equalTo(titleLabel)
} }
urlLabel.snp.makeConstraints { (make) in urlLabel.snp.makeConstraints { make in
make.left.right.equalTo(bodyLabel) make.left.right.equalTo(bodyLabel)
} }
dateLabel.snp.remakeConstraints { (make) in dateLabel.snp.remakeConstraints { make in
make.left.equalTo(bodyLabel) make.left.equalTo(bodyLabel)
make.top.equalTo(bodyStackView.snp.bottom).offset(12) make.top.equalTo(bodyStackView.snp.bottom).offset(12)
} }
separatorLine.snp.remakeConstraints { (make) in separatorLine.snp.remakeConstraints { make in
make.left.right.bottom.equalToSuperview() make.left.right.bottom.equalToSuperview()
make.top.equalTo(dateLabel.snp.bottom).offset(12) make.top.equalTo(dateLabel.snp.bottom).offset(12)
make.height.equalTo(10) make.height.equalTo(10)
@ -127,12 +131,11 @@ class MessageTableViewCell: BaseTableViewCell {
viewModel.url.bind(to: self.urlLabel.rx.text).disposed(by: rx.reuseBag) viewModel.url.bind(to: self.urlLabel.rx.text).disposed(by: rx.reuseBag)
viewModel.date.bind(to: self.dateLabel.rx.text).disposed(by: rx.reuseBag) viewModel.date.bind(to: self.dateLabel.rx.text).disposed(by: rx.reuseBag)
viewModel.title.map{ $0.count <= 0}.bind(to: self.titleLabel.rx.isHidden).disposed(by: rx.reuseBag) viewModel.title.map { $0.count <= 0 }.bind(to: self.titleLabel.rx.isHidden).disposed(by: rx.reuseBag)
viewModel.url.map{ $0.count <= 0}.bind(to: self.urlLabel.rx.isHidden).disposed(by: rx.reuseBag) viewModel.url.map { $0.count <= 0 }.bind(to: self.urlLabel.rx.isHidden).disposed(by: rx.reuseBag)
self.urlLabel.gestureRecognizers?.first?.rx.event self.urlLabel.gestureRecognizers?.first?.rx.event
.map{[weak self] _ in self?.urlLabel.text ?? "" } .map { [weak self] _ in self?.urlLabel.text ?? "" }
.bind(to: viewModel.urlTap).disposed(by: rx.reuseBag) .bind(to: viewModel.urlTap).disposed(by: rx.reuseBag)
} }
} }

View File

@ -6,12 +6,11 @@
// Copyright © 2020 Fin. All rights reserved. // Copyright © 2020 Fin. All rights reserved.
// //
import Differentiator
import Foundation import Foundation
import RxCocoa import RxCocoa
import Differentiator
import RxDataSources import RxDataSources
class MessageTableViewCellViewModel: ViewModel { class MessageTableViewCellViewModel: ViewModel {
let message: Message let message: Message
@ -22,7 +21,7 @@ class MessageTableViewCellViewModel: ViewModel {
let urlTap: PublishRelay<String> let urlTap: PublishRelay<String>
init(message:Message) { init(message: Message) {
self.message = message self.message = message
self.title = BehaviorRelay<String>(value: message.title ?? "") self.title = BehaviorRelay<String>(value: message.title ?? "")
@ -35,13 +34,12 @@ class MessageTableViewCellViewModel: ViewModel {
} }
} }
struct MessageSection { struct MessageSection {
var header: String var header: String
var messages:[MessageTableViewCellViewModel] var messages: [MessageTableViewCellViewModel]
} }
extension MessageSection:AnimatableSectionModelType { extension MessageSection: AnimatableSectionModelType {
typealias Item = MessageTableViewCellViewModel typealias Item = MessageTableViewCellViewModel
typealias Identity = String typealias Identity = String
@ -62,7 +60,7 @@ extension MessageSection:AnimatableSectionModelType {
extension MessageTableViewCellViewModel: IdentifiableType { extension MessageTableViewCellViewModel: IdentifiableType {
typealias Identity = String typealias Identity = String
var identity: String{ var identity: String {
return "\(self.message.id)" return "\(self.message.id)"
} }

View File

@ -6,11 +6,10 @@
// Copyright © 2018 Fin. All rights reserved. // Copyright © 2018 Fin. All rights reserved.
// //
import UIKit
import Material import Material
import UIKit
class PreviewCardCell: BaseTableViewCell { class PreviewCardCell: BaseTableViewCell {
let previewButton = IconButton(image: Icon.cm.skipForward, tintColor: Color.grey.base) let previewButton = IconButton(image: Icon.cm.skipForward, tintColor: Color.grey.base)
let copyButton = IconButton(image: UIImage(named: "baseline_file_copy_white_24pt"), tintColor: Color.grey.base) let copyButton = IconButton(image: UIImage(named: "baseline_file_copy_white_24pt"), tintColor: Color.grey.base)
@ -21,6 +20,7 @@ class PreviewCardCell: BaseTableViewCell {
label.numberOfLines = 0 label.numberOfLines = 0
return label return label
}() }()
let bodyLabel: UILabel = { let bodyLabel: UILabel = {
let label = UILabel() let label = UILabel()
label.font = RobotoFont.regular(with: 14) label.font = RobotoFont.regular(with: 14)
@ -37,12 +37,14 @@ class PreviewCardCell: BaseTableViewCell {
label.isUserInteractionEnabled = true label.isUserInteractionEnabled = true
return label return label
}() }()
let contentImageView:UIImageView = {
let contentImageView: UIImageView = {
let imageView = UIImageView() let imageView = UIImageView()
imageView.contentMode = .scaleAspectFit imageView.contentMode = .scaleAspectFit
return imageView return imageView
}() }()
let card:UIView = {
let card: UIView = {
let view = UIView() let view = UIView()
view.backgroundColor = Color.white view.backgroundColor = Color.white
view.layer.cornerRadius = 2 view.layer.cornerRadius = 2
@ -60,6 +62,7 @@ class PreviewCardCell: BaseTableViewCell {
return label return label
}() }()
@available(*, unavailable)
required init?(coder aDecoder: NSCoder) { required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented") fatalError("init(coder:) has not been implemented")
} }
@ -73,23 +76,22 @@ class PreviewCardCell: BaseTableViewCell {
card.addSubview(copyButton) card.addSubview(copyButton)
card.addSubview(previewButton) card.addSubview(previewButton)
card.snp.makeConstraints { (make) in card.snp.makeConstraints { make in
make.left.top.equalToSuperview().offset(16) make.left.top.equalToSuperview().offset(16)
make.right.equalToSuperview().offset(-16) make.right.equalToSuperview().offset(-16)
make.bottom.equalToSuperview() make.bottom.equalToSuperview()
} }
previewButton.snp.makeConstraints { (make) in previewButton.snp.makeConstraints { make in
make.right.equalToSuperview().offset(-10) make.right.equalToSuperview().offset(-10)
make.centerY.equalTo(card.snp.top).offset(40) make.centerY.equalTo(card.snp.top).offset(40)
make.width.height.equalTo(40) make.width.height.equalTo(40)
} }
copyButton.snp.makeConstraints { (make) in copyButton.snp.makeConstraints { make in
make.right.equalTo(previewButton.snp.left).offset(-10) make.right.equalTo(previewButton.snp.left).offset(-10)
make.centerY.equalTo(previewButton) make.centerY.equalTo(previewButton)
make.width.height.equalTo(40) make.width.height.equalTo(40)
} }
let titleStackView = UIStackView() let titleStackView = UIStackView()
titleStackView.axis = .vertical titleStackView.axis = .vertical
titleStackView.addArrangedSubview(titleLabel) titleStackView.addArrangedSubview(titleLabel)
@ -97,20 +99,19 @@ class PreviewCardCell: BaseTableViewCell {
card.addSubview(titleStackView) card.addSubview(titleStackView)
titleLabel.snp.makeConstraints { (make) in titleLabel.snp.makeConstraints { make in
make.left.equalToSuperview().offset(15) make.left.equalToSuperview().offset(15)
} }
bodyLabel.snp.makeConstraints { (make) in bodyLabel.snp.makeConstraints { make in
make.left.equalToSuperview().offset(15) make.left.equalToSuperview().offset(15)
} }
titleStackView.snp.makeConstraints { (make) in titleStackView.snp.makeConstraints { make in
make.centerY.equalTo(copyButton) make.centerY.equalTo(copyButton)
make.left.equalToSuperview() make.left.equalToSuperview()
make.right.equalTo(copyButton.snp.left) make.right.equalTo(copyButton.snp.left)
} }
let contentStackView = UIStackView() let contentStackView = UIStackView()
contentStackView.axis = .vertical contentStackView.axis = .vertical
contentStackView.spacing = 20 contentStackView.spacing = 20
@ -121,18 +122,18 @@ class PreviewCardCell: BaseTableViewCell {
contentStackView.addArrangedSubview(contentLabel) contentStackView.addArrangedSubview(contentLabel)
contentStackView.addArrangedSubview(noticeLabel) contentStackView.addArrangedSubview(noticeLabel)
contentLabel.snp.makeConstraints { (make) in contentLabel.snp.makeConstraints { make in
make.left.equalToSuperview().offset(12) make.left.equalToSuperview().offset(12)
make.right.equalToSuperview().offset(-12) make.right.equalToSuperview().offset(-12)
} }
contentImageView.snp.remakeConstraints { (make) in contentImageView.snp.remakeConstraints { make in
make.left.right.equalToSuperview() make.left.right.equalToSuperview()
} }
noticeLabel.snp.makeConstraints { (make) in noticeLabel.snp.makeConstraints { make in
make.left.equalTo(10) make.left.equalTo(10)
make.right.equalTo(-10) make.right.equalTo(-10)
} }
contentStackView.snp.makeConstraints { (make) in contentStackView.snp.makeConstraints { make in
make.left.right.equalToSuperview() make.left.right.equalToSuperview()
make.top.equalTo(previewButton.snp.bottom).offset(20) make.top.equalTo(previewButton.snp.bottom).offset(20)
make.bottom.equalToSuperview().offset(-10) make.bottom.equalToSuperview().offset(-10)
@ -141,7 +142,6 @@ class PreviewCardCell: BaseTableViewCell {
noticeLabel.addGestureRecognizer(UITapGestureRecognizer()) noticeLabel.addGestureRecognizer(UITapGestureRecognizer())
} }
override func bindViewModel(model: ViewModel) { override func bindViewModel(model: ViewModel) {
guard let viewModel = model as? PreviewCardCellViewModel else { guard let viewModel = model as? PreviewCardCellViewModel else {
return return
@ -155,7 +155,7 @@ class PreviewCardCell: BaseTableViewCell {
viewModel.notice viewModel.notice
.bind(to: self.noticeLabel.rx.attributedText).disposed(by: rx.reuseBag) .bind(to: self.noticeLabel.rx.attributedText).disposed(by: rx.reuseBag)
viewModel.contentImage viewModel.contentImage
.compactMap{ $0 } .compactMap { $0 }
.bind(to: self.contentImageView.rx.image) .bind(to: self.contentImageView.rx.image)
.disposed(by: rx.reuseBag) .disposed(by: rx.reuseBag)
viewModel.contentImage viewModel.contentImage
@ -163,34 +163,33 @@ class PreviewCardCell: BaseTableViewCell {
.bind(to: self.contentImageView.rx.isHidden) .bind(to: self.contentImageView.rx.isHidden)
.disposed(by: rx.reuseBag) .disposed(by: rx.reuseBag)
// //
noticeLabel.gestureRecognizers!.first! noticeLabel.gestureRecognizers!.first!
.rx.event .rx.event
.compactMap{[weak weakModel = viewModel](_) -> ViewModel? in .compactMap { [weak weakModel = viewModel] _ -> ViewModel? in
// moreViewModel // moreViewModel
return weakModel?.previewModel.moreViewModel weakModel?.previewModel.moreViewModel
} }
.bind(to: viewModel.noticeTap) .bind(to: viewModel.noticeTap)
.disposed(by: rx.reuseBag) .disposed(by: rx.reuseBag)
// //
copyButton.rx.tap.map {[weak self] () -> String in copyButton.rx.tap.map { [weak self] () -> String in
return self?.contentLabel.text ?? "" self?.contentLabel.text ?? ""
} }
.bind(to: viewModel.copy) .bind(to: viewModel.copy)
.disposed(by: rx.reuseBag) .disposed(by: rx.reuseBag)
// //
previewButton.rx.tap.compactMap {[weak self] () -> URL? in previewButton.rx.tap.compactMap { [weak self] () -> URL? in
if let urlStr = self?.contentLabel.text?.urlEncoded(), if let urlStr = self?.contentLabel.text?.urlEncoded(),
let url = URL(string: urlStr){ let url = URL(string: urlStr)
{
return url return url
} }
return nil return nil
} }
.bind(to: viewModel.preview) .bind(to: viewModel.preview)
.disposed(by: rx.reuseBag) .disposed(by: rx.reuseBag)
} }
} }

View File

@ -20,8 +20,8 @@ class PreviewCardCellViewModel: ViewModel {
let copy = PublishRelay<String>() let copy = PublishRelay<String>()
let preview = PublishRelay<URL>() let preview = PublishRelay<URL>()
let previewModel:PreviewModel let previewModel: PreviewModel
init( previewModel:PreviewModel, clientState: Driver<Client.ClienState> ) { init(previewModel: PreviewModel, clientState: Driver<Client.ClienState>) {
self.previewModel = previewModel self.previewModel = previewModel
contentImage = BehaviorRelay<UIImage?>(value: previewModel.image) contentImage = BehaviorRelay<UIImage?>(value: previewModel.image)
@ -39,30 +39,29 @@ class PreviewCardCellViewModel: ViewModel {
// ServerManager.shared.currentAddress Client.shared.key // ServerManager.shared.currentAddress Client.shared.key
// viewModel input currentAddress key // viewModel input currentAddress key
// MVC MVVM // MVC MVVM
clientState.compactMap({[weak self] (_) -> NSAttributedString? in clientState.compactMap { [weak self] _ -> NSAttributedString? in
return self?.contentAttrStr() self?.contentAttrStr()
}) }
.drive(content) .drive(content)
.disposed(by: rx.disposeBag) .disposed(by: rx.disposeBag)
let noticeStr = "\(previewModel.notice ?? "")" let noticeStr = "\(previewModel.notice ?? "")"
let noticeAttrStr = NSMutableAttributedString(string: noticeStr, attributes: [ let noticeAttrStr = NSMutableAttributedString(string: noticeStr, attributes: [
NSAttributedString.Key.foregroundColor: Color.grey.base, NSAttributedString.Key.foregroundColor: Color.grey.base,
NSAttributedString.Key.font : RobotoFont.regular(with: 12) NSAttributedString.Key.font: RobotoFont.regular(with: 12)
]) ])
if let moreInfo = previewModel.moreInfo { if let moreInfo = previewModel.moreInfo {
noticeAttrStr.append(NSMutableAttributedString(string: " \(moreInfo)", attributes: [ noticeAttrStr.append(NSMutableAttributedString(string: " \(moreInfo)", attributes: [
NSAttributedString.Key.foregroundColor: Color.blue.base, NSAttributedString.Key.foregroundColor: Color.blue.base,
NSAttributedString.Key.font : RobotoFont.regular(with: 12) NSAttributedString.Key.font: RobotoFont.regular(with: 12)
])) ]))
} }
notice.accept(noticeAttrStr) notice.accept(noticeAttrStr)
} }
func contentAttrStr() -> NSAttributedString { func contentAttrStr() -> NSAttributedString {
var fontSize:CGFloat = 14 var fontSize: CGFloat = 14
if UIScreen.main.bounds.size.width <= 320 { if UIScreen.main.bounds.size.width <= 320 {
fontSize = 11 fontSize = 11
} }
@ -70,30 +69,30 @@ class PreviewCardCellViewModel: ViewModel {
let attrStr = NSMutableAttributedString(string: "") let attrStr = NSMutableAttributedString(string: "")
attrStr.append(NSAttributedString(string: serverUrl.absoluteString, attributes: [ attrStr.append(NSAttributedString(string: serverUrl.absoluteString, attributes: [
NSAttributedString.Key.foregroundColor: Color.grey.darken4, NSAttributedString.Key.foregroundColor: Color.grey.darken4,
NSAttributedString.Key.font : RobotoFont.regular(with: fontSize) NSAttributedString.Key.font: RobotoFont.regular(with: fontSize)
])) ]))
attrStr.append(NSAttributedString(string: "/\(Client.shared.key ?? "Your Key")", attributes: [ attrStr.append(NSAttributedString(string: "/\(Client.shared.key ?? "Your Key")", attributes: [
NSAttributedString.Key.foregroundColor: Color.grey.darken3, NSAttributedString.Key.foregroundColor: Color.grey.darken3,
NSAttributedString.Key.font : RobotoFont.regular(with: fontSize) NSAttributedString.Key.font: RobotoFont.regular(with: fontSize)
])) ]))
if let modelTitle = previewModel.title { if let modelTitle = previewModel.title {
attrStr.append(NSAttributedString(string: "/\(modelTitle)", attributes: [ attrStr.append(NSAttributedString(string: "/\(modelTitle)", attributes: [
NSAttributedString.Key.foregroundColor: Color.grey.darken1, NSAttributedString.Key.foregroundColor: Color.grey.darken1,
NSAttributedString.Key.font : RobotoFont.regular(with: fontSize) NSAttributedString.Key.font: RobotoFont.regular(with: fontSize)
])) ]))
} }
if let modelBody = previewModel.body { if let modelBody = previewModel.body {
attrStr.append(NSAttributedString(string: "/\(modelBody)", attributes: [ attrStr.append(NSAttributedString(string: "/\(modelBody)", attributes: [
NSAttributedString.Key.foregroundColor: Color.grey.base, NSAttributedString.Key.foregroundColor: Color.grey.base,
NSAttributedString.Key.font : RobotoFont.regular(with: fontSize) NSAttributedString.Key.font: RobotoFont.regular(with: fontSize)
])) ]))
} }
if let queryParameter = previewModel.queryParameter { if let queryParameter = previewModel.queryParameter {
attrStr.append(NSAttributedString(string: "?\(queryParameter)", attributes: [ attrStr.append(NSAttributedString(string: "?\(queryParameter)", attributes: [
NSAttributedString.Key.foregroundColor: Color.grey.lighten1, NSAttributedString.Key.foregroundColor: Color.grey.lighten1,
NSAttributedString.Key.font : RobotoFont.regular(with: fontSize) NSAttributedString.Key.font: RobotoFont.regular(with: fontSize)
])) ]))
} }

View File

@ -6,24 +6,26 @@
// Copyright © 2020 Fin. All rights reserved. // Copyright © 2020 Fin. All rights reserved.
// //
import UIKit
import Material
import AVKit import AVKit
import Material
import UIKit
class SoundCell: BaseTableViewCell { class SoundCell: BaseTableViewCell {
let copyButton = IconButton(image: UIImage(named: "baseline_file_copy_white_24pt"), tintColor: Color.grey.base) let copyButton = IconButton(image: UIImage(named: "baseline_file_copy_white_24pt"), tintColor: Color.grey.base)
let nameLabel:UILabel = { let nameLabel: UILabel = {
let label = UILabel() let label = UILabel()
label.fontSize = 14 label.fontSize = 14
label.textColor = Color.darkText.primary label.textColor = Color.darkText.primary
return label return label
}() }()
let durationLabel:UILabel = {
let durationLabel: UILabel = {
let label = UILabel() let label = UILabel()
label.fontSize = 12 label.fontSize = 12
label.textColor = Color.darkText.secondary label.textColor = Color.darkText.secondary
return label return label
}() }()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier) super.init(style: style, reuseIdentifier: reuseIdentifier)
self.selectionStyle = .none self.selectionStyle = .none
@ -32,25 +34,27 @@ class SoundCell: BaseTableViewCell {
self.contentView.addSubview(durationLabel) self.contentView.addSubview(durationLabel)
self.contentView.addSubview(copyButton) self.contentView.addSubview(copyButton)
nameLabel.snp.makeConstraints { (make) in nameLabel.snp.makeConstraints { make in
make.left.top.equalToSuperview().offset(15) make.left.top.equalToSuperview().offset(15)
} }
durationLabel.snp.makeConstraints { (make) in durationLabel.snp.makeConstraints { make in
make.left.equalTo(nameLabel) make.left.equalTo(nameLabel)
make.top.equalTo(nameLabel.snp.bottom).offset(5) make.top.equalTo(nameLabel.snp.bottom).offset(5)
make.bottom.equalToSuperview().offset(-15) make.bottom.equalToSuperview().offset(-15)
} }
copyButton.snp.makeConstraints { (make) in copyButton.snp.makeConstraints { make in
make.right.equalToSuperview().offset(-15) make.right.equalToSuperview().offset(-15)
make.centerY.equalToSuperview() make.centerY.equalToSuperview()
make.width.height.equalTo(40) make.width.height.equalTo(40)
} }
} }
@available(*, unavailable)
required init?(coder aDecoder: NSCoder) { required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented") fatalError("init(coder:) has not been implemented")
} }
override func bindViewModel(model:ViewModel){ override func bindViewModel(model: ViewModel) {
super.bindViewModel(model: model) super.bindViewModel(model: model)
guard let viewModel = model as? SoundCellViewModel else { guard let viewModel = model as? SoundCellViewModel else {
return return
@ -60,12 +64,12 @@ class SoundCell: BaseTableViewCell {
.bind(to: nameLabel.rx.text) .bind(to: nameLabel.rx.text)
.disposed(by: rx.reuseBag) .disposed(by: rx.reuseBag)
viewModel.duration viewModel.duration
.map { String(format: "%.2g second(s)", CMTimeGetSeconds($0) ) } .map { String(format: "%.2g second(s)", CMTimeGetSeconds($0)) }
.bind(to: durationLabel.rx.text) .bind(to: durationLabel.rx.text)
.disposed(by: rx.reuseBag) .disposed(by: rx.reuseBag)
copyButton.rx.tap copyButton.rx.tap
.map{ viewModel.name.value } .map { viewModel.name.value }
.bind(to: viewModel.copyNameAction) .bind(to: viewModel.copyNameAction)
.disposed(by: rx.reuseBag) .disposed(by: rx.reuseBag)
} }

View File

@ -6,12 +6,12 @@
// Copyright © 2020 Fin. All rights reserved. // Copyright © 2020 Fin. All rights reserved.
// //
import Foundation
import RxSwift
import RxCocoa
import AVKit import AVKit
import Foundation
import RxCocoa
import RxSwift
class SoundCellViewModel:ViewModel { class SoundCellViewModel: ViewModel {
let name = BehaviorRelay<String>(value: "") let name = BehaviorRelay<String>(value: "")
let duration = BehaviorRelay<CMTime>(value: .zero) let duration = BehaviorRelay<CMTime>(value: .zero)

View File

@ -9,18 +9,21 @@
import UIKit import UIKit
class SpacerCell: UITableViewCell { class SpacerCell: UITableViewCell {
var height:CGFloat = 0 { var height: CGFloat = 0 {
didSet{ didSet {
self.contentView.snp.remakeConstraints { make in self.contentView.snp.remakeConstraints { make in
make.height.equalTo(height) make.height.equalTo(height)
} }
} }
} }
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier) super.init(style: style, reuseIdentifier: reuseIdentifier)
self.backgroundColor = UIColor.clear self.backgroundColor = UIColor.clear
self.selectionStyle = .none self.selectionStyle = .none
} }
@available(*, unavailable)
required init?(coder aDecoder: NSCoder) { required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented") fatalError("init(coder:) has not been implemented")
} }

View File

@ -9,14 +9,14 @@
import UIKit import UIKit
class DetailTextCell: UITableViewCell { class DetailTextCell: UITableViewCell {
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: .value1, reuseIdentifier: reuseIdentifier) super.init(style: .value1, reuseIdentifier: reuseIdentifier)
self.selectionStyle = .none self.selectionStyle = .none
self.accessoryType = .disclosureIndicator self.accessoryType = .disclosureIndicator
} }
@available(*, unavailable)
required init?(coder aDecoder: NSCoder) { required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented") fatalError("init(coder:) has not been implemented")
} }
} }

View File

@ -8,40 +8,40 @@
import UIKit import UIKit
// item UIBarButtonItem 8 // item UIBarButtonItem 8
//16 // 16
// fixedSpace UIBarButtonItem // fixedSpace UIBarButtonItem
// AlignmentRectInsetsOverridable / // AlignmentRectInsetsOverridable /
// HitTestSlopable // HitTestSlopable
extension UINavigationItem { extension UINavigationItem {
func setLeftBarButtonItem(item: UIBarButtonItem) {
func setLeftBarButtonItem(item: UIBarButtonItem){
setBarButtonItems(items: [item], left: true) setBarButtonItems(items: [item], left: true)
} }
func setRightBarButtonItem(item: UIBarButtonItem){
func setRightBarButtonItem(item: UIBarButtonItem) {
setBarButtonItems(items: [item], left: false) setBarButtonItems(items: [item], left: false)
} }
func setBarButtonItems(items: [UIBarButtonItem], left:Bool){ func setBarButtonItems(items: [UIBarButtonItem], left: Bool) {
guard items.count > 0 else { guard items.count > 0 else {
self.leftBarButtonItems = nil self.leftBarButtonItems = nil
return return
} }
var buttonItems = items var buttonItems = items
if #available(iOS 11.0, *) { if #available(iOS 11.0, *) {
buttonItems.forEach { (item) in buttonItems.forEach { item in
guard let view = item.customView else {return} guard let view = item.customView else { return }
item.customView?.translatesAutoresizingMaskIntoConstraints = false item.customView?.translatesAutoresizingMaskIntoConstraints = false
(item.customView as? HitTestSlopable)?.hitTestSlop = UIEdgeInsets(top: -10, left: -10, bottom: -10, right: -10) (item.customView as? HitTestSlopable)?.hitTestSlop = UIEdgeInsets(top: -10, left: -10, bottom: -10, right: -10)
(item.customView as? AlignmentRectInsetsOverridable)?.alignmentRectInsetsOverride = UIEdgeInsets(top: 0, left: left ? 8 : -8, bottom: 0, right: left ? -8 : 8) (item.customView as? AlignmentRectInsetsOverridable)?.alignmentRectInsetsOverride = UIEdgeInsets(top: 0, left: left ? 8 : -8, bottom: 0, right: left ? -8 : 8)
item.customView?.snp.makeConstraints({ (make) in item.customView?.snp.makeConstraints { make in
make.width.equalTo(view.bounds.size.width > 24 ? view.bounds.width : 24) make.width.equalTo(view.bounds.size.width > 24 ? view.bounds.width : 24)
make.height.equalTo(view.bounds.size.height > 24 ? view.bounds.height : 24) make.height.equalTo(view.bounds.size.height > 24 ? view.bounds.height : 24)
}) }
} }
buttonItems.insert(UIBarButtonItem(barButtonSystemItem: .fixedSpace, target: nil, action: nil), at: 0) buttonItems.insert(UIBarButtonItem(barButtonSystemItem: .fixedSpace, target: nil, action: nil), at: 0)
} }
else{ else {
let spacer = UIBarButtonItem(barButtonSystemItem: .fixedSpace, target: nil, action: nil) let spacer = UIBarButtonItem(barButtonSystemItem: .fixedSpace, target: nil, action: nil)
spacer.width = -8 spacer.width = -8
buttonItems.insert(spacer, at: 0) buttonItems.insert(spacer, at: 0)

View File

@ -6,8 +6,8 @@
// Copyright © 2020 Fin. All rights reserved. // Copyright © 2020 Fin. All rights reserved.
// //
import UIKit
import CloudKit import CloudKit
import UIKit
class iCloudStatusCell: UITableViewCell { class iCloudStatusCell: UITableViewCell {
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
@ -16,7 +16,7 @@ class iCloudStatusCell: UITableViewCell {
self.textLabel?.text = NSLocalizedString("iCloudSatatus") self.textLabel?.text = NSLocalizedString("iCloudSatatus")
self.detailTextLabel?.text = "" self.detailTextLabel?.text = ""
CKContainer.default().accountStatus { (status, error) in CKContainer.default().accountStatus { status, _ in
dispatch_sync_safely_main_queue { dispatch_sync_safely_main_queue {
switch status { switch status {
case .available: case .available:
@ -32,6 +32,8 @@ class iCloudStatusCell: UITableViewCell {
} }
} }
} }
@available(*, unavailable)
required init?(coder aDecoder: NSCoder) { required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented") fatalError("init(coder:) has not been implemented")
} }

View File

@ -11,10 +11,9 @@ import UserNotifications
import UserNotificationsUI import UserNotificationsUI
class NotificationViewController: UIViewController, UNNotificationContentExtension { class NotificationViewController: UIViewController, UNNotificationContentExtension {
let noticeLabel: UILabel = {
let noticeLabel:UILabel = {
let label = UILabel() let label = UILabel()
label.textColor = UIColor.init(named: "notification_copy_color") label.textColor = UIColor(named: "notification_copy_color")
label.text = NSLocalizedString("Copy", comment: "") label.text = NSLocalizedString("Copy", comment: "")
label.font = UIFont.systemFont(ofSize: 16) label.font = UIFont.systemFont(ofSize: 16)
label.textAlignment = .center label.textAlignment = .center
@ -26,10 +25,12 @@ class NotificationViewController: UIViewController, UNNotificationContentExtensi
self.view.addSubview(self.noticeLabel) self.view.addSubview(self.noticeLabel)
self.preferredContentSize = CGSize(width: 0, height: 1) self.preferredContentSize = CGSize(width: 0, height: 1)
} }
override func viewWillAppear(_ animated: Bool) { override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated) super.viewWillAppear(animated)
self.preferredContentSize = CGSize(width: 0, height: 1) self.preferredContentSize = CGSize(width: 0, height: 1)
} }
override func viewDidAppear(_ animated: Bool) { override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated) super.viewDidAppear(animated)
self.preferredContentSize = CGSize(width: 0, height: 1) self.preferredContentSize = CGSize(width: 0, height: 1)
@ -37,26 +38,25 @@ class NotificationViewController: UIViewController, UNNotificationContentExtensi
func didReceive(_ notification: UNNotification) { func didReceive(_ notification: UNNotification) {
guard notification.request.content.userInfo["autocopy"] as? String == "1" guard notification.request.content.userInfo["autocopy"] as? String == "1"
|| notification.request.content.userInfo["automaticallycopy"] as? String == "1" else { || notification.request.content.userInfo["automaticallycopy"] as? String == "1"
else {
return return
} }
if let copy = notification.request.content.userInfo["copy"] as? String if let copy = notification.request.content.userInfo["copy"] as? String {
{
UIPasteboard.general.string = copy UIPasteboard.general.string = copy
} }
else{ else {
UIPasteboard.general.string = notification.request.content.body UIPasteboard.general.string = notification.request.content.body
} }
} }
func didReceive(_ response: UNNotificationResponse, completionHandler completion: @escaping (UNNotificationContentExtensionResponseOption) -> Void) { func didReceive(_ response: UNNotificationResponse, completionHandler completion: @escaping (UNNotificationContentExtensionResponseOption) -> Void) {
let userInfo = response.notification.request.content.userInfo let userInfo = response.notification.request.content.userInfo
if let copy = userInfo["copy"] as? String { if let copy = userInfo["copy"] as? String {
UIPasteboard.general.string = copy UIPasteboard.general.string = copy
} }
else{ else {
UIPasteboard.general.string = response.notification.request.content.body UIPasteboard.general.string = response.notification.request.content.body
} }
@ -64,6 +64,5 @@ class NotificationViewController: UIViewController, UNNotificationContentExtensi
self.noticeLabel.frame = CGRect(x: 0, y: 0, width: self.view.bounds.width, height: 40) self.noticeLabel.frame = CGRect(x: 0, y: 0, width: self.view.bounds.width, height: 40)
completion(.doNotDismiss) completion(.doNotDismiss)
} }
} }