16
16
17
17
package com.google.samples.apps.iosched.ui.speaker
18
18
19
- import androidx.lifecycle.LiveData
20
- import androidx.lifecycle.MutableLiveData
19
+ import androidx.lifecycle.SavedStateHandle
21
20
import androidx.lifecycle.ViewModel
22
- import androidx.lifecycle.asFlow
23
- import androidx.lifecycle.liveData
24
- import androidx.lifecycle.switchMap
21
+ import androidx.lifecycle.asLiveData
25
22
import androidx.lifecycle.viewModelScope
26
23
import com.google.samples.apps.iosched.model.Speaker
27
24
import com.google.samples.apps.iosched.model.SpeakerId
@@ -33,16 +30,21 @@ import com.google.samples.apps.iosched.shared.domain.settings.GetTimeZoneUseCase
33
30
import com.google.samples.apps.iosched.shared.domain.speakers.LoadSpeakerUseCase
34
31
import com.google.samples.apps.iosched.shared.domain.speakers.LoadSpeakerUseCaseResult
35
32
import com.google.samples.apps.iosched.shared.result.Result
33
+ import com.google.samples.apps.iosched.shared.result.Result.Loading
36
34
import com.google.samples.apps.iosched.shared.result.data
37
35
import com.google.samples.apps.iosched.shared.result.successOr
38
36
import com.google.samples.apps.iosched.shared.util.TimeUtils
39
- import com.google.samples.apps.iosched.shared.util.map
40
37
import com.google.samples.apps.iosched.ui.sessioncommon.EventActionsViewModelDelegate
41
38
import com.google.samples.apps.iosched.ui.signin.SignInViewModelDelegate
39
+ import com.google.samples.apps.iosched.util.WhileViewSubscribed
42
40
import dagger.hilt.android.lifecycle.HiltViewModel
43
41
import kotlinx.coroutines.flow.SharingStarted
42
+ import kotlinx.coroutines.flow.StateFlow
44
43
import kotlinx.coroutines.flow.collect
44
+ import kotlinx.coroutines.flow.flow
45
+ import kotlinx.coroutines.flow.mapLatest
45
46
import kotlinx.coroutines.flow.stateIn
47
+ import kotlinx.coroutines.flow.transformLatest
46
48
import org.threeten.bp.ZoneId
47
49
import javax.inject.Inject
48
50
@@ -51,6 +53,7 @@ import javax.inject.Inject
51
53
*/
52
54
@HiltViewModel
53
55
class SpeakerViewModel @Inject constructor(
56
+ savedStateHandle : SavedStateHandle ,
54
57
private val loadSpeakerUseCase : LoadSpeakerUseCase ,
55
58
private val loadSpeakerSessionsUseCase : LoadUserSessionsUseCase ,
56
59
getTimeZoneUseCase : GetTimeZoneUseCase ,
@@ -61,55 +64,45 @@ class SpeakerViewModel @Inject constructor(
61
64
SignInViewModelDelegate by signInViewModelDelegate,
62
65
EventActionsViewModelDelegate by eventActionsViewModelDelegate {
63
66
64
- private val speakerId = MutableLiveData <String >()
67
+ // TODO: remove hardcoded string when https://issuetracker.google.com/136967621 is available
68
+ private val speakerId: SpeakerId ? = savedStateHandle.get<SpeakerId >(" speaker_id" )
65
69
66
- private val loadSpeakerUseCaseResult: LiveData <Result <LoadSpeakerUseCaseResult >> =
67
- speakerId.switchMap { speakerId ->
68
- liveData {
69
- emit(loadSpeakerUseCase(speakerId))
70
- }
71
- }
70
+ private val loadSpeakerUseCaseResult: StateFlow <Result <LoadSpeakerUseCaseResult >> =
71
+ flow {
72
+ speakerId?.let { emit(loadSpeakerUseCase(speakerId)) }
73
+ }.stateIn(viewModelScope, SharingStarted .Eagerly , Loading )
72
74
73
- val speakerUserSessions: LiveData <List <UserSession >> =
74
- loadSpeakerUseCaseResult.switchMap { speaker ->
75
- liveData {
76
- emit(emptyList()) // Reset value
77
- speaker.data?.let {
78
- loadSpeakerSessionsUseCase(it.speaker.id to it.sessionIds).collect {
79
- it.data?.let { data ->
80
- emit(data)
81
- }
75
+ val speakerUserSessions: StateFlow <List <UserSession >> =
76
+ loadSpeakerUseCaseResult.transformLatest { speaker ->
77
+ speaker.data?.let {
78
+ loadSpeakerSessionsUseCase(it.speaker.id to it.sessionIds).collect {
79
+ it.data?.let { data ->
80
+ emit(data)
82
81
}
83
82
}
84
83
}
85
- }
84
+ }.stateIn(viewModelScope, WhileViewSubscribed , emptyList())
86
85
87
- val speaker: LiveData <Speaker ?> = loadSpeakerUseCaseResult.map {
86
+ val speaker: StateFlow <Speaker ?> = loadSpeakerUseCaseResult.mapLatest {
88
87
it.data?.speaker
89
- }
88
+ }.stateIn(viewModelScope, WhileViewSubscribed , null )
90
89
91
- val hasNoProfileImage: LiveData <Boolean > = loadSpeakerUseCaseResult.map {
90
+ val hasNoProfileImage: StateFlow <Boolean > = loadSpeakerUseCaseResult.mapLatest {
92
91
it.data?.speaker?.imageUrl.isNullOrEmpty()
93
- }
92
+ }.stateIn(viewModelScope, WhileViewSubscribed , true )
94
93
95
- val timeZoneId = liveData {
96
- val timeZone = getTimeZoneUseCase(Unit )
97
- if (timeZone.successOr(true )) {
94
+ // Exposed to the view as a StateFlow but it's a one-shot operation.
95
+ // TODO: Rename with timeZoneId when all usages are migrated
96
+ val timeZoneIdFlow = flow<ZoneId > {
97
+ if (getTimeZoneUseCase(Unit ).successOr(true )) {
98
98
emit(TimeUtils .CONFERENCE_TIMEZONE )
99
99
} else {
100
100
emit(ZoneId .systemDefault())
101
101
}
102
- }
102
+ }.stateIn(viewModelScope, SharingStarted . Lazily , TimeUtils . CONFERENCE_TIMEZONE )
103
103
104
- val timeZoneIdFlow = timeZoneId.asFlow()
105
- .stateIn(viewModelScope, SharingStarted .Lazily , ZoneId .systemDefault())
106
-
107
- /* *
108
- * Provides the speaker ID which initiates all data loading.
109
- */
110
- fun setSpeakerId (id : SpeakerId ) {
111
- speakerId.value = id
112
- }
104
+ // TODO: Replace with timeZoneIdFlow when SearchViewModel is migrated
105
+ val timeZoneId = timeZoneIdFlow.asLiveData()
113
106
114
107
override fun onStarClicked (userSession : UserSession ) {
115
108
eventActionsViewModelDelegate.onStarClicked(userSession)
@@ -124,7 +117,7 @@ class SpeakerViewModel @Inject constructor(
124
117
val sessionId = userSession.userEvent.id
125
118
val sessions = speakerUserSessions.value
126
119
127
- if (sessions != null ) {
120
+ if (sessions.isNotEmpty() ) {
128
121
val session = sessions.first { it.session.id == sessionId }.session
129
122
analyticsHelper.logUiEvent(session.title, AnalyticsActions .STARRED )
130
123
}
0 commit comments