Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Autosave or update draft #11251

Merged
merged 17 commits into from
Feb 19, 2020
Merged
Changes from 1 commit
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
be3ad8b
A rough introduction of AutoSavePostIfNotDraftUseCase
oguzkocer Feb 6, 2020
32da8c4
Rough integration of AutoSavePostIfNotDraftUseCase
oguzkocer Feb 6, 2020
ab80870
Refactor AutoSavePostIfNotDraftUseCase so it's reusable
oguzkocer Feb 6, 2020
4cf02b7
Update FluxC hash which contains some fixes to fetching post status
oguzkocer Feb 6, 2020
738e71e
Throw IllegalArgumentException if autosave use case is already handli…
oguzkocer Feb 10, 2020
6767c91
Refactor AutoSavePostIfNotDraftUseCase
oguzkocer Feb 10, 2020
0b2a89e
Handle errors in AutoSavePostIfNotDraftUseCase
oguzkocer Feb 10, 2020
a70f33e
Merge remote-tracking branch 'origin/develop' into issue/autosave-or-…
oguzkocer Feb 11, 2020
d01c3f8
Handle all AutoSavePostIfNotDraftResult types
oguzkocer Feb 11, 2020
bb985b1
Observe AutoSavePostIfNotDraftUseCase.onPostChanged on MAIN thread wi…
oguzkocer Feb 13, 2020
63fd0fe
Don't show error notifications for post autosave fails
oguzkocer Feb 13, 2020
1268a78
Add documentation for auto-save use case
oguzkocer Feb 13, 2020
174b742
Sets up AutoSavePostIfNotDraftUseCaseTest and adds fetch post status …
oguzkocer Feb 13, 2020
19fad9d
Adds unit test for PostAutoSaveFailed
oguzkocer Feb 13, 2020
a3d06fb
Adds post is draft in remote unit test for AutoSavePostIfNotDraftUseCase
oguzkocer Feb 13, 2020
2c77170
Adds post auto-saved unit test for AutoSavePostIfNotDraftUseCase
oguzkocer Feb 13, 2020
26db1f9
Adds unit test for calling autoSavePostOrUpdateDraft with a local draft
oguzkocer Feb 13, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Refactor AutoSavePostIfNotDraftUseCase
oguzkocer committed Feb 10, 2020
commit 6767c91dc1638619cdf49cd701ecd06f5f589fdd
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.wordpress.android.ui.uploads

import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.suspendCancellableCoroutine
@@ -13,33 +14,40 @@ import org.wordpress.android.fluxc.model.PostModel
import org.wordpress.android.fluxc.store.PostStore
import org.wordpress.android.fluxc.store.PostStore.OnPostChanged
import org.wordpress.android.fluxc.store.PostStore.OnPostStatusFetched
import org.wordpress.android.fluxc.store.PostStore.PostError
import org.wordpress.android.fluxc.store.PostStore.RemotePostPayload
import org.wordpress.android.modules.BG_THREAD
import org.wordpress.android.ui.uploads.AutoSavePostIfNotDraftResult.PostAutoSaved
import org.wordpress.android.ui.uploads.AutoSavePostIfNotDraftResult.PostIsDraftInRemote
import java.lang.IllegalArgumentException
import javax.inject.Inject
import javax.inject.Named
import kotlin.coroutines.Continuation
import kotlin.coroutines.resume

// TODO: Not be so verbose in naming

private const val DRAFT_POST_STATUS = "draft"

interface OnAutoSavePostIfNotDraftCallback {
fun handleAutoSavePostIfNotDraftResult(result: AutoSavePostIfNotDraftResult)
}

sealed class AutoSavePostIfNotDraftResult {
// Initial fetch post status request failed
data class PostStatusCheckFailed(val post:PostModel, val error: PostError) : AutoSavePostIfNotDraftResult()
// Post status is `DRAFT` in remote which means we'll want to update the draft directly
data class PostIsDraftInRemote(val post: PostModel) : AutoSavePostIfNotDraftResult()
// Post status is not `DRAFT` in remote and the post was auto-saved successfully
data class PostAutoSaved(val post: PostModel) : AutoSavePostIfNotDraftResult()
// TODO: Add error result types
// Post status is not `DRAFT` in remote but the post auto-save failed
data class PostAutoSaveFailed(val post:PostModel, val error: PostError) : AutoSavePostIfNotDraftResult()
}

// TODO: Add documentation
// TODO: Add documentation (add shortcode for the p2 discussion)
// TODO: Add unit tests
class AutoSavePostIfNotDraftUseCase @Inject constructor(
private val dispatcher: Dispatcher,
private val postStore: PostStore
private val postStore: PostStore,
@Named(BG_THREAD) private val bgDispatcher: CoroutineDispatcher
) {
private val postStatusContinuations = HashMap<LocalId, Continuation<String>>()
private val autoSaveContinuations = HashMap<LocalId, Continuation<PostModel>>()
@@ -53,34 +61,41 @@ class AutoSavePostIfNotDraftUseCase @Inject constructor(
callback: OnAutoSavePostIfNotDraftCallback
) {
val localPostId = LocalId(remotePostPayload.post.id)
if (remotePostPayload.post.isLocalDraft) {
throw IllegalArgumentException("Local drafts should not be auto-saved")
}
if (postStatusContinuations.containsKey(localPostId) ||
autoSaveContinuations.containsKey(localPostId)) {
throw IllegalArgumentException("This post is already being processed. Make sure not to start an autoSave " +
"or update draft action while another one is going on.")
}
// TODO: What scope should we use for this?
GlobalScope.launch {
val remotePostStatus: String = suspendCancellableCoroutine { cont ->
postStatusContinuations[localPostId] = cont
dispatcher.dispatch(PostActionBuilder.newFetchPostStatusAction(remotePostPayload))
}

GlobalScope.launch(bgDispatcher) {
val remotePostStatus = fetchRemotePostStatus(remotePostPayload)
if (remotePostStatus == DRAFT_POST_STATUS) {
callback.handleAutoSavePostIfNotDraftResult(PostIsDraftInRemote(remotePostPayload.post))
} else {
val updatedPost: PostModel = suspendCancellableCoroutine { cont ->
autoSaveContinuations[localPostId] = cont
dispatcher.dispatch(
PostActionBuilder.newRemoteAutoSavePostAction(
remotePostPayload
)
)
}
val updatedPost = autoSavePost(remotePostPayload)
callback.handleAutoSavePostIfNotDraftResult(PostAutoSaved(updatedPost))
}
}
}

private suspend fun fetchRemotePostStatus(remotePostPayload: RemotePostPayload): String {
val localPostId = LocalId(remotePostPayload.post.id)
return suspendCancellableCoroutine { cont ->
postStatusContinuations[localPostId] = cont
dispatcher.dispatch(PostActionBuilder.newFetchPostStatusAction(remotePostPayload))
}
}

private suspend fun autoSavePost(remotePostPayload: RemotePostPayload): PostModel {
val localPostId = LocalId(remotePostPayload.post.id)
return suspendCancellableCoroutine { cont ->
autoSaveContinuations[localPostId] = cont
dispatcher.dispatch(PostActionBuilder.newRemoteAutoSavePostAction(remotePostPayload))
}
}

// TODO: Handle errors
@Subscribe(threadMode = BACKGROUND)
@Suppress("unused")