@@ -4,16 +4,24 @@ import kotlinx.coroutines.CoroutineScope
4
4
import kotlinx.coroutines.launch
5
5
import org.wordpress.android.fluxc.Dispatcher
6
6
import org.wordpress.android.fluxc.generated.MediaActionBuilder
7
+ import org.wordpress.android.fluxc.generated.UploadActionBuilder
7
8
import org.wordpress.android.fluxc.model.MediaModel
9
+ import org.wordpress.android.fluxc.model.MediaModel.MediaUploadState
8
10
import org.wordpress.android.fluxc.model.SiteModel
9
11
import org.wordpress.android.fluxc.module.FLUXC_SCOPE
10
12
import org.wordpress.android.fluxc.network.rest.wpapi.applicationpasswords.WpAppNotifierHandler
11
13
import org.wordpress.android.fluxc.store.MediaStore.FetchMediaListResponsePayload
14
+ import org.wordpress.android.fluxc.store.MediaStore.MediaError
15
+ import org.wordpress.android.fluxc.store.MediaStore.MediaErrorType
16
+ import org.wordpress.android.fluxc.store.MediaStore.MediaPayload
17
+ import org.wordpress.android.fluxc.store.MediaStore.ProgressPayload
12
18
import org.wordpress.android.fluxc.utils.AppLogWrapper
19
+ import org.wordpress.android.fluxc.utils.MediaUtils
13
20
import org.wordpress.android.fluxc.utils.MimeType
14
21
import org.wordpress.android.util.AppLog
15
22
import rs.wordpress.api.kotlin.WpApiClient
16
23
import rs.wordpress.api.kotlin.WpRequestResult
24
+ import uniffi.wp_api.MediaCreateParams
17
25
import uniffi.wp_api.MediaDetailsPayload
18
26
import uniffi.wp_api.MediaListParams
19
27
import uniffi.wp_api.MediaWithEditContext
@@ -36,19 +44,7 @@ class MediaRSApiRestClient @Inject constructor(
36
44
) {
37
45
fun fetchMediaList (site : SiteModel , number : Int , offset : Int , mimeType : MimeType .Type ? ) {
38
46
scope.launch {
39
- val authProvider = WpAuthenticationProvider .staticWithUsernameAndPassword(
40
- username = site.apiRestUsernamePlain, password = site.apiRestPasswordPlain
41
- )
42
- val apiRootUrl = URL (site.buildUrl())
43
- val client = WpApiClient (
44
- wpOrgSiteApiRootUrl = apiRootUrl,
45
- authProvider = authProvider,
46
- appNotifier = object : WpAppNotifier {
47
- override suspend fun requestedWithInvalidAuthentication () {
48
- wpAppNotifierHandler.notifyRequestedWithInvalidAuthentication(site)
49
- }
50
- }
51
- )
47
+ val client = getWpApiClient(site)
52
48
val mediaResponse = client.request { requestBuilder ->
53
49
requestBuilder.media().listWithEditContext(
54
50
MediaListParams (
@@ -62,12 +58,12 @@ class MediaRSApiRestClient @Inject constructor(
62
58
63
59
val mediaModelList = when (mediaResponse) {
64
60
is WpRequestResult .Success -> {
65
- appLogWrapper.d(AppLog .T .MAIN , " Fetched media list: ${mediaResponse.response.data.size} " )
61
+ appLogWrapper.d(AppLog .T .MEDIA , " Fetched media list: ${mediaResponse.response.data.size} " )
66
62
mediaResponse.response.data.toMediaModelList(site.id)
67
63
}
68
64
69
65
else -> {
70
- appLogWrapper.e(AppLog .T .MAIN , " Fetch media list failed: $mediaResponse " )
66
+ appLogWrapper.e(AppLog .T .MEDIA , " Fetch media list failed: $mediaResponse " )
71
67
emptyList()
72
68
}
73
69
}
@@ -92,6 +88,207 @@ class MediaRSApiRestClient @Inject constructor(
92
88
dispatcher.dispatch(MediaActionBuilder .newFetchedMediaListAction(payload))
93
89
}
94
90
91
+ fun fetchMedia (site : SiteModel , media : MediaModel ? ) {
92
+ if (media == null ) {
93
+ val error = MediaError (MediaErrorType .NULL_MEDIA_ARG )
94
+ error.logMessage = " Requested media is null"
95
+ notifyMediaFetched(site, null , error)
96
+ return
97
+ }
98
+
99
+ scope.launch {
100
+ val client = getWpApiClient(site)
101
+
102
+ val mediaResponse = client.request { requestBuilder ->
103
+ requestBuilder.media().retrieveWithEditContext(media.mediaId)
104
+ }
105
+
106
+
107
+ when (mediaResponse) {
108
+ is WpRequestResult .Success -> {
109
+ appLogWrapper.d(AppLog .T .MEDIA , " Fetched media with ID: " + media.mediaId)
110
+
111
+ val responseMedia: MediaModel = mediaResponse.response.data.toMediaModel(site.id).apply {
112
+ localSiteId = site.id
113
+ }
114
+ notifyMediaFetched(site, responseMedia, null )
115
+ }
116
+
117
+ else -> {
118
+ val mediaError = parseMediaError(mediaResponse)
119
+ appLogWrapper.e(AppLog .T .MEDIA , " Fetch media failed: ${mediaError.message} " )
120
+ notifyMediaFetched(site, media, mediaError)
121
+ }
122
+ }
123
+ }
124
+ }
125
+
126
+ @Suppress(" UseCheckOrError" ) // Allow to throw IllegalStateException
127
+ private fun parseMediaError (mediaResponse : WpRequestResult <* >): MediaError {
128
+ return when (mediaResponse) {
129
+ is WpRequestResult .Success -> {
130
+ throw IllegalStateException (" Success media response should not be parsed as an error" )
131
+ }
132
+ is WpRequestResult .MediaFileNotFound <* > -> {
133
+ appLogWrapper.e(AppLog .T .MEDIA , " Media file not found: $mediaResponse " )
134
+ MediaError (MediaErrorType .NOT_FOUND ).apply {
135
+ message = " Media file not found"
136
+ }
137
+ }
138
+
139
+ is WpRequestResult .ResponseParsingError <* > -> {
140
+ appLogWrapper.e(AppLog .T .MEDIA , " Response parsing error: $mediaResponse " )
141
+ MediaError (MediaErrorType .PARSE_ERROR ).apply {
142
+ message = " Failed to parse response"
143
+ }
144
+ }
145
+
146
+ is WpRequestResult .SiteUrlParsingError <* > -> {
147
+ appLogWrapper.e(AppLog .T .MEDIA , " Site URL parsing error: $mediaResponse " )
148
+ MediaError (MediaErrorType .MALFORMED_MEDIA_ARG ).apply {
149
+ message = " Invalid site URL"
150
+ }
151
+ }
152
+
153
+ is WpRequestResult .InvalidHttpStatusCode <* >,
154
+ is WpRequestResult .WpError <* >,
155
+ is WpRequestResult .RequestExecutionFailed <* >,
156
+ is WpRequestResult .UnknownError <* > -> {
157
+ appLogWrapper.e(AppLog .T .MEDIA , " Unknown error: $mediaResponse " )
158
+ MediaError (MediaErrorType .GENERIC_ERROR ).apply {
159
+ message = " Unknown error occurred"
160
+ }
161
+ }
162
+ }
163
+ }
164
+
165
+ private fun notifyMediaFetched (
166
+ site : SiteModel ,
167
+ media : MediaModel ? ,
168
+ error : MediaError ?
169
+ ) {
170
+ val payload = MediaPayload (site, media, error)
171
+ dispatcher.dispatch(MediaActionBuilder .newFetchedMediaAction(payload))
172
+ }
173
+
174
+ fun deleteMedia (site : SiteModel , media : MediaModel ? ) {
175
+ if (media == null ) {
176
+ val error = MediaError (MediaErrorType .NULL_MEDIA_ARG )
177
+ error.logMessage = " Media to delete is null"
178
+ notifyMediaDeleted(site, null , error)
179
+ return
180
+ }
181
+
182
+ scope.launch {
183
+ val client = getWpApiClient(site)
184
+
185
+ val mediaResponse = client.request { requestBuilder ->
186
+ requestBuilder.media().delete(media.mediaId)
187
+ }
188
+
189
+ when (mediaResponse) {
190
+ is WpRequestResult .Success -> {
191
+ appLogWrapper.d(AppLog .T .MEDIA , " Deleted media with ID: " + media.mediaId)
192
+
193
+ val responseMedia: MediaModel = mediaResponse.response.data.previous.toMediaModel(site.id).apply {
194
+ localSiteId = site.id
195
+ }
196
+ notifyMediaDeleted(site, responseMedia, null )
197
+ }
198
+
199
+ else -> {
200
+ val mediaError = parseMediaError(mediaResponse)
201
+ appLogWrapper.e(AppLog .T .MEDIA , " Delete media failed: ${mediaError.message} " )
202
+ notifyMediaDeleted(site, media, mediaError)
203
+ }
204
+ }
205
+ }
206
+ }
207
+
208
+ private fun notifyMediaDeleted (
209
+ site : SiteModel ,
210
+ media : MediaModel ? ,
211
+ error : MediaError ?
212
+ ) {
213
+ val payload = MediaPayload (site, media, error)
214
+ dispatcher.dispatch(MediaActionBuilder .newDeletedMediaAction(payload))
215
+ }
216
+
217
+ fun uploadMedia (site : SiteModel , media : MediaModel ? ) {
218
+ if (media == null || media.id == 0 ) {
219
+ // we can't have a MediaModel without an ID - otherwise we can't keep track of them.
220
+ val error = MediaError (MediaErrorType .INVALID_ID )
221
+ if (media == null ) {
222
+ error.logMessage = " Media object is null on upload"
223
+ } else {
224
+ error.logMessage = " Media ID is 0 on upload"
225
+ }
226
+ notifyMediaUploaded(media, error)
227
+ return
228
+ }
229
+
230
+ if (media.filePath == null || ! MediaUtils .canReadFile(media.filePath)) {
231
+ val error = MediaError (MediaErrorType .FS_READ_PERMISSION_DENIED )
232
+ error.logMessage = " Can't read file on upload"
233
+ notifyMediaUploaded(media, error)
234
+ return
235
+ }
236
+
237
+ scope.launch {
238
+ val client = getWpApiClient(site)
239
+
240
+ val mediaResponse = client.request { requestBuilder ->
241
+ requestBuilder.media().create(
242
+ params = MediaCreateParams (title = media.title),
243
+ filePath = media.filePath!! , // We have already checked the nullability but it's mutable
244
+ fileContentType = media.mimeType.orEmpty(),
245
+ requestId = null
246
+ )
247
+ }
248
+
249
+ when (mediaResponse) {
250
+ is WpRequestResult .Success -> {
251
+ appLogWrapper.d(AppLog .T .MEDIA , " Uploaded media with ID: " + media.id)
252
+
253
+ val responseMedia: MediaModel = mediaResponse.response.data.toMediaModel(site.id).apply {
254
+ id = media.id // be sure we are using the same local id when getting the remote response
255
+ localSiteId = site.id
256
+ }
257
+ notifyMediaUploaded(responseMedia, null )
258
+ }
259
+
260
+ else -> {
261
+ val mediaError = parseMediaError(mediaResponse)
262
+ appLogWrapper.e(AppLog .T .MEDIA , " Upload media failed: ${mediaError.message} " )
263
+ notifyMediaUploaded(media, mediaError)
264
+ }
265
+ }
266
+ }
267
+ }
268
+
269
+ private fun notifyMediaUploaded (media : MediaModel ? , error : MediaError ? ) {
270
+ media?.setUploadState(if (error == null ) MediaUploadState .UPLOADED else MediaUploadState .FAILED )
271
+ val payload = ProgressPayload (media, 1f , error == null , error)
272
+ dispatcher.dispatch(UploadActionBuilder .newUploadedMediaAction(payload))
273
+ }
274
+
275
+ private fun getWpApiClient (site : SiteModel ): WpApiClient {
276
+ val authProvider = WpAuthenticationProvider .staticWithUsernameAndPassword(
277
+ username = site.apiRestUsernamePlain, password = site.apiRestPasswordPlain
278
+ )
279
+ val apiRootUrl = URL (site.buildUrl())
280
+ val client = WpApiClient (
281
+ wpOrgSiteApiRootUrl = apiRootUrl,
282
+ authProvider = authProvider,
283
+ appNotifier = object : WpAppNotifier {
284
+ override suspend fun requestedWithInvalidAuthentication () {
285
+ wpAppNotifierHandler.notifyRequestedWithInvalidAuthentication(site)
286
+ }
287
+ }
288
+ )
289
+ return client
290
+ }
291
+
95
292
private fun List<MediaWithEditContext>.toMediaModelList (
96
293
siteId : Int
97
294
): List <MediaModel > = map { it.toMediaModel(siteId) }
@@ -110,7 +307,7 @@ class MediaRSApiRestClient @Inject constructor(
110
307
fileExtension = this @toMediaModel.mediaType.toString()
111
308
uploadDate = this @toMediaModel.date
112
309
authorId = this @toMediaModel.author
113
- uploadState = org.wordpress.android.fluxc.model. MediaModel . MediaUploadState .UPLOADED .toString()
310
+ uploadState = MediaUploadState .UPLOADED .toString()
114
311
115
312
// Parse the media details
116
313
when (val parsedType = this @toMediaModel.mediaDetails.parseAsMimeType(this @toMediaModel.mimeType)) {
0 commit comments