Skip to content

Commit efe1e5c

Browse files
committed
fix: align pubky ring flow
1 parent 752d21a commit efe1e5c

16 files changed

Lines changed: 494 additions & 27 deletions

app/src/main/AndroidManifest.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
xmlns:tools="http://schemas.android.com/tools">
44

55
<queries>
6+
<package android:name="to.pubky.ring" />
67
<intent>
78
<action android:name="android.settings.APPLICATION_DETAILS_SETTINGS" />
89
</intent>

app/src/main/java/to/bitkit/repositories/PubkyRepo.kt

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,6 @@ class PubkyRepo @Inject constructor(
210210
_authState.update { PubkyAuthState.Authenticated }
211211
Logger.info("Pubky auth completed for '$pk'", context = TAG)
212212
loadProfile()
213-
loadContacts()
214213
}.map { }
215214
}
216215

@@ -583,6 +582,7 @@ class PubkyRepo @Inject constructor(
583582
}
584583

585584
suspend fun prepareImport(): Result<Unit> = runCatching {
585+
clearPendingImport()
586586
val pk = requireNotNull(_publicKey.value) { "Not authenticated" }
587587
withContext(ioDispatcher) {
588588
val contactKeys = pubkyService.getContacts(pk)
@@ -610,6 +610,11 @@ class PubkyRepo @Inject constructor(
610610
}
611611
}
612612

613+
suspend fun clearPendingImport() {
614+
_pendingImportProfile.update { null }
615+
_pendingImportContacts.update { emptyList() }
616+
}
617+
613618
// endregion
614619

615620
// region Auth approval
@@ -646,8 +651,7 @@ class PubkyRepo @Inject constructor(
646651
_publicKey.update { null }
647652
_profile.update { null }
648653
_contacts.update { emptyList() }
649-
_pendingImportProfile.update { null }
650-
_pendingImportContacts.update { emptyList() }
654+
clearPendingImport()
651655
_authState.update { PubkyAuthState.Idle }
652656
}
653657

app/src/main/java/to/bitkit/ui/ContentView.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ import to.bitkit.ui.screens.contacts.ContactsScreen
7272
import to.bitkit.ui.screens.contacts.ContactsViewModel
7373
import to.bitkit.ui.screens.contacts.EditContactScreen
7474
import to.bitkit.ui.screens.contacts.EditContactViewModel
75+
import to.bitkit.ui.screens.contacts.shouldDiscardPendingImport
7576
import to.bitkit.ui.screens.profile.CreateProfileScreen
7677
import to.bitkit.ui.screens.profile.CreateProfileViewModel
7778
import to.bitkit.ui.screens.profile.EditProfileScreen
@@ -516,6 +517,11 @@ fun ContentView(
516517
rootNavController = navController,
517518
hasSeenWidgetsIntro = hasSeenWidgetsIntro,
518519
hasSeenShopIntro = hasSeenShopIntro,
520+
onBeforeNavigate = { destination ->
521+
if (shouldDiscardPendingImport(navController.currentDestination, destination)) {
522+
appViewModel.clearPendingPubkyImport()
523+
}
524+
},
519525
hasSeenProfileIntro = hasSeenProfileIntro,
520526
hasSeenContactsIntro = hasSeenContactsIntro,
521527
isProfileAuthenticated = isProfileAuthenticated,

app/src/main/java/to/bitkit/ui/components/DrawerMenu.kt

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ fun DrawerMenu(
6969
rootNavController: NavController,
7070
hasSeenWidgetsIntro: Boolean,
7171
hasSeenShopIntro: Boolean,
72+
onBeforeNavigate: (Routes?) -> Unit,
7273
modifier: Modifier = Modifier,
7374
hasSeenProfileIntro: Boolean = false,
7475
hasSeenContactsIntro: Boolean = false,
@@ -108,32 +109,59 @@ fun DrawerMenu(
108109
drawerState = drawerState,
109110
onClickAddWidget = {
110111
if (!hasSeenWidgetsIntro) {
112+
onBeforeNavigate(Routes.WidgetsIntro)
111113
rootNavController.navigateIfNotCurrent(Routes.WidgetsIntro)
112114
} else {
115+
onBeforeNavigate(Routes.AddWidget)
113116
rootNavController.navigateIfNotCurrent(Routes.AddWidget)
114117
}
115118
},
116119
onClickShop = {
117120
if (!hasSeenShopIntro) {
121+
onBeforeNavigate(Routes.ShopIntro)
118122
rootNavController.navigateIfNotCurrent(Routes.ShopIntro)
119123
} else {
124+
onBeforeNavigate(Routes.ShopDiscover)
120125
rootNavController.navigateIfNotCurrent(Routes.ShopDiscover)
121126
}
122127
},
123128
onClickContacts = {
124129
when {
125-
!hasSeenContactsIntro -> rootNavController.navigateIfNotCurrent(Routes.ContactsIntro)
126-
isProfileAuthenticated -> rootNavController.navigateIfNotCurrent(Routes.Contacts)
127-
hasSeenProfileIntro -> rootNavController.navigateIfNotCurrent(Routes.PubkyChoice)
128-
else -> rootNavController.navigateIfNotCurrent(Routes.ProfileIntro)
130+
!hasSeenContactsIntro -> {
131+
onBeforeNavigate(Routes.ContactsIntro)
132+
rootNavController.navigateIfNotCurrent(Routes.ContactsIntro)
133+
}
134+
135+
isProfileAuthenticated -> {
136+
onBeforeNavigate(Routes.Contacts)
137+
rootNavController.navigateIfNotCurrent(Routes.Contacts)
138+
}
139+
140+
hasSeenProfileIntro -> {
141+
onBeforeNavigate(Routes.PubkyChoice)
142+
rootNavController.navigateIfNotCurrent(Routes.PubkyChoice)
143+
}
144+
145+
else -> {
146+
onBeforeNavigate(Routes.ProfileIntro)
147+
rootNavController.navigateIfNotCurrent(Routes.ProfileIntro)
148+
}
129149
}
130150
},
131151
onClickProfile = {
152+
onBeforeNavigate(
153+
when {
154+
isProfileAuthenticated -> Routes.Profile
155+
hasSeenProfileIntro -> Routes.PubkyChoice
156+
else -> Routes.ProfileIntro
157+
}
158+
)
132159
rootNavController.navigateToProfile(
133160
isAuthenticated = isProfileAuthenticated,
134161
hasSeenIntro = hasSeenProfileIntro,
135162
)
136163
},
164+
onBeforeNavigate = onBeforeNavigate,
137165
)
138166
}
139167
}
@@ -146,6 +174,7 @@ private fun Menu(
146174
onClickShop: () -> Unit,
147175
onClickContacts: () -> Unit,
148176
onClickProfile: () -> Unit,
177+
onBeforeNavigate: (Routes?) -> Unit,
149178
) {
150179
val scope = rememberCoroutineScope()
151180

@@ -163,6 +192,7 @@ private fun Menu(
163192
iconRes = R.drawable.ic_coins,
164193
onClick = {
165194
val isInHome = rootNavController.currentBackStackEntry?.destination?.hasRoute<Routes.Home>() ?: false
195+
onBeforeNavigate(null)
166196
if (!isInHome) rootNavController.navigateToHome()
167197
scope.launch { drawerState.close() }
168198
},
@@ -173,6 +203,7 @@ private fun Menu(
173203
label = stringResource(R.string.wallet__drawer__activity),
174204
iconRes = R.drawable.ic_heartbeat,
175205
onClick = {
206+
onBeforeNavigate(Routes.AllActivity)
176207
rootNavController.navigateIfNotCurrent(Routes.AllActivity)
177208
scope.launch { drawerState.close() }
178209
},
@@ -223,6 +254,7 @@ private fun Menu(
223254
label = stringResource(R.string.wallet__drawer__support),
224255
iconRes = R.drawable.ic_chats_circle,
225256
onClick = {
257+
onBeforeNavigate(Routes.Support)
226258
rootNavController.navigateIfNotCurrent(Routes.Support)
227259
scope.launch { drawerState.close() }
228260
},
@@ -233,6 +265,7 @@ private fun Menu(
233265
label = stringResource(R.string.wallet__drawer__settings),
234266
iconRes = R.drawable.ic_settings,
235267
onClick = {
268+
onBeforeNavigate(Routes.Settings)
236269
rootNavController.navigateIfNotCurrent(Routes.Settings)
237270
scope.launch { drawerState.close() }
238271
},
@@ -246,6 +279,7 @@ private fun Menu(
246279
modifier = Modifier
247280
.fillMaxWidth()
248281
.clickableAlpha {
282+
onBeforeNavigate(Routes.AppStatus)
249283
rootNavController.navigateIfNotCurrent(Routes.AppStatus)
250284
scope.launch { drawerState.close() }
251285
}
@@ -336,6 +370,7 @@ private fun Preview() {
336370
drawerState = rememberDrawerState(initialValue = DrawerValue.Open),
337371
hasSeenWidgetsIntro = false,
338372
hasSeenShopIntro = false,
373+
onBeforeNavigate = {},
339374
modifier = Modifier.align(Alignment.TopEnd)
340375
)
341376
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package to.bitkit.ui.screens.contacts
2+
3+
import androidx.navigation.NavDestination
4+
import androidx.navigation.NavDestination.Companion.hasRoute
5+
import to.bitkit.models.PubkyProfile
6+
import to.bitkit.ui.Routes
7+
8+
internal fun hasPendingImport(profile: PubkyProfile?, contacts: List<PubkyProfile>): Boolean =
9+
profile != null && contacts.isNotEmpty()
10+
11+
internal fun shouldDiscardPendingImport(currentDestination: NavDestination?, destination: Routes?): Boolean {
12+
if (!currentDestination.isContactImportRoute()) {
13+
return false
14+
}
15+
16+
return !destination.isContactImportRoute()
17+
}
18+
19+
private fun NavDestination?.isContactImportRoute(): Boolean =
20+
this?.hasRoute<Routes.ContactImportOverview>() == true ||
21+
this?.hasRoute<Routes.ContactImportSelect>() == true
22+
23+
private fun Routes?.isContactImportRoute(): Boolean =
24+
this == Routes.ContactImportOverview || this == Routes.ContactImportSelect

app/src/main/java/to/bitkit/ui/screens/contacts/ContactImportOverviewScreen.kt

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package to.bitkit.ui.screens.contacts
22

3+
import androidx.activity.compose.BackHandler
34
import androidx.compose.foundation.background
45
import androidx.compose.foundation.layout.Arrangement
56
import androidx.compose.foundation.layout.Box
@@ -56,14 +57,23 @@ fun ContactImportOverviewScreen(
5657
viewModel.effects.collect {
5758
when (it) {
5859
ContactImportOverviewEffect.ImportComplete -> onImportComplete()
60+
ContactImportOverviewEffect.NavigateBack -> onBackClick()
5961
ContactImportOverviewEffect.NavigateToSelect -> onNavigateToSelect()
6062
}
6163
}
6264
}
6365

66+
LaunchedEffect(uiState.shouldRedirectToPayContacts) {
67+
if (uiState.shouldRedirectToPayContacts) {
68+
onImportComplete()
69+
}
70+
}
71+
72+
BackHandler(onBack = viewModel::onBackClick)
73+
6474
Content(
6575
uiState = uiState,
66-
onBackClick = onBackClick,
76+
onBackClick = viewModel::onBackClick,
6777
onClickSelect = { viewModel.navigateToSelect() },
6878
onClickImportAll = { viewModel.importAll() },
6979
)

app/src/main/java/to/bitkit/ui/screens/contacts/ContactImportOverviewViewModel.kt

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,10 @@ class ContactImportOverviewViewModel @Inject constructor(
4444
viewModelScope.launch {
4545
val profile = pubkyRepo.pendingImportProfile.value
4646
val contacts = pubkyRepo.pendingImportContacts.value
47+
if (!hasPendingImport(profile, contacts)) {
48+
_uiState.update { it.copy(shouldRedirectToPayContacts = true) }
49+
return@launch
50+
}
4751
_uiState.update {
4852
it.copy(
4953
profile = profile,
@@ -61,6 +65,7 @@ class ContactImportOverviewViewModel @Inject constructor(
6165
_uiState.update { it.copy(isImporting = true) }
6266
pubkyRepo.importContacts(contacts.map { it.publicKey })
6367
.onSuccess {
68+
pubkyRepo.clearPendingImport()
6469
_uiState.update { it.copy(isImporting = false) }
6570
_effects.emit(ContactImportOverviewEffect.ImportComplete)
6671
}
@@ -81,16 +86,25 @@ class ContactImportOverviewViewModel @Inject constructor(
8186
_effects.emit(ContactImportOverviewEffect.NavigateToSelect)
8287
}
8388
}
89+
90+
fun onBackClick() {
91+
viewModelScope.launch {
92+
pubkyRepo.clearPendingImport()
93+
_effects.emit(ContactImportOverviewEffect.NavigateBack)
94+
}
95+
}
8496
}
8597

8698
@Stable
8799
data class ContactImportOverviewUiState(
88100
val profile: PubkyProfile? = null,
89101
val contacts: ImmutableList<PubkyProfile> = persistentListOf(),
90102
val isImporting: Boolean = false,
103+
val shouldRedirectToPayContacts: Boolean = false,
91104
)
92105

93106
sealed interface ContactImportOverviewEffect {
94107
data object ImportComplete : ContactImportOverviewEffect
95108
data object NavigateToSelect : ContactImportOverviewEffect
109+
data object NavigateBack : ContactImportOverviewEffect
96110
}

app/src/main/java/to/bitkit/ui/screens/contacts/ContactImportSelectScreen.kt

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package to.bitkit.ui.screens.contacts
22

3+
import androidx.activity.compose.BackHandler
34
import androidx.compose.foundation.background
45
import androidx.compose.foundation.layout.Arrangement
56
import androidx.compose.foundation.layout.Box
@@ -57,13 +58,22 @@ fun ContactImportSelectScreen(
5758
viewModel.effects.collect {
5859
when (it) {
5960
ContactImportSelectEffect.ImportComplete -> onImportComplete()
61+
ContactImportSelectEffect.NavigateBack -> onBackClick()
6062
}
6163
}
6264
}
6365

66+
LaunchedEffect(uiState.shouldRedirectToPayContacts) {
67+
if (uiState.shouldRedirectToPayContacts) {
68+
onImportComplete()
69+
}
70+
}
71+
72+
BackHandler(onBack = viewModel::onBackClick)
73+
6474
Content(
6575
uiState = uiState,
66-
onBackClick = onBackClick,
76+
onBackClick = viewModel::onBackClick,
6777
onToggleContact = { viewModel.toggleContact(it) },
6878
onSelectAll = { viewModel.selectAll() },
6979
onSelectNone = { viewModel.selectNone() },
@@ -136,7 +146,6 @@ private fun Content(
136146
text = stringResource(R.string.common__continue),
137147
onClick = onClickContinue,
138148
isLoading = uiState.isImporting,
139-
enabled = uiState.selectedCount > 0,
140149
)
141150
VerticalSpacer(16.dp)
142151
}

app/src/main/java/to/bitkit/ui/screens/contacts/ContactImportSelectViewModel.kt

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,12 @@ class ContactImportSelectViewModel @Inject constructor(
4242

4343
init {
4444
viewModelScope.launch {
45+
val profile = pubkyRepo.pendingImportProfile.value
4546
val contacts = pubkyRepo.pendingImportContacts.value
47+
if (!hasPendingImport(profile, contacts)) {
48+
_uiState.update { it.copy(shouldRedirectToPayContacts = true) }
49+
return@launch
50+
}
4651
_uiState.update {
4752
it.copy(
4853
contacts = contacts.map { profile ->
@@ -81,12 +86,17 @@ class ContactImportSelectViewModel @Inject constructor(
8186

8287
fun importSelected() {
8388
val selected = _uiState.value.contacts.filter { it.isSelected }
84-
if (selected.isEmpty()) return
85-
8689
viewModelScope.launch {
90+
if (selected.isEmpty()) {
91+
pubkyRepo.clearPendingImport()
92+
_effects.emit(ContactImportSelectEffect.ImportComplete)
93+
return@launch
94+
}
95+
8796
_uiState.update { it.copy(isImporting = true) }
8897
pubkyRepo.importContacts(selected.map { it.profile.publicKey })
8998
.onSuccess {
99+
pubkyRepo.clearPendingImport()
90100
_uiState.update { it.copy(isImporting = false) }
91101
_effects.emit(ContactImportSelectEffect.ImportComplete)
92102
}
@@ -101,6 +111,12 @@ class ContactImportSelectViewModel @Inject constructor(
101111
}
102112
}
103113
}
114+
115+
fun onBackClick() {
116+
viewModelScope.launch {
117+
_effects.emit(ContactImportSelectEffect.NavigateBack)
118+
}
119+
}
104120
}
105121

106122
@Stable
@@ -113,10 +129,12 @@ data class SelectableContact(
113129
data class ContactImportSelectUiState(
114130
val contacts: ImmutableList<SelectableContact> = persistentListOf(),
115131
val isImporting: Boolean = false,
132+
val shouldRedirectToPayContacts: Boolean = false,
116133
) {
117134
val selectedCount: Int get() = contacts.count { it.isSelected }
118135
}
119136

120137
sealed interface ContactImportSelectEffect {
121138
data object ImportComplete : ContactImportSelectEffect
139+
data object NavigateBack : ContactImportSelectEffect
122140
}

0 commit comments

Comments
 (0)