Skip to content

Commit 38beb89

Browse files
committed
chore(fc): improve transition on profile screen between states
Signed-off-by: Brandon McAnsh <git@bmcreations.dev>
1 parent 5349ed2 commit 38beb89

1 file changed

Lines changed: 226 additions & 80 deletions

File tree

flipchatApp/src/main/kotlin/xyz/flipchat/app/features/profile/ProfileScreen.kt

Lines changed: 226 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import androidx.compose.animation.slideOutVertically
1414
import androidx.compose.foundation.Image
1515
import androidx.compose.foundation.layout.Column
1616
import androidx.compose.foundation.layout.Spacer
17+
import androidx.compose.foundation.layout.fillMaxSize
1718
import androidx.compose.foundation.layout.fillMaxWidth
1819
import androidx.compose.foundation.layout.padding
1920
import androidx.compose.foundation.layout.size
@@ -43,6 +44,8 @@ import androidx.compose.ui.unit.dp
4344
import androidx.lifecycle.Lifecycle
4445
import cafe.adriel.voyager.core.registry.ScreenRegistry
4546
import cafe.adriel.voyager.core.screen.Screen
47+
import cafe.adriel.voyager.core.screen.ScreenKey
48+
import cafe.adriel.voyager.core.screen.uniqueScreenKey
4649
import cafe.adriel.voyager.hilt.getViewModel
4750
import com.getcode.manager.BottomBarManager
4851
import com.getcode.model.ID
@@ -74,6 +77,9 @@ import xyz.flipchat.services.internal.data.mapper.nullIfEmpty
7477

7578
@Parcelize
7679
class ProfileScreen(val userId: ID? = null, val isInTab: Boolean) : Screen, Parcelable {
80+
81+
override val key: ScreenKey = uniqueScreenKey
82+
7783
@Composable
7884
override fun Content() {
7985
val navigator = LocalCodeNavigator.current
@@ -200,19 +206,32 @@ private fun ProfileContent(
200206
}
201207
)
202208

203-
// Crossfade between no-account and linked-account states
204-
Crossfade(
205-
targetState = state.linkedSocialProfile != null,
206-
animationSpec = tween(durationMillis = 300)
207-
) { isLinked ->
208-
if (!isLinked) {
209-
// No account linked
210-
Column(horizontalAlignment = Alignment.CenterHorizontally) {
211-
Text(
212-
text = state.displayName,
213-
style = CodeTheme.typography.textLarge,
214-
color = CodeTheme.colors.textMain
215-
)
209+
// SocialUserDisplay (title with platform logo)
210+
if (state.linkedSocialProfile != null) {
211+
SocialUserDisplay(
212+
modifier = Modifier.fillMaxWidth(),
213+
profile = state.linkedSocialProfile
214+
)
215+
} else {
216+
Text(
217+
text = state.displayName,
218+
style = CodeTheme.typography.textLarge,
219+
color = CodeTheme.colors.textMain,
220+
textAlign = TextAlign.Center
221+
)
222+
}
223+
224+
Column(
225+
modifier = Modifier.fillMaxWidth(),
226+
horizontalAlignment = Alignment.CenterHorizontally
227+
) {
228+
// Crossfade for linked vs unlinked content
229+
Crossfade(
230+
targetState = state.linkedSocialProfile != null,
231+
animationSpec = tween(durationMillis = 300)
232+
) { isLinked ->
233+
if (!isLinked) {
234+
// Unlinked state
216235
if (state.canConnectAccount && state.isSelf) {
217236
val xOAuthLauncher = rememberLauncherForOAuth(OAuthProvider.X) { accessToken ->
218237
println("x access token=$accessToken")
@@ -238,92 +257,219 @@ private fun ProfileContent(
238257
contentDescription = null
239258
)
240259
Spacer(Modifier.width(CodeTheme.dimens.grid.x2))
241-
Text(
242-
text = stringResource(R.string.action_connectYourAccount),
243-
)
260+
Text(text = stringResource(R.string.action_connectYourAccount))
244261
}
245262
)
246263
}
247264
}
248-
}
249-
} else {
250-
// Linked account state
251-
state.linkedSocialProfile?.let { linkedProfile ->
252-
Column(horizontalAlignment = Alignment.CenterHorizontally) {
253-
SocialUserDisplay(
254-
modifier = Modifier.fillMaxWidth(),
255-
profile = linkedProfile
256-
)
257-
258-
state.username?.let { username ->
259-
AnimatedVisibility(
260-
visible = true,
261-
enter = fadeIn(),
262-
exit = fadeOut() + shrinkVertically()
263-
) {
264-
Text(
265-
modifier = Modifier.fillMaxWidth(),
266-
text = username,
267-
style = CodeTheme.typography.textSmall,
268-
color = CodeTheme.colors.textSecondary,
269-
textAlign = TextAlign.Center
270-
)
271-
}
272-
}
273-
274-
when (linkedProfile) {
275-
is SocialProfile.Unknown -> Unit
276-
is SocialProfile.X -> {
265+
} else {
266+
// Linked state
267+
state.linkedSocialProfile?.let { linkedProfile ->
268+
Column(
269+
horizontalAlignment = Alignment.CenterHorizontally
270+
) {
271+
state.username?.let { username ->
277272
AnimatedVisibility(
278273
visible = true,
279274
enter = fadeIn(),
280275
exit = fadeOut() + shrinkVertically()
281276
) {
282-
Column(horizontalAlignment = Alignment.CenterHorizontally) {
283-
Text(
284-
modifier = Modifier
285-
.fillMaxWidth()
286-
.padding(top = CodeTheme.dimens.grid.x8),
287-
text = "${linkedProfile.followerCountFormatted} Followers",
288-
style = CodeTheme.typography.textSmall,
289-
color = CodeTheme.colors.textSecondary,
290-
textAlign = TextAlign.Center
291-
)
292-
Text(
293-
modifier = Modifier
294-
.fillMaxWidth(0.70f)
295-
.padding(top = CodeTheme.dimens.grid.x1),
296-
text = linkedProfile.description,
297-
style = CodeTheme.typography.textSmall,
298-
color = CodeTheme.colors.textSecondary,
299-
textAlign = TextAlign.Center,
300-
)
277+
Text(
278+
modifier = Modifier.fillMaxWidth(),
279+
text = username,
280+
style = CodeTheme.typography.textSmall,
281+
color = CodeTheme.colors.textSecondary,
282+
textAlign = TextAlign.Center
283+
)
284+
}
285+
}
286+
287+
when (linkedProfile) {
288+
is SocialProfile.X -> {
289+
AnimatedVisibility(
290+
visible = true,
291+
enter = fadeIn(),
292+
exit = fadeOut() + shrinkVertically()
293+
) {
294+
Column(horizontalAlignment = Alignment.CenterHorizontally) {
295+
Text(
296+
modifier = Modifier
297+
.fillMaxWidth()
298+
.padding(top = CodeTheme.dimens.grid.x8),
299+
text = "${linkedProfile.followerCountFormatted} Followers",
300+
style = CodeTheme.typography.textSmall,
301+
color = CodeTheme.colors.textSecondary,
302+
textAlign = TextAlign.Center
303+
)
304+
Text(
305+
modifier = Modifier
306+
.fillMaxWidth(0.70f)
307+
.padding(top = CodeTheme.dimens.grid.x1),
308+
text = linkedProfile.description,
309+
style = CodeTheme.typography.textSmall,
310+
color = CodeTheme.colors.textSecondary,
311+
textAlign = TextAlign.Center
312+
)
313+
}
301314
}
302315
}
316+
is SocialProfile.Unknown -> Unit
303317
}
304-
}
305318

306-
if (!isInTab) {
307-
AnimatedVisibility(
308-
visible = true,
309-
enter = fadeIn() + slideInVertically(initialOffsetY = { it / 2 }),
310-
exit = fadeOut() + slideOutVertically(targetOffsetY = { it / 2 })
311-
) {
312-
CodeButton(
313-
modifier = Modifier
314-
.fillMaxWidth()
315-
.padding(top = CodeTheme.dimens.grid.x12)
316-
.padding(horizontal = CodeTheme.dimens.inset),
317-
buttonState = ButtonState.Filled,
318-
onClick = { uriHandler.openUri(linkedProfile.profileUrl) },
319-
text = stringResource(R.string.action_openProfileOnPlatform, linkedProfile.platformTypeName)
320-
)
319+
if (!isInTab) {
320+
AnimatedVisibility(
321+
visible = true,
322+
enter = fadeIn() + slideInVertically(initialOffsetY = { it / 2 }),
323+
exit = fadeOut() + slideOutVertically(targetOffsetY = { it / 2 })
324+
) {
325+
CodeButton(
326+
modifier = Modifier
327+
.fillMaxWidth()
328+
.padding(top = CodeTheme.dimens.grid.x12)
329+
.padding(horizontal = CodeTheme.dimens.inset),
330+
buttonState = ButtonState.Filled,
331+
onClick = { uriHandler.openUri(linkedProfile.profileUrl) },
332+
text = stringResource(R.string.action_openProfileOnPlatform, linkedProfile.platformTypeName)
333+
)
334+
}
321335
}
322336
}
323337
}
324338
}
325339
}
326340
}
341+
342+
// // Crossfade between no-account and linked-account states
343+
// Crossfade(
344+
// modifier = Modifier.fillMaxSize(),
345+
// targetState = state.linkedSocialProfile != null,
346+
// animationSpec = tween(durationMillis = 300)
347+
// ) { isLinked ->
348+
// if (!isLinked) {
349+
// // No account linked
350+
// Column(
351+
// modifier = Modifier.fillMaxSize(),
352+
// horizontalAlignment = Alignment.CenterHorizontally
353+
// ) {
354+
// Text(
355+
// text = state.displayName,
356+
// style = CodeTheme.typography.textLarge,
357+
// color = CodeTheme.colors.textMain
358+
// )
359+
// if (state.canConnectAccount && state.isSelf) {
360+
// val xOAuthLauncher = rememberLauncherForOAuth(OAuthProvider.X) { accessToken ->
361+
// println("x access token=$accessToken")
362+
// dispatchEvent(ProfileViewModel.Event.LinkXAccount(accessToken))
363+
// }
364+
//
365+
// AnimatedVisibility(
366+
// visible = true,
367+
// enter = fadeIn() + slideInVertically(initialOffsetY = { it / 2 }),
368+
// exit = fadeOut() + slideOutVertically(targetOffsetY = { it / 2 })
369+
// ) {
370+
// CodeButton(
371+
// modifier = Modifier
372+
// .fillMaxWidth()
373+
// .padding(top = CodeTheme.dimens.grid.x12)
374+
// .padding(horizontal = CodeTheme.dimens.inset),
375+
// buttonState = ButtonState.Filled,
376+
// onClick = { xOAuthLauncher.launch(OAuthProvider.X.launchIntent(context)) },
377+
// content = {
378+
// Image(
379+
// painter = rememberVectorPainter(image = ImageVector.vectorResource(id = R.drawable.ic_twitter_x)),
380+
// colorFilter = ColorFilter.tint(Color.Black),
381+
// contentDescription = null
382+
// )
383+
// Spacer(Modifier.width(CodeTheme.dimens.grid.x2))
384+
// Text(
385+
// text = stringResource(R.string.action_connectYourAccount),
386+
// )
387+
// }
388+
// )
389+
// }
390+
// }
391+
// }
392+
// } else {
393+
// // Linked account state
394+
// state.linkedSocialProfile?.let { linkedProfile ->
395+
// Column(
396+
// modifier = Modifier.fillMaxSize(),
397+
// horizontalAlignment = Alignment.CenterHorizontally
398+
// ) {
399+
// SocialUserDisplay(
400+
// modifier = Modifier.fillMaxWidth(),
401+
// profile = linkedProfile
402+
// )
403+
//
404+
// state.username?.let { username ->
405+
// AnimatedVisibility(
406+
// visible = true,
407+
// enter = fadeIn(),
408+
// exit = fadeOut() + shrinkVertically()
409+
// ) {
410+
// Text(
411+
// modifier = Modifier.fillMaxWidth(),
412+
// text = username,
413+
// style = CodeTheme.typography.textSmall,
414+
// color = CodeTheme.colors.textSecondary,
415+
// textAlign = TextAlign.Center
416+
// )
417+
// }
418+
// }
419+
//
420+
// when (linkedProfile) {
421+
// is SocialProfile.Unknown -> Unit
422+
// is SocialProfile.X -> {
423+
// AnimatedVisibility(
424+
// visible = true,
425+
// enter = fadeIn(),
426+
// exit = fadeOut() + shrinkVertically()
427+
// ) {
428+
// Column(horizontalAlignment = Alignment.CenterHorizontally) {
429+
// Text(
430+
// modifier = Modifier
431+
// .fillMaxWidth()
432+
// .padding(top = CodeTheme.dimens.grid.x8),
433+
// text = "${linkedProfile.followerCountFormatted} Followers",
434+
// style = CodeTheme.typography.textSmall,
435+
// color = CodeTheme.colors.textSecondary,
436+
// textAlign = TextAlign.Center
437+
// )
438+
// Text(
439+
// modifier = Modifier
440+
// .fillMaxWidth(0.70f)
441+
// .padding(top = CodeTheme.dimens.grid.x1),
442+
// text = linkedProfile.description,
443+
// style = CodeTheme.typography.textSmall,
444+
// color = CodeTheme.colors.textSecondary,
445+
// textAlign = TextAlign.Center,
446+
// )
447+
// }
448+
// }
449+
// }
450+
// }
451+
//
452+
// if (!isInTab) {
453+
// AnimatedVisibility(
454+
// visible = true,
455+
// enter = fadeIn() + slideInVertically(initialOffsetY = { it / 2 }),
456+
// exit = fadeOut() + slideOutVertically(targetOffsetY = { it / 2 })
457+
// ) {
458+
// CodeButton(
459+
// modifier = Modifier
460+
// .fillMaxWidth()
461+
// .padding(top = CodeTheme.dimens.grid.x12)
462+
// .padding(horizontal = CodeTheme.dimens.inset),
463+
// buttonState = ButtonState.Filled,
464+
// onClick = { uriHandler.openUri(linkedProfile.profileUrl) },
465+
// text = stringResource(R.string.action_openProfileOnPlatform, linkedProfile.platformTypeName)
466+
// )
467+
// }
468+
// }
469+
// }
470+
// }
471+
// }
472+
// }
327473
}
328474
}
329475

0 commit comments

Comments
 (0)