Skip to content

Commit

Permalink
Merge pull request #57 from akiomik/improve-user-navigation
Browse files Browse the repository at this point in the history
Add UserScreen
  • Loading branch information
akiomik authored Mar 21, 2023
2 parents 07eb2b7 + aefaa84 commit d12e0b8
Show file tree
Hide file tree
Showing 12 changed files with 189 additions and 131 deletions.
13 changes: 7 additions & 6 deletions app/src/main/java/io/github/akiomik/seiun/ui/app/App.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,22 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.lifecycle.viewmodel.compose.viewModel
import io.github.akiomik.seiun.SeiunApplication
import androidx.navigation.compose.rememberNavController
import io.github.akiomik.seiun.ui.theme.SeiunTheme
import io.github.akiomik.seiun.viewmodels.AppViewModel

@Composable
fun App(from: String?) {
val drawerState = rememberDrawerState(DrawerValue.Closed)
val navController = rememberNavController()
val viewModel: AppViewModel = viewModel()
val profile by viewModel.profile.collectAsState()
val atpService by SeiunApplication.instance!!.atpService.collectAsState()
val drawerEnabled = atpService != null && profile != null
val drawerEnabled by viewModel.showDrawer.collectAsState()

SeiunTheme {
AppDrawer(drawerState, enabled = drawerEnabled) {
AppScaffold(from = from, drawerState = drawerState)
AppDrawer(drawerState, enabled = drawerEnabled, onProfileClick = { profile ->
navController.navigate("user/${profile.did}")
}) {
AppScaffold(from = from, drawerState = drawerState, navController = navController)
}
}
}
20 changes: 14 additions & 6 deletions app/src/main/java/io/github/akiomik/seiun/ui/app/AppBottomBar.kt
Original file line number Diff line number Diff line change
Expand Up @@ -39,15 +39,23 @@ fun AppBottomBar(
selected = currentDestination?.hierarchy?.any { it.route == screen.route } == true,
onClick = {
navController.navigate(screen.route) {
if (screen.route == "timeline" && screen.route == currentDestination?.route) {
coroutineScope.launch {
timelineListState.animateScrollToItem(0)
if (screen.route == "timeline") {
navController.popBackStack("timeline", false)

if (screen.route == currentDestination?.route) {
coroutineScope.launch {
timelineListState.animateScrollToItem(0)
}
}
}

if (screen.route == "notification" && screen.route == currentDestination?.route) {
coroutineScope.launch {
notificationListState.animateScrollToItem(0)
if (screen.route == "notification") {
navController.popBackStack("notification", false)

if (screen.route == currentDestination?.route) {
coroutineScope.launch {
notificationListState.animateScrollToItem(0)
}
}
}

Expand Down
34 changes: 14 additions & 20 deletions app/src/main/java/io/github/akiomik/seiun/ui/app/AppDrawer.kt
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,6 @@ import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
Expand All @@ -37,30 +35,18 @@ import coil.compose.AsyncImage
import com.google.android.gms.oss.licenses.OssLicensesMenuActivity
import io.github.akiomik.seiun.R
import io.github.akiomik.seiun.model.app.bsky.actor.Profile
import io.github.akiomik.seiun.ui.user.UserModal
import io.github.akiomik.seiun.viewmodels.AppViewModel
import kotlinx.coroutines.launch

@Composable
private fun Profile(drawerState: DrawerState) {
private fun Profile(onClicked: (Profile) -> Unit) {
val viewModel: AppViewModel = viewModel()
val profile by viewModel.profile.collectAsState()
var showUserModal by remember { mutableStateOf(false) }
val scope = rememberCoroutineScope()

if (showUserModal) {
profile?.let {
UserModal(profile = it) {
showUserModal = false
}
}
}

Row(
modifier = Modifier.padding(16.dp).clickable {
scope.launch { drawerState.close() }
showUserModal = true
},
modifier = Modifier
.padding(16.dp)
.clickable { profile?.let(onClicked) },
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(12.dp)
) {
Expand Down Expand Up @@ -97,7 +83,12 @@ private fun NameAndHandle(profile: Profile?) {
}

@Composable
fun AppDrawer(state: DrawerState, enabled: Boolean, content: @Composable () -> Unit) {
fun AppDrawer(
state: DrawerState,
enabled: Boolean,
onProfileClick: (Profile) -> Unit,
content: @Composable () -> Unit
) {
// var selected by remember { mutableStateOf("") }
val context = LocalContext.current
val scope = rememberCoroutineScope()
Expand All @@ -108,7 +99,10 @@ fun AppDrawer(state: DrawerState, enabled: Boolean, content: @Composable () -> U
drawerContent = {
ModalDrawerSheet {
if (enabled) {
Profile(state)
Profile {
scope.launch { state.close() }
onProfileClick(it)
}
Divider()
}

Expand Down
22 changes: 20 additions & 2 deletions app/src/main/java/io/github/akiomik/seiun/ui/app/AppNavigation.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,16 @@ import androidx.compose.ui.platform.LocalContext
import androidx.core.content.ContextCompat
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavHostController
import androidx.navigation.NavType
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.navArgument
import io.github.akiomik.seiun.SeiunApplication
import io.github.akiomik.seiun.ui.login.LoginScreen
import io.github.akiomik.seiun.ui.notification.NotificationScreen
import io.github.akiomik.seiun.ui.registration.RegistrationScreen
import io.github.akiomik.seiun.ui.timeline.TimelineScreen
import io.github.akiomik.seiun.ui.user.UserScreen
import io.github.akiomik.seiun.viewmodels.AppViewModel

@Composable
Expand Down Expand Up @@ -73,7 +76,10 @@ fun AppNavigation(
application.registerNotificationWorker()
}

TimelineScreen(timelineListState)
TimelineScreen(
timelineListState,
onProfileClick = { navController.navigate("user/$it") }
)
}
composable("notification") {
if (atpService == null) {
Expand All @@ -94,7 +100,19 @@ fun AppNavigation(
application.registerNotificationWorker()
}

NotificationScreen(notificationListState)
NotificationScreen(
notificationListState,
onProfileClick = { navController.navigate("user/$it") }
)
}
composable(
"user/{did}",
arguments = listOf(navArgument("did") { type = NavType.StringType })
) {
UserScreen(
it.arguments?.getString("did")!!,
onProfileClick = { did -> navController.navigate("user/$did") }
)
}
composable("login") {
LoginScreen(onLoginSuccess = {
Expand Down
47 changes: 22 additions & 25 deletions app/src/main/java/io/github/akiomik/seiun/ui/app/AppScaffold.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,31 +10,29 @@ import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavHostController
import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController
import androidx.work.WorkManager
import io.github.akiomik.seiun.SeiunApplication
import io.github.akiomik.seiun.ui.timeline.NewPostFab
import io.github.akiomik.seiun.viewmodels.AppViewModel

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun AppScaffold(from: String?, drawerState: DrawerState) {
val navController = rememberNavController()
fun AppScaffold(from: String?, drawerState: DrawerState, navController: NavHostController) {
val viewModel: AppViewModel = viewModel()
val navBackStackEntry by navController.currentBackStackEntryAsState()
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior()
val timelineListState = rememberLazyListState()
val notificationListState = rememberLazyListState()
var topBarState by rememberSaveable { (mutableStateOf(false)) }
var bottomBarState by rememberSaveable { (mutableStateOf(false)) }
var fabState by remember { (mutableStateOf<@Composable () -> Unit>({})) }
val atpService by SeiunApplication.instance!!.atpService.collectAsState()
val showTopBar by viewModel.showTopBar.collectAsState()
val showBottomBar by viewModel.showBottomBar.collectAsState()
val fab by viewModel.fab.collectAsState()

val startDestination =
if (from == "notification" && atpService != null) {
Expand All @@ -43,38 +41,37 @@ fun AppScaffold(from: String?, drawerState: DrawerState) {
"login"
}

when (navBackStackEntry?.destination?.route) {
"timeline" -> {
topBarState = true
bottomBarState = true
fabState = { NewPostFab() }
val route = navBackStackEntry?.destination?.route
val matchUser = route?.startsWith("user/", true) ?: false
when {
route == "timeline" -> {
viewModel.onTimeline()
}
"notification" -> {
topBarState = true
bottomBarState = true
fabState = {}
route == "notification" -> {
viewModel.onNotification()
}
matchUser -> {
viewModel.onUser()
}
else -> {
Log.d(SeiunApplication.TAG, "Cancel all work")
WorkManager.getInstance(LocalContext.current).cancelAllWork()

topBarState = false
bottomBarState = false
fabState = {}
viewModel.onLoginOrRegistration()
}
}

Scaffold(
topBar = { AppTopBar(scrollBehavior, visible = topBarState, drawerState = drawerState) },
topBar = { AppTopBar(scrollBehavior, visible = showTopBar, drawerState = drawerState) },
bottomBar = {
AppBottomBar(
navController = navController,
visible = bottomBarState,
visible = showBottomBar,
timelineListState = timelineListState,
notificationListState = notificationListState
)
},
floatingActionButton = fabState,
floatingActionButton = fab,
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
content = {
AppNavigation(
Expand Down
17 changes: 4 additions & 13 deletions app/src/main/java/io/github/akiomik/seiun/ui/feed/FeedPost.kt
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,6 @@ import io.github.akiomik.seiun.ui.dialog.MuteDialog
import io.github.akiomik.seiun.ui.dialog.ReportDialog
import io.github.akiomik.seiun.ui.theme.Green700
import io.github.akiomik.seiun.ui.theme.Red700
import io.github.akiomik.seiun.ui.user.UserModal
import io.github.akiomik.seiun.utilities.NumberFormatter
import io.github.akiomik.seiun.viewmodels.AppViewModel
import io.github.akiomik.seiun.viewmodels.PostViewModel
Expand Down Expand Up @@ -102,23 +101,15 @@ private fun ReplyText(viewPost: FeedViewPost) {
}

@Composable
private fun Avatar(viewPost: FeedViewPost) {
var showUserModal by remember { mutableStateOf(false) }

if (showUserModal) {
UserModal(did = viewPost.post.author.did) {
showUserModal = false
}
}

private fun Avatar(viewPost: FeedViewPost, onClick: (String) -> Unit) {
AsyncImage(
model = viewPost.post.author.avatar,
contentDescription = null,
modifier = Modifier
.width(50.dp)
.height(50.dp)
.clip(CircleShape)
.clickable { showUserModal = true }
.clickable { onClick(viewPost.post.author.did) }
)
}

Expand Down Expand Up @@ -483,7 +474,7 @@ fun ImagePager(images: List<PresentedImage>, initialIndex: Int, onDismissRequest
}

@Composable
fun FeedPost(viewPost: FeedViewPost) {
fun FeedPost(viewPost: FeedViewPost, onProfileClick: (String) -> Unit) {
Column(modifier = Modifier.padding(14.dp)) {
if (viewPost.reason?.type == "app.bsky.feed.feedViewPost#reasonRepost") {
RepostText(viewPost = viewPost)
Expand All @@ -492,7 +483,7 @@ fun FeedPost(viewPost: FeedViewPost) {
}

Row(horizontalArrangement = Arrangement.spacedBy(10.dp)) {
Avatar(viewPost = viewPost)
Avatar(viewPost = viewPost, onClick = onProfileClick)
FeedPostContent(viewPost = viewPost)
}
}
Expand Down
Loading

0 comments on commit d12e0b8

Please sign in to comment.