From 9b5164fb2810a0df8b52d167342a4464d486bba2 Mon Sep 17 00:00:00 2001 From: Nick Bradbury Date: Fri, 2 Aug 2024 09:30:50 -0400 Subject: [PATCH 01/12] Added attachment IDs --- .../org/wordpress/android/support/ZendeskHelper.kt | 2 ++ .../ui/main/feedbackform/FeedbackFormAttachment.kt | 12 +----------- .../ui/main/feedbackform/FeedbackFormViewModel.kt | 3 +++ 3 files changed, 6 insertions(+), 11 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/support/ZendeskHelper.kt b/WordPress/src/main/java/org/wordpress/android/support/ZendeskHelper.kt index ae69ad25270c..9da9c9cb52c4 100644 --- a/WordPress/src/main/java/org/wordpress/android/support/ZendeskHelper.kt +++ b/WordPress/src/main/java/org/wordpress/android/support/ZendeskHelper.kt @@ -172,6 +172,7 @@ class ZendeskHelper( origin: Origin?, selectedSite: SiteModel?, extraTags: List?, + attachmentIds: List?, requestDescription: String, callback: CreateRequestCallback ) { @@ -186,6 +187,7 @@ class ZendeskHelper( ticketFormId = TicketFieldIds.form tags = buildZendeskTags(siteStore.sites, selectedSite, origin ?: Origin.UNKNOWN, extraTags) .plus("DocsBot") + attachments = attachmentIds } Support.INSTANCE.provider()?.requestProvider()?.createRequest(request, object : ZendeskCallback() { diff --git a/WordPress/src/main/java/org/wordpress/android/ui/main/feedbackform/FeedbackFormAttachment.kt b/WordPress/src/main/java/org/wordpress/android/ui/main/feedbackform/FeedbackFormAttachment.kt index 1dddd517f914..73def4f6176d 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/main/feedbackform/FeedbackFormAttachment.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/main/feedbackform/FeedbackFormAttachment.kt @@ -10,20 +10,10 @@ data class FeedbackFormAttachment( val mimeType: String, val attachmentType: FeedbackFormAttachmentType, val size: Long, + val zendeskId: String? = null ) enum class FeedbackFormAttachmentType { IMAGE, VIDEO, } - -/** - * TODO - * -fun FeedbackFormAttachment.toZenDeskAttachment(): SupportNetworkService.ZenDeskSupportTicket.Attachment { - return SupportNetworkService.ZenDeskSupportTicket.Attachment( - file = this.tempFile, - type = this.mimeType - ) -} -*/ diff --git a/WordPress/src/main/java/org/wordpress/android/ui/main/feedbackform/FeedbackFormViewModel.kt b/WordPress/src/main/java/org/wordpress/android/ui/main/feedbackform/FeedbackFormViewModel.kt index 8d0b8d0ea40b..995ee6400e0a 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/main/feedbackform/FeedbackFormViewModel.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/main/feedbackform/FeedbackFormViewModel.kt @@ -92,6 +92,9 @@ class FeedbackFormViewModel @Inject constructor( selectedSite = selectedSiteRepository.getSelectedSite(), extraTags = listOf("in_app_feedback"), requestDescription = _messageText.value, + attachmentIds = _attachments.value.filter { !it.zendeskId.isNullOrEmpty() }.map { + it.zendeskId!! + }, callback = callback ) } From a624caaf62726a81a5c5cff56feb60e6a6e9d797 Mon Sep 17 00:00:00 2001 From: Nick Bradbury Date: Fri, 2 Aug 2024 10:32:28 -0400 Subject: [PATCH 02/12] First pass at ZendeskUploadHelper --- .../android/support/ZendeskUploadHelper.kt | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 WordPress/src/main/java/org/wordpress/android/support/ZendeskUploadHelper.kt diff --git a/WordPress/src/main/java/org/wordpress/android/support/ZendeskUploadHelper.kt b/WordPress/src/main/java/org/wordpress/android/support/ZendeskUploadHelper.kt new file mode 100644 index 000000000000..a19338fb73b2 --- /dev/null +++ b/WordPress/src/main/java/org/wordpress/android/support/ZendeskUploadHelper.kt @@ -0,0 +1,58 @@ +package org.wordpress.android.support + +import android.content.Context +import android.net.Uri +import com.zendesk.service.ErrorResponse +import com.zendesk.service.ZendeskCallback +import org.wordpress.android.util.AppLog +import org.wordpress.android.util.AppLog.T +import org.wordpress.android.util.extensions.fileName +import org.wordpress.android.util.extensions.mimeType +import zendesk.support.Support +import zendesk.support.UploadResponse +import java.io.File + +class ZendeskUploadHelper { + suspend fun uploadFile( + context: Context, + uri: Uri, + ): zendesk.support.Attachment? { + val uploadProvider = Support.INSTANCE.provider()?.uploadProvider() + if (uploadProvider == null) { + AppLog.e(AppLog.T.SUPPORT, "Upload provider is null") + return null + } + + val file = File(uri.fileName(context)) + val mimeType = uri.mimeType(context) + + uploadProvider.uploadAttachment( + file.name, + file, + mimeType, + object : ZendeskCallback() { + override fun onSuccess(result: UploadResponse) { + // return result.attachment + } + + override fun onError(errorResponse: ErrorResponse?) { + AppLog.v( + T.SUPPORT, "Uploading to Zendesk failed with" + + " error: ${errorResponse?.reason}" + ) + } + }) + + return null + } + + suspend fun uploadFiles( + context: Context, + uris: List, + ): zendesk.support.Attachment? { + uris.forEach { + uploadFile(context, it) + } + return null + } +} From 4d42520f55ad8555f6b4a73ad2a4080b87fcb726 Mon Sep 17 00:00:00 2001 From: Nick Bradbury Date: Mon, 5 Aug 2024 08:45:00 -0400 Subject: [PATCH 03/12] Fixed warnings --- .../android/support/ZendeskUploadHelper.kt | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/support/ZendeskUploadHelper.kt b/WordPress/src/main/java/org/wordpress/android/support/ZendeskUploadHelper.kt index a19338fb73b2..6aa507c19b4d 100644 --- a/WordPress/src/main/java/org/wordpress/android/support/ZendeskUploadHelper.kt +++ b/WordPress/src/main/java/org/wordpress/android/support/ZendeskUploadHelper.kt @@ -12,24 +12,30 @@ import zendesk.support.Support import zendesk.support.UploadResponse import java.io.File +/** + * https://zendesk.github.io/mobile_sdk_javadocs/supportv2/v301/index.html?zendesk/support/UploadProvider.html + */ class ZendeskUploadHelper { - suspend fun uploadFile( + fun uploadFile( context: Context, uri: Uri, ): zendesk.support.Attachment? { val uploadProvider = Support.INSTANCE.provider()?.uploadProvider() if (uploadProvider == null) { - AppLog.e(AppLog.T.SUPPORT, "Upload provider is null") + AppLog.e(T.SUPPORT, "Upload provider is null") return null } - val file = File(uri.fileName(context)) - val mimeType = uri.mimeType(context) + val file = uri.fileName(context)?.let { File(it) } + if (file == null) { + AppLog.e(T.SUPPORT, "Upload file is null") + return null + } uploadProvider.uploadAttachment( file.name, file, - mimeType, + uri.mimeType(context), object : ZendeskCallback() { override fun onSuccess(result: UploadResponse) { // return result.attachment @@ -46,7 +52,7 @@ class ZendeskUploadHelper { return null } - suspend fun uploadFiles( + fun uploadFiles( context: Context, uris: List, ): zendesk.support.Attachment? { From 398c6a6e2e2d40af879a238fb85cfde2b2744098 Mon Sep 17 00:00:00 2001 From: Nick Bradbury Date: Mon, 5 Aug 2024 08:55:39 -0400 Subject: [PATCH 04/12] Make attachmentIds optional --- .../main/java/org/wordpress/android/support/ZendeskHelper.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/support/ZendeskHelper.kt b/WordPress/src/main/java/org/wordpress/android/support/ZendeskHelper.kt index 9da9c9cb52c4..2e0408e78eeb 100644 --- a/WordPress/src/main/java/org/wordpress/android/support/ZendeskHelper.kt +++ b/WordPress/src/main/java/org/wordpress/android/support/ZendeskHelper.kt @@ -172,9 +172,9 @@ class ZendeskHelper( origin: Origin?, selectedSite: SiteModel?, extraTags: List?, - attachmentIds: List?, requestDescription: String, - callback: CreateRequestCallback + callback: CreateRequestCallback, + attachmentIds: List = emptyList() ) { require(isZendeskEnabled) { zendeskNeedsToBeEnabledError From be10c4f60fc3636d624cd0e3d9277fda8d8d828d Mon Sep 17 00:00:00 2001 From: Nick Bradbury Date: Mon, 5 Aug 2024 11:47:00 -0400 Subject: [PATCH 05/12] Upload multiple attachments --- .../android/support/ZendeskUploadHelper.kt | 45 ++++++++++++------- .../feedbackform/FeedbackFormViewModel.kt | 12 +++++ 2 files changed, 40 insertions(+), 17 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/support/ZendeskUploadHelper.kt b/WordPress/src/main/java/org/wordpress/android/support/ZendeskUploadHelper.kt index 6aa507c19b4d..d40eec6cd3eb 100644 --- a/WordPress/src/main/java/org/wordpress/android/support/ZendeskUploadHelper.kt +++ b/WordPress/src/main/java/org/wordpress/android/support/ZendeskUploadHelper.kt @@ -4,6 +4,9 @@ import android.content.Context import android.net.Uri import com.zendesk.service.ErrorResponse import com.zendesk.service.ZendeskCallback +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch import org.wordpress.android.util.AppLog import org.wordpress.android.util.AppLog.T import org.wordpress.android.util.extensions.fileName @@ -11,14 +14,16 @@ import org.wordpress.android.util.extensions.mimeType import zendesk.support.Support import zendesk.support.UploadResponse import java.io.File +import javax.inject.Inject /** * https://zendesk.github.io/mobile_sdk_javadocs/supportv2/v301/index.html?zendesk/support/UploadProvider.html */ -class ZendeskUploadHelper { - fun uploadFile( +class ZendeskUploadHelper @Inject constructor() { + private fun uploadAttachment( context: Context, uri: Uri, + callback: ZendeskCallback, ): zendesk.support.Attachment? { val uploadProvider = Support.INSTANCE.provider()?.uploadProvider() if (uploadProvider == null) { @@ -36,28 +41,34 @@ class ZendeskUploadHelper { file.name, file, uri.mimeType(context), - object : ZendeskCallback() { - override fun onSuccess(result: UploadResponse) { - // return result.attachment - } - - override fun onError(errorResponse: ErrorResponse?) { - AppLog.v( - T.SUPPORT, "Uploading to Zendesk failed with" + - " error: ${errorResponse?.reason}" - ) - } - }) + callback + ) return null } - fun uploadFiles( + suspend fun uploadAttachments( context: Context, + scope: CoroutineScope, uris: List, ): zendesk.support.Attachment? { - uris.forEach { - uploadFile(context, it) + val callback = object : ZendeskCallback() { + override fun onSuccess(result: UploadResponse) { + // return result.attachment + } + + override fun onError(errorResponse: ErrorResponse?) { + AppLog.v( + T.SUPPORT, "Uploading to Zendesk failed with" + + " error: ${errorResponse?.reason}" + ) + } + } + uris.forEach { uri -> + val job = scope.launch(context = Dispatchers.Default) { + uploadAttachment(context, uri, callback) + } + job.join() } return null } diff --git a/WordPress/src/main/java/org/wordpress/android/ui/main/feedbackform/FeedbackFormViewModel.kt b/WordPress/src/main/java/org/wordpress/android/ui/main/feedbackform/FeedbackFormViewModel.kt index 995ee6400e0a..14b5923b9302 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/main/feedbackform/FeedbackFormViewModel.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/main/feedbackform/FeedbackFormViewModel.kt @@ -16,6 +16,7 @@ import org.wordpress.android.R import org.wordpress.android.fluxc.utils.AppLogWrapper import org.wordpress.android.modules.UI_THREAD import org.wordpress.android.support.ZendeskHelper +import org.wordpress.android.support.ZendeskUploadHelper import org.wordpress.android.ui.accounts.HelpActivity import org.wordpress.android.ui.media.MediaBrowserType import org.wordpress.android.ui.mysite.SelectedSiteRepository @@ -36,6 +37,7 @@ import javax.inject.Named class FeedbackFormViewModel @Inject constructor( @Named(UI_THREAD) mainDispatcher: CoroutineDispatcher, private val zendeskHelper: ZendeskHelper, + private val zendeskUploadHelper: ZendeskUploadHelper, private val selectedSiteRepository: SelectedSiteRepository, private val appLogWrapper: AppLogWrapper, private val toastUtilsWrapper: ToastUtilsWrapper, @@ -66,6 +68,16 @@ class FeedbackFormViewModel @Inject constructor( // identity if it hasn't been previously set zendeskHelper.createAnonymousIdentityIfNeeded() + if (_attachments.value.isNotEmpty()) { + launch { + zendeskUploadHelper.uploadAttachments( + context = context, + scope = viewModelScope, + uris = _attachments.value.map { it.uri } + ) + } + } + _isProgressShowing.value = true createZendeskFeedbackRequest( context = context, From 4dc33aba46f06aae29801216d0c5137e54376435 Mon Sep 17 00:00:00 2001 From: Nick Bradbury Date: Mon, 5 Aug 2024 12:13:34 -0400 Subject: [PATCH 06/12] Pass attachment IDs when creating request --- .../android/support/ZendeskUploadHelper.kt | 19 +++++++++++-------- .../feedbackform/FeedbackFormViewModel.kt | 10 ++++++---- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/support/ZendeskUploadHelper.kt b/WordPress/src/main/java/org/wordpress/android/support/ZendeskUploadHelper.kt index d40eec6cd3eb..5e014d2a60f5 100644 --- a/WordPress/src/main/java/org/wordpress/android/support/ZendeskUploadHelper.kt +++ b/WordPress/src/main/java/org/wordpress/android/support/ZendeskUploadHelper.kt @@ -24,17 +24,17 @@ class ZendeskUploadHelper @Inject constructor() { context: Context, uri: Uri, callback: ZendeskCallback, - ): zendesk.support.Attachment? { + ) { val uploadProvider = Support.INSTANCE.provider()?.uploadProvider() if (uploadProvider == null) { AppLog.e(T.SUPPORT, "Upload provider is null") - return null + return } val file = uri.fileName(context)?.let { File(it) } if (file == null) { AppLog.e(T.SUPPORT, "Upload file is null") - return null + return } uploadProvider.uploadAttachment( @@ -43,18 +43,20 @@ class ZendeskUploadHelper @Inject constructor() { uri.mimeType(context), callback ) - - return null } suspend fun uploadAttachments( context: Context, scope: CoroutineScope, uris: List, - ): zendesk.support.Attachment? { + ): List { + val attachments = mutableListOf() + val callback = object : ZendeskCallback() { override fun onSuccess(result: UploadResponse) { - // return result.attachment + result.attachment?.let { + attachments.add(it) + } } override fun onError(errorResponse: ErrorResponse?) { @@ -70,6 +72,7 @@ class ZendeskUploadHelper @Inject constructor() { } job.join() } - return null + + return attachments } } diff --git a/WordPress/src/main/java/org/wordpress/android/ui/main/feedbackform/FeedbackFormViewModel.kt b/WordPress/src/main/java/org/wordpress/android/ui/main/feedbackform/FeedbackFormViewModel.kt index 14b5923b9302..4ece58debcbb 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/main/feedbackform/FeedbackFormViewModel.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/main/feedbackform/FeedbackFormViewModel.kt @@ -68,19 +68,22 @@ class FeedbackFormViewModel @Inject constructor( // identity if it hasn't been previously set zendeskHelper.createAnonymousIdentityIfNeeded() + val zendeskAttachments = ArrayList() if (_attachments.value.isNotEmpty()) { launch { - zendeskUploadHelper.uploadAttachments( + zendeskAttachments.addAll(zendeskUploadHelper.uploadAttachments( context = context, scope = viewModelScope, uris = _attachments.value.map { it.uri } ) + ) } } _isProgressShowing.value = true createZendeskFeedbackRequest( context = context, + attachmentIds = zendeskAttachments.map { it.id.toString() }, callback = object : ZendeskHelper.CreateRequestCallback() { override fun onSuccess() { _isProgressShowing.value = false @@ -96,6 +99,7 @@ class FeedbackFormViewModel @Inject constructor( private fun createZendeskFeedbackRequest( context: Context, + attachmentIds: List, callback: ZendeskHelper.CreateRequestCallback ) { zendeskHelper.createRequest( @@ -104,9 +108,7 @@ class FeedbackFormViewModel @Inject constructor( selectedSite = selectedSiteRepository.getSelectedSite(), extraTags = listOf("in_app_feedback"), requestDescription = _messageText.value, - attachmentIds = _attachments.value.filter { !it.zendeskId.isNullOrEmpty() }.map { - it.zendeskId!! - }, + attachmentIds = attachmentIds, callback = callback ) } From 5bd4a3cb4c2500e5ddb26ce2f24694b792417866 Mon Sep 17 00:00:00 2001 From: Nick Bradbury Date: Tue, 6 Aug 2024 07:28:24 -0400 Subject: [PATCH 07/12] Moved uploading attachments to the view model --- .../android/support/ZendeskUploadHelper.kt | 37 +--------------- .../feedbackform/FeedbackFormViewModel.kt | 44 ++++++++++++++++--- 2 files changed, 39 insertions(+), 42 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/support/ZendeskUploadHelper.kt b/WordPress/src/main/java/org/wordpress/android/support/ZendeskUploadHelper.kt index 5e014d2a60f5..473f49b7197e 100644 --- a/WordPress/src/main/java/org/wordpress/android/support/ZendeskUploadHelper.kt +++ b/WordPress/src/main/java/org/wordpress/android/support/ZendeskUploadHelper.kt @@ -2,11 +2,7 @@ package org.wordpress.android.support import android.content.Context import android.net.Uri -import com.zendesk.service.ErrorResponse import com.zendesk.service.ZendeskCallback -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch import org.wordpress.android.util.AppLog import org.wordpress.android.util.AppLog.T import org.wordpress.android.util.extensions.fileName @@ -20,7 +16,7 @@ import javax.inject.Inject * https://zendesk.github.io/mobile_sdk_javadocs/supportv2/v301/index.html?zendesk/support/UploadProvider.html */ class ZendeskUploadHelper @Inject constructor() { - private fun uploadAttachment( + fun uploadAttachment( context: Context, uri: Uri, callback: ZendeskCallback, @@ -44,35 +40,4 @@ class ZendeskUploadHelper @Inject constructor() { callback ) } - - suspend fun uploadAttachments( - context: Context, - scope: CoroutineScope, - uris: List, - ): List { - val attachments = mutableListOf() - - val callback = object : ZendeskCallback() { - override fun onSuccess(result: UploadResponse) { - result.attachment?.let { - attachments.add(it) - } - } - - override fun onError(errorResponse: ErrorResponse?) { - AppLog.v( - T.SUPPORT, "Uploading to Zendesk failed with" + - " error: ${errorResponse?.reason}" - ) - } - } - uris.forEach { uri -> - val job = scope.launch(context = Dispatchers.Default) { - uploadAttachment(context, uri, callback) - } - job.join() - } - - return attachments - } } diff --git a/WordPress/src/main/java/org/wordpress/android/ui/main/feedbackform/FeedbackFormViewModel.kt b/WordPress/src/main/java/org/wordpress/android/ui/main/feedbackform/FeedbackFormViewModel.kt index 4ece58debcbb..1ce062908179 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/main/feedbackform/FeedbackFormViewModel.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/main/feedbackform/FeedbackFormViewModel.kt @@ -7,8 +7,11 @@ import android.net.Uri import androidx.annotation.StringRes import androidx.lifecycle.viewModelScope import com.google.android.material.dialog.MaterialAlertDialogBuilder +import com.zendesk.service.ErrorResponse +import com.zendesk.service.ZendeskCallback import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.launch @@ -23,6 +26,7 @@ import org.wordpress.android.ui.mysite.SelectedSiteRepository import org.wordpress.android.ui.photopicker.MediaPickerConstants import org.wordpress.android.ui.photopicker.MediaPickerLauncher import org.wordpress.android.util.AppLog +import org.wordpress.android.util.AppLog.T import org.wordpress.android.util.NetworkUtils import org.wordpress.android.util.ToastUtilsWrapper import org.wordpress.android.util.extensions.copyToTempFile @@ -30,6 +34,7 @@ import org.wordpress.android.util.extensions.fileSize import org.wordpress.android.util.extensions.mimeType import org.wordpress.android.util.extensions.sizeFmt import org.wordpress.android.viewmodel.ScopedViewModel +import zendesk.support.UploadResponse import javax.inject.Inject import javax.inject.Named @@ -71,11 +76,8 @@ class FeedbackFormViewModel @Inject constructor( val zendeskAttachments = ArrayList() if (_attachments.value.isNotEmpty()) { launch { - zendeskAttachments.addAll(zendeskUploadHelper.uploadAttachments( - context = context, - scope = viewModelScope, - uris = _attachments.value.map { it.uri } - ) + zendeskAttachments.addAll( + uploadAttachments(context) ) } } @@ -141,7 +143,7 @@ class FeedbackFormViewModel @Inject constructor( } private fun onFailure(errorMessage: String? = null) { - appLogWrapper.e(AppLog.T.SUPPORT, "Failed to submit feedback form: $errorMessage") + appLogWrapper.e(T.SUPPORT, "Failed to submit feedback form: $errorMessage") showToast(R.string.feedback_form_failure) } @@ -225,6 +227,36 @@ class FeedbackFormViewModel @Inject constructor( } } + private suspend fun uploadAttachments( + context: Context, + ): List { + val uploadedAttachments = mutableListOf() + val uris = _attachments.value.map { it.uri } + + val callback = object : ZendeskCallback() { + override fun onSuccess(result: UploadResponse) { + result.attachment?.let { + uploadedAttachments.add(it) + } + } + + override fun onError(errorResponse: ErrorResponse?) { + AppLog.v( + T.SUPPORT, "Uploading to Zendesk failed with" + + " error: ${errorResponse?.reason}" + ) + } + } + uris.forEach { uri -> + val job = viewModelScope.launch(context = Dispatchers.Default) { + zendeskUploadHelper.uploadAttachment(context, uri, callback) + } + job.join() + } + + return uploadedAttachments + } + companion object { private const val MAX_SINGLE_ATTACHMENT_SIZE = 50000000 private const val MAX_TOTAL_ATTACHMENT_SIZE = MAX_SINGLE_ATTACHMENT_SIZE * 3 From d1f9d5ed8db43f556ae8ab87da66462926efbcc6 Mon Sep 17 00:00:00 2001 From: Nick Bradbury Date: Tue, 6 Aug 2024 11:45:47 -0400 Subject: [PATCH 08/12] Updated to use attachment tokens --- .../android/support/ZendeskHelper.kt | 4 +-- .../android/support/ZendeskUploadHelper.kt | 26 +++++++++++++++++++ .../feedbackform/FeedbackFormViewModel.kt | 25 +++++++++--------- 3 files changed, 41 insertions(+), 14 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/support/ZendeskHelper.kt b/WordPress/src/main/java/org/wordpress/android/support/ZendeskHelper.kt index 2e0408e78eeb..47a361ce22e9 100644 --- a/WordPress/src/main/java/org/wordpress/android/support/ZendeskHelper.kt +++ b/WordPress/src/main/java/org/wordpress/android/support/ZendeskHelper.kt @@ -174,7 +174,7 @@ class ZendeskHelper( extraTags: List?, requestDescription: String, callback: CreateRequestCallback, - attachmentIds: List = emptyList() + attachmentTokens: List = emptyList() ) { require(isZendeskEnabled) { zendeskNeedsToBeEnabledError @@ -187,7 +187,7 @@ class ZendeskHelper( ticketFormId = TicketFieldIds.form tags = buildZendeskTags(siteStore.sites, selectedSite, origin ?: Origin.UNKNOWN, extraTags) .plus("DocsBot") - attachments = attachmentIds + attachments = attachmentTokens } Support.INSTANCE.provider()?.requestProvider()?.createRequest(request, object : ZendeskCallback() { diff --git a/WordPress/src/main/java/org/wordpress/android/support/ZendeskUploadHelper.kt b/WordPress/src/main/java/org/wordpress/android/support/ZendeskUploadHelper.kt index 473f49b7197e..c8dfb6d690c1 100644 --- a/WordPress/src/main/java/org/wordpress/android/support/ZendeskUploadHelper.kt +++ b/WordPress/src/main/java/org/wordpress/android/support/ZendeskUploadHelper.kt @@ -2,6 +2,7 @@ package org.wordpress.android.support import android.content.Context import android.net.Uri +import com.zendesk.service.ErrorResponse import com.zendesk.service.ZendeskCallback import org.wordpress.android.util.AppLog import org.wordpress.android.util.AppLog.T @@ -16,6 +17,9 @@ import javax.inject.Inject * https://zendesk.github.io/mobile_sdk_javadocs/supportv2/v301/index.html?zendesk/support/UploadProvider.html */ class ZendeskUploadHelper @Inject constructor() { + /** + * Uploads an attachment to Zendesk. Note that the UploadResponse will contain the attachment token. + */ fun uploadAttachment( context: Context, uri: Uri, @@ -40,4 +44,26 @@ class ZendeskUploadHelper @Inject constructor() { callback ) } + + /** + * Deletes an attachment from Zendesk. This is currently used only during development. + */ + @Suppress("unused") + fun deleteAttachment(token: String) { + val uploadProvider = Support.INSTANCE.provider()?.uploadProvider() + if (uploadProvider == null) { + AppLog.e(T.SUPPORT, "Upload provider is null") + return + } + uploadProvider.deleteAttachment(token, object : ZendeskCallback() { + override fun onSuccess(result: Void?) { + AppLog.i(T.SUPPORT, "Successfully deleted Zendesk attachment") + } + + override fun onError(error: ErrorResponse?) { + AppLog.e(T.SUPPORT, "Unable to delete Zendesk attachment") + } + + }) + } } diff --git a/WordPress/src/main/java/org/wordpress/android/ui/main/feedbackform/FeedbackFormViewModel.kt b/WordPress/src/main/java/org/wordpress/android/ui/main/feedbackform/FeedbackFormViewModel.kt index 1ce062908179..c01c424e6801 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/main/feedbackform/FeedbackFormViewModel.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/main/feedbackform/FeedbackFormViewModel.kt @@ -73,10 +73,10 @@ class FeedbackFormViewModel @Inject constructor( // identity if it hasn't been previously set zendeskHelper.createAnonymousIdentityIfNeeded() - val zendeskAttachments = ArrayList() + val tokens = ArrayList() if (_attachments.value.isNotEmpty()) { launch { - zendeskAttachments.addAll( + tokens.addAll( uploadAttachments(context) ) } @@ -85,7 +85,7 @@ class FeedbackFormViewModel @Inject constructor( _isProgressShowing.value = true createZendeskFeedbackRequest( context = context, - attachmentIds = zendeskAttachments.map { it.id.toString() }, + attachmentTokens = tokens, callback = object : ZendeskHelper.CreateRequestCallback() { override fun onSuccess() { _isProgressShowing.value = false @@ -101,7 +101,7 @@ class FeedbackFormViewModel @Inject constructor( private fun createZendeskFeedbackRequest( context: Context, - attachmentIds: List, + attachmentTokens: List = emptyList(), callback: ZendeskHelper.CreateRequestCallback ) { zendeskHelper.createRequest( @@ -110,7 +110,7 @@ class FeedbackFormViewModel @Inject constructor( selectedSite = selectedSiteRepository.getSelectedSite(), extraTags = listOf("in_app_feedback"), requestDescription = _messageText.value, - attachmentIds = attachmentIds, + attachmentTokens = attachmentTokens, callback = callback ) } @@ -227,17 +227,18 @@ class FeedbackFormViewModel @Inject constructor( } } + /** + * Uploads the attachments to Zendesk and returns a list of their tokens. + */ private suspend fun uploadAttachments( context: Context, - ): List { - val uploadedAttachments = mutableListOf() + ): List { + val tokens = mutableListOf() val uris = _attachments.value.map { it.uri } val callback = object : ZendeskCallback() { override fun onSuccess(result: UploadResponse) { - result.attachment?.let { - uploadedAttachments.add(it) - } + tokens.add(result.token.toString()) } override fun onError(errorResponse: ErrorResponse?) { @@ -248,13 +249,13 @@ class FeedbackFormViewModel @Inject constructor( } } uris.forEach { uri -> - val job = viewModelScope.launch(context = Dispatchers.Default) { + val job = viewModelScope.launch(Dispatchers.Default) { zendeskUploadHelper.uploadAttachment(context, uri, callback) } job.join() } - return uploadedAttachments + return tokens } companion object { From c084d4a7977c4c4b29b3f9ae17574613b14a949d Mon Sep 17 00:00:00 2001 From: Nick Bradbury Date: Tue, 6 Aug 2024 12:30:27 -0400 Subject: [PATCH 09/12] Use tempfile rather than uri for upload --- .../android/support/ZendeskUploadHelper.kt | 16 +++---------- .../feedbackform/FeedbackFormAttachment.kt | 1 - .../feedbackform/FeedbackFormViewModel.kt | 23 +++++++++++-------- 3 files changed, 17 insertions(+), 23 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/support/ZendeskUploadHelper.kt b/WordPress/src/main/java/org/wordpress/android/support/ZendeskUploadHelper.kt index c8dfb6d690c1..764372ed53db 100644 --- a/WordPress/src/main/java/org/wordpress/android/support/ZendeskUploadHelper.kt +++ b/WordPress/src/main/java/org/wordpress/android/support/ZendeskUploadHelper.kt @@ -1,13 +1,9 @@ package org.wordpress.android.support -import android.content.Context -import android.net.Uri import com.zendesk.service.ErrorResponse import com.zendesk.service.ZendeskCallback import org.wordpress.android.util.AppLog import org.wordpress.android.util.AppLog.T -import org.wordpress.android.util.extensions.fileName -import org.wordpress.android.util.extensions.mimeType import zendesk.support.Support import zendesk.support.UploadResponse import java.io.File @@ -21,8 +17,8 @@ class ZendeskUploadHelper @Inject constructor() { * Uploads an attachment to Zendesk. Note that the UploadResponse will contain the attachment token. */ fun uploadAttachment( - context: Context, - uri: Uri, + file: File, + mimeType: String, callback: ZendeskCallback, ) { val uploadProvider = Support.INSTANCE.provider()?.uploadProvider() @@ -31,16 +27,10 @@ class ZendeskUploadHelper @Inject constructor() { return } - val file = uri.fileName(context)?.let { File(it) } - if (file == null) { - AppLog.e(T.SUPPORT, "Upload file is null") - return - } - uploadProvider.uploadAttachment( file.name, file, - uri.mimeType(context), + mimeType, callback ) } diff --git a/WordPress/src/main/java/org/wordpress/android/ui/main/feedbackform/FeedbackFormAttachment.kt b/WordPress/src/main/java/org/wordpress/android/ui/main/feedbackform/FeedbackFormAttachment.kt index 73def4f6176d..13f03d0ccd20 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/main/feedbackform/FeedbackFormAttachment.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/main/feedbackform/FeedbackFormAttachment.kt @@ -10,7 +10,6 @@ data class FeedbackFormAttachment( val mimeType: String, val attachmentType: FeedbackFormAttachmentType, val size: Long, - val zendeskId: String? = null ) enum class FeedbackFormAttachmentType { diff --git a/WordPress/src/main/java/org/wordpress/android/ui/main/feedbackform/FeedbackFormViewModel.kt b/WordPress/src/main/java/org/wordpress/android/ui/main/feedbackform/FeedbackFormViewModel.kt index c01c424e6801..6f3498f034b7 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/main/feedbackform/FeedbackFormViewModel.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/main/feedbackform/FeedbackFormViewModel.kt @@ -77,12 +77,16 @@ class FeedbackFormViewModel @Inject constructor( if (_attachments.value.isNotEmpty()) { launch { tokens.addAll( - uploadAttachments(context) + uploadAttachments() ) + // TODO for now we delete the attachments (only for testing) + tokens.forEach { + zendeskUploadHelper.deleteAttachment(it) + } } } - _isProgressShowing.value = true + /*_isProgressShowing.value = true createZendeskFeedbackRequest( context = context, attachmentTokens = tokens, @@ -96,7 +100,7 @@ class FeedbackFormViewModel @Inject constructor( _isProgressShowing.value = false onFailure(errorMessage) } - }) + })*/ } private fun createZendeskFeedbackRequest( @@ -230,11 +234,8 @@ class FeedbackFormViewModel @Inject constructor( /** * Uploads the attachments to Zendesk and returns a list of their tokens. */ - private suspend fun uploadAttachments( - context: Context, - ): List { + private suspend fun uploadAttachments(): List { val tokens = mutableListOf() - val uris = _attachments.value.map { it.uri } val callback = object : ZendeskCallback() { override fun onSuccess(result: UploadResponse) { @@ -248,9 +249,13 @@ class FeedbackFormViewModel @Inject constructor( ) } } - uris.forEach { uri -> + + _attachments.value.forEach { attachment -> val job = viewModelScope.launch(Dispatchers.Default) { - zendeskUploadHelper.uploadAttachment(context, uri, callback) + zendeskUploadHelper.uploadAttachment( + file = attachment.tempFile, + mimeType = attachment.mimeType, + callback = callback) } job.join() } From c8c55af0230416d2618b7700028474293ca23e9d Mon Sep 17 00:00:00 2001 From: Nick Bradbury Date: Tue, 6 Aug 2024 13:56:20 -0400 Subject: [PATCH 10/12] Use a completion handler to await uploads --- .../android/support/ZendeskUploadHelper.kt | 1 - .../feedbackform/FeedbackFormViewModel.kt | 83 ++++++++++--------- 2 files changed, 44 insertions(+), 40 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/support/ZendeskUploadHelper.kt b/WordPress/src/main/java/org/wordpress/android/support/ZendeskUploadHelper.kt index 764372ed53db..cfa8bdadd0bb 100644 --- a/WordPress/src/main/java/org/wordpress/android/support/ZendeskUploadHelper.kt +++ b/WordPress/src/main/java/org/wordpress/android/support/ZendeskUploadHelper.kt @@ -26,7 +26,6 @@ class ZendeskUploadHelper @Inject constructor() { AppLog.e(T.SUPPORT, "Upload provider is null") return } - uploadProvider.uploadAttachment( file.name, file, diff --git a/WordPress/src/main/java/org/wordpress/android/ui/main/feedbackform/FeedbackFormViewModel.kt b/WordPress/src/main/java/org/wordpress/android/ui/main/feedbackform/FeedbackFormViewModel.kt index 6f3498f034b7..23ed3adbcb63 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/main/feedbackform/FeedbackFormViewModel.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/main/feedbackform/FeedbackFormViewModel.kt @@ -58,6 +58,8 @@ class FeedbackFormViewModel @Inject constructor( private val _attachments = MutableStateFlow>(emptyList()) val attachments = _attachments.asStateFlow() + private val attachmentTokens = ArrayList() + fun updateMessageText(message: String) { if (message != _messageText.value) { _messageText.value = message @@ -73,41 +75,22 @@ class FeedbackFormViewModel @Inject constructor( // identity if it hasn't been previously set zendeskHelper.createAnonymousIdentityIfNeeded() - val tokens = ArrayList() + // if there are attachments, upload them first then create the feedback request when they're all uploaded. + // using a completion handler isn't ideal but it's done since Zendesk only provides uploading using callbacks. if (_attachments.value.isNotEmpty()) { - launch { - tokens.addAll( - uploadAttachments() - ) - // TODO for now we delete the attachments (only for testing) - tokens.forEach { - zendeskUploadHelper.deleteAttachment(it) - } - } + uploadAttachments( + completionHandler = { createZendeskFeedbackRequest(context) } + ) + } else { + createZendeskFeedbackRequest(context) } - - /*_isProgressShowing.value = true - createZendeskFeedbackRequest( - context = context, - attachmentTokens = tokens, - callback = object : ZendeskHelper.CreateRequestCallback() { - override fun onSuccess() { - _isProgressShowing.value = false - onSuccess(context) - } - - override fun onError(errorMessage: String?) { - _isProgressShowing.value = false - onFailure(errorMessage) - } - })*/ } private fun createZendeskFeedbackRequest( context: Context, - attachmentTokens: List = emptyList(), - callback: ZendeskHelper.CreateRequestCallback ) { + _isProgressShowing.value = true + zendeskHelper.createRequest( context = context, origin = HelpActivity.Origin.FEEDBACK_FORM, @@ -115,8 +98,17 @@ class FeedbackFormViewModel @Inject constructor( extraTags = listOf("in_app_feedback"), requestDescription = _messageText.value, attachmentTokens = attachmentTokens, - callback = callback - ) + callback = object : ZendeskHelper.CreateRequestCallback() { + override fun onSuccess() { + _isProgressShowing.value = false + onSuccess(context) + } + + override fun onError(errorMessage: String?) { + _isProgressShowing.value = false + onFailure(errorMessage) + } + }) } fun onCloseClick(context: Context) { @@ -232,35 +224,48 @@ class FeedbackFormViewModel @Inject constructor( } /** - * Uploads the attachments to Zendesk and returns a list of their tokens. + * Uploads the attachments to Zendesk */ - private suspend fun uploadAttachments(): List { - val tokens = mutableListOf() + private fun uploadAttachments( + completionHandler: () -> Unit + ) { + attachmentTokens.clear() + var numAttachments = _attachments.value.size + + fun decAttachments() { + numAttachments-- + if (numAttachments == 0) { + completionHandler() + } + } val callback = object : ZendeskCallback() { override fun onSuccess(result: UploadResponse) { - tokens.add(result.token.toString()) + result.token?.let { + attachmentTokens.add(it) + } + decAttachments() } override fun onError(errorResponse: ErrorResponse?) { - AppLog.v( + AppLog.e( T.SUPPORT, "Uploading to Zendesk failed with" + " error: ${errorResponse?.reason}" ) + decAttachments() } } _attachments.value.forEach { attachment -> - val job = viewModelScope.launch(Dispatchers.Default) { + viewModelScope.launch(Dispatchers.Default) { zendeskUploadHelper.uploadAttachment( file = attachment.tempFile, mimeType = attachment.mimeType, - callback = callback) + callback = callback + ) } - job.join() } - return tokens } companion object { From 9bbb3a88dd7871443445199fed22daebf3e129e7 Mon Sep 17 00:00:00 2001 From: Nick Bradbury Date: Tue, 6 Aug 2024 14:03:20 -0400 Subject: [PATCH 11/12] Minor cleanup --- .../feedbackform/FeedbackFormViewModel.kt | 20 +++++++++---------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/main/feedbackform/FeedbackFormViewModel.kt b/WordPress/src/main/java/org/wordpress/android/ui/main/feedbackform/FeedbackFormViewModel.kt index 23ed3adbcb63..b8f1b6c273d2 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/main/feedbackform/FeedbackFormViewModel.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/main/feedbackform/FeedbackFormViewModel.kt @@ -11,7 +11,6 @@ import com.zendesk.service.ErrorResponse import com.zendesk.service.ZendeskCallback import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.CoroutineDispatcher -import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.launch @@ -234,7 +233,8 @@ class FeedbackFormViewModel @Inject constructor( fun decAttachments() { numAttachments-- - if (numAttachments == 0) { + if (numAttachments <= 0) { + _isProgressShowing.value = false completionHandler() } } @@ -249,21 +249,19 @@ class FeedbackFormViewModel @Inject constructor( override fun onError(errorResponse: ErrorResponse?) { AppLog.e( - T.SUPPORT, "Uploading to Zendesk failed with" + - " error: ${errorResponse?.reason}" + T.SUPPORT, "Uploading to Zendesk failed with ${errorResponse?.reason}" ) decAttachments() } } + _isProgressShowing.value = true _attachments.value.forEach { attachment -> - viewModelScope.launch(Dispatchers.Default) { - zendeskUploadHelper.uploadAttachment( - file = attachment.tempFile, - mimeType = attachment.mimeType, - callback = callback - ) - } + zendeskUploadHelper.uploadAttachment( + file = attachment.tempFile, + mimeType = attachment.mimeType, + callback = callback + ) } } From 765f77f3e84ef2c57b5dd6e2241d40a9f0558e63 Mon Sep 17 00:00:00 2001 From: Nick Bradbury Date: Tue, 6 Aug 2024 14:23:12 -0400 Subject: [PATCH 12/12] Fixed checkstyle warnings --- .../java/org/wordpress/android/support/ZendeskUploadHelper.kt | 3 +-- .../android/ui/main/feedbackform/FeedbackFormViewModel.kt | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/support/ZendeskUploadHelper.kt b/WordPress/src/main/java/org/wordpress/android/support/ZendeskUploadHelper.kt index cfa8bdadd0bb..d7b9c8ec49d7 100644 --- a/WordPress/src/main/java/org/wordpress/android/support/ZendeskUploadHelper.kt +++ b/WordPress/src/main/java/org/wordpress/android/support/ZendeskUploadHelper.kt @@ -45,14 +45,13 @@ class ZendeskUploadHelper @Inject constructor() { return } uploadProvider.deleteAttachment(token, object : ZendeskCallback() { - override fun onSuccess(result: Void?) { + override fun onSuccess(result: Void) { AppLog.i(T.SUPPORT, "Successfully deleted Zendesk attachment") } override fun onError(error: ErrorResponse?) { AppLog.e(T.SUPPORT, "Unable to delete Zendesk attachment") } - }) } } diff --git a/WordPress/src/main/java/org/wordpress/android/ui/main/feedbackform/FeedbackFormViewModel.kt b/WordPress/src/main/java/org/wordpress/android/ui/main/feedbackform/FeedbackFormViewModel.kt index b8f1b6c273d2..c77a35dc1118 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/main/feedbackform/FeedbackFormViewModel.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/main/feedbackform/FeedbackFormViewModel.kt @@ -263,7 +263,6 @@ class FeedbackFormViewModel @Inject constructor( callback = callback ) } - } companion object {