From 46027cda6dd8bfa6066b76eaa6a08a1cb4b321cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=9F=E3=81=A4=E3=81=9D=E3=82=99=E3=81=86?= Date: Thu, 6 May 2021 07:21:44 +0800 Subject: [PATCH] Building NewDawn greeting feature --- EhPanda.xcodeproj/project.pbxproj | 8 +- EhPanda/App/Tools/Parser.swift | 79 ++++++++++++++ EhPanda/Models/Misc.swift | 21 ++++ EhPanda/View/Detail/ArchiveView.swift | 19 ++-- EhPanda/View/Setting/SettingView.swift | 2 +- EhPanda/View/Tools/NewDawnView.swift | 136 +++++++++++++++++++++++++ EhPanda/View/Tools/RatingView.swift | 2 +- 7 files changed, 252 insertions(+), 15 deletions(-) create mode 100644 EhPanda/View/Tools/NewDawnView.swift diff --git a/EhPanda.xcodeproj/project.pbxproj b/EhPanda.xcodeproj/project.pbxproj index 3ff5f99a..ed296f4e 100644 --- a/EhPanda.xcodeproj/project.pbxproj +++ b/EhPanda.xcodeproj/project.pbxproj @@ -25,6 +25,7 @@ ABA732D925A8018A00B3D9AB /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABA732D825A8018A00B3D9AB /* Extensions.swift */; }; ABA732DF25A852D800B3D9AB /* Filter.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABA732DE25A852D800B3D9AB /* Filter.swift */; }; ABC1FAB6264152C800A9F352 /* StoreAccessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABC1FAB5264152C800A9F352 /* StoreAccessor.swift */; }; + ABC1FAB82642C37D00A9F352 /* NewDawnView.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABC1FAB72642C37D00A9F352 /* NewDawnView.swift */; }; ABC3C7852593699B00E0C11B /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = ABC3C7692593699A00E0C11B /* Assets.xcassets */; }; ABC3C7862593699B00E0C11B /* Utility.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABC3C76A2593699A00E0C11B /* Utility.swift */; }; ABC3C7872593699B00E0C11B /* EhPandaApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABC3C76B2593699A00E0C11B /* EhPandaApp.swift */; }; @@ -95,6 +96,7 @@ ABA732D825A8018A00B3D9AB /* Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Extensions.swift; sourceTree = ""; }; ABA732DE25A852D800B3D9AB /* Filter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Filter.swift; sourceTree = ""; }; ABC1FAB5264152C800A9F352 /* StoreAccessor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoreAccessor.swift; sourceTree = ""; }; + ABC1FAB72642C37D00A9F352 /* NewDawnView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewDawnView.swift; sourceTree = ""; }; ABC3C7542593696C00E0C11B /* EhPanda.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = EhPanda.app; sourceTree = BUILT_PRODUCTS_DIR; }; ABC3C7692593699A00E0C11B /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; ABC3C76A2593699A00E0C11B /* Utility.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Utility.swift; sourceTree = ""; }; @@ -298,6 +300,7 @@ children = ( ABF45ACE25F3313D00ECB568 /* MangaSummaryRow.swift */, ABF45ACC25F3313D00ECB568 /* AlertView.swift */, + ABC1FAB72642C37D00A9F352 /* NewDawnView.swift */, ABF45AC825F3313D00ECB568 /* Comment.swift */, ABF45ACD25F3313D00ECB568 /* Placeholder.swift */, ABF45AC925F3313D00ECB568 /* KRefreshScrollView.swift */, @@ -501,6 +504,7 @@ ABF45AE625F3313D00ECB568 /* KRefreshScrollView.swift in Sources */, ABF313A525B1AB6600D47A2F /* Misc.swift in Sources */, ABA732DF25A852D800B3D9AB /* Filter.swift in Sources */, + ABC1FAB82642C37D00A9F352 /* NewDawnView.swift in Sources */, ABF45AE825F3313D00ECB568 /* LinkedText.swift in Sources */, ABF45AE525F3313D00ECB568 /* Comment.swift in Sources */, ABC1FAB6264152C800A9F352 /* StoreAccessor.swift in Sources */, @@ -661,7 +665,7 @@ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_ENTITLEMENTS = EhPanda/EhPanda.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 44; + CURRENT_PROJECT_VERSION = 45; DEVELOPMENT_ASSET_PATHS = ""; DEVELOPMENT_TEAM = 9SKQ7QTZ74; ENABLE_PREVIEWS = YES; @@ -688,7 +692,7 @@ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_ENTITLEMENTS = EhPanda/EhPanda.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 44; + CURRENT_PROJECT_VERSION = 45; DEVELOPMENT_ASSET_PATHS = ""; DEVELOPMENT_TEAM = 9SKQ7QTZ74; ENABLE_PREVIEWS = YES; diff --git a/EhPanda/App/Tools/Parser.swift b/EhPanda/App/Tools/Parser.swift index fbc681e1..d5f5af3a 100644 --- a/EhPanda/App/Tools/Parser.swift +++ b/EhPanda/App/Tools/Parser.swift @@ -593,6 +593,85 @@ struct Parser { } extension Parser { + // MARK: Greeting + static func parseGreeting(_ doc: HTMLDocument) throws -> Greeting { + func trimString(_ string: String) -> String { + string + .replacingOccurrences(of: ",", with: "") + .replacingOccurrences(of: "!", with: "") + .replacingOccurrences(of: "and", with: "") + .trimmingCharacters(in: .whitespacesAndNewlines) + } + + func trimInt(_ value: String) -> Int? { + Int( + value + .replacingOccurrences(of: ",", with: "") + .replacingOccurrences(of: " ", with: "") + ) + } + + guard let node = doc.at_xpath("//div [@id='eventpane']") + else { throw AppError.parseFailed } + + var greeting = Greeting() + for link in node.xpath("//p") { + guard var text = link.text, + text.contains("You gain") == true + else { continue } + + var gainedValues = [String]() + for strongLink in link.xpath("//strong") { + if let strongText = strongLink.text { + gainedValues.append(strongText) + } + } + + var gainedTypes = [String]() + for value in gainedValues { + guard let range = text.range(of: value) else { break } + let removeText = String(text.prefix(upTo: range.upperBound)) + + if value != gainedValues.first { + gainedTypes.append(trimString(removeText)) + } + + text = text.replacingOccurrences(of: removeText, with: "") + + if value == gainedValues.last { + gainedTypes.append(trimString(text)) + } + } + + let gainedIntValues = gainedValues.compactMap { trimInt($0) } + guard gainedIntValues.count == gainedTypes.count + else { throw AppError.parseFailed } + + for (index, type) in gainedTypes.enumerated() { + let value = gainedIntValues[index] + switch type { + case "EXP": + greeting.gainedEXP = value + case "Credits": + greeting.gainedCredits = value + case "GP": + greeting.gainedGP = value + case "Hath": + greeting.gainedHath = value + default: + break + } + } + break + } + + guard !greeting.gainedNothing + else { throw AppError.parseFailed } + + greeting.updateTime = Date() + return greeting + } + // MARK: APIKey static func parseAPIKey(_ doc: HTMLDocument) throws -> APIKey { var tmpKey: APIKey? diff --git a/EhPanda/Models/Misc.swift b/EhPanda/Models/Misc.swift index a421e2ea..4b69f424 100644 --- a/EhPanda/Models/Misc.swift +++ b/EhPanda/Models/Misc.swift @@ -5,6 +5,8 @@ // Created by 荒木辰造 on R 3/01/15. // +import Foundation + typealias FavoritesIndex = Int typealias Depth = Int typealias Percentage = Int @@ -31,3 +33,22 @@ struct AssociatedItem { var pageNum = PageNumber() var mangas: [Manga] } + +struct Greeting: Codable { + var gainedEXP: Int? + var gainedCredits: Int? + var gainedGP: Int? + var gainedHath: Int? + var updateTime: Date? + + var gainedNothing: Bool { + [ + gainedEXP, + gainedCredits, + gainedGP, + gainedHath + ] + .compactMap({ $0 }) + .isEmpty + } +} diff --git a/EhPanda/View/Detail/ArchiveView.swift b/EhPanda/View/Detail/ArchiveView.swift index 604bd8ac..a9a052a4 100644 --- a/EhPanda/View/Detail/ArchiveView.swift +++ b/EhPanda/View/Detail/ArchiveView.swift @@ -54,8 +54,7 @@ struct ArchiveView: View, StoreAccessor { Spacer() - if isSameAccount, - let galleryPoints = currentGP, + if let galleryPoints = currentGP, let credits = currentCredits { BalanceView(galleryPoints: galleryPoints, credits: credits) @@ -161,9 +160,7 @@ private extension ArchiveView { func fetchMangaArchive() { store.dispatch(.fetchMangaArchive(gid: gid)) - if currentGP == nil - || currentCredits == nil - { + if currentGP == nil || currentCredits == nil, isSameAccount { store.dispatch(.fetchMangaArchiveFunds(gid: gid)) } } @@ -179,7 +176,7 @@ private struct ArchiveGrid: View { || archive.gpPrice == "N/A" } private var disabledColor: Color { - Color.gray.opacity(0.5) + .gray.opacity(0.5) } private var fileSizeColor: Color { if disabled { @@ -298,19 +295,19 @@ private struct DownloadButton: View { private extension DownloadButton { var textColor: Color { if isDisabled { - return Color.white.opacity(0.5) + return .white.opacity(0.5) } else { return isPressed - ? Color.white.opacity(0.5) + ? .white.opacity(0.5) : .white } } var backgroundColor: Color { if isDisabled { - return Color.accentColor.opacity(0.5) + return .accentColor.opacity(0.5) } else { return isPressed - ? Color.accentColor.opacity(0.5) + ? .accentColor.opacity(0.5) : .accentColor } } @@ -340,7 +337,7 @@ private extension DownloadButton { } } -private struct ArchiveView_Previews: PreviewProvider { +struct ArchiveView_Previews: PreviewProvider { static var previews: some View { let store = Store() var user = User.empty diff --git a/EhPanda/View/Setting/SettingView.swift b/EhPanda/View/Setting/SettingView.swift index 801a0e04..f64b3727 100644 --- a/EhPanda/View/Setting/SettingView.swift +++ b/EhPanda/View/Setting/SettingView.swift @@ -231,7 +231,7 @@ enum SettingViewSheetState: Identifiable { case webviewMyTags } -private struct SettingView_Previews: PreviewProvider { +struct SettingView_Previews: PreviewProvider { static var previews: some View { let store = Store() store.appState.settings.setting = Setting() diff --git a/EhPanda/View/Tools/NewDawnView.swift b/EhPanda/View/Tools/NewDawnView.swift new file mode 100644 index 00000000..cbc26e4b --- /dev/null +++ b/EhPanda/View/Tools/NewDawnView.swift @@ -0,0 +1,136 @@ +// +// NewDawnView.swift +// EhPanda +// +// Created by 荒木辰造 on R 3/05/05. +// + +import SwiftUI + +struct NewDawnView: View { + @Environment(\.colorScheme) private var colorScheme + @State private var rotationAngle: Double = 0 + @State private var timer = Timer + .publish( + every: 1/10, + on: .main, + in: .common + ) + .autoconnect() + + private var reversePrimary: Color { + colorScheme == .light ? .white : .black + } + private let offset = screenW * 0.2 + + var body: some View { + ZStack { + LinearGradient( + gradient: Gradient( + colors: [Color(.systemTeal), Color(.systemIndigo)] + ), + startPoint: .top, + endPoint: .bottom + ) + .ignoresSafeArea() + VStack { + HStack { + Spacer() + SunView() + .rotationEffect(Angle(degrees: rotationAngle)) + .offset(x: offset, y: -offset) + } + Spacer() + } + .ignoresSafeArea() + VStack(spacing: 10) { + HStack { + Text("It is the dawn of a new day!") + .fontWeight(.bold) + .font(.largeTitle) + .foregroundColor(reversePrimary) + Spacer() + } + HStack { + Text("Reflecting on your journey so far, you find that you are a little wiser.") + .fontWeight(.bold) + .font(.title2) + .foregroundColor(reversePrimary) + Spacer() + } + .padding(.bottom, 50) + HStack { + Text("You gain 30 EXP, 10,393 Credits, 10,000 GP and 11 Hath!") + .fontWeight(.bold) + .font(.title3) + .foregroundColor(reversePrimary) + Spacer() + } + } + .padding() + } + .onReceive(timer, perform: onReceiveTimer) + } +} + +private extension NewDawnView { + func onReceiveTimer(_: Date) { + withAnimation { + rotationAngle += 1 + } + } +} + +private struct SunView: View { + private let width = screenW * 0.75 + private var offset: CGFloat { width / 2 + 70 } + private var evenOffset: CGFloat { offset / sqrt(2) } + private var sizes: [CGSize] { + [ + CGSize(width: 0, height: -offset), + CGSize(width: evenOffset, height: -evenOffset), + CGSize(width: offset, height: 0), + CGSize(width: evenOffset, height: evenOffset), + CGSize(width: 0, height: offset), + CGSize(width: -evenOffset, height: evenOffset), + CGSize(width: -offset, height: 0), + CGSize(width: -evenOffset, height: -evenOffset) + ] + } + private var degrees: [Double] = [ + 0, 45, 90, 135, 180, 225, 270, 315 + ] + + var body: some View { + ZStack { + Circle() + .foregroundColor(.yellow) + .frame(width: width, height: width) + ForEach(0..<8, id: \.self) { index in + SunBeamView() + .rotationEffect(Angle(degrees: degrees[index])) + .offset(sizes[index]) + } + } + } +} + +private struct SunBeamView: View { + private let width = screenW * 0.05 + private var height: CGFloat { + width * 5 + } + + var body: some View { + Rectangle() + .foregroundColor(.yellow) + .frame(width: width, height: height) + .cornerRadius(5) + } +} + +struct NewDawnView_Previews: PreviewProvider { + static var previews: some View { + NewDawnView() + } +} diff --git a/EhPanda/View/Tools/RatingView.swift b/EhPanda/View/Tools/RatingView.swift index 3d9346e5..d21e40f5 100644 --- a/EhPanda/View/Tools/RatingView.swift +++ b/EhPanda/View/Tools/RatingView.swift @@ -77,7 +77,7 @@ private extension RatingView { } } -private struct RatingView_Previews: PreviewProvider { +struct RatingView_Previews: PreviewProvider { static let values: [Float] = { var tmpArray: [Float] = [] for value in stride(from: 0.0, through: 5.0, by: 0.5) {