MutationQueue
Enqueues "forgivable" mutations (likes, follows, and similar fire-and-forget writes) for background execution with retry, instead of awaiting them inline from a ViewModel.
enqueueMutation/enqueue return immediately — the actual ApiClient.request call and any retries run on scope, which the caller owns (e.g. an app-scoped CoroutineScope that outlives any single screen). This is the fix for two problems with calling apiClient.request(...) directly for mutations:
No queueing: if the process dies mid-flight, the mutation is silently lost.
No retry: DefaultRetryPolicy deliberately excludes
POST/PATCHby default (retrying a non-idempotent call risks a duplicate write), so a plainNetworkClientnever retries them — correct for most calls, wrong for ones the caller has decided are safe/idempotent-in-effect to retry (e.g. "set like state to true" is idempotent even though it's a POST).
Behavior:
Coalescing: enqueueing under a MutationKey that already has a pending/retrying mutation replaces it — including cancelling any in-progress retry backoff — so only the latest desired state for that key is ever sent. See MutationKey.
Retry: reuses RetryPolicy (the same interface io.github.maniramezan.kenwork.network.NetworkClient uses), but as a queue-level (or per-enqueue) setting distinct from io.github.maniramezan.kenwork.network.NetworkClientConfiguration.retryPolicy — so opting a mutation into non-idempotent retry never loosens the client's own default.
Persistence: pass a MutationCodec to enqueue to have the mutation survive in store (see MutationStore for durability); omit it for a purely in-process, fire-and-forget mutation.
Status: observe statusFlow for a key to reflect pending/retrying/succeeded/failed in the UI.
Parameters
executes the underlying HTTP calls.
owns every mutation's execution + retry backoff; mutations outlive the caller's own scope (e.g. a ViewModel's) as long as this scope is alive.
where enqueued mutations (that were given a MutationCodec) are persisted.
applied to mutations enqueued without an explicit retryPolicy. Defaults to retrying non-idempotent methods too — the whole point of this queue is to make POST/PATCH mutations retryable — unlike DefaultRetryPolicy's own conservative default.
MutationCodecs to register upfront, so restore can decode their records even before any matching enqueue call runs in this process. Enqueueing with a new codec also registers it.
Constructors
Functions
Enqueues a bodyless mutation (e.g. POST /videos/42/like with no request body).
Enqueues a mutation and returns immediately; endpoint (with body, described by bodyType) is executed on scope in the background, retried per retryPolicy (or defaultRetryPolicy when null), and coalesced with any other pending mutation sharing key.
Loads every persisted MutationRecord from store and resubmits it for execution, using the MutationCodec registered under MutationRecord.codecId (via the constructor's codecs or a prior enqueue call) to reconstruct the endpoint + body.
The current/most recent MutationStatus for key, or null if nothing has ever been enqueued under it. Keeps emitting past a terminal MutationStatus.Succeeded/MutationStatus.Failed until a new mutation is enqueued for the same key.