From 2b1d1f165861828007ada31715ed509e94de0fb7 Mon Sep 17 00:00:00 2001 From: polstianka Date: Thu, 31 Oct 2024 00:52:14 -0700 Subject: [PATCH] bug fixeds --- .../wallet/data/account/AccountRepository.kt | 7 ++- .../data/account/source/StorageSource.kt | 2 +- .../wallet/data/account/source/VaultSource.kt | 1 + .../wallet/data/dapps/DAppsRepository.kt | 2 + .../data/dapps/source/DatabaseSource.kt | 2 +- .../tonapps/wallet/data/events/Extensions.kt | 4 +- .../data/events/source/LocalDataSource.kt | 2 +- .../data/passcode/source/PasscodeStore.kt | 2 +- apps/wallet/data/rn/build.gradle.kts | 3 + .../com/tonapps/wallet/data/rn/RNLegacy.kt | 12 +++- .../java/com/tonapps/wallet/data/rn/RNSql.kt | 5 ++ .../core/entities/AssetsExtendedEntity.kt | 3 + .../tonkeeper/core/history/HistoryHelper.kt | 20 +++---- .../list/holder/HistoryActionHolder.kt | 11 +++- .../tonkeeper/extensions/RawMessageEntity.kt | 3 +- .../tonkeeper/koin/viewModelWalletModule.kt | 3 + .../manager/tonconnect/TonConnectManager.kt | 1 + .../ui/base/WalletFragmentFactory.kt | 4 +- .../ui/component/coin/CoinInputView.kt | 3 +- .../ui/screen/browser/dapp/DAppViewModel.kt | 17 ++---- .../browser/search/BrowserSearchScreen.kt | 16 +++++- .../ui/screen/send/main/SendScreen.kt | 22 ++++++- .../ui/screen/send/main/SendViewModel.kt | 10 ++++ .../ui/screen/settings/apps/AppsScreen.kt | 53 +++++++++++++++++ .../ui/screen/settings/apps/AppsViewModel.kt | 57 +++++++++++++++++++ .../ui/screen/settings/apps/list/Adapter.kt | 24 ++++++++ .../ui/screen/settings/apps/list/Item.kt | 35 ++++++++++++ .../settings/apps/list/holder/AppHolder.kt | 32 +++++++++++ .../apps/list/holder/DisconnectAllHolder.kt | 20 +++++++ .../settings/apps/list/holder/EmptyHolder.kt | 11 ++++ .../settings/apps/list/holder/Holder.kt | 11 ++++ .../ui/screen/settings/main/SettingsScreen.kt | 29 ++++++++++ .../screen/settings/main/SettingsViewModel.kt | 25 ++++---- .../ui/screen/settings/main/list/Item.kt | 11 +++- .../ui/screen/wallet/manage/list/Item.kt | 6 +- .../wallet/manage/list/holder/TokenHolder.kt | 20 +++++-- .../src/main/res/layout/view_coin_input.xml | 3 +- .../src/main/res/layout/view_settings_app.xml | 49 ++++++++++++++++ .../layout/view_settings_app_disconnect.xml | 15 +++++ .../res/layout/view_settings_app_empty.xml | 14 +++++ apps/wallet/instance/main/build.gradle.kts | 2 +- .../src/main/res/values-bg/strings.xml | 5 ++ .../src/main/res/values-es/strings.xml | 5 ++ .../src/main/res/values-in/strings.xml | 5 ++ .../src/main/res/values-ru/strings.xml | 7 ++- .../src/main/res/values-tr/strings.xml | 5 ++ .../src/main/res/values-uk/strings.xml | 5 ++ .../src/main/res/values-uz/strings.xml | 5 ++ .../src/main/res/values-zh/strings.xml | 5 ++ .../src/main/res/values/strings.xml | 6 ++ .../blockchain/ton/extensions/AddrStd.kt | 8 +++ .../java/com/tonapps/icu/CurrencyFormatter.kt | 10 +++- .../java/com/tonapps/security/Security.kt | 1 + .../button_small_tertiary_button_selector.xml | 9 +++ ...ton_small_tertiary_foreground_selector.xml | 10 ++++ .../bg_small_tertiary_button_primary.xml | 10 ++++ ui/uikit/core/src/main/res/values/styles.xml | 5 ++ .../icon/src/main/res/drawable/ic_apps_28.xml | 20 +++++++ .../main/res/drawable/ic_link_square_28.xml | 16 ++++++ 59 files changed, 647 insertions(+), 62 deletions(-) create mode 100644 apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/ui/screen/settings/apps/AppsScreen.kt create mode 100644 apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/ui/screen/settings/apps/AppsViewModel.kt create mode 100644 apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/ui/screen/settings/apps/list/Adapter.kt create mode 100644 apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/ui/screen/settings/apps/list/Item.kt create mode 100644 apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/ui/screen/settings/apps/list/holder/AppHolder.kt create mode 100644 apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/ui/screen/settings/apps/list/holder/DisconnectAllHolder.kt create mode 100644 apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/ui/screen/settings/apps/list/holder/EmptyHolder.kt create mode 100644 apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/ui/screen/settings/apps/list/holder/Holder.kt create mode 100644 apps/wallet/instance/app/src/main/res/layout/view_settings_app.xml create mode 100644 apps/wallet/instance/app/src/main/res/layout/view_settings_app_disconnect.xml create mode 100644 apps/wallet/instance/app/src/main/res/layout/view_settings_app_empty.xml create mode 100644 ui/uikit/core/src/main/res/color/button_small_tertiary_button_selector.xml create mode 100644 ui/uikit/core/src/main/res/color/button_small_tertiary_foreground_selector.xml create mode 100644 ui/uikit/core/src/main/res/drawable/bg_small_tertiary_button_primary.xml create mode 100644 ui/uikit/icon/src/main/res/drawable/ic_apps_28.xml create mode 100644 ui/uikit/icon/src/main/res/drawable/ic_link_square_28.xml diff --git a/apps/wallet/data/account/src/main/java/com/tonapps/wallet/data/account/AccountRepository.kt b/apps/wallet/data/account/src/main/java/com/tonapps/wallet/data/account/AccountRepository.kt index 901f19226..8161f748b 100644 --- a/apps/wallet/data/account/src/main/java/com/tonapps/wallet/data/account/AccountRepository.kt +++ b/apps/wallet/data/account/src/main/java/com/tonapps/wallet/data/account/AccountRepository.kt @@ -3,6 +3,7 @@ package com.tonapps.wallet.data.account import android.app.KeyguardManager import android.content.Context import android.util.Log +import com.google.firebase.crashlytics.FirebaseCrashlytics import com.tonapps.blockchain.ton.contract.BaseWalletContract import com.tonapps.blockchain.ton.contract.WalletVersion import com.tonapps.blockchain.ton.contract.walletVersion @@ -19,6 +20,7 @@ import com.tonapps.wallet.data.account.entities.WalletEvent import com.tonapps.wallet.data.account.source.DatabaseSource import com.tonapps.wallet.data.account.source.StorageSource import com.tonapps.wallet.data.account.source.VaultSource +import com.tonapps.wallet.data.core.recordException import com.tonapps.wallet.data.rn.RNLegacy import com.tonapps.wallet.data.rn.data.RNKeystone import com.tonapps.wallet.data.rn.data.RNLedger @@ -69,8 +71,8 @@ class AccountRepository( private val scope = CoroutineScope(Dispatchers.IO + SupervisorJob()) private val database = DatabaseSource(context, scope) - private val storageSource: StorageSource by lazy { StorageSource(context) } - private val vaultSource: VaultSource by lazy { VaultSource(context) } + private val storageSource: StorageSource by lazy(LazyThreadSafetyMode.SYNCHRONIZED) { StorageSource(context) } + private val vaultSource: VaultSource by lazy(LazyThreadSafetyMode.SYNCHRONIZED) { VaultSource(context) } private val migrationHelper = RNMigrationHelper(rnLegacy) private val _selectedStateFlow = MutableStateFlow(SelectedState.Initialization) @@ -376,6 +378,7 @@ class AccountRepository( ) api.tonconnectProof(address.toAccountId(), proof.string(false)) } catch (e: Throwable) { + recordException(e) null } } diff --git a/apps/wallet/data/account/src/main/java/com/tonapps/wallet/data/account/source/StorageSource.kt b/apps/wallet/data/account/src/main/java/com/tonapps/wallet/data/account/source/StorageSource.kt index 64a33ad27..c286c9cc7 100644 --- a/apps/wallet/data/account/src/main/java/com/tonapps/wallet/data/account/source/StorageSource.kt +++ b/apps/wallet/data/account/src/main/java/com/tonapps/wallet/data/account/source/StorageSource.kt @@ -20,7 +20,7 @@ internal class StorageSource(context: Context) { } private val prefs = context.prefs(NAME) - private val encryptedPrefs: SharedPreferences by lazy { Security.pref(context, KEY_ALIAS, NAME) } + private val encryptedPrefs: SharedPreferences by lazy(LazyThreadSafetyMode.SYNCHRONIZED) { Security.pref(context, KEY_ALIAS, NAME) } fun getTonProofToken(publicKey: PublicKeyEd25519): String? { val key = tonProofTokenKey(publicKey) diff --git a/apps/wallet/data/account/src/main/java/com/tonapps/wallet/data/account/source/VaultSource.kt b/apps/wallet/data/account/src/main/java/com/tonapps/wallet/data/account/source/VaultSource.kt index 346831b5e..79316cdd4 100644 --- a/apps/wallet/data/account/src/main/java/com/tonapps/wallet/data/account/source/VaultSource.kt +++ b/apps/wallet/data/account/src/main/java/com/tonapps/wallet/data/account/source/VaultSource.kt @@ -1,6 +1,7 @@ package com.tonapps.wallet.data.account.source import android.content.Context +import android.content.SharedPreferences import androidx.core.content.edit import com.tonapps.blockchain.ton.extensions.hex import com.tonapps.extensions.getByteArray diff --git a/apps/wallet/data/dapps/src/main/java/com/tonapps/wallet/data/dapps/DAppsRepository.kt b/apps/wallet/data/dapps/src/main/java/com/tonapps/wallet/data/dapps/DAppsRepository.kt index e6eaac00c..8b635533e 100644 --- a/apps/wallet/data/dapps/src/main/java/com/tonapps/wallet/data/dapps/DAppsRepository.kt +++ b/apps/wallet/data/dapps/src/main/java/com/tonapps/wallet/data/dapps/DAppsRepository.kt @@ -5,6 +5,7 @@ import android.net.Uri import android.util.Log import androidx.collection.ArrayMap import androidx.core.net.toUri +import com.google.firebase.crashlytics.FirebaseCrashlytics import com.tonapps.blockchain.ton.extensions.toRawAddress import com.tonapps.blockchain.ton.extensions.toUserFriendly import com.tonapps.extensions.map @@ -158,6 +159,7 @@ class DAppsRepository( } return true } catch (e: Throwable) { + recordException(e) return false } } diff --git a/apps/wallet/data/dapps/src/main/java/com/tonapps/wallet/data/dapps/source/DatabaseSource.kt b/apps/wallet/data/dapps/src/main/java/com/tonapps/wallet/data/dapps/source/DatabaseSource.kt index b9aa51808..bbccbd173 100644 --- a/apps/wallet/data/dapps/src/main/java/com/tonapps/wallet/data/dapps/source/DatabaseSource.kt +++ b/apps/wallet/data/dapps/src/main/java/com/tonapps/wallet/data/dapps/source/DatabaseSource.kt @@ -70,7 +70,7 @@ internal class DatabaseSource( private val coroutineContext: CoroutineContext = Dispatchers.IO.limitedParallelism(1) - private val encryptedPrefs: SharedPreferences by lazy { Security.pref(context, KEY_ALIAS, DATABASE_NAME) } + private val encryptedPrefs: SharedPreferences by lazy(LazyThreadSafetyMode.SYNCHRONIZED) { Security.pref(context, KEY_ALIAS, DATABASE_NAME) } private val prefs = context.prefs("tonconnect") private fun createAppTable(db: SQLiteDatabase) { diff --git a/apps/wallet/data/events/src/main/java/com/tonapps/wallet/data/events/Extensions.kt b/apps/wallet/data/events/src/main/java/com/tonapps/wallet/data/events/Extensions.kt index 396fd448e..c7775c8b5 100644 --- a/apps/wallet/data/events/src/main/java/com/tonapps/wallet/data/events/Extensions.kt +++ b/apps/wallet/data/events/src/main/java/com/tonapps/wallet/data/events/Extensions.kt @@ -23,7 +23,7 @@ fun Action.isOutTransfer(accountId: String): Boolean { } val Action.recipient: AccountAddress? - get() = tonTransfer?.recipient ?: jettonTransfer?.recipient ?: nftItemTransfer?.recipient ?: jettonSwap?.userWallet ?: jettonMint?.recipient ?: depositStake?.staker ?: withdrawStake?.staker ?: withdrawStakeRequest?.staker + get() = nftItemTransfer?.recipient ?: tonTransfer?.recipient ?: jettonTransfer?.recipient ?: jettonSwap?.userWallet ?: jettonMint?.recipient ?: depositStake?.staker ?: withdrawStake?.staker ?: withdrawStakeRequest?.staker val Action.sender: AccountAddress? - get() = tonTransfer?.sender ?: jettonTransfer?.sender ?: nftItemTransfer?.sender ?: jettonSwap?.userWallet ?: jettonBurn?.sender ?: depositStake?.staker ?: withdrawStake?.staker ?: withdrawStakeRequest?.staker + get() = nftItemTransfer?.sender ?:tonTransfer?.sender ?: jettonTransfer?.sender ?: jettonSwap?.userWallet ?: jettonBurn?.sender ?: depositStake?.staker ?: withdrawStake?.staker ?: withdrawStakeRequest?.staker diff --git a/apps/wallet/data/events/src/main/java/com/tonapps/wallet/data/events/source/LocalDataSource.kt b/apps/wallet/data/events/src/main/java/com/tonapps/wallet/data/events/source/LocalDataSource.kt index 80c9527f5..847455a89 100644 --- a/apps/wallet/data/events/src/main/java/com/tonapps/wallet/data/events/source/LocalDataSource.kt +++ b/apps/wallet/data/events/src/main/java/com/tonapps/wallet/data/events/source/LocalDataSource.kt @@ -34,7 +34,7 @@ internal class LocalDataSource( private val _decryptedCommentFlow = MutableEffectFlow() val decryptedCommentFlow = _decryptedCommentFlow.shareIn(scope, SharingStarted.WhileSubscribed(), 1) - private val encryptedPrefs: SharedPreferences by lazy { Security.pref(context, KEY_ALIAS, NAME) } + private val encryptedPrefs: SharedPreferences by lazy(LazyThreadSafetyMode.SYNCHRONIZED) { Security.pref(context, KEY_ALIAS, NAME) } private fun keyDecryptedComment(txId: String): String { return "tx_$txId" diff --git a/apps/wallet/data/passcode/src/main/java/com/tonapps/wallet/data/passcode/source/PasscodeStore.kt b/apps/wallet/data/passcode/src/main/java/com/tonapps/wallet/data/passcode/source/PasscodeStore.kt index e90f718bb..aa9109fe5 100644 --- a/apps/wallet/data/passcode/src/main/java/com/tonapps/wallet/data/passcode/source/PasscodeStore.kt +++ b/apps/wallet/data/passcode/src/main/java/com/tonapps/wallet/data/passcode/source/PasscodeStore.kt @@ -19,7 +19,7 @@ class PasscodeStore(context: Context) { private const val KEY_ALIAS = "_com_tonapps_passcode_master_key_" } - private val keyValue: SharedPreferences by lazy { Security.pref(context, KEY_ALIAS, NAME) } + private val keyValue: SharedPreferences by lazy(LazyThreadSafetyMode.SYNCHRONIZED) { Security.pref(context, KEY_ALIAS, NAME) } val hasPinCode: Boolean get() = keyValue.contains(CODE_KEY) && !keyValue.getString(CODE_KEY, null).isNullOrBlank() diff --git a/apps/wallet/data/rn/build.gradle.kts b/apps/wallet/data/rn/build.gradle.kts index ee45ebe33..d50032f8f 100644 --- a/apps/wallet/data/rn/build.gradle.kts +++ b/apps/wallet/data/rn/build.gradle.kts @@ -8,6 +8,9 @@ android { } dependencies { + api(platform(Dependence.Firebase.bom)) + api(Dependence.Firebase.crashlytics) + implementation(Dependence.AndroidX.biometric) implementation(project(Dependence.Lib.sqlite)) implementation(project(Dependence.Lib.security)) diff --git a/apps/wallet/data/rn/src/main/java/com/tonapps/wallet/data/rn/RNLegacy.kt b/apps/wallet/data/rn/src/main/java/com/tonapps/wallet/data/rn/RNLegacy.kt index df80e008a..3f494472e 100644 --- a/apps/wallet/data/rn/src/main/java/com/tonapps/wallet/data/rn/RNLegacy.kt +++ b/apps/wallet/data/rn/src/main/java/com/tonapps/wallet/data/rn/RNLegacy.kt @@ -3,6 +3,7 @@ package com.tonapps.wallet.data.rn import android.content.Context import android.util.Log import androidx.fragment.app.FragmentActivity +import com.google.firebase.crashlytics.FirebaseCrashlytics import com.tonapps.extensions.map import com.tonapps.wallet.data.rn.data.RNDecryptedData import com.tonapps.wallet.data.rn.data.RNFavorites @@ -27,8 +28,14 @@ class RNLegacy( const val DEFAULT_KEYSTORE_ALIAS = "key_v1" } - private val sql = RNSql(context) - private val seedStorage = RNSeedStorage(context) + private val sql: RNSql by lazy(LazyThreadSafetyMode.SYNCHRONIZED) { + RNSql(context) + } + + private val seedStorage: RNSeedStorage by lazy(LazyThreadSafetyMode.SYNCHRONIZED) { + RNSeedStorage(context) + } + private var cacheWallets: RNWallets? = null @Volatile @@ -121,6 +128,7 @@ class RNLegacy( try { seedStorage.get(passcode) } catch (e: Throwable) { + FirebaseCrashlytics.getInstance().recordException(e) RNVaultState() } } diff --git a/apps/wallet/data/rn/src/main/java/com/tonapps/wallet/data/rn/RNSql.kt b/apps/wallet/data/rn/src/main/java/com/tonapps/wallet/data/rn/RNSql.kt index 98b240c0a..261858de4 100644 --- a/apps/wallet/data/rn/src/main/java/com/tonapps/wallet/data/rn/RNSql.kt +++ b/apps/wallet/data/rn/src/main/java/com/tonapps/wallet/data/rn/RNSql.kt @@ -4,6 +4,7 @@ import android.content.ContentValues import android.content.Context import android.database.sqlite.SQLiteDatabase import android.os.SystemClock +import com.google.firebase.crashlytics.FirebaseCrashlytics import com.tonapps.sqlite.SQLiteHelper import org.json.JSONArray import org.json.JSONObject @@ -41,6 +42,7 @@ internal class RNSql(context: Context): SQLiteHelper(context, DATABASE_NAME, DAT db.close() return value } catch (e: Throwable) { + FirebaseCrashlytics.getInstance().recordException(e) if (attempt > 3) { return null } @@ -63,6 +65,7 @@ internal class RNSql(context: Context): SQLiteHelper(context, DATABASE_NAME, DAT db.insertWithOnConflict(KV_TABLE_NAME, null, values, SQLiteDatabase.CONFLICT_REPLACE) db.close() } catch (e: Throwable) { + FirebaseCrashlytics.getInstance().recordException(e) if (attempt > 3) { return } @@ -76,6 +79,7 @@ internal class RNSql(context: Context): SQLiteHelper(context, DATABASE_NAME, DAT return try { JSONObject(string) } catch (e: Throwable) { + FirebaseCrashlytics.getInstance().recordException(e) null } } @@ -85,6 +89,7 @@ internal class RNSql(context: Context): SQLiteHelper(context, DATABASE_NAME, DAT return try { JSONArray(string) } catch (e: Throwable) { + FirebaseCrashlytics.getInstance().recordException(e) null } } diff --git a/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/core/entities/AssetsExtendedEntity.kt b/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/core/entities/AssetsExtendedEntity.kt index 9f384134d..9f842d532 100644 --- a/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/core/entities/AssetsExtendedEntity.kt +++ b/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/core/entities/AssetsExtendedEntity.kt @@ -64,6 +64,9 @@ data class AssetsExtendedEntity( val verified: Boolean get() = token.verified + val blacklist: Boolean + get() = token.blacklist + val pinned: Boolean get() = prefs.pinned diff --git a/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/core/history/HistoryHelper.kt b/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/core/history/HistoryHelper.kt index 91f0ee892..d1d81285c 100644 --- a/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/core/history/HistoryHelper.kt +++ b/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/core/history/HistoryHelper.kt @@ -591,11 +591,10 @@ class HistoryHelper( } else if (action.nftItemTransfer != null) { val nftItemTransfer = action.nftItemTransfer!! - val isOut = !wallet.isMyAddress(nftItemTransfer.recipient?.address ?: "-") - val sender = nftItemTransfer.sender ?: action.simplePreview.accounts.firstOrNull() - val isBurn = nftItemTransfer.recipient?.let { - isBurnAccount(it) - } ?: false + val sender = nftItemTransfer.sender + val recipient = nftItemTransfer.recipient + val isOut = !wallet.isMyAddress(recipient?.address ?: "-") + val isBurn = recipient?.let { isBurnAccount(it) } ?: false val itemAction: ActionType val iconURL: String? @@ -603,15 +602,15 @@ class HistoryHelper( if (isBurn) { itemAction = ActionType.JettonBurn - iconURL = nftItemTransfer.recipient?.iconURL + iconURL = recipient?.iconURL subtitle = api.getBurnAddress() - } else if (isOut || wallet.isMyAddress(nftItemTransfer.sender?.address ?: "")) { + } else if (isOut || wallet.isMyAddress(sender?.address ?: "")) { itemAction = ActionType.NftSend - iconURL = nftItemTransfer.recipient?.iconURL - subtitle = sender?.getNameOrAddress(wallet.testnet, true) ?: "" + iconURL = recipient?.iconURL + subtitle = recipient?.getNameOrAddress(wallet.testnet, true) ?: "" } else { itemAction = ActionType.NftReceived - iconURL = nftItemTransfer.sender?.iconURL + iconURL = sender?.iconURL subtitle = sender?.getNameOrAddress(wallet.testnet, true) ?: "" } @@ -644,6 +643,7 @@ class HistoryHelper( timestamp = timestamp, date = date, dateDetails = dateDetails, + unverifiedToken = nftItem?.verified == false, isOut = isOut, sender = HistoryItem.Account.ofSender(action, wallet.testnet), recipient = HistoryItem.Account.ofRecipient(action, wallet.testnet), diff --git a/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/core/history/list/holder/HistoryActionHolder.kt b/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/core/history/list/holder/HistoryActionHolder.kt index 6946865d4..ffbdd8fb9 100644 --- a/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/core/history/list/holder/HistoryActionHolder.kt +++ b/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/core/history/list/holder/HistoryActionHolder.kt @@ -24,6 +24,7 @@ import com.tonapps.tonkeeper.ui.screen.transaction.TransactionScreen import com.tonapps.tonkeeper.ui.screen.nft.NftScreen import com.tonapps.tonkeeperx.R import com.tonapps.uikit.color.accentGreenColor +import com.tonapps.uikit.color.accentOrangeColor import com.tonapps.uikit.color.iconSecondaryColor import com.tonapps.uikit.color.stateList import com.tonapps.uikit.color.textPrimaryColor @@ -77,7 +78,7 @@ class HistoryActionHolder( override fun onBind(item: HistoryItem.Event) { commentView.clearDrawables() - unverifiedTokenView.visibility = if (item.unverifiedToken) { + unverifiedTokenView.visibility = if (item.nft == null && item.unverifiedToken) { View.VISIBLE } else { View.GONE @@ -210,14 +211,20 @@ class HistoryActionHolder( Navigation.from(context)?.add(NftScreen.newInstance(item.wallet, nft)) } loadNftImage(nft.mediumUri, item.hiddenBalance) - if (item.hiddenBalance) { + if (item.unverifiedToken) { + nftNameView.text = if (item.hiddenBalance) HIDDEN_BALANCE else nft.name + nftCollectionView.text = getString(Localization.nft_unverified) + nftCollectionView.setTextColor(context.accentOrangeColor) + } else if (item.hiddenBalance) { nftNameView.text = HIDDEN_BALANCE nftCollectionView.text = HIDDEN_BALANCE + nftCollectionView.setTextColor(context.textSecondaryColor) } else { nftNameView.text = nft.name nftCollectionView.text = nft.collectionName.ifEmpty { getString(Localization.unnamed_collection) } + nftCollectionView.setTextColor(context.textSecondaryColor) } } diff --git a/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/extensions/RawMessageEntity.kt b/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/extensions/RawMessageEntity.kt index cf153c5e9..85917905b 100644 --- a/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/extensions/RawMessageEntity.kt +++ b/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/extensions/RawMessageEntity.kt @@ -2,6 +2,7 @@ package com.tonapps.tonkeeper.extensions import com.tonapps.blockchain.ton.TONOpCode import com.tonapps.blockchain.ton.TonTransferHelper +import com.tonapps.blockchain.ton.extensions.isBounceable import com.tonapps.blockchain.ton.extensions.loadAddress import com.tonapps.blockchain.ton.extensions.loadCoins import com.tonapps.blockchain.ton.extensions.loadMaybeAddress @@ -142,7 +143,7 @@ fun RawMessageEntity.getWalletTransfer( val builder = WalletTransferBuilder() builder.destination = address builder.messageData = MessageData.Raw(body, newStateInit ?: getStateInitRef()) - // builder.bounceable = address.isBounceable() + builder.bounceable = address.isBounceable() if (newCustomPayload != null) { val defCoins = Coins.of(0.5) if (defCoins.amount.value > coins.amount.value) { diff --git a/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/koin/viewModelWalletModule.kt b/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/koin/viewModelWalletModule.kt index c1182d428..211600efc 100644 --- a/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/koin/viewModelWalletModule.kt +++ b/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/koin/viewModelWalletModule.kt @@ -28,7 +28,9 @@ import com.tonapps.tonkeeper.ui.screen.staking.viewer.StakeViewerViewModel import com.tonapps.tonkeeper.ui.screen.staking.unstake.UnStakeViewModel import com.tonapps.tonkeeper.ui.screen.staking.stake.StakingViewModel import com.tonapps.tonkeeper.ui.screen.send.transaction.SendTransactionViewModel +import com.tonapps.tonkeeper.ui.screen.settings.apps.AppsViewModel import com.tonapps.tonkeeper.ui.screen.staking.withdraw.StakeWithdrawViewModel +import org.koin.core.module.dsl.viewModel import org.koin.core.module.dsl.viewModelOf val viewModelWalletModule = module { @@ -60,4 +62,5 @@ val viewModelWalletModule = module { viewModelOf(::StakeWithdrawViewModel) viewModelOf(::AddContactViewModel) viewModelOf(::EditContactViewModel) + viewModelOf(::AppsViewModel) } \ No newline at end of file diff --git a/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/manager/tonconnect/TonConnectManager.kt b/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/manager/tonconnect/TonConnectManager.kt index d299dfa9d..ed3e6c6e0 100644 --- a/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/manager/tonconnect/TonConnectManager.kt +++ b/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/manager/tonconnect/TonConnectManager.kt @@ -45,6 +45,7 @@ import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.flow.shareIn +import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import org.json.JSONObject diff --git a/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/ui/base/WalletFragmentFactory.kt b/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/ui/base/WalletFragmentFactory.kt index e1513830d..98f32c281 100644 --- a/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/ui/base/WalletFragmentFactory.kt +++ b/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/ui/base/WalletFragmentFactory.kt @@ -10,12 +10,12 @@ class WalletFragmentFactory: FragmentFactory() { val fragmentClass = loadFragmentClass(classLoader, className) val constructors = fragmentClass.constructors if (constructors.size > 1) { - throw IllegalStateException("${fragmentClass.javaClass} class should have only one constructor") + throw IllegalStateException("$fragmentClass class should have only one constructor") } val constructor = constructors.first() val parameters = constructor.parameterTypes if (parameters.size > 1) { - throw IllegalStateException("${fragmentClass.javaClass} class should have only one constructor with one parameter") + throw IllegalStateException("$fragmentClass class should have only one constructor with one parameter") } else if (parameters.isEmpty()) { return fragmentClass.getConstructor().newInstance() } diff --git a/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/ui/component/coin/CoinInputView.kt b/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/ui/component/coin/CoinInputView.kt index 5f9502045..54ade404e 100644 --- a/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/ui/component/coin/CoinInputView.kt +++ b/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/ui/component/coin/CoinInputView.kt @@ -79,7 +79,6 @@ class CoinInputView @JvmOverloads constructor( updateClearViewVisible() } editText.setRightDrawable(suffixDrawable) - editText.compoundDrawablePadding = 36.dp clearView = findViewById(R.id.coin_input_clear) clearView.setOnClickListener { clear() } @@ -97,8 +96,10 @@ class CoinInputView @JvmOverloads constructor( private fun updateSuffix() { if (suffix.isNullOrBlank() || expanded) { suffixDrawable.text = null + editText.compoundDrawablePadding = 0 } else { suffixDrawable.text = suffix + editText.compoundDrawablePadding = 36.dp } invalidate() } diff --git a/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/ui/screen/browser/dapp/DAppViewModel.kt b/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/ui/screen/browser/dapp/DAppViewModel.kt index 7e47ce854..c7981310a 100644 --- a/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/ui/screen/browser/dapp/DAppViewModel.kt +++ b/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/ui/screen/browser/dapp/DAppViewModel.kt @@ -31,8 +31,10 @@ class DAppViewModel( ): BaseWalletVM(app) { val connectionFlow = tonConnectManager.walletConnectionsFlow(wallet).filterList { connection -> - connection.type == AppConnectEntity.Type.Internal && connection.appUrl == url - }.map { it.firstOrNull() } + connection.type == AppConnectEntity.Type.Internal && connection.appUrl.host == url.host + }.map { + it.firstOrNull() + } fun mute() { DAppPushToggleWorker.run( @@ -56,14 +58,7 @@ class DAppViewModel( } } - private suspend fun loadConnection(): AppConnectEntity? = withTimeoutOrNull(2.seconds) { - while (isActive) { - val connection = connectionFlow.first() - if (connection != null) { - return@withTimeoutOrNull connection - } - delay(100) - } - return@withTimeoutOrNull null + private suspend fun loadConnection(): AppConnectEntity? { + return connectionFlow.firstOrNull() } } \ No newline at end of file diff --git a/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/ui/screen/browser/search/BrowserSearchScreen.kt b/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/ui/screen/browser/search/BrowserSearchScreen.kt index 2ee63747f..66c4d232e 100644 --- a/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/ui/screen/browser/search/BrowserSearchScreen.kt +++ b/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/ui/screen/browser/search/BrowserSearchScreen.kt @@ -6,6 +6,7 @@ import android.view.ViewGroup import androidx.appcompat.widget.AppCompatEditText import androidx.appcompat.widget.AppCompatTextView import androidx.core.net.toUri +import androidx.core.view.WindowInsetsCompat import androidx.core.view.updateLayoutParams import androidx.core.widget.doAfterTextChanged import androidx.recyclerview.widget.RecyclerView @@ -21,6 +22,7 @@ import uikit.drawable.FooterDrawable import uikit.extensions.collectFlow import uikit.extensions.doKeyboardAnimation import uikit.extensions.focusWithKeyboard +import uikit.extensions.getRootWindowInsetsCompat import uikit.extensions.hideKeyboard import uikit.extensions.isMaxScrollReached import uikit.utils.RecyclerVerticalScrollListener @@ -58,7 +60,9 @@ class BrowserSearchScreen(wallet: WalletEntity): WalletContextScreen(R.layout.fr bottomMargin = offset } if (!isShowing) { - finish() + view.postDelayed({ + finishAfterHideKeyboard() + }, 300) } } @@ -79,6 +83,16 @@ class BrowserSearchScreen(wallet: WalletEntity): WalletContextScreen(R.layout.fr } } + private fun finishAfterHideKeyboard() { + if (adapter.itemCount > 0) { + return + } + val windowInsets = searchInput.getRootWindowInsetsCompat() ?: return + if (!windowInsets.isVisible(WindowInsetsCompat.Type.ime())) { + finish() + } + } + private fun submitList(items: List) { adapter.submitList(items) { listView.scrollToPosition(0) diff --git a/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/ui/screen/send/main/SendScreen.kt b/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/ui/screen/send/main/SendScreen.kt index 3d9fa244e..97c2b0e7e 100644 --- a/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/ui/screen/send/main/SendScreen.kt +++ b/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/ui/screen/send/main/SendScreen.kt @@ -24,6 +24,7 @@ import com.tonapps.tonkeeper.ui.base.WalletContextScreen import com.tonapps.tonkeeper.ui.component.coin.CoinInputView import com.tonapps.tonkeeper.ui.screen.camera.CameraMode import com.tonapps.tonkeeper.ui.screen.camera.CameraScreen +import com.tonapps.tonkeeper.ui.screen.nft.NftScreen import com.tonapps.tonkeeper.ui.screen.send.InsufficientFundsDialog import com.tonapps.tonkeeper.ui.screen.send.contacts.main.SendContactsScreen import com.tonapps.tonkeeper.ui.screen.send.main.state.SendAmountState @@ -44,9 +45,12 @@ import com.tonapps.wallet.data.collectibles.entities.NftEntity import com.tonapps.wallet.data.core.HIDDEN_BALANCE import com.tonapps.wallet.localization.Localization import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.flow.take import org.koin.core.parameter.parametersOf import org.ton.cell.Cell import uikit.base.BaseFragment +import uikit.dialog.alert.AlertDialog import uikit.extensions.collectFlow import uikit.extensions.doKeyboardAnimation import uikit.extensions.dp @@ -263,8 +267,22 @@ class SendScreen(wallet: WalletEntity) : WalletContextScreen(R.layout.fragment_s initializeArgs(args.targetAddress, args.amountNano, args.text, args.tokenAddress, args.bin) } + private fun confirmSendAll() { + val builder = AlertDialog.Builder(requireContext()) + builder.setMessage(Localization.send_all_balance) + builder.setNegativeButton(Localization.continue_action) { viewModel.sign() } + builder.setPositiveButton(Localization.cancel) + builder.show() + } + private fun signAndSend() { - viewModel.sign() + collectFlow(viewModel.userInputMaxFlow.take(1)) { isMax -> + if (isMax) { + confirmSendAll() + } else { + viewModel.sign() + } + } } private fun openAddressBook() { @@ -392,7 +410,7 @@ class SendScreen(wallet: WalletEntity) : WalletContextScreen(R.layout.fragment_s navigation?.openURL("tonkeeper://activity") navigation?.removeByClass({ postDelayed(2000, ::finish) - }, TokenScreen::class.java) + }, NftScreen::class.java, TokenScreen::class.java) } private fun setDefault() { diff --git a/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/ui/screen/send/main/SendViewModel.kt b/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/ui/screen/send/main/SendViewModel.kt index 7b7413f49..fa2a1c178 100644 --- a/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/ui/screen/send/main/SendViewModel.kt +++ b/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/ui/screen/send/main/SendViewModel.kt @@ -301,6 +301,7 @@ class SendViewModel( ) } + // Using only for UI private val uiTransferAmountFlow = combine( amountTokenFlow, selectedTokenFlow, @@ -310,6 +311,8 @@ class SendViewModel( val value = if (sendTransferType is SendTransferType.Gasless && amount >= token.balance.value) { amount - fee + } else if (token.isTon && amount >= token.balance.value) { + amount - fee } else { amount } @@ -398,6 +401,13 @@ class SendViewModel( builder.build() }.flowOn(Dispatchers.IO).shareIn(viewModelScope, SharingStarted.Eagerly, 1) + val userInputMaxFlow = combine( + userInputFlow, + selectedTokenFlow + ) { input, selected -> + input.amount >= selected.balance.value + } + init { viewModelScope.launch(Dispatchers.IO) { _tokensFlow.value = tokenRepository.get(currency, wallet.accountId, wallet.testnet) diff --git a/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/ui/screen/settings/apps/AppsScreen.kt b/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/ui/screen/settings/apps/AppsScreen.kt new file mode 100644 index 000000000..13a33ef70 --- /dev/null +++ b/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/ui/screen/settings/apps/AppsScreen.kt @@ -0,0 +1,53 @@ +package com.tonapps.tonkeeper.ui.screen.settings.apps + +import android.os.Bundle +import android.util.Log +import android.view.View +import com.tonapps.tonkeeper.koin.walletViewModel +import com.tonapps.tonkeeper.ui.base.BaseListWalletScreen +import com.tonapps.tonkeeper.ui.base.ScreenContext +import com.tonapps.tonkeeper.ui.screen.settings.apps.list.Adapter +import com.tonapps.wallet.data.account.entities.WalletEntity +import com.tonapps.wallet.data.dapps.entities.AppEntity +import com.tonapps.wallet.localization.Localization +import uikit.base.BaseFragment +import uikit.dialog.alert.AlertDialog +import uikit.extensions.collectFlow + +class AppsScreen(wallet: WalletEntity): BaseListWalletScreen(ScreenContext.Wallet(wallet)), BaseFragment.SwipeBack { + + override val viewModel: AppsViewModel by walletViewModel() + + private val adapter = Adapter({ app -> disconnectApp(app) }, { disconnectAll() }) + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + collectFlow(viewModel.uiItemsFlow, adapter::submitList) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + setTitle(getString(Localization.apps)) + setAdapter(adapter) + } + + private fun disconnectApp(app: AppEntity) { + val builder = AlertDialog.Builder(requireContext()) + builder.setMessage(getString(Localization.disconnect_dapp_confirm, app.name)) + builder.setNegativeButton(Localization.disconnect) { viewModel.disconnectApp(app) } + builder.setPositiveButton(Localization.cancel) + builder.show() + } + + private fun disconnectAll() { + val builder = AlertDialog.Builder(requireContext()) + builder.setMessage(getString(Localization.disconnect_all_apps_confirm)) + builder.setNegativeButton(Localization.disconnect) { viewModel.disconnectAll() } + builder.setPositiveButton(Localization.cancel) + builder.show() + } + + companion object { + fun newInstance(wallet: WalletEntity) = AppsScreen(wallet) + } +} \ No newline at end of file diff --git a/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/ui/screen/settings/apps/AppsViewModel.kt b/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/ui/screen/settings/apps/AppsViewModel.kt new file mode 100644 index 000000000..45e8fea9e --- /dev/null +++ b/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/ui/screen/settings/apps/AppsViewModel.kt @@ -0,0 +1,57 @@ +package com.tonapps.tonkeeper.ui.screen.settings.apps + +import android.app.Application +import android.util.Log +import androidx.lifecycle.viewModelScope +import com.tonapps.extensions.mapList +import com.tonapps.extensions.singleValue +import com.tonapps.tonkeeper.manager.tonconnect.TonConnectManager +import com.tonapps.tonkeeper.ui.base.BaseWalletVM +import com.tonapps.tonkeeper.ui.screen.settings.apps.list.Item +import com.tonapps.uikit.list.ListCell +import com.tonapps.wallet.data.account.entities.WalletEntity +import com.tonapps.wallet.data.dapps.entities.AppEntity +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.firstOrNull +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.take +import kotlinx.coroutines.launch + +class AppsViewModel( + application: Application, + private val wallet: WalletEntity, + private val tonConnectManager: TonConnectManager +): BaseWalletVM(application) { + + val uiItemsFlow = tonConnectManager.walletAppsFlow(wallet).map { apps -> + val uiItems = mutableListOf() + if (apps.isNotEmpty()) { + uiItems.add(Item.DisconnectAll) + for ((index, app) in apps.withIndex()) { + val position = ListCell.getPosition(apps.size, index) + uiItems.add( + Item.App( + app = app, + wallet = wallet, + position = position + ) + ) + } + } else { + uiItems.add(Item.Empty) + } + uiItems.toList() + } + + fun disconnectApp(app: AppEntity) { + tonConnectManager.disconnect(wallet, app.url) + } + + fun disconnectAll() { + tonConnectManager.walletAppsFlow(wallet).take(1).collectFlow { apps -> + for (app in apps) { + tonConnectManager.disconnect(wallet, app.url) + } + } + } +} \ No newline at end of file diff --git a/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/ui/screen/settings/apps/list/Adapter.kt b/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/ui/screen/settings/apps/list/Adapter.kt new file mode 100644 index 000000000..2e78ba261 --- /dev/null +++ b/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/ui/screen/settings/apps/list/Adapter.kt @@ -0,0 +1,24 @@ +package com.tonapps.tonkeeper.ui.screen.settings.apps.list + +import android.view.ViewGroup +import com.tonapps.tonkeeper.ui.screen.settings.apps.list.holder.AppHolder +import com.tonapps.tonkeeper.ui.screen.settings.apps.list.holder.DisconnectAllHolder +import com.tonapps.tonkeeper.ui.screen.settings.apps.list.holder.EmptyHolder +import com.tonapps.uikit.list.BaseListAdapter +import com.tonapps.uikit.list.BaseListHolder +import com.tonapps.uikit.list.BaseListItem +import com.tonapps.wallet.data.dapps.entities.AppEntity + +class Adapter( + private val disconnectApp: (app: AppEntity) -> Unit, + private val disconnectAll: () -> Unit +): BaseListAdapter() { + override fun createHolder(parent: ViewGroup, viewType: Int): BaseListHolder { + return when (viewType) { + Item.TYPE_APP -> AppHolder(parent, disconnectApp) + Item.TYPE_DISCONNECT_ALL -> DisconnectAllHolder(parent, disconnectAll) + Item.TYPE_EMPTY -> EmptyHolder(parent) + else -> throw IllegalArgumentException("Unknown viewType: $viewType") + } + } +} \ No newline at end of file diff --git a/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/ui/screen/settings/apps/list/Item.kt b/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/ui/screen/settings/apps/list/Item.kt new file mode 100644 index 000000000..7284c8772 --- /dev/null +++ b/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/ui/screen/settings/apps/list/Item.kt @@ -0,0 +1,35 @@ +package com.tonapps.tonkeeper.ui.screen.settings.apps.list + +import com.tonapps.uikit.list.BaseListItem +import com.tonapps.uikit.list.ListCell +import com.tonapps.wallet.data.account.entities.WalletEntity +import com.tonapps.wallet.data.dapps.entities.AppEntity + +sealed class Item(type: Int): BaseListItem(type) { + + companion object { + const val TYPE_APP = 0 + const val TYPE_DISCONNECT_ALL = 2 + const val TYPE_EMPTY = 3 + } + + data object DisconnectAll: Item(TYPE_DISCONNECT_ALL) + + data object Empty: Item(TYPE_EMPTY) + + data class App( + val app: AppEntity, + val wallet: WalletEntity, + val position: ListCell.Position + ): Item(TYPE_APP) { + + val iconUrl: String + get() = app.iconUrl + + val title: String + get() = app.name + + val host: String + get() = app.host + } +} \ No newline at end of file diff --git a/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/ui/screen/settings/apps/list/holder/AppHolder.kt b/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/ui/screen/settings/apps/list/holder/AppHolder.kt new file mode 100644 index 000000000..6d54c730d --- /dev/null +++ b/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/ui/screen/settings/apps/list/holder/AppHolder.kt @@ -0,0 +1,32 @@ +package com.tonapps.tonkeeper.ui.screen.settings.apps.list.holder + +import android.view.ViewGroup +import android.widget.Button +import androidx.appcompat.widget.AppCompatTextView +import com.facebook.imagepipeline.common.ResizeOptions +import com.tonapps.tonkeeper.ui.screen.settings.apps.list.Item +import com.tonapps.tonkeeperx.R +import com.tonapps.wallet.data.dapps.entities.AppEntity +import uikit.extensions.drawable +import uikit.widget.FrescoView + +class AppHolder( + parent: ViewGroup, + private val disconnectApp: (app: AppEntity) -> Unit +): Holder(parent, R.layout.view_settings_app) { + + private val iconView = findViewById(R.id.icon) + private val titleView = findViewById(R.id.title) + private val hostView = findViewById(R.id.host) + private val disconnectButton = findViewById