mirror of
https://github.com/Finb/Bark.git
synced 2025-12-08 21:36:01 +00:00
格式化代码
This commit is contained in:
parent
7be222b370
commit
13a11ebcf4
@ -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 haven’t migrated anything yet, so oldSchemaVersion == 0
|
// We haven’t 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:.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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]
|
||||||
|
|||||||
@ -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()
|
||||||
|
|||||||
@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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 是 0,与iOS12 不一致,这里强行让他们保持一致,方便开发
|
// iOS 11下,不是全面屏的手机 safeAreaInsets.top 是 0,与iOS12 不一致,这里强行让他们保持一致,方便开发
|
||||||
|
|||||||
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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) {
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
||||||
|
|
||||||
|
|||||||
@ -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")
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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()
|
||||||
|
|||||||
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
|
|||||||
@ -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(){
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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 {}
|
||||||
|
|
||||||
}
|
|
||||||
|
|||||||
@ -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())
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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)
|
||||||
|
|
||||||
|
|
||||||
// 打开APP时,历史消息列表距离上次刷新超过1小时,则自动刷新一下
|
// 打开APP时,历史消息列表距离上次刷新超过1小时,则自动刷新一下
|
||||||
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 {
|
||||||
/*
|
/*
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
@ -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)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
@ -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)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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() }
|
||||||
|
|||||||
@ -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()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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 }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
@ -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"]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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 haven’t migrated anything yet, so oldSchemaVersion == 0
|
// We haven’t 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)
|
||||||
|
|||||||
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
@ -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)
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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")
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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)
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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]
|
||||||
|
|||||||
@ -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)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
@ -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")
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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: "")
|
||||||
|
|||||||
@ -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")
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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")
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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")
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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)
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user