mirror of
https://github.com/JustinZeus/Wholphin.git
synced 2026-07-08 23:51:21 +02:00
Basic navigation
This commit is contained in:
parent
51ad92f539
commit
80070f4174
18 changed files with 496 additions and 22 deletions
|
|
@ -3,22 +3,31 @@ package com.github.damontecres.dolphin
|
|||
import android.os.Bundle
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
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
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.RectangleShape
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.datastore.core.DataStore
|
||||
import androidx.tv.material3.ExperimentalTvMaterial3Api
|
||||
import androidx.tv.material3.MaterialTheme
|
||||
import androidx.tv.material3.Surface
|
||||
import com.github.damontecres.dolphin.data.ServerRepository
|
||||
import com.github.damontecres.dolphin.preferences.UserPreferences
|
||||
import com.github.damontecres.dolphin.ui.ServerLoginPage
|
||||
import com.github.damontecres.dolphin.ui.main.MainPage
|
||||
import com.github.damontecres.dolphin.ui.nav.ApplicationContent
|
||||
import com.github.damontecres.dolphin.ui.theme.DolphinTheme
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import kotlinx.coroutines.launch
|
||||
import okhttp3.OkHttpClient
|
||||
import javax.inject.Inject
|
||||
|
||||
|
|
@ -45,26 +54,37 @@ class MainActivity : AppCompatActivity() {
|
|||
) {
|
||||
CoilConfig(serverRepository, okHttpClient, false)
|
||||
|
||||
var isRestoringSession by remember { mutableStateOf(true) }
|
||||
val preferences by userPreferencesDataStore.data.collectAsState(null)
|
||||
preferences?.let { preferences ->
|
||||
if (preferences.currentServerId.isNotBlank() && preferences.currentUserId.isNotBlank()) {
|
||||
scope.launch {
|
||||
LaunchedEffect(Unit) {
|
||||
if (preferences.currentServerId.isNotBlank() && preferences.currentUserId.isNotBlank()) {
|
||||
serverRepository.restoreSession(
|
||||
preferences.currentServerId,
|
||||
preferences.currentUserId,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
ServerLoginPage(modifier = Modifier.fillMaxSize())
|
||||
isRestoringSession = false
|
||||
}
|
||||
val server = serverRepository.currentServer
|
||||
val user = serverRepository.currentUser
|
||||
if (server != null && user != null) {
|
||||
// TODO navigation
|
||||
MainPage(
|
||||
ApplicationContent(
|
||||
preferences = preferences,
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
)
|
||||
} else if (isRestoringSession) {
|
||||
Box(
|
||||
modifier = Modifier.size(200.dp),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
CircularProgressIndicator(
|
||||
color = MaterialTheme.colorScheme.border,
|
||||
modifier = Modifier.align(Alignment.Center),
|
||||
)
|
||||
}
|
||||
} else {
|
||||
ServerLoginPage(modifier = Modifier.fillMaxSize())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,11 @@ package com.github.damontecres.dolphin.data
|
|||
import androidx.room.Database
|
||||
import androidx.room.RoomDatabase
|
||||
|
||||
@Database(entities = [JellyfinServer::class, JellyfinUser::class], version = 1, exportSchema = false)
|
||||
@Database(
|
||||
entities = [JellyfinServer::class, JellyfinUser::class],
|
||||
version = 2,
|
||||
exportSchema = false,
|
||||
)
|
||||
abstract class DolphinDatabase : RoomDatabase() {
|
||||
abstract fun serverDao(): JellyfinServerDao
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ import androidx.room.Transaction
|
|||
@Entity(tableName = "servers")
|
||||
data class JellyfinServer(
|
||||
@PrimaryKey val id: String,
|
||||
val name: String,
|
||||
val name: String?,
|
||||
val url: String,
|
||||
)
|
||||
|
||||
|
|
@ -37,7 +37,7 @@ data class JellyfinUser(
|
|||
val name: String?,
|
||||
@ColumnInfo(index = true)
|
||||
val serverId: String,
|
||||
val accessToken: String,
|
||||
val accessToken: String?,
|
||||
)
|
||||
|
||||
data class JellyfinServerUsers(
|
||||
|
|
|
|||
|
|
@ -10,7 +10,9 @@ import org.jellyfin.sdk.api.client.exception.InvalidStatusException
|
|||
import org.jellyfin.sdk.api.client.extensions.userApi
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class ServerRepository
|
||||
@Inject
|
||||
constructor(
|
||||
|
|
@ -55,11 +57,22 @@ class ServerRepository
|
|||
Timber.v("Changing user to ${user.name} on ${server.url}")
|
||||
apiClient.update(baseUrl = server.url, accessToken = user.accessToken)
|
||||
try {
|
||||
apiClient.userApi
|
||||
.getCurrentUser()
|
||||
.content.name
|
||||
_currentServer = server
|
||||
_currentUser = user
|
||||
val userDto =
|
||||
apiClient.userApi
|
||||
.getCurrentUser()
|
||||
.content
|
||||
val updatedServer = server.copy(name = userDto.serverName)
|
||||
val updatedUser =
|
||||
user.copy(
|
||||
id = userDto.id.toString(),
|
||||
name = userDto.name,
|
||||
)
|
||||
withContext(Dispatchers.IO) {
|
||||
serverDao.addServer(updatedServer)
|
||||
serverDao.addUser(updatedUser)
|
||||
}
|
||||
_currentServer = updatedServer
|
||||
_currentUser = updatedUser
|
||||
} catch (e: InvalidStatusException) {
|
||||
// TODO
|
||||
Timber.e(e)
|
||||
|
|
|
|||
|
|
@ -16,6 +16,13 @@ fun convertModel(
|
|||
api: ApiClient,
|
||||
): DolphinModel =
|
||||
when (dto.type) {
|
||||
BaseItemKind.COLLECTION_FOLDER -> Library.fromDto(dto, api)
|
||||
|
||||
// TODO
|
||||
BaseItemKind.VIDEO -> Video.fromDto(dto, api)
|
||||
BaseItemKind.SERIES -> Video.fromDto(dto, api)
|
||||
BaseItemKind.MOVIE -> Video.fromDto(dto, api)
|
||||
BaseItemKind.SEASON -> Video.fromDto(dto, api)
|
||||
BaseItemKind.EPISODE -> Video.fromDto(dto, api)
|
||||
else -> throw IllegalArgumentException("Unsupported item type: ${dto.type}")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,28 @@
|
|||
package com.github.damontecres.dolphin.data.model
|
||||
|
||||
import org.jellyfin.sdk.api.client.ApiClient
|
||||
import org.jellyfin.sdk.api.client.extensions.imageApi
|
||||
import org.jellyfin.sdk.model.api.BaseItemDto
|
||||
import org.jellyfin.sdk.model.api.CollectionType
|
||||
import org.jellyfin.sdk.model.api.ImageType
|
||||
import java.util.UUID
|
||||
|
||||
data class Library(
|
||||
override val id: UUID,
|
||||
override val name: String?,
|
||||
override val imageUrl: String?,
|
||||
val type: CollectionType,
|
||||
) : DolphinModel {
|
||||
companion object {
|
||||
fun fromDto(
|
||||
dto: BaseItemDto,
|
||||
api: ApiClient,
|
||||
): Library =
|
||||
Library(
|
||||
id = dto.id,
|
||||
name = dto.name,
|
||||
imageUrl = api.imageApi.getItemImageUrl(dto.id, ImageType.PRIMARY),
|
||||
type = dto.collectionType ?: CollectionType.UNKNOWN,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -30,7 +30,8 @@ object DatabaseModule {
|
|||
context,
|
||||
DolphinDatabase::class.java,
|
||||
"dolphin",
|
||||
).build()
|
||||
).fallbackToDestructiveMigration(false)
|
||||
.build()
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
|
|
|
|||
|
|
@ -57,17 +57,18 @@ class ServerManagementViewModel
|
|||
|
||||
val accessToken = authenticationResult.accessToken
|
||||
if (accessToken != null) {
|
||||
val authedUser = authenticationResult.user
|
||||
val server =
|
||||
authenticationResult.serverId?.let {
|
||||
JellyfinServer(
|
||||
id = it,
|
||||
name = serverUrl,
|
||||
name = authedUser?.serverName,
|
||||
url = serverUrl,
|
||||
)
|
||||
}
|
||||
if (server != null) {
|
||||
val user =
|
||||
authenticationResult.user?.let {
|
||||
authedUser?.let {
|
||||
JellyfinUser(
|
||||
id = it.id.toString(),
|
||||
name = it.name,
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package com.github.damontecres.dolphin.ui.cards
|
|||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import com.github.damontecres.dolphin.data.model.DolphinModel
|
||||
import com.github.damontecres.dolphin.data.model.Library
|
||||
import com.github.damontecres.dolphin.data.model.Video
|
||||
|
||||
@Composable
|
||||
|
|
@ -18,6 +19,8 @@ fun DolphinCard(
|
|||
onClick = onClick,
|
||||
modifier = modifier,
|
||||
)
|
||||
|
||||
is Library -> TODO()
|
||||
null -> TODO()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,16 +14,18 @@ import androidx.compose.runtime.livedata.observeAsState
|
|||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import androidx.tv.material3.Text
|
||||
import com.github.damontecres.dolphin.data.model.DolphinModel
|
||||
import com.github.damontecres.dolphin.data.model.convertModel
|
||||
import com.github.damontecres.dolphin.isNotNullOrBlank
|
||||
import com.github.damontecres.dolphin.preferences.UserPreferences
|
||||
import com.github.damontecres.dolphin.ui.cards.DolphinCard
|
||||
import com.github.damontecres.dolphin.ui.nav.Destination
|
||||
import com.github.damontecres.dolphin.ui.nav.NavigationManager
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
|
|
@ -118,8 +120,9 @@ class MainViewModel
|
|||
@Composable
|
||||
fun MainPage(
|
||||
preferences: UserPreferences,
|
||||
navigationManager: NavigationManager,
|
||||
modifier: Modifier,
|
||||
viewModel: MainViewModel = viewModel(),
|
||||
viewModel: MainViewModel = hiltViewModel(),
|
||||
) {
|
||||
val homeRows by viewModel.homeRows.observeAsState(listOf())
|
||||
Column(modifier = modifier) {
|
||||
|
|
@ -129,6 +132,7 @@ fun MainPage(
|
|||
item {
|
||||
HomePageRow(
|
||||
row = row,
|
||||
onClickItem = { navigationManager.navigateTo(Destination.MediaItem(it.id)) },
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
)
|
||||
}
|
||||
|
|
@ -140,6 +144,7 @@ fun MainPage(
|
|||
@Composable
|
||||
fun HomePageRow(
|
||||
row: HomeRow,
|
||||
onClickItem: (DolphinModel) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Column(
|
||||
|
|
@ -160,7 +165,7 @@ fun HomePageRow(
|
|||
items(row.items) { item ->
|
||||
DolphinCard(
|
||||
item = item,
|
||||
onClick = {},
|
||||
onClick = { onClickItem.invoke(item) },
|
||||
modifier = Modifier,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,67 @@
|
|||
package com.github.damontecres.dolphin.ui.nav
|
||||
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.lifecycle.viewmodel.navigation3.rememberViewModelStoreNavEntryDecorator
|
||||
import androidx.navigation3.runtime.NavEntry
|
||||
import androidx.navigation3.runtime.rememberNavBackStack
|
||||
import androidx.navigation3.runtime.rememberSavedStateNavEntryDecorator
|
||||
import androidx.navigation3.scene.rememberSceneSetupNavEntryDecorator
|
||||
import androidx.navigation3.ui.NavDisplay
|
||||
import com.github.damontecres.dolphin.preferences.UserPreferences
|
||||
|
||||
@Composable
|
||||
fun ApplicationContent(
|
||||
preferences: UserPreferences,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val backStack = rememberNavBackStack(Destination.Main)
|
||||
|
||||
val navigationManager =
|
||||
object : NavigationManager {
|
||||
override fun navigateTo(destination: Destination) {
|
||||
backStack.add(destination)
|
||||
}
|
||||
|
||||
override fun goBack() {
|
||||
backStack.removeLastOrNull()
|
||||
}
|
||||
|
||||
override fun goToHome() {
|
||||
backStack.clear()
|
||||
backStack.add(Destination.Main)
|
||||
}
|
||||
}
|
||||
|
||||
NavDisplay(
|
||||
backStack = backStack,
|
||||
onBack = { backStack.removeLastOrNull() },
|
||||
entryDecorators =
|
||||
listOf(
|
||||
rememberSceneSetupNavEntryDecorator(),
|
||||
rememberSavedStateNavEntryDecorator(),
|
||||
rememberViewModelStoreNavEntryDecorator(),
|
||||
),
|
||||
entryProvider = { key ->
|
||||
NavEntry(key) {
|
||||
key as Destination
|
||||
if (key.fullScreen) {
|
||||
DestinationContent(
|
||||
destination = key,
|
||||
preferences = preferences,
|
||||
navigationManager = navigationManager,
|
||||
modifier = modifier.fillMaxSize(),
|
||||
)
|
||||
} else {
|
||||
NavDrawer(
|
||||
destination = key,
|
||||
preferences = preferences,
|
||||
navigationManager = navigationManager,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
package com.github.damontecres.dolphin.ui.nav
|
||||
|
||||
import androidx.navigation3.runtime.NavKey
|
||||
import com.github.damontecres.dolphin.util.UuidSerializer
|
||||
import kotlinx.serialization.Serializable
|
||||
import java.util.UUID
|
||||
|
||||
@Serializable
|
||||
sealed class Destination(
|
||||
val fullScreen: Boolean = false,
|
||||
) : NavKey {
|
||||
@Serializable
|
||||
data object Setup : Destination(true)
|
||||
|
||||
@Serializable
|
||||
data object Main : Destination()
|
||||
|
||||
@Serializable
|
||||
data object Settings : Destination(true)
|
||||
|
||||
@Serializable
|
||||
data object Search : Destination()
|
||||
|
||||
@Serializable
|
||||
data class MediaItem(
|
||||
@Serializable(with = UuidSerializer::class) val itemId: UUID,
|
||||
) : Destination()
|
||||
|
||||
@Serializable
|
||||
data class Playback(
|
||||
val itemId: String,
|
||||
val positionMs: Long,
|
||||
) : Destination(true)
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
package com.github.damontecres.dolphin.ui.nav
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.tv.material3.Text
|
||||
import com.github.damontecres.dolphin.preferences.UserPreferences
|
||||
import com.github.damontecres.dolphin.ui.main.MainPage
|
||||
|
||||
@Composable
|
||||
fun DestinationContent(
|
||||
destination: Destination,
|
||||
preferences: UserPreferences,
|
||||
navigationManager: NavigationManager,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
when (destination) {
|
||||
Destination.Main -> {
|
||||
MainPage(
|
||||
preferences = preferences,
|
||||
navigationManager = navigationManager,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
is Destination.MediaItem -> {
|
||||
Text("MediaItem: ${destination.itemId}")
|
||||
}
|
||||
is Destination.Playback -> TODO()
|
||||
Destination.Search -> TODO()
|
||||
Destination.Settings -> TODO()
|
||||
Destination.Setup -> TODO()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,219 @@
|
|||
package com.github.damontecres.dolphin.ui.nav
|
||||
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.focusGroup
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.AccountCircle
|
||||
import androidx.compose.material.icons.filled.DateRange
|
||||
import androidx.compose.material.icons.filled.Email
|
||||
import androidx.compose.material.icons.filled.Home
|
||||
import androidx.compose.material.icons.filled.Info
|
||||
import androidx.compose.material.icons.filled.Settings
|
||||
import androidx.compose.material.icons.filled.ShoppingCart
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.livedata.observeAsState
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.focus.focusRequester
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalView
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.findViewTreeViewModelStoreOwner
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import androidx.tv.material3.DrawerValue
|
||||
import androidx.tv.material3.Icon
|
||||
import androidx.tv.material3.MaterialTheme
|
||||
import androidx.tv.material3.NavigationDrawer
|
||||
import androidx.tv.material3.NavigationDrawerItem
|
||||
import androidx.tv.material3.NavigationDrawerScope
|
||||
import androidx.tv.material3.ProvideTextStyle
|
||||
import androidx.tv.material3.Text
|
||||
import androidx.tv.material3.rememberDrawerState
|
||||
import com.github.damontecres.dolphin.data.ServerRepository
|
||||
import com.github.damontecres.dolphin.data.model.Library
|
||||
import com.github.damontecres.dolphin.preferences.UserPreferences
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.launch
|
||||
import org.jellyfin.sdk.api.client.ApiClient
|
||||
import org.jellyfin.sdk.api.client.extensions.userViewsApi
|
||||
import org.jellyfin.sdk.model.api.CollectionType
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
class NavDrawerViewModel
|
||||
@Inject
|
||||
constructor(
|
||||
val serverRepository: ServerRepository,
|
||||
val api: ApiClient,
|
||||
) : ViewModel() {
|
||||
val libraries = MutableLiveData<List<Library>>(listOf())
|
||||
|
||||
init {
|
||||
viewModelScope.launch {
|
||||
val userViews =
|
||||
api.userViewsApi
|
||||
.getUserViews()
|
||||
.content.items
|
||||
Timber.v("userViews: ${userViews.map { it.type }}")
|
||||
libraries.value = userViews.map { Library.fromDto(it, api) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun NavDrawer(
|
||||
destination: Destination,
|
||||
preferences: UserPreferences,
|
||||
navigationManager: NavigationManager,
|
||||
modifier: Modifier = Modifier,
|
||||
viewModel: NavDrawerViewModel = hiltViewModel(LocalView.current.findViewTreeViewModelStoreOwner()!!),
|
||||
) {
|
||||
val drawerState = rememberDrawerState(DrawerValue.Closed)
|
||||
val listState = rememberLazyListState()
|
||||
val scope = rememberCoroutineScope()
|
||||
val context = LocalContext.current
|
||||
val drawerFocusRequester = remember { FocusRequester() }
|
||||
BackHandler(enabled = (drawerState.currentValue == DrawerValue.Closed && destination == Destination.Main)) {
|
||||
drawerState.setValue(DrawerValue.Open)
|
||||
drawerFocusRequester.requestFocus()
|
||||
}
|
||||
|
||||
val user = viewModel.serverRepository.currentUser
|
||||
val libraries by viewModel.libraries.observeAsState(listOf())
|
||||
|
||||
NavigationDrawer(
|
||||
modifier =
|
||||
modifier
|
||||
.focusRequester(drawerFocusRequester),
|
||||
drawerState = drawerState,
|
||||
drawerContent = {
|
||||
ProvideTextStyle(MaterialTheme.typography.labelMedium) {
|
||||
LazyColumn(
|
||||
state = listState,
|
||||
contentPadding = PaddingValues(0.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.SpaceBetween,
|
||||
modifier =
|
||||
Modifier
|
||||
.fillMaxHeight()
|
||||
.background(MaterialTheme.colorScheme.background)
|
||||
.focusGroup(),
|
||||
) {
|
||||
item {
|
||||
IconNavItem(
|
||||
text = user?.name ?: "",
|
||||
icon = Icons.Default.AccountCircle,
|
||||
onClick = {},
|
||||
)
|
||||
}
|
||||
item {
|
||||
IconNavItem(
|
||||
text = "Home",
|
||||
icon = Icons.Default.Home,
|
||||
onClick = {},
|
||||
)
|
||||
}
|
||||
items(libraries) {
|
||||
LibraryNavItem(
|
||||
library = it,
|
||||
onClick = { navigationManager.navigateTo(Destination.MediaItem(it.id)) },
|
||||
)
|
||||
}
|
||||
item {
|
||||
IconNavItem(
|
||||
text = "Settings",
|
||||
icon = Icons.Default.Settings,
|
||||
onClick = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
) {
|
||||
// Drawer content
|
||||
DestinationContent(
|
||||
destination = destination,
|
||||
preferences = preferences,
|
||||
navigationManager = navigationManager,
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun NavigationDrawerScope.IconNavItem(
|
||||
text: String,
|
||||
icon: ImageVector,
|
||||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
NavigationDrawerItem(
|
||||
modifier = modifier,
|
||||
selected = false,
|
||||
onClick = onClick,
|
||||
leadingContent = {
|
||||
Icon(
|
||||
icon,
|
||||
contentDescription = null,
|
||||
)
|
||||
},
|
||||
interactionSource = null,
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier,
|
||||
text = text,
|
||||
maxLines = 1,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun NavigationDrawerScope.LibraryNavItem(
|
||||
library: Library,
|
||||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
// TODO
|
||||
val icon =
|
||||
when (library.type) {
|
||||
CollectionType.MOVIES -> Icons.Default.Email
|
||||
CollectionType.TVSHOWS -> Icons.Default.DateRange
|
||||
CollectionType.HOMEVIDEOS -> Icons.Default.ShoppingCart
|
||||
else -> Icons.Default.Info
|
||||
}
|
||||
NavigationDrawerItem(
|
||||
modifier = modifier,
|
||||
selected = false,
|
||||
onClick = onClick,
|
||||
leadingContent = {
|
||||
Icon(
|
||||
icon,
|
||||
contentDescription = null,
|
||||
)
|
||||
},
|
||||
interactionSource = null,
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier,
|
||||
text = library.name ?: library.id.toString(),
|
||||
maxLines = 1,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
package com.github.damontecres.dolphin.ui.nav
|
||||
|
||||
interface NavigationManager {
|
||||
fun navigateTo(destination: Destination)
|
||||
|
||||
fun goBack()
|
||||
|
||||
fun goToHome()
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
package com.github.damontecres.dolphin.util
|
||||
|
||||
import kotlinx.serialization.KSerializer
|
||||
import kotlinx.serialization.descriptors.PrimitiveKind
|
||||
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
|
||||
import kotlinx.serialization.descriptors.SerialDescriptor
|
||||
import kotlinx.serialization.encoding.Decoder
|
||||
import kotlinx.serialization.encoding.Encoder
|
||||
import java.util.UUID
|
||||
|
||||
class UuidSerializer : KSerializer<UUID> {
|
||||
override val descriptor: SerialDescriptor
|
||||
get() = PrimitiveSerialDescriptor("UUID", PrimitiveKind.LONG)
|
||||
|
||||
override fun deserialize(decoder: Decoder): UUID = UUID(decoder.decodeLong(), decoder.decodeLong())
|
||||
|
||||
override fun serialize(
|
||||
encoder: Encoder,
|
||||
value: UUID,
|
||||
) {
|
||||
encoder.encodeLong(value.mostSignificantBits)
|
||||
encoder.encodeLong(value.leastSignificantBits)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue