Skip to content

Commit 8873f41

Browse files
authored
Merge pull request #518 from synonymdev/fix/boost-details
fix: explore screen async loading
2 parents f556b2f + feef79e commit 8873f41

File tree

6 files changed

+494
-149
lines changed

6 files changed

+494
-149
lines changed

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1089,7 +1089,6 @@ private fun NavGraphBuilder.activityItem(
10891089
}
10901090
composableWithDefaultTransitions<Routes.ActivityExplore> {
10911091
ActivityExploreScreen(
1092-
listViewModel = activityListViewModel,
10931092
route = it.toRoute(),
10941093
onBackClick = { navController.popBackStack() },
10951094
)

app/src/main/java/to/bitkit/ui/screens/wallets/activity/ActivityDetailScreen.kt

Lines changed: 163 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,11 @@ import androidx.compose.foundation.layout.size
1616
import androidx.compose.foundation.layout.width
1717
import androidx.compose.foundation.rememberScrollState
1818
import androidx.compose.foundation.verticalScroll
19+
import androidx.compose.material3.CircularProgressIndicator
1920
import androidx.compose.material3.HorizontalDivider
2021
import androidx.compose.material3.Icon
2122
import androidx.compose.runtime.Composable
23+
import androidx.compose.runtime.DisposableEffect
2224
import androidx.compose.runtime.LaunchedEffect
2325
import androidx.compose.runtime.ReadOnlyComposable
2426
import androidx.compose.runtime.getValue
@@ -75,7 +77,6 @@ import to.bitkit.ui.theme.AppThemeSurface
7577
import to.bitkit.ui.theme.Colors
7678
import to.bitkit.ui.utils.copyToClipboard
7779
import to.bitkit.ui.utils.getScreenTitleRes
78-
import to.bitkit.utils.Logger
7980
import to.bitkit.viewmodels.ActivityDetailViewModel
8081
import to.bitkit.viewmodels.ActivityListViewModel
8182

@@ -89,128 +90,185 @@ fun ActivityDetailScreen(
8990
onCloseClick: () -> Unit,
9091
onChannelClick: ((String) -> Unit)? = null,
9192
) {
92-
val activities by listViewModel.filteredActivities.collectAsStateWithLifecycle()
93-
val item = activities?.find { it.rawId() == route.id }
94-
if (item == null) {
95-
Logger.error("Activity not found")
96-
return
97-
}
93+
val uiState by detailViewModel.uiState.collectAsStateWithLifecycle()
9894

99-
val app = appViewModel ?: return
100-
val copyToastTitle = stringResource(R.string.common__copied)
101-
102-
val tags by detailViewModel.tags.collectAsStateWithLifecycle()
103-
val boostSheetVisible by detailViewModel.boostSheetVisible.collectAsStateWithLifecycle()
104-
var showAddTagSheet by remember { mutableStateOf(false) }
105-
var isCpfpChild by remember { mutableStateOf(false) }
106-
var boostTxDoesExist by remember { mutableStateOf<Map<String, Boolean>>(emptyMap()) }
107-
108-
LaunchedEffect(item) {
109-
detailViewModel.setActivity(item)
110-
if (item is Activity.Onchain) {
111-
isCpfpChild = detailViewModel.isCpfpChildTransaction(item.v1.txId)
112-
boostTxDoesExist = if (item.v1.boostTxIds.isNotEmpty()) {
113-
detailViewModel.getBoostTxDoesExist(item.v1.boostTxIds)
114-
} else {
115-
emptyMap()
116-
}
117-
} else {
118-
isCpfpChild = false
119-
boostTxDoesExist = emptyMap()
120-
}
95+
// Load activity on composition
96+
LaunchedEffect(route.id) {
97+
detailViewModel.loadActivity(route.id)
12198
}
12299

123-
// Update boostTxDoesExist when boostTxIds change
124-
LaunchedEffect(if (item is Activity.Onchain) item.v1.boostTxIds else emptyList()) {
125-
if (item is Activity.Onchain && item.v1.boostTxIds.isNotEmpty()) {
126-
boostTxDoesExist = detailViewModel.getBoostTxDoesExist(item.v1.boostTxIds)
100+
// Clear state on disposal
101+
DisposableEffect(Unit) {
102+
onDispose {
103+
detailViewModel.clearActivityState()
127104
}
128105
}
129106

130-
val context = LocalContext.current
131-
132107
Box(
133108
modifier = Modifier.fillMaxWidth()
134109
) {
135-
Column(
136-
modifier = Modifier.background(Colors.Black)
137-
) {
138-
AppTopBar(
139-
titleText = stringResource(
140-
if (isCpfpChild) {
141-
R.string.wallet__activity_boost_fee
142-
} else {
143-
item.getScreenTitleRes()
144-
}
145-
),
146-
onBackClick = onBackClick,
147-
actions = { DrawerNavIcon() },
148-
)
149-
ActivityDetailContent(
150-
item = item,
151-
tags = tags,
152-
onRemoveTag = { detailViewModel.removeTag(it) },
153-
onAddTagClick = { showAddTagSheet = true },
154-
onClickBoost = detailViewModel::onClickBoost,
155-
onExploreClick = onExploreClick,
156-
onChannelClick = onChannelClick,
157-
detailViewModel = detailViewModel,
158-
isCpfpChild = isCpfpChild,
159-
boostTxDoesExist = boostTxDoesExist,
160-
onCopy = { text ->
161-
app.toast(
162-
type = Toast.ToastType.SUCCESS,
163-
title = copyToastTitle,
164-
description = text.ellipsisMiddle(40)
110+
when (val loadState = uiState.activityLoadState) {
111+
is ActivityDetailViewModel.ActivityLoadState.Initial,
112+
is ActivityDetailViewModel.ActivityLoadState.Loading,
113+
-> {
114+
Column(modifier = Modifier.background(Colors.Black)) {
115+
AppTopBar(
116+
titleText = stringResource(R.string.wallet__activity),
117+
onBackClick = onBackClick,
118+
actions = { DrawerNavIcon() },
165119
)
120+
Box(
121+
modifier = Modifier
122+
.fillMaxSize()
123+
.padding(16.dp),
124+
contentAlignment = Alignment.Center
125+
) {
126+
CircularProgressIndicator()
127+
}
166128
}
167-
)
168-
if (showAddTagSheet) {
169-
ActivityAddTagSheet(
170-
listViewModel = listViewModel,
171-
activityViewModel = detailViewModel,
172-
onDismiss = { showAddTagSheet = false },
173-
)
174129
}
175-
}
176130

177-
if (boostSheetVisible) {
178-
(item as? Activity.Onchain)?.let {
179-
BoostTransactionSheet(
180-
onDismiss = detailViewModel::onDismissBoostSheet,
181-
item = it,
182-
onSuccess = {
183-
app.toast(
184-
type = Toast.ToastType.SUCCESS,
185-
title = context.getString(R.string.wallet__boost_success_title),
186-
description = context.getString(R.string.wallet__boost_success_msg)
131+
is ActivityDetailViewModel.ActivityLoadState.Error -> {
132+
Column(modifier = Modifier.background(Colors.Black)) {
133+
AppTopBar(
134+
titleText = stringResource(R.string.wallet__activity),
135+
onBackClick = onBackClick,
136+
actions = { DrawerNavIcon() },
137+
)
138+
Column(
139+
modifier = Modifier
140+
.fillMaxSize()
141+
.padding(16.dp),
142+
horizontalAlignment = Alignment.CenterHorizontally,
143+
verticalArrangement = Arrangement.Center
144+
) {
145+
BodySSB(
146+
text = loadState.message,
147+
modifier = Modifier.padding(bottom = 16.dp)
187148
)
188-
listViewModel.resync()
189-
onCloseClick()
190-
},
191-
onFailure = {
192-
app.toast(
193-
type = Toast.ToastType.ERROR,
194-
title = context.getString(R.string.wallet__boost_error_title),
195-
description = context.getString(R.string.wallet__boost_error_msg)
149+
PrimaryButton(
150+
text = stringResource(R.string.common__back),
151+
onClick = onBackClick
196152
)
197-
detailViewModel.onDismissBoostSheet()
198-
},
199-
onMaxFee = {
200-
app.toast(
201-
type = Toast.ToastType.ERROR,
202-
title = context.getString(R.string.wallet__send_fee_error),
203-
description = "Unable to increase the fee any further. Otherwise, it will exceed half the current input balance" // TODO CREATE STRING RESOURCE
153+
}
154+
}
155+
}
156+
157+
is ActivityDetailViewModel.ActivityLoadState.Success -> {
158+
val item = loadState.activity
159+
val app = appViewModel ?: return@Box
160+
val copyToastTitle = stringResource(R.string.common__copied)
161+
162+
val tags by detailViewModel.tags.collectAsStateWithLifecycle()
163+
val boostSheetVisible by detailViewModel.boostSheetVisible.collectAsStateWithLifecycle()
164+
var showAddTagSheet by remember { mutableStateOf(false) }
165+
var isCpfpChild by remember { mutableStateOf(false) }
166+
var boostTxDoesExist by remember { mutableStateOf<Map<String, Boolean>>(emptyMap()) }
167+
168+
LaunchedEffect(item) {
169+
if (item is Activity.Onchain) {
170+
isCpfpChild = detailViewModel.isCpfpChildTransaction(item.v1.txId)
171+
boostTxDoesExist = if (item.v1.boostTxIds.isNotEmpty()) {
172+
detailViewModel.getBoostTxDoesExist(item.v1.boostTxIds)
173+
} else {
174+
emptyMap()
175+
}
176+
} else {
177+
isCpfpChild = false
178+
boostTxDoesExist = emptyMap()
179+
}
180+
}
181+
182+
// Update boostTxDoesExist when boostTxIds change
183+
LaunchedEffect(if (item is Activity.Onchain) item.v1.boostTxIds else emptyList()) {
184+
if (item is Activity.Onchain && item.v1.boostTxIds.isNotEmpty()) {
185+
boostTxDoesExist = detailViewModel.getBoostTxDoesExist(item.v1.boostTxIds)
186+
}
187+
}
188+
189+
val context = LocalContext.current
190+
191+
Column(
192+
modifier = Modifier.background(Colors.Black)
193+
) {
194+
AppTopBar(
195+
titleText = stringResource(
196+
if (isCpfpChild) {
197+
R.string.wallet__activity_boost_fee
198+
} else {
199+
item.getScreenTitleRes()
200+
}
201+
),
202+
onBackClick = onBackClick,
203+
actions = { DrawerNavIcon() },
204+
)
205+
ActivityDetailContent(
206+
item = item,
207+
tags = tags,
208+
onRemoveTag = { detailViewModel.removeTag(it) },
209+
onAddTagClick = { showAddTagSheet = true },
210+
onClickBoost = detailViewModel::onClickBoost,
211+
onExploreClick = onExploreClick,
212+
onChannelClick = onChannelClick,
213+
detailViewModel = detailViewModel,
214+
isCpfpChild = isCpfpChild,
215+
boostTxDoesExist = boostTxDoesExist,
216+
onCopy = { text ->
217+
app.toast(
218+
type = Toast.ToastType.SUCCESS,
219+
title = copyToastTitle,
220+
description = text.ellipsisMiddle(40)
221+
)
222+
}
223+
)
224+
if (showAddTagSheet) {
225+
ActivityAddTagSheet(
226+
listViewModel = listViewModel,
227+
activityViewModel = detailViewModel,
228+
onDismiss = { showAddTagSheet = false },
204229
)
205-
},
206-
onMinFee = {
207-
app.toast(
208-
type = Toast.ToastType.ERROR,
209-
title = context.getString(R.string.wallet__send_fee_error),
210-
description = context.getString(R.string.wallet__send_fee_error_min)
230+
}
231+
}
232+
233+
if (boostSheetVisible) {
234+
(item as? Activity.Onchain)?.let {
235+
BoostTransactionSheet(
236+
onDismiss = detailViewModel::onDismissBoostSheet,
237+
item = it,
238+
onSuccess = {
239+
app.toast(
240+
type = Toast.ToastType.SUCCESS,
241+
title = context.getString(R.string.wallet__boost_success_title),
242+
description = context.getString(R.string.wallet__boost_success_msg)
243+
)
244+
listViewModel.resync()
245+
onCloseClick()
246+
},
247+
onFailure = {
248+
app.toast(
249+
type = Toast.ToastType.ERROR,
250+
title = context.getString(R.string.wallet__boost_error_title),
251+
description = context.getString(R.string.wallet__boost_error_msg)
252+
)
253+
detailViewModel.onDismissBoostSheet()
254+
},
255+
onMaxFee = {
256+
app.toast(
257+
type = Toast.ToastType.ERROR,
258+
title = context.getString(R.string.wallet__send_fee_error),
259+
description = context.getString(R.string.wallet__send_fee_error_max)
260+
)
261+
},
262+
onMinFee = {
263+
app.toast(
264+
type = Toast.ToastType.ERROR,
265+
title = context.getString(R.string.wallet__send_fee_error),
266+
description = context.getString(R.string.wallet__send_fee_error_min)
267+
)
268+
}
211269
)
212270
}
213-
)
271+
}
214272
}
215273
}
216274
}

0 commit comments

Comments
 (0)