Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package one.mixin.android.api.response

import android.os.Parcelable
import com.google.gson.annotations.SerializedName
import kotlinx.parcelize.Parcelize

@Parcelize
data class UserAddressView(
@SerializedName("destination")
val destination: String,
@SerializedName("chain_id")
val chainId: String
) : Parcelable
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import one.mixin.android.api.response.RampWebUrlResponse
import one.mixin.android.api.response.RouteCreateTokenResponse
import one.mixin.android.api.response.RouteOrderResponse
import one.mixin.android.api.response.RouteTickerResponse
import one.mixin.android.api.response.UserAddressView
import one.mixin.android.api.response.web3.ParsedTx
import one.mixin.android.api.response.web3.QuoteResult
import one.mixin.android.api.response.web3.StakeAccount
Expand Down Expand Up @@ -313,4 +314,10 @@ interface RouteService {
suspend fun rampWebUrl(
@Body request: RampWebUrlRequest
): MixinResponse<RampWebUrlResponse>

@GET("users/{user_id}/address")
suspend fun getUserAddress(
@Path("user_id") userId: String,
@Query("chain_id") chainId: String
): MixinResponse<UserAddressView>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
package one.mixin.android.ui.address

import android.os.Bundle
import android.view.View
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.fragment.app.setFragmentResult
import androidx.fragment.app.viewModels
import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.launch
import one.mixin.android.Constants
import one.mixin.android.R
import one.mixin.android.api.response.UserAddressView
import one.mixin.android.compose.theme.MixinAppTheme
import one.mixin.android.databinding.FragmentComposeBinding
import one.mixin.android.db.web3.vo.Web3TokenItem
import one.mixin.android.extension.getParcelableCompat
import one.mixin.android.extension.openUrl
import one.mixin.android.ui.address.components.FetchAddressContent
import one.mixin.android.ui.address.components.FetchAddressState
import one.mixin.android.ui.address.viewmodel.FetchAddressViewModel
import one.mixin.android.ui.common.BaseFragment
import one.mixin.android.ui.wallet.InputFragment
import one.mixin.android.util.viewBinding
import one.mixin.android.vo.User
import timber.log.Timber

@AndroidEntryPoint
class FetchUserAddressFragment : BaseFragment(R.layout.fragment_compose) {

companion object {
const val TAG = "FetchUserAddressFragment"
const val ARGS_WEB3_TOKEN = "args_web3_token"
const val ARGS_TO_USER = "args_to_user"
const val ARGS_FROM_ADDRESS = "args_from_address"
const val ARGS_CHAIN_TOKEN = "args_chain_token"

fun newInstance(
web3Token: Web3TokenItem,
toUser: User,
fromAddress: String,
chainToken: Web3TokenItem
): FetchUserAddressFragment {
val fragment = FetchUserAddressFragment()
val args = Bundle()
args.putParcelable(ARGS_WEB3_TOKEN, web3Token)
args.putParcelable(ARGS_TO_USER, toUser)
args.putString(ARGS_FROM_ADDRESS, fromAddress)
args.putParcelable(ARGS_CHAIN_TOKEN, chainToken)
fragment.arguments = args
return fragment
}
}

private val binding by viewBinding(FragmentComposeBinding::bind)
private val viewModel by viewModels<FetchAddressViewModel>()

private val web3Token by lazy {
requireArguments().getParcelableCompat(ARGS_WEB3_TOKEN, Web3TokenItem::class.java)
}

private val toUser by lazy {
requireArguments().getParcelableCompat(ARGS_TO_USER, User::class.java)
}

private val fromAddress by lazy {
requireArguments().getString(ARGS_FROM_ADDRESS)
}

private val chainToken by lazy {
requireArguments().getParcelableCompat(ARGS_CHAIN_TOKEN, Web3TokenItem::class.java)
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)

binding.titleView.leftIb.setOnClickListener {
parentFragmentManager.popBackStack()
}
binding.titleView.rightIb.setImageResource(R.drawable.ic_support)
binding.titleView.rightAnimator.visibility = View.VISIBLE
binding.titleView.rightAnimator.displayedChild = 0
binding.titleView.rightAnimator.setOnClickListener {
context?.openUrl(Constants.HelpLink.CUSTOMER_SERVICE)
}

binding.compose.setContent {
MixinAppTheme {
val state by viewModel.state.collectAsState()
val userAddress by viewModel.userAddress.collectAsState()
val errorMessage by viewModel.errorMessage.collectAsState()

FetchAddressContent(
state = state,
errorMessage = errorMessage,
onRetry = {
retryFetchAddress()
},
onCancel = {
parentFragmentManager.popBackStack()
}
)

// Handle success state with navigation
if (state == FetchAddressState.SUCCESS) {
userAddress?.let { address ->
parentFragmentManager.popBackStack()
findNavController().navigate(R.id.action_fetch_user_address_to_input, Bundle().apply {
putParcelable(
InputFragment.ARGS_WEB3_TOKEN,
web3Token
)
putString(InputFragment.ARGS_FROM_ADDRESS, fromAddress)
putString(InputFragment.ARGS_TO_ADDRESS, address.destination)
putParcelable(InputFragment.ARGS_WEB3_CHAIN_TOKEN, chainToken)
putParcelable(InputFragment.ARGS_TO_USER, toUser)
})

}
}
}
}

// Start fetching address when fragment is created
startFetchingAddress()
}

private fun startFetchingAddress() {
if (web3Token == null || toUser == null) {
parentFragmentManager.popBackStack()
return
}

viewModel.fetchUserAddress(toUser!!.userId, web3Token!!.chainId)
}

private fun retryFetchAddress() {
if (web3Token == null || toUser == null) {
parentFragmentManager.popBackStack()
return
}

viewModel.retry(toUser!!.userId, web3Token!!.chainId)
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import android.os.Bundle
import android.view.View
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.ActivityResultRegistry
import androidx.annotation.IdRes
import androidx.annotation.VisibleForTesting
import androidx.compose.animation.AnimatedContentTransitionScope
import androidx.compose.animation.core.tween
Expand All @@ -29,6 +30,7 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import one.mixin.android.R
import one.mixin.android.api.response.UserAddressView
import one.mixin.android.compose.theme.MixinAppTheme
import one.mixin.android.databinding.FragmentAddressInputBinding
import one.mixin.android.db.web3.vo.Web3TokenItem
Expand All @@ -40,11 +42,13 @@ import one.mixin.android.extension.hideKeyboard
import one.mixin.android.extension.indeterminateProgressDialog
import one.mixin.android.extension.isExternalTransferUrl
import one.mixin.android.extension.isLightningUrl
import one.mixin.android.extension.navTo
import one.mixin.android.extension.openPermissionSetting
import one.mixin.android.extension.toast
import one.mixin.android.job.MixinJobManager
import one.mixin.android.job.RefreshAddressJob
import one.mixin.android.job.SyncOutputJob
import one.mixin.android.ui.address.FetchUserAddressFragment.Companion.ARGS_TO_USER
import one.mixin.android.ui.address.page.AddressInputPage
import one.mixin.android.ui.address.page.LabelInputPage
import one.mixin.android.ui.address.page.MemoInputPage
Expand Down Expand Up @@ -216,8 +220,9 @@ class TransferDestinationInputFragment() : BaseFragment(R.layout.fragment_addres
contentText = scannedTransferDest,
toContact = {
requireView().hideKeyboard()
Timber.e("Transfer to contact $token $web3Token")
token?.let { t ->
TransferContactBottomSheetDialogFragment.newInstance(t)
TransferContactBottomSheetDialogFragment.newInstance()
.apply {
onUserClick = { user->
navigateToInputFragmentWithBundle(
Expand All @@ -232,6 +237,38 @@ class TransferDestinationInputFragment() : BaseFragment(R.layout.fragment_addres
TransferContactBottomSheetDialogFragment.TAG
)
}
web3Token?.let { t ->
TransferContactBottomSheetDialogFragment.newInstance()
.apply {
onUserClick = { user ->
this@TransferDestinationInputFragment.lifecycleScope.launch {
val fromAddress = web3ViewModel.getAddressesByChainId(t.walletId, t.chainId)?.destination
if (fromAddress.isNullOrBlank()) {
toast(R.string.Alert_Not_Support)
return@launch
}
val chain = chainToken ?: web3ViewModel.web3TokenItemById(t.walletId, t.chainId)
if (chain == null) {
toast(R.string.Alert_Not_Support)
return@launch
}
findNavController().navigate(
R.id.action_transfer_destination_to_fetch_user_address,
Bundle().apply {
putParcelable(ARGS_TO_USER, user)
putParcelable(FetchUserAddressFragment.ARGS_WEB3_TOKEN, t)
putString(FetchUserAddressFragment.ARGS_FROM_ADDRESS, fromAddress)
putParcelable(FetchUserAddressFragment.ARGS_CHAIN_TOKEN, chain)
}
)
}
}
}
.show(
parentFragmentManager,
TransferContactBottomSheetDialogFragment.TAG
)
}
},
toWallet = { fromWalletId ->
requireView().hideKeyboard()
Expand Down
Loading
Loading