From 5d25617322e0977a986e03e86eca64b664d99535 Mon Sep 17 00:00:00 2001 From: graycreate Date: Mon, 1 Dec 2025 18:59:12 +0800 Subject: [PATCH 1/4] feat: add splash screen with centered logo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add LaunchScreen.storyboard with: - Centered V2er logo (100x100) - SplashBackground color (white/dark gray for light/dark mode) - SplashLogoColor for logo tint (matches Android implementation) Colors match Android app: - Light mode: white background (#FFFFFF), dark logo (#111214) - Dark mode: dark background (#111214), white logo (#FFFFFF) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- V2er.xcodeproj/project.pbxproj | 4 ++ .../SplashBackground.colorset/Contents.json | 38 +++++++++++++ .../SplashLogoColor.colorset/Contents.json | 38 +++++++++++++ .../SplashLogo.imageset/Contents.json | 25 +++++++++ .../SplashLogo.imageset/splash_logo.png | Bin 0 -> 33239 bytes V2er/LaunchScreen.storyboard | 51 ++++++++++++++++++ 6 files changed, 156 insertions(+) create mode 100644 V2er/Assets.xcassets/Colors/SplashBackground.colorset/Contents.json create mode 100644 V2er/Assets.xcassets/Colors/SplashLogoColor.colorset/Contents.json create mode 100644 V2er/Assets.xcassets/SplashLogo.imageset/Contents.json create mode 100644 V2er/Assets.xcassets/SplashLogo.imageset/splash_logo.png create mode 100644 V2er/LaunchScreen.storyboard diff --git a/V2er.xcodeproj/project.pbxproj b/V2er.xcodeproj/project.pbxproj index 6c6530e..95403fb 100644 --- a/V2er.xcodeproj/project.pbxproj +++ b/V2er.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 0B1A2B3C4D5E6F7081920A01 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0B1A2B3C4D5E6F7081920A02 /* LaunchScreen.storyboard */; }; 1AEBC3AC5DAA63523F5448F5 /* RichContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E205F350A3537A3E41B1AFC3 /* RichContentView.swift */; }; 28B24CA92EA3460D00F82B2A /* BalanceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28B24CA82EA3460D00F82B2A /* BalanceView.swift */; }; 28B24CAB2EA3561400F82B2A /* OnlineStatsInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28B24CAA2EA3561400F82B2A /* OnlineStatsInfo.swift */; }; @@ -184,6 +185,7 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + 0B1A2B3C4D5E6F7081920A02 /* LaunchScreen.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = LaunchScreen.storyboard; sourceTree = ""; }; 28B24CA82EA3460D00F82B2A /* BalanceView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BalanceView.swift; sourceTree = ""; }; 28B24CAA2EA3561400F82B2A /* OnlineStatsInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnlineStatsInfo.swift; sourceTree = ""; }; 31C4B81E79369CDE4880B773 /* RichContentView+Preview.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "RichContentView+Preview.swift"; path = "V2er/Sources/RichView/Views/RichContentView+Preview.swift"; sourceTree = ""; }; @@ -530,6 +532,7 @@ 5D436FEE24791C2D00FFA37E /* Assets.xcassets */, 5DEC5D662730F25800B34BC5 /* www */, 5D436FF624791C2D00FFA37E /* Info.plist */, + 0B1A2B3C4D5E6F7081920A02 /* LaunchScreen.storyboard */, 5D436FF024791C2D00FFA37E /* Preview Content */, ); path = V2er; @@ -937,6 +940,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 0B1A2B3C4D5E6F7081920A01 /* LaunchScreen.storyboard in Resources */, 5D436FF224791C2D00FFA37E /* Preview Assets.xcassets in Resources */, 5DEC5D7E2730F29000B34BC5 /* image_holder_loading.gif in Resources */, 5DEC5D732730F28F00B34BC5 /* bootstrap.min.css in Resources */, diff --git a/V2er/Assets.xcassets/Colors/SplashBackground.colorset/Contents.json b/V2er/Assets.xcassets/Colors/SplashBackground.colorset/Contents.json new file mode 100644 index 0000000..903bdcd --- /dev/null +++ b/V2er/Assets.xcassets/Colors/SplashBackground.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "1.000", + "green" : "1.000", + "red" : "1.000" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.078", + "green" : "0.071", + "red" : "0.067" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/V2er/Assets.xcassets/Colors/SplashLogoColor.colorset/Contents.json b/V2er/Assets.xcassets/Colors/SplashLogoColor.colorset/Contents.json new file mode 100644 index 0000000..e6910b6 --- /dev/null +++ b/V2er/Assets.xcassets/Colors/SplashLogoColor.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.078", + "green" : "0.071", + "red" : "0.067" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "1.000", + "green" : "1.000", + "red" : "1.000" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/V2er/Assets.xcassets/SplashLogo.imageset/Contents.json b/V2er/Assets.xcassets/SplashLogo.imageset/Contents.json new file mode 100644 index 0000000..ceabb10 --- /dev/null +++ b/V2er/Assets.xcassets/SplashLogo.imageset/Contents.json @@ -0,0 +1,25 @@ +{ + "images" : [ + { + "filename" : "splash_logo.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/V2er/Assets.xcassets/SplashLogo.imageset/splash_logo.png b/V2er/Assets.xcassets/SplashLogo.imageset/splash_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..899af704b208b25573f13bc12163edcb0df98fa8 GIT binary patch literal 33239 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%0A{2z-OQN|12@ij;^pz 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 44c830c57aaa7438efade5745436c1e08ce39268 Mon Sep 17 00:00:00 2001 From: graycreate Date: Mon, 1 Dec 2025 19:11:31 +0800 Subject: [PATCH 2/4] fix: address Copilot review comments for splash screen MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Remove hardcoded RGB color values from storyboard resources section - Use named color references only (SplashBackground, SplashLogoColor) - Update image resource dimensions to match display size (100x100) - Simplify image asset configuration (single universal image) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../Assets.xcassets/SplashLogo.imageset/Contents.json | 11 +---------- V2er/LaunchScreen.storyboard | 10 +++------- 2 files changed, 4 insertions(+), 17 deletions(-) diff --git a/V2er/Assets.xcassets/SplashLogo.imageset/Contents.json b/V2er/Assets.xcassets/SplashLogo.imageset/Contents.json index ceabb10..fbfc095 100644 --- a/V2er/Assets.xcassets/SplashLogo.imageset/Contents.json +++ b/V2er/Assets.xcassets/SplashLogo.imageset/Contents.json @@ -2,16 +2,7 @@ "images" : [ { "filename" : "splash_logo.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" + "idiom" : "universal" } ], "info" : { diff --git a/V2er/LaunchScreen.storyboard b/V2er/LaunchScreen.storyboard index 2c2c71b..0cd162b 100644 --- a/V2er/LaunchScreen.storyboard +++ b/V2er/LaunchScreen.storyboard @@ -40,12 +40,8 @@ - - - - - - - + + + From 6efdd2d1bdbe83d6a5895a86b3f47720c12826c2 Mon Sep 17 00:00:00 2001 From: graycreate Date: Mon, 1 Dec 2025 21:58:04 +0800 Subject: [PATCH 3/4] feat: implement SwiftUI splash screen with typewriter animation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Replace LaunchScreen.storyboard with SwiftUI SplashView - Add vector PDF logo from Android project for better scaling - Support light/dark mode with adaptive colors - Add TypewriterView with ease-out animation for slogan - Slogan: "Way to explore" with monospaced font - Logo size: 200x200, centered with slogan below 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- V2er.xcodeproj/project.pbxproj | 20 ++++- .../SplashLogoColor.colorset/Contents.json | 38 -------- .../SplashLogo.imageset/Contents.json | 2 +- .../SplashLogo.imageset/splash_logo.pdf | Bin 0 -> 1437 bytes .../SplashLogo.imageset/splash_logo.png | Bin 33239 -> 0 bytes V2er/General/RootView.swift | 34 +++++--- V2er/LaunchScreen.storyboard | 47 ---------- V2er/State/DataFlow/State/GlobalState.swift | 1 + V2er/View/Splash/SplashView.swift | 64 ++++++++++++++ V2er/View/Splash/TypewriterView.swift | 82 ++++++++++++++++++ 10 files changed, 186 insertions(+), 102 deletions(-) delete mode 100644 V2er/Assets.xcassets/Colors/SplashLogoColor.colorset/Contents.json create mode 100644 V2er/Assets.xcassets/SplashLogo.imageset/splash_logo.pdf delete mode 100644 V2er/Assets.xcassets/SplashLogo.imageset/splash_logo.png delete mode 100644 V2er/LaunchScreen.storyboard create mode 100644 V2er/View/Splash/SplashView.swift create mode 100644 V2er/View/Splash/TypewriterView.swift diff --git a/V2er.xcodeproj/project.pbxproj b/V2er.xcodeproj/project.pbxproj index 95403fb..7ab68e0 100644 --- a/V2er.xcodeproj/project.pbxproj +++ b/V2er.xcodeproj/project.pbxproj @@ -7,7 +7,8 @@ objects = { /* Begin PBXBuildFile section */ - 0B1A2B3C4D5E6F7081920A01 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0B1A2B3C4D5E6F7081920A02 /* LaunchScreen.storyboard */; }; + 0B1A2B3C4D5E6F7081920A03 /* SplashView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B1A2B3C4D5E6F7081920A04 /* SplashView.swift */; }; + 0B1A2B3C4D5E6F7081920A07 /* TypewriterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B1A2B3C4D5E6F7081920A08 /* TypewriterView.swift */; }; 1AEBC3AC5DAA63523F5448F5 /* RichContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E205F350A3537A3E41B1AFC3 /* RichContentView.swift */; }; 28B24CA92EA3460D00F82B2A /* BalanceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28B24CA82EA3460D00F82B2A /* BalanceView.swift */; }; 28B24CAB2EA3561400F82B2A /* OnlineStatsInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28B24CAA2EA3561400F82B2A /* OnlineStatsInfo.swift */; }; @@ -185,7 +186,8 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ - 0B1A2B3C4D5E6F7081920A02 /* LaunchScreen.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = LaunchScreen.storyboard; sourceTree = ""; }; + 0B1A2B3C4D5E6F7081920A04 /* SplashView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SplashView.swift; sourceTree = ""; }; + 0B1A2B3C4D5E6F7081920A08 /* TypewriterView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TypewriterView.swift; sourceTree = ""; }; 28B24CA82EA3460D00F82B2A /* BalanceView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BalanceView.swift; sourceTree = ""; }; 28B24CAA2EA3561400F82B2A /* OnlineStatsInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnlineStatsInfo.swift; sourceTree = ""; }; 31C4B81E79369CDE4880B773 /* RichContentView+Preview.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "RichContentView+Preview.swift"; path = "V2er/Sources/RichView/Views/RichContentView+Preview.swift"; sourceTree = ""; }; @@ -532,7 +534,6 @@ 5D436FEE24791C2D00FFA37E /* Assets.xcassets */, 5DEC5D662730F25800B34BC5 /* www */, 5D436FF624791C2D00FFA37E /* Info.plist */, - 0B1A2B3C4D5E6F7081920A02 /* LaunchScreen.storyboard */, 5D436FF024791C2D00FFA37E /* Preview Content */, ); path = V2er; @@ -721,6 +722,15 @@ path = FeedDetail; sourceTree = ""; }; + 0B1A2B3C4D5E6F7081920A06 /* Splash */ = { + isa = PBXGroup; + children = ( + 0B1A2B3C4D5E6F7081920A04 /* SplashView.swift */, + 0B1A2B3C4D5E6F7081920A08 /* TypewriterView.swift */, + ); + path = Splash; + sourceTree = ""; + }; 5DE5B4C826845F4F00569684 /* View */ = { isa = PBXGroup; children = ( @@ -737,6 +747,7 @@ 5D1D7B8526FC9AF6008E0C08 /* Login */, 5D843E9626A46CB800C47D95 /* Message */, 5D179BFD2496F6EC00E40E90 /* Widget */, + 0B1A2B3C4D5E6F7081920A06 /* Splash */, 4EC32AF129D818FC003A3BD4 /* WebBrowserView.swift */, ); path = View; @@ -940,7 +951,6 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 0B1A2B3C4D5E6F7081920A01 /* LaunchScreen.storyboard in Resources */, 5D436FF224791C2D00FFA37E /* Preview Assets.xcassets in Resources */, 5DEC5D7E2730F29000B34BC5 /* image_holder_loading.gif in Resources */, 5DEC5D732730F28F00B34BC5 /* bootstrap.min.css in Resources */, @@ -979,6 +989,8 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 0B1A2B3C4D5E6F7081920A03 /* SplashView.swift in Sources */, + 0B1A2B3C4D5E6F7081920A07 /* TypewriterView.swift in Sources */, 5D73FBDA27284ADB004558E9 /* RichText.swift in Sources */, 5D2DD00A26FB443D0001C85A /* GlobalActions.swift in Sources */, 5D71DF57247C153C00B53ED4 /* ExplorePage.swift in Sources */, diff --git a/V2er/Assets.xcassets/Colors/SplashLogoColor.colorset/Contents.json b/V2er/Assets.xcassets/Colors/SplashLogoColor.colorset/Contents.json deleted file mode 100644 index e6910b6..0000000 --- a/V2er/Assets.xcassets/Colors/SplashLogoColor.colorset/Contents.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0.078", - "green" : "0.071", - "red" : "0.067" - } - }, - "idiom" : "universal" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "1.000", - "green" : "1.000", - "red" : "1.000" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/V2er/Assets.xcassets/SplashLogo.imageset/Contents.json b/V2er/Assets.xcassets/SplashLogo.imageset/Contents.json index fbfc095..b8db323 100644 --- a/V2er/Assets.xcassets/SplashLogo.imageset/Contents.json +++ b/V2er/Assets.xcassets/SplashLogo.imageset/Contents.json @@ -1,7 +1,7 @@ { "images" : [ { - "filename" : "splash_logo.png", + "filename" : "splash_logo.pdf", "idiom" : "universal" } ], diff --git a/V2er/Assets.xcassets/SplashLogo.imageset/splash_logo.pdf b/V2er/Assets.xcassets/SplashLogo.imageset/splash_logo.pdf new file mode 100644 index 0000000000000000000000000000000000000000..f7aedae4519420cad7066bab37aba8a2afd63990 GIT binary patch literal 1437 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%0A{2z-OQN|12@ij;^pz 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 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/V2er/State/DataFlow/State/GlobalState.swift b/V2er/State/DataFlow/State/GlobalState.swift index 95901a1..4e0ce0c 100644 --- a/V2er/State/DataFlow/State/GlobalState.swift +++ b/V2er/State/DataFlow/State/GlobalState.swift @@ -19,6 +19,7 @@ struct GlobalState: FluxState { var lastSelectedTab: TabId = .none var scrollTopTab: TabId = .none var toast = Toast() + var launchFinished: Bool = false static var account: AccountInfo? { AccountState.getAccount() diff --git a/V2er/View/Splash/SplashView.swift b/V2er/View/Splash/SplashView.swift new file mode 100644 index 0000000..cd212ba --- /dev/null +++ b/V2er/View/Splash/SplashView.swift @@ -0,0 +1,64 @@ +// +// SplashView.swift +// V2er +// +// Created by Claude on 2024/12/1. +// + +import SwiftUI + +struct SplashView: View { + @EnvironmentObject private var store: Store + @Environment(\.colorScheme) private var colorScheme + + @State private var showSlogan = false + + private let slogan = "Way to explore" + + // Logo color adapts to color scheme (matches Android) + private var logoColor: Color { + colorScheme == .dark ? .white : Color(red: 0.067, green: 0.071, blue: 0.078) + } + + var body: some View { + ZStack { + // Background color - matches Android implementation + Color("SplashBackground") + .ignoresSafeArea() + + // Logo - vector PDF with template rendering (fixed position) + Image("SplashLogo") + .renderingMode(.template) + .resizable() + .aspectRatio(contentMode: .fit) + .frame(width: 200, height: 200) + .foregroundColor(logoColor) + + // Slogan with typewriter effect (fixed position below logo) + if showSlogan { + TypewriterView(text: slogan, typingDelay: .milliseconds(35)) + .font(.system(size: 17, weight: .semibold, design: .default)) + .foregroundColor(logoColor.opacity(0.85)) + .offset(y: 74) + } + } + .onAppear { + // Show slogan after a short delay + DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { + showSlogan = true + } + + // Hide splash after animation completes + DispatchQueue.main.asyncAfter(deadline: .now() + 1.2) { + withAnimation(.easeOut(duration: 0.3)) { + store.appState.globalState.launchFinished = true + } + } + } + } +} + +#Preview { + SplashView() + .environmentObject(Store.shared) +} diff --git a/V2er/View/Splash/TypewriterView.swift b/V2er/View/Splash/TypewriterView.swift new file mode 100644 index 0000000..386e0cf --- /dev/null +++ b/V2er/View/Splash/TypewriterView.swift @@ -0,0 +1,82 @@ +// +// TypewriterView.swift +// V2er +// +// Created by Claude on 2024/12/1. +// + +import SwiftUI + +struct TypewriterView: View { + var text: String + var typingDelay: Duration = .milliseconds(50) + var easeIn: Bool = true + + @State private var animatedText: AttributedString = "" + @State private var typingTask: Task? + @State private var hasAppeared = false + + var body: some View { + Text(animatedText) + .onChange(of: text) { _ in + if hasAppeared { + animateText() + } + } + .onAppear() { + animateText() + hasAppeared = true + } + } + + private func animateText() { + typingTask?.cancel() + + typingTask = Task { + let defaultAttributes = AttributeContainer() + animatedText = AttributedString(text, + attributes: defaultAttributes.foregroundColor(.clear) + ) + + let totalChars = text.count + var charIndex = 0 + var index = animatedText.startIndex + + while index < animatedText.endIndex { + try Task.checkCancellation() + + // Update the style + animatedText[animatedText.startIndex...index] + .setAttributes(defaultAttributes) + + // Calculate delay with ease-out effect (starts fast, slows down) + let delay: Duration + if easeIn && totalChars > 1 { + // Ease-out: start fast, end slow - more natural typing feel + let progress = Double(charIndex) / Double(totalChars - 1) + let easeOutProgress = 1 - pow(1 - progress, 2) // quadratic ease-out + let baseDelay = Double(typingDelay.components.attoseconds) / 1_000_000_000_000_000_000 + let minDelay = baseDelay * 0.6 + let maxDelay = baseDelay * 1.5 + let currentDelay = minDelay + (maxDelay - minDelay) * easeOutProgress + delay = .milliseconds(Int(currentDelay * 1000)) + } else { + delay = typingDelay + } + + // Wait + try await Task.sleep(for: delay) + + // Advance the index, character by character + index = animatedText.index(afterCharacter: index) + charIndex += 1 + } + } + } +} + +#Preview { + TypewriterView(text: "Way to explore") + .font(.title) + .padding() +} From cffc8a2d894cb5bb08c4ce38877f28fb851627fc Mon Sep 17 00:00:00 2001 From: graycreate Date: Mon, 1 Dec 2025 22:05:45 +0800 Subject: [PATCH 4/4] fix: address Copilot review comments for splash screen MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Use runInMain utility instead of DispatchQueue.main.asyncAfter - Use LaunchFinishedAction and Redux pattern instead of direct state mutation - Change TypewriterView Task type from Task? to Task? since the task doesn't throw errors 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- V2er/State/DataFlow/Actions/GlobalActions.swift | 7 +++++++ V2er/View/Splash/SplashView.swift | 8 +++----- V2er/View/Splash/TypewriterView.swift | 6 +++--- 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/V2er/State/DataFlow/Actions/GlobalActions.swift b/V2er/State/DataFlow/Actions/GlobalActions.swift index 5248d35..557f343 100644 --- a/V2er/State/DataFlow/Actions/GlobalActions.swift +++ b/V2er/State/DataFlow/Actions/GlobalActions.swift @@ -40,6 +40,10 @@ struct ShowToastAction: Action { var icon: String = .empty } +struct LaunchFinishedAction: Action { + var target: Reducer = R +} + func globalStateReducer(_ state: GlobalState, _ action: Action?) -> (GlobalState, Action?) { var state = state @@ -50,6 +54,9 @@ func globalStateReducer(_ state: GlobalState, _ action: Action?) -> (GlobalState state.toast.icon = action.icon state.toast.isPresented = true break + case _ as LaunchFinishedAction: + state.launchFinished = true + break default: break } diff --git a/V2er/View/Splash/SplashView.swift b/V2er/View/Splash/SplashView.swift index cd212ba..f3a01c9 100644 --- a/V2er/View/Splash/SplashView.swift +++ b/V2er/View/Splash/SplashView.swift @@ -44,15 +44,13 @@ struct SplashView: View { } .onAppear { // Show slogan after a short delay - DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { + runInMain(delay: 300) { showSlogan = true } // Hide splash after animation completes - DispatchQueue.main.asyncAfter(deadline: .now() + 1.2) { - withAnimation(.easeOut(duration: 0.3)) { - store.appState.globalState.launchFinished = true - } + runInMain(delay: 1200) { + store.dispatch(LaunchFinishedAction(), animation: .easeOut(duration: 0.3)) } } } diff --git a/V2er/View/Splash/TypewriterView.swift b/V2er/View/Splash/TypewriterView.swift index 386e0cf..f3b52a8 100644 --- a/V2er/View/Splash/TypewriterView.swift +++ b/V2er/View/Splash/TypewriterView.swift @@ -13,7 +13,7 @@ struct TypewriterView: View { var easeIn: Bool = true @State private var animatedText: AttributedString = "" - @State private var typingTask: Task? + @State private var typingTask: Task? @State private var hasAppeared = false var body: some View { @@ -43,7 +43,7 @@ struct TypewriterView: View { var index = animatedText.startIndex while index < animatedText.endIndex { - try Task.checkCancellation() + guard !Task.isCancelled else { return } // Update the style animatedText[animatedText.startIndex...index] @@ -65,7 +65,7 @@ struct TypewriterView: View { } // Wait - try await Task.sleep(for: delay) + try? await Task.sleep(for: delay) // Advance the index, character by character index = animatedText.index(afterCharacter: index)