Skip to content

Commit

Permalink
Merge pull request #58 from akiomik/improve-user-screen
Browse files Browse the repository at this point in the history
Improve user screen
  • Loading branch information
akiomik authored Mar 21, 2023
2 parents d12e0b8 + f8f2390 commit 8e75b6c
Show file tree
Hide file tree
Showing 4 changed files with 126 additions and 48 deletions.
100 changes: 83 additions & 17 deletions app/src/main/java/io/github/akiomik/seiun/ui/user/UserScreen.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@ import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.Divider
Expand All @@ -27,27 +29,33 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.compose.ui.zIndex
import androidx.lifecycle.viewmodel.compose.viewModel
import coil.compose.AsyncImage
import io.github.akiomik.seiun.R
import io.github.akiomik.seiun.model.app.bsky.actor.Profile
import io.github.akiomik.seiun.ui.theme.Indigo800
import io.github.akiomik.seiun.viewmodels.AppViewModel
import io.github.akiomik.seiun.viewmodels.UserFeedViewModel
import me.onebone.toolbar.CollapsingToolbarScaffold
import me.onebone.toolbar.ScrollStrategy
import me.onebone.toolbar.rememberCollapsingToolbarScaffoldState

@Composable
private fun UserBanner(profile: Profile, height: Dp = 128.dp) {
if (profile.banner == null) {
Box {
// fallback
Box(
modifier = Modifier
.background(color = Indigo800)
.height(height)
.fillMaxWidth()
) {}
} else {
)

AsyncImage(
model = profile.banner,
contentDescription = null,
Expand All @@ -58,13 +66,12 @@ private fun UserBanner(profile: Profile, height: Dp = 128.dp) {
}

@Composable
private fun Avatar(profile: Profile) {
private fun Avatar(profile: Profile, modifier: Modifier = Modifier, size: Dp = 64.dp) {
AsyncImage(
model = profile.avatar,
contentDescription = null,
modifier = Modifier
.width(60.dp)
.height(60.dp)
modifier = modifier
.size(size)
.clip(CircleShape)
)
}
Expand All @@ -87,31 +94,90 @@ private fun NameAndHandle(profile: Profile) {
}

@Composable
fun Profile(profile: Profile) {
private fun StatRow(profile: Profile) {
Row(horizontalArrangement = Arrangement.spacedBy(12.dp)) {
Row(verticalAlignment = Alignment.CenterVertically) {
Text(
text = profile.followsCount.toString(),
fontWeight = FontWeight.Bold
)
Text(
text = stringResource(R.string.follows),
style = MaterialTheme.typography.labelMedium
)
}
Row(verticalAlignment = Alignment.CenterVertically) {
Text(
text = profile.followersCount.toString(),
fontWeight = FontWeight.Bold
)
Text(
text = stringResource(R.string.followers),
style = MaterialTheme.typography.labelMedium
)
}
Row(verticalAlignment = Alignment.CenterVertically) {
Text(
text = profile.postsCount.toString(),
fontWeight = FontWeight.Bold
)
Text(
text = stringResource(R.string.posts),
style = MaterialTheme.typography.labelMedium
)
}
}
}

@Composable
private fun Profile(profile: Profile) {
val viewModel: AppViewModel = viewModel()
val viewer by viewModel.profile.collectAsState()

Column(
modifier = Modifier.padding(16.dp),
modifier = Modifier.padding(top = 8.dp, end = 16.dp, bottom = 16.dp, start = 16.dp),
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
Row(
horizontalArrangement = Arrangement.spacedBy(16.dp),
verticalAlignment = Alignment.CenterVertically
) {
Avatar(profile = profile)
NameAndHandle(profile = profile)
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.End) {
// TODO: Implement edit and follow
Spacer(modifier = Modifier.height(32.dp))
// if (profile.did == viewer?.did) {
// Button(onClick = {}) {
// Text(stringResource(R.string.edit))
// }
// } else {
// Button(onClick = {}) {
// Text(stringResource(R.string.follow))
// }
// }
}

NameAndHandle(profile = profile)

Text(profile.description.orEmpty())

StatRow(profile = profile)
}
}

@Composable
private fun UserModalContent(profile: Profile, onProfileClick: (String) -> Unit) {
val bannerHeight = 128.dp
val avatarSize = 96.dp

// TODO: Use ExitUntilCollapsed
CollapsingToolbarScaffold(
state = rememberCollapsingToolbarScaffoldState(),
toolbar = {
Column {
UserBanner(profile)
Box(modifier = Modifier.zIndex(2f)) {
UserBanner(profile, height = bannerHeight)
Avatar(
profile = profile,
modifier = Modifier.offset(x = 16.dp, y = bannerHeight - (avatarSize / 2)),
size = avatarSize
)
}
Box(
modifier = Modifier
.background(color = MaterialTheme.colorScheme.surfaceVariant)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package io.github.akiomik.seiun.viewmodels

import android.util.Log
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.lifecycle.viewModelScope
import io.github.akiomik.seiun.SeiunApplication
import io.github.akiomik.seiun.model.app.bsky.actor.Profile
Expand All @@ -13,62 +12,63 @@ import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.stateIn

class AppViewModel : ApplicationViewModel() {
private val _atpService = SeiunApplication.instance!!.atpService
object AppViewModel : ApplicationViewModel() {
private val innerAtpService = SeiunApplication.instance!!.atpService

private var _profile = MutableStateFlow<Profile?>(null)
private var _showDrawer = MutableStateFlow(false)
private var _showTopBar = MutableStateFlow(false)
private var _showBottomBar = MutableStateFlow(false)
private var _fab = MutableStateFlow<@Composable () -> Unit>({})
private var innerProfile = MutableStateFlow<Profile?>(null)
private var innerShowDrawer = MutableStateFlow(false)
private var innerShowTopBar = MutableStateFlow(false)
private var innerShowBottomBar = MutableStateFlow(false)
private var innerFab = MutableStateFlow<@Composable () -> Unit>({})

private val isDrawerAvailable = _atpService.combine(_profile) { atpService, profile ->
private val isDrawerAvailable = innerAtpService.combine(innerProfile) { atpService, profile ->
atpService != null && profile != null
}

val profile = _profile.asStateFlow()
val showDrawer = _showDrawer.combine(isDrawerAvailable) { showDrawer, isAvailable ->
val profile = innerProfile.asStateFlow()
val showDrawer = innerShowDrawer.combine(isDrawerAvailable) { showDrawer, isAvailable ->
showDrawer && isAvailable
}.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), false)
val showTopBar = _showTopBar.asStateFlow()
val showBottomBar = _showBottomBar.asStateFlow()
val fab = _fab.asStateFlow()
val showTopBar = innerShowTopBar.asStateFlow()
val showBottomBar = innerShowBottomBar.asStateFlow()
val fab = innerFab.asStateFlow()

private val userRepository = SeiunApplication.instance!!.userRepository

fun updateProfile() {
wrapError(
run = { userRepository.getProfile() },
onSuccess = { _profile.value = it },
onSuccess = { innerProfile.value = it },
onError = { Log.d(SeiunApplication.TAG, "Failed to init ProfileViewModel: $it") }
)
}

fun onTimeline() {
_showDrawer.value = true
_showTopBar.value = true
_showBottomBar.value = true
_fab.value = { NewPostFab() }
innerShowDrawer.value = true
innerShowTopBar.value = true
innerShowBottomBar.value = true
innerFab.value = { NewPostFab() }
}

fun onNotification() {
_showDrawer.value = true
_showTopBar.value = true
_showBottomBar.value = true
_fab.value = {}
innerShowDrawer.value = true
innerShowTopBar.value = true
innerShowBottomBar.value = true
innerFab.value = {}
}

fun onUser() {
_showDrawer.value = false
_showTopBar.value = false
_showBottomBar.value = true
_fab.value = {}
innerShowDrawer.value = false
innerShowTopBar.value = false
innerShowBottomBar.value = true
innerFab.value = {}
}

fun onLoginOrRegistration() {
_showDrawer.value = false
_showTopBar.value = false
_showBottomBar.value = false
_fab.value = {}
innerProfile.value = null
innerShowDrawer.value = false
innerShowTopBar.value = false
innerShowBottomBar.value = false
innerFab.value = {}
}
}
6 changes: 6 additions & 0 deletions app/src/main/res/values-ja-rJP/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@
<string name="license">ライセンス</string>
<string name="login_title">ログイン</string>
<string name="service_provider">サービスプロバイダ</string>
<string name="follows">フォロー中</string>
<string name="followers">フォロワー</string>
<string name="posts">投稿</string>
<string name="edit">編集</string>
<string name="follow">フォロー</string>
<string name="unfollow">フォロー解除</string>
<string name="login_handle_or_email">ハンドルネーム または メールアドレス</string>
<string name="login_handle_or_email_placeholder">jack.bsky.social または [email protected]</string>
<string name="login_password">パスワード</string>
Expand Down
6 changes: 6 additions & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@
<string name="mute">Mute</string>
<string name="license">License</string>
<string name="service_provider">Service Provider</string>
<string name="follows">follows</string>
<string name="followers">followers</string>
<string name="posts">posts</string>
<string name="edit">Edit</string>
<string name="follow">Follow</string>
<string name="unfollow">Unfollow</string>
<string name="login_title">Login</string>
<string name="login_handle_or_email">Handle or Email</string>
<string name="login_handle_or_email_placeholder">jack.bsky.social or [email protected]</string>
Expand Down

0 comments on commit 8e75b6c

Please sign in to comment.