diff --git a/EhPanda/App/Tools/Clients/AuthorizationClient.swift b/EhPanda/App/Tools/Clients/AuthorizationClient.swift index 7537a562..e4c1cefe 100644 --- a/EhPanda/App/Tools/Clients/AuthorizationClient.swift +++ b/EhPanda/App/Tools/Clients/AuthorizationClient.swift @@ -21,21 +21,21 @@ extension AuthorizationClient { return !LAContext().canEvaluatePolicy(.deviceOwnerAuthentication, error: &error) }, localAuthroize: { reason in - Future { promise in - let context = LAContext() - var error: NSError? + Effect.publisher { + Future { promise in + let context = LAContext() + var error: NSError? - if context.canEvaluatePolicy(.deviceOwnerAuthentication, error: &error) { - context.evaluatePolicy(.deviceOwnerAuthentication, localizedReason: reason) { isSuccess, _ in - promise(.success(isSuccess)) + if context.canEvaluatePolicy(.deviceOwnerAuthentication, error: &error) { + context.evaluatePolicy(.deviceOwnerAuthentication, localizedReason: reason) { isSuccess, _ in + promise(.success(isSuccess)) + } + } else { + promise(.success(false)) } - } else { - promise(.success(false)) } + .receive(on: DispatchQueue.main) } - .eraseToAnyPublisher() - .receive(on: DispatchQueue.main) - .eraseToEffect() } ) } diff --git a/EhPanda/App/Tools/Clients/DatabaseClient.swift b/EhPanda/App/Tools/Clients/DatabaseClient.swift index a27777df..8b141273 100644 --- a/EhPanda/App/Tools/Clients/DatabaseClient.swift +++ b/EhPanda/App/Tools/Clients/DatabaseClient.swift @@ -20,36 +20,36 @@ struct DatabaseClient { extension DatabaseClient { static let live: Self = .init( prepareDatabase: { - Future { promise in - PersistenceController.shared.prepare { - switch $0 { - case .success: - promise(.success(nil)) + Effect.publisher { + Future { promise in + PersistenceController.shared.prepare { + switch $0 { + case .success: + promise(.success(nil)) - case .failure(let appError): - promise(.success(appError)) + case .failure(let appError): + promise(.success(appError)) + } } } + .receive(on: DispatchQueue.main) } - .eraseToAnyPublisher() - .receive(on: DispatchQueue.main) - .eraseToEffect() }, dropDatabase: { - Future { promise in - PersistenceController.shared.rebuild { - switch $0 { - case .success: - promise(.success(nil)) + Effect.publisher { + Future { promise in + PersistenceController.shared.rebuild { + switch $0 { + case .success: + promise(.success(nil)) - case .failure(let appError): - promise(.success(appError)) + case .failure(let appError): + promise(.success(appError)) + } } } + .receive(on: DispatchQueue.main) } - .eraseToAnyPublisher() - .receive(on: DispatchQueue.main) - .eraseToEffect() }, saveContext: { let context = PersistenceController.shared.container.viewContext @@ -217,49 +217,49 @@ extension DatabaseClient { return entity } func fetchAppEnv() -> Effect { - Future { promise in - DispatchQueue.main.async { - promise(.success(fetchOrCreate(entityType: AppEnvMO.self).toEntity())) + Effect.publisher { + Future { promise in + DispatchQueue.main.async { + promise(.success(fetchOrCreate(entityType: AppEnvMO.self).toEntity())) + } } + .receive(on: DispatchQueue.main) } - .eraseToAnyPublisher() - .receive(on: DispatchQueue.main) - .eraseToEffect() } func fetchAppEnvSynchronously() -> AppEnv { fetchOrCreate(entityType: AppEnvMO.self).toEntity() } func fetchGalleryState(gid: String) -> Effect { guard gid.isValidGID else { return .none } - return Future { promise in - DispatchQueue.main.async { - promise(.success( - fetchOrCreate(entityType: GalleryStateMO.self, gid: gid).toEntity() - )) + return Effect.publisher { + Future { promise in + DispatchQueue.main.async { + promise(.success( + fetchOrCreate(entityType: GalleryStateMO.self, gid: gid).toEntity() + )) + } } + .receive(on: DispatchQueue.main) } - .eraseToAnyPublisher() - .receive(on: DispatchQueue.main) - .eraseToEffect() } func fetchHistoryGalleries(fetchLimit: Int = 0) -> Effect<[Gallery]> { - Future { promise in - DispatchQueue.main.async { - let predicate = NSPredicate(format: "lastOpenDate != nil") - let sortDescriptor = NSSortDescriptor( - keyPath: \GalleryMO.lastOpenDate, ascending: false - ) - let galleries = batchFetch( - entityType: GalleryMO.self, fetchLimit: fetchLimit, predicate: predicate, - findBeforeFetch: false, sortDescriptors: [sortDescriptor] - ) - .map { $0.toEntity() } - promise(.success(galleries)) + Effect.publisher { + Future { promise in + DispatchQueue.main.async { + let predicate = NSPredicate(format: "lastOpenDate != nil") + let sortDescriptor = NSSortDescriptor( + keyPath: \GalleryMO.lastOpenDate, ascending: false + ) + let galleries = batchFetch( + entityType: GalleryMO.self, fetchLimit: fetchLimit, predicate: predicate, + findBeforeFetch: false, sortDescriptors: [sortDescriptor] + ) + .map { $0.toEntity() } + promise(.success(galleries)) + } } + .receive(on: DispatchQueue.main) } - .eraseToAnyPublisher() - .receive(on: DispatchQueue.main) - .eraseToEffect() } } // MARK: FetchAccessor @@ -529,14 +529,15 @@ extension DatabaseClient { // Update User func updateUserProperty(_ commitChanges: @escaping (inout User) -> Void) -> Effect { - fetchAppEnv().map(\.user) - .map { (user: User) -> User in - var user = user - commitChanges(&user) - return user - } - .flatMap(updateUser) - .eraseToEffect() + Effect.publisher { + fetchAppEnv().map(\.user) + .map { (user: User) -> User in + var user = user + commitChanges(&user) + return user + } + .flatMap(updateUser) + } } func updateGreeting(_ greeting: Greeting) -> Effect { updateUserProperty { user in diff --git a/EhPanda/App/Tools/Clients/ImageClient.swift b/EhPanda/App/Tools/Clients/ImageClient.swift index 07943812..43c142bd 100644 --- a/EhPanda/App/Tools/Clients/ImageClient.swift +++ b/EhPanda/App/Tools/Clients/ImageClient.swift @@ -26,23 +26,24 @@ extension ImageClient { }) }, saveImageToPhotoLibrary: { (image, isAnimated) in - Future { promise in - DispatchQueue.global(qos: .utility).async { - if let data = image.kf.data(format: isAnimated ? .GIF : .unknown) { - PHPhotoLibrary.shared().performChanges { - let request = PHAssetCreationRequest.forAsset() - request.addResource(with: .photo, data: data, options: nil) - } completionHandler: { (isSuccess, _) in - promise(.success(isSuccess)) + Effect.publisher { + Future { promise in + DispatchQueue.global(qos: .utility).async { + if let data = image.kf.data(format: isAnimated ? .GIF : .unknown) { + PHPhotoLibrary.shared().performChanges { + let request = PHAssetCreationRequest.forAsset() + request.addResource(with: .photo, data: data, options: nil) + } completionHandler: { (isSuccess, _) in + promise(.success(isSuccess)) + } + } else { + promise(.success(false)) } - } else { - promise(.success(false)) } } + .eraseToAnyPublisher() + .receive(on: DispatchQueue.main) } - .eraseToAnyPublisher() - .receive(on: DispatchQueue.main) - .eraseToEffect() }, downloadImage: { url in Future { promise in diff --git a/EhPanda/App/Tools/Clients/LibraryClient.swift b/EhPanda/App/Tools/Clients/LibraryClient.swift index 3d9d5bed..9861231a 100644 --- a/EhPanda/App/Tools/Clients/LibraryClient.swift +++ b/EhPanda/App/Tools/Clients/LibraryClient.swift @@ -67,23 +67,23 @@ extension LibraryClient { }) }, analyzeImageColors: { image in - Future { promise in - image.getColors(quality: .lowest) { colors in - promise(.success(colors)) + Effect.publisher { + Future { promise in + image.getColors(quality: .lowest) { colors in + promise(.success(colors)) + } } } - .eraseToAnyPublisher() - .eraseToEffect() }, calculateWebImageDiskCacheSize: { - Future { promise in - KingfisherManager.shared.cache.calculateDiskStorageSize { - promise(.success(try? $0.get())) + Effect.publisher { + Future { promise in + KingfisherManager.shared.cache.calculateDiskStorageSize { + promise(.success(try? $0.get())) + } } + .receive(on: DispatchQueue.main) } - .eraseToAnyPublisher() - .receive(on: DispatchQueue.main) - .eraseToEffect() } ) } diff --git a/EhPanda/DataFlow/AppReducer.swift b/EhPanda/DataFlow/AppReducer.swift index 760f1b90..18aa0a4d 100644 --- a/EhPanda/DataFlow/AppReducer.swift +++ b/EhPanda/DataFlow/AppReducer.swift @@ -155,12 +155,13 @@ struct AppReducer: Reducer { effects.append(Effect.send(.setting(.setNavigation(.account)))) if !cookieClient.didLogin { effects.append( - Effect.send(.setting(.account(.setNavigation(.login)))) - .delay( - for: .milliseconds(deviceClient.isPad() ? 1200 : 200), - scheduler: DispatchQueue.main - ) - .eraseToEffect() + Effect.publisher { + Effect.send(.setting(.account(.setNavigation(.login)))) + .delay( + for: .milliseconds(deviceClient.isPad() ? 1200 : 200), + scheduler: DispatchQueue.main + ) + } ) } return .merge(effects) diff --git a/EhPanda/DataFlow/AppRouteReducer.swift b/EhPanda/DataFlow/AppRouteReducer.swift index f2fc9e19..bcc9460a 100644 --- a/EhPanda/DataFlow/AppRouteReducer.swift +++ b/EhPanda/DataFlow/AppRouteReducer.swift @@ -101,11 +101,15 @@ struct AppRouteReducer: Reducer { let (isGalleryImageURL, _, _) = urlClient.analyzeURL(url) let gid = urlClient.parseGalleryID(url) guard databaseClient.fetchGallery(gid: gid) == nil else { - return Effect.send(.handleGalleryLink(url)) - .delay(for: .milliseconds(delay + 250), scheduler: DispatchQueue.main).eraseToEffect() + return Effect.publisher { + Effect.send(.handleGalleryLink(url)) + .delay(for: .milliseconds(delay + 250), scheduler: DispatchQueue.main) + } + } + return Effect.publisher { + Effect.send(.fetchGallery(url, isGalleryImageURL)) + .delay(for: .milliseconds(delay), scheduler: DispatchQueue.main) } - return Effect.send(.fetchGallery(url, isGalleryImageURL)) - .delay(for: .milliseconds(delay), scheduler: DispatchQueue.main).eraseToEffect() case .handleGalleryLink(let url): let (_, pageIndex, commentID) = urlClient.analyzeURL(url) @@ -116,14 +120,18 @@ struct AppRouteReducer: Reducer { if let pageIndex = pageIndex { effects.append(Effect.send(.updateReadingProgress(gid, pageIndex))) effects.append( - Effect.send(.detail(.setNavigation(.reading))) - .delay(for: .milliseconds(500), scheduler: DispatchQueue.main).eraseToEffect() + Effect.publisher { + Effect.send(.detail(.setNavigation(.reading))) + .delay(for: .milliseconds(500), scheduler: DispatchQueue.main) + } ) } else if let commentID = commentID { state.detailState.commentsState?.scrollCommentID = commentID effects.append( - Effect.send(.detail(.setNavigation(.comments(url)))) - .delay(for: .milliseconds(500), scheduler: DispatchQueue.main).eraseToEffect() + Effect.publisher { + Effect.send(.detail(.setNavigation(.comments(url)))) + .delay(for: .milliseconds(500), scheduler: DispatchQueue.main) + } ) } effects.append(Effect.send(.setNavigation(.detail(gid)))) @@ -148,8 +156,10 @@ struct AppRouteReducer: Reducer { Effect.send(.handleGalleryLink(url)) ) case .failure: - return Effect.send(.setHUDConfig(.error)) - .delay(for: .milliseconds(500), scheduler: DispatchQueue.main).eraseToEffect() + return Effect.publisher { + Effect.send(Action.setHUDConfig(.error)) + .delay(for: .milliseconds(500), scheduler: DispatchQueue.main) + } } case .fetchGreetingDone(let result): diff --git a/EhPanda/View/Detail/Comments/CommentsReducer.swift b/EhPanda/View/Detail/Comments/CommentsReducer.swift index f0bd3286..1243b661 100644 --- a/EhPanda/View/Detail/Comments/CommentsReducer.swift +++ b/EhPanda/View/Detail/Comments/CommentsReducer.swift @@ -113,12 +113,18 @@ struct CommentsReducer: Reducer { case .performScrollOpacityEffect: return .merge( - Effect.send(.setScrollRowOpacity(0.25)) - .delay(for: .milliseconds(750), scheduler: DispatchQueue.main).eraseToEffect(), - Effect.send(.setScrollRowOpacity(1)) - .delay(for: .milliseconds(1250), scheduler: DispatchQueue.main).eraseToEffect(), - Effect.send(.clearScrollCommentID) - .delay(for: .milliseconds(2000), scheduler: DispatchQueue.main).eraseToEffect() + Effect.publisher { + Effect.send(.setScrollRowOpacity(0.25)) + .delay(for: .milliseconds(750), scheduler: DispatchQueue.main) + }, + Effect.publisher { + Effect.send(.setScrollRowOpacity(1)) + .delay(for: .milliseconds(1250), scheduler: DispatchQueue.main) + }, + Effect.publisher { + Effect.send(.clearScrollCommentID) + .delay(for: .milliseconds(2000), scheduler: DispatchQueue.main) + } ) case .handleCommentLink(let url): @@ -139,22 +145,28 @@ struct CommentsReducer: Reducer { if let pageIndex = pageIndex { effects.append(Effect.send(.updateReadingProgress(gid, pageIndex))) effects.append( - Effect.send(.detail(.setNavigation(.reading))) - .delay(for: .milliseconds(750), scheduler: DispatchQueue.main).eraseToEffect() + Effect.publisher { + Effect.send(.detail(.setNavigation(.reading))) + .delay(for: .milliseconds(750), scheduler: DispatchQueue.main) + } ) } else if let commentID = commentID { state.detailState.commentsState?.scrollCommentID = commentID effects.append( - Effect.send(.detail(.setNavigation(.comments(url)))) - .delay(for: .milliseconds(750), scheduler: DispatchQueue.main).eraseToEffect() + Effect.publisher { + Effect.send(.detail(.setNavigation(.comments(url)))) + .delay(for: .milliseconds(750), scheduler: DispatchQueue.main) + } ) } effects.append(Effect.send(.setNavigation(.detail(gid)))) return .merge(effects) case .onPostCommentAppear: - return Effect.send(.setPostCommentFocused(true)) - .delay(for: .milliseconds(750), scheduler: DispatchQueue.main).eraseToEffect() + return Effect.publisher { + Effect.send(.setPostCommentFocused(true)) + .delay(for: .milliseconds(750), scheduler: DispatchQueue.main) + } case .onAppear: if state.detailState == nil { @@ -209,8 +221,10 @@ struct CommentsReducer: Reducer { Effect.send(.handleGalleryLink(url)) ) case .failure: - return Effect.send(.setHUDConfig(.error)) - .delay(for: .milliseconds(500), scheduler: DispatchQueue.main).eraseToEffect() + return Effect.publisher { + Effect.send(.setHUDConfig(.error)) + .delay(for: .milliseconds(500), scheduler: DispatchQueue.main) + } } case .detail: diff --git a/EhPanda/View/Detail/DetailReducer.swift b/EhPanda/View/Detail/DetailReducer.swift index 3a1ae81d..567911b2 100644 --- a/EhPanda/View/Detail/DetailReducer.swift +++ b/EhPanda/View/Detail/DetailReducer.swift @@ -151,8 +151,10 @@ struct DetailReducer: Reducer { ) case .onPostCommentAppear: - return Effect.send(.setPostCommentFocused(true)) - .delay(for: .milliseconds(750), scheduler: DispatchQueue.main).eraseToEffect() + return Effect.publisher { + Effect.send(.setPostCommentFocused(true)) + .delay(for: .milliseconds(750), scheduler: DispatchQueue.main) + } case .onAppear(let gid, let showsNewDawnGreeting): state.showsNewDawnGreeting = showsNewDawnGreeting @@ -189,7 +191,9 @@ struct DetailReducer: Reducer { return .merge( Effect.send(.rateGallery), .run(operation: { _ in hapticsClient.generateFeedback(.soft) }), - Effect.send(.confirmRatingDone).delay(for: 1, scheduler: DispatchQueue.main).eraseToEffect() + Effect.publisher { + Effect.send(.confirmRatingDone).delay(for: 1, scheduler: DispatchQueue.main) + } ) case .confirmRatingDone: diff --git a/EhPanda/View/Home/History/HistoryReducer.swift b/EhPanda/View/Home/History/HistoryReducer.swift index 419f57e9..0da2bf44 100644 --- a/EhPanda/View/Home/History/HistoryReducer.swift +++ b/EhPanda/View/Home/History/HistoryReducer.swift @@ -70,8 +70,10 @@ struct HistoryReducer: Reducer { case .clearHistoryGalleries: return .merge( databaseClient.clearHistoryGalleries().fireAndForget(), - Effect.send(.fetchGalleries) - .delay(for: .milliseconds(200), scheduler: DispatchQueue.main).eraseToEffect() + Effect.publisher { + Effect.send(.fetchGalleries) + .delay(for: .milliseconds(200), scheduler: DispatchQueue.main) + } ) case .fetchGalleries: diff --git a/EhPanda/View/Home/HomeReducer.swift b/EhPanda/View/Home/HomeReducer.swift index d045c472..89a828fd 100644 --- a/EhPanda/View/Home/HomeReducer.swift +++ b/EhPanda/View/Home/HomeReducer.swift @@ -105,9 +105,10 @@ struct HomeReducer: Reducer { guard state.cardPageIndex < state.popularGalleries.count else { return .none } state.currentCardID = state.popularGalleries[state.cardPageIndex].gid state.allowsCardHitTesting = false - return Effect.send(.setAllowsCardHitTesting(true)) - .delay(for: .milliseconds(300), scheduler: DispatchQueue.main) - .eraseToEffect() + return Effect.publisher { + Effect.send(.setAllowsCardHitTesting(true)) + .delay(for: .milliseconds(300), scheduler: DispatchQueue.main) + } case .binding: return .none diff --git a/EhPanda/View/Migration/MigrationReducer.swift b/EhPanda/View/Migration/MigrationReducer.swift index f91ac749..bc6a4e36 100644 --- a/EhPanda/View/Migration/MigrationReducer.swift +++ b/EhPanda/View/Migration/MigrationReducer.swift @@ -60,9 +60,11 @@ struct MigrationReducer: Reducer { case .dropDatabase: state.databaseState = .loading - return databaseClient.dropDatabase() - .delay(for: .milliseconds(500), scheduler: DispatchQueue.main) - .eraseToEffect().map(Action.dropDatabaseDone) + return Effect.publisher { + databaseClient.dropDatabase() + .delay(for: .milliseconds(500), scheduler: DispatchQueue.main) + .map(Action.dropDatabaseDone) + } case .dropDatabaseDone(let appError): if let appError {