diff --git a/damus.xcodeproj/project.pbxproj b/damus.xcodeproj/project.pbxproj index 2d52979..93d7402 100644 --- a/damus.xcodeproj/project.pbxproj +++ b/damus.xcodeproj/project.pbxproj @@ -8,8 +8,8 @@ /* Begin PBXBuildFile section */ 3169CAE6294E69C000EE4006 /* EmptyTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3169CAE5294E69C000EE4006 /* EmptyTimelineView.swift */; }; - 3169CAEB294FCABA00EE4006 /* Shimmer in Frameworks */ = {isa = PBXBuildFile; productRef = 3169CAEA294FCABA00EE4006 /* Shimmer */; }; 3169CAED294FCCFC00EE4006 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3169CAEC294FCCFC00EE4006 /* Constants.swift */; }; + 31D2E847295218AF006D67F8 /* Shimmer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31D2E846295218AF006D67F8 /* Shimmer.swift */; }; 4C06670128FC7C5900038D2A /* RelayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C06670028FC7C5900038D2A /* RelayView.swift */; }; 4C06670428FC7EC500038D2A /* Kingfisher in Frameworks */ = {isa = PBXBuildFile; productRef = 4C06670328FC7EC500038D2A /* Kingfisher */; }; 4C06670628FCB08600038D2A /* ImageCarousel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C06670528FCB08600038D2A /* ImageCarousel.swift */; }; @@ -151,6 +151,7 @@ /* Begin PBXFileReference section */ 3169CAE5294E69C000EE4006 /* EmptyTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmptyTimelineView.swift; sourceTree = ""; }; 3169CAEC294FCCFC00EE4006 /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = Constants.swift; path = damus/Util/Constants.swift; sourceTree = SOURCE_ROOT; }; + 31D2E846295218AF006D67F8 /* Shimmer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Shimmer.swift; sourceTree = ""; }; 4C06670028FC7C5900038D2A /* RelayView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayView.swift; sourceTree = ""; }; 4C06670528FCB08600038D2A /* ImageCarousel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageCarousel.swift; sourceTree = ""; }; 4C06670828FDE64700038D2A /* damus-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "damus-Bridging-Header.h"; sourceTree = ""; }; @@ -312,7 +313,6 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 3169CAEB294FCABA00EE4006 /* Shimmer in Frameworks */, 4C06670428FC7EC500038D2A /* Kingfisher in Frameworks */, 4CE6DF1227F7A2B300C66700 /* Starscream in Frameworks */, 4C649881286E0EE300EAE2B3 /* secp256k1 in Frameworks */, @@ -516,6 +516,7 @@ 4CE4F9DF285287A000C00DD9 /* Components */ = { isa = PBXGroup; children = ( + 31D2E846295218AF006D67F8 /* Shimmer.swift */, 4CE4F9E0285287B800C00DD9 /* TextFieldAlert.swift */, 4CD7641A28A1641400B6928F /* EndBlock.swift */, 4C06670528FCB08600038D2A /* ImageCarousel.swift */, @@ -622,7 +623,6 @@ 4CE6DF1127F7A2B300C66700 /* Starscream */, 4C649880286E0EE300EAE2B3 /* secp256k1 */, 4C06670328FC7EC500038D2A /* Kingfisher */, - 3169CAEA294FCABA00EE4006 /* Shimmer */, ); productName = damus; productReference = 4CE6DEE327F7A08100C66700 /* damus.app */; @@ -788,6 +788,7 @@ 4C3BEFDC281DCE6100B3DE84 /* Liked.swift in Sources */, 4C75EFB128049D510006080F /* NostrResponse.swift in Sources */, 4CEE2AF7280B2DEA00AB5EEF /* ProfileName.swift in Sources */, + 31D2E847295218AF006D67F8 /* Shimmer.swift in Sources */, 4C285C8228385570008A31F1 /* CarouselView.swift in Sources */, 4C3EA67F28FFC01D00C48A62 /* InvoiceView.swift in Sources */, 4CEE2B02280B39E800AB5EEF /* EventActionBar.swift in Sources */, @@ -1240,11 +1241,6 @@ /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ - 3169CAEA294FCABA00EE4006 /* Shimmer */ = { - isa = XCSwiftPackageProductDependency; - package = 3169CAE9294FCABA00EE4006 /* XCRemoteSwiftPackageReference "Shimmer" */; - productName = Shimmer; - }; 4C06670328FC7EC500038D2A /* Kingfisher */ = { isa = XCSwiftPackageProductDependency; package = 4C06670228FC7EC500038D2A /* XCRemoteSwiftPackageReference "Kingfisher" */; diff --git a/damus/Components/Shimmer.swift b/damus/Components/Shimmer.swift new file mode 100644 index 0000000..38956fc --- /dev/null +++ b/damus/Components/Shimmer.swift @@ -0,0 +1,77 @@ +// +// Shimmer.swift +// +// +// Created by Joshua Homann on 2/20/21. +// + +import SwiftUI + +public struct ShimmerConfiguration { + + @Environment(\.colorScheme) var colorScheme + + public let gradient: Gradient + public let initialLocation: (start: UnitPoint, end: UnitPoint) + public let finalLocation: (start: UnitPoint, end: UnitPoint) + public let duration: TimeInterval + public let opacity: Double + public static let `default` = ShimmerConfiguration( + gradient: Gradient(stops: [ + .init(color: .clear, location: 0), + .init(color: .black, location: 0.3), + .init(color: .black, location: 0.7), + .init(color: .clear, location: 1), + ]), + initialLocation: (start: UnitPoint(x: -1, y: 0.5), end: .leading), + finalLocation: (start: .trailing, end: UnitPoint(x: 2, y: 0.5)), + duration: 2, + opacity: 0.6 + ) +} + +struct ShimmeringView: View { + private let content: () -> Content + private let configuration: ShimmerConfiguration + @State private var startPoint: UnitPoint + @State private var endPoint: UnitPoint + init(configuration: ShimmerConfiguration, @ViewBuilder content: @escaping () -> Content) { + self.configuration = configuration + self.content = content + _startPoint = .init(wrappedValue: configuration.initialLocation.start) + _endPoint = .init(wrappedValue: configuration.initialLocation.end) + } + var body: some View { + ZStack { + content() + LinearGradient( + gradient: configuration.gradient, + startPoint: startPoint, + endPoint: endPoint + ) + .opacity(configuration.opacity) + .blendMode(.overlay) + .onAppear { + withAnimation(Animation.linear(duration: configuration.duration).repeatForever(autoreverses: false)) { + startPoint = configuration.finalLocation.start + endPoint = configuration.finalLocation.end + } + } + } + .edgesIgnoringSafeArea(.all) + } +} + +public struct ShimmerModifier: ViewModifier { + let configuration: ShimmerConfiguration + public func body(content: Content) -> some View { + ShimmeringView(configuration: configuration) { content } + } +} + + +public extension View { + func shimmer(configuration: ShimmerConfiguration = .default) -> some View { + modifier(ShimmerModifier(configuration: configuration)) + } +} diff --git a/damus/Views/TimelineView.swift b/damus/Views/TimelineView.swift index 289b831..83fa3b7 100644 --- a/damus/Views/TimelineView.swift +++ b/damus/Views/TimelineView.swift @@ -6,7 +6,6 @@ // import SwiftUI -import Shimmer enum TimelineAction { case chillin