diff --git a/app/src/main/java/org/listenbrainz/android/model/SocialUiState.kt b/app/src/main/java/org/listenbrainz/android/model/SocialUiState.kt index c6faa9bf..c2d24bf6 100644 --- a/app/src/main/java/org/listenbrainz/android/model/SocialUiState.kt +++ b/app/src/main/java/org/listenbrainz/android/model/SocialUiState.kt @@ -1,5 +1,6 @@ package org.listenbrainz.android.model data class SocialUiState( - val error: ResponseError? = null + val error: ResponseError? = null, + val successMsgId : Int? = null ) \ No newline at end of file diff --git a/app/src/main/java/org/listenbrainz/android/ui/components/SuccessBar.kt b/app/src/main/java/org/listenbrainz/android/ui/components/SuccessBar.kt new file mode 100644 index 00000000..aa6aa344 --- /dev/null +++ b/app/src/main/java/org/listenbrainz/android/ui/components/SuccessBar.kt @@ -0,0 +1,50 @@ +package org.listenbrainz.android.ui.components + +import android.content.res.Configuration.UI_MODE_NIGHT_YES +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.expandVertically +import androidx.compose.animation.shrinkVertically +import androidx.compose.material3.SnackbarHostState +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.tooling.preview.Preview +import kotlinx.coroutines.delay +import org.listenbrainz.android.ui.theme.ListenBrainzTheme +import org.listenbrainz.android.R + +@Composable +fun SuccessBar( + resId : Int?, + onMessageShown: () -> Unit, + snackbarState : SnackbarHostState +) { + val context = LocalContext.current; + LaunchedEffect(resId) { + if (resId != null) { + delay(4000) + onMessageShown() + } + } + + AnimatedVisibility( + visible = resId != null, + enter = expandVertically(), + exit = shrinkVertically() + ) { + LaunchedEffect(key1 = resId){ + if(resId != null){ + snackbarState.showSnackbar(context.getString(resId)) + } + } + } +} + +@Preview +@Preview(uiMode = UI_MODE_NIGHT_YES) +@Composable +private fun SuccessBarPreview() { + ListenBrainzTheme { + SuccessBar(resId = R.string.about_title , onMessageShown = {} , snackbarState = SnackbarHostState()) + } +} \ No newline at end of file diff --git a/app/src/main/java/org/listenbrainz/android/ui/navigation/AppNavigation.kt b/app/src/main/java/org/listenbrainz/android/ui/navigation/AppNavigation.kt index 84f9345a..bc13fcfd 100644 --- a/app/src/main/java/org/listenbrainz/android/ui/navigation/AppNavigation.kt +++ b/app/src/main/java/org/listenbrainz/android/ui/navigation/AppNavigation.kt @@ -1,6 +1,7 @@ package org.listenbrainz.android.ui.navigation import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.material3.SnackbarHostState import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.navigation.NavController @@ -20,6 +21,7 @@ fun AppNavigation( navController: NavController = rememberNavController(), scrollRequestState: Boolean, onScrollToTop: (suspend () -> Unit) -> Unit, + snackbarState : SnackbarHostState ) { NavHost( navController = navController as NavHostController, @@ -38,7 +40,8 @@ fun AppNavigation( composable(route = AppNavigationItem.Profile.route){ ProfileScreen( onScrollToTop = onScrollToTop, - scrollRequestState = scrollRequestState + scrollRequestState = scrollRequestState, + snackbarState = snackbarState ) } composable(route = AppNavigationItem.Settings.route){ diff --git a/app/src/main/java/org/listenbrainz/android/ui/screens/listens/ListensScreen.kt b/app/src/main/java/org/listenbrainz/android/ui/screens/listens/ListensScreen.kt index 4c83d3d2..74204df4 100644 --- a/app/src/main/java/org/listenbrainz/android/ui/screens/listens/ListensScreen.kt +++ b/app/src/main/java/org/listenbrainz/android/ui/screens/listens/ListensScreen.kt @@ -1,5 +1,6 @@ package org.listenbrainz.android.ui.screens.listens +import android.os.Bundle import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.core.tween import androidx.compose.animation.fadeIn @@ -11,25 +12,50 @@ import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.pager.HorizontalPager import androidx.compose.foundation.pager.rememberPagerState +import androidx.compose.material3.SnackbarHostState import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.MutableState 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.saveable.rememberSaveable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalUriHandler +import androidx.compose.ui.platform.UriHandler import androidx.compose.ui.tooling.preview.Preview import androidx.hilt.navigation.compose.hiltViewModel +import kotlinx.coroutines.launch +import org.listenbrainz.android.R +import org.listenbrainz.android.model.Listen +import org.listenbrainz.android.model.Metadata +import org.listenbrainz.android.model.SocialUiState import org.listenbrainz.android.model.TrackMetadata +import org.listenbrainz.android.model.feed.ReviewEntityType +import org.listenbrainz.android.ui.components.ErrorBar import org.listenbrainz.android.ui.components.ListenCardSmall import org.listenbrainz.android.ui.components.LoadingAnimation +import org.listenbrainz.android.ui.components.SuccessBar +import org.listenbrainz.android.ui.components.dialogs.Dialog +import org.listenbrainz.android.ui.components.dialogs.PersonalRecommendationDialog +import org.listenbrainz.android.ui.components.dialogs.PinDialog +import org.listenbrainz.android.ui.components.dialogs.ReviewDialog +import org.listenbrainz.android.ui.components.dialogs.rememberDialogsState +import org.listenbrainz.android.ui.screens.feed.FeedUiState +import org.listenbrainz.android.ui.screens.feed.SocialDropdown import org.listenbrainz.android.ui.screens.profile.UserData import org.listenbrainz.android.ui.screens.settings.PreferencesUiState import org.listenbrainz.android.ui.theme.ListenBrainzTheme import org.listenbrainz.android.util.Utils +import org.listenbrainz.android.viewmodel.FeedViewModel import org.listenbrainz.android.viewmodel.ListensViewModel import org.listenbrainz.android.viewmodel.SocialViewModel @@ -37,21 +63,30 @@ import org.listenbrainz.android.viewmodel.SocialViewModel fun ListensScreen( viewModel: ListensViewModel = hiltViewModel(), socialViewModel: SocialViewModel = hiltViewModel(), + feedViewModel : FeedViewModel = hiltViewModel(), scrollRequestState: Boolean, onScrollToTop: (suspend () -> Unit) -> Unit, + snackbarState : SnackbarHostState ) { val uiState by viewModel.uiState.collectAsState() val preferencesUiState by viewModel.preferencesUiState.collectAsState() - + val socialUiState by socialViewModel.uiState.collectAsState() + val feedUiState by feedViewModel.uiState.collectAsState() + val dropdownItemIndex: MutableState = rememberSaveable { + mutableStateOf(null) + } + ListensScreen( scrollRequestState = scrollRequestState, onScrollToTop = onScrollToTop, uiState = uiState, + feedUiState = feedUiState, preferencesUiState = preferencesUiState, updateNotificationServicePermissionStatus = { viewModel.updateNotificationServicePermissionStatus() }, + dropdownItemIndex = dropdownItemIndex, validateUserToken = { token -> viewModel.validateUserToken(token) }, @@ -60,10 +95,52 @@ fun ListensScreen( }, playListen = { socialViewModel.playListen(it) + }, + snackbarState = snackbarState, + socialUiState = socialUiState, + onRecommend = {metadata -> + socialViewModel.recommend(metadata) + dropdownItemIndex.value = null + }, + onErrorShown = { + socialViewModel.clearErrorFlow() + }, + onMessageShown = { + socialViewModel.clearMsgFlow() + }, + onPin = { + metadata, blurbContent -> socialViewModel.pin(metadata , blurbContent) + dropdownItemIndex.value = null + }, + searchUsers = { + query -> feedViewModel.searchUser(query) + }, + isCritiqueBrainzLinked = { + feedViewModel.isCritiqueBrainzLinked() + }, + onReview = { + type, blurbContent, rating, locale, metadata -> socialViewModel.review(metadata , type , blurbContent , rating , locale) + }, + onPersonallyRecommend = { + metadata, users, blurbContent -> socialViewModel.personallyRecommend(metadata, users, blurbContent) } ) } +private enum class ListenDialogBundleKeys { + PAGE, + EVENT_INDEX; + companion object { + fun listenDialogBundle(page: Int, eventIndex: Int): Bundle { + return Bundle().apply { + putInt(PAGE.name, page) + putInt(EVENT_INDEX.name, eventIndex) + } + } + } +} + + @OptIn(ExperimentalFoundationApi::class) @Composable @@ -71,13 +148,32 @@ fun ListensScreen( scrollRequestState: Boolean, onScrollToTop: (suspend () -> Unit) -> Unit, uiState: ListensUiState, + feedUiState: FeedUiState, preferencesUiState: PreferencesUiState, updateNotificationServicePermissionStatus: () -> Unit, + dropdownItemIndex : MutableState, validateUserToken: suspend (String) -> Boolean, setToken: (String) -> Unit, - playListen: (TrackMetadata) -> Unit + playListen: (TrackMetadata) -> Unit, + uriHandler: UriHandler = LocalUriHandler.current, + snackbarState: SnackbarHostState, + socialUiState: SocialUiState, + onRecommend : (metadata : Metadata) -> Unit, + onErrorShown : () -> Unit, + onMessageShown : () -> Unit, + onPin : (metadata : Metadata , blurbContent : String) -> Unit, + searchUsers: (String) -> Unit, + isCritiqueBrainzLinked: suspend () -> Boolean?, + onReview: (type: ReviewEntityType, blurbContent: String, rating: Int?, locale: String, metadata: Metadata) -> Unit, + onPersonallyRecommend: (metadata: Metadata, users: List, blurbContent: String) -> Unit ) { val listState = rememberLazyListState() + + val dialogsState = rememberDialogsState() + + val scope = rememberCoroutineScope() + + val context = LocalContext.current // Scroll to the top when shouldScrollToTop becomes true LaunchedEffect(scrollRequestState) { @@ -134,7 +230,8 @@ fun ListensScreen( } } - items(items = uiState.listens) { listen -> + itemsIndexed(items = uiState.listens) { index , listen -> + val metadata = Metadata(trackMetadata = listen.trackMetadata) ListenCardSmall( modifier = Modifier.padding( horizontal = ListenBrainzTheme.paddings.horizontal, @@ -145,12 +242,70 @@ fun ListensScreen( coverArtUrl = Utils.getCoverArtUrl( caaReleaseMbid = listen.trackMetadata.mbidMapping?.caaReleaseMbid, caaId = listen.trackMetadata.mbidMapping?.caaId - ) + ), + dropDown = { + SocialDropdown( + isExpanded = dropdownItemIndex.value == index, + onDismiss = { + dropdownItemIndex.value = null + }, + metadata = metadata, + onRecommend = { onRecommend(metadata) }, + onPersonallyRecommend = { + dialogsState.activateDialog(Dialog.PERSONAL_RECOMMENDATION , ListenDialogBundleKeys.listenDialogBundle(0, index)) + dropdownItemIndex.value = null + }, + onReview = { + dialogsState.activateDialog(Dialog.REVIEW , ListenDialogBundleKeys.listenDialogBundle(0, index)) + dropdownItemIndex.value = null + }, + onPin = { + dialogsState.activateDialog(Dialog.PIN , ListenDialogBundleKeys.listenDialogBundle(0, index)) + dropdownItemIndex.value = null + }, + onOpenInMusicBrainz = { + try { + uriHandler.openUri("https://musicbrainz.org/recording/${metadata.trackMetadata?.mbidMapping?.recordingMbid}") + } + catch(e : Error) { + scope.launch { + snackbarState.showSnackbar(context.getString(R.string.err_generic_toast)) + } + } + dropdownItemIndex.value = null + } + + ) + }, + enableDropdownIcon = true, + onDropdownIconClick = { + dropdownItemIndex.value = index + } ) { playListen(listen.trackMetadata) } } } + + ErrorBar(error = socialUiState.error, onErrorShown = onErrorShown ) + SuccessBar(resId = socialUiState.successMsgId, onMessageShown = onMessageShown, snackbarState = snackbarState) + + Dialogs( + deactivateDialog = { + dialogsState.deactivateDialog() + }, + currentDialog = dialogsState.currentDialog, + currentIndex = dialogsState.metadata?.getInt(ListenDialogBundleKeys.EVENT_INDEX.name), + listens = uiState.listens, + onPin = {metadata, blurbContent -> onPin(metadata, blurbContent)}, + searchUsers = { query -> searchUsers(query) }, + feedUiState = feedUiState, + isCritiqueBrainzLinked = isCritiqueBrainzLinked, + onReview = {type, blurbContent, rating, locale, metadata -> onReview(type, blurbContent, rating, locale, metadata) }, + onPersonallyRecommend = {metadata, users, blurbContent -> onPersonallyRecommend(metadata, users, blurbContent)}, + snackbarState = snackbarState, + socialUiState = socialUiState + ) // Loading Animation AnimatedVisibility( @@ -164,6 +319,72 @@ fun ListensScreen( } } +@Composable +private fun Dialogs( + deactivateDialog: () -> Unit, + currentDialog: Dialog, + feedUiState: FeedUiState, + currentIndex : Int?, + listens : List, + onPin : (metadata: Metadata , blurbContent : String) -> Unit, + searchUsers : (String) -> Unit, + isCritiqueBrainzLinked: suspend () -> Boolean?, + onReview : (type: ReviewEntityType, blurbContent: String, rating: Int?, locale: String , metadata : Metadata) -> Unit, + onPersonallyRecommend : (metadata : Metadata , users : List , blurbContent : String) -> Unit, + snackbarState: SnackbarHostState, + socialUiState: SocialUiState +) { + val context = LocalContext.current + when (currentDialog) { + Dialog.NONE -> Unit + Dialog.PIN -> { + PinDialog(trackName = listens[currentIndex!!].trackMetadata.trackName, artistName = listens[currentIndex].trackMetadata.artistName, onDismiss = deactivateDialog, onSubmit = { + blurbContent -> + onPin(Metadata(trackMetadata = listens[currentIndex].trackMetadata) , blurbContent) + }) + LaunchedEffect(socialUiState.error){ + if(socialUiState.error == null){ + snackbarState.showSnackbar(context.getString(R.string.pin_greeting)) + } + } + } + Dialog.PERSONAL_RECOMMENDATION -> { + PersonalRecommendationDialog( + trackName = listens[currentIndex!!].trackMetadata.trackName, + onDismiss = deactivateDialog, + searchResult = feedUiState.searchResult, + searchUsers = searchUsers, + onSubmit = { + users, blurbContent -> + onPersonallyRecommend( + Metadata(trackMetadata = listens[currentIndex].trackMetadata), + users, + blurbContent + ) + } + ) + } + Dialog.REVIEW -> { + ReviewDialog( + trackName = listens[currentIndex!!].trackMetadata.trackName, + artistName = listens[currentIndex].trackMetadata.artistName, + releaseName = listens[currentIndex].trackMetadata.releaseName, + onDismiss = deactivateDialog, + isCritiqueBrainzLinked = isCritiqueBrainzLinked, + onSubmit = { type, blurbContent, rating, locale -> + onReview( + type, + blurbContent, + rating, + locale, + Metadata(trackMetadata = listens[currentIndex].trackMetadata) + ) + } + ) + } + } +} + @Preview @Composable fun ListensScreenPreview() { @@ -172,9 +393,21 @@ fun ListensScreenPreview() { scrollRequestState = false, updateNotificationServicePermissionStatus = {}, uiState = ListensUiState(), + feedUiState = FeedUiState(), preferencesUiState = PreferencesUiState(), validateUserToken = { true }, setToken = {}, - playListen = {} + playListen = {}, + socialUiState = SocialUiState(), + onRecommend = {}, + onErrorShown = {}, + onMessageShown = {}, + onPin = {_,_ ->}, + searchUsers = {_ ->}, + isCritiqueBrainzLinked = { true }, + onReview = {_,_,_,_,_ ->}, + onPersonallyRecommend = {_,_,_ ->}, + dropdownItemIndex = remember { mutableStateOf(null) }, + snackbarState = remember { SnackbarHostState() } ) } diff --git a/app/src/main/java/org/listenbrainz/android/ui/screens/main/MainActivity.kt b/app/src/main/java/org/listenbrainz/android/ui/screens/main/MainActivity.kt index 925972dc..48890922 100644 --- a/app/src/main/java/org/listenbrainz/android/ui/screens/main/MainActivity.kt +++ b/app/src/main/java/org/listenbrainz/android/ui/screens/main/MainActivity.kt @@ -138,7 +138,7 @@ class MainActivity : ComponentActivity() { val backdropScaffoldState = rememberBackdropScaffoldState(initialValue = BackdropValue.Revealed) var scrollToTopState by remember { mutableStateOf(false) } - val snackbarState = SnackbarHostState() + val snackbarState = remember { SnackbarHostState() } val searchBarState = rememberSearchBarState() val scope = rememberCoroutineScope() @@ -183,7 +183,8 @@ class MainActivity : ComponentActivity() { scrollToTopState = false } } - } + }, + snackbarState = snackbarState ) } } diff --git a/app/src/main/java/org/listenbrainz/android/ui/screens/profile/ProfileScreen.kt b/app/src/main/java/org/listenbrainz/android/ui/screens/profile/ProfileScreen.kt index 2a70412e..f0206fd5 100644 --- a/app/src/main/java/org/listenbrainz/android/ui/screens/profile/ProfileScreen.kt +++ b/app/src/main/java/org/listenbrainz/android/ui/screens/profile/ProfileScreen.kt @@ -16,6 +16,7 @@ import androidx.compose.material.Button import androidx.compose.material.ButtonDefaults import androidx.compose.material.MaterialTheme import androidx.compose.material.Text +import androidx.compose.material3.SnackbarHostState import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState @@ -44,7 +45,8 @@ fun ProfileScreen( context: Context = LocalContext.current, viewModel: ProfileViewModel = hiltViewModel(), scrollRequestState: Boolean, - onScrollToTop: (suspend () -> Unit) -> Unit + onScrollToTop: (suspend () -> Unit) -> Unit, + snackbarState : SnackbarHostState ) { val scrollState = rememberScrollState() @@ -61,7 +63,8 @@ fun ProfileScreen( STATUS_LOGGED_IN -> { ListensScreen( onScrollToTop = onScrollToTop, - scrollRequestState = scrollRequestState + scrollRequestState = scrollRequestState, + snackbarState = snackbarState ) } else -> { diff --git a/app/src/main/java/org/listenbrainz/android/ui/screens/yim23/YearInMusic23Activity.kt b/app/src/main/java/org/listenbrainz/android/ui/screens/yim23/YearInMusic23Activity.kt index a3700dcc..f641f483 100644 --- a/app/src/main/java/org/listenbrainz/android/ui/screens/yim23/YearInMusic23Activity.kt +++ b/app/src/main/java/org/listenbrainz/android/ui/screens/yim23/YearInMusic23Activity.kt @@ -25,7 +25,6 @@ class YearInMusic23Activity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val yim23ViewModel : Yim23ViewModel by viewModels() - val socialViewModel : SocialViewModel by viewModels() val networkConnectivityViewModel: NetworkConnectivityViewModel = ViewModelProvider(this)[NetworkConnectivityViewModelImpl::class.java] @@ -37,8 +36,7 @@ class YearInMusic23Activity : ComponentActivity() { Toast.LENGTH_LONG).show() finish() } - Yim23Navigation(yimViewModel = yim23ViewModel, socialViewModel = socialViewModel - , networkConnectivityViewModel = networkConnectivityViewModel, activity = this) + Yim23Navigation(yimViewModel = yim23ViewModel ,networkConnectivityViewModel = networkConnectivityViewModel, activity = this) } } } \ No newline at end of file diff --git a/app/src/main/java/org/listenbrainz/android/ui/screens/yim23/navigation/Yim23Navigation.kt b/app/src/main/java/org/listenbrainz/android/ui/screens/yim23/navigation/Yim23Navigation.kt index e7f5a3ec..da15981a 100644 --- a/app/src/main/java/org/listenbrainz/android/ui/screens/yim23/navigation/Yim23Navigation.kt +++ b/app/src/main/java/org/listenbrainz/android/ui/screens/yim23/navigation/Yim23Navigation.kt @@ -61,13 +61,10 @@ private const val screenTransitionDuration = 900 @Composable fun Yim23Navigation( yimViewModel: Yim23ViewModel, - socialViewModel: SocialViewModel, activity: ComponentActivity, networkConnectivityViewModel: NetworkConnectivityViewModel, ) { val navController = rememberNavController() - var scrollToTopState by remember { mutableStateOf(false) } - val scope = rememberCoroutineScope() NavHost( navController = navController, modifier = Modifier.fillMaxSize(), @@ -94,22 +91,6 @@ fun Yim23Navigation( Yim23HomeScreen(viewModel = yimViewModel, networkConnectivityViewModel = networkConnectivityViewModel,navController = navController, activity = activity) } - composable(route = AppNavigationItem.Profile.route){ - Surface (color = ListenBrainzTheme.colorScheme.background) { - ProfileScreen( - onScrollToTop = { scrollToTop -> - scope.launch { - if (scrollToTopState){ - scrollToTop() - scrollToTopState = false - } - } - }, - scrollRequestState = false - ) - } - } - addYimScreen( route = Yim23Screens.YimChartTitleScreen.name ){ Yim23ChartTitleScreen(viewModel = yimViewModel, navController = navController) diff --git a/app/src/main/java/org/listenbrainz/android/viewmodel/BaseViewModel.kt b/app/src/main/java/org/listenbrainz/android/viewmodel/BaseViewModel.kt index 4a118d8f..9e8952da 100644 --- a/app/src/main/java/org/listenbrainz/android/viewmodel/BaseViewModel.kt +++ b/app/src/main/java/org/listenbrainz/android/viewmodel/BaseViewModel.kt @@ -10,7 +10,7 @@ import org.listenbrainz.android.model.ResponseError abstract class BaseViewModel: ViewModel() { protected val errorFlow = MutableStateFlow(null) - + protected val successMsgFlow = MutableStateFlow(null) /** Visible Ui-State for UI to consume.*/ abstract val uiState: StateFlow @@ -22,9 +22,19 @@ abstract class BaseViewModel: ViewModel() { errorFlow.emit(error) } } + + protected fun emitMsg(messageId : Int?){ + viewModelScope.launch { + successMsgFlow.emit(messageId) + } + } /** Call this function to reset [errorFlow]'s latest emission.*/ fun clearErrorFlow() { emitError(null) } + + fun clearMsgFlow() { + emitMsg(null) + } } \ No newline at end of file diff --git a/app/src/main/java/org/listenbrainz/android/viewmodel/SocialViewModel.kt b/app/src/main/java/org/listenbrainz/android/viewmodel/SocialViewModel.kt index 605e7e33..99af8003 100644 --- a/app/src/main/java/org/listenbrainz/android/viewmodel/SocialViewModel.kt +++ b/app/src/main/java/org/listenbrainz/android/viewmodel/SocialViewModel.kt @@ -6,7 +6,7 @@ import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @@ -25,6 +25,7 @@ import org.listenbrainz.android.repository.remoteplayer.RemotePlaybackHandler import org.listenbrainz.android.repository.social.SocialRepository import org.listenbrainz.android.util.Resource import javax.inject.Inject +import org.listenbrainz.android.R @HiltViewModel class SocialViewModel @Inject constructor( @@ -39,8 +40,11 @@ class SocialViewModel @Inject constructor( override val uiState: StateFlow = createUiStateFlow() override fun createUiStateFlow(): StateFlow = - errorFlow.map { - SocialUiState(it) + combine( + errorFlow, + successMsgFlow + ){ + error , message -> SocialUiState(error , message) }.stateIn( viewModelScope, SharingStarted.Eagerly, @@ -103,6 +107,9 @@ class SocialViewModel @Inject constructor( if (result.status == Resource.Status.FAILED){ emitError(result.error) } + else if(result.status == Resource.Status.SUCCESS){ + emitMsg(R.string.recommendation_greeting) + } } } @@ -127,6 +134,9 @@ class SocialViewModel @Inject constructor( if (result.status == Resource.Status.FAILED){ emitError(result.error) } + else if(result.status == Resource.Status.SUCCESS){ + emitMsg(R.string.personal_recommendation_greeting) + } } } @@ -151,6 +161,9 @@ class SocialViewModel @Inject constructor( if (result.status == Resource.Status.FAILED){ emitError(result.error) } + else if(result.status == Resource.Status.SUCCESS){ + emitMsg(R.string.review_greeting) + } } } @@ -166,6 +179,9 @@ class SocialViewModel @Inject constructor( if (result.status == Resource.Status.FAILED){ emitError(result.error) } + else if(result.status == Resource.Status.SUCCESS){ + emitMsg(R.string.pin_greeting) + } } } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 43c9be5d..1695cdbb 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -93,6 +93,11 @@ Add some text to search No email client found Search history deleted + Song recommended successfully! + Song recommendation sent successfully! + Song review submitted successfully! + Song pinned successfully! + Error occurred! Please try again later! You need to login in order to view your collections.