feat: add copy button to seed phrase backup screen

Allow users to copy their mnemonic phrase to clipboard from the manual
backup screen for easier migration to Welati Telegram mini app.
This commit is contained in:
2026-02-20 05:15:47 +03:00
parent 03075104b4
commit eb2c6cda55
10 changed files with 76 additions and 12 deletions
@@ -61,4 +61,8 @@ class ManualBackupAdvancedSecretsFragment :
override fun onTapToRevealClicked(item: ManualBackupSecretsVisibilityRvItem) {
viewModel.onTapToRevealClicked(item)
}
override fun onMnemonicCopyClicked(mnemonicString: String) {
viewModel.copyMnemonic(mnemonicString)
}
}
@@ -1,6 +1,7 @@
package io.novafoundation.nova.feature_account_impl.presentation.manualBackup.secrets.advanced
import io.novafoundation.nova.common.base.BaseViewModel
import io.novafoundation.nova.common.resources.ClipboardManager
import io.novafoundation.nova.common.resources.ResourceManager
import io.novafoundation.nova.common.utils.flowOf
import io.novafoundation.nova.feature_account_impl.R
@@ -16,7 +17,8 @@ class ManualBackupAdvancedSecretsViewModel(
private val resourceManager: ResourceManager,
private val router: AccountRouter,
private val payload: ManualBackupCommonPayload,
private val secretsAdapterItemFactory: ManualBackupSecretsAdapterItemFactory
private val secretsAdapterItemFactory: ManualBackupSecretsAdapterItemFactory,
private val clipboardManager: ClipboardManager
) : BaseViewModel() {
val exportList = flowOf { buildSecrets() }
@@ -31,6 +33,11 @@ class ManualBackupAdvancedSecretsViewModel(
router.exportJsonAction(payload.toExportPayload())
}
fun copyMnemonic(mnemonicString: String) {
clipboardManager.addToClipboard(mnemonicString)
showToast(resourceManager.getString(R.string.common_copied))
}
fun backClicked() {
router.back()
}
@@ -8,6 +8,7 @@ import dagger.Provides
import dagger.multibindings.IntoMap
import io.novafoundation.nova.common.di.viewmodel.ViewModelKey
import io.novafoundation.nova.common.di.viewmodel.ViewModelModule
import io.novafoundation.nova.common.resources.ClipboardManager
import io.novafoundation.nova.common.resources.ResourceManager
import io.novafoundation.nova.feature_account_impl.presentation.AccountRouter
import io.novafoundation.nova.feature_account_impl.presentation.manualBackup.secrets.advanced.ManualBackupAdvancedSecretsViewModel
@@ -24,13 +25,15 @@ class ManualBackupAdvancedSecretsModule {
resourceManager: ResourceManager,
router: AccountRouter,
payload: ManualBackupCommonPayload,
secretsAdapterItemFactory: ManualBackupSecretsAdapterItemFactory
secretsAdapterItemFactory: ManualBackupSecretsAdapterItemFactory,
clipboardManager: ClipboardManager
): ViewModel {
return ManualBackupAdvancedSecretsViewModel(
resourceManager,
router,
payload,
secretsAdapterItemFactory
secretsAdapterItemFactory,
clipboardManager
)
}
@@ -8,4 +8,6 @@ interface ManualBackupItemHandler {
fun onExportJsonClick(item: ManualBackupJsonRvItem)
fun onTapToRevealClicked(item: ManualBackupSecretsVisibilityRvItem)
fun onMnemonicCopyClicked(mnemonicString: String)
}
@@ -25,5 +25,6 @@ class ManualBackupMnemonicViewHolder(private val binder: ItemManualBackupMnemoni
binder.manualBackupSecretsMnemonic.setWordsString(item.mnemonic)
binder.manualBackupSecretsMnemonic.showContent(item.isShown)
binder.manualBackupSecretsMnemonic.onContentShownListener { itemHandler.onTapToRevealClicked(item) }
binder.manualBackupSecretsMnemonic.setOnCopyClickListener { itemHandler.onMnemonicCopyClicked(it) }
}
}
@@ -74,4 +74,8 @@ class ManualBackupSecretsFragment : BaseFragment<ManualBackupSecretsViewModel, F
override fun onTapToRevealClicked(item: ManualBackupSecretsVisibilityRvItem) {
viewModel.onTapToRevealClicked(item)
}
override fun onMnemonicCopyClicked(mnemonicString: String) {
viewModel.copyMnemonic(mnemonicString)
}
}
@@ -1,6 +1,7 @@
package io.novafoundation.nova.feature_account_impl.presentation.manualBackup.secrets.main
import io.novafoundation.nova.common.base.BaseViewModel
import io.novafoundation.nova.common.resources.ClipboardManager
import io.novafoundation.nova.common.resources.ResourceManager
import io.novafoundation.nova.common.utils.flowOf
import io.novafoundation.nova.feature_account_api.domain.interfaces.AccountInteractor
@@ -23,7 +24,8 @@ class ManualBackupSecretsViewModel(
private val accountInteractor: AccountInteractor,
private val commonExportSecretsInteractor: CommonExportSecretsInteractor,
private val secretsAdapterItemFactory: ManualBackupSecretsAdapterItemFactory,
private val walletUiUseCase: WalletUiUseCase
private val walletUiUseCase: WalletUiUseCase,
private val clipboardManager: ClipboardManager
) : BaseViewModel() {
val walletModel = walletUiUseCase.walletUiFlowFor(payload.metaId, payload.getChainIdOrNull(), showAddressIcon = true)
@@ -47,6 +49,11 @@ class ManualBackupSecretsViewModel(
router.exportJsonAction(payload.toExportPayload())
}
fun copyMnemonic(mnemonicString: String) {
clipboardManager.addToClipboard(mnemonicString)
showToast(resourceManager.getString(R.string.common_copied))
}
fun backClicked() {
router.back()
}
@@ -8,6 +8,7 @@ import dagger.Provides
import dagger.multibindings.IntoMap
import io.novafoundation.nova.common.di.viewmodel.ViewModelKey
import io.novafoundation.nova.common.di.viewmodel.ViewModelModule
import io.novafoundation.nova.common.resources.ClipboardManager
import io.novafoundation.nova.common.resources.ResourceManager
import io.novafoundation.nova.feature_account_api.domain.interfaces.AccountInteractor
import io.novafoundation.nova.feature_account_api.presenatation.account.wallet.WalletUiUseCase
@@ -30,7 +31,8 @@ class ManualBackupSecretsModule {
accountInteractor: AccountInteractor,
commonExportSecretsInteractor: CommonExportSecretsInteractor,
secretsAdapterItemFactory: ManualBackupSecretsAdapterItemFactory,
walletUiUseCase: WalletUiUseCase
walletUiUseCase: WalletUiUseCase,
clipboardManager: ClipboardManager
): ViewModel {
return ManualBackupSecretsViewModel(
resourceManager,
@@ -39,7 +41,8 @@ class ManualBackupSecretsModule {
accountInteractor,
commonExportSecretsInteractor,
secretsAdapterItemFactory,
walletUiUseCase
walletUiUseCase,
clipboardManager
)
}
@@ -25,6 +25,8 @@ class MnemonicCardView @JvmOverloads constructor(
private val adapter = BackupMnemonicAdapter(this)
private var wordClickedListener: BackupMnemonicAdapter.ItemHandler? = null
private var onCopyClickListener: ((String) -> Unit)? = null
private var currentWords: List<String> = emptyList()
private val binder = ViewMnemonicCardViewBinding.inflate(inflater(), this)
@@ -38,6 +40,12 @@ class MnemonicCardView @JvmOverloads constructor(
binder.mnemonicCardPhrase.setItemPadding(4.dp)
binder.mnemonicCardPhrase.adapter = adapter
binder.mnemonicCardCopy.setOnClickListener {
if (currentWords.isNotEmpty()) {
onCopyClickListener?.invoke(currentWords.joinToString(" "))
}
}
binder.mnemonicCardTitle.text = SpannableFormatter.format(
context.getString(R.string.mnemonic_card_title),
context.getString(R.string.mnemonic_card_title_highlight)
@@ -52,10 +60,12 @@ class MnemonicCardView @JvmOverloads constructor(
}
fun setWords(words: List<MnemonicWord>) {
currentWords = words.map { it.content }
adapter.submitList(words)
}
fun setWordsString(list: List<String>) {
currentWords = list
val words = list.mapIndexed { index, item ->
MnemonicWord(
id = index,
@@ -64,7 +74,11 @@ class MnemonicCardView @JvmOverloads constructor(
removed = false
)
}
setWords(words)
adapter.submitList(words)
}
fun setOnCopyClickListener(listener: ((String) -> Unit)?) {
onCopyClickListener = listener
}
fun setWordClickedListener(listener: BackupMnemonicAdapter.ItemHandler?) {
@@ -15,13 +15,32 @@
android:orientation="vertical"
android:padding="12dp">
<TextView
android:id="@+id/mnemonicCardTitle"
style="@style/TextAppearance.NovaFoundation.SemiBold.SubHeadline"
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="@color/text_secondary"
tools:text="Please do not share with anyone" />
android:orientation="horizontal"
android:gravity="center_vertical">
<TextView
android:id="@+id/mnemonicCardTitle"
style="@style/TextAppearance.NovaFoundation.SemiBold.SubHeadline"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:textColor="@color/text_secondary"
tools:text="Please do not share with anyone" />
<ImageView
android:id="@+id/mnemonicCardCopy"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginStart="8dp"
android:background="?attr/selectableItemBackgroundBorderless"
android:src="@drawable/ic_copy_outline"
app:tint="@color/icon_secondary"
android:contentDescription="@string/common_copied" />
</LinearLayout>
<io.novafoundation.nova.feature_account_impl.presentation.mnemonic.confirm.view.MnemonicContainerView
android:id="@+id/mnemonicCardPhrase"