capacitor-lottie-splash 0.1.2 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/android/src/main/java/com/headcount/plugins/lottiesplash/LottieSplashCoordinator.kt +1 -46
- package/android/src/main/java/com/headcount/plugins/lottiesplash/LottieSplashPlugin.kt +1 -2
- package/dist/esm/definitions.d.ts +1 -1
- package/dist/esm/state-machine.d.ts +0 -1
- package/dist/esm/state-machine.js +0 -6
- package/dist/esm/web.d.ts +0 -2
- package/dist/esm/web.js +0 -26
- package/dist/plugin.cjs.js +0 -32
- package/dist/plugin.js +0 -32
- package/ios/Sources/LottieSplashPlugin/LottieSplashCoordinator.swift +1 -42
- package/ios/Sources/LottieSplashPlugin/LottieSplashPlugin.swift +0 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -101,9 +101,9 @@ The documented HeadCount defaults are light `#f0f0f0 → #f5f5f5` and dark `#1e1
|
|
|
101
101
|
|
|
102
102
|
## Recovery and accessibility
|
|
103
103
|
|
|
104
|
-
When playback completes before readiness, its final frame holds. After one second, a subtle indeterminate progress rail appears beneath the mark
|
|
104
|
+
When playback completes before readiness, its final frame holds. After one second, a subtle indeterminate progress rail appears beneath the mark and remains visible until the application is ready. Reduced-motion and asset failures retain the theme-matched native cover instead of exposing a blank screen.
|
|
105
105
|
|
|
106
|
-
For a staged native/over-the-air rollout, the consuming app must keep its web bundle and native host compatible. The native cover stays visible until the app-ready latch is set
|
|
106
|
+
For a staged native/over-the-air rollout, the consuming app must keep its web bundle and native host compatible. The native cover stays visible until the app-ready latch is set.
|
|
107
107
|
|
|
108
108
|
## Compatibility
|
|
109
109
|
|
|
@@ -12,10 +12,7 @@ import android.view.View
|
|
|
12
12
|
import android.view.ViewGroup
|
|
13
13
|
import android.view.animation.AccelerateDecelerateInterpolator
|
|
14
14
|
import android.view.animation.LinearInterpolator
|
|
15
|
-
import android.widget.Button
|
|
16
15
|
import android.widget.FrameLayout
|
|
17
|
-
import android.widget.LinearLayout
|
|
18
|
-
import android.widget.TextView
|
|
19
16
|
import com.airbnb.lottie.LottieAnimationView
|
|
20
17
|
import com.airbnb.lottie.LottieCompositionFactory
|
|
21
18
|
import androidx.activity.ComponentActivity
|
|
@@ -23,7 +20,7 @@ import java.util.UUID
|
|
|
23
20
|
import kotlin.math.max
|
|
24
21
|
import kotlin.math.roundToInt
|
|
25
22
|
|
|
26
|
-
enum class AndroidSplashPhase { PREPARING, PLAYING, HOLDING, HIDING, HIDDEN
|
|
23
|
+
enum class AndroidSplashPhase { PREPARING, PLAYING, HOLDING, HIDING, HIDDEN }
|
|
27
24
|
enum class AndroidSplashPlayback { PLAY, HOLD_FINAL_FRAME }
|
|
28
25
|
|
|
29
26
|
/** A small indeterminate rail that reads as loading without covering the finished logo. */
|
|
@@ -82,7 +79,6 @@ object LottieSplashCoordinator {
|
|
|
82
79
|
// incorrectly dismissing a healthy native cover during that startup work.
|
|
83
80
|
private const val WEB_ADOPTION_GRACE_MS = 30_000L
|
|
84
81
|
private const val PROGRESS_DELAY_MS = 1_000L
|
|
85
|
-
private const val RECOVERY_DELAY_MS = 15_000L
|
|
86
82
|
private const val COVER_FADE_OUT_MS = 500L
|
|
87
83
|
private val main = Handler(Looper.getMainLooper())
|
|
88
84
|
|
|
@@ -94,8 +90,6 @@ object LottieSplashCoordinator {
|
|
|
94
90
|
private var compositionReady = false
|
|
95
91
|
private var requestedPlayback = AndroidSplashPlayback.PLAY
|
|
96
92
|
private var loadingRail: LottieSplashLoadingRail? = null
|
|
97
|
-
private var recoveryMessage: TextView? = null
|
|
98
|
-
private var recoveryRetry: Button? = null
|
|
99
93
|
private var presentationId: String? = null
|
|
100
94
|
private var phase = AndroidSplashPhase.HIDDEN
|
|
101
95
|
private var playbackSettled = false
|
|
@@ -106,16 +100,13 @@ object LottieSplashCoordinator {
|
|
|
106
100
|
private var completion: ((String, String?) -> Unit)? = null
|
|
107
101
|
private var pendingHide: ((Throwable?) -> Unit)? = null
|
|
108
102
|
private var progressTask: Runnable? = null
|
|
109
|
-
private var recoveryTask: Runnable? = null
|
|
110
103
|
private var adoptionTask: Runnable? = null
|
|
111
104
|
private var adoptedByWeb = false
|
|
112
105
|
private var progressRemainingMs = PROGRESS_DELAY_MS
|
|
113
|
-
private var recoveryRemainingMs = RECOVERY_DELAY_MS
|
|
114
106
|
private var waitingClockStartedAtMs = 0L
|
|
115
107
|
private var waitingClockRunning = false
|
|
116
108
|
|
|
117
109
|
var onStateChange: (() -> Unit)? = null
|
|
118
|
-
var onRecoveryRequested: (() -> Unit)? = null
|
|
119
110
|
|
|
120
111
|
/**
|
|
121
112
|
* Called by AndroidX while deciding whether its system-owned launch surface may leave.
|
|
@@ -153,8 +144,6 @@ object LottieSplashCoordinator {
|
|
|
153
144
|
playbackOutcome = "static-fallback"
|
|
154
145
|
playbackReason = null
|
|
155
146
|
loadingRail = null
|
|
156
|
-
recoveryMessage = null
|
|
157
|
-
recoveryRetry = null
|
|
158
147
|
|
|
159
148
|
val backgroundStart = if (darkTheme) Color.rgb(30, 30, 30) else Color.rgb(240, 240, 240)
|
|
160
149
|
root.setBackgroundColor(backgroundStart)
|
|
@@ -354,7 +343,6 @@ object LottieSplashCoordinator {
|
|
|
354
343
|
private fun resetWaitingUi(id: String) {
|
|
355
344
|
cancelWaitingTasks()
|
|
356
345
|
progressRemainingMs = PROGRESS_DELAY_MS
|
|
357
|
-
recoveryRemainingMs = RECOVERY_DELAY_MS
|
|
358
346
|
scheduleWaitingUi(id)
|
|
359
347
|
}
|
|
360
348
|
|
|
@@ -371,24 +359,19 @@ object LottieSplashCoordinator {
|
|
|
371
359
|
waitingClockRunning = true
|
|
372
360
|
waitingClockStartedAtMs = SystemClock.elapsedRealtime()
|
|
373
361
|
progressTask = Runnable { showLoadingRail(id) }
|
|
374
|
-
recoveryTask = Runnable { showRecovery(id) }
|
|
375
362
|
if (loadingRail == null) main.postDelayed(progressTask!!, progressRemainingMs)
|
|
376
|
-
main.postDelayed(recoveryTask!!, recoveryRemainingMs)
|
|
377
363
|
}
|
|
378
364
|
|
|
379
365
|
private fun pauseWaitingClock() {
|
|
380
366
|
if (!waitingClockRunning) return
|
|
381
367
|
val elapsed = SystemClock.elapsedRealtime() - waitingClockStartedAtMs
|
|
382
368
|
progressRemainingMs = max(0, progressRemainingMs - elapsed)
|
|
383
|
-
recoveryRemainingMs = max(0, recoveryRemainingMs - elapsed)
|
|
384
369
|
cancelWaitingTasks()
|
|
385
370
|
}
|
|
386
371
|
|
|
387
372
|
private fun cancelWaitingTasks() {
|
|
388
373
|
progressTask?.let(main::removeCallbacks)
|
|
389
|
-
recoveryTask?.let(main::removeCallbacks)
|
|
390
374
|
progressTask = null
|
|
391
|
-
recoveryTask = null
|
|
392
375
|
waitingClockRunning = false
|
|
393
376
|
}
|
|
394
377
|
|
|
@@ -404,36 +387,10 @@ object LottieSplashCoordinator {
|
|
|
404
387
|
}
|
|
405
388
|
}
|
|
406
389
|
|
|
407
|
-
private fun showRecovery(id: String) {
|
|
408
|
-
recoveryRemainingMs = 0
|
|
409
|
-
if (presentationId != id || appReady || phase != AndroidSplashPhase.HOLDING) return
|
|
410
|
-
cancelWaitingTasks()
|
|
411
|
-
phase = AndroidSplashPhase.RECOVERY
|
|
412
|
-
loadingRail?.visibility = View.GONE
|
|
413
|
-
val context = cover?.context ?: return
|
|
414
|
-
val textColor = if (darkTheme) Color.WHITE else Color.rgb(30, 30, 30)
|
|
415
|
-
val message = TextView(context).apply { text = "Still starting…"; textSize = 16f; setTextColor(textColor); gravity = Gravity.CENTER }
|
|
416
|
-
val retry = Button(context).apply { text = "Try again"; setOnClickListener { onRecoveryRequested?.invoke() } }
|
|
417
|
-
val recoveryGroup = LinearLayout(context).apply {
|
|
418
|
-
orientation = LinearLayout.VERTICAL
|
|
419
|
-
gravity = Gravity.CENTER_HORIZONTAL
|
|
420
|
-
addView(message)
|
|
421
|
-
addView(retry)
|
|
422
|
-
}
|
|
423
|
-
recoveryMessage = message
|
|
424
|
-
recoveryRetry = retry
|
|
425
|
-
cover?.addView(recoveryGroup, FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.CENTER_HORIZONTAL or Gravity.BOTTOM).apply { bottomMargin = dp(recoveryGroup, 48) })
|
|
426
|
-
recoveryGroup.announceForAccessibility("Still starting. Try again is available.")
|
|
427
|
-
emit()
|
|
428
|
-
}
|
|
429
|
-
|
|
430
390
|
private fun clearWaitingUi() {
|
|
431
391
|
cancelWaitingTasks()
|
|
432
392
|
loadingRail?.let { cover?.removeView(it) }
|
|
433
|
-
recoveryMessage?.parent?.let { parent -> (parent as? ViewGroup)?.let { cover?.removeView(it) } }
|
|
434
393
|
loadingRail = null
|
|
435
|
-
recoveryMessage = null
|
|
436
|
-
recoveryRetry = null
|
|
437
394
|
}
|
|
438
395
|
|
|
439
396
|
private fun dismiss(callback: (Throwable?) -> Unit) {
|
|
@@ -464,8 +421,6 @@ object LottieSplashCoordinator {
|
|
|
464
421
|
animation = null
|
|
465
422
|
hostActivity = null
|
|
466
423
|
loadingRail = null
|
|
467
|
-
recoveryMessage = null
|
|
468
|
-
recoveryRetry = null
|
|
469
424
|
presentationId = null
|
|
470
425
|
phase = AndroidSplashPhase.HIDDEN
|
|
471
426
|
compositionReady = false
|
|
@@ -20,7 +20,6 @@ class LottieSplashPlugin : Plugin() {
|
|
|
20
20
|
}
|
|
21
21
|
override fun load() {
|
|
22
22
|
LottieSplashCoordinator.onStateChange = { notifyListeners("stateChange", stateObject()) }
|
|
23
|
-
LottieSplashCoordinator.onRecoveryRequested = { bridge?.webView?.reload() }
|
|
24
23
|
}
|
|
25
24
|
|
|
26
25
|
@PluginMethod
|
|
@@ -89,7 +88,7 @@ class LottieSplashPlugin : Plugin() {
|
|
|
89
88
|
webView.postVisualStateCallback(SystemClock.uptimeMillis(), object : WebView.VisualStateCallback() {
|
|
90
89
|
override fun onComplete(requestId: Long) = finishHandoff()
|
|
91
90
|
})
|
|
92
|
-
// A stalled GPU/compositor must not leave an app-ready screen
|
|
91
|
+
// A stalled GPU/compositor must not leave an app-ready screen covered.
|
|
93
92
|
main.postDelayed(fallback, VISUAL_STATE_TIMEOUT_MS)
|
|
94
93
|
} catch (_: Throwable) {
|
|
95
94
|
finishHandoff()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { PluginListenerHandle } from '@capacitor/core';
|
|
2
2
|
export type SplashRenderer = 'ios' | 'android' | 'web';
|
|
3
|
-
export type SplashPhase = 'preparing' | 'playing' | 'holding' | 'hiding' | 'hidden'
|
|
3
|
+
export type SplashPhase = 'preparing' | 'playing' | 'holding' | 'hiding' | 'hidden';
|
|
4
4
|
export type PlaybackOutcome = 'completed' | 'reduced-motion' | 'static-fallback';
|
|
5
5
|
export type SplashTheme = 'light' | 'dark';
|
|
6
6
|
export interface SplashAssets {
|
|
@@ -10,7 +10,6 @@ export declare class SplashStateMachine {
|
|
|
10
10
|
playing(presentationId: string): StateMachineSnapshot;
|
|
11
11
|
settlePlayback(presentationId: string, outcome: PlaybackOutcome, reason?: string): StateMachineSnapshot;
|
|
12
12
|
requestHide(presentationId: string): StateMachineSnapshot;
|
|
13
|
-
recovery(presentationId: string): StateMachineSnapshot;
|
|
14
13
|
hidden(presentationId: string): StateMachineSnapshot;
|
|
15
14
|
getPlaybackResult(presentationId: string): PlaybackResult | undefined;
|
|
16
15
|
current(): StateMachineSnapshot;
|
|
@@ -45,12 +45,6 @@ export class SplashStateMachine {
|
|
|
45
45
|
this.snapshot.phase = 'hiding';
|
|
46
46
|
return this.current();
|
|
47
47
|
}
|
|
48
|
-
recovery(presentationId) {
|
|
49
|
-
this.requireCurrent(presentationId);
|
|
50
|
-
if (!this.snapshot.appReady)
|
|
51
|
-
this.snapshot.phase = 'recovery';
|
|
52
|
-
return this.current();
|
|
53
|
-
}
|
|
54
48
|
hidden(presentationId) {
|
|
55
49
|
this.requireCurrent(presentationId);
|
|
56
50
|
this.snapshot = {
|
package/dist/esm/web.d.ts
CHANGED
|
@@ -6,7 +6,6 @@ export declare class LottieSplashWeb extends WebPlugin implements LottieSplashPl
|
|
|
6
6
|
private resolvePlayback?;
|
|
7
7
|
private overlay?;
|
|
8
8
|
private progressTimer?;
|
|
9
|
-
private recoveryTimer?;
|
|
10
9
|
show(options: SplashShowOptions): Promise<Presentation>;
|
|
11
10
|
setLaunchTheme(_options: SplashLaunchThemeOptions): Promise<void>;
|
|
12
11
|
waitForCompletion(options: {
|
|
@@ -21,7 +20,6 @@ export declare class LottieSplashWeb extends WebPlugin implements LottieSplashPl
|
|
|
21
20
|
private playComposition;
|
|
22
21
|
private settle;
|
|
23
22
|
private showLoadingRail;
|
|
24
|
-
private showRecovery;
|
|
25
23
|
private removeOverlay;
|
|
26
24
|
private ensureCurrent;
|
|
27
25
|
private emitState;
|
package/dist/esm/web.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { WebPlugin } from '@capacitor/core';
|
|
2
2
|
import { SplashStateMachine } from './state-machine';
|
|
3
|
-
const RECOVERY_TIMEOUT_MS = 15_000;
|
|
4
3
|
const PROGRESS_DELAY_MS = 1_000;
|
|
5
4
|
export class LottieSplashWeb extends WebPlugin {
|
|
6
5
|
stateMachine = new SplashStateMachine();
|
|
@@ -8,7 +7,6 @@ export class LottieSplashWeb extends WebPlugin {
|
|
|
8
7
|
resolvePlayback;
|
|
9
8
|
overlay;
|
|
10
9
|
progressTimer;
|
|
11
|
-
recoveryTimer;
|
|
12
10
|
async show(options) {
|
|
13
11
|
const state = this.stateMachine.current();
|
|
14
12
|
if (state.visible && state.presentationId)
|
|
@@ -104,7 +102,6 @@ export class LottieSplashWeb extends WebPlugin {
|
|
|
104
102
|
this.emitState();
|
|
105
103
|
if (!state.appReady) {
|
|
106
104
|
this.progressTimer = window.setTimeout(() => this.showLoadingRail(presentationId), PROGRESS_DELAY_MS);
|
|
107
|
-
this.recoveryTimer = window.setTimeout(() => this.showRecovery(presentationId), RECOVERY_TIMEOUT_MS);
|
|
108
105
|
}
|
|
109
106
|
}
|
|
110
107
|
catch {
|
|
@@ -127,31 +124,8 @@ export class LottieSplashWeb extends WebPlugin {
|
|
|
127
124
|
rail.append(segment);
|
|
128
125
|
this.overlay?.append(style, rail);
|
|
129
126
|
}
|
|
130
|
-
showRecovery(presentationId) {
|
|
131
|
-
let state;
|
|
132
|
-
try {
|
|
133
|
-
state = this.stateMachine.recovery(presentationId);
|
|
134
|
-
}
|
|
135
|
-
catch {
|
|
136
|
-
return;
|
|
137
|
-
}
|
|
138
|
-
if (state.presentationId !== presentationId || state.appReady)
|
|
139
|
-
return;
|
|
140
|
-
this.overlay?.querySelector('[data-splash-loading-rail]')?.remove();
|
|
141
|
-
const recovery = document.createElement('div');
|
|
142
|
-
recovery.style.cssText = 'position:absolute;bottom:12%;display:grid;gap:12px;place-items:center;color:inherit;font:500 16px system-ui;';
|
|
143
|
-
recovery.textContent = 'Still starting…';
|
|
144
|
-
const retry = document.createElement('button');
|
|
145
|
-
retry.type = 'button';
|
|
146
|
-
retry.textContent = 'Try again';
|
|
147
|
-
retry.onclick = () => window.location.reload();
|
|
148
|
-
recovery.append(retry);
|
|
149
|
-
this.overlay?.append(recovery);
|
|
150
|
-
this.emitState();
|
|
151
|
-
}
|
|
152
127
|
removeOverlay(presentationId) {
|
|
153
128
|
window.clearTimeout(this.progressTimer);
|
|
154
|
-
window.clearTimeout(this.recoveryTimer);
|
|
155
129
|
this.overlay?.remove();
|
|
156
130
|
this.overlay = undefined;
|
|
157
131
|
this.stateMachine.hidden(presentationId);
|
package/dist/plugin.cjs.js
CHANGED
|
@@ -53,12 +53,6 @@ class SplashStateMachine {
|
|
|
53
53
|
this.snapshot.phase = 'hiding';
|
|
54
54
|
return this.current();
|
|
55
55
|
}
|
|
56
|
-
recovery(presentationId) {
|
|
57
|
-
this.requireCurrent(presentationId);
|
|
58
|
-
if (!this.snapshot.appReady)
|
|
59
|
-
this.snapshot.phase = 'recovery';
|
|
60
|
-
return this.current();
|
|
61
|
-
}
|
|
62
56
|
hidden(presentationId) {
|
|
63
57
|
this.requireCurrent(presentationId);
|
|
64
58
|
this.snapshot = {
|
|
@@ -85,7 +79,6 @@ class SplashStateMachine {
|
|
|
85
79
|
}
|
|
86
80
|
}
|
|
87
81
|
|
|
88
|
-
const RECOVERY_TIMEOUT_MS = 15_000;
|
|
89
82
|
const PROGRESS_DELAY_MS = 1_000;
|
|
90
83
|
class LottieSplashWeb extends core.WebPlugin {
|
|
91
84
|
stateMachine = new SplashStateMachine();
|
|
@@ -93,7 +86,6 @@ class LottieSplashWeb extends core.WebPlugin {
|
|
|
93
86
|
resolvePlayback;
|
|
94
87
|
overlay;
|
|
95
88
|
progressTimer;
|
|
96
|
-
recoveryTimer;
|
|
97
89
|
async show(options) {
|
|
98
90
|
const state = this.stateMachine.current();
|
|
99
91
|
if (state.visible && state.presentationId)
|
|
@@ -189,7 +181,6 @@ class LottieSplashWeb extends core.WebPlugin {
|
|
|
189
181
|
this.emitState();
|
|
190
182
|
if (!state.appReady) {
|
|
191
183
|
this.progressTimer = window.setTimeout(() => this.showLoadingRail(presentationId), PROGRESS_DELAY_MS);
|
|
192
|
-
this.recoveryTimer = window.setTimeout(() => this.showRecovery(presentationId), RECOVERY_TIMEOUT_MS);
|
|
193
184
|
}
|
|
194
185
|
}
|
|
195
186
|
catch {
|
|
@@ -212,31 +203,8 @@ class LottieSplashWeb extends core.WebPlugin {
|
|
|
212
203
|
rail.append(segment);
|
|
213
204
|
this.overlay?.append(style, rail);
|
|
214
205
|
}
|
|
215
|
-
showRecovery(presentationId) {
|
|
216
|
-
let state;
|
|
217
|
-
try {
|
|
218
|
-
state = this.stateMachine.recovery(presentationId);
|
|
219
|
-
}
|
|
220
|
-
catch {
|
|
221
|
-
return;
|
|
222
|
-
}
|
|
223
|
-
if (state.presentationId !== presentationId || state.appReady)
|
|
224
|
-
return;
|
|
225
|
-
this.overlay?.querySelector('[data-splash-loading-rail]')?.remove();
|
|
226
|
-
const recovery = document.createElement('div');
|
|
227
|
-
recovery.style.cssText = 'position:absolute;bottom:12%;display:grid;gap:12px;place-items:center;color:inherit;font:500 16px system-ui;';
|
|
228
|
-
recovery.textContent = 'Still starting…';
|
|
229
|
-
const retry = document.createElement('button');
|
|
230
|
-
retry.type = 'button';
|
|
231
|
-
retry.textContent = 'Try again';
|
|
232
|
-
retry.onclick = () => window.location.reload();
|
|
233
|
-
recovery.append(retry);
|
|
234
|
-
this.overlay?.append(recovery);
|
|
235
|
-
this.emitState();
|
|
236
|
-
}
|
|
237
206
|
removeOverlay(presentationId) {
|
|
238
207
|
window.clearTimeout(this.progressTimer);
|
|
239
|
-
window.clearTimeout(this.recoveryTimer);
|
|
240
208
|
this.overlay?.remove();
|
|
241
209
|
this.overlay = undefined;
|
|
242
210
|
this.stateMachine.hidden(presentationId);
|
package/dist/plugin.js
CHANGED
|
@@ -67,12 +67,6 @@ var capacitorLottieSplash = (function (exports, core) {
|
|
|
67
67
|
this.snapshot.phase = 'hiding';
|
|
68
68
|
return this.current();
|
|
69
69
|
}
|
|
70
|
-
recovery(presentationId) {
|
|
71
|
-
this.requireCurrent(presentationId);
|
|
72
|
-
if (!this.snapshot.appReady)
|
|
73
|
-
this.snapshot.phase = 'recovery';
|
|
74
|
-
return this.current();
|
|
75
|
-
}
|
|
76
70
|
hidden(presentationId) {
|
|
77
71
|
this.requireCurrent(presentationId);
|
|
78
72
|
this.snapshot = {
|
|
@@ -99,7 +93,6 @@ var capacitorLottieSplash = (function (exports, core) {
|
|
|
99
93
|
}
|
|
100
94
|
}
|
|
101
95
|
|
|
102
|
-
const RECOVERY_TIMEOUT_MS = 15_000;
|
|
103
96
|
const PROGRESS_DELAY_MS = 1_000;
|
|
104
97
|
class LottieSplashWeb extends core.WebPlugin {
|
|
105
98
|
stateMachine = new SplashStateMachine();
|
|
@@ -107,7 +100,6 @@ var capacitorLottieSplash = (function (exports, core) {
|
|
|
107
100
|
resolvePlayback;
|
|
108
101
|
overlay;
|
|
109
102
|
progressTimer;
|
|
110
|
-
recoveryTimer;
|
|
111
103
|
async show(options) {
|
|
112
104
|
const state = this.stateMachine.current();
|
|
113
105
|
if (state.visible && state.presentationId)
|
|
@@ -203,7 +195,6 @@ var capacitorLottieSplash = (function (exports, core) {
|
|
|
203
195
|
this.emitState();
|
|
204
196
|
if (!state.appReady) {
|
|
205
197
|
this.progressTimer = window.setTimeout(() => this.showLoadingRail(presentationId), PROGRESS_DELAY_MS);
|
|
206
|
-
this.recoveryTimer = window.setTimeout(() => this.showRecovery(presentationId), RECOVERY_TIMEOUT_MS);
|
|
207
198
|
}
|
|
208
199
|
}
|
|
209
200
|
catch {
|
|
@@ -226,31 +217,8 @@ var capacitorLottieSplash = (function (exports, core) {
|
|
|
226
217
|
rail.append(segment);
|
|
227
218
|
this.overlay?.append(style, rail);
|
|
228
219
|
}
|
|
229
|
-
showRecovery(presentationId) {
|
|
230
|
-
let state;
|
|
231
|
-
try {
|
|
232
|
-
state = this.stateMachine.recovery(presentationId);
|
|
233
|
-
}
|
|
234
|
-
catch {
|
|
235
|
-
return;
|
|
236
|
-
}
|
|
237
|
-
if (state.presentationId !== presentationId || state.appReady)
|
|
238
|
-
return;
|
|
239
|
-
this.overlay?.querySelector('[data-splash-loading-rail]')?.remove();
|
|
240
|
-
const recovery = document.createElement('div');
|
|
241
|
-
recovery.style.cssText = 'position:absolute;bottom:12%;display:grid;gap:12px;place-items:center;color:inherit;font:500 16px system-ui;';
|
|
242
|
-
recovery.textContent = 'Still starting…';
|
|
243
|
-
const retry = document.createElement('button');
|
|
244
|
-
retry.type = 'button';
|
|
245
|
-
retry.textContent = 'Try again';
|
|
246
|
-
retry.onclick = () => window.location.reload();
|
|
247
|
-
recovery.append(retry);
|
|
248
|
-
this.overlay?.append(recovery);
|
|
249
|
-
this.emitState();
|
|
250
|
-
}
|
|
251
220
|
removeOverlay(presentationId) {
|
|
252
221
|
window.clearTimeout(this.progressTimer);
|
|
253
|
-
window.clearTimeout(this.recoveryTimer);
|
|
254
222
|
this.overlay?.remove();
|
|
255
223
|
this.overlay = undefined;
|
|
256
224
|
this.stateMachine.hidden(presentationId);
|
|
@@ -149,7 +149,7 @@ public struct LottieSplashConfiguration {
|
|
|
149
149
|
}
|
|
150
150
|
|
|
151
151
|
public enum LottieSplashPhase: String {
|
|
152
|
-
case preparing, playing, holding, hiding, hidden
|
|
152
|
+
case preparing, playing, holding, hiding, hidden
|
|
153
153
|
}
|
|
154
154
|
|
|
155
155
|
public final class LottieSplashCoordinator {
|
|
@@ -159,7 +159,6 @@ public final class LottieSplashCoordinator {
|
|
|
159
159
|
/// permanently obscure that bundle during an over-the-air/native rollout mismatch.
|
|
160
160
|
private let webAdoptionGracePeriod: TimeInterval = 10
|
|
161
161
|
private let progressDelay: TimeInterval = 1
|
|
162
|
-
private let recoveryDelay: TimeInterval = 15
|
|
163
162
|
|
|
164
163
|
public private(set) var presentationId: String?
|
|
165
164
|
public private(set) var phase: LottieSplashPhase = .hidden
|
|
@@ -167,20 +166,17 @@ public final class LottieSplashCoordinator {
|
|
|
167
166
|
public private(set) var appReady = false
|
|
168
167
|
public private(set) var theme: LottieSplashTheme = .dark
|
|
169
168
|
public var onStateChange: ((LottieSplashCoordinator) -> Void)?
|
|
170
|
-
public var onRecoveryRequested: (() -> Void)?
|
|
171
169
|
|
|
172
170
|
private weak var hostController: UIViewController?
|
|
173
171
|
private var cover: LottieSplashCoverView?
|
|
174
172
|
private var animationWebView: WKWebView?
|
|
175
173
|
private var animationMessageBridge: LottieSplashMessageBridge?
|
|
176
174
|
private var loadingRail: LottieSplashLoadingRail?
|
|
177
|
-
private var recoveryWorkItem: DispatchWorkItem?
|
|
178
175
|
private var progressWorkItem: DispatchWorkItem?
|
|
179
176
|
private var adoptionWorkItem: DispatchWorkItem?
|
|
180
177
|
private var adoptedByWeb = false
|
|
181
178
|
private var webRendererLoaded = false
|
|
182
179
|
private var progressRemaining = TimeInterval(1)
|
|
183
|
-
private var recoveryRemaining = TimeInterval(15)
|
|
184
180
|
private var waitingClockStartedAt: Date?
|
|
185
181
|
private var playbackCompletion: ((String, String?) -> Void)?
|
|
186
182
|
private let queue = DispatchQueue.main
|
|
@@ -401,20 +397,13 @@ public final class LottieSplashCoordinator {
|
|
|
401
397
|
])
|
|
402
398
|
self.loadingRail = rail
|
|
403
399
|
}
|
|
404
|
-
let recovery = DispatchWorkItem { [weak self] in
|
|
405
|
-
self?.recoveryRemaining = 0
|
|
406
|
-
self?.showRecovery(for: id)
|
|
407
|
-
}
|
|
408
400
|
progressWorkItem = progress
|
|
409
|
-
recoveryWorkItem = recovery
|
|
410
401
|
if self.loadingRail == nil { queue.asyncAfter(deadline: .now() + progressRemaining, execute: progress) }
|
|
411
|
-
queue.asyncAfter(deadline: .now() + recoveryRemaining, execute: recovery)
|
|
412
402
|
}
|
|
413
403
|
|
|
414
404
|
private func resetWaitingIndicators(for id: String) {
|
|
415
405
|
cancelWaitingTasks()
|
|
416
406
|
progressRemaining = progressDelay
|
|
417
|
-
recoveryRemaining = recoveryDelay
|
|
418
407
|
scheduleWaitingIndicators(for: id)
|
|
419
408
|
}
|
|
420
409
|
|
|
@@ -422,15 +411,12 @@ public final class LottieSplashCoordinator {
|
|
|
422
411
|
guard let waitingClockStartedAt else { return }
|
|
423
412
|
let elapsed = Date().timeIntervalSince(waitingClockStartedAt)
|
|
424
413
|
progressRemaining = max(0, progressRemaining - elapsed)
|
|
425
|
-
recoveryRemaining = max(0, recoveryRemaining - elapsed)
|
|
426
414
|
cancelWaitingTasks()
|
|
427
415
|
}
|
|
428
416
|
|
|
429
417
|
private func cancelWaitingTasks() {
|
|
430
418
|
progressWorkItem?.cancel()
|
|
431
|
-
recoveryWorkItem?.cancel()
|
|
432
419
|
progressWorkItem = nil
|
|
433
|
-
recoveryWorkItem = nil
|
|
434
420
|
waitingClockStartedAt = nil
|
|
435
421
|
}
|
|
436
422
|
|
|
@@ -445,33 +431,6 @@ public final class LottieSplashCoordinator {
|
|
|
445
431
|
queue.asyncAfter(deadline: .now() + webAdoptionGracePeriod, execute: fallback)
|
|
446
432
|
}
|
|
447
433
|
|
|
448
|
-
private func showRecovery(for id: String) {
|
|
449
|
-
guard presentationId == id, !appReady else { return }
|
|
450
|
-
cancelWaitingTasks()
|
|
451
|
-
phase = .recovery
|
|
452
|
-
loadingRail?.removeFromSuperview()
|
|
453
|
-
loadingRail = nil
|
|
454
|
-
let stack = UIStackView()
|
|
455
|
-
stack.axis = .vertical
|
|
456
|
-
stack.spacing = 12
|
|
457
|
-
stack.alignment = .center
|
|
458
|
-
stack.translatesAutoresizingMaskIntoConstraints = false
|
|
459
|
-
let label = UILabel()
|
|
460
|
-
label.text = "Still starting…"
|
|
461
|
-
label.accessibilityTraits = .updatesFrequently
|
|
462
|
-
let retry = UIButton(type: .system)
|
|
463
|
-
retry.setTitle("Try again", for: .normal)
|
|
464
|
-
retry.addAction(UIAction { [weak self] _ in self?.onRecoveryRequested?() }, for: .primaryActionTriggered)
|
|
465
|
-
stack.addArrangedSubview(label)
|
|
466
|
-
stack.addArrangedSubview(retry)
|
|
467
|
-
cover?.addSubview(stack)
|
|
468
|
-
if let cover {
|
|
469
|
-
NSLayoutConstraint.activate([stack.centerXAnchor.constraint(equalTo: cover.centerXAnchor), stack.bottomAnchor.constraint(equalTo: cover.safeAreaLayoutGuide.bottomAnchor, constant: -48)])
|
|
470
|
-
}
|
|
471
|
-
UIAccessibility.post(notification: .announcement, argument: "Still starting")
|
|
472
|
-
emitState()
|
|
473
|
-
}
|
|
474
|
-
|
|
475
434
|
private func dismiss(completion: @escaping (Error?) -> Void) {
|
|
476
435
|
guard phase != .hidden, let cover, let id = presentationId else { completion(nil); return }
|
|
477
436
|
phase = .hiding
|
|
@@ -17,7 +17,6 @@ public class LottieSplashPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
17
17
|
|
|
18
18
|
override public func load() {
|
|
19
19
|
coordinator.onStateChange = { [weak self] coordinator in self?.notifyListeners("stateChange", data: self?.stateData(coordinator) ?? [:]) }
|
|
20
|
-
coordinator.onRecoveryRequested = { [weak self] in self?.bridge?.webView?.reload() }
|
|
21
20
|
}
|
|
22
21
|
|
|
23
22
|
@objc public func show(_ call: CAPPluginCall) {
|