Skip to content

Commit 0cece80

Browse files
committed
SDK Queuing System #10
1 parent 2138051 commit 0cece80

File tree

4 files changed

+88
-105
lines changed

4 files changed

+88
-105
lines changed

example/lib/main.dart

+13-13
Original file line numberDiff line numberDiff line change
@@ -34,19 +34,19 @@ class _MyAppState extends State<MyApp> {
3434
showPanel: true,
3535
wrapperBuilder: (context, child) {
3636
return MaterialApp(
37-
home: child,
38-
// home: Column(
39-
// children: [
40-
// ElevatedButton.icon(
41-
// onPressed: () {
42-
// Codelessly.instance.reset(clearCache: true);
43-
// },
44-
// icon: const Icon(Icons.refresh),
45-
// label: const Text('Reset'),
46-
// ),
47-
// if (child != null) child,
48-
// ],
49-
// ),
37+
// home: child,
38+
home: Column(
39+
children: [
40+
ElevatedButton.icon(
41+
onPressed: () {
42+
Codelessly.instance.reset(clearCache: true);
43+
},
44+
icon: const Icon(Icons.refresh),
45+
label: const Text('Reset'),
46+
),
47+
if (child != null) Expanded(child: child),
48+
],
49+
),
5050
);
5151
},
5252
stories: [

lib/src/data/data_manager.dart

+20-23
Original file line numberDiff line numberDiff line change
@@ -67,9 +67,6 @@ class DataManager {
6767

6868
StreamSubscription<SDKPublishModel?>? _publishModelDocumentListener;
6969

70-
/// The slug for the project as defined in the editor's publish settings.
71-
late String? slug = config.slug;
72-
7370
/// The download queue holds the list of layout IDs that need to be
7471
/// downloaded in order. Tracking this as an external queue allows
7572
/// us to interrupt and inject different layouts to prioritize when needed.
@@ -130,36 +127,29 @@ class DataManager {
130127

131128
// A slug was specified. We need a layout FAST.
132129
// No authentication is required; let's download a complete publish bundle.
133-
if (config.slug != null &&
134-
(slug != config.slug || authManager.authData == null)) {
135-
if (slug == config.slug) {
136-
log('[DataManager] [slug] Slug is the same as the last cached slug, but auth data is null, so going through slug flow again...');
137-
} else {
138-
log('[DataManager] [slug] Slug changed from $slug to ${config.slug}. Going through slug flow...');
139-
}
130+
if (config.slug case String slug) {
131+
log('[DataManager] [slug] Slug was specified [$slug]. Going through slug flow...');
140132

141133
final Stopwatch bundleStopWatch = Stopwatch()..start();
142134
try {
143135
log('[DataManager] [slug] Downloading complete publish bundle for slug $slug.');
144136

145-
slug = config.slug;
146-
147137
if (_publishModel == null) {
148-
log('[DataManager] [slug] Publish model is not cached locally. Downloading complete publish bundle for slug ${config.slug} in foreground.');
138+
log('[DataManager] [slug] Publish model is not cached locally. Downloading complete publish bundle for slug $slug in foreground.');
149139
} else {
150-
log('[DataManager] [slug] Publish model is already cached locally. Downloading complete publish bundle for slug ${config.slug} in background.');
140+
log('[DataManager] [slug] Publish model is already cached locally. Downloading complete publish bundle for slug $slug in background.');
151141
}
152142

153143
final publishBundleFuture = fetchCompletePublishBundle(
154-
slug: slug!,
144+
slug: slug,
155145
source: config.publishSource,
156146
).then((success) {
157147
if (success) {
158148
log('[DataManager] [slug] Complete publish model from slug is downloaded in background. Emitting.');
159149

160150
loadFontsFromPublishModel();
161151
} else {
162-
log('[DataManager] [slug] Failed to download complete publish bundle for slug ${config.slug}.');
152+
log('[DataManager] [slug] Failed to download complete publish bundle for slug $slug.');
163153
}
164154
});
165155

@@ -177,18 +167,14 @@ class DataManager {
177167

178168
_logTime(stopwatch);
179169

180-
log('[DataManager] [slug] Failed to download complete publish bundle for slug ${config.slug}.');
170+
log('[DataManager] [slug] Failed to download complete publish bundle for slug $slug.');
181171
return;
182172
} finally {
183173
bundleStopWatch.stop();
184174
log('[DataManager] [slug] Publish bundle flow took ${bundleStopWatch.elapsedMilliseconds}ms or ${bundleStopWatch.elapsed.inSeconds}s.');
185175
}
186176
} else {
187-
if (config.slug == null) {
188-
log('[DataManager] [slug] Slug is null. Skipping slug flow.');
189-
} else if (config.slug == slug) {
190-
log('[DataManager] [slug] Slug is the same as the last cached slug and auth data exists. We can load through the normal flow. Skipping slug flow.');
191-
}
177+
log('[DataManager] [slug] Slug is ${config.slug}. Skipping slug flow.');
192178
}
193179

194180
if (authManager.authData == null) {
@@ -861,7 +847,18 @@ class DataManager {
861847
log('[DataManager] [queueLayout] Layout [$layoutID] download complete.');
862848
} else {
863849
if (_downloadQueue.contains(layoutID)) {
864-
log('[DataManager] [queueLayout] Layout [$layoutID] is already in the queue. Skipping.');
850+
if (prioritize) {
851+
if (_downloadQueue.first == layoutID) {
852+
log('[DataManager] [queueLayout] Layout [$layoutID] is already at the front of the queue. Skipping.');
853+
return;
854+
} else {
855+
log('[DataManager] [queueLayout] Layout [$layoutID] is already in the queue. Moving it to the front to prioritize it.');
856+
_downloadQueue.remove(layoutID);
857+
}
858+
} else {
859+
log('[DataManager] [queueLayout] Layout [$layoutID] is already in the queue. Skipping.');
860+
return;
861+
}
865862
return;
866863
}
867864

lib/src/ui/codelessly_widget.dart

+19-5
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,7 @@ class CodelesslyWidget extends StatefulWidget {
188188
null),
189189
controller != null
190190
? 'You must provide a [config] inside of your [controller] or configure the provided codelessly instance with one.'
191-
: 'You must provide a [config inside of your CodelesslyWidget or through the Codelessly instance.',
191+
: 'You must provide a [config] inside of your CodelesslyWidget or through the Codelessly instance.',
192192
),
193193
assert(
194194
((layoutID != null) != (controller != null)) ||
@@ -253,9 +253,9 @@ class _CodelesslyWidgetState extends State<CodelesslyWidget> {
253253

254254
if (!CodelesslyErrorHandler.didInitialize) {
255255
_effectiveController.effectiveCodelessly.initErrorHandler(
256-
firebaseProjectId: _effectiveController.config?.firebaseProjectId,
256+
firebaseProjectId: _effectiveController.config.firebaseProjectId,
257257
automaticallySendCrashReports:
258-
_effectiveController.config?.automaticallySendCrashReports ?? false,
258+
_effectiveController.config.automaticallySendCrashReports,
259259
);
260260
}
261261

@@ -406,8 +406,22 @@ class _CodelesslyWidgetState extends State<CodelesslyWidget> {
406406
}
407407

408408
final SDKPublishModel model = snapshot.data!;
409-
final String layoutID =
410-
_effectiveController.layoutID ?? model.entryLayoutId!;
409+
final String? layoutID =
410+
_effectiveController.layoutID ?? model.entryLayoutId;
411+
412+
if (layoutID == null) {
413+
return widget.errorBuilder?.call(
414+
context,
415+
CodelesslyException.notInitializedError(
416+
message:
417+
'A layoutID was not specified and the model does not have an entry layoutID specified.',
418+
),
419+
) ??
420+
CodelesslyErrorScreen(
421+
publishSource: _effectiveController.publishSource,
422+
exception: null,
423+
);
424+
}
411425

412426
if (!model.layouts.containsKey(layoutID)) {
413427
return widget.loadingBuilder?.call(context) ??

lib/src/ui/codelessly_widget_controller.dart

+36-64
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ class CodelesslyWidgetController extends ChangeNotifier {
4747
bool get isGlobalInstance => codelessly == null;
4848

4949
/// Holds initialization configuration options for the SDK.
50-
final CodelesslyConfig? config;
50+
late final CodelesslyConfig config;
5151

5252
/// Optional auth manager for advanced control over the SDK's authentication
5353
/// flow.
@@ -113,12 +113,11 @@ class CodelesslyWidgetController extends ChangeNotifier {
113113
this.publishDataManager,
114114
this.previewDataManager,
115115
this.cacheManager,
116-
}) : config = config ?? (codelessly ?? Codelessly.instance).config {
117-
assert(
118-
(config ?? (codelessly ?? Codelessly.instance).config) != null,
119-
'A [config] must be provided. Please provide one either in the constructor of this controller, or in the passed Codelessly instance.',
120-
);
121-
}
116+
}) : assert(
117+
(config ?? (codelessly ?? Codelessly.instance).config) != null,
118+
'A [config] must be provided. Please provide one either in the constructor of this controller, or in the passed Codelessly instance.',
119+
),
120+
config = config ?? (codelessly ?? Codelessly.instance).config!;
122121

123122
CodelesslyWidgetController copyWith({
124123
String? layoutID,
@@ -150,23 +149,9 @@ class CodelesslyWidgetController extends ChangeNotifier {
150149

151150
/// Listens to the SDK's status. If the SDK is done, then we can start
152151
/// listening to the data manager's status for layout updates.
153-
void initialize({
154-
CodelesslyConfig? config,
155-
String? layoutID,
156-
}) {
157-
assert(
158-
(config == null) != (this.config == null),
159-
config == null
160-
? 'A [config] must be provided. Please provide one either in the initialize() function, or the constructor of this controller, or in the Codelessly instance.'
161-
: 'A config was already provided from '
162-
'${effectiveCodelessly.config == null ? 'the constructor of this controller.' : 'from the configured Codelessly instance.'}'
163-
' You cannot specify it again in the initialize function of this controller.',
164-
);
165-
166-
config ??= this.config;
167-
152+
void initialize({String? layoutID}) {
168153
assert(
169-
config!.slug != null || ((layoutID == null) != (this.layoutID == null)),
154+
config.slug != null || ((layoutID == null) != (this.layoutID == null)),
170155
layoutID == null
171156
? 'The [layoutID] must be provided once either from the constructor of this controller or in the initialize function.'
172157
"\nIf you don't, then a slug must be configured in the config."
@@ -228,11 +213,7 @@ class CodelesslyWidgetController extends ChangeNotifier {
228213
} else {
229214
log('[CodelesslyWidgetController] [${this.layoutID}]: Codelessly SDK is already loaded. Woo!');
230215
}
231-
_verifyAndListenToDataManager(
232-
effectiveCodelessly.status is CLoading
233-
? (effectiveCodelessly.status as CLoading).step
234-
: null,
235-
);
216+
_verifyAndListenToDataManager();
236217
}
237218

238219
log('[CodelesslyWidgetController] [${this.layoutID}]: Listening to sdk status stream.');
@@ -246,19 +227,15 @@ class CodelesslyWidgetController extends ChangeNotifier {
246227
} else {
247228
log('[CodelesslyWidgetController] [${this.layoutID}]: Codelessly SDK is done loading.');
248229
}
249-
_verifyAndListenToDataManager(
250-
effectiveCodelessly.status is CLoading?
251-
? (effectiveCodelessly.status as CLoading).step
252-
: null,
253-
);
230+
_verifyAndListenToDataManager();
254231
}
255232
});
256233
}
257234

258235
/// Listens to the data manager's status. If the data manager is initialized,
259236
/// then we can signal to the manager that the desired layout passed to this
260237
/// widget is ready to be rendered and needs to be downloaded and prepared.
261-
void _verifyAndListenToDataManager(String? loadingStep) {
238+
void _verifyAndListenToDataManager() {
262239
log('[CodelesslyWidgetController] [$layoutID]: Verifying and listening to data manager stream.');
263240

264241
notifyListeners();
@@ -280,38 +257,15 @@ class CodelesslyWidgetController extends ChangeNotifier {
280257
);
281258
});
282259
}
283-
284-
// DataManager is initialized. If the layoutID is not null, then we need
285-
// to signal to the data manager that we want to download the layout.
286-
// If the config has preloading set to true, then the DataManager is already
287-
// taking care of this layout and we just need to wait. But if the config
288-
// has preloading set to false, then we need to manually request the layout
289-
// from the data manager on-demand.
290-
else if (config!.preload == false &&
291-
layoutID != null &&
292-
loadingStep == null) {
293-
log('[CodelesslyWidgetController] [$layoutID]: Config preloading is false.');
294-
log('[CodelesslyWidgetController] [$layoutID]: Requesting layout [$layoutID] from data manager since preloading is false, we need to manually request it.');
295-
log('[CodelesslyWidgetController] [$layoutID]: Using publish source $publishSource.');
296-
297-
dataManager.queueLayout(layoutID: layoutID!).catchError((error, str) {
298-
CodelesslyErrorHandler.instance.captureException(
299-
error,
300-
stacktrace: str,
301-
layoutID: layoutID,
302-
);
303-
});
304-
}
305260
// If the config has a slug specified and the data manager doesn't have
306261
// a publish model yet, then we need to fetch the publish model from the
307262
// data manager.
308-
else if (config!.slug != null &&
309-
dataManager.publishModel == null &&
310-
loadingStep == null) {
263+
else if (config.slug != null && dataManager.publishModel == null) {
311264
log('[CodelesslyWidgetController] [$layoutID]: A slug is specified and publish model is null.');
312265
log('[CodelesslyWidgetController] [$layoutID]: Fetching complete publish bundle from data manager.');
313-
dataManager.fetchCompletePublishBundle(
314-
slug: config!.slug!,
266+
dataManager
267+
.fetchCompletePublishBundle(
268+
slug: config.slug!,
315269
source: publishSource,
316270
)
317271
.catchError((error, str) {
@@ -322,9 +276,17 @@ class CodelesslyWidgetController extends ChangeNotifier {
322276
);
323277
return false;
324278
});
325-
} else {
326-
log('[CodelesslyWidgetController] [$layoutID]: Config preloading is true, but a layout is requested immediately.');
327-
log('[CodelesslyWidgetController] [$layoutID]: Inserting this layoutID to the top of the download queue.');
279+
}
280+
// DataManager is initialized or downloading a publish bundle. If the
281+
// layoutID is not null, then we need to signal to the data manager that we
282+
// want to download the layout.
283+
//
284+
// If the config has preloading set to true, then the DataManager is already
285+
// taking care of this layout and we just need to tell it to prioritize it.
286+
else if (layoutID != null) {
287+
log('[CodelesslyWidgetController] [$layoutID]: Queuing layout [$layoutID] from data manager.');
288+
log('[CodelesslyWidgetController] [$layoutID]: Using publish source $publishSource.');
289+
328290
dataManager
329291
.queueLayout(layoutID: layoutID!, prioritize: true)
330292
.catchError((error, str) {
@@ -335,5 +297,15 @@ class CodelesslyWidgetController extends ChangeNotifier {
335297
);
336298
});
337299
}
300+
301+
// At this point in the execution, layoutID is null, slug is not specified.
302+
// Preloading must be true, so this controller can only wait...
303+
else {
304+
if (layoutID != null) {
305+
log('[CodelesslyWidgetController] [$layoutID]: LayoutID specified, but preload is set to ${config.preload}, skipping to let data manager to download everything');
306+
} else {
307+
log('[CodelesslyWidgetController] [$layoutID]: LayoutID is null, skipping to let data manager to download everything.');
308+
}
309+
}
338310
}
339311
}

0 commit comments

Comments
 (0)