Skip to content

Commit

Permalink
Implement banner
Browse files Browse the repository at this point in the history
Move to feature prelude

Localize
  • Loading branch information
nikola-milicevic committed Apr 26, 2023
1 parent 5e12956 commit 44b43da
Show file tree
Hide file tree
Showing 12 changed files with 244 additions and 0 deletions.
2 changes: 2 additions & 0 deletions App/Shared/WalletApp.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import FeaturePrelude
// MARK: - WalletApp
@main
struct WalletApp: SwiftUI.App {
@UIApplicationDelegateAdaptor var delegate: AppDelegate

var body: some SwiftUI.Scene {
WindowGroup {
App.View(
Expand Down
2 changes: 2 additions & 0 deletions Sources/Core/FeaturePrelude/AddressView/AddressView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ public struct AddressView: SwiftUI.View, Sendable {

@Dependency(\.pasteboardClient) var pasteboardClient
@Dependency(\.openURL) var openURL
@Dependency(\.bannerClient) var bannerClient

public init(
_ address: AddressType,
Expand Down Expand Up @@ -112,6 +113,7 @@ extension AddressView {
private func copyToPasteboard() {
guard let addressString else { return }
pasteboardClient.copyString(addressString)
Task { await bannerClient.presentBanner(L10n.Common.copied) }
}

private func viewOnRadixDashboard() {
Expand Down
23 changes: 23 additions & 0 deletions Sources/Core/FeaturePrelude/Banner/BannerClient+Interface.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import Dependencies
import UIKit

// MARK: - BannerClient
@MainActor
public struct BannerClient {
public var setWindowScene: SetWindowScene
public var presentBanner: PresentBanner
static var scene: UIWindowScene?
static var window: UIWindow?
}

extension BannerClient {
public typealias SetWindowScene = (UIWindowScene) async -> Void
public typealias PresentBanner = (String) async -> Void
}

extension DependencyValues {
public var bannerClient: BannerClient {
get { self[BannerClient.self] }
set { self[BannerClient.self] = newValue }
}
}
24 changes: 24 additions & 0 deletions Sources/Core/FeaturePrelude/Banner/BannerClient+Live.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import Dependencies
import UIKit

extension BannerClient: DependencyKey {
public static let liveValue: Self = .init(
setWindowScene: { windowScene in
scene = windowScene
},
presentBanner: { text in
guard window == nil, let windowScene = scene else { return }
window = UIWindow(windowScene: windowScene)
guard let bannerWindow = window else { return }
bannerWindow.rootViewController = BannerViewController(
text: text,
completed: {
window = nil
}
)
bannerWindow.windowLevel = .normal + 1
bannerWindow.isUserInteractionEnabled = false
bannerWindow.makeKeyAndVisible()
}
)
}
17 changes: 17 additions & 0 deletions Sources/Core/FeaturePrelude/Banner/BannerClient+Test.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import Dependencies

// MARK: - BannerClient + TestDependencyKey
extension BannerClient: TestDependencyKey {
public static let previewValue = Self.noop
public static let testValue: Self = .init(
setWindowScene: unimplemented("\(Self.self).setWindowScene"),
presentBanner: unimplemented("\(Self.self).presentBanner")
)
}

extension BannerClient {
public static let noop: Self = .init(
setWindowScene: { _ in },
presentBanner: { _ in }
)
}
64 changes: 64 additions & 0 deletions Sources/Core/FeaturePrelude/Banner/BannerView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import DesignSystem
import Resources
import SwiftUI
import UIKit

// MARK: - BannerView
public final class BannerView: UIView {
private let text: String
private let label = UILabel()
private let padding: CGFloat = .small1
private var shadowLayer: CAShapeLayer?

override public var intrinsicContentSize: CGSize {
.init(width: label.intrinsicContentSize.width + .large1,
height: label.intrinsicContentSize.height + .medium1)
}

public init(text: String) {
self.text = text
super.init(frame: .zero)
setUpLabel()
}

@available(*, unavailable)
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

override public func layoutSubviews() {
super.layoutSubviews()
guard shadowLayer == nil else { return }
setUpShadowLayer()
}
}

// MARK: - Private Methods
extension BannerView {
private func setUpLabel() {
label.text = text
label.textColor = UIColor(Color.app.gray1)
label.font = UIFont(name: FontFamily.IBMPlexSans.medium.name, size: 16)
label.translatesAutoresizingMaskIntoConstraints = false
addSubview(label)
NSLayoutConstraint.activate([
label.centerXAnchor.constraint(equalTo: centerXAnchor),
label.centerYAnchor.constraint(equalTo: centerYAnchor),
])
}

private func setUpShadowLayer() {
shadowLayer = CAShapeLayer()
shadowLayer?.path = UIBezierPath(roundedRect: bounds, cornerRadius: frame.height / 2).cgPath
shadowLayer?.fillColor = UIColor(Color.app.white).cgColor

shadowLayer?.shadowColor = UIColor.black.cgColor
shadowLayer?.shadowPath = shadowLayer?.path
shadowLayer?.shadowOffset = CGSize(width: 0, height: 2)
shadowLayer?.shadowOpacity = 0.3
shadowLayer?.shadowRadius = 5

guard let shadowLayer = shadowLayer else { return }
layer.insertSublayer(shadowLayer, at: 0)
}
}
75 changes: 75 additions & 0 deletions Sources/Core/FeaturePrelude/Banner/BannerViewController.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import UIKit

// MARK: - BannerViewController
public final class BannerViewController: UIViewController {
private let banner: BannerView
private let completed: () -> Void

public init(
text: String,
completed: @escaping () -> Void
) {
self.banner = BannerView(text: text)
self.completed = completed
super.init(nibName: nil, bundle: nil)
}

@available(*, unavailable)
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

override public func viewDidLoad() {
super.viewDidLoad()
setInitialState()
}

override public func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
showBannerView()
}
}

// MARK: - Private Methods
extension BannerViewController {
private func setInitialState() {
banner.alpha = 0
banner.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(banner)
NSLayoutConstraint.activate([
banner.centerXAnchor.constraint(equalTo: view.centerXAnchor),
banner.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -100),
])
}

private func showBannerView() {
UIView.animate(
withDuration: Constants.animationDuration,
animations: { [weak self] in
self?.banner.alpha = 1
}, completion: { [weak self] _ in
self?.hideBannerView()
}
)
}

private func hideBannerView() {
UIView.animate(
withDuration: Constants.animationDuration,
delay: Constants.presentationDuration,
animations: { [weak self] in
self?.banner.alpha = 0
}, completion: { [weak self] _ in
self?.completed()
}
)
}
}

// MARK: BannerViewController.Constants
extension BannerViewController {
private enum Constants {
static let animationDuration: TimeInterval = 0.15
static let presentationDuration: TimeInterval = 2
}
}
2 changes: 2 additions & 0 deletions Sources/Core/Resources/Generated/L10n.generated.swift
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ public enum L10n {
public static let title = L10n.tr("Localizable", "authorizedDapps.title", fallback: "Authorized dApps")
}
public enum Common {
/// Copied
public static let copied = L10n.tr("Localizable", "common.copied", fallback: "Copied")
public enum Account {
/// Account
public static let kind = L10n.tr("Localizable", "common.account.kind", fallback: "Account")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

"common.account.kind" = "Account";
"common.persona.kind" = "Persona";
"common.copied" = "Copied";

"createEntity.introduction.button.continue" = "Continue";
"createEntity.introduction.persona.title" = "Create a Persona";
Expand Down
8 changes: 8 additions & 0 deletions Sources/Features/AppFeature/App+View.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ import SplashFeature
extension App {
@MainActor
public struct View: SwiftUI.View {
@EnvironmentObject var sceneDelegate: SceneDelegate
@Dependency(\.bannerClient) var bannerClient

private let store: StoreOf<App>

public init(store: StoreOf<App>) {
Expand Down Expand Up @@ -34,6 +37,11 @@ extension App {
then: { Splash.View(store: $0) }
)
}
.onAppear {
if let windowScene = sceneDelegate.windowScene {
Task { await bannerClient.setWindowScene(windowScene) }
}
}
.alert(
store: store.scope(state: \.$alert, action: { .view(.alert($0)) }),
state: /App.Alerts.State.userErrorAlert,
Expand Down
13 changes: 13 additions & 0 deletions Sources/Prelude/AppDelegate.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import UIKit

public final class AppDelegate: NSObject, UIApplicationDelegate {
public func application(
_ application: UIApplication,
configurationForConnecting connectingSceneSession: UISceneSession,
options: UIScene.ConnectionOptions
) -> UISceneConfiguration {
let sceneConfig = UISceneConfiguration(name: nil, sessionRole: connectingSceneSession.role)
sceneConfig.delegateClass = SceneDelegate.self
return sceneConfig
}
}
13 changes: 13 additions & 0 deletions Sources/Prelude/SceneDelegate.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import UIKit

public final class SceneDelegate: NSObject, UIWindowSceneDelegate, ObservableObject {
public weak var windowScene: UIWindowScene?

public func scene(
_ scene: UIScene,
willConnectTo session: UISceneSession,
options connectionOptions: UIScene.ConnectionOptions
) {
windowScene = scene as? UIWindowScene
}
}

0 comments on commit 44b43da

Please sign in to comment.