From 9dc83d85478f00abcb4bcc737f9be226265b3857 Mon Sep 17 00:00:00 2001 From: reocat Date: Sun, 12 Jan 2025 17:08:14 +0300 Subject: [PATCH] test2 --- .../outertune/ui/screens/HistoryScreen.kt | 487 +++++++++--------- app/src/main/res/values/strings.xml | 1 + 2 files changed, 248 insertions(+), 240 deletions(-) diff --git a/app/src/main/java/com/dd3boh/outertune/ui/screens/HistoryScreen.kt b/app/src/main/java/com/dd3boh/outertune/ui/screens/HistoryScreen.kt index e54f65201..351b1b3e2 100644 --- a/app/src/main/java/com/dd3boh/outertune/ui/screens/HistoryScreen.kt +++ b/app/src/main/java/com/dd3boh/outertune/ui/screens/HistoryScreen.kt @@ -66,7 +66,6 @@ import com.dd3boh.outertune.LocalDownloadUtil import com.dd3boh.outertune.LocalIsNetworkConnected import com.dd3boh.outertune.LocalPlayerAwareWindowInsets import com.dd3boh.outertune.LocalPlayerConnection -import com.dd3boh.outertune.LocalIsNetworkConnected import com.dd3boh.outertune.R import com.dd3boh.outertune.constants.HistorySource import com.dd3boh.outertune.constants.InnerTubeCookieKey @@ -146,36 +145,30 @@ fun HistoryScreen( BackHandler(onBack = onExitSelectionMode) } - // no multiselect for remote history (yet) - val historyPage by viewModel.historyPage - - val innerTubeCookie by rememberPreference(InnerTubeCookieKey, "") - val isLoggedIn = remember(innerTubeCookie) { - "SAPISID" in parseCookieString(innerTubeCookie) - } - - fun dateAgoToString(dateAgo: DateAgo): String { - return when (dateAgo) { - DateAgo.Today -> context.getString(R.string.today) - DateAgo.Yesterday -> context.getString(R.string.yesterday) - DateAgo.ThisWeek -> context.getString(R.string.this_week) - DateAgo.LastWeek -> context.getString(R.string.last_week) - is DateAgo.Other -> dateAgo.date.format(DateTimeFormatter.ofPattern("yyyy/MM")) - } + // Debugging: Log eventsMap + val eventsMap by viewModel.events.collectAsState() + LaunchedEffect(eventsMap) { + println("Events Map: $eventsMap") } - val eventsMap by viewModel.events.collectAsState() val filteredEventsMap = remember(eventsMap, query) { - if (query.text.isEmpty()) eventsMap - else eventsMap - .mapValues { (_, songs) -> + if (query.text.isEmpty()) { + eventsMap + } else { + eventsMap.mapValues { (_, songs) -> songs.filter { song -> song.song.title.contains(query.text, ignoreCase = true) || song.song.artists.fastAny { it.name.contains(query.text, ignoreCase = true) } } - } - .filterValues { it.isNotEmpty() } + }.filterValues { it.isNotEmpty() } + } + } + + // Debugging: Log filteredEventsMap + LaunchedEffect(filteredEventsMap) { + println("Filtered Events Map: $filteredEventsMap") } + val filteredEventIndex: Map by remember(filteredEventsMap) { derivedStateOf { filteredEventsMap.flatMap { it.value }.associateBy { it.event.id } @@ -192,256 +185,270 @@ fun HistoryScreen( val lazyListState = rememberLazyListState() Box(Modifier.fillMaxSize()) { - LazyColumn( - state = lazyListState, - contentPadding = LocalPlayerAwareWindowInsets.current - .only(WindowInsetsSides.Horizontal + WindowInsetsSides.Bottom) - .union(WindowInsets.ime) - .asPaddingValues(), - modifier = Modifier.windowInsetsPadding( - LocalPlayerAwareWindowInsets.current - .only(WindowInsetsSides.Top) - ) - ) { - stickyHeader( - key = "searchbar" + if (filteredEventsMap.isNotEmpty()) { + LazyColumn( + state = lazyListState, + contentPadding = LocalPlayerAwareWindowInsets.current + .only(WindowInsetsSides.Horizontal + WindowInsetsSides.Bottom) + .union(WindowInsets.ime) + .asPaddingValues(), + modifier = Modifier.windowInsetsPadding( + LocalPlayerAwareWindowInsets.current + .only(WindowInsetsSides.Top) + ) ) { - if (isSearching) { - Row( - verticalAlignment = Alignment.CenterVertically, - modifier = Modifier.background(MaterialTheme.colorScheme.background) - ) { - IconButton( - onClick = { isSearching = true } + stickyHeader( + key = "searchbar" + ) { + if (isSearching) { + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier.background(MaterialTheme.colorScheme.background) ) { - Icon( - Icons.Rounded.Search, - contentDescription = null + IconButton( + onClick = { isSearching = true } + ) { + Icon( + Icons.Rounded.Search, + contentDescription = null + ) + } + TextField( + value = query, + onValueChange = { query = it }, + placeholder = { + Text( + text = stringResource(R.string.search), + style = MaterialTheme.typography.titleLarge + ) + }, + singleLine = true, + textStyle = MaterialTheme.typography.titleLarge, + keyboardOptions = KeyboardOptions(imeAction = ImeAction.Search), + colors = TextFieldDefaults.colors( + focusedContainerColor = Color.Transparent, + unfocusedContainerColor = Color.Transparent, + focusedIndicatorColor = Color.Transparent, + unfocusedIndicatorColor = Color.Transparent, + disabledIndicatorColor = Color.Transparent, + ), + modifier = Modifier + .fillMaxWidth() + .focusRequester(focusRequester) ) } - TextField( - value = query, - onValueChange = { query = it }, - placeholder = { - Text( - text = stringResource(R.string.search), - style = MaterialTheme.typography.titleLarge - ) - }, - singleLine = true, - textStyle = MaterialTheme.typography.titleLarge, - keyboardOptions = KeyboardOptions(imeAction = ImeAction.Search), - colors = TextFieldDefaults.colors( - focusedContainerColor = Color.Transparent, - unfocusedContainerColor = Color.Transparent, - focusedIndicatorColor = Color.Transparent, - unfocusedIndicatorColor = Color.Transparent, - disabledIndicatorColor = Color.Transparent, - ), - modifier = Modifier - .fillMaxWidth() - .focusRequester(focusRequester) - ) } } - } - item { - ChipsRow( - chips = if (isLoggedIn) listOf( - HistorySource.LOCAL to stringResource(R.string.local_history), - HistorySource.REMOTE to stringResource(R.string.remote_history), - ) else { - listOf(HistorySource.LOCAL to stringResource(R.string.local_history)) - }, - currentValue = historySource, - onValueUpdate = { - viewModel.historySource.value = it - if (it == HistorySource.REMOTE) { - viewModel.fetchRemoteHistory() + item { + ChipsRow( + chips = if (isLoggedIn) listOf( + HistorySource.LOCAL to stringResource(R.string.local_history), + HistorySource.REMOTE to stringResource(R.string.remote_history), + ) else { + listOf(HistorySource.LOCAL to stringResource(R.string.local_history)) + }, + currentValue = historySource, + onValueUpdate = { + viewModel.historySource.value = it + if (it == HistorySource.REMOTE) { + viewModel.fetchRemoteHistory() + } } - } - ) - } + ) + } - if (historySource == HistorySource.REMOTE && isLoggedIn) { - historyPage?.sections?.forEach { section -> - stickyHeader { - NavigationTitle( - title = section.title, - modifier = Modifier - .fillMaxWidth() - .background(MaterialTheme.colorScheme.background) - ) - } + if (historySource == HistorySource.REMOTE && isLoggedIn) { + historyPage?.sections?.forEach { section -> + stickyHeader { + NavigationTitle( + title = section.title, + modifier = Modifier + .fillMaxWidth() + .background(MaterialTheme.colorScheme.background) + ) + } - items( - items = section.songs, - key = { it.id } - ) { song -> - val available = downloads[song.id]?.isAvailableOffline() ?: false || isNetworkConnected + items( + items = section.songs, + key = { it.id } + ) { song -> + val available = downloads[song.id]?.isAvailableOffline() ?: false || isNetworkConnected - val content: @Composable () -> Unit = { - YouTubeListItem( - item = song, - isActive = song.id == mediaMetadata?.id, - isPlaying = isPlaying, - trailingContent = { - if (available) { - IconButton( - onClick = { - menuState.show { - YouTubeSongMenu( - song = song, - navController = navController, - onDismiss = menuState::dismiss - ) + val content: @Composable () -> Unit = { + YouTubeListItem( + item = song, + isActive = song.id == mediaMetadata?.id, + isPlaying = isPlaying, + trailingContent = { + if (available) { + IconButton( + onClick = { + menuState.show { + YouTubeSongMenu( + song = song, + navController = navController, + onDismiss = menuState::dismiss + ) + } } + ) { + Icon( + Icons.Rounded.MoreVert, + contentDescription = null + ) } - ) { - Icon( - Icons.Rounded.MoreVert, - contentDescription = null - ) } - } - }, - modifier = Modifier - .fillMaxWidth() - .combinedClickable( - onClick = { - if (available) { - if (song.id == mediaMetadata?.id) { - playerConnection.player.togglePlayPause() - } else if (song.id.startsWith("LA")) { - playerConnection.playQueue( - ListQueue( - title = "History", - items = section.songs.map { it.toMediaMetadata() } - ) - ) - } else { - playerConnection.playQueue( - if (isNetworkConnected) { - YouTubeQueue( - endpoint = WatchEndpoint(videoId = song.id), - preloadItem = song.toMediaMetadata() - ) - } else { + }, + modifier = Modifier + .fillMaxWidth() + .combinedClickable( + onClick = { + if (available) { + if (song.id == mediaMetadata?.id) { + playerConnection.player.togglePlayPause() + } else if (song.id.startsWith("LA")) { + playerConnection.playQueue( ListQueue( - title = "${context.getString(R.string.queue_searched_songs)} $viewModel.query", - items = listOf(song.toMediaMetadata()) + title = "History", + items = section.songs.map { it.toMediaMetadata() } ) - } - ) + ) + } else { + playerConnection.playQueue( + if (isNetworkConnected) { + YouTubeQueue( + endpoint = WatchEndpoint(videoId = song.id), + preloadItem = song.toMediaMetadata() + ) + } else { + ListQueue( + title = "${context.getString(R.string.queue_searched_songs)} $viewModel.query", + items = listOf(song.toMediaMetadata()) + ) + } + ) + } + } + }, + onLongClick = { + if (available) { + menuState.show { + YouTubeSongMenu( + song = song, + navController = navController, + onDismiss = menuState::dismiss + ) + } } } + ) + ) + } + + if (available) { + SwipeToQueueBox( + item = song.toMediaItem(), + content = { content() }, + snackbarHostState = snackbarHostState + ) + } else { + content() + } + } + } + } else { + filteredEventsMap.forEach { (dateAgo, eventsGroup) -> + stickyHeader { + NavigationTitle( + title = dateAgoToString(dateAgo), + modifier = Modifier + .fillMaxWidth() + .background(MaterialTheme.colorScheme.surface) + ) + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier + .fillMaxWidth() + .background(MaterialTheme.colorScheme.surface) + ) { + Spacer(modifier = Modifier.width(16.dp)) // why compose no margin... + + if (inSelectMode) { + SelectHeader( + selectedItems = eventsMap.flatMap { + group -> group.value.filter{ it.event.id in selection } + }.map { it.song.toMediaMetadata() }, + totalItemCount = eventsMap.flatMap { group -> group.value.map { it.song }.getAvailableSongs(isNetworkConnected)}.size, + onSelectAll = { + selection.clear() + selection.addAll(eventsMap.flatMap { group -> + group.value.filter{ it.song.song.isAvailableOffline() || isNetworkConnected }.map { it.event.id } + }) }, - onLongClick = { - if (available) { - menuState.show { - YouTubeSongMenu( - song = song, - navController = navController, - onDismiss = menuState::dismiss - ) + onDeselectAll = { selection.clear() }, + menuState = menuState, + onDismiss = onExitSelectionMode, + onRemoveFromHistory = { + val sel = selection.mapNotNull { eventId -> + filteredEventIndex[eventId]?.event + } + database.query { + sel.forEach { + delete(it) } } - } + }, ) - ) - } + } - if (available) { - SwipeToQueueBox( - item = song.toMediaItem(), - content = { content() }, - snackbarHostState = snackbarHostState - ) - } else { - content() + Spacer(modifier = Modifier.width(16.dp)) + } } - } - } - } else { - eventsMap.forEach { (dateAgo, eventsGroup) -> - stickyHeader { - NavigationTitle( - title = dateAgoToString(dateAgo), - modifier = Modifier - .fillMaxWidth() - .background(MaterialTheme.colorScheme.surface) - ) - Row( - verticalAlignment = Alignment.CenterVertically, - modifier = Modifier - .fillMaxWidth() - .background(MaterialTheme.colorScheme.surface) - ) { - Spacer(modifier = Modifier.width(16.dp)) // why compose no margin... - if (inSelectMode) { - SelectHeader( - selectedItems = eventsMap.flatMap { - group -> group.value.filter{ it.event.id in selection } - }.map { it.song.toMediaMetadata() }, - totalItemCount = eventsMap.flatMap { group -> group.value.map { it.song }.getAvailableSongs(isNetworkConnected)}.size, - onSelectAll = { - selection.clear() - selection.addAll(eventsMap.flatMap { group -> - group.value.filter{ it.song.song.isAvailableOffline() || isNetworkConnected }.map { it.event.id } - }) + itemsIndexed( + items = eventsGroup, + ) { index, event -> + if (index < eventsGroup.size) { + SongListItem( + song = event.song, + onPlay = { + playerConnection.playQueue( + ListQueue( + title = dateAgoToString(dateAgo), + items = eventsGroup.map { it.song.toMediaMetadata() }, + startIndex = index + ) + ) }, - onDeselectAll = { selection.clear() }, - menuState = menuState, - onDismiss = onExitSelectionMode, - onRemoveFromHistory = { - val sel = selection.mapNotNull { eventId -> - filteredEventIndex[eventId]?.event - } - database.query { - sel.forEach { - delete(it) - } + onSelectedChange = { + inSelectMode = true + if (it) { + selection.add(event.event.id) + } else { + selection.remove(event.event.id) } }, + inSelectMode = inSelectMode, + isSelected = selection.contains(event.event.id), + navController = navController, + modifier = Modifier.fillMaxWidth().animateItem() ) } - - Spacer(modifier = Modifier.width(16.dp)) } } - - itemsIndexed( - items = eventsGroup, - ) { index, event -> - SongListItem( - song = event.song, - onPlay = { - playerConnection.playQueue( - ListQueue( - title = dateAgoToString(dateAgo), - items = eventsGroup.map { it.song.toMediaMetadata() }, - startIndex = index - ) - ) - }, - onSelectedChange = { - inSelectMode = true - if (it) { - selection.add(event.event.id) - } else { - selection.remove(event.event.id) - } - }, - inSelectMode = inSelectMode, - isSelected = selection.contains(event.event.id), - navController = navController, - modifier = Modifier.fillMaxWidth().animateItem() - ) - } } } + } else { + Box( + modifier = Modifier.fillMaxSize(), + contentAlignment = Alignment.Center + ) { + Text( + text = stringResource(R.string.no_history), + style = MaterialTheme.typography.titleLarge + ) + } } HideOnScrollFAB( @@ -498,4 +505,4 @@ fun HistoryScreen( } } ) -} +} \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 035292475..d246bf4f3 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -469,4 +469,5 @@ Clear translation models OFF ON + No listening history yet