feat(dapps): show the bundled DApps catalog until one can be fetched

The DApps catalog lived only in memory and every read waited on first(), so
with the catalog URL unreachable the DApps screen waited forever.

feature-dapp-impl now bundles dapps_full.json at build time. On the first
sync the repository tries the network once and, if that fails, publishes the
bundled catalog so every read returns; the fetched catalog replaces it as
soon as it arrives.

The download task moves to scripts/bundled-config.gradle and serves both
the chain list and the DApps catalog. runtime and feature-dapp-impl unit
tests return defaults for android.util.Log, which the fallback calls.
This commit is contained in:
Satoshi Qazi Muhammed committed 2026-10-31 00:43:06 +04:00
1 parent ec9a2cf307
commit 5c66cd7f1d
7 files changed
+216 -43

No files matched your search

+11 -39
View File
@@ -1,8 +1,8 @@
apply from: '../scripts/secrets.gradle'
apply plugin: 'kotlin-parcelize'
apply from: '../scripts/bundled-config.gradle'
def chainsUrl = "https://raw.githubusercontent.com/pezkuwichain/pezkuwi-wallet-utils/master/chains/v22/android/chains.json"
def bundledConfigDir = "$buildDir/generated/bundledConfig"
android {
@@ -37,6 +37,14 @@ android {
}
}
namespace 'io.novafoundation.nova.runtime'
testOptions {
unitTests {
// android.util.Log.* throws "Method ... not mocked" in plain JVM unit tests; the first-install
// fallback logs the failed fetch it recovers from.
returnDefaultValues = true
}
}
}
dependencies {
@@ -71,41 +79,5 @@ dependencies {
androidTestImplementation androidJunitDep
}
// The chain list a first install starts from when none can be fetched (AssetBundledChains). It is the live
// list at build time, so every build carries the chains that were current when it was made.
def bundleChainsConfig = tasks.register("bundleChainsConfig") {
def target = file("$bundledConfigDir/bundled-config/chains.json")
outputs.file(target)
outputs.upToDateWhen { false }
doLast {
def sources = [
chainsUrl,
chainsUrl.replace("https://raw.githubusercontent.com/pezkuwichain/pezkuwi-wallet-utils/master/",
"https://cdn.jsdelivr.net/gh/pezkuwichain/pezkuwi-wallet-utils@master/")
]
def bytes = null
for (source in sources) {
try {
bytes = new URL(source).getBytes(connectTimeout: 20_000, readTimeout: 60_000)
break
} catch (IOException e) {
logger.warn("bundleChainsConfig: $source failed: $e")
}
}
if (bytes == null) throw new GradleException("bundleChainsConfig: no source answered")
def chains = new groovy.json.JsonSlurper().parse(bytes)
if (!(chains instanceof List) || chains.isEmpty()) {
throw new GradleException("bundleChainsConfig: the chain list is not a non-empty JSON array")
}
target.parentFile.mkdirs()
target.bytes = bytes
def sha256 = java.security.MessageDigest.getInstance("SHA-256").digest(bytes).encodeHex().toString()
logger.lifecycle("bundleChainsConfig: ${chains.size()} chains, sha256 $sha256")
}
}
android.sourceSets.main.assets.srcDir(bundledConfigDir)
preBuild.dependsOn(bundleChainsConfig)
// The chain list a first install starts from when none can be fetched (AssetBundledChains).
bundleConfigAsset("bundleChainsConfig", chainsUrl, "bundled-config/chains.json") { it instanceof List && !it.isEmpty() }