diff --git a/WordPress/src/main/java/org/wordpress/android/ui/posts/EditPostActivity.java b/WordPress/src/main/java/org/wordpress/android/ui/posts/EditPostActivity.java index 982d90addf21..a4e7c787e6ab 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/posts/EditPostActivity.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/posts/EditPostActivity.java @@ -171,7 +171,6 @@ import org.wordpress.android.ui.stories.StoryRepositoryWrapper; import org.wordpress.android.ui.stories.prefs.StoriesPrefs; import org.wordpress.android.ui.stories.usecase.LoadStoryFromStoriesPrefsUseCase; -import org.wordpress.android.ui.stories.usecase.LoadStoryFromStoriesPrefsUseCase.ReCreateStoryResult; import org.wordpress.android.ui.uploads.PostEvents; import org.wordpress.android.ui.uploads.UploadService; import org.wordpress.android.ui.uploads.UploadUtils; @@ -677,7 +676,7 @@ protected void onCreate(Bundle savedInstanceState) { setupPrepublishingBottomSheetRunnable(); - mStoriesEventListener.start(this.getLifecycle(), mSite); + mStoriesEventListener.start(this.getLifecycle(), mSite, mEditPostRepository); setupPreviewUI(); } @@ -3252,110 +3251,30 @@ public void onTrackableEvent(TrackableEvent event, Map propertie } @Override public void onStoryComposerLoadRequested(ArrayList mediaFiles, String blockId) { - ReCreateStoryResult result = mLoadStoryFromStoriesPrefsUseCase - .loadStoryFromMemoryOrRecreateFromPrefs(mSite, mediaFiles); - if (!result.getNoSlidesLoaded()) { - // Story instance loaded or re-created! Load it onto the StoryComposer for editing now - ActivityLauncher.editStoryForResult( - this, - mSite, - new LocalId(mEditPostRepository.getId()), - result.getStoryIndex(), - result.getAllStorySlidesAreEditable(), - true, - blockId - ); - } else { - // unfortunately we couldn't even load the remote media Ids indicated by the StoryBlock so we can't allow - // editing at this time :( - if (mNetworkErrorOnLastMediaFetchAttempt) { - // there was an error fetching media when we were loading the editor, - // we *may* still have a possibility, tell the user they may try refreshing the media again - AlertDialog.Builder builder = new MaterialAlertDialogBuilder(this); - builder.setTitle(getString(R.string.dialog_edit_story_unavailable_title)); - builder.setMessage(getString(R.string.dialog_edit_story_unavailable_message)); - builder.setPositiveButton(R.string.dialog_button_ok, (dialog, id) -> { - // try another fetchMedia request - fetchMediaList(); - dialog.dismiss(); - }); - AlertDialog dialog = builder.create(); - dialog.show(); - } else { - // unrecoverable error, nothing we can do, inform the user :(. - AlertDialog.Builder builder = new MaterialAlertDialogBuilder(this); - builder.setTitle(getString(R.string.dialog_edit_story_unrecoverable_title)); - builder.setMessage(getString(R.string.dialog_edit_story_unrecoverable_message)); - builder.setPositiveButton(R.string.dialog_button_ok, (dialog, id) -> { - dialog.dismiss(); - }); - AlertDialog dialog = builder.create(); - dialog.show(); - } + boolean noSlidesLoaded = mStoriesEventListener.onRequestMediaFilesEditorLoad( + this, + new LocalId(mEditPostRepository.getId()), + mNetworkErrorOnLastMediaFetchAttempt, + mediaFiles, + blockId + ); + + if (mNetworkErrorOnLastMediaFetchAttempt && noSlidesLoaded) { + // try another fetchMedia request + fetchMediaList(); } } @Override public void onRetryUploadForMediaCollection(ArrayList mediaFiles) { - ArrayList mediaIdsToRetry = new ArrayList<>(); - for (Object mediaFile : mediaFiles) { - int localMediaId - = StringUtils.stringToInt(((HashMap) mediaFile).get("id").toString(), 0); - if (localMediaId != 0) { - MediaModel media = mMediaStore.getMediaWithLocalId(localMediaId); - // if we find at least one item in the mediaFiles collection passed - // for which we don't have a local MediaModel, just tell the user and bail - if (media == null) { - AppLog.e(T.MEDIA, "Can't find media with local id: " + localMediaId); - AlertDialog.Builder builder = new MaterialAlertDialogBuilder(this); - builder.setTitle(getString(R.string.cannot_retry_deleted_media_item_fatal)); - builder.setPositiveButton(R.string.yes, (dialog, id) -> { - dialog.dismiss(); - }); - - builder.setNegativeButton(getString(R.string.no), (dialog, id) -> dialog.dismiss()); - - AlertDialog dialog = builder.create(); - dialog.show(); - - return; - } - - if (media.getUrl() != null && media.getUploadState().equals(MediaUploadState.UPLOADED.toString())) { - // Note: we should actually do this when the editor fragment starts instead of waiting for user - // input. - // Notify the editor fragment upload was successful and it should replace the local url by the - // remote url. - if (mEditorMediaUploadListener != null) { - mEditorMediaUploadListener.onMediaUploadSucceeded(String.valueOf(media.getId()), - FluxCUtils.mediaFileFromMediaModel(media)); - } - } else { - UploadService.cancelFinalNotification(this, mEditPostRepository.getPost()); - UploadService.cancelFinalNotificationForMedia(this, mSite); - mediaIdsToRetry.add(localMediaId); - } - } - } - - if (!mediaIdsToRetry.isEmpty()) { - mEditorMedia.retryFailedMediaAsync(mediaIdsToRetry); - } - AnalyticsTracker.track(Stat.EDITOR_UPLOAD_MEDIA_RETRIED); + mStoriesEventListener.onRetryUploadForMediaCollection(this, mediaFiles, mEditorMediaUploadListener); } @Override public void onCancelUploadForMediaCollection(ArrayList mediaFiles) { - // just cancel upload for each media - for (Object mediaFile : mediaFiles) { - int localMediaId - = StringUtils.stringToInt(((HashMap) mediaFile).get("id").toString(), 0); - if (localMediaId != 0) { - mEditorMedia.cancelMediaUploadAsync(localMediaId, false); - } - } + mStoriesEventListener.onCancelUploadForMediaCollection(mediaFiles); } @Override public void onCancelSaveForMediaCollection(ArrayList mediaFiles) { - // TODO implement cancelling save process for media collection + mStoriesEventListener.onCancelSaveForMediaCollection(mediaFiles); } // FluxC events diff --git a/WordPress/src/main/java/org/wordpress/android/ui/posts/editor/StoriesEventListener.kt b/WordPress/src/main/java/org/wordpress/android/ui/posts/editor/StoriesEventListener.kt index 4469a26bb3a8..2732766f11db 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/posts/editor/StoriesEventListener.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/posts/editor/StoriesEventListener.kt @@ -1,10 +1,14 @@ package org.wordpress.android.ui.posts.editor +import android.app.Activity +import android.content.DialogInterface import android.net.Uri +import androidx.appcompat.app.AlertDialog.Builder import androidx.lifecycle.Lifecycle import androidx.lifecycle.Lifecycle.State.CREATED import androidx.lifecycle.LifecycleObserver import androidx.lifecycle.OnLifecycleEvent +import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.wordpress.stories.compose.frame.StorySaveEvents.FrameSaveCompleted import com.wordpress.stories.compose.frame.StorySaveEvents.FrameSaveFailed import com.wordpress.stories.compose.frame.StorySaveEvents.FrameSaveProgress @@ -12,27 +16,47 @@ import com.wordpress.stories.compose.frame.StorySaveEvents.FrameSaveStart import com.wordpress.stories.compose.frame.StorySaveEvents.StorySaveResult import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.ThreadMode +import org.wordpress.android.R +import org.wordpress.android.R.string +import org.wordpress.android.analytics.AnalyticsTracker +import org.wordpress.android.analytics.AnalyticsTracker.Stat.EDITOR_UPLOAD_MEDIA_RETRIED +import org.wordpress.android.editor.EditorMediaUploadListener import org.wordpress.android.editor.gutenberg.StorySaveMediaListener import org.wordpress.android.fluxc.Dispatcher +import org.wordpress.android.fluxc.model.LocalOrRemoteId.LocalId import org.wordpress.android.fluxc.model.MediaModel +import org.wordpress.android.fluxc.model.MediaModel.MediaUploadState.UPLOADED import org.wordpress.android.fluxc.model.SiteModel import org.wordpress.android.fluxc.store.MediaStore +import org.wordpress.android.ui.ActivityLauncher +import org.wordpress.android.ui.posts.EditPostRepository +import org.wordpress.android.ui.posts.editor.media.EditorMedia import org.wordpress.android.ui.stories.SaveStoryGutenbergBlockUseCase.Companion.TEMPORARY_ID_PREFIX import org.wordpress.android.ui.stories.StoryRepositoryWrapper import org.wordpress.android.ui.stories.media.StoryMediaSaveUploadBridge.StoryFrameMediaModelCreatedEvent +import org.wordpress.android.ui.stories.usecase.LoadStoryFromStoriesPrefsUseCase +import org.wordpress.android.ui.uploads.UploadService +import org.wordpress.android.util.AppLog +import org.wordpress.android.util.AppLog.T.MEDIA import org.wordpress.android.util.EventBusWrapper import org.wordpress.android.util.FluxCUtils +import org.wordpress.android.util.StringUtils import org.wordpress.android.util.helpers.MediaFile +import java.util.ArrayList +import java.util.HashMap import javax.inject.Inject class StoriesEventListener @Inject constructor( private val dispatcher: Dispatcher, private val mediaStore: MediaStore, private val eventBusWrapper: EventBusWrapper, + private val editorMedia: EditorMedia, + private val loadStoryFromStoriesPrefsUseCase: LoadStoryFromStoriesPrefsUseCase, private val storyRepositoryWrapper: StoryRepositoryWrapper ) : LifecycleObserver { private lateinit var lifecycle: Lifecycle private lateinit var site: SiteModel + private lateinit var editPostRepository: EditPostRepository private var storySaveMediaListener: StorySaveMediaListener? = null @OnLifecycleEvent(Lifecycle.Event.ON_CREATE) @@ -43,7 +67,7 @@ class StoriesEventListener @Inject constructor( /** * Handles the [Lifecycle.Event.ON_DESTROY] event to cleanup the registration for dispatcher and removing the - * observer for lifecycle. + * observer for lifecycle . */ @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY) private fun onDestroy() { @@ -52,8 +76,9 @@ class StoriesEventListener @Inject constructor( eventBusWrapper.unregister(this) } - fun start(lifecycle: Lifecycle, site: SiteModel) { + fun start(lifecycle: Lifecycle, site: SiteModel, editPostRepository: EditPostRepository) { this.site = site + this.editPostRepository = editPostRepository this.lifecycle = lifecycle this.lifecycle.addObserver(this) } @@ -153,4 +178,129 @@ class StoriesEventListener @Inject constructor( storySaveMediaListener?.onStorySaveResult(localMediaId, event.isSuccess()) } } + + // Editor load / cancel events + fun onRequestMediaFilesEditorLoad( + activity: Activity, + postId: LocalId, + networkErrorOnLastMediaFetchAttempt: Boolean, + mediaFiles: ArrayList, + blockId: String + ): Boolean { + val reCreateStoryResult = loadStoryFromStoriesPrefsUseCase + .loadStoryFromMemoryOrRecreateFromPrefs(site, mediaFiles) + if (!reCreateStoryResult.noSlidesLoaded) { + // Story instance loaded or re-created! Load it onto the StoryComposer for editing now + ActivityLauncher.editStoryForResult( + activity, + site, + postId, + reCreateStoryResult.storyIndex, + reCreateStoryResult.allStorySlidesAreEditable, + true, + blockId + ) + } else { + // unfortunately we couldn't even load the remote media Ids indicated by the StoryBlock so we can't allow + // editing at this time :( + if (networkErrorOnLastMediaFetchAttempt) { + // there was an error fetching media when we were loading the editor, + // we *may* still have a possibility, tell the user they may try refreshing the media again + val builder: Builder = MaterialAlertDialogBuilder( + activity + ) + builder.setTitle(activity.getString(R.string.dialog_edit_story_unavailable_title)) + builder.setMessage(activity.getString(R.string.dialog_edit_story_unavailable_message)) + builder.setPositiveButton(R.string.dialog_button_ok) { dialog, id -> + dialog.dismiss() + } + val dialog = builder.create() + dialog.show() + } else { + // unrecoverable error, nothing we can do, inform the user :(. + val builder: Builder = MaterialAlertDialogBuilder( + activity + ) + builder.setTitle(activity.getString(R.string.dialog_edit_story_unrecoverable_title)) + builder.setMessage(activity.getString(R.string.dialog_edit_story_unrecoverable_message)) + builder.setPositiveButton(R.string.dialog_button_ok) { dialog, id -> dialog.dismiss() } + val dialog = builder.create() + dialog.show() + } + } + return reCreateStoryResult.noSlidesLoaded + } + + fun onCancelUploadForMediaCollection(mediaFiles: ArrayList) { + // just cancel upload for each media + for (mediaFile in mediaFiles) { + val localMediaId = StringUtils.stringToInt( + (mediaFile as HashMap)["id"].toString(), 0 + ) + if (localMediaId != 0) { + editorMedia.cancelMediaUploadAsync(localMediaId, false) + } + } + } + + fun onRetryUploadForMediaCollection( + activity: Activity, + mediaFiles: ArrayList, + editorMediaUploadListener: EditorMediaUploadListener? + ) { + val mediaIdsToRetry = ArrayList() + for (mediaFile in mediaFiles) { + val localMediaId = StringUtils.stringToInt( + (mediaFile as HashMap)["id"].toString(), 0 + ) + if (localMediaId != 0) { + val media: MediaModel = mediaStore.getMediaWithLocalId(localMediaId) + // if we find at least one item in the mediaFiles collection passed + // for which we don't have a local MediaModel, just tell the user and bail + if (media == null) { + AppLog.e( + MEDIA, + "Can't find media with local id: $localMediaId" + ) + val builder: Builder = MaterialAlertDialogBuilder( + activity + ) + builder.setTitle(activity.getString(string.cannot_retry_deleted_media_item_fatal)) + builder.setPositiveButton(string.yes) { dialog, id -> dialog.dismiss() } + builder.setNegativeButton(activity.getString(string.no), + DialogInterface.OnClickListener { dialog: DialogInterface, id: Int -> dialog.dismiss() } + ) + val dialog = builder.create() + dialog.show() + return + } + if (media.url != null && media.uploadState == UPLOADED.toString()) { + // Note: we should actually do this when the editor fragment starts instead of waiting for user + // input. + // Notify the editor fragment upload was successful and it should replace the local url by the + // remote url. + editorMediaUploadListener?.onMediaUploadSucceeded( + media.id.toString(), + FluxCUtils.mediaFileFromMediaModel(media) + ) + } else { + UploadService.cancelFinalNotification( + activity, + editPostRepository.getPost() + ) + UploadService.cancelFinalNotificationForMedia(activity, site) + mediaIdsToRetry.add(localMediaId) + } + } + } + + if (!mediaIdsToRetry.isEmpty()) { + editorMedia.retryFailedMediaAsync(mediaIdsToRetry) + } + AnalyticsTracker.track(EDITOR_UPLOAD_MEDIA_RETRIED) + } + + fun onCancelSaveForMediaCollection(mediaFiles: ArrayList) { + // TODO implement cancelling save process for media collection + } }