Updated on 2026-08-14
This commit is contained in:
commit
678c1f4e88
24 changed files with 269 additions and 39 deletions
|
|
@ -17,6 +17,7 @@ RUN apt-get update && apt-get install -y \
|
|||
ruby \
|
||||
ruby-dev \
|
||||
build-essential \
|
||||
jq \
|
||||
&& locale-gen en_US.UTF-8 \
|
||||
&& update-locale LANG=en_US.UTF-8 \
|
||||
&& apt-get clean
|
||||
|
|
|
|||
134
ci_resources/upload_aab_to_huawei_app_gallery.sh
Executable file
134
ci_resources/upload_aab_to_huawei_app_gallery.sh
Executable file
|
|
@ -0,0 +1,134 @@
|
|||
#!/bin/bash
|
||||
|
||||
set -eo pipefail
|
||||
|
||||
# ----------------------------------------
|
||||
# 1. Obtain OAuth access token
|
||||
# ----------------------------------------
|
||||
# Sending a POST request to retrieve access token using client credentials
|
||||
TOKEN=$(curl -s -v -X POST "https://connect-api.cloud.huawei.com/api/oauth2/v1/token" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{
|
||||
\"grant_type\": \"client_credentials\",
|
||||
\"client_id\": \"$CLIENT_ID\",
|
||||
\"client_secret\": \"$CLIENT_SECRET\"
|
||||
}" | jq -r .access_token)
|
||||
|
||||
echo "------------------------------------------------"
|
||||
echo "[INFO] Token = $TOKEN"
|
||||
echo "------------------------------------------------"
|
||||
|
||||
# ----------------------------------------
|
||||
# 2. Get upload information (temporary URL + required headers)
|
||||
# ----------------------------------------
|
||||
# Extract file name and size for the upload request
|
||||
echo "[INFO] File path = $FILE_PATH"
|
||||
FILE_NAME=$(basename "$FILE_PATH")
|
||||
echo "[INFO] File name = $FILE_NAME"
|
||||
CONTENT_LENGTH=$(stat -c %s "$FILE_PATH")
|
||||
echo "[INFO] Content length = $CONTENT_LENGTH"
|
||||
|
||||
# Request upload URL and headers for OBS (Huawei Object Storage Service)
|
||||
UPLOAD_INFO=$(curl -s -v -X GET \
|
||||
"https://connect-api.cloud.huawei.com/api/publish/v2/upload-url/for-obs?appId=$APP_ID&fileName=$FILE_NAME&contentLength=$CONTENT_LENGTH" \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "client_id: $CLIENT_ID" \
|
||||
-H "Content-Type: application/json")
|
||||
|
||||
echo "------------------------------------------------"
|
||||
echo "[INFO] Upload: $UPLOAD_INFO"
|
||||
echo "------------------------------------------------"
|
||||
|
||||
# ----------------------------------------
|
||||
# 3. Upload the AAB file to Huawei OBS using signed URL and headers
|
||||
# ----------------------------------------
|
||||
# Parse the response to get upload URL and required headers
|
||||
UPLOAD_URL=$(echo "$UPLOAD_INFO" | jq -r '.urlInfo.url')
|
||||
HEADERS=$(echo "$UPLOAD_INFO" | jq -r '.urlInfo.headers')
|
||||
|
||||
echo "------------------------------------------------"
|
||||
echo "[INFO] Parsed upload URL: $UPLOAD_URL"
|
||||
echo "[INFO] Extracting headers..."
|
||||
echo "------------------------------------------------"
|
||||
|
||||
# Extract individual headers for the PUT request
|
||||
AUTH_HEADER=$(echo "$HEADERS" | jq -r '."Authorization"')
|
||||
SHA256_HEADER=$(echo "$HEADERS" | jq -r '."x-amz-content-sha256"')
|
||||
DATE_HEADER=$(echo "$HEADERS" | jq -r '."x-amz-date"')
|
||||
HOST_HEADER=$(echo "$HEADERS" | jq -r '."Host"')
|
||||
UA_HEADER=$(echo "$HEADERS" | jq -r '."user-agent"')
|
||||
CT_HEADER=$(echo "$HEADERS" | jq -r '."Content-Type"')
|
||||
|
||||
# Log headers (for debug purposes)
|
||||
echo "[INFO] Authorization: $AUTH_HEADER"
|
||||
echo "[INFO] x-amz-content-sha256: $SHA256_HEADER"
|
||||
echo "[INFO] x-amz-date: $DATE_HEADER"
|
||||
echo "[INFO] Host: $HOST_HEADER"
|
||||
echo "[INFO] user-agent: $UA_HEADER"
|
||||
echo "[INFO] Content-Type: $CT_HEADER"
|
||||
echo "------------------------------------------------"
|
||||
|
||||
# Perform the actual file upload using the signed PUT URL
|
||||
echo "[INFO] Uploading file '$FILE_PATH' to Huawei OBS..."
|
||||
RESPONSE=$(curl -v -X PUT "$UPLOAD_URL" \
|
||||
-H "Authorization: $AUTH_HEADER" \
|
||||
-H "x-amz-content-sha256: $SHA256_HEADER" \
|
||||
-H "x-amz-date: $DATE_HEADER" \
|
||||
-H "Host: $HOST_HEADER" \
|
||||
-H "user-agent: $UA_HEADER" \
|
||||
-H "Content-Type: $CT_HEADER" \
|
||||
--data-binary @"$FILE_PATH" 2>&1)
|
||||
|
||||
echo "$RESPONSE"
|
||||
echo "------------------------------------------------"
|
||||
|
||||
# Check if upload succeeded
|
||||
if echo "$RESPONSE" | grep -q "HTTP/1.1 200"; then
|
||||
echo "[SUCCESS] File upload completed successfully."
|
||||
else
|
||||
echo "[ERROR] File upload failed:"
|
||||
echo "$RESPONSE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# ----------------------------------------
|
||||
# 4. Commit the uploaded file to AppGallery (register the uploaded file)
|
||||
# ----------------------------------------
|
||||
# Huawei requires an additional API call to register the uploaded file to the app
|
||||
echo "[INFO] Committing uploaded AAB file to AppGallery..."
|
||||
|
||||
# Extract path (excluding hostname) from the upload URL
|
||||
FILE_DEST_URL=$(echo "$UPLOAD_URL" | sed -E 's|https://[^/]+/||')
|
||||
|
||||
# Prepare commit payload
|
||||
COMMIT_PAYLOAD=$(cat <<EOF
|
||||
{
|
||||
"fileType": 5,
|
||||
"files": [{
|
||||
"fileName": "$FILE_NAME",
|
||||
"fileDestUrl": "$FILE_DEST_URL"
|
||||
}]
|
||||
}
|
||||
EOF
|
||||
)
|
||||
|
||||
# Send PUT request to finalize (commit) the uploaded file
|
||||
COMMIT_RESPONSE=$(curl -s -X PUT \
|
||||
"https://connect-api.cloud.huawei.com/api/publish/v2/app-file-info?appId=$APP_ID" \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "client_id: $CLIENT_ID" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "$COMMIT_PAYLOAD")
|
||||
|
||||
# Log commit response and validate success
|
||||
echo "------------------------------------------------"
|
||||
echo "[INFO] Commit response:"
|
||||
echo "$COMMIT_RESPONSE"
|
||||
|
||||
if echo "$COMMIT_RESPONSE" | jq -e '.ret.code == 0' > /dev/null; then
|
||||
echo "[SUCCESS] File committed successfully and is now visible in AppGallery Console."
|
||||
else
|
||||
echo "[ERROR] Failed to commit file:"
|
||||
echo "$COMMIT_RESPONSE"
|
||||
exit 1
|
||||
fi
|
||||
|
|
@ -49,7 +49,7 @@
|
|||
},
|
||||
{
|
||||
"name": "NEW_ATTESTATION_ENABLED",
|
||||
"version": "5.23.0"
|
||||
"version": "undefined"
|
||||
},
|
||||
{
|
||||
"name": "TWIN_REFACTORING_ENABLED",
|
||||
|
|
|
|||
|
|
@ -23,9 +23,10 @@ internal fun <T : Any> Response<T>.toSafeApiResponse(analyticsErrorHandler: Anal
|
|||
if (code == null) {
|
||||
ApiResponseError.UnknownException(IllegalArgumentException("Unknown error status code: ${code()}"))
|
||||
} else {
|
||||
sendHttpError(code, analyticsErrorHandler)
|
||||
val errorBody = errorBody()?.string().orEmpty() // !!!Beware!!! string() closes stream after invocation
|
||||
sendHttpError(code, analyticsErrorHandler, errorBody)
|
||||
|
||||
ApiResponseError.HttpException(code, message(), errorBody()?.string())
|
||||
ApiResponseError.HttpException(code, message(), errorBody)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e, "UnknownException occured")
|
||||
|
|
@ -39,6 +40,7 @@ internal fun <T : Any> Response<T>.toSafeApiResponse(analyticsErrorHandler: Anal
|
|||
private fun <T : Any> Response<T>.sendHttpError(
|
||||
code: ApiResponseError.HttpException.Code,
|
||||
analyticsErrorHandler: AnalyticsErrorHandler,
|
||||
errorBody: String,
|
||||
) {
|
||||
val fullRequestUrl = raw().request.url.toUrl()
|
||||
val shortUrl = fullRequestUrl.authority + fullRequestUrl.path
|
||||
|
|
@ -46,7 +48,7 @@ private fun <T : Any> Response<T>.sendHttpError(
|
|||
ApiErrorEvent(
|
||||
endpoint = shortUrl,
|
||||
code = code.numericCode,
|
||||
message = errorBody()?.string().orEmpty(),
|
||||
message = errorBody,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ import com.tangem.domain.managetokens.model.ManagedCryptoCurrency.SourceNetwork
|
|||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.tokens.model.Network
|
||||
import timber.log.Timber
|
||||
|
||||
internal class ManagedCryptoCurrencyFactory(
|
||||
private val excludedBlockchains: ExcludedBlockchains,
|
||||
|
|
@ -187,11 +188,18 @@ internal class ManagedCryptoCurrencyFactory(
|
|||
decimals = blockchain.decimals(),
|
||||
isL2Network = l2BlockchainsList.contains(blockchain),
|
||||
)
|
||||
network.canHandleTokens -> SourceNetwork.Default(
|
||||
network = network,
|
||||
decimals = decimals ?: return null,
|
||||
contractAddress = contractAddress,
|
||||
)
|
||||
network.canHandleTokens -> {
|
||||
val formattedContractAddress = blockchain.reformatContractAddress(contractAddress)
|
||||
if (formattedContractAddress == null) {
|
||||
Timber.w("Couldn't reformat $contractAddress")
|
||||
return null
|
||||
}
|
||||
SourceNetwork.Default(
|
||||
network = network,
|
||||
decimals = decimals ?: return null,
|
||||
contractAddress = formattedContractAddress,
|
||||
)
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ internal class TokenMarketInfoConverter(
|
|||
TokenMarketInfo.Network(
|
||||
networkId = network.networkId,
|
||||
exchangeable = network.exchangeable,
|
||||
contractAddress = network.contractAddress,
|
||||
contractAddress = blockchain.reformatContractAddress(network.contractAddress),
|
||||
decimalCount = network.decimalCount,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -63,11 +63,17 @@ internal class DefaultOnrampTransactionRepository(
|
|||
}.map(transactionConverter::convert)
|
||||
}
|
||||
|
||||
override suspend fun updateTransactionStatus(externalTxId: String, status: OnrampStatus.Status) =
|
||||
withContext(dispatchers.io) {
|
||||
val updatedTx = getTransactionById(externalTxId)?.copy(status = status) ?: return@withContext
|
||||
storeTransaction(updatedTx)
|
||||
}
|
||||
override suspend fun updateTransactionStatus(
|
||||
externalTxId: String,
|
||||
externalTxUrl: String,
|
||||
status: OnrampStatus.Status,
|
||||
) = withContext(dispatchers.io) {
|
||||
val updatedTx = getTransactionById(externalTxId)?.copy(
|
||||
externalTxUrl = externalTxUrl,
|
||||
status = status,
|
||||
) ?: return@withContext
|
||||
storeTransaction(updatedTx)
|
||||
}
|
||||
|
||||
override suspend fun removeTransaction(externalTxId: String) {
|
||||
withContext(dispatchers.io) {
|
||||
|
|
|
|||
|
|
@ -10,7 +10,12 @@ class OnrampUpdateTransactionStatusUseCase(
|
|||
private val errorResolver: OnrampErrorResolver,
|
||||
) {
|
||||
|
||||
suspend operator fun invoke(externalTxId: String, status: OnrampStatus.Status) = Either.catch {
|
||||
onrampTransactionRepository.updateTransactionStatus(externalTxId = externalTxId, status = status)
|
||||
}.mapLeft(errorResolver::resolve)
|
||||
suspend operator fun invoke(externalTxId: String, externalTxUrl: String, status: OnrampStatus.Status) =
|
||||
Either.catch {
|
||||
onrampTransactionRepository.updateTransactionStatus(
|
||||
externalTxId = externalTxId,
|
||||
externalTxUrl = externalTxUrl,
|
||||
status = status,
|
||||
)
|
||||
}.mapLeft(errorResolver::resolve)
|
||||
}
|
||||
|
|
@ -14,7 +14,7 @@ interface OnrampTransactionRepository {
|
|||
|
||||
fun getTransactions(userWalletId: UserWalletId, cryptoCurrencyId: CryptoCurrency.ID): Flow<List<OnrampTransaction>>
|
||||
|
||||
suspend fun updateTransactionStatus(externalTxId: String, status: OnrampStatus.Status)
|
||||
suspend fun updateTransactionStatus(externalTxId: String, externalTxUrl: String, status: OnrampStatus.Status)
|
||||
|
||||
suspend fun removeTransaction(externalTxId: String)
|
||||
}
|
||||
|
|
@ -87,7 +87,6 @@ internal class OnrampStateFactory(
|
|||
topBarConfig = state.topBarConfig.copy(endButtonUM = endButton),
|
||||
buyButtonConfig = state.buyButtonConfig.copy(enabled = false),
|
||||
amountBlockState = state.amountBlockState.copy(
|
||||
amountFieldModel = state.amountBlockState.amountFieldModel.copy(isError = true),
|
||||
secondaryFieldModel = OnrampAmountSecondaryFieldUM.Content(TextReference.EMPTY),
|
||||
),
|
||||
providerBlockState = OnrampProviderBlockUM.Empty,
|
||||
|
|
|
|||
|
|
@ -335,6 +335,7 @@ internal class OnrampMainComponentModel @Inject constructor(
|
|||
(it as? OnrampMainComponentUM.Content)?.copy(
|
||||
errorNotification = null,
|
||||
providerBlockState = OnrampProviderBlockUM.Loading,
|
||||
amountBlockState = it.amountBlockState.copy(secondaryFieldModel = OnrampAmountSecondaryFieldUM.Loading),
|
||||
) ?: it
|
||||
}
|
||||
startLoadingQuotes()
|
||||
|
|
|
|||
|
|
@ -42,13 +42,16 @@ internal fun OnrampAmountContent(state: OnrampAmountBlockUM, modifier: Modifier
|
|||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
OnrampCurrencyIcon(currencyUM = state.currencyUM)
|
||||
OnrampAmountField(amountField = state.amountFieldModel)
|
||||
OnrampAmountField(
|
||||
amountField = state.amountFieldModel,
|
||||
isLoading = state.secondaryFieldModel is OnrampAmountSecondaryFieldUM.Loading,
|
||||
)
|
||||
OnrampAmountSecondary(state = state.secondaryFieldModel)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun OnrampAmountField(amountField: AmountFieldModel) {
|
||||
private fun OnrampAmountField(amountField: AmountFieldModel, isLoading: Boolean) {
|
||||
val decimalFormat = rememberDecimalFormat()
|
||||
val requester = remember { FocusRequester() }
|
||||
AmountTextField(
|
||||
|
|
@ -67,7 +70,7 @@ private fun OnrampAmountField(amountField: AmountFieldModel) {
|
|||
color = TangemTheme.colors.text.primary1,
|
||||
textAlign = TextAlign.Center,
|
||||
),
|
||||
isEnabled = !amountField.isError,
|
||||
isEnabled = !amountField.isError && !isLoading,
|
||||
isAutoResize = true,
|
||||
isValuePasted = amountField.isValuePasted,
|
||||
onValuePastedTriggerDismiss = amountField.onValuePastedTriggerDismiss,
|
||||
|
|
|
|||
|
|
@ -232,7 +232,7 @@ internal class DefaultSendComponent @AssistedInject constructor(
|
|||
params = SendConfirmComponent.Params(
|
||||
state = model.uiState.value,
|
||||
userWallet = model.userWallet,
|
||||
currentRoute = currentRoute.filterIsInstance<CommonSendRoute.Confirm>(),
|
||||
currentRoute = currentRoute,
|
||||
isBalanceHidingFlow = model.isBalanceHiddenFlow,
|
||||
analyticsCategoryName = SendAnalyticEvents.SEND_CATEGORY,
|
||||
cryptoCurrencyStatus = model.cryptoCurrencyStatus,
|
||||
|
|
|
|||
|
|
@ -141,7 +141,7 @@ internal class SendConfirmComponent(
|
|||
val feeCryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
val appCurrency: AppCurrency,
|
||||
val callback: ModelCallback,
|
||||
val currentRoute: Flow<CommonSendRoute.Confirm>,
|
||||
val currentRoute: Flow<CommonSendRoute>,
|
||||
val isBalanceHidingFlow: StateFlow<Boolean>,
|
||||
val predefinedValues: PredefinedValues,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -430,7 +430,7 @@ internal class SendConfirmModel @Inject constructor(
|
|||
flow = uiState,
|
||||
flow2 = params.currentRoute,
|
||||
transform = { state, route -> state to route },
|
||||
).onEach { (state, _) ->
|
||||
).filter { it.second is CommonSendRoute.Confirm }.onEach { (state, _) ->
|
||||
val amountUM = state.amountUM as? AmountState.Data
|
||||
val confirmUM = state.confirmUM
|
||||
params.callback.onResult(
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ import com.tangem.features.send.v2.send.confirm.SendConfirmComponent
|
|||
import com.tangem.features.send.v2.send.confirm.model.SendConfirmAlertFactory
|
||||
import com.tangem.features.send.v2.send.ui.state.SendUM
|
||||
import com.tangem.features.send.v2.subcomponents.amount.SendAmountComponent
|
||||
import com.tangem.features.send.v2.subcomponents.amount.SendAmountUpdateQRTrigger
|
||||
import com.tangem.features.send.v2.subcomponents.destination.SendDestinationComponent
|
||||
import com.tangem.features.send.v2.subcomponents.destination.model.transformers.SendDestinationInitialStateTransformer
|
||||
import com.tangem.features.send.v2.subcomponents.destination.ui.state.DestinationUM
|
||||
|
|
@ -75,6 +76,7 @@ internal class SendModel @Inject constructor(
|
|||
private val getCardInfoUseCase: GetCardInfoUseCase,
|
||||
private val sendFeedbackEmailUseCase: SendFeedbackEmailUseCase,
|
||||
private val getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase,
|
||||
private val sendAmountUpdateQRTrigger: SendAmountUpdateQRTrigger,
|
||||
) : Model(), SendComponentCallback {
|
||||
|
||||
private val params: SendComponent.Params = paramsContainer.require()
|
||||
|
|
@ -111,6 +113,7 @@ internal class SendModel @Inject constructor(
|
|||
}
|
||||
|
||||
override fun onAmountResult(amountUM: AmountState) {
|
||||
resetPredefinedAmount()
|
||||
_uiState.update { it.copy(amountUM = amountUM) }
|
||||
}
|
||||
|
||||
|
|
@ -119,9 +122,20 @@ internal class SendModel @Inject constructor(
|
|||
}
|
||||
|
||||
override fun onResult(sendUM: SendUM) {
|
||||
resetPredefinedAmount()
|
||||
_uiState.update { sendUM }
|
||||
}
|
||||
|
||||
private fun resetPredefinedAmount() {
|
||||
// reset predefined amount
|
||||
val internalPredefinedValues = predefinedValues
|
||||
predefinedValues = when (internalPredefinedValues) {
|
||||
is PredefinedValues.Content.Deeplink -> internalPredefinedValues.copy(amount = "")
|
||||
is PredefinedValues.Content.QrCode -> internalPredefinedValues.copy(amount = "")
|
||||
PredefinedValues.Empty -> internalPredefinedValues
|
||||
}
|
||||
}
|
||||
|
||||
private fun initAppCurrency() {
|
||||
modelScope.launch {
|
||||
appCurrency = getSelectedAppCurrencyUseCase.invokeSync().getOrElse { AppCurrency.Default }
|
||||
|
|
@ -228,11 +242,18 @@ internal class SendModel @Inject constructor(
|
|||
|
||||
private fun onQrCodeScanned(address: String) {
|
||||
val parsedQrCode = parseQrCodeUseCase(address, cryptoCurrency).getOrNull()
|
||||
// Decompose component can be active or inactive depending on its state and navigation stack
|
||||
// If it is in inactive state use parameter to pass value to amount component
|
||||
val amount = parsedQrCode?.amount?.parseBigDecimal(cryptoCurrency.decimals)
|
||||
predefinedValues = PredefinedValues.Content.QrCode(
|
||||
amount = parsedQrCode?.amount?.parseBigDecimal(cryptoCurrency.decimals).orEmpty(),
|
||||
amount = amount.orEmpty(),
|
||||
address = parsedQrCode?.address.orEmpty(),
|
||||
memo = parsedQrCode?.memo,
|
||||
)
|
||||
// If it is in active state use flow to update value in amount component
|
||||
modelScope.launch {
|
||||
amount?.let { sendAmountUpdateQRTrigger.triggerUpdateAmount(it) }
|
||||
}
|
||||
}
|
||||
|
||||
private fun onFailedTxEmailClick(errorMessage: String? = null) {
|
||||
|
|
|
|||
|
|
@ -25,14 +25,33 @@ interface SendAmountReduceListener {
|
|||
val ignoreReduceTriggerFlow: Flow<Unit>
|
||||
}
|
||||
|
||||
/**
|
||||
* Trigger amount change from another component.
|
||||
* Different from another triggers because it takes raw string instead of BigDecimal
|
||||
*/
|
||||
interface SendAmountUpdateQRTrigger {
|
||||
suspend fun triggerUpdateAmount(amountValue: String)
|
||||
}
|
||||
|
||||
/**
|
||||
* Trigger amount change from another component.
|
||||
* Different from another triggers because it takes raw string instead of BigDecimal
|
||||
*/
|
||||
interface SendAmountUpdateQRListener {
|
||||
val updateAmountTriggerFlow: Flow<String>
|
||||
}
|
||||
|
||||
@Singleton
|
||||
internal class DefaultSendAmountReduceTrigger @Inject constructor() :
|
||||
SendAmountReduceTrigger,
|
||||
SendAmountReduceListener {
|
||||
SendAmountReduceListener,
|
||||
SendAmountUpdateQRTrigger,
|
||||
SendAmountUpdateQRListener {
|
||||
|
||||
override val reduceToTriggerFlow = MutableSharedFlow<BigDecimal>()
|
||||
override val reduceByTriggerFlow = MutableSharedFlow<ReduceByData>()
|
||||
override val ignoreReduceTriggerFlow = MutableSharedFlow<Unit>()
|
||||
override val updateAmountTriggerFlow = MutableSharedFlow<String>()
|
||||
|
||||
override suspend fun triggerReduceBy(reduceBy: ReduceByData) {
|
||||
reduceByTriggerFlow.emit(reduceBy)
|
||||
|
|
@ -45,4 +64,8 @@ internal class DefaultSendAmountReduceTrigger @Inject constructor() :
|
|||
override suspend fun triggerIgnoreReduce() {
|
||||
ignoreReduceTriggerFlow.emit(Unit)
|
||||
}
|
||||
|
||||
override suspend fun triggerUpdateAmount(amountValue: String) {
|
||||
updateAmountTriggerFlow.emit(amountValue)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +1,7 @@
|
|||
package com.tangem.features.send.v2.subcomponents.amount.di
|
||||
|
||||
import com.tangem.features.send.v2.subcomponents.amount.*
|
||||
import com.tangem.features.send.v2.subcomponents.amount.DefaultSendAmountReduceTrigger
|
||||
import com.tangem.features.send.v2.subcomponents.amount.SendAmountReduceListener
|
||||
import com.tangem.features.send.v2.subcomponents.amount.SendAmountReduceTrigger
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.hilt.InstallIn
|
||||
|
|
@ -20,4 +19,12 @@ internal interface SendAmountModule {
|
|||
@Singleton
|
||||
@Binds
|
||||
fun provideSendAmountReduceListener(impl: DefaultSendAmountReduceTrigger): SendAmountReduceListener
|
||||
|
||||
@Singleton
|
||||
@Binds
|
||||
fun provideSendAmountUpdateQRListener(impl: DefaultSendAmountReduceTrigger): SendAmountUpdateQRListener
|
||||
|
||||
@Singleton
|
||||
@Binds
|
||||
fun provideSendAmountUpdateQRTrigger(impl: DefaultSendAmountReduceTrigger): SendAmountUpdateQRTrigger
|
||||
}
|
||||
|
|
@ -23,6 +23,7 @@ import com.tangem.features.send.v2.impl.R
|
|||
import com.tangem.features.send.v2.send.ui.state.ButtonsUM
|
||||
import com.tangem.features.send.v2.subcomponents.amount.SendAmountComponentParams
|
||||
import com.tangem.features.send.v2.subcomponents.amount.SendAmountReduceListener
|
||||
import com.tangem.features.send.v2.subcomponents.amount.SendAmountUpdateQRListener
|
||||
import com.tangem.features.send.v2.subcomponents.amount.analytics.SendAmountAnalyticEvents
|
||||
import com.tangem.features.send.v2.subcomponents.amount.analytics.SendAmountAnalyticEvents.SelectedCurrencyType
|
||||
import com.tangem.features.send.v2.subcomponents.fee.SendFeeData
|
||||
|
|
@ -44,6 +45,7 @@ internal class SendAmountModel @Inject constructor(
|
|||
private val getMinimumTransactionAmountSyncUseCase: GetMinimumTransactionAmountSyncUseCase,
|
||||
private val sendAmountReduceListener: SendAmountReduceListener,
|
||||
private val feeReloadTrigger: SendFeeReloadTrigger,
|
||||
private val sendAmountUpdateQRListener: SendAmountUpdateQRListener,
|
||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
) : Model(), AmountScreenClickIntents {
|
||||
|
||||
|
|
@ -65,6 +67,7 @@ internal class SendAmountModel @Inject constructor(
|
|||
subscribeOnAmountReduceByTriggerUpdates()
|
||||
subscribeOnAmountReduceToTriggerUpdates()
|
||||
subscribeOnAmountIgnoreReduceTriggerUpdates()
|
||||
subscribeOnAmountUpdateQRTriggerUpdates()
|
||||
}
|
||||
|
||||
private fun initMinBoundary() {
|
||||
|
|
@ -99,10 +102,10 @@ internal class SendAmountModel @Inject constructor(
|
|||
),
|
||||
)
|
||||
}
|
||||
val predefinedValues = params.predefinedValues as? PredefinedValues.Content
|
||||
if (predefinedValues?.amount != null) {
|
||||
onAmountValueChange(predefinedValues.amount)
|
||||
}
|
||||
}
|
||||
val predefinedValues = params.predefinedValues as? PredefinedValues.Content
|
||||
if (predefinedValues?.amount != null) {
|
||||
onAmountValueChange(predefinedValues.amount)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -207,6 +210,14 @@ internal class SendAmountModel @Inject constructor(
|
|||
.launchIn(modelScope)
|
||||
}
|
||||
|
||||
private fun subscribeOnAmountUpdateQRTriggerUpdates() {
|
||||
sendAmountUpdateQRListener.updateAmountTriggerFlow
|
||||
.onEach { amount ->
|
||||
onAmountValueChange(amount)
|
||||
saveResult()
|
||||
}.launchIn(modelScope)
|
||||
}
|
||||
|
||||
private fun saveResult() {
|
||||
val params = params as? SendAmountComponentParams.AmountParams ?: return
|
||||
params.callback.onAmountResult(uiState.value)
|
||||
|
|
|
|||
|
|
@ -89,6 +89,7 @@ internal fun StakingFeeBlock(feeState: FeeState) {
|
|||
iconRes = R.drawable.ic_bird_24,
|
||||
isSelected = true,
|
||||
paddingValues = PaddingValues(),
|
||||
showDivider = false,
|
||||
)
|
||||
FeeError(feeState)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,9 +15,9 @@ import com.tangem.domain.tokens.model.CryptoCurrency
|
|||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.tokens.model.analytics.TokenOnrampAnalyticsEvent
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.model.TokenDetailsClickIntents
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsState
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.factory.TokenDetailsOnrampTransactionStateConverter
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.model.TokenDetailsClickIntents
|
||||
import com.tangem.utils.Provider
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
|
|
@ -116,7 +116,11 @@ internal class OnrampStatusFactory @AssistedInject constructor(
|
|||
fiatCurrency = onrampTx.fromCurrencyCode,
|
||||
),
|
||||
)
|
||||
onrampUpdateTransactionStatusUseCase(externalTxId = externalTxId, statusModel.status)
|
||||
onrampUpdateTransactionStatusUseCase(
|
||||
externalTxId = externalTxId,
|
||||
externalTxUrl = statusModel.externalTxUrl.orEmpty(),
|
||||
status = statusModel.status,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -72,7 +72,11 @@ internal class OnrampStatusFactory @Inject constructor(
|
|||
fiatCurrency = onrampTx.fromCurrencyCode,
|
||||
),
|
||||
)
|
||||
onrampUpdateTransactionStatusUseCase(externalTxId = externalTxId, statusModel.status)
|
||||
onrampUpdateTransactionStatusUseCase(
|
||||
externalTxId = externalTxId,
|
||||
externalTxUrl = statusModel.externalTxUrl.orEmpty(),
|
||||
status = statusModel.status,
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
# http://www.gradle.org/docs/current/userguide/build_environment.html
|
||||
# Specifies the JVM arguments used for the daemon process.
|
||||
# The setting is particularly useful for tweaking memory settings.
|
||||
org.gradle.jvmargs = -Xmx6144m -XX:MaxMetaspaceSize=768m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
|
||||
org.gradle.jvmargs = -Xmx6144m -XX:MaxMetaspaceSize=768m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 -XX:+UseParallelGC
|
||||
# When configured, Gradle will run in incubating parallel mode.
|
||||
# This option should only be used with decoupled projects. More details, visit
|
||||
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
# https://github.com/tangem/tangem-sdk-android/
|
||||
# https://github.com/tangem/vico
|
||||
|
||||
tangemBlockchainSdk = "develop-1034"
|
||||
tangemBlockchainSdk = "develop-1040"
|
||||
#tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds
|
||||
tangemCardSdk = "develop-455"
|
||||
#tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue