diff --git a/Sources/SwiftNEW/Animations/FloatingParticlesView.swift b/Sources/SwiftNEW/Animations/FloatingParticlesView.swift new file mode 100644 index 0000000..9b8b2f7 --- /dev/null +++ b/Sources/SwiftNEW/Animations/FloatingParticlesView.swift @@ -0,0 +1,37 @@ +// +// FloatingParticlesView.swift +// SwiftNEW +// + +import SwiftUI + +struct FloatingParticlesView: View { + private let particleCount = 30 + private let particleColors: [Color] = [ + .red, .orange, .yellow, .green, .mint, .teal, .cyan, .blue, .indigo, .purple, .pink + ] + + var body: some View { + TimelineView(.animation) { timeline in + Canvas { context, size in + let time = timeline.date.timeIntervalSinceReferenceDate + for i in 0.. geometry.size.height { - snowflakes[index].y = -snowflakes[index].size - snowflakes[index].x = CGFloat.random(in: 0...geometry.size.width) - } } } } + .allowsHitTesting(false) + .accessibilityHidden(true) } } diff --git a/Sources/SwiftNEW/Extensions/SwiftNEW+Functions.swift b/Sources/SwiftNEW/Extensions/SwiftNEW+Functions.swift index 3efbc56..e4eb925 100644 --- a/Sources/SwiftNEW/Extensions/SwiftNEW+Functions.swift +++ b/Sources/SwiftNEW/Extensions/SwiftNEW+Functions.swift @@ -42,31 +42,29 @@ extension SwiftNEW { } public func loadData() { - if data.contains("http") { - // MARK: Remote Data - let url = URL(string: data) - URLSession.shared.dataTask(with: url!) { data, response, error in - if let data = data { - do { - let decoder = JSONDecoder() - items = try decoder.decode([Vmodel].self, from: data) - self.loading = false - } catch { - print(error) - } + let source = data + Task { + do { + let decoded: [Vmodel] + if source.contains("http") { + // MARK: Remote Data + guard let url = URL(string: source) else { return } + let (responseData, _) = try await URLSession.shared.data(from: url) + decoded = try JSONDecoder().decode([Vmodel].self, from: responseData) + } else { + // MARK: Local Data + guard let url = Bundle.main.url(forResource: source, withExtension: "json") else { return } + decoded = try await Task.detached { + let fileData = try Data(contentsOf: url) + return try JSONDecoder().decode([Vmodel].self, from: fileData) + }.value } - }.resume() - } else { - // MARK: Local Data - if let url = Bundle.main.url(forResource: data, withExtension: "json") { - do { - let data = try Data(contentsOf: url) - let decoder = JSONDecoder() - items = try decoder.decode([Vmodel].self, from: data) + await MainActor.run { + items = decoded loading = false - } catch { - print("error: \(error)") } + } catch { + print("SwiftNEW loadData error: \(error)") } } } diff --git a/Sources/SwiftNEW/Localizable.xcstrings b/Sources/SwiftNEW/Localizable.xcstrings index 3dcc28e..bfa2798 100644 --- a/Sources/SwiftNEW/Localizable.xcstrings +++ b/Sources/SwiftNEW/Localizable.xcstrings @@ -70,7 +70,7 @@ "ar" : { "stringUnit" : { "state" : "translated", - "value" : "الإستعمالات السابقة" + "value" : "التحديثات السابقة" } }, "de" : { @@ -123,7 +123,7 @@ "ar" : { "stringUnit" : { "state" : "translated", - "value" : "...جاري التحميل" + "value" : "جاري التحميل..." } }, "de" : { @@ -229,7 +229,7 @@ "ar" : { "stringUnit" : { "state" : "translated", - "value" : "إعرض الإستعمالات السابقة" + "value" : "إعرض التحديثات السابقة" } }, "de" : { @@ -329,55 +329,55 @@ } } }, - "Version" : { + "Version %@" : { "extractionState" : "manual", "localizations" : { "ar" : { "stringUnit" : { "state" : "translated", - "value" : "الإصدار" + "value" : "الإصدار %@" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Version" + "value" : "Version %@" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Version" + "value" : "Version %@" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "La Version" + "value" : "La Version %@" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "La Versione" + "value" : "La Versione %@" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "版本" + "value" : "版本 %@" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "版本" + "value" : "版本 %@" } }, "zh-HK" : { "stringUnit" : { "state" : "translated", - "value" : "版本" + "value" : "版本 %@" } } } diff --git a/Sources/SwiftNEW/Model.swift b/Sources/SwiftNEW/Model.swift index 498fbbd..20f6b95 100644 --- a/Sources/SwiftNEW/Model.swift +++ b/Sources/SwiftNEW/Model.swift @@ -8,21 +8,15 @@ import SwiftUI // MARK: - Model -public struct Vmodel: Codable, Hashable { +public struct Vmodel: Codable, Hashable, Sendable { var version: String var subVersion: String? var new: [Model] } -public struct Model: Codable, Hashable { +public struct Model: Codable, Hashable, Sendable { var icon: String var title: String var subtitle: String var body: String } -struct Snowflake: Identifiable { - let id: UUID - var x: CGFloat - var y: CGFloat - var size: CGFloat - var speed: CGFloat -} + diff --git a/Sources/SwiftNEW/Styles/MeshView.swift b/Sources/SwiftNEW/Styles/MeshView.swift index a0aafd2..1347e25 100644 --- a/Sources/SwiftNEW/Styles/MeshView.swift +++ b/Sources/SwiftNEW/Styles/MeshView.swift @@ -22,7 +22,8 @@ struct MeshView: View { ]) .ignoresSafeArea(.all) .overlay( - NoiseView(size: 100000) + NoiseView(size: 5000) + .drawingGroup() ) } else { // Fallback on earlier versions diff --git a/Sources/SwiftNEW/SwiftNEW.swift b/Sources/SwiftNEW/SwiftNEW.swift index d9c6557..6710008 100644 --- a/Sources/SwiftNEW/SwiftNEW.swift +++ b/Sources/SwiftNEW/SwiftNEW.swift @@ -23,6 +23,7 @@ public enum SwiftNEWPresentation { public enum SwiftNEWSpecialEffect { case none case christmas + case particles } @available(iOS 15.0, watchOS 8.0, macOS 12.0, tvOS 17.0, *) @@ -47,6 +48,7 @@ public struct SwiftNEW: View { @Binding var specialEffect: SwiftNEWSpecialEffect @Binding var glass: Bool @Binding var presentation: SwiftNEWPresentation + @Binding var showBuild: Bool #if os(iOS) || os(visionOS) public init( @@ -62,7 +64,8 @@ public struct SwiftNEW: View { mesh: Bool? = true, specialEffect: SwiftNEWSpecialEffect? = .none, glass: Bool? = true, - presentation: SwiftNEWPresentation? = .sheet + presentation: SwiftNEWPresentation? = .sheet, + showBuild: Bool? = true ) { _show = show _align = .constant(align ?? .center) @@ -77,6 +80,7 @@ public struct SwiftNEW: View { _specialEffect = .constant(specialEffect ?? .none) _glass = .constant(glass ?? true) _presentation = .constant(presentation ?? .sheet) + _showBuild = .constant(showBuild ?? true) compareVersion() } @@ -94,7 +98,8 @@ public struct SwiftNEW: View { mesh: Binding? = .constant(true), specialEffect: Binding? = .constant(.none), glass: Binding? = .constant(true), - presentation: Binding? = .constant(.sheet) + presentation: Binding? = .constant(.sheet), + showBuild: Binding? = .constant(true) ) { _show = show _align = align ?? .constant(.center) @@ -109,6 +114,7 @@ public struct SwiftNEW: View { _specialEffect = specialEffect ?? .constant(.none) _glass = glass ?? .constant(true) _presentation = presentation ?? .constant(.sheet) + _showBuild = showBuild ?? .constant(true) compareVersion() } #elseif os(macOS) @@ -125,7 +131,8 @@ public struct SwiftNEW: View { mesh: Bool? = true, specialEffect: SwiftNEWSpecialEffect? = .none, glass: Bool? = true, - presentation: SwiftNEWPresentation? = .sheet + presentation: SwiftNEWPresentation? = .sheet, + showBuild: Bool? = true ) { _show = show _align = .constant(align ?? .center) @@ -140,6 +147,7 @@ public struct SwiftNEW: View { _specialEffect = .constant(specialEffect ?? .none) _glass = .constant(glass ?? true) _presentation = .constant(presentation ?? .sheet) + _showBuild = .constant(showBuild ?? true) compareVersion() } @_disfavoredOverload @@ -156,7 +164,8 @@ public struct SwiftNEW: View { mesh: Binding? = .constant(true), specialEffect: Binding? = .constant(.none), glass: Binding? = .constant(true), - presentation: Binding? = .constant(.sheet) + presentation: Binding? = .constant(.sheet), + showBuild: Binding? = .constant(true) ) { _show = show _align = align ?? .constant(.center) @@ -171,6 +180,7 @@ public struct SwiftNEW: View { _specialEffect = specialEffect ?? .constant(.none) _glass = glass ?? .constant(true) _presentation = presentation ?? .constant(.sheet) + _showBuild = showBuild ?? .constant(true) compareVersion() } #else @@ -187,7 +197,8 @@ public struct SwiftNEW: View { mesh: Bool? = true, specialEffect: SwiftNEWSpecialEffect? = .none, glass: Bool? = true, - presentation: SwiftNEWPresentation? = .sheet + presentation: SwiftNEWPresentation? = .sheet, + showBuild: Bool? = true ) { _show = show _align = .constant(align ?? .center) @@ -202,6 +213,7 @@ public struct SwiftNEW: View { _specialEffect = .constant(specialEffect ?? .none) _glass = .constant(glass ?? true) _presentation = .constant(presentation ?? .sheet) + _showBuild = .constant(showBuild ?? true) compareVersion() } @_disfavoredOverload @@ -218,7 +230,8 @@ public struct SwiftNEW: View { mesh: Binding? = .constant(true), specialEffect: Binding? = .constant(.none), glass: Binding? = .constant(true), - presentation: Binding? = .constant(.sheet) + presentation: Binding? = .constant(.sheet), + showBuild: Binding? = .constant(true) ) { _show = show _align = align ?? .constant(.center) @@ -233,6 +246,7 @@ public struct SwiftNEW: View { _specialEffect = specialEffect ?? .constant(.none) _glass = glass ?? .constant(true) _presentation = presentation ?? .constant(.sheet) + _showBuild = showBuild ?? .constant(true) compareVersion() } #endif diff --git a/Sources/SwiftNEW/Views/Components/HeaderView.swift b/Sources/SwiftNEW/Views/Components/HeaderView.swift index 9f1e3c9..45fbf90 100644 --- a/Sources/SwiftNEW/Views/Components/HeaderView.swift +++ b/Sources/SwiftNEW/Views/Components/HeaderView.swift @@ -26,7 +26,7 @@ extension SwiftNEW { } Text(String(localized: "What's New in", bundle: .module)) .bold().font(.largeTitle) - Text("\(String(localized: "Version", bundle: .module)) \(Bundle.versionBuild)") + Text(String(localized: "Version \(showBuild ? Bundle.versionBuild : Bundle.version)", bundle: .module)) .bold().font(.title).foregroundColor(.secondary) } if align == .trailing { @@ -39,7 +39,7 @@ extension SwiftNEW { VStack { Text(String(localized: "What's New in", bundle: .module)) .bold().font(.largeTitle) - Text("\(String(localized: "Version", bundle: .module)) \(Bundle.versionBuild)") + Text(String(localized: "Version \(showBuild ? Bundle.versionBuild : Bundle.version)", bundle: .module)) .bold().font(.title).foregroundColor(.secondary) } } diff --git a/Sources/SwiftNEW/Views/Sheets/CurrentVersionSheet.swift b/Sources/SwiftNEW/Views/Sheets/CurrentVersionSheet.swift index d32b5d9..78e3a57 100644 --- a/Sources/SwiftNEW/Views/Sheets/CurrentVersionSheet.swift +++ b/Sources/SwiftNEW/Views/Sheets/CurrentVersionSheet.swift @@ -55,7 +55,7 @@ extension SwiftNEW { VStack(alignment: align == .trailing ? .trailing : .leading) { Text(new.title).font(.headline).lineLimit(1) Text(new.subtitle).font(.subheadline).foregroundColor(.secondary).lineLimit(1) - Text(new.body).font(.caption).foregroundColor(.secondary).lineLimit(2) + Text(new.body).font(.caption).foregroundColor(.secondary) } if align == .trailing { diff --git a/Sources/SwiftNEW/Views/Sheets/HistorySheet.swift b/Sources/SwiftNEW/Views/Sheets/HistorySheet.swift index 3e21ee3..7725bca 100644 --- a/Sources/SwiftNEW/Views/Sheets/HistorySheet.swift +++ b/Sources/SwiftNEW/Views/Sheets/HistorySheet.swift @@ -5,95 +5,95 @@ // Created by Ming on 11/6/2022. // +import SwiftGlass import SwiftUI import SwiftVB -import SwiftGlass @available(iOS 15.0, watchOS 8.0, macOS 12.0, tvOS 17.0, *) extension SwiftNEW { - - // MARK: - History List View - public var sheetHistory: some View { - VStack(alignment: align) { - Spacer() - - Text(String(localized: "History", bundle: .module)) - .bold().font(.largeTitle) - - Spacer() - - ScrollView(showsIndicators: false) { - ForEach(items, id: \.self) { item in - ZStack { - color.opacity(0.25) - Text(item.version).bold().font(.title2) - .foregroundColor(color.adaptedTextColor) - }.glass(radius: 15, shadowColor: color) - .frame(width: 75, height: 30) - .cornerRadius(15) - .padding(.bottom) - - ForEach(item.new, id: \.self) { new in - HStack { - if align == .leading || align == .center { - ZStack { - color - Image(systemName: new.icon) - .foregroundColor(.white) - }.glass(radius: 15, shadowColor: color) - #if !os(tvOS) - .frame(width: 50, height: 50) - #else - .frame(width: 100, height: 100) - #endif - .cornerRadius(15) - .padding(.trailing) - } else { - Spacer() - } - - VStack(alignment: align == .trailing ? .trailing : .leading) { - Text(new.title).font(.headline).lineLimit(1) - Text(new.subtitle).font(.subheadline).foregroundColor(.secondary).lineLimit(1) - Text(new.body).font(.caption).foregroundColor(.secondary).lineLimit(2) - } - - if align == .trailing { - ZStack { - color - Image(systemName: new.icon) - .foregroundColor(.white) - } - #if !os(tvOS) - .frame(width: 50, height: 50) - #else - .frame(width: 100, height: 100) - #endif - .cornerRadius(15) - .padding(.leading) - } else { - Spacer() - } - }.padding(.bottom) - } + + // MARK: - History List View + public var sheetHistory: some View { + VStack(alignment: align) { + Spacer() + + Text(String(localized: "History", bundle: .module)) + .bold().font(.largeTitle) + + Spacer() + + ScrollView(showsIndicators: false) { + ForEach(items, id: \.self) { item in + ZStack { + color.opacity(0.25) + Text(item.version).bold().font(.title2) + .foregroundColor(color.adaptedTextColor) + }.glass(radius: 15, shadowColor: color) + .frame(width: 120, height: 40) + .cornerRadius(15) + .padding(.bottom) + + ForEach(item.new, id: \.self) { new in + HStack { + if align == .leading || align == .center { + ZStack { + color + Image(systemName: new.icon) + .foregroundColor(.white) + }.glass(radius: 15, shadowColor: color) + #if !os(tvOS) + .frame(width: 50, height: 50) + #else + .frame(width: 100, height: 100) + #endif + .cornerRadius(15) + .padding(.trailing) + } else { + Spacer() + } + + VStack(alignment: align == .trailing ? .trailing : .leading) { + Text(new.title).font(.headline).lineLimit(1) + Text(new.subtitle).font(.subheadline).foregroundColor(.secondary).lineLimit(1) + Text(new.body).font(.caption).foregroundColor(.secondary) + } + + if align == .trailing { + ZStack { + color + Image(systemName: new.icon) + .foregroundColor(.white) } - } - #if !os(tvOS) - .frame(width: 300) - #elseif !os(macOS) - .frame(maxHeight: UIScreen.main.bounds.height * 0.5) - #endif - - Spacer() - - closeHistoryButton - .padding(.bottom) + #if !os(tvOS) + .frame(width: 50, height: 50) + #else + .frame(width: 100, height: 100) + #endif + .cornerRadius(15) + .padding(.leading) + } else { + Spacer() + } + }.padding(.bottom) + } } - #if os(macOS) - .padding() - .frame(width: 600, height: 600) - #elseif os(tvOS) - .frame(width: 600) - #endif + } + #if !os(tvOS) + .frame(width: 300) + #elseif !os(macOS) + .frame(maxHeight: UIScreen.main.bounds.height * 0.5) + #endif + + Spacer() + + closeHistoryButton + .padding(.bottom) } + #if os(macOS) + .padding() + .frame(width: 600, height: 600) + #elseif os(tvOS) + .frame(width: 600) + #endif + } } diff --git a/Sources/SwiftNEW/Views/SwiftNEW+View.swift b/Sources/SwiftNEW/Views/SwiftNEW+View.swift index 889bb56..5713e1f 100644 --- a/Sources/SwiftNEW/Views/SwiftNEW+View.swift +++ b/Sources/SwiftNEW/Views/SwiftNEW+View.swift @@ -64,6 +64,8 @@ extension SwiftNEW { } if specialEffect == .christmas { SnowfallView() + } else if specialEffect == .particles { + FloatingParticlesView() } sheetCurrent .sheet(isPresented: $historySheet) { @@ -84,6 +86,8 @@ extension SwiftNEW { } if specialEffect == .christmas { SnowfallView() + } else if specialEffect == .particles { + FloatingParticlesView() } sheetHistory #if os(visionOS)