Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ABW-1340 Show dApp detail view from tx review #591

Merged
merged 7 commits into from
Jun 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -429,6 +429,7 @@ package.addModules([
.feature(
name: "TransactionReviewFeature",
dependencies: [
"AuthorizedDappsClient",
"GatewayAPI",
"TransactionClient",
"SigningFeature",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@ extension ImportOlympiaLedgerAccountsAndFactorSources {
)
)
}

.sheet(
store: store.scope(
state: \.$derivePublicKeys,
Expand All @@ -66,7 +65,6 @@ extension ImportOlympiaLedgerAccountsAndFactorSources {
DerivePublicKeys.View(store: $0)
}
)

.padding(.horizontal, .medium3)
}
}
Expand Down
20 changes: 10 additions & 10 deletions Sources/Features/PersonaDetailsFeature/PersonaDetails+View.swift
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ extension PersonaDetails.View {
store: store.destination,
state: /PersonaDetails.Destination.State.dAppDetails,
action: PersonaDetails.Destination.Action.dAppDetails,
destination: { SimpleDappDetails.View(store: $0) }
destination: { SimpleAuthDappDetails.View(store: $0) }
)
.sheet(
store: store.destination,
Expand Down Expand Up @@ -363,7 +363,7 @@ extension PersonaDetails.View {

// MARK: - SimpleDappDetails

extension SimpleDappDetails {
extension SimpleAuthDappDetails {
@MainActor
public struct View: SwiftUI.View {
let store: Store
Expand Down Expand Up @@ -394,7 +394,7 @@ extension SimpleDappDetails {

// MARK: - Body

extension SimpleDappDetails.View {
extension SimpleAuthDappDetails.View {
public var body: some View {
WithViewStore(store, observe: \.viewState, send: { .view($0) }) { viewStore in
ScrollView {
Expand Down Expand Up @@ -422,8 +422,8 @@ extension SimpleDappDetails.View {

// MARK: - Extensions

private extension SimpleDappDetails.State {
var viewState: SimpleDappDetails.ViewState {
private extension SimpleAuthDappDetails.State {
var viewState: SimpleAuthDappDetails.ViewState {
.init(
title: dApp.displayName?.rawValue ?? L10n.DAppRequest.Metadata.unknownName,
description: metadata?.description ?? L10n.AuthorizedDapps.DAppDetails.missingDescription,
Expand All @@ -442,10 +442,10 @@ private extension SimpleDappDetails.State {

// MARK: Child Views

extension SimpleDappDetails.View {
extension SimpleAuthDappDetails.View {
@MainActor
struct InfoBlock: View {
let store: StoreOf<SimpleDappDetails>
let store: StoreOf<SimpleAuthDappDetails>

var body: some View {
WithViewStore(store, observe: \.viewState, send: { .view($0) }) { viewStore in
Expand Down Expand Up @@ -486,7 +486,7 @@ extension SimpleDappDetails.View {

@MainActor
struct FungiblesList: View {
let store: StoreOf<SimpleDappDetails>
let store: StoreOf<SimpleAuthDappDetails>

var body: some View {
WithViewStore(store, observe: \.viewState.fungibles, send: { .view($0) }) { viewStore in
Expand All @@ -499,7 +499,7 @@ extension SimpleDappDetails.View {

@MainActor
struct NonFungiblesListList: View {
let store: StoreOf<SimpleDappDetails>
let store: StoreOf<SimpleAuthDappDetails>

var body: some View {
WithViewStore(store, observe: \.viewState.nonFungibles, send: { .view($0) }) { viewStore in
Expand Down Expand Up @@ -540,7 +540,7 @@ extension SimpleDappDetails.View {

@MainActor
struct Personas: View {
let personas: [SimpleDappDetails.Persona]
let personas: [SimpleAuthDappDetails.Persona]

var body: some View {
if personas.isEmpty {
Expand Down
16 changes: 8 additions & 8 deletions Sources/Features/PersonaDetailsFeature/PersonaDetails.swift
Original file line number Diff line number Diff line change
Expand Up @@ -108,15 +108,15 @@ public struct PersonaDetails: Sendable, FeatureReducer {
public enum State: Hashable {
case editPersona(EditPersona.State)
case createAuthKey(CreateAuthKey.State)
case dAppDetails(SimpleDappDetails.State)
case dAppDetails(SimpleAuthDappDetails.State)

case confirmForgetAlert(AlertState<Action.ConfirmForgetAlert>)
}

public enum Action: Equatable {
case editPersona(EditPersona.Action)
case createAuthKey(CreateAuthKey.Action)
case dAppDetails(SimpleDappDetails.Action)
case dAppDetails(SimpleAuthDappDetails.Action)

case confirmForgetAlert(ConfirmForgetAlert)

Expand All @@ -134,7 +134,7 @@ public struct PersonaDetails: Sendable, FeatureReducer {
CreateAuthKey()
}
Scope(state: /State.dAppDetails, action: /Action.dAppDetails) {
SimpleDappDetails()
SimpleAuthDappDetails()
}
}
}
Expand Down Expand Up @@ -353,10 +353,10 @@ extension AlertState<PersonaDetails.Destination.Action.ConfirmForgetAlert> {
}
}

// MARK: - SimpleDappDetails
// MARK: - SimpleAuthDappDetails
// FIXME: Remove and make settings use stacks

public struct SimpleDappDetails: Sendable, FeatureReducer {
public struct SimpleAuthDappDetails: Sendable, FeatureReducer {
@Dependency(\.errorQueue) var errorQueue
@Dependency(\.gatewayAPIClient) var gatewayAPIClient
@Dependency(\.openURL) var openURL
Expand Down Expand Up @@ -497,7 +497,7 @@ public struct SimpleDappDetails: Sendable, FeatureReducer {
private func loadResources(
metadata: GatewayAPI.EntityMetadataCollection,
validated dappDefinitionAddress: DappDefinitionAddress
) async -> Loadable<SimpleDappDetails.State.Resources> {
) async -> Loadable<SimpleAuthDappDetails.State.Resources> {
guard let claimedEntities = metadata.claimedEntities, !claimedEntities.isEmpty else {
return .idle
}
Expand Down Expand Up @@ -550,7 +550,7 @@ public struct SimpleDappDetails: Sendable, FeatureReducer {
}

extension GatewayAPI.StateEntityDetailsResponseItem {
var resourceDetails: SimpleDappDetails.State.Resources.ResourceDetails? {
var resourceDetails: SimpleAuthDappDetails.State.Resources.ResourceDetails? {
guard let fungibility else { return nil }
return .init(address: .init(address: address),
fungibility: fungibility,
Expand All @@ -560,7 +560,7 @@ extension GatewayAPI.StateEntityDetailsResponseItem {
iconURL: metadata.iconURL)
}

private var fungibility: SimpleDappDetails.State.Resources.ResourceDetails.Fungibility? {
private var fungibility: SimpleAuthDappDetails.State.Resources.ResourceDetails.Fungibility? {
guard let details else { return nil }
switch details {
case .fungibleResource:
Expand Down
178 changes: 178 additions & 0 deletions Sources/Features/TransactionReviewFeature/TransactionReview+View.swift
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,7 @@ extension View {
fileprivate func destinations(with store: StoreOf<TransactionReview>) -> some View {
let destinationStore = store.scope(state: \.$destination, action: { .child(.destination($0)) })
return customizeGuarantees(with: destinationStore)
.dApp(with: destinationStore)
.selectFeePayer(with: destinationStore)
.signing(with: destinationStore)
.submitting(with: destinationStore)
Expand All @@ -235,6 +236,16 @@ extension View {
)
}

@MainActor
private func dApp(with destinationStore: PresentationStoreOf<TransactionReview.Destinations>) -> some View {
sheet(
store: destinationStore,
state: /TransactionReview.Destinations.State.dApp,
action: TransactionReview.Destinations.Action.dApp,
content: { SimpleDappDetails.View(store: $0) }
)
}

@MainActor
private func selectFeePayer(with destinationStore: PresentationStoreOf<TransactionReview.Destinations>) -> some View {
sheet(
Expand Down Expand Up @@ -412,3 +423,170 @@ extension Button where Label == Image {
}
}
}

// FIXME: Remove and make settings use stacks
GhenadieVP marked this conversation as resolved.
Show resolved Hide resolved

// MARK: - SimpleDappDetails

extension SimpleDappDetails {
@MainActor
public struct View: SwiftUI.View {
let store: Store

public init(store: Store) {
self.store = store
}
}

public struct ViewState: Equatable {
let title: String
let description: String
let domain: URL?
let thumbnail: URL?
let address: DappDefinitionAddress
let fungibles: [State.Resources.ResourceDetails]?
let nonFungibles: [State.Resources.ResourceDetails]?
let associatedDapps: [State.AssociatedDapp]?
}
}

// MARK: - Body

extension SimpleDappDetails.View {
public var body: some View {
WithViewStore(store, observe: \.viewState, send: { .view($0) }) { viewStore in
ScrollView {
VStack(spacing: 0) {
DappThumbnail(.known(viewStore.thumbnail), size: .veryLarge)
.padding(.vertical, .large2)

InfoBlock(store: store)

FungiblesList(store: store)

NonFungiblesListList(store: store)
}
.onAppear {
viewStore.send(.appeared)
}
.navigationTitle(viewStore.title)
}
}
}
}

// MARK: - Extensions

private extension SimpleDappDetails.State {
var viewState: SimpleDappDetails.ViewState {
.init(
title: metadata?.name ?? L10n.DAppRequest.Metadata.unknownName,
description: metadata?.description ?? L10n.AuthorizedDapps.DAppDetails.missingDescription,
domain: metadata?.claimedWebsites?.first,
thumbnail: metadata?.iconURL,
address: dAppID,
fungibles: resources?.fungible,
nonFungibles: resources?.nonFungible,
associatedDapps: associatedDapps
)
}
}

// MARK: Child Views

extension SimpleDappDetails.View {
@MainActor
struct InfoBlock: View {
let store: StoreOf<SimpleDappDetails>

var body: some View {
WithViewStore(store, observe: \.viewState, send: { .view($0) }) { viewStore in
VStack(alignment: .leading, spacing: .medium2) {
Separator()

Text(viewStore.description)
.textBlock
.flushedLeft

Separator()

HStack(spacing: 0) {
Text(L10n.AuthorizedDapps.DAppDetails.dAppDefinition)
.sectionHeading

Spacer(minLength: 0)

AddressView(.address(.account(viewStore.address)))
.foregroundColor(.app.gray1)
.textStyle(.body1HighImportance)
}

if let domain = viewStore.domain {
Text(L10n.AuthorizedDapps.DAppDetails.website)
.sectionHeading
Button(domain.absoluteString) {
viewStore.send(.openURLTapped(domain))
}
.buttonStyle(.url)
}
}
.padding(.horizontal, .medium1)
.padding(.bottom, .large2)
}
}
}

@MainActor
struct FungiblesList: View {
let store: StoreOf<SimpleDappDetails>

var body: some View {
WithViewStore(store, observe: \.viewState.fungibles, send: { .view($0) }) { viewStore in
ListWithHeading(heading: L10n.AuthorizedDapps.DAppDetails.tokens, elements: viewStore.state, title: \.name) { resource in
TokenThumbnail(.known(resource.iconURL), size: .small)
}
}
}
}

@MainActor
struct NonFungiblesListList: View {
let store: StoreOf<SimpleDappDetails>

var body: some View {
WithViewStore(store, observe: \.viewState.nonFungibles, send: { .view($0) }) { viewStore in
ListWithHeading(heading: L10n.AuthorizedDapps.DAppDetails.nfts, elements: viewStore.state, title: \.name) { resource in
NFTThumbnail(resource.iconURL, size: .small)
}
}
}
}

@MainActor
struct ListWithHeading<Element: Identifiable, Icon: View>: View {
let heading: String
let elements: [Element]?
let title: (Element) -> String
let icon: (Element) -> Icon

var body: some View {
if let elements, !elements.isEmpty {
VStack(alignment: .leading, spacing: .medium3) {
Text(heading)
.sectionHeading
.padding(.horizontal, .medium1)

ForEach(elements) { element in
Card {
PlainListRow(title: title(element), showChevron: false) {
icon(element)
}
}
.padding(.horizontal, .medium3)
}
}
.padding(.bottom, .medium1)
}
}
}
}
Loading