格式化代码

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,14 +22,15 @@ 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
@ -64,7 +64,7 @@ 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),
@ -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()
@ -111,14 +111,16 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
// //
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]
@ -142,9 +144,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
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
} }
@ -152,7 +153,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
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"
@ -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,8 +6,8 @@
// 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
@ -22,9 +22,7 @@ enum BarkSettingKey:String {
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 {
@ -66,6 +64,7 @@ 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,29 +6,27 @@
// 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? { var currentNavigationController: UINavigationController? {
get {
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"
@ -72,7 +70,7 @@ 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() {
@ -92,7 +90,7 @@ 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()

View File

@ -16,7 +16,6 @@ extension Date {
} }
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)
@ -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

@ -32,14 +32,13 @@ func NSLocalizedString( _ key:String ) -> String {
} }
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 便

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
@ -49,11 +46,9 @@ enum MJRefreshAction {
} }
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:
@ -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

@ -20,6 +20,7 @@ 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):
@ -41,6 +42,4 @@ extension BarkApi: BarkTargetType {
return "/register" return "/register"
} }
} }
} }

View File

@ -6,12 +6,12 @@
// 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 }
@ -21,6 +21,7 @@ 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,7 +43,6 @@ extension BarkTargetType {
} }
var requestTaskWithParameters: Task { var requestTaskWithParameters: Task {
get {
// //
var defaultParameters: [String: Any] = [:] var defaultParameters: [String: Any] = [:]
// //
@ -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 {
@ -92,8 +91,8 @@ extension BarkTargetType {
} }
} }
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,11 +8,11 @@
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)
@ -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
} }
} }
@ -38,7 +38,7 @@ extension Observable where Element: Moya.Response {
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)
} }
@ -59,7 +59,8 @@ 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 {
@ -109,7 +110,7 @@ extension Observable where Element: Moya.Response {
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]
} }
@ -131,13 +132,13 @@ 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? { private func resultFromJSON<T: Mappable>(json: JSON) -> T? {
if let str = json.rawString() { if let str = json.rawString() {
return resultFromJSON(jsonString: str) return resultFromJSON(jsonString: str)

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)
@ -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,9 +6,9 @@
// 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

View File

@ -12,7 +12,7 @@ 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
} }
@ -37,6 +37,7 @@ class ServerManager: NSObject {
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 {
@ -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,9 +9,10 @@
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()

View File

@ -6,9 +6,9 @@
// 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() {
@ -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 {
@ -66,6 +65,5 @@ class StateStorageTabBarController: UITabBarController, UITabBarControllerDelega
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()
@ -22,9 +21,6 @@ class BarkSFSafariViewController: SFSafariViewController {
} }
override var preferredStatusBarStyle: UIStatusBarStyle { override var preferredStatusBarStyle: UIStatusBarStyle {
get {
return .default return .default
} }
} }
}

View File

@ -6,10 +6,9 @@
// 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
@ -18,21 +17,21 @@ class BaseViewController: UIViewController {
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,7 +48,7 @@ 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()
@ -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()
} }
@ -102,6 +102,4 @@ class GroupFilterViewController: BaseViewController {
} }
} }
extension GroupFilterViewController: UITableViewDelegate { extension GroupFilterViewController: UITableViewDelegate {}
}

View File

@ -6,15 +6,16 @@
// 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 { class GroupFilterViewModel: ViewModel, ViewModelType {
let groups: [GroupFilterModel] let groups: [GroupFilterModel]
init(groups: [GroupFilterModel]) { init(groups: [GroupFilterModel]) {
@ -35,11 +36,10 @@ class GroupFilterViewModel: ViewModel,ViewModelType {
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
@ -50,20 +50,20 @@ class GroupFilterViewModel: ViewModel,ViewModelType {
// 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 []
@ -74,8 +74,7 @@ class GroupFilterViewModel: ViewModel,ViewModelType {
} }
.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 () }
@ -89,6 +88,4 @@ class GroupFilterViewModel: ViewModel,ViewModelType {
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)
@ -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)
@ -58,11 +57,11 @@ 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
@ -79,7 +78,7 @@ class HomeViewController: BaseViewController {
) )
) )
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
@ -157,7 +156,6 @@ class HomeViewController: BaseViewController {
self?.tableView.reloadData() self?.tableView.reloadData()
}) })
.disposed(by: rx.disposeBag) .disposed(by: rx.disposeBag)
} }
func pushViewModel(viewModel: ViewModel) { func pushViewModel(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,6 +20,7 @@ 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>
@ -35,14 +36,16 @@ class HomeViewModel: ViewModel, ViewModelType {
} }
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"),
@ -87,22 +90,21 @@ 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 } let customServer = input.addCustomServerTap.map { NewServerViewModel() as ViewModel }
// title // 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)
@ -116,7 +118,7 @@ class HomeViewModel: ViewModel, ViewModelType {
.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
@ -126,8 +128,8 @@ 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()
@ -135,9 +137,9 @@ class HomeViewModel: ViewModel, ViewModelType {
.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()
@ -152,13 +154,12 @@ class HomeViewModel: ViewModel, ViewModelType {
.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")
} }
@ -177,10 +178,10 @@ class HomeViewModel: ViewModel, ViewModelType {
// 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)
@ -199,5 +200,4 @@ class HomeViewModel: ViewModel, ViewModelType {
registerForRemoteNotifications: registerForRemoteNotifications registerForRemoteNotifications: registerForRemoteNotifications
) )
} }
} }

View File

@ -6,13 +6,13 @@
// 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
@ -21,7 +21,6 @@ enum MessageDeleteType: Int{
case allTime case allTime
var string: String { var string: String {
get {
return [ return [
NSLocalizedString("lastHour"), NSLocalizedString("lastHour"),
NSLocalizedString("today"), NSLocalizedString("today"),
@ -30,7 +29,6 @@ enum MessageDeleteType: Int{
][self.rawValue] ][self.rawValue]
} }
} }
}
class MessageListViewController: BaseViewController { class MessageListViewController: BaseViewController {
let deleteButton: BKButton = { let deleteButton: BKButton = {
@ -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)
@ -75,11 +73,10 @@ class MessageListViewController: BaseViewController {
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
@ -96,7 +93,6 @@ class MessageListViewController: BaseViewController {
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,7 +102,7 @@ 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) {
@ -145,8 +141,7 @@ class MessageListViewController: BaseViewController {
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
@ -159,14 +154,14 @@ class MessageListViewController: BaseViewController {
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
@ -197,13 +192,12 @@ class MessageListViewController: BaseViewController {
// //
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"))
}) })
@ -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)
} }
@ -241,6 +235,7 @@ extension MessageListViewController: UISearchControllerDelegate{
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 {
/* /*

View File

@ -7,10 +7,10 @@
// //
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 {
@ -70,9 +70,8 @@ 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 = ""
@ -105,9 +104,9 @@ class MessageListViewModel: ViewModel,ViewModelType {
// 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)]
} }
// //
@ -151,9 +150,9 @@ class MessageListViewModel: ViewModel,ViewModelType {
.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 {
@ -180,10 +179,10 @@ 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)
} }
@ -231,13 +230,13 @@ class MessageListViewModel: ViewModel,ViewModelType {
.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)

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,7 +53,7 @@ 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
@ -81,7 +83,5 @@ class MessageSettingsViewController: BaseViewController {
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,21 +7,22 @@
// //
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 { struct Output {
var settings: Driver<[SectionModel<String, MessageSettingItem>]> var settings: Driver<[SectionModel<String, MessageSettingItem>]>
var openUrl: Driver<URL> var openUrl: Driver<URL>
} }
func transform(input: Input) -> Output {
func transform(input: Input) -> Output {
let settings: [MessageSettingItem] = { let settings: [MessageSettingItem] = {
var settings = [MessageSettingItem]() var settings = [MessageSettingItem]()
settings.append(.label(text: "iCloud")) settings.append(.label(text: "iCloud"))
@ -32,7 +33,8 @@ 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",
@ -42,7 +44,6 @@ class MessageSettingsViewModel: ViewModel, ViewModelType {
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,14 +86,11 @@ 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 {

View File

@ -6,15 +6,14 @@
// 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
@ -52,26 +51,27 @@ 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
@ -79,8 +79,6 @@ class NewServerViewController: BaseViewController {
.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,
@ -117,7 +115,5 @@ class NewServerViewController: BaseViewController {
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,10 +7,10 @@
// //
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 {
@ -27,13 +27,11 @@ 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>()
@ -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() }

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()
} }

View File

@ -6,17 +6,17 @@
// 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>
@ -26,10 +26,10 @@ 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
} }
@ -46,5 +46,4 @@ class SoundsViewModel:ViewModel,ViewModelType {
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,9 +6,9 @@
// 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?
@ -23,6 +23,7 @@ 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

@ -26,8 +26,8 @@ class PreviewModel: NSObject {
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,15 +6,14 @@
// 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")
@ -22,28 +21,29 @@ class NotificationService: UNNotificationServiceExtension {
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
} }
@ -53,7 +53,6 @@ class NotificationService: UNNotificationServiceExtension {
} }
} }
/// ///
/// - Parameter userInfo: /// - Parameter userInfo:
/// `isarchive` `isarchive` /// `isarchive` `isarchive`
@ -73,7 +72,7 @@ class NotificationService: UNNotificationServiceExtension {
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
@ -86,7 +85,6 @@ class NotificationService: UNNotificationServiceExtension {
} }
} }
/// ///
/// - Parameters: /// - Parameters:
/// - userInfo: /// - userInfo:
@ -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,7 +128,8 @@ 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)
@ -146,12 +145,14 @@ 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 {
@ -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
} }
@ -238,8 +241,7 @@ class NotificationService: UNNotificationServiceExtension {
} }
} }
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

@ -11,13 +11,13 @@ import UIKit
protocol AlignmentRectInsetsOverridable: AnyObject { protocol AlignmentRectInsetsOverridable: AnyObject {
var alignmentRectInsetsOverride: UIEdgeInsets? { get set } var alignmentRectInsetsOverride: UIEdgeInsets? { get set }
} }
protocol HitTestSlopable: AnyObject { protocol HitTestSlopable: AnyObject {
var hitTestSlop: UIEdgeInsets { get set } var hitTestSlop: UIEdgeInsets { get set }
} }
class BKButton: UIButton, HitTestSlopable, AlignmentRectInsetsOverridable { class BKButton: UIButton, HitTestSlopable, AlignmentRectInsetsOverridable {
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)

View File

@ -9,8 +9,7 @@
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 {
@ -20,5 +19,4 @@ class BKLabel: UILabel {
return self.bounds.inset(by: hitTestSlop).contains(point) return self.bounds.inset(by: hitTestSlop).contains(point)
} }
} }
} }

View File

@ -6,10 +6,10 @@
// 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)

View File

@ -6,8 +6,8 @@
// 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 = {
@ -16,6 +16,7 @@ class GroupTableViewCell: BaseTableViewCell {
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,7 +38,7 @@ 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)
@ -48,6 +49,8 @@ class GroupTableViewCell: BaseTableViewCell {
(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")
} }
@ -59,9 +62,9 @@ class GroupTableViewCell: BaseTableViewCell {
} }
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)
@ -73,7 +76,5 @@ class GroupTableViewCell: BaseTableViewCell {
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,11 +49,13 @@ 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
@ -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)
@ -133,6 +137,5 @@ class MessageTableViewCell: BaseTableViewCell {
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
@ -35,7 +34,6 @@ class MessageTableViewCellViewModel: ViewModel {
} }
} }
struct MessageSection { struct MessageSection {
var header: String var header: String
var messages: [MessageTableViewCellViewModel] var messages: [MessageTableViewCellViewModel]

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,11 +37,13 @@ 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
@ -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
@ -166,16 +166,16 @@ class PreviewCardCell: BaseTableViewCell {
// //
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)
@ -183,14 +183,13 @@ class PreviewCardCell: BaseTableViewCell {
// //
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

@ -39,13 +39,12 @@ 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,

View File

@ -6,9 +6,9 @@
// 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)
@ -18,12 +18,14 @@ class SoundCell: BaseTableViewCell {
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,20 +34,22 @@ 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")
} }

View File

@ -6,10 +6,10 @@
// 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: "")

View File

@ -16,11 +16,14 @@ class SpacerCell: UITableViewCell {
} }
} }
} }
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

@ -14,10 +14,10 @@ import UIKit
// 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)
} }
@ -29,15 +29,15 @@ extension UINavigationItem {
} }
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)
} }

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,11 +38,11 @@ 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 {
@ -50,7 +51,6 @@ class NotificationViewController: UIViewController, UNNotificationContentExtensi
} }
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 {
@ -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)
} }
} }