feat: initial tablet support
Signed-off-by: Infi <wingit@geist.ga>
This commit is contained in:
parent
6a7cbc335d
commit
5dd89fa070
|
|
@ -126,6 +126,7 @@ dependencies {
|
||||||
implementation "androidx.compose.ui:ui-util"
|
implementation "androidx.compose.ui:ui-util"
|
||||||
implementation 'androidx.compose.material:material'
|
implementation 'androidx.compose.material:material'
|
||||||
implementation 'androidx.compose.material3:material3'
|
implementation 'androidx.compose.material3:material3'
|
||||||
|
implementation 'androidx.compose.material3:material3-window-size-class'
|
||||||
implementation "androidx.compose.ui:ui-tooling-preview"
|
implementation "androidx.compose.ui:ui-tooling-preview"
|
||||||
implementation "androidx.compose.runtime:runtime-livedata"
|
implementation "androidx.compose.runtime:runtime-livedata"
|
||||||
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.6.1'
|
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.6.1'
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,9 @@ import androidx.compose.animation.fadeOut
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Surface
|
import androidx.compose.material3.Surface
|
||||||
|
import androidx.compose.material3.windowsizeclass.ExperimentalMaterial3WindowSizeClassApi
|
||||||
|
import androidx.compose.material3.windowsizeclass.WindowSizeClass
|
||||||
|
import androidx.compose.material3.windowsizeclass.calculateWindowSizeClass
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
|
|
@ -45,6 +48,7 @@ import io.sentry.android.core.SentryAndroid
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class MainActivity : FragmentActivity() {
|
class MainActivity : FragmentActivity() {
|
||||||
|
@OptIn(ExperimentalMaterial3WindowSizeClassApi::class)
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
|
|
@ -56,7 +60,8 @@ class MainActivity : FragmentActivity() {
|
||||||
WindowCompat.setDecorFitsSystemWindows(window, false)
|
WindowCompat.setDecorFitsSystemWindows(window, false)
|
||||||
|
|
||||||
setContent {
|
setContent {
|
||||||
AppEntrypoint()
|
val windowSizeClass = calculateWindowSizeClass(this)
|
||||||
|
AppEntrypoint(windowSizeClass)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -67,7 +72,7 @@ val RevoltTweenDp: FiniteAnimationSpec<Dp> = tween(400, easing = EaseInOutExpo)
|
||||||
val RevoltTweenColour: FiniteAnimationSpec<Color> = tween(400, easing = EaseInOutExpo)
|
val RevoltTweenColour: FiniteAnimationSpec<Color> = tween(400, easing = EaseInOutExpo)
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun AppEntrypoint() {
|
fun AppEntrypoint(windowSizeClass: WindowSizeClass) {
|
||||||
val navController = rememberNavController()
|
val navController = rememberNavController()
|
||||||
|
|
||||||
RevoltTheme(
|
RevoltTheme(
|
||||||
|
|
@ -126,7 +131,7 @@ fun AppEntrypoint() {
|
||||||
}
|
}
|
||||||
composable("register/onboarding") { OnboardingScreen(navController) }
|
composable("register/onboarding") { OnboardingScreen(navController) }
|
||||||
|
|
||||||
composable("chat") { ChatRouterScreen(navController) }
|
composable("chat") { ChatRouterScreen(navController, windowSizeClass) }
|
||||||
|
|
||||||
composable("settings") { SettingsScreen(navController) }
|
composable("settings") { SettingsScreen(navController) }
|
||||||
composable("settings/appearance") { AppearanceSettingsScreen(navController) }
|
composable("settings/appearance") { AppearanceSettingsScreen(navController) }
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.layout.width
|
import androidx.compose.foundation.layout.width
|
||||||
|
|
@ -31,6 +32,7 @@ fun ChannelHeader(
|
||||||
channel: Channel,
|
channel: Channel,
|
||||||
onChannelClick: (String) -> Unit,
|
onChannelClick: (String) -> Unit,
|
||||||
onToggleDrawer: () -> Unit,
|
onToggleDrawer: () -> Unit,
|
||||||
|
useDrawer: Boolean,
|
||||||
) {
|
) {
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
|
|
@ -41,17 +43,26 @@ fun ChannelHeader(
|
||||||
.padding(vertical = 4.dp, horizontal = 4.dp),
|
.padding(vertical = 4.dp, horizontal = 4.dp),
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
) {
|
) {
|
||||||
IconButton(onClick = {
|
if (useDrawer) {
|
||||||
onToggleDrawer()
|
IconButton(onClick = {
|
||||||
}) {
|
onToggleDrawer()
|
||||||
Icon(
|
}) {
|
||||||
imageVector = Icons.Default.Menu,
|
Icon(
|
||||||
contentDescription = stringResource(R.string.menu),
|
imageVector = Icons.Default.Menu,
|
||||||
|
contentDescription = stringResource(R.string.menu),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.width(4.dp))
|
||||||
|
} else {
|
||||||
|
// Compensate for the IconButton not increasing our height
|
||||||
|
Spacer(
|
||||||
|
modifier = Modifier
|
||||||
|
.height(48.dp)
|
||||||
|
.width(12.dp)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
Spacer(modifier = Modifier.width(4.dp))
|
|
||||||
|
|
||||||
channel.channelType?.let {
|
channel.channelType?.let {
|
||||||
ChannelIcon(
|
ChannelIcon(
|
||||||
channelType = it,
|
channelType = it,
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import androidx.compose.animation.AnimatedVisibility
|
||||||
import androidx.compose.animation.Crossfade
|
import androidx.compose.animation.Crossfade
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.RowScope
|
||||||
import androidx.compose.foundation.layout.aspectRatio
|
import androidx.compose.foundation.layout.aspectRatio
|
||||||
import androidx.compose.foundation.layout.fillMaxHeight
|
import androidx.compose.foundation.layout.fillMaxHeight
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
|
@ -19,6 +20,7 @@ import androidx.compose.material.icons.filled.Add
|
||||||
import androidx.compose.material3.AlertDialog
|
import androidx.compose.material3.AlertDialog
|
||||||
import androidx.compose.material3.DismissibleDrawerSheet
|
import androidx.compose.material3.DismissibleDrawerSheet
|
||||||
import androidx.compose.material3.DismissibleNavigationDrawer
|
import androidx.compose.material3.DismissibleNavigationDrawer
|
||||||
|
import androidx.compose.material3.DrawerState
|
||||||
import androidx.compose.material3.DrawerValue
|
import androidx.compose.material3.DrawerValue
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
|
|
@ -27,6 +29,8 @@ import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TextButton
|
import androidx.compose.material3.TextButton
|
||||||
import androidx.compose.material3.rememberDrawerState
|
import androidx.compose.material3.rememberDrawerState
|
||||||
import androidx.compose.material3.rememberModalBottomSheetState
|
import androidx.compose.material3.rememberModalBottomSheetState
|
||||||
|
import androidx.compose.material3.windowsizeclass.WindowSizeClass
|
||||||
|
import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
|
|
@ -46,6 +50,7 @@ import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import androidx.navigation.NavController
|
import androidx.navigation.NavController
|
||||||
|
import androidx.navigation.NavHostController
|
||||||
import androidx.navigation.compose.NavHost
|
import androidx.navigation.compose.NavHost
|
||||||
import androidx.navigation.compose.composable
|
import androidx.navigation.compose.composable
|
||||||
import androidx.navigation.compose.dialog
|
import androidx.navigation.compose.dialog
|
||||||
|
|
@ -181,7 +186,11 @@ class ChatRouterViewModel @Inject constructor(
|
||||||
ExperimentalMaterial3Api::class
|
ExperimentalMaterial3Api::class
|
||||||
)
|
)
|
||||||
@Composable
|
@Composable
|
||||||
fun ChatRouterScreen(topNav: NavController, viewModel: ChatRouterViewModel = hiltViewModel()) {
|
fun ChatRouterScreen(
|
||||||
|
topNav: NavController,
|
||||||
|
windowSizeClass: WindowSizeClass,
|
||||||
|
viewModel: ChatRouterViewModel = hiltViewModel()
|
||||||
|
) {
|
||||||
val drawerState = rememberDrawerState(DrawerValue.Closed)
|
val drawerState = rememberDrawerState(DrawerValue.Closed)
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
val keyboardController = LocalSoftwareKeyboardController.current
|
val keyboardController = LocalSoftwareKeyboardController.current
|
||||||
|
|
@ -206,6 +215,8 @@ fun ChatRouterScreen(topNav: NavController, viewModel: ChatRouterViewModel = hil
|
||||||
var userContextSheetTarget by remember { mutableStateOf("") }
|
var userContextSheetTarget by remember { mutableStateOf("") }
|
||||||
var userContextSheetServer by remember { mutableStateOf<String?>(null) }
|
var userContextSheetServer by remember { mutableStateOf<String?>(null) }
|
||||||
|
|
||||||
|
var useTabletAwareUI by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
val drawerBackHandler = remember {
|
val drawerBackHandler = remember {
|
||||||
{
|
{
|
||||||
scope.launch {
|
scope.launch {
|
||||||
|
|
@ -270,6 +281,14 @@ fun ChatRouterScreen(topNav: NavController, viewModel: ChatRouterViewModel = hil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LaunchedEffect(windowSizeClass) {
|
||||||
|
snapshotFlow { windowSizeClass }
|
||||||
|
.distinctUntilChanged()
|
||||||
|
.collect { sizeClass ->
|
||||||
|
useTabletAwareUI = sizeClass.widthSizeClass == WindowWidthSizeClass.Expanded
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (showSidebarSpark.value) {
|
if (showSidebarSpark.value) {
|
||||||
AlertDialog(
|
AlertDialog(
|
||||||
onDismissRequest = {},
|
onDismissRequest = {},
|
||||||
|
|
@ -408,241 +427,329 @@ fun ChatRouterScreen(topNav: NavController, viewModel: ChatRouterViewModel = hil
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
DismissibleNavigationDrawer(
|
if (useTabletAwareUI) {
|
||||||
drawerState = drawerState,
|
Row {
|
||||||
drawerContent = {
|
Sidebar(
|
||||||
DismissibleDrawerSheet(
|
viewModel = viewModel,
|
||||||
drawerContainerColor = Color.Transparent,
|
navController = navController,
|
||||||
) {
|
onShowStatusSheet = {
|
||||||
Column(Modifier.fillMaxWidth()) {
|
showStatusSheet = true
|
||||||
Row {
|
},
|
||||||
Column(
|
onShowServerContextSheet = {
|
||||||
|
serverContextSheetTarget = it
|
||||||
|
showServerContextSheet = true
|
||||||
|
},
|
||||||
|
onShowAddServerSheet = {
|
||||||
|
showAddServerSheet = true
|
||||||
|
},
|
||||||
|
useDrawer = false,
|
||||||
|
)
|
||||||
|
ChannelNavigator(
|
||||||
|
navController = navController,
|
||||||
|
topNav = topNav,
|
||||||
|
useDrawer = false,
|
||||||
|
drawerBackHandler = {
|
||||||
|
drawerBackHandler()
|
||||||
|
},
|
||||||
|
onShowUserContextSheet = { target, server ->
|
||||||
|
userContextSheetTarget = target
|
||||||
|
userContextSheetServer = server
|
||||||
|
showUserContextSheet = true
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
DismissibleNavigationDrawer(
|
||||||
|
drawerState = drawerState,
|
||||||
|
drawerContent = {
|
||||||
|
DismissibleDrawerSheet(
|
||||||
|
drawerContainerColor = Color.Transparent,
|
||||||
|
) {
|
||||||
|
Row(Modifier.fillMaxWidth()) {
|
||||||
|
Sidebar(
|
||||||
|
viewModel = viewModel,
|
||||||
|
navController = navController,
|
||||||
|
onShowStatusSheet = {
|
||||||
|
showStatusSheet = true
|
||||||
|
},
|
||||||
|
onShowServerContextSheet = {
|
||||||
|
serverContextSheetTarget = it
|
||||||
|
showServerContextSheet = true
|
||||||
|
},
|
||||||
|
onShowAddServerSheet = {
|
||||||
|
showAddServerSheet = true
|
||||||
|
},
|
||||||
|
drawerState = drawerState,
|
||||||
|
useDrawer = true,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
content = {
|
||||||
|
Row(Modifier.fillMaxSize()) {
|
||||||
|
ChannelNavigator(
|
||||||
|
navController = navController,
|
||||||
|
topNav = topNav,
|
||||||
|
useDrawer = true,
|
||||||
|
drawerBackHandler = {
|
||||||
|
drawerBackHandler()
|
||||||
|
},
|
||||||
|
drawerState = drawerState,
|
||||||
|
onShowUserContextSheet = { target, server ->
|
||||||
|
userContextSheetTarget = target
|
||||||
|
userContextSheetServer = server
|
||||||
|
showUserContextSheet = true
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun RowScope.Sidebar(
|
||||||
|
viewModel: ChatRouterViewModel,
|
||||||
|
navController: NavHostController,
|
||||||
|
drawerState: DrawerState? = null,
|
||||||
|
onShowStatusSheet: () -> Unit,
|
||||||
|
onShowServerContextSheet: (String) -> Unit,
|
||||||
|
onShowAddServerSheet: () -> Unit,
|
||||||
|
useDrawer: Boolean = false,
|
||||||
|
) {
|
||||||
|
val scope = rememberCoroutineScope()
|
||||||
|
|
||||||
|
Column(Modifier.then(if (useDrawer) Modifier.fillMaxWidth() else Modifier.weight(0.3f))) {
|
||||||
|
Row {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxHeight()
|
||||||
|
.verticalScroll(rememberScrollState()),
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
|
) {
|
||||||
|
UserAvatar(
|
||||||
|
username = RevoltAPI.userCache[RevoltAPI.selfId]?.let {
|
||||||
|
User.resolveDefaultName(
|
||||||
|
it
|
||||||
|
)
|
||||||
|
}
|
||||||
|
?: "",
|
||||||
|
presence = presenceFromStatus(
|
||||||
|
RevoltAPI.userCache[RevoltAPI.selfId]?.status?.presence
|
||||||
|
?: ""
|
||||||
|
),
|
||||||
|
userId = RevoltAPI.selfId ?: "",
|
||||||
|
avatar = RevoltAPI.userCache[RevoltAPI.selfId]?.avatar,
|
||||||
|
size = 48.dp,
|
||||||
|
presenceSize = 16.dp,
|
||||||
|
onClick = {
|
||||||
|
viewModel.navigateToServer("home", navController)
|
||||||
|
},
|
||||||
|
onLongClick = onShowStatusSheet,
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(8.dp)
|
||||||
|
.size(48.dp)
|
||||||
|
)
|
||||||
|
|
||||||
|
DirectMessages.unreadDMs().forEach {
|
||||||
|
when (it.channelType) {
|
||||||
|
ChannelType.Group -> GroupIcon(
|
||||||
|
name = it.name ?: "?",
|
||||||
|
size = 48.dp,
|
||||||
|
onClick = {
|
||||||
|
it.id?.let { id ->
|
||||||
|
viewModel.navigateToServer(
|
||||||
|
"home",
|
||||||
|
navController
|
||||||
|
)
|
||||||
|
viewModel.navigateToChannel(
|
||||||
|
id,
|
||||||
|
navController
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
icon = it.icon,
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(8.dp)
|
||||||
|
.size(48.dp)
|
||||||
|
)
|
||||||
|
|
||||||
|
else -> {
|
||||||
|
val partner =
|
||||||
|
if (it.channelType == ChannelType.DirectMessage) RevoltAPI.userCache[ChannelUtils.resolveDMPartner(
|
||||||
|
it
|
||||||
|
)] else null
|
||||||
|
|
||||||
|
UserAvatar(
|
||||||
|
username = partner?.let { p ->
|
||||||
|
User.resolveDefaultName(
|
||||||
|
p
|
||||||
|
)
|
||||||
|
} ?: it.name ?: "?",
|
||||||
|
presence = presenceFromStatus(
|
||||||
|
partner?.status?.presence ?: ""
|
||||||
|
),
|
||||||
|
userId = partner?.id ?: it.id ?: "",
|
||||||
|
avatar = partner?.avatar ?: it.icon,
|
||||||
|
size = 48.dp,
|
||||||
|
presenceSize = 16.dp,
|
||||||
|
onClick = {
|
||||||
|
it.id?.let { id ->
|
||||||
|
viewModel.navigateToServer(
|
||||||
|
"home",
|
||||||
|
navController
|
||||||
|
)
|
||||||
|
viewModel.navigateToChannel(
|
||||||
|
id,
|
||||||
|
navController
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxHeight()
|
.padding(8.dp)
|
||||||
.verticalScroll(rememberScrollState()),
|
.size(48.dp)
|
||||||
horizontalAlignment = Alignment.CenterHorizontally
|
)
|
||||||
) {
|
|
||||||
UserAvatar(
|
|
||||||
username = RevoltAPI.userCache[RevoltAPI.selfId]?.let {
|
|
||||||
User.resolveDefaultName(
|
|
||||||
it
|
|
||||||
)
|
|
||||||
}
|
|
||||||
?: "",
|
|
||||||
presence = presenceFromStatus(
|
|
||||||
RevoltAPI.userCache[RevoltAPI.selfId]?.status?.presence
|
|
||||||
?: ""
|
|
||||||
),
|
|
||||||
userId = RevoltAPI.selfId ?: "",
|
|
||||||
avatar = RevoltAPI.userCache[RevoltAPI.selfId]?.avatar,
|
|
||||||
size = 48.dp,
|
|
||||||
presenceSize = 16.dp,
|
|
||||||
onClick = {
|
|
||||||
viewModel.navigateToServer("home", navController)
|
|
||||||
},
|
|
||||||
onLongClick = {
|
|
||||||
showStatusSheet = true
|
|
||||||
},
|
|
||||||
modifier = Modifier
|
|
||||||
.padding(8.dp)
|
|
||||||
.size(48.dp)
|
|
||||||
)
|
|
||||||
|
|
||||||
DirectMessages.unreadDMs().forEach {
|
|
||||||
when (it.channelType) {
|
|
||||||
ChannelType.Group -> GroupIcon(
|
|
||||||
name = it.name ?: "?",
|
|
||||||
size = 48.dp,
|
|
||||||
onClick = {
|
|
||||||
it.id?.let { id ->
|
|
||||||
viewModel.navigateToServer(
|
|
||||||
"home",
|
|
||||||
navController
|
|
||||||
)
|
|
||||||
viewModel.navigateToChannel(
|
|
||||||
id,
|
|
||||||
navController
|
|
||||||
)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
icon = it.icon,
|
|
||||||
modifier = Modifier
|
|
||||||
.padding(8.dp)
|
|
||||||
.size(48.dp)
|
|
||||||
)
|
|
||||||
|
|
||||||
else -> {
|
|
||||||
val partner =
|
|
||||||
if (it.channelType == ChannelType.DirectMessage) RevoltAPI.userCache[ChannelUtils.resolveDMPartner(
|
|
||||||
it
|
|
||||||
)] else null
|
|
||||||
|
|
||||||
UserAvatar(
|
|
||||||
username = partner?.let { p ->
|
|
||||||
User.resolveDefaultName(
|
|
||||||
p
|
|
||||||
)
|
|
||||||
} ?: it.name ?: "?",
|
|
||||||
presence = presenceFromStatus(
|
|
||||||
partner?.status?.presence ?: ""
|
|
||||||
),
|
|
||||||
userId = partner?.id ?: it.id ?: "",
|
|
||||||
avatar = partner?.avatar ?: it.icon,
|
|
||||||
size = 48.dp,
|
|
||||||
presenceSize = 16.dp,
|
|
||||||
onClick = {
|
|
||||||
it.id?.let { id ->
|
|
||||||
viewModel.navigateToServer(
|
|
||||||
"home",
|
|
||||||
navController
|
|
||||||
)
|
|
||||||
viewModel.navigateToChannel(
|
|
||||||
id,
|
|
||||||
navController
|
|
||||||
)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
modifier = Modifier
|
|
||||||
.padding(8.dp)
|
|
||||||
.size(48.dp)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ServerDrawerSeparator()
|
|
||||||
|
|
||||||
// This seems to confuse the formatter, here's what it does:
|
|
||||||
// - Take the list of servers and filter them by the ones that are in the ordering.
|
|
||||||
// - Sort the servers that are in the ordering using the ordering.
|
|
||||||
// - Add the servers that aren't in the ordering to the end of the list.
|
|
||||||
// - Sort the servers that aren't in the ordering by their ID (creation order).
|
|
||||||
((RevoltAPI.serverCache.values.filter {
|
|
||||||
SyncedSettings.ordering.servers.contains(
|
|
||||||
it.id
|
|
||||||
)
|
|
||||||
}
|
|
||||||
.sortedBy { SyncedSettings.ordering.servers.indexOf(it.id) }) + (RevoltAPI.serverCache.values.filter {
|
|
||||||
!SyncedSettings.ordering.servers.contains(
|
|
||||||
it.id
|
|
||||||
)
|
|
||||||
}.sortedBy { it.id }
|
|
||||||
))
|
|
||||||
.forEach { server ->
|
|
||||||
if (server.id == null || server.name == null) return@forEach
|
|
||||||
|
|
||||||
DrawerServer(
|
|
||||||
iconId = server.icon?.id,
|
|
||||||
serverName = server.name,
|
|
||||||
hasUnreads = RevoltAPI.unreads.serverHasUnread(
|
|
||||||
server.id
|
|
||||||
),
|
|
||||||
onLongClick = {
|
|
||||||
serverContextSheetTarget = server.id
|
|
||||||
showServerContextSheet = true
|
|
||||||
},
|
|
||||||
) {
|
|
||||||
viewModel.navigateToServer(
|
|
||||||
server.id,
|
|
||||||
navController
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DrawerServerlikeIcon(
|
|
||||||
onClick = {
|
|
||||||
showAddServerSheet = true
|
|
||||||
}
|
|
||||||
) {
|
|
||||||
Icon(
|
|
||||||
Icons.Default.Add,
|
|
||||||
contentDescription = stringResource(id = R.string.server_plus_alt),
|
|
||||||
modifier = Modifier.padding(4.dp)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Crossfade(
|
|
||||||
targetState = viewModel.currentServer,
|
|
||||||
label = "Channel List"
|
|
||||||
) {
|
|
||||||
ChannelList(
|
|
||||||
serverId = it,
|
|
||||||
currentDestination = navController.currentDestination?.route,
|
|
||||||
currentChannel = viewModel.currentChannel,
|
|
||||||
onChannelClick = { channelId ->
|
|
||||||
viewModel.navigateToChannel(channelId, navController)
|
|
||||||
scope.launch { drawerState.close() }
|
|
||||||
},
|
|
||||||
onSpecialClick = { destination ->
|
|
||||||
viewModel.navigateToSpecial(destination, navController)
|
|
||||||
scope.launch { drawerState.close() }
|
|
||||||
},
|
|
||||||
onServerSheetOpenFor = { target ->
|
|
||||||
serverContextSheetTarget = target
|
|
||||||
showServerContextSheet = true
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
content = {
|
|
||||||
Column(Modifier.fillMaxSize()) {
|
|
||||||
NavHost(navController = navController, startDestination = "home") {
|
|
||||||
composable("home") {
|
|
||||||
BackHandler {
|
|
||||||
drawerBackHandler()
|
|
||||||
}
|
|
||||||
HomeScreen(navController = topNav)
|
|
||||||
}
|
|
||||||
|
|
||||||
composable("channel/{channelId}") { backStackEntry ->
|
ServerDrawerSeparator()
|
||||||
BackHandler {
|
|
||||||
drawerBackHandler()
|
|
||||||
}
|
|
||||||
|
|
||||||
val channelId = backStackEntry.arguments?.getString("channelId")
|
// This seems to confuse the formatter, here's what it does:
|
||||||
if (channelId != null) {
|
// - Take the list of servers and filter them by the ones that are in the ordering.
|
||||||
ChannelScreen(
|
// - Sort the servers that are in the ordering using the ordering.
|
||||||
navController = navController,
|
// - Add the servers that aren't in the ordering to the end of the list.
|
||||||
channelId = channelId,
|
// - Sort the servers that aren't in the ordering by their ID (creation order).
|
||||||
onToggleDrawer = {
|
((RevoltAPI.serverCache.values.filter {
|
||||||
scope.launch {
|
SyncedSettings.ordering.servers.contains(
|
||||||
if (drawerState.isOpen) drawerState.close()
|
it.id
|
||||||
else drawerState.open()
|
)
|
||||||
}
|
}
|
||||||
},
|
.sortedBy { SyncedSettings.ordering.servers.indexOf(it.id) }) + (RevoltAPI.serverCache.values.filter {
|
||||||
onUserSheetOpenFor = { target, server ->
|
!SyncedSettings.ordering.servers.contains(
|
||||||
userContextSheetTarget = target
|
it.id
|
||||||
userContextSheetServer = server
|
)
|
||||||
|
}.sortedBy { it.id }
|
||||||
|
))
|
||||||
|
.forEach { server ->
|
||||||
|
if (server.id == null || server.name == null) return@forEach
|
||||||
|
|
||||||
showUserContextSheet = true
|
DrawerServer(
|
||||||
},
|
iconId = server.icon?.id,
|
||||||
)
|
serverName = server.name,
|
||||||
}
|
hasUnreads = RevoltAPI.unreads.serverHasUnread(
|
||||||
}
|
server.id
|
||||||
|
),
|
||||||
composable("no_current_channel") {
|
onLongClick = {
|
||||||
BackHandler {
|
/*serverContextSheetTarget = server.id
|
||||||
drawerBackHandler()
|
showServerContextSheet = true*/
|
||||||
}
|
onShowServerContextSheet(server.id)
|
||||||
|
},
|
||||||
NoCurrentChannelScreen()
|
) {
|
||||||
}
|
viewModel.navigateToServer(
|
||||||
|
server.id,
|
||||||
dialog("report/message/{messageId}") { backStackEntry ->
|
navController
|
||||||
val messageId = backStackEntry.arguments?.getString("messageId")
|
)
|
||||||
if (messageId != null) {
|
|
||||||
ReportMessageDialog(
|
|
||||||
navController = navController,
|
|
||||||
messageId = messageId
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DrawerServerlikeIcon(
|
||||||
|
onClick = onShowAddServerSheet,
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
Icons.Default.Add,
|
||||||
|
contentDescription = stringResource(id = R.string.server_plus_alt),
|
||||||
|
modifier = Modifier.padding(4.dp)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
|
|
||||||
|
Crossfade(
|
||||||
|
targetState = viewModel.currentServer,
|
||||||
|
label = "Channel List"
|
||||||
|
) {
|
||||||
|
ChannelList(
|
||||||
|
serverId = it,
|
||||||
|
currentDestination = navController.currentDestination?.route,
|
||||||
|
currentChannel = viewModel.currentChannel,
|
||||||
|
onChannelClick = { channelId ->
|
||||||
|
viewModel.navigateToChannel(channelId, navController)
|
||||||
|
scope.launch { drawerState?.close() }
|
||||||
|
},
|
||||||
|
onSpecialClick = { destination ->
|
||||||
|
viewModel.navigateToSpecial(destination, navController)
|
||||||
|
scope.launch { drawerState?.close() }
|
||||||
|
},
|
||||||
|
onServerSheetOpenFor = { target ->
|
||||||
|
onShowServerContextSheet(target)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun RowScope.ChannelNavigator(
|
||||||
|
navController: NavHostController,
|
||||||
|
topNav: NavController,
|
||||||
|
useDrawer: Boolean,
|
||||||
|
drawerBackHandler: () -> Unit,
|
||||||
|
drawerState: DrawerState? = null,
|
||||||
|
onShowUserContextSheet: (String, String?) -> Unit,
|
||||||
|
) {
|
||||||
|
val scope = rememberCoroutineScope()
|
||||||
|
|
||||||
|
Column(Modifier.then(if (useDrawer) Modifier.fillMaxSize() else Modifier.weight(0.7f))) {
|
||||||
|
NavHost(navController = navController, startDestination = "home") {
|
||||||
|
composable("home") {
|
||||||
|
BackHandler(enabled = useDrawer) {
|
||||||
|
drawerBackHandler()
|
||||||
|
}
|
||||||
|
HomeScreen(navController = topNav)
|
||||||
|
}
|
||||||
|
|
||||||
|
composable("channel/{channelId}") { backStackEntry ->
|
||||||
|
BackHandler(enabled = useDrawer) {
|
||||||
|
drawerBackHandler()
|
||||||
|
}
|
||||||
|
|
||||||
|
val channelId = backStackEntry.arguments?.getString("channelId")
|
||||||
|
if (channelId != null) {
|
||||||
|
ChannelScreen(
|
||||||
|
navController = navController,
|
||||||
|
channelId = channelId,
|
||||||
|
onToggleDrawer = {
|
||||||
|
scope.launch {
|
||||||
|
if (drawerState?.isOpen == true) drawerState.close()
|
||||||
|
else drawerState?.open()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onUserSheetOpenFor = { target, server ->
|
||||||
|
onShowUserContextSheet(target, server)
|
||||||
|
},
|
||||||
|
useDrawer = useDrawer
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
composable("no_current_channel") {
|
||||||
|
BackHandler(enabled = useDrawer) {
|
||||||
|
drawerBackHandler()
|
||||||
|
}
|
||||||
|
|
||||||
|
NoCurrentChannelScreen()
|
||||||
|
}
|
||||||
|
|
||||||
|
dialog("report/message/{messageId}") { backStackEntry ->
|
||||||
|
val messageId = backStackEntry.arguments?.getString("messageId")
|
||||||
|
if (messageId != null) {
|
||||||
|
ReportMessageDialog(
|
||||||
|
navController = navController,
|
||||||
|
messageId = messageId
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -101,6 +101,7 @@ fun ChannelScreen(
|
||||||
channelId: String,
|
channelId: String,
|
||||||
onToggleDrawer: () -> Unit,
|
onToggleDrawer: () -> Unit,
|
||||||
onUserSheetOpenFor: (String, String?) -> Unit,
|
onUserSheetOpenFor: (String, String?) -> Unit,
|
||||||
|
useDrawer: Boolean,
|
||||||
viewModel: ChannelScreenViewModel = viewModel()
|
viewModel: ChannelScreenViewModel = viewModel()
|
||||||
) {
|
) {
|
||||||
val channel = viewModel.activeChannel
|
val channel = viewModel.activeChannel
|
||||||
|
|
@ -238,7 +239,8 @@ fun ChannelScreen(
|
||||||
onChannelClick = {
|
onChannelClick = {
|
||||||
channelInfoSheetShown = true
|
channelInfoSheetShown = true
|
||||||
},
|
},
|
||||||
onToggleDrawer = onToggleDrawer
|
onToggleDrawer = onToggleDrawer,
|
||||||
|
useDrawer = useDrawer,
|
||||||
)
|
)
|
||||||
|
|
||||||
val isScrolledToBottom = remember(lazyListState) {
|
val isScrolledToBottom = remember(lazyListState) {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue