@@ -17,9 +17,11 @@ import androidx.compose.foundation.layout.size
1717import androidx.compose.foundation.layout.width
1818import androidx.compose.foundation.rememberScrollState
1919import androidx.compose.foundation.verticalScroll
20+ import androidx.compose.material3.CircularProgressIndicator
2021import androidx.compose.material3.HorizontalDivider
2122import androidx.compose.material3.Icon
2223import androidx.compose.runtime.Composable
24+ import androidx.compose.runtime.DisposableEffect
2325import androidx.compose.runtime.LaunchedEffect
2426import androidx.compose.runtime.ReadOnlyComposable
2527import androidx.compose.runtime.getValue
@@ -77,7 +79,6 @@ import to.bitkit.ui.theme.AppThemeSurface
7779import to.bitkit.ui.theme.Colors
7880import to.bitkit.ui.utils.copyToClipboard
7981import to.bitkit.ui.utils.getScreenTitleRes
80- import to.bitkit.utils.Logger
8182import to.bitkit.viewmodels.ActivityDetailViewModel
8283import to.bitkit.viewmodels.ActivityListViewModel
8384
@@ -91,129 +92,185 @@ fun ActivityDetailScreen(
9192 onCloseClick : () -> Unit ,
9293 onChannelClick : ((String ) -> Unit )? = null,
9394) {
94- val activities by listViewModel.filteredActivities.collectAsStateWithLifecycle()
95- val item = activities?.find { it.rawId() == route.id }
96- if (item == null ) {
97- Logger .error(" Activity not found" )
98- return
99- }
95+ val uiState by detailViewModel.uiState.collectAsStateWithLifecycle()
10096
101- val app = appViewModel ? : return
102- val copyToastTitle = stringResource(R .string.common__copied)
103-
104- val tags by detailViewModel.tags.collectAsStateWithLifecycle()
105- val boostSheetVisible by detailViewModel.boostSheetVisible.collectAsStateWithLifecycle()
106- var showAddTagSheet by remember { mutableStateOf(false ) }
107- var isCpfpChild by remember { mutableStateOf(false ) }
108- var boostTxDoesExist by remember { mutableStateOf<Map <String , Boolean >>(emptyMap()) }
109-
110- LaunchedEffect (item) {
111- detailViewModel.setActivity(item)
112- if (item is Activity .Onchain ) {
113- isCpfpChild = detailViewModel.isCpfpChildTransaction(item.v1.txId)
114- boostTxDoesExist = if (item.v1.boostTxIds.isNotEmpty()) {
115- detailViewModel.getBoostTxDoesExist(item.v1.boostTxIds)
116- } else {
117- emptyMap()
118- }
119- } else {
120- isCpfpChild = false
121- boostTxDoesExist = emptyMap()
122- }
97+ // Load activity on composition
98+ LaunchedEffect (route.id) {
99+ detailViewModel.loadActivity(route.id)
123100 }
124101
125- // Update boostTxDoesExist when boostTxIds change
126- LaunchedEffect ( if (item is Activity . Onchain ) item.v1.boostTxIds else emptyList() ) {
127- if (item is Activity . Onchain && item.v1.boostTxIds.isNotEmpty()) {
128- boostTxDoesExist = detailViewModel.getBoostTxDoesExist(item.v1.boostTxIds )
102+ // Clear state on disposal
103+ DisposableEffect ( Unit ) {
104+ onDispose {
105+ detailViewModel.clearActivityState( )
129106 }
130107 }
131108
132- val context = LocalContext .current
133-
134109 Box (
135110 modifier = Modifier .fillMaxWidth()
136111 ) {
137- Column (
138- modifier = Modifier .background(Colors .Black )
139- ) {
140- AppTopBar (
141- titleText = stringResource(
142- if (isCpfpChild) {
143- R .string.wallet__activity_boost_fee
144- } else {
145- item.getScreenTitleRes()
146- }
147- ),
148- onBackClick = onBackClick,
149- actions = { DrawerNavIcon () },
150- )
151- ActivityDetailContent (
152- item = item,
153- tags = tags,
154- onRemoveTag = { detailViewModel.removeTag(it) },
155- onAddTagClick = { showAddTagSheet = true },
156- onClickBoost = detailViewModel::onClickBoost,
157- onExploreClick = onExploreClick,
158- onChannelClick = onChannelClick,
159- detailViewModel = detailViewModel,
160- isCpfpChild = isCpfpChild,
161- boostTxDoesExist = boostTxDoesExist,
162- onCopy = { text ->
163- app.toast(
164- type = Toast .ToastType .SUCCESS ,
165- title = copyToastTitle,
166- description = text.ellipsisMiddle(40 )
112+ when (val loadState = uiState.activityLoadState) {
113+ is ActivityDetailViewModel .ActivityLoadState .Initial ,
114+ is ActivityDetailViewModel .ActivityLoadState .Loading ,
115+ -> {
116+ Column (modifier = Modifier .background(Colors .Black )) {
117+ AppTopBar (
118+ titleText = stringResource(R .string.wallet__activity),
119+ onBackClick = onBackClick,
120+ actions = { DrawerNavIcon () },
167121 )
122+ Box (
123+ modifier = Modifier
124+ .fillMaxSize()
125+ .padding(16 .dp),
126+ contentAlignment = Alignment .Center
127+ ) {
128+ CircularProgressIndicator ()
129+ }
168130 }
169- )
170- if (showAddTagSheet) {
171- ActivityAddTagSheet (
172- listViewModel = listViewModel,
173- activityViewModel = detailViewModel,
174- onDismiss = { showAddTagSheet = false },
175- )
176131 }
177- }
178132
179- if (boostSheetVisible) {
180- (item as ? Activity .Onchain )?.let {
181- @SuppressLint(" LocalContextGetResourceValueCall" )
182- BoostTransactionSheet (
183- onDismiss = detailViewModel::onDismissBoostSheet,
184- item = it,
185- onSuccess = {
186- app.toast(
187- type = Toast .ToastType .SUCCESS ,
188- title = context.getString(R .string.wallet__boost_success_title),
189- description = context.getString(R .string.wallet__boost_success_msg)
133+ is ActivityDetailViewModel .ActivityLoadState .Error -> {
134+ Column (modifier = Modifier .background(Colors .Black )) {
135+ AppTopBar (
136+ titleText = stringResource(R .string.wallet__activity),
137+ onBackClick = onBackClick,
138+ actions = { DrawerNavIcon () },
139+ )
140+ Column (
141+ modifier = Modifier
142+ .fillMaxSize()
143+ .padding(16 .dp),
144+ horizontalAlignment = Alignment .CenterHorizontally ,
145+ verticalArrangement = Arrangement .Center
146+ ) {
147+ BodySSB (
148+ text = loadState.message,
149+ modifier = Modifier .padding(bottom = 16 .dp)
190150 )
191- listViewModel.resync()
192- onCloseClick()
193- },
194- onFailure = {
195- app.toast(
196- type = Toast .ToastType .ERROR ,
197- title = context.getString(R .string.wallet__boost_error_title),
198- description = context.getString(R .string.wallet__boost_error_msg)
151+ PrimaryButton (
152+ text = stringResource(R .string.common__back),
153+ onClick = onBackClick
199154 )
200- detailViewModel.onDismissBoostSheet()
201- },
202- onMaxFee = {
203- app.toast(
204- type = Toast .ToastType .ERROR ,
205- title = context.getString(R .string.wallet__send_fee_error),
206- description = context.getString(R .string.wallet__boost_error_msg_max)
155+ }
156+ }
157+ }
158+
159+ is ActivityDetailViewModel .ActivityLoadState .Success -> {
160+ val item = loadState.activity
161+ val app = appViewModel ? : return @Box
162+ val copyToastTitle = stringResource(R .string.common__copied)
163+
164+ val tags by detailViewModel.tags.collectAsStateWithLifecycle()
165+ val boostSheetVisible by detailViewModel.boostSheetVisible.collectAsStateWithLifecycle()
166+ var showAddTagSheet by remember { mutableStateOf(false ) }
167+ var isCpfpChild by remember { mutableStateOf(false ) }
168+ var boostTxDoesExist by remember { mutableStateOf<Map <String , Boolean >>(emptyMap()) }
169+
170+ LaunchedEffect (item) {
171+ if (item is Activity .Onchain ) {
172+ isCpfpChild = detailViewModel.isCpfpChildTransaction(item.v1.txId)
173+ boostTxDoesExist = if (item.v1.boostTxIds.isNotEmpty()) {
174+ detailViewModel.getBoostTxDoesExist(item.v1.boostTxIds)
175+ } else {
176+ emptyMap()
177+ }
178+ } else {
179+ isCpfpChild = false
180+ boostTxDoesExist = emptyMap()
181+ }
182+ }
183+
184+ // Update boostTxDoesExist when boostTxIds change
185+ LaunchedEffect (if (item is Activity .Onchain ) item.v1.boostTxIds else emptyList()) {
186+ if (item is Activity .Onchain && item.v1.boostTxIds.isNotEmpty()) {
187+ boostTxDoesExist = detailViewModel.getBoostTxDoesExist(item.v1.boostTxIds)
188+ }
189+ }
190+
191+ val context = LocalContext .current
192+
193+ Column (
194+ modifier = Modifier .background(Colors .Black )
195+ ) {
196+ AppTopBar (
197+ titleText = stringResource(
198+ if (isCpfpChild) {
199+ R .string.wallet__activity_boost_fee
200+ } else {
201+ item.getScreenTitleRes()
202+ }
203+ ),
204+ onBackClick = onBackClick,
205+ actions = { DrawerNavIcon () },
206+ )
207+ ActivityDetailContent (
208+ item = item,
209+ tags = tags,
210+ onRemoveTag = { detailViewModel.removeTag(it) },
211+ onAddTagClick = { showAddTagSheet = true },
212+ onClickBoost = detailViewModel::onClickBoost,
213+ onExploreClick = onExploreClick,
214+ onChannelClick = onChannelClick,
215+ detailViewModel = detailViewModel,
216+ isCpfpChild = isCpfpChild,
217+ boostTxDoesExist = boostTxDoesExist,
218+ onCopy = { text ->
219+ app.toast(
220+ type = Toast .ToastType .SUCCESS ,
221+ title = copyToastTitle,
222+ description = text.ellipsisMiddle(40 )
223+ )
224+ }
225+ )
226+ if (showAddTagSheet) {
227+ ActivityAddTagSheet (
228+ listViewModel = listViewModel,
229+ activityViewModel = detailViewModel,
230+ onDismiss = { showAddTagSheet = false },
207231 )
208- },
209- onMinFee = {
210- app.toast(
211- type = Toast .ToastType .ERROR ,
212- title = context.getString(R .string.wallet__send_fee_error),
213- description = context.getString(R .string.wallet__send_fee_error_min)
232+ }
233+ }
234+
235+ if (boostSheetVisible) {
236+ (item as ? Activity .Onchain )?.let {
237+ BoostTransactionSheet (
238+ onDismiss = detailViewModel::onDismissBoostSheet,
239+ item = it,
240+ onSuccess = {
241+ app.toast(
242+ type = Toast .ToastType .SUCCESS ,
243+ title = context.getString(R .string.wallet__boost_success_title),
244+ description = context.getString(R .string.wallet__boost_success_msg)
245+ )
246+ listViewModel.resync()
247+ onCloseClick()
248+ },
249+ onFailure = {
250+ app.toast(
251+ type = Toast .ToastType .ERROR ,
252+ title = context.getString(R .string.wallet__boost_error_title),
253+ description = context.getString(R .string.wallet__boost_error_msg)
254+ )
255+ detailViewModel.onDismissBoostSheet()
256+ },
257+ onMaxFee = {
258+ app.toast(
259+ type = Toast .ToastType .ERROR ,
260+ title = context.getString(R .string.wallet__send_fee_error),
261+ description = context.getString(R .string.wallet__send_fee_error_max)
262+ )
263+ },
264+ onMinFee = {
265+ app.toast(
266+ type = Toast .ToastType .ERROR ,
267+ title = context.getString(R .string.wallet__send_fee_error),
268+ description = context.getString(R .string.wallet__send_fee_error_min)
269+ )
270+ }
214271 )
215272 }
216- )
273+ }
217274 }
218275 }
219276 }
0 commit comments