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

IOS-8083 Make ai token view token description error #4003

Open
wants to merge 15 commits into
base: develop
Choose a base branch
from
Open
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
33 changes: 33 additions & 0 deletions Tangem/App/Email/DataCollectors.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ private extension EmailDataCollector {
}
}

// MARK: - NegativeFeedbackDataCollector

struct NegativeFeedbackDataCollector: EmailDataCollector {
var logData: Data? {
formatData(userWalletEmailData)
Expand All @@ -46,6 +48,8 @@ struct NegativeFeedbackDataCollector: EmailDataCollector {
}
}

// MARK: - SendScreenDataCollector

struct SendScreenDataCollector: EmailDataCollector {
var logData: Data? {
var data = userWalletEmailData
Expand Down Expand Up @@ -142,6 +146,8 @@ struct SendScreenDataCollector: EmailDataCollector {
}
}

// MARK: - PushScreenDataCollector

struct PushScreenDataCollector: EmailDataCollector {
var logData: Data? {
var data = userWalletEmailData
Expand Down Expand Up @@ -193,6 +199,8 @@ struct PushScreenDataCollector: EmailDataCollector {
}
}

// MARK: - DetailsFeedbackDataCollector

struct DetailsFeedbackDataCollector: EmailDataCollector {
var logData: Data? {
var dataToFormat: [EmailCollectedData] = []
Expand Down Expand Up @@ -253,7 +261,32 @@ struct DetailsFeedbackDataCollector: EmailDataCollector {
}
}

// MARK: - DetailsFeedbackData

struct DetailsFeedbackData {
let userWalletEmailData: [EmailCollectedData]
let walletModels: [WalletModel]
}

// MARK: - TokenErrorDescriptionDataCollector

struct TokenErrorDescriptionDataCollector: EmailDataCollector {
var logData: Data? {
var dataToFormat: [EmailCollectedData] = []
dataToFormat.append(.separator(.dashes))

dataToFormat.append(EmailCollectedData(type: .token(.id), data: tokenId))
dataToFormat.append(EmailCollectedData(type: .token(.name), data: tokenName))

dataToFormat.append(.separator(.dashes))
return formatData(dataToFormat)
}

private let tokenId: String
private let tokenName: String

init(tokenId: String, tokenName: String) {
self.tokenId = tokenId
self.tokenName = tokenName
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ class TokenMarketsDetailsCoordinator: CoordinatorObject {
@Published var sendCoordinator: SendCoordinator? = nil
@Published var expressCoordinator: ExpressCoordinator? = nil
@Published var stakingDetailsCoordinator: StakingDetailsCoordinator? = nil
@Published var mailViewModel: MailViewModel? = nil

private var safariHandle: SafariHandle?

Expand Down Expand Up @@ -79,6 +80,12 @@ extension TokenMarketsDetailsCoordinator: TokenMarketsDetailsRoutable {
)
}

func openMail(with dataCollector: EmailDataCollector, emailType: EmailType) {
let logsComposer = LogsComposer(infoProvider: dataCollector)
let recipient = EmailConfig.default.recipient
mailViewModel = MailViewModel(logsComposer: logsComposer, recipient: recipient, emailType: emailType)
}

func openURL(_ url: URL) {
safariManager.openURL(url)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ struct TokenMarketsDetailsCoordinatorView: CoordinatorView {
.sheet(item: $coordinator.modalWebViewModel) {
WebViewContainer(viewModel: $0)
}
.sheet(item: $coordinator.mailViewModel) {
MailView(viewModel: $0)
}
.iOS16UIKitSheet(item: $coordinator.expressCoordinator) {
ExpressCoordinatorView(coordinator: $0)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,6 @@ import Foundation
protocol TokenMarketsDetailsRoutable: AnyObject, MarketsPortfolioContainerRoutable {
func openURL(_ url: URL)
func openTokenSelector(with model: TokenMarketsDetailsModel, walletDataProvider: MarketsWalletDataProvider)
func openMail(with dataCollector: EmailDataCollector, emailType: EmailType)
func closeModule()
}
Original file line number Diff line number Diff line change
Expand Up @@ -147,9 +147,10 @@ struct TokenMarketsDetailsView: View {
.opacity(viewModel.overlayContentHidingProgress)
.coordinateSpace(name: scrollViewFrameCoordinateSpaceName)
.bindAlert($viewModel.alert)
.descriptionBottomSheet(
.tokenDescriptionBottomSheet(
info: $viewModel.descriptionBottomSheetInfo,
backgroundColor: Colors.Background.action
backgroundColor: Colors.Background.action,
onGeneratedAITapAction: viewModel.onGenerateAITapAction
)
.animation(.default, value: viewModel.state)
.animation(.default, value: viewModel.isLoading)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,6 @@ class TokenMarketsDetailsViewModel: MarketsBaseViewModel {
descriptionBottomSheetInfo = .init(
title: Localization.marketsTokenDetailsAboutTokenTitle(tokenInfo.name),
description: fullDescription,
isGeneratedWithAI: true,
showCloseButton: true
)
}
Expand All @@ -220,6 +219,17 @@ class TokenMarketsDetailsViewModel: MarketsBaseViewModel {
// In this case, content should be hidden (i.e. the initial progress should be zero)
overlayContentHidingInitialProgress = state.isCollapsed ? 0.0 : 1.0
}

func onGenerateAITapAction() {
descriptionBottomSheetInfo = nil
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

а его точно надо скрывать?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

а его точно надо скрывать?

Так точно, добавлю коммент, иначе не открывается поверх Боттом шит емейла

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

а его точно надо скрывать?

Так точно, добавлю коммент, иначе не открывается поверх Боттом шит емейла

Ну и в целом это нормальное поведение чтобы не городить матрешек по шторкам


DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { [weak self] in
guard let self else { return }

let dataCollector = TokenErrorDescriptionDataCollector(tokenId: tokenInfo.id, tokenName: tokenInfo.name)
coordinator?.openMail(with: dataCollector, emailType: .appFeedback(subject: Localization.feedbackTokenDescriptionError))
}
}
}

// MARK: - Details response processing
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,7 @@
"give_permission_unlimited" = "Unlimited";
"home_button_order" = "Order card";
"home_button_scan" = "Scan card";
"information_generated_with_ai" = "This information was generated with AI";
"information_generated_with_ai" = "This information was generated with AI.\nTap here, If you find any errors.";
"initial_message_change_access_code_body" = "To change the access code tap the card as shown above and do not remove until the end of the operation";
"initial_message_change_passcode_body" = "To change the passcode tap the card as shown above and do not remove until the end of the operation";
"initial_message_create_wallet_body" = "To create the wallet tap the card as shown above and do not remove until the end of the operation";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,8 @@ import MarkdownUI

struct DescriptionBottomSheetInfo: Identifiable, Equatable {
let id: UUID = .init()

let title: String?
let description: String
var isGeneratedWithAI: Bool = false
var showCloseButton: Bool = false

static func == (lhs: DescriptionBottomSheetInfo, rhs: DescriptionBottomSheetInfo) -> Bool {
Expand All @@ -34,7 +32,7 @@ struct DescriptionBottomSheetView: View {
}

private var content: some View {
VStack(spacing: 14) {
VStack(spacing: 12) {
headerView

Markdown { info.description }
Expand All @@ -52,12 +50,7 @@ struct DescriptionBottomSheetView: View {
.frame(maxWidth: .infinity, alignment: .topLeading)
.fixedSize(horizontal: false, vertical: true)
.multilineTextAlignment(.leading)

if info.isGeneratedWithAI {
generatedWithAILabel
}
}
.padding(.bottom, 10)
}
}

Expand Down Expand Up @@ -99,18 +92,6 @@ private extension DescriptionBottomSheetView {
.padding(.vertical, 8)
})
}

var generatedWithAILabel: some View {
HStack(spacing: 12) {
Assets.stars.image
.foregroundStyle(Colors.Icon.accent)

Text(Localization.informationGeneratedWithAi)
.style(Fonts.Regular.footnote, color: Colors.Text.primary1)
.frame(maxWidth: .infinity, alignment: .leading)
}
.defaultRoundedBackground(with: Colors.Background.tertiary)
}
}

#Preview {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
//
// TokenDescriptionBottomSheetView.swift
// Tangem
//
// Created by Alexander Skibin on 01.10.2024.
// Copyright © 2024 Tangem AG. All rights reserved.
//

import SwiftUI
import MarkdownUI

struct TokenDescriptionBottomSheetView: View {
let info: DescriptionBottomSheetInfo
var generatedWithAIAction: (() -> Void)? = nil

@Environment(\.dismiss) private var dismissSheetAction
@State private var containerHeight: CGFloat = 0

var body: some View {
VStack(alignment: .leading, spacing: 10) {
DescriptionBottomSheetView(info: info)

Button {
generatedWithAIAction?()
} label: {
generatedWithAILabel
}
.padding(.horizontal, 16)
.disabled(generatedWithAIAction == nil)
}
}
}

// View components
private extension TokenDescriptionBottomSheetView {
var generatedWithAILabel: some View {
HStack(spacing: 12) {
Assets.stars.image
.foregroundStyle(Colors.Icon.accent)

Text(Localization.informationGeneratedWithAi)
.multilineTextAlignment(.leading)
.style(Fonts.Regular.footnote, color: Colors.Text.primary1)
.frame(maxWidth: .infinity, alignment: .leading)
}
.defaultRoundedBackground(with: Colors.Background.tertiary)
}
}

#Preview {
TokenDescriptionBottomSheetView(info: .init(
title: "About Ethereum",
description: "Ethereum network is a blockchain network.\n- Ethereum is an open-source platform for decentralized applications.\n- It aims to create a world computer for building applications in a decentralized manner.\n- Supports smart contracts allowing developers to program digital value.\n- Examples of dapps include tokens, NFTs, DeFi apps, lending protocols, and decentralized exchanges.\n- Transactions and smart contract executions require Gas fees, paid in Ether (ETH).\n- Gas measures the computational effort needed for operations, with fees fluctuating based on network demand.\n\n• Tether (USDT) is a stablecoin pegged to the U.S. dollar. \n • It offers stability in the volatile crypto market. \n• Issued by Tether Limited, governed by British Virgin Islands laws.\n• Used on exchanges like Bitfinex for trading cryptocurrencies.\n• Facilitates quick and cheap fiat movement in exchanges.\n• No transaction fees, but conversion fees apply on Tether.to.\n• Supports Ethereum network; ending support on EOS, Algorand, and others."
))
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,23 @@ extension View {
) -> some View {
sheet(item: info) { info in
DescriptionBottomSheetView(info: info)
.padding(.bottom, 10)
.adaptivePresentationDetents()
.background(backgroundColor.ignoresSafeArea(.all, edges: .bottom))
}
}

@ViewBuilder
func tokenDescriptionBottomSheet(
info: Binding<DescriptionBottomSheetInfo?>,
backgroundColor: Color?,
onGeneratedAITapAction: (() -> Void)?
) -> some View {
sheet(item: info) { info in
TokenDescriptionBottomSheetView(info: info, generatedWithAIAction: onGeneratedAITapAction)
.adaptivePresentationDetents()
.padding(.bottom, 10)
.background(backgroundColor.ignoresSafeArea(.all, edges: .bottom))
}
}
}
4 changes: 4 additions & 0 deletions TangemApp.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@
2DDFA7952C230E2F00E51F7F /* MarketCapFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DDFA7942C230E2F00E51F7F /* MarketCapFormatter.swift */; };
2DE0BA992CA93100002C09D9 /* BlockHeaderTitleButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DE0BA982CA93100002C09D9 /* BlockHeaderTitleButtonView.swift */; };
2DE0BB602CAD41E3002C09D9 /* BlockHeaderTitleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DE0BB5F2CAD41E3002C09D9 /* BlockHeaderTitleView.swift */; };
2DE0BBB42CAED2CF002C09D9 /* TokenDescriptionBottomSheetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DE0BBB32CAED2CF002C09D9 /* TokenDescriptionBottomSheetView.swift */; };
2DF1D92A2C2C519C00B1CEE7 /* MarketsChartModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DF1D9292C2C519C00B1CEE7 /* MarketsChartModel.swift */; };
2DF5A81F2B13C33D00AA5E73 /* ManageTokensNetworkDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DF5A81E2B13C33C00AA5E73 /* ManageTokensNetworkDataSource.swift */; };
2DFC278D2C78E7A5009B247E /* TokenContextActionsBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DFC278C2C78E7A5009B247E /* TokenContextActionsBuilder.swift */; };
Expand Down Expand Up @@ -2035,6 +2036,7 @@
2DDFA7942C230E2F00E51F7F /* MarketCapFormatter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MarketCapFormatter.swift; sourceTree = "<group>"; };
2DE0BA982CA93100002C09D9 /* BlockHeaderTitleButtonView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlockHeaderTitleButtonView.swift; sourceTree = "<group>"; };
2DE0BB5F2CAD41E3002C09D9 /* BlockHeaderTitleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlockHeaderTitleView.swift; sourceTree = "<group>"; };
2DE0BBB32CAED2CF002C09D9 /* TokenDescriptionBottomSheetView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TokenDescriptionBottomSheetView.swift; sourceTree = "<group>"; };
2DF1D9292C2C519C00B1CEE7 /* MarketsChartModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MarketsChartModel.swift; sourceTree = "<group>"; };
2DF5A81E2B13C33C00AA5E73 /* ManageTokensNetworkDataSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ManageTokensNetworkDataSource.swift; sourceTree = "<group>"; };
2DFC278C2C78E7A5009B247E /* TokenContextActionsBuilder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TokenContextActionsBuilder.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -6332,6 +6334,7 @@
isa = PBXGroup;
children = (
B0EA18AC2C465D860023B72A /* DescriptionBottomSheetView.swift */,
2DE0BBB32CAED2CF002C09D9 /* TokenDescriptionBottomSheetView.swift */,
B0EA18AE2C465DC60023B72A /* View+DescriptionBottomSheet.swift */,
);
path = DescriptionBottomSheet;
Expand Down Expand Up @@ -12172,6 +12175,7 @@
DC5022AC2B98FEB10085A19F /* UserWalletSerializable.swift in Sources */,
B60395A02C3793E900591E18 /* WelcomeOnboardingPushNotificationsView.swift in Sources */,
B04570942B2B071700B0794F /* TotalBalance.swift in Sources */,
2DE0BBB42CAED2CF002C09D9 /* TokenDescriptionBottomSheetView.swift in Sources */,
B0E1564B26B941A4005B33E4 /* SingleCardOnboardingView.swift in Sources */,
DC37A0772A584BD000C2A3F2 /* WalletModelsManagerMock.swift in Sources */,
B0ACC30E2C8EEE5400C985AF /* ManageTokensListLoaderView.swift in Sources */,
Expand Down
Loading