1
1
package org.wordpress.android.ui.accounts.applicationpassword
2
2
3
- import android.util.Log
4
3
import androidx.lifecycle.ViewModel
5
4
import androidx.lifecycle.viewModelScope
6
5
import kotlinx.coroutines.CoroutineDispatcher
7
6
import kotlinx.coroutines.flow.MutableSharedFlow
8
7
import kotlinx.coroutines.flow.asSharedFlow
9
8
import kotlinx.coroutines.launch
10
9
import kotlinx.coroutines.withContext
10
+ import org.greenrobot.eventbus.Subscribe
11
+ import org.greenrobot.eventbus.ThreadMode
12
+ import org.wordpress.android.fluxc.Dispatcher
11
13
import org.wordpress.android.fluxc.generated.SiteActionBuilder
12
14
import org.wordpress.android.fluxc.network.discovery.SelfHostedEndpointFinder
13
- import org.wordpress.android.fluxc.store.SiteStore.RefreshSitesXMLRPCPayload
14
15
import org.wordpress.android.fluxc.store.SiteStore
16
+ import org.wordpress.android.fluxc.store.SiteStore.OnProfileFetched
17
+ import org.wordpress.android.fluxc.store.SiteStore.OnSiteChanged
18
+ import org.wordpress.android.fluxc.store.SiteStore.RefreshSitesXMLRPCPayload
19
+ import org.wordpress.android.fluxc.utils.AppLogWrapper
20
+ import org.wordpress.android.login.util.SiteUtils
15
21
import org.wordpress.android.modules.IO_THREAD
16
22
import org.wordpress.android.ui.accounts.login.ApplicationPasswordLoginHelper
17
-
23
+ import org.wordpress.android.ui.accounts.login.ApplicationPasswordLoginHelper.UriLogin
24
+ import org.wordpress.android.ui.prefs.AppPrefsWrapper
25
+ import org.wordpress.android.util.AppLog
26
+ import org.wordpress.android.util.UrlUtils
18
27
import javax.inject.Inject
19
28
import javax.inject.Named
20
29
21
- private const val TAG = " ApplicationPasswordLoginViewModel"
22
-
23
30
class ApplicationPasswordLoginViewModel @Inject constructor(
24
31
@Named(IO_THREAD ) private val ioDispatcher : CoroutineDispatcher ,
32
+ // Dispatcher is the way to dispatch actions to Flux. It will call siteStore.onAction()
33
+ private val dispatcher : Dispatcher ,
25
34
private val applicationPasswordLoginHelper : ApplicationPasswordLoginHelper ,
26
35
private val selfHostedEndpointFinder : SelfHostedEndpointFinder ,
27
36
private val siteStore : SiteStore ,
37
+ private val appPrefsWrapper : AppPrefsWrapper ,
38
+ private val appLogWrapper : AppLogWrapper ,
28
39
) : ViewModel() {
29
- private val _onFinishedEvent = MutableSharedFlow <String ? >()
40
+ private val _onFinishedEvent = MutableSharedFlow <NavigationActionData >()
30
41
/* *
31
42
* A shared flow that emits the site URL when the setup is finished.
32
43
* It can emit null if the site could not be set up.
33
44
*/
34
45
val onFinishedEvent = _onFinishedEvent .asSharedFlow()
35
46
47
+ private var currentUrlLogin: UriLogin ? = null
48
+ private var oldSitesIDs: ArrayList <Int >? = null
49
+
50
+ fun onStart () {
51
+ dispatcher.register(this )
52
+ oldSitesIDs = SiteUtils .getCurrentSiteIds(siteStore, false )
53
+ }
54
+
55
+ fun onStop () {
56
+ dispatcher.unregister(this )
57
+ }
58
+
36
59
/* *
37
60
* This method is called to set up the site with the provided raw data.
38
61
*
39
62
* @param rawData The raw data containing the callback data from the application password login.
40
63
*/
41
64
fun setupSite (rawData : String ) {
42
65
viewModelScope.launch {
66
+ if (rawData.isEmpty()) {
67
+ appLogWrapper.e(AppLog .T .MAIN , " Cannot store credentials: rawData is empty" )
68
+ _onFinishedEvent .emit(
69
+ NavigationActionData (
70
+ showSiteSelector = false ,
71
+ showPostSignupInterstitial = false ,
72
+ siteUrl = " " ,
73
+ oldSitesIDs = oldSitesIDs,
74
+ isError = true
75
+ )
76
+ )
77
+ return @launch
78
+ }
43
79
val urlLogin = applicationPasswordLoginHelper.getSiteUrlLoginFromRawData(rawData)
44
80
// Store credentials if the site already exists
45
81
val credentialsStored = storeCredentials(rawData)
82
+ // If the site already exists, we can skip fetching it again
46
83
if (credentialsStored) {
47
- _onFinishedEvent .emit(urlLogin.siteUrl)
84
+ _onFinishedEvent .emit(
85
+ NavigationActionData (
86
+ showSiteSelector = false ,
87
+ showPostSignupInterstitial = false ,
88
+ siteUrl = urlLogin.siteUrl,
89
+ oldSitesIDs = oldSitesIDs,
90
+ isError = false
91
+ )
92
+ )
93
+ } else {
94
+ fetchSites(urlLogin)
95
+ currentUrlLogin = urlLogin
96
+ }
97
+ }
98
+ }
99
+
100
+ @Suppress(" TooGenericExceptionCaught" )
101
+ private suspend fun storeCredentials (rawData : String ): Boolean = withContext(ioDispatcher) {
102
+ try {
103
+ if (rawData.isEmpty()) {
104
+ appLogWrapper.e(AppLog .T .DB , " Cannot store credentials: rawData is empty" )
105
+ false
48
106
} else {
49
- val siteFetched = fetchSites(urlLogin )
50
- _onFinishedEvent .emit( if (siteFetched) urlLogin.siteUrl else null )
107
+ val credentialsStored = applicationPasswordLoginHelper.storeApplicationPasswordCredentialsFrom(rawData )
108
+ credentialsStored
51
109
}
110
+ } catch (e: Exception ) {
111
+ appLogWrapper.e(AppLog .T .DB , " Error storing credentials: ${e.stackTrace} " )
112
+ false
52
113
}
53
114
}
54
115
55
116
@Suppress(" TooGenericExceptionCaught" )
56
117
private suspend fun fetchSites (
57
- urlLogin : ApplicationPasswordLoginHelper . UriLogin
58
- ): Boolean = withContext(ioDispatcher) {
118
+ urlLogin : UriLogin
119
+ ) = withContext(ioDispatcher) {
59
120
try {
60
121
if (urlLogin.user.isNullOrEmpty() ||
61
122
urlLogin.password.isNullOrEmpty() ||
62
123
urlLogin.siteUrl.isNullOrEmpty()) {
63
- Log .e(TAG , " Cannot store credentials: rawData is empty" )
64
- false
124
+ appLogWrapper .e(AppLog . T . MAIN , " Cannot store credentials: rawData is empty" )
125
+ emitErrorFetching(urlLogin)
65
126
} else {
66
127
val xmlRpcEndpoint =
67
128
selfHostedEndpointFinder.verifyOrDiscoverXMLRPCEndpoint(urlLogin.siteUrl)
68
- siteStore.onAction (
129
+ dispatcher.dispatch (
69
130
SiteActionBuilder .newFetchSitesXmlRpcFromApplicationPasswordAction(
70
131
RefreshSitesXMLRPCPayload (
71
132
username = urlLogin.user,
@@ -74,27 +135,68 @@ class ApplicationPasswordLoginViewModel @Inject constructor(
74
135
)
75
136
)
76
137
)
77
- true
78
138
}
79
139
} catch (e: Exception ) {
80
- Log .e(TAG , " Error storing credentials" , e )
81
- false
140
+ appLogWrapper .e(AppLog . T . API , " Error storing credentials: ${e.stackTrace} " )
141
+ emitErrorFetching(urlLogin)
82
142
}
83
143
}
84
144
85
- @Suppress(" TooGenericExceptionCaught" )
86
- private suspend fun storeCredentials (rawData : String ): Boolean = withContext(ioDispatcher) {
87
- try {
88
- if (rawData.isEmpty()) {
89
- Log .e(TAG , " Cannot store credentials: rawData is empty" )
90
- false
91
- } else {
92
- val credentialsStored = applicationPasswordLoginHelper.storeApplicationPasswordCredentialsFrom(rawData)
93
- credentialsStored
145
+ private suspend fun emitErrorFetching (urlLogin : UriLogin ) = _onFinishedEvent .emit(
146
+ NavigationActionData (
147
+ showSiteSelector = false ,
148
+ showPostSignupInterstitial = false ,
149
+ siteUrl = urlLogin.siteUrl,
150
+ oldSitesIDs = oldSitesIDs,
151
+ isError = true
152
+ )
153
+ )
154
+
155
+ @SuppressWarnings(" unused" )
156
+ @Subscribe(threadMode = ThreadMode .BACKGROUND )
157
+ fun onSiteChanged (event : OnSiteChanged ) {
158
+ val currentNormalizedUrl = UrlUtils .normalizeUrl(currentUrlLogin?.siteUrl)
159
+ val site = siteStore.sites.firstOrNull { UrlUtils .normalizeUrl(it.url) == currentNormalizedUrl }
160
+ if (site == null ) {
161
+ appLogWrapper.e(AppLog .T .MAIN , " Site not found for URL: ${currentUrlLogin?.siteUrl} " )
162
+ viewModelScope.launch {
163
+ _onFinishedEvent .emit(
164
+ NavigationActionData (
165
+ showSiteSelector = false ,
166
+ showPostSignupInterstitial = false ,
167
+ siteUrl = currentUrlLogin?.siteUrl,
168
+ oldSitesIDs = oldSitesIDs,
169
+ isError = true
170
+ )
171
+ )
94
172
}
95
- } catch (e: Exception ) {
96
- Log .e(TAG , " Error storing credentials" , e)
97
- false
173
+ } else {
174
+ dispatcher.dispatch(SiteActionBuilder .newFetchProfileXmlRpcAction(site))
98
175
}
99
176
}
177
+
178
+ @SuppressWarnings(" unused" )
179
+ @Subscribe(threadMode = ThreadMode .BACKGROUND )
180
+ fun onProfileFetched (event : OnProfileFetched ) {
181
+ viewModelScope.launch {
182
+ _onFinishedEvent .emit(
183
+ NavigationActionData (
184
+ showSiteSelector = siteStore.hasSite(),
185
+ showPostSignupInterstitial = ! siteStore.hasSite()
186
+ && appPrefsWrapper.shouldShowPostSignupInterstitial,
187
+ siteUrl = event.site.url,
188
+ oldSitesIDs = oldSitesIDs,
189
+ isError = false
190
+ )
191
+ )
192
+ }
193
+ }
194
+
195
+ data class NavigationActionData (
196
+ val showSiteSelector : Boolean ,
197
+ val showPostSignupInterstitial : Boolean ,
198
+ val siteUrl : String? ,
199
+ val oldSitesIDs : ArrayList <Int >? ,
200
+ val isError : Boolean
201
+ )
100
202
}
0 commit comments