1 Commits

2 changed files with 28 additions and 46 deletions

View File

@@ -1,6 +1,5 @@
package com.acitelight.aether.view package com.acitelight.aether.view
import android.R.id.tabs
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
@@ -20,12 +19,7 @@ import androidx.compose.foundation.lazy.grid.items
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Card import androidx.compose.material3.Card
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.PrimaryScrollableTabRow
import androidx.compose.material3.Tab import androidx.compose.material3.Tab
import androidx.compose.material3.TabRow
import androidx.compose.material3.TabRowDefaults
import androidx.compose.material3.TabRowDefaults.tabIndicatorOffset
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
@@ -39,19 +33,16 @@ import androidx.compose.ui.unit.sp
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import coil3.compose.AsyncImage import coil3.compose.AsyncImage
import com.acitelight.aether.model.Video import com.acitelight.aether.model.Video
import com.acitelight.aether.service.MediaManager
import com.acitelight.aether.viewModel.VideoScreenViewModel import com.acitelight.aether.viewModel.VideoScreenViewModel
import androidx.compose.material3.PrimaryTabRow
import androidx.compose.material3.ScrollableTabRow import androidx.compose.material3.ScrollableTabRow
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.style.TextOverflow
import androidx.navigation.NavHostController import androidx.navigation.NavHostController
import coil3.request.ImageRequest import coil3.request.ImageRequest
import com.acitelight.aether.Global import com.acitelight.aether.Global
import kotlinx.coroutines.flow.first
import java.nio.charset.Charset import java.nio.charset.Charset
fun String.toHex(): String { fun String.toHex(): String {
@@ -72,6 +63,7 @@ fun String.hexToString(charset: Charset = Charsets.UTF_8): String {
@Composable @Composable
fun VideoScreen(videoScreenViewModel: VideoScreenViewModel = viewModel(), navController: NavHostController) fun VideoScreen(videoScreenViewModel: VideoScreenViewModel = viewModel(), navController: NavHostController)
{ {
val tabIndex by videoScreenViewModel.tabIndex;
videoScreenViewModel.SetupClient() videoScreenViewModel.SetupClient()
Column( Column(
@@ -86,8 +78,11 @@ fun VideoScreen(videoScreenViewModel: VideoScreenViewModel = viewModel(), navCon
horizontalArrangement = Arrangement.spacedBy(8.dp) horizontalArrangement = Arrangement.spacedBy(8.dp)
) )
{ {
items(videoScreenViewModel.videos) { video -> if(videoScreenViewModel.classes.isNotEmpty())
VideoCard(video, navController, videoScreenViewModel) {
items(videoScreenViewModel.classesMap[videoScreenViewModel.classes[tabIndex]] ?: mutableStateListOf()) { video ->
VideoCard(video, navController, videoScreenViewModel)
}
} }
} }
} }
@@ -98,12 +93,11 @@ fun VideoScreen(videoScreenViewModel: VideoScreenViewModel = viewModel(), navCon
fun TopRow(videoScreenViewModel: VideoScreenViewModel) fun TopRow(videoScreenViewModel: VideoScreenViewModel)
{ {
val tabIndex by videoScreenViewModel.tabIndex; val tabIndex by videoScreenViewModel.tabIndex;
val klasses by videoScreenViewModel.klasses.collectAsState();
if(klasses.isEmpty()) return if(videoScreenViewModel.classes.isEmpty()) return
ScrollableTabRow (selectedTabIndex = tabIndex) { ScrollableTabRow (selectedTabIndex = tabIndex) {
klasses.forEachIndexed { index, title -> videoScreenViewModel.classes.forEachIndexed { index, title ->
Tab( Tab(
selected = tabIndex == index, selected = tabIndex == index,
onClick = { videoScreenViewModel.setTabIndex(index) }, onClick = { videoScreenViewModel.setTabIndex(index) },
@@ -115,13 +109,14 @@ fun TopRow(videoScreenViewModel: VideoScreenViewModel)
@Composable @Composable
fun VideoCard(video: Video, navController: NavHostController, videoScreenViewModel: VideoScreenViewModel) { fun VideoCard(video: Video, navController: NavHostController, videoScreenViewModel: VideoScreenViewModel) {
val tabIndex by videoScreenViewModel.tabIndex;
Card( Card(
shape = RoundedCornerShape(6.dp), shape = RoundedCornerShape(6.dp),
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.wrapContentHeight(), .wrapContentHeight(),
onClick = { onClick = {
Global.sameClassVideos = videoScreenViewModel.videos Global.sameClassVideos = videoScreenViewModel.classesMap[videoScreenViewModel.classes[tabIndex]] ?: mutableStateListOf()
val route = "video_player_route/${ "${video.klass}/${video.id}".toHex() }" val route = "video_player_route/${ "${video.klass}/${video.id}".toHex() }"
navController.navigate(route) navController.navigate(route)
} }

View File

@@ -3,51 +3,35 @@ package com.acitelight.aether.viewModel
import android.app.Application import android.app.Application
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.State import androidx.compose.runtime.State
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateListOf import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.mutableStateMapOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.snapshots.SnapshotStateList
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.datastore.preferences.core.stringPreferencesKey import androidx.datastore.preferences.core.stringPreferencesKey
import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import coil3.ImageLoader import coil3.ImageLoader
import coil3.network.okhttp.OkHttpNetworkFetcherFactory import coil3.network.okhttp.OkHttpNetworkFetcherFactory
import com.acitelight.aether.Global
import com.acitelight.aether.dataStore import com.acitelight.aether.dataStore
import com.acitelight.aether.model.Video import com.acitelight.aether.model.Video
import com.acitelight.aether.service.ApiClient
import com.acitelight.aether.service.ApiClient.createOkHttp import com.acitelight.aether.service.ApiClient.createOkHttp
import com.acitelight.aether.service.AuthManager
import com.acitelight.aether.service.MediaManager import com.acitelight.aether.service.MediaManager
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
class VideoScreenViewModel(application: Application) : AndroidViewModel(application) class VideoScreenViewModel(application: Application) : AndroidViewModel(application)
{ {
private val dataStore = application.dataStore
private val USER_NAME_KEY = stringPreferencesKey("user_name")
private val PRIVATE_KEY = stringPreferencesKey("private_key")
val userNameFlow: Flow<String> = dataStore.data.map { preferences ->
preferences[USER_NAME_KEY] ?: ""
}
val privateKeyFlow: Flow<String> = dataStore.data.map { preferences ->
preferences[PRIVATE_KEY] ?: ""
}
private val _tabIndex = mutableIntStateOf(0) private val _tabIndex = mutableIntStateOf(0)
val tabIndex: State<Int> = _tabIndex val tabIndex: State<Int> = _tabIndex
// val videos = mutableStateListOf<Video>()
// private val _klasses = MutableStateFlow<List<String>>(emptyList())
var classes = mutableStateListOf<String>()
val classesMap = mutableStateMapOf<String, SnapshotStateList<Video>>()
val videos = mutableStateListOf<Video>()
private val _klasses = MutableStateFlow<List<String>>(emptyList())
val klasses: StateFlow<List<String>> = _klasses;
var imageLoader: ImageLoader? = null; var imageLoader: ImageLoader? = null;
@Composable @Composable
@@ -62,10 +46,14 @@ class VideoScreenViewModel(application: Application) : AndroidViewModel(applicat
} }
suspend fun init() { suspend fun init() {
_klasses.value = MediaManager.listVideoKlasses() classes.addAll(MediaManager.listVideoKlasses())
for(it in classes)
{
classesMap[it] = mutableStateListOf<Video>()
}
MediaManager.listVideos(_klasses.value.first()){ MediaManager.listVideos(classes[0]){
v -> if(0 == tabIndex.value && !videos.contains(v)) videos.add(videos.size, v) v -> classesMap[classes[0]]?.add(v)
} }
} }
@@ -75,10 +63,9 @@ class VideoScreenViewModel(application: Application) : AndroidViewModel(applicat
{ {
_tabIndex.intValue = index; _tabIndex.intValue = index;
videos.clear() MediaManager.listVideos(classes[index])
MediaManager.listVideos(_klasses.value[index])
{ {
v -> if(index == tabIndex.value) videos.add(videos.size, v) v -> classesMap[classes[index]]?.add(v)
} }
} }
} }