Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Changed
- Improve Pubky profile restore, contact editing, and contact routing flows #905

## [2.2.0] - 2026-04-07

### Fixed
Expand Down
17 changes: 17 additions & 0 deletions app/src/main/java/to/bitkit/models/BackupPayloads.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import com.synonym.bitkitcore.IBtInfo
import com.synonym.bitkitcore.IBtOrder
import com.synonym.bitkitcore.IcJitEntry
import com.synonym.bitkitcore.PreActivityMetadata
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import to.bitkit.data.AppCacheData
import to.bitkit.data.SettingsData
Expand All @@ -26,8 +27,24 @@ data class MetadataBackupV1(
val createdAt: Long,
val tagMetadata: List<PreActivityMetadata>,
val cache: AppCacheData,
val pubkySession: PubkySessionBackupV1? = null,
)

@Serializable
data class PubkySessionBackupV1(
val kind: PubkySessionBackupKind,
val sessionSecret: String? = null,
)

@Serializable
enum class PubkySessionBackupKind {
@SerialName("localSeed")
LocalSeed,

@SerialName("externalSession")
ExternalSession,
}

@Serializable
data class BlocktankBackupV1(
val version: Int = 1,
Expand Down
43 changes: 31 additions & 12 deletions app/src/main/java/to/bitkit/repositories/BackupRepo.kt
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ class BackupRepo @Inject constructor(
private val widgetsStore: WidgetsStore,
private val blocktankRepo: BlocktankRepo,
private val activityRepo: ActivityRepo,
private val pubkyRepo: PubkyRepo,
private val preActivityMetadataRepo: PreActivityMetadataRepo,
private val lightningService: LightningService,
private val clock: Clock,
Expand Down Expand Up @@ -268,6 +269,16 @@ class BackupRepo @Inject constructor(
}
dataListenerJobs.add(preActivityMetadataJob)

val pubkyStateJob = scope.launch {
pubkyRepo.backupStateVersion
.drop(1)
.collect {
if (shouldSkipBackup()) return@collect
markBackupRequired(BackupCategory.METADATA)
}
}
dataListenerJobs.add(pubkyStateJob)

// BLOCKTANK - Observe blocktank state changes (orders, cjitEntries, info)
val blocktankJob = scope.launch {
blocktankRepo.blocktankState
Expand Down Expand Up @@ -461,18 +472,7 @@ class BackupRepo @Inject constructor(
json.encodeToString(payload).toByteArray()
}

BackupCategory.METADATA -> {
val preActivityMetadata = preActivityMetadataRepo.getAllPreActivityMetadata().getOrDefault(emptyList())
val cacheData = cacheStore.data.first()

val payload = MetadataBackupV1(
createdAt = currentTimeMillis(),
tagMetadata = preActivityMetadata,
cache = cacheData,
)

json.encodeToString(payload).toByteArray()
}
BackupCategory.METADATA -> getMetadataBackupDataBytes()

BackupCategory.BLOCKTANK -> {
val blocktankState = blocktankRepo.blocktankState.first()
Expand Down Expand Up @@ -505,6 +505,21 @@ class BackupRepo @Inject constructor(
BackupCategory.LIGHTNING_CONNECTIONS -> throw NotImplementedError("LIGHTNING backup is managed by ldk-node")
}

private suspend fun getMetadataBackupDataBytes(): ByteArray = withContext(ioDispatcher) {
val preActivityMetadata = preActivityMetadataRepo.getAllPreActivityMetadata().getOrDefault(emptyList())
val cacheData = cacheStore.data.first()
val pubkySession = pubkyRepo.snapshotSessionBackupState().getOrDefault(null)

val payload = MetadataBackupV1(
createdAt = currentTimeMillis(),
tagMetadata = preActivityMetadata,
cache = cacheData,
pubkySession = pubkySession,
)

json.encodeToString(payload).toByteArray()
}

suspend fun performFullRestoreFromLatestBackup(
onCacheRestored: suspend () -> Unit = {},
): Result<Unit> = withContext(ioDispatcher) {
Expand All @@ -520,6 +535,10 @@ class BackupRepo @Inject constructor(
Logger.debug("Restored caches: ${jsonLogOf(parsed.cache.copy(cachedRates = emptyList()))}", TAG)
onCacheRestored()
preActivityMetadataRepo.upsertPreActivityMetadata(parsed.tagMetadata).getOrNull()
pubkyRepo.restoreSessionBackupState(parsed.pubkySession)
.onFailure {
Logger.warn("Failed to restore pubky session backup state", it, context = TAG)
}
Logger.debug("Restored ${parsed.tagMetadata.size} pre-activity metadata", TAG)
parsed.createdAt
}
Expand Down
Loading
Loading