[#1528] Coinbase on-ramp integration
* [#1528] Coinbase integration Closes #1528 * [#1528] CI hotfix Closes #1528 * Remove duplicate lines * Improve CI scripts + variable renaming * Remove coinbase button in testnet build * Update changelogs --------- Co-authored-by: Honza <rychnovsky.honza@gmail.com>
This commit is contained in:
parent
fa9ea0c03a
commit
35c01df313
|
@ -63,9 +63,11 @@ jobs:
|
|||
# GOOGLE_PLAY_WORKLOAD_IDENTITY_PROVIDER: ${{ secrets.GOOGLE_PLAY_WORKLOAD_IDENTITY_PROVIDER }}
|
||||
GOOGLE_PLAY_SERVICE_ACCOUNT_KEY: ${{ secrets.GOOGLE_PLAY_SERVICE_ACCOUNT_KEY }}
|
||||
GOOGLE_PLAY_PUBLISHER_API_KEY: ${{ secrets.GOOGLE_PLAY_PUBLISHER_API_KEY }}
|
||||
COINBASE_APP_ID: ${{ secrets.COINBASE_APP_ID }}
|
||||
if: "${{ env.GOOGLE_PLAY_CLOUD_PROJECT != '' &&
|
||||
env.GOOGLE_PLAY_SERVICE_ACCOUNT_KEY != '' &&
|
||||
env.GOOGLE_PLAY_PUBLISHER_API_KEY != ''
|
||||
env.GOOGLE_PLAY_PUBLISHER_API_KEY != '' &&
|
||||
env.COINBASE_APP_ID != ''
|
||||
}}"
|
||||
run: echo "defined=true" >> $GITHUB_OUTPUT
|
||||
|
||||
|
@ -154,6 +156,7 @@ jobs:
|
|||
ORG_GRADLE_PROJECT_ZCASH_RELEASE_KEYSTORE_PASSWORD: ${{ secrets.UPLOAD_KEYSTORE_PASSWORD }}
|
||||
ORG_GRADLE_PROJECT_ZCASH_RELEASE_KEY_ALIAS: ${{ secrets.UPLOAD_KEY_ALIAS }}
|
||||
ORG_GRADLE_PROJECT_ZCASH_RELEASE_KEY_ALIAS_PASSWORD: ${{ secrets.UPLOAD_KEY_ALIAS_PASSWORD }}
|
||||
ORG_GRADLE_PROJECT_ZCASH_COINBASE_APP_ID: ${{ secrets.COINBASE_APP_ID }}
|
||||
run: |
|
||||
./gradlew :app:publishToGooglePlay
|
||||
- name: Collect Artifacts
|
||||
|
|
|
@ -63,6 +63,17 @@ jobs:
|
|||
if: "${{ env.EMULATOR_WTF_API_KEY != '' }}"
|
||||
run: echo "defined=true" >> $GITHUB_OUTPUT
|
||||
|
||||
check_coinbase_secrets:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
has-secrets: ${{ steps.check_coinbase_secrets.outputs.defined }}
|
||||
steps:
|
||||
- id: check_coinbase_secrets
|
||||
env:
|
||||
COINBASE_APP_ID: ${{ secrets.COINBASE_APP_ID }}
|
||||
if: "${{ env.COINBASE_APP_ID != '' }}"
|
||||
run: echo "defined=true" >> $GITHUB_OUTPUT
|
||||
|
||||
check_properties:
|
||||
needs: validate_gradle_wrapper
|
||||
runs-on: ubuntu-latest
|
||||
|
@ -252,8 +263,8 @@ jobs:
|
|||
|
||||
# Emulator.wtf is preferred if it has an API key.
|
||||
test_android_modules_ftl:
|
||||
if: needs.check_firebase_secrets.outputs.has-secrets == 'true' && needs.check_emulator_wtf_secrets.outputs.has-secrets == 'false'
|
||||
needs: [validate_gradle_wrapper, check_firebase_secrets, check_emulator_wtf_secrets]
|
||||
if: needs.check_firebase_secrets.outputs.has-secrets == 'true' && needs.check_emulator_wtf_secrets.outputs.has-secrets == 'false' && needs.check_coinbase_secrets.outputs.has-secrets == 'true'
|
||||
needs: [validate_gradle_wrapper, check_firebase_secrets, check_emulator_wtf_secrets, check_coinbase_secrets]
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
|
@ -295,6 +306,7 @@ jobs:
|
|||
ORG_GRADLE_PROJECT_ZCASH_FIREBASE_TEST_LAB_API_KEY_PATH: ${{ steps.auth_test_lab.outputs.credentials_file_path }}
|
||||
# Because Fulladle doesn't allow Test Orchestrator to be enabled/disabled for a specific submodule, it must be enabled for all modules
|
||||
ORG_GRADLE_PROJECT_IS_USE_TEST_ORCHESTRATOR: true
|
||||
ORG_GRADLE_PROJECT_ZCASH_COINBASE_APP_ID: ${{ secrets.COINBASE_APP_ID }}
|
||||
run: |
|
||||
./gradlew runFlank
|
||||
- name: Collect Artifacts
|
||||
|
@ -316,8 +328,8 @@ jobs:
|
|||
path: ~/artifacts
|
||||
|
||||
test_android_modules_wtf_coverage:
|
||||
if: needs.check_emulator_wtf_secrets.outputs.has-secrets == 'true'
|
||||
needs: [ validate_gradle_wrapper, check_emulator_wtf_secrets ]
|
||||
if: needs.check_emulator_wtf_secrets.outputs.has-secrets == 'true' && needs.check_coinbase_secrets.outputs.has-secrets == 'true'
|
||||
needs: [ validate_gradle_wrapper, check_emulator_wtf_secrets, check_coinbase_secrets ]
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
|
@ -342,6 +354,7 @@ jobs:
|
|||
ORG_GRADLE_PROJECT_ZCASH_DEBUG_APP_NAME_SUFFIX: ""
|
||||
ORG_GRADLE_PROJECT_ZCASH_EMULATOR_WTF_API_KEY: ${{ secrets.EMULATOR_WTF_API_KEY }}
|
||||
ORG_GRADLE_PROJECT_IS_ANDROID_INSTRUMENTATION_TEST_COVERAGE_ENABLED: true
|
||||
ORG_GRADLE_PROJECT_ZCASH_COINBASE_APP_ID: ${{ secrets.COINBASE_APP_ID }}
|
||||
run: |
|
||||
./gradlew testDebugWithEmulatorWtf :ui-integration-test:testZcashmainnetDebugWithEmulatorWtf
|
||||
- name: Collect Artifacts
|
||||
|
@ -363,8 +376,8 @@ jobs:
|
|||
path: ~/artifacts
|
||||
|
||||
test_android_modules_wtf_no_coverage:
|
||||
if: needs.check_emulator_wtf_secrets.outputs.has-secrets == 'true'
|
||||
needs: [ validate_gradle_wrapper, check_emulator_wtf_secrets ]
|
||||
if: needs.check_emulator_wtf_secrets.outputs.has-secrets == 'true' && needs.check_coinbase_secrets.outputs.has-secrets == 'true'
|
||||
needs: [ validate_gradle_wrapper, check_emulator_wtf_secrets, check_coinbase_secrets ]
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
|
@ -389,6 +402,7 @@ jobs:
|
|||
ORG_GRADLE_PROJECT_ZCASH_DEBUG_APP_NAME_SUFFIX: ""
|
||||
ORG_GRADLE_PROJECT_ZCASH_EMULATOR_WTF_API_KEY: ${{ secrets.EMULATOR_WTF_API_KEY }}
|
||||
ORG_GRADLE_PROJECT_IS_ANDROID_INSTRUMENTATION_TEST_COVERAGE_ENABLED: false
|
||||
ORG_GRADLE_PROJECT_ZCASH_COINBASE_APP_ID: ${{ secrets.COINBASE_APP_ID }}
|
||||
run: |
|
||||
./gradlew :app:testZcashmainnetDebugWithEmulatorWtf :ui-screenshot-test:testZcashmainnetDebugWithEmulatorWtf
|
||||
- name: Collect Artifacts
|
||||
|
@ -411,8 +425,8 @@ jobs:
|
|||
|
||||
# Performs a button mash test on the debug build of the app with strict mode enabled
|
||||
test_robo_debug:
|
||||
if: needs.check_firebase_secrets.outputs.has-secrets == 'true'
|
||||
needs: [check_firebase_secrets]
|
||||
if: needs.check_firebase_secrets.outputs.has-secrets == 'true' && needs.check_coinbase_secrets.outputs.has-secrets == 'true'
|
||||
needs: [check_firebase_secrets, check_coinbase_secrets]
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
packages: read
|
||||
|
@ -447,6 +461,7 @@ jobs:
|
|||
env:
|
||||
ORG_GRADLE_PROJECT_ZCASH_SUPPORT_EMAIL_ADDRESS: ${{ vars.SUPPORT_EMAIL_ADDRESS }}
|
||||
ORG_GRADLE_PROJECT_IS_CRASH_ON_STRICT_MODE_VIOLATION: true
|
||||
ORG_GRADLE_PROJECT_ZCASH_COINBASE_APP_ID: ${{ secrets.COINBASE_APP_ID }}
|
||||
run: |
|
||||
./gradlew :app:assembleDebug
|
||||
- name: Authenticate to Google Cloud for Firebase Test Lab
|
||||
|
@ -464,11 +479,13 @@ jobs:
|
|||
# This first environment variable is used by Flank, since the temporary token is missing the project name
|
||||
GOOGLE_CLOUD_PROJECT: ${{ vars.FIREBASE_TEST_LAB_PROJECT }}
|
||||
ORG_GRADLE_PROJECT_ZCASH_FIREBASE_TEST_LAB_API_KEY_PATH: ${{ steps.auth_test_lab.outputs.credentials_file_path }}
|
||||
ORG_GRADLE_PROJECT_COINBASE_APP_ID: ${{ secrets.COINBASE_APP_ID }}
|
||||
run: |
|
||||
./gradlew :app:runFlankSanityConfigDebug
|
||||
|
||||
build:
|
||||
needs: validate_gradle_wrapper
|
||||
if: needs.check_coinbase_secrets.outputs.has-secrets == 'true'
|
||||
needs: [validate_gradle_wrapper, check_coinbase_secrets]
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
|
@ -515,6 +532,7 @@ jobs:
|
|||
ORG_GRADLE_PROJECT_ZCASH_RELEASE_KEYSTORE_PASSWORD: android
|
||||
ORG_GRADLE_PROJECT_ZCASH_RELEASE_KEY_ALIAS: androiddebugkey
|
||||
ORG_GRADLE_PROJECT_ZCASH_RELEASE_KEY_ALIAS_PASSWORD: android
|
||||
ORG_GRADLE_PROJECT_ZCASH_COINBASE_APP_ID: ${{ secrets.COINBASE_APP_ID }}
|
||||
run: |
|
||||
./gradlew :app:assembleDebug :app:bundleRelease :app:packageZcashmainnetReleaseUniversalApk
|
||||
- name: Collect Artifacts
|
||||
|
@ -538,8 +556,8 @@ jobs:
|
|||
|
||||
# Performs a button mash test on the release build of the app
|
||||
test_robo_release:
|
||||
if: needs.check_firebase_secrets.outputs.has-secrets == 'true'
|
||||
needs: [build, check_firebase_secrets]
|
||||
if: needs.check_firebase_secrets.outputs.has-secrets == 'true' && needs.check_coinbase_secrets.outputs.has-secrets == 'true'
|
||||
needs: [build, check_firebase_secrets, check_coinbase_secrets]
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
packages: read
|
||||
|
@ -579,6 +597,7 @@ jobs:
|
|||
# This first environment variable is used by Flank, since the temporary token is missing the project name
|
||||
GOOGLE_CLOUD_PROJECT: ${{ vars.FIREBASE_TEST_LAB_PROJECT }}
|
||||
ORG_GRADLE_PROJECT_ZCASH_FIREBASE_TEST_LAB_API_KEY_PATH: ${{ steps.auth_test_lab.outputs.credentials_file_path }}
|
||||
ORG_GRADLE_PROJECT_ZCASH_COINBASE_APP_ID: ${{ secrets.COINBASE_APP_ID }}
|
||||
run: |
|
||||
unzip ${BINARIES_ZIP_PATH}
|
||||
./gradlew :app:runFlankSanityConfigRelease
|
||||
|
|
|
@ -10,6 +10,7 @@ and this application adheres to [Semantic Versioning](https://semver.org/spec/v2
|
|||
- Transaction resubmission feature has been added. It periodically searches for unmined sent transactions that
|
||||
are still within their expiry window and resubmits them if there are any.
|
||||
- The Choose server screen now provides a new search for the three fastest servers feature
|
||||
- Coinbase Onramp integration button has been added to the Advanced Settings screen
|
||||
|
||||
### Changed
|
||||
- Choose server screen has been redesigned
|
||||
|
|
|
@ -121,6 +121,7 @@ tasks {
|
|||
"ZCASH_GOOGLE_PLAY_DEPLOY_TRACK" to "internal",
|
||||
"ZCASH_GOOGLE_PLAY_DEPLOY_STATUS" to "draft",
|
||||
|
||||
"ZCASH_COINBASE_APP_ID" to "",
|
||||
"SDK_INCLUDED_BUILD_PATH" to "",
|
||||
"BIP_39_INCLUDED_BUILD_PATH" to ""
|
||||
)
|
||||
|
@ -192,12 +193,16 @@ fladle {
|
|||
|
||||
flankVersion.set(libs.versions.flank.get())
|
||||
|
||||
filesToDownload.set(listOf(
|
||||
".*/matrix_.*/.*test_results_merged\\.xml",
|
||||
".*/matrix_.*/.*/artifacts/sdcard/googletest/test_outputfiles/.*\\.png"
|
||||
))
|
||||
filesToDownload.set(
|
||||
listOf(
|
||||
".*/matrix_.*/.*test_results_merged\\.xml",
|
||||
".*/matrix_.*/.*/artifacts/sdcard/googletest/test_outputfiles/.*\\.png"
|
||||
)
|
||||
)
|
||||
|
||||
directoriesToPull.set(listOf(
|
||||
"/sdcard/googletest/test_outputfiles"
|
||||
))
|
||||
directoriesToPull.set(
|
||||
listOf(
|
||||
"/sdcard/googletest/test_outputfiles"
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ directly impact users rather than highlighting other key architectural updates.*
|
|||
- Transaction resubmission feature has been added. It periodically searches for unmined sent transactions that
|
||||
are still within their expiry window and resubmits them if there are any.
|
||||
- The Choose server screen now provides a new search for the three fastest servers feature
|
||||
- Coinbase Onramp integration button has been added to the Advanced Settings screen
|
||||
|
||||
### Changed
|
||||
- Choose server screen has been redesigned
|
||||
|
|
|
@ -84,6 +84,10 @@ IS_SECURE_SCREEN_PROTECTION_ACTIVE=true
|
|||
# Set whether the screen rotation is enabled or the screen orientation is locked in the portrait mode.
|
||||
IS_SCREEN_ROTATION_ENABLED=false
|
||||
|
||||
# Set the Coinbase app project ID to test the Coinbase Onramp integrations locally. Keep it empty as our CI actions
|
||||
# set it up.
|
||||
ZCASH_COINBASE_APP_ID=
|
||||
|
||||
# Set keystore details to enable build signing. Typically these
|
||||
# are overridden via ~/.gradle/gradle.properties to allow secure injection.
|
||||
# Debug keystore is useful if using Google Maps or Firebase, which require API keys to be linked
|
||||
|
@ -184,6 +188,7 @@ ANDROIDX_STARTUP_VERSION=1.1.1
|
|||
ANDROIDX_TEST_SERVICE_VERSION=1.4.2
|
||||
ANDROIDX_UI_AUTOMATOR_VERSION=2.3.0
|
||||
ANDROIDX_WORK_MANAGER_VERSION=2.9.0
|
||||
ANDROIDX_BROWSER_VERSION=1.8.0
|
||||
CORE_LIBRARY_DESUGARING_VERSION=2.0.4
|
||||
FIREBASE_BOM_VERSION_MATCHER=32.8.1
|
||||
GOOGLE_AUTH_LIB_JAVA_VERSION=1.18.0
|
||||
|
|
|
@ -167,6 +167,7 @@ dependencyResolutionManagement {
|
|||
val androidxTestRunnerVersion = extra["ANDROIDX_TEST_RUNNER_VERSION"].toString()
|
||||
val androidxUiAutomatorVersion = extra["ANDROIDX_UI_AUTOMATOR_VERSION"].toString()
|
||||
val androidxWorkManagerVersion = extra["ANDROIDX_WORK_MANAGER_VERSION"].toString()
|
||||
val androidxBrowserVersion = extra["ANDROIDX_BROWSER_VERSION"].toString()
|
||||
val coreLibraryDesugaringVersion = extra["CORE_LIBRARY_DESUGARING_VERSION"].toString()
|
||||
val flankVersion = extra["FLANK_VERSION"].toString()
|
||||
val jacocoVersion = extra["JACOCO_VERSION"].toString()
|
||||
|
@ -220,6 +221,7 @@ dependencyResolutionManagement {
|
|||
library("androidx-startup", "androidx.startup:startup-runtime:$androidxStartupVersion")
|
||||
library("androidx-viewmodel-compose", "androidx.lifecycle:lifecycle-viewmodel-compose:$androidxLifecycleVersion")
|
||||
library("androidx-workmanager", "androidx.work:work-runtime-ktx:$androidxWorkManagerVersion")
|
||||
library("androidx-browser", "androidx.browser:browser:$androidxBrowserVersion")
|
||||
library("desugaring", "com.android.tools:desugar_jdk_libs:$coreLibraryDesugaringVersion")
|
||||
library("firebase-bom", "com.google.firebase:firebase-bom:${extra["FIREBASE_BOM_VERSION_MATCHER"]}")
|
||||
library("firebase-installations", "com.google.firebase", "firebase-installations").withoutVersion()
|
||||
|
|
|
@ -25,8 +25,28 @@ import androidx.compose.ui.unit.sp
|
|||
import co.electriccoin.zcash.ui.design.R
|
||||
import co.electriccoin.zcash.ui.design.newcomponent.PreviewScreens
|
||||
import co.electriccoin.zcash.ui.design.theme.ZcashTheme
|
||||
import co.electriccoin.zcash.ui.design.util.getValue
|
||||
import co.electriccoin.zcash.ui.design.util.orDark
|
||||
|
||||
@Composable
|
||||
fun ZashiSettingsListItem(
|
||||
state: ButtonState,
|
||||
@DrawableRes icon: Int,
|
||||
trailing: @Composable () -> Unit = {
|
||||
Image(
|
||||
painter = painterResource(R.drawable.ic_chevron_right orDark R.drawable.ic_chevron_right_dark),
|
||||
contentDescription = state.text.getValue(),
|
||||
)
|
||||
}
|
||||
) {
|
||||
ZashiSettingsListItem(
|
||||
text = state.text.getValue(),
|
||||
icon = icon,
|
||||
trailing = trailing,
|
||||
onClick = state.onClick
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ZashiSettingsListItem(
|
||||
text: String,
|
||||
|
|
|
@ -74,6 +74,14 @@ androidComponents {
|
|||
comment = "Whether is the SecureScreen sensitive data protection enabled"
|
||||
)
|
||||
)
|
||||
variant.buildConfigFields.put(
|
||||
"ZCASH_COINBASE_APP_ID",
|
||||
BuildConfigField(
|
||||
type = "String",
|
||||
value = "\"${project.property("ZCASH_COINBASE_APP_ID")?.toString().orEmpty()}\"",
|
||||
comment = "App ID of the Coinbase Onramp integration"
|
||||
)
|
||||
)
|
||||
// To configure screen orientation in runtime
|
||||
variant.buildConfigFields.put(
|
||||
"IS_SCREEN_ROTATION_ENABLED",
|
||||
|
@ -96,6 +104,7 @@ dependencies {
|
|||
implementation(libs.androidx.splash)
|
||||
implementation(libs.androidx.workmanager)
|
||||
api(libs.bundles.androidx.biometric)
|
||||
implementation(libs.androidx.browser)
|
||||
implementation(libs.bundles.androidx.camera)
|
||||
implementation(libs.bundles.androidx.compose.core)
|
||||
implementation(libs.bundles.androidx.compose.extended)
|
||||
|
|
|
@ -2,6 +2,7 @@ package co.electriccoin.zcash.di
|
|||
|
||||
import co.electriccoin.zcash.ui.common.provider.GetDefaultServersProvider
|
||||
import co.electriccoin.zcash.ui.common.provider.GetVersionInfoProvider
|
||||
import co.electriccoin.zcash.ui.common.provider.GetZcashCurrencyProvider
|
||||
import org.koin.core.module.dsl.factoryOf
|
||||
import org.koin.dsl.module
|
||||
|
||||
|
@ -9,4 +10,5 @@ val providerModule =
|
|||
module {
|
||||
factoryOf(::GetDefaultServersProvider)
|
||||
factoryOf(::GetVersionInfoProvider)
|
||||
factoryOf(::GetZcashCurrencyProvider)
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package co.electriccoin.zcash.di
|
|||
import co.electriccoin.zcash.ui.common.usecase.GetPersistableWalletUseCase
|
||||
import co.electriccoin.zcash.ui.common.usecase.GetSelectedEndpointUseCase
|
||||
import co.electriccoin.zcash.ui.common.usecase.GetSynchronizerUseCase
|
||||
import co.electriccoin.zcash.ui.common.usecase.GetTransparentAddressUseCase
|
||||
import co.electriccoin.zcash.ui.common.usecase.ObserveConfigurationUseCase
|
||||
import co.electriccoin.zcash.ui.common.usecase.ObserveFastestServersUseCase
|
||||
import co.electriccoin.zcash.ui.common.usecase.ObserveSelectedEndpointUseCase
|
||||
|
@ -27,4 +28,5 @@ val useCaseModule =
|
|||
singleOf(::GetSelectedEndpointUseCase)
|
||||
singleOf(::ObserveConfigurationUseCase)
|
||||
singleOf(::RescanBlockchainUseCase)
|
||||
singleOf(::GetTransparentAddressUseCase)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
package co.electriccoin.zcash.ui.common.provider
|
||||
|
||||
import android.app.Application
|
||||
import cash.z.ecc.sdk.type.ZcashCurrency
|
||||
|
||||
class GetZcashCurrencyProvider(private val application: Application) {
|
||||
operator fun invoke() = ZcashCurrency.fromResources(application)
|
||||
|
||||
fun getLocalizedName() = ZcashCurrency.getLocalizedName(application)
|
||||
}
|
|
@ -6,10 +6,12 @@ import cash.z.ecc.android.sdk.Synchronizer
|
|||
import cash.z.ecc.android.sdk.WalletCoordinator
|
||||
import cash.z.ecc.android.sdk.model.FastestServersResult
|
||||
import cash.z.ecc.android.sdk.model.PersistableWallet
|
||||
import cash.z.ecc.android.sdk.model.WalletAddresses
|
||||
import cash.z.ecc.sdk.ANDROID_STATE_FLOW_TIMEOUT
|
||||
import co.electriccoin.lightwallet.client.model.LightWalletEndpoint
|
||||
import co.electriccoin.zcash.preference.EncryptedPreferenceProvider
|
||||
import co.electriccoin.zcash.preference.StandardPreferenceProvider
|
||||
import co.electriccoin.zcash.spackle.Twig
|
||||
import co.electriccoin.zcash.ui.common.model.FastestServersState
|
||||
import co.electriccoin.zcash.ui.common.model.OnboardingState
|
||||
import co.electriccoin.zcash.ui.common.provider.GetDefaultServersProvider
|
||||
|
@ -48,6 +50,7 @@ interface WalletRepository {
|
|||
val secretState: StateFlow<SecretState?>
|
||||
val fastestServers: StateFlow<FastestServersState>
|
||||
val persistableWallet: Flow<PersistableWallet?>
|
||||
val addresses: StateFlow<WalletAddresses?>
|
||||
|
||||
fun persistWallet(persistableWallet: PersistableWallet)
|
||||
|
||||
|
@ -160,6 +163,21 @@ class WalletRepositoryImpl(
|
|||
(it as? SecretState.Ready?)?.persistableWallet
|
||||
}
|
||||
|
||||
override val addresses: StateFlow<WalletAddresses?> =
|
||||
synchronizer
|
||||
.filterNotNull()
|
||||
.map {
|
||||
runCatching {
|
||||
WalletAddresses.new(it)
|
||||
}.onFailure {
|
||||
Twig.warn { "Wait until the SDK starts providing the addresses" }
|
||||
}.getOrNull()
|
||||
}.stateIn(
|
||||
scope,
|
||||
SharingStarted.WhileSubscribed(ANDROID_STATE_FLOW_TIMEOUT),
|
||||
null
|
||||
)
|
||||
|
||||
/**
|
||||
* Persists a wallet asynchronously. Clients observe [secretState] to see the side effects.
|
||||
*/
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
package co.electriccoin.zcash.ui.common.usecase
|
||||
|
||||
import co.electriccoin.zcash.ui.common.repository.WalletRepository
|
||||
import kotlinx.coroutines.flow.filterNotNull
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.map
|
||||
|
||||
class GetTransparentAddressUseCase(
|
||||
private val walletRepository: WalletRepository
|
||||
) {
|
||||
suspend operator fun invoke() = walletRepository.addresses.filterNotNull().map { it.transparent }.first()
|
||||
}
|
|
@ -223,20 +223,7 @@ class WalletViewModel(
|
|||
null
|
||||
)
|
||||
|
||||
val addresses: StateFlow<WalletAddresses?> =
|
||||
synchronizer
|
||||
.filterNotNull()
|
||||
.map {
|
||||
runCatching {
|
||||
WalletAddresses.new(it)
|
||||
}.onFailure {
|
||||
Twig.warn { "Wait until the SDK starts providing the addresses" }
|
||||
}.getOrNull()
|
||||
}.stateIn(
|
||||
viewModelScope,
|
||||
SharingStarted.WhileSubscribed(ANDROID_STATE_FLOW_TIMEOUT),
|
||||
null
|
||||
)
|
||||
val addresses: StateFlow<WalletAddresses?> = walletRepository.addresses
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
val transactionHistoryState =
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package co.electriccoin.zcash.ui.screen.advancedsettings
|
||||
|
||||
import co.electriccoin.zcash.ui.design.component.ButtonState
|
||||
|
||||
data class AdvancedSettingsState(
|
||||
val onBack: () -> Unit,
|
||||
val onRecoveryPhraseClick: () -> Unit,
|
||||
|
@ -7,4 +9,5 @@ data class AdvancedSettingsState(
|
|||
val onChooseServerClick: () -> Unit,
|
||||
val onCurrencyConversionClick: () -> Unit,
|
||||
val onDeleteZashiClick: () -> Unit,
|
||||
val coinbaseButton: ButtonState?,
|
||||
)
|
||||
|
|
|
@ -2,11 +2,14 @@
|
|||
|
||||
package co.electriccoin.zcash.ui.screen.advancedsettings
|
||||
|
||||
import android.net.Uri
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.browser.customtabs.CustomTabsIntent
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import co.electriccoin.zcash.di.koinActivityViewModel
|
||||
import co.electriccoin.zcash.ui.common.compose.LocalActivity
|
||||
import co.electriccoin.zcash.ui.common.compose.LocalNavController
|
||||
import co.electriccoin.zcash.ui.common.viewmodel.WalletViewModel
|
||||
import co.electriccoin.zcash.ui.screen.advancedsettings.view.AdvancedSettings
|
||||
|
@ -20,6 +23,7 @@ internal fun WrapAdvancedSettings(
|
|||
goExportPrivateData: () -> Unit,
|
||||
goSeedRecovery: () -> Unit,
|
||||
) {
|
||||
val activity = LocalActivity.current
|
||||
val navController = LocalNavController.current
|
||||
val walletViewModel = koinActivityViewModel<WalletViewModel>()
|
||||
val viewModel = koinViewModel<AdvancedSettingsViewModel>()
|
||||
|
@ -41,6 +45,18 @@ internal fun WrapAdvancedSettings(
|
|||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
viewModel.coinbaseNavigationCommand.collect { uri ->
|
||||
val intent =
|
||||
CustomTabsIntent.Builder()
|
||||
.setUrlBarHidingEnabled(true)
|
||||
.setShowTitle(true)
|
||||
.setShareState(CustomTabsIntent.SHARE_STATE_OFF)
|
||||
.build()
|
||||
intent.launchUrl(activity, Uri.parse(uri))
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
viewModel.backNavigationCommand.collect {
|
||||
navController.popBackStack()
|
||||
|
|
|
@ -26,12 +26,14 @@ import androidx.compose.ui.unit.sp
|
|||
import co.electriccoin.zcash.ui.R
|
||||
import co.electriccoin.zcash.ui.common.model.TopAppBarSubTitleState
|
||||
import co.electriccoin.zcash.ui.design.component.BlankBgScaffold
|
||||
import co.electriccoin.zcash.ui.design.component.ButtonState
|
||||
import co.electriccoin.zcash.ui.design.component.ZashiSettingsListItem
|
||||
import co.electriccoin.zcash.ui.design.component.ZashiSmallTopAppBar
|
||||
import co.electriccoin.zcash.ui.design.component.ZashiTopAppBarBackNavigation
|
||||
import co.electriccoin.zcash.ui.design.newcomponent.PreviewScreens
|
||||
import co.electriccoin.zcash.ui.design.theme.ZcashTheme
|
||||
import co.electriccoin.zcash.ui.design.util.orDark
|
||||
import co.electriccoin.zcash.ui.design.util.stringRes
|
||||
import co.electriccoin.zcash.ui.screen.advancedsettings.AdvancedSettingsState
|
||||
import co.electriccoin.zcash.ui.screen.advancedsettings.AdvancedSettingsTag
|
||||
import co.electriccoin.zcash.ui.screen.exchangerate.ZashiButton
|
||||
|
@ -92,6 +94,13 @@ fun AdvancedSettings(
|
|||
R.drawable.ic_advanced_settings_currency_conversion_dark,
|
||||
onClick = state.onCurrencyConversionClick
|
||||
)
|
||||
if (state.coinbaseButton != null) {
|
||||
HorizontalDivider(color = ZcashTheme.zashiColors.divider)
|
||||
ZashiSettingsListItem(
|
||||
icon = R.drawable.ic_advanced_settings_coinbase,
|
||||
state = state.coinbaseButton
|
||||
)
|
||||
}
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
Row(
|
||||
|
@ -161,6 +170,11 @@ private fun AdvancedSettingsPreview() =
|
|||
onChooseServerClick = {},
|
||||
onCurrencyConversionClick = {},
|
||||
onDeleteZashiClick = {},
|
||||
coinbaseButton =
|
||||
ButtonState(
|
||||
text = stringRes("Coinbase"),
|
||||
onClick = {}
|
||||
)
|
||||
),
|
||||
topAppBarSubTitleState = TopAppBarSubTitleState.None,
|
||||
)
|
||||
|
|
|
@ -2,16 +2,28 @@ package co.electriccoin.zcash.ui.screen.advancedsettings.viewmodel
|
|||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import co.electriccoin.zcash.ui.BuildConfig
|
||||
import co.electriccoin.zcash.ui.NavigationTargets
|
||||
import co.electriccoin.zcash.ui.R
|
||||
import co.electriccoin.zcash.ui.common.provider.GetVersionInfoProvider
|
||||
import co.electriccoin.zcash.ui.common.provider.GetZcashCurrencyProvider
|
||||
import co.electriccoin.zcash.ui.common.usecase.GetTransparentAddressUseCase
|
||||
import co.electriccoin.zcash.ui.design.component.ButtonState
|
||||
import co.electriccoin.zcash.ui.design.util.stringRes
|
||||
import co.electriccoin.zcash.ui.screen.advancedsettings.AdvancedSettingsState
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class AdvancedSettingsViewModel : ViewModel() {
|
||||
val state: StateFlow<AdvancedSettingsState> =
|
||||
class AdvancedSettingsViewModel(
|
||||
getVersionInfo: GetVersionInfoProvider,
|
||||
getZcashCurrency: GetZcashCurrencyProvider,
|
||||
private val getTransparentAddress: GetTransparentAddressUseCase,
|
||||
) : ViewModel() {
|
||||
private val forceShowCoinbaseForDebug = getVersionInfo().let { it.isDebuggable && !it.isRunningUnderTestService }
|
||||
|
||||
val state =
|
||||
MutableStateFlow(
|
||||
AdvancedSettingsState(
|
||||
onBack = ::onBack,
|
||||
|
@ -19,12 +31,23 @@ class AdvancedSettingsViewModel : ViewModel() {
|
|||
onExportPrivateDataClick = {},
|
||||
onChooseServerClick = ::onChooseServerClick,
|
||||
onCurrencyConversionClick = ::onCurrencyConversionClick,
|
||||
onDeleteZashiClick = {}
|
||||
onDeleteZashiClick = {},
|
||||
coinbaseButton =
|
||||
ButtonState(
|
||||
// Set the wallet currency by app build is more future-proof, although we hide it from the UI
|
||||
// in the Testnet build
|
||||
text = stringRes(R.string.advanced_settings_coinbase, getZcashCurrency.getLocalizedName()),
|
||||
onClick = { onBuyWithCoinbaseClicked() }
|
||||
).takeIf {
|
||||
!getVersionInfo().isTestnet &&
|
||||
(BuildConfig.ZCASH_COINBASE_APP_ID.isNotEmpty() || forceShowCoinbaseForDebug)
|
||||
}
|
||||
)
|
||||
).asStateFlow()
|
||||
|
||||
val navigationCommand = MutableSharedFlow<String>()
|
||||
val backNavigationCommand = MutableSharedFlow<Unit>()
|
||||
val coinbaseNavigationCommand = MutableSharedFlow<String>()
|
||||
|
||||
private fun onChooseServerClick() =
|
||||
viewModelScope.launch {
|
||||
|
@ -36,6 +59,32 @@ class AdvancedSettingsViewModel : ViewModel() {
|
|||
navigationCommand.emit(NavigationTargets.SETTINGS_EXCHANGE_RATE_OPT_IN)
|
||||
}
|
||||
|
||||
private fun onBuyWithCoinbaseClicked() {
|
||||
viewModelScope.launch {
|
||||
val appId = BuildConfig.ZCASH_COINBASE_APP_ID
|
||||
|
||||
when {
|
||||
appId.isEmpty() && forceShowCoinbaseForDebug ->
|
||||
coinbaseNavigationCommand.emit("https://www.coinbase.com") // fallback debug url
|
||||
|
||||
appId.isEmpty() && forceShowCoinbaseForDebug -> {
|
||||
// should not happen
|
||||
}
|
||||
|
||||
appId.isNotEmpty() -> {
|
||||
val address = getTransparentAddress().address
|
||||
val url =
|
||||
"https://pay.coinbase.com/buy/select-asset?appId=$appId&addresses={\"${address}\":[\"zcash\"]}"
|
||||
coinbaseNavigationCommand.emit(url)
|
||||
}
|
||||
|
||||
else -> {
|
||||
// should not happen
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun onBack() =
|
||||
viewModelScope.launch {
|
||||
backNavigationCommand.emit(Unit)
|
||||
|
|
|
@ -94,7 +94,7 @@ class SettingsViewModel(
|
|||
combine(isLoading, troubleshootingState) { isLoading, troubleshootingState ->
|
||||
SettingsState(
|
||||
isLoading = isLoading,
|
||||
version = stringRes(R.string.settings_version, getVersionInfo().versionName),
|
||||
version = stringRes(R.string.settings_version, versionInfo.versionName),
|
||||
settingsTroubleshootingState = troubleshootingState,
|
||||
onBack = ::onBack,
|
||||
onAdvancedSettingsClick = ::onAdvancedSettingsClick,
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<string name="advanced_settings_export">Export Private Data</string>
|
||||
<string name="advanced_settings_choose_server">Choose a Server</string>
|
||||
<string name="advanced_settings_currency_conversion">Currency Conversion</string>
|
||||
<string name="advanced_settings_coinbase">Buy ZEC with Coinbase</string>
|
||||
<string name="advanced_settings_coinbase">Buy <xliff:g id="currency" example="ZEC">%1$s</xliff:g> with Coinbase</string>
|
||||
<string name="advanced_settings_info">You will be asked to confirm on the next screen</string>
|
||||
<string name="advanced_settings_delete_button">Delete Zashi</string>
|
||||
</resources>
|
||||
|
|
Loading…
Reference in New Issue