Skip to content

Commit fc14b4e

Browse files
committed
Fix bad layout id crashing causing all codelessly widgets to crash.
1 parent 2bf6073 commit fc14b4e

File tree

3 files changed

+116
-20
lines changed

3 files changed

+116
-20
lines changed

lib/src/data/data_manager.dart

+14-3
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import 'package:flutter/services.dart';
66
import 'package:hive/hive.dart';
77

88
import '../../codelessly_sdk.dart';
9-
import '../logging/error_handler.dart';
109

1110
/// Orchestrates the data flow for the SDK.
1211
class DataManager {
@@ -991,8 +990,20 @@ class DataManager {
991990
}) async {
992991
if (_publishModel != null && queuingDone) {
993992
log('[queueLayout] No longer queuing. Downloading layout [$layoutID] immediately...');
994-
await getOrFetchPopulatedLayout(layoutID: layoutID);
995-
log('[queueLayout] Layout [$layoutID] download complete.');
993+
try {
994+
await getOrFetchPopulatedLayout(layoutID: layoutID);
995+
log('[queueLayout] Layout [$layoutID] download complete.');
996+
} catch (e, str) {
997+
log('[queueLayout] Layout [$layoutID] failed to download immediately.');
998+
final exception = CodelesslyException(
999+
'Failed to download layout [$layoutID].',
1000+
originalException: e,
1001+
stacktrace: str,
1002+
layoutID: layoutID,
1003+
type: ErrorType.layoutFailed,
1004+
);
1005+
errorHandler.captureException(exception, stacktrace: str);
1006+
}
9961007
} else {
9971008
if (_downloadQueue.contains(layoutID)) {
9981009
if (prioritize) {

lib/src/logging/error_handler.dart

+3-3
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ enum ErrorType {
2323
projectNotFound('Project not found'),
2424

2525
/// The layout was not found in the database.
26-
layoutNotFound('Layout not found'),
26+
layoutFailed('Layout failed'),
2727

2828
/// Failed to store an object in cache.
2929
cacheStoreException('Failed to store value in cache'),
@@ -230,7 +230,7 @@ class CodelesslyException implements Exception {
230230
StackTrace? stacktrace,
231231
}) : this(
232232
message,
233-
type: ErrorType.layoutNotFound,
233+
type: ErrorType.layoutFailed,
234234
layoutID: layoutID,
235235
url: url,
236236
originalException: originalException,
@@ -246,7 +246,7 @@ class CodelesslyException implements Exception {
246246
StackTrace? stacktrace,
247247
}) : this(
248248
message,
249-
type: ErrorType.layoutNotFound,
249+
type: ErrorType.layoutFailed,
250250
apiId: apiId,
251251
url: url,
252252
originalException: originalException,

lib/src/ui/codelessly_error_screen.dart

+99-14
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
import 'package:flutter/material.dart';
2+
import 'package:flutter/services.dart';
23

34
import '../logging/error_handler.dart';
45
import '../model/publish_source.dart';
56

67
/// A dialog UI that is displayed when something crashes in the SDK. This is
78
/// a graceful way of dealing with exceptions.
8-
class CodelesslyErrorScreen extends StatelessWidget {
9+
class CodelesslyErrorScreen extends StatefulWidget {
910
final dynamic exception;
1011
final PublishSource publishSource;
1112

@@ -15,14 +16,21 @@ class CodelesslyErrorScreen extends StatelessWidget {
1516
required this.publishSource,
1617
});
1718

19+
@override
20+
State<CodelesslyErrorScreen> createState() => _CodelesslyErrorScreenState();
21+
}
22+
23+
class _CodelesslyErrorScreenState extends State<CodelesslyErrorScreen> {
24+
bool detailsVisible = false;
25+
1826
@override
1927
Widget build(BuildContext context) {
2028
// if (!publishSource.isPreview) return const SizedBox.shrink();
2129

2230
final String message;
2331
String? title;
24-
if (exception is CodelesslyException) {
25-
final CodelesslyException ce = exception as CodelesslyException;
32+
if (widget.exception is CodelesslyException) {
33+
final CodelesslyException ce = widget.exception as CodelesslyException;
2634
title = ce.type.label;
2735
switch (ce.type) {
2836
case ErrorType.invalidAuthToken:
@@ -34,7 +42,7 @@ class CodelesslyErrorScreen extends StatelessWidget {
3442
case ErrorType.projectNotFound:
3543
message = '${ce.message ?? 'Project not found!'}'
3644
'\nPlease check the provided auth token and try again.';
37-
case ErrorType.layoutNotFound:
45+
case ErrorType.layoutFailed:
3846
message = '${ce.message ?? 'Layout not found.'}'
3947
'\nAre you sure the layout ID is correct? If yes, are you sure '
4048
'you published it successfully through the Codelessly publish '
@@ -65,18 +73,18 @@ class CodelesslyErrorScreen extends StatelessWidget {
6573
message = '${ce.message ?? 'Failed to connect to the internet.'}'
6674
'\nPlease check your internet connection and try again.';
6775
case ErrorType.assertionError:
68-
message = (exception as CodelesslyException).message ??
76+
message = (widget.exception as CodelesslyException).message ??
6977
'Assertion error!\nYou used the SDK incorrectly!.';
7078
case ErrorType.notInitializedError:
71-
message = (exception as CodelesslyException).message ??
79+
message = (widget.exception as CodelesslyException).message ??
7280
'Not initialized error!\nYou used the SDK incorrectly!.';
7381
case ErrorType.other:
74-
message = (exception as CodelesslyException).message ??
82+
message = (widget.exception as CodelesslyException).message ??
7583
'Unknown error!\nSorry this happened :(';
7684
}
7785
} else {
7886
message =
79-
'An unexpected error happened!${exception != null ? '\n$exception' : ''}';
87+
'An unexpected error happened!${widget.exception != null ? '\n${widget.exception}' : ''}';
8088
}
8189
return Padding(
8290
padding: const EdgeInsets.all(16),
@@ -118,13 +126,90 @@ class CodelesslyErrorScreen extends StatelessWidget {
118126
textAlign: TextAlign.left,
119127
style: Theme.of(context).textTheme.bodyMedium,
120128
),
121-
if (exception case CodelesslyException ex)
122-
if (ex.originalException != null)
123-
Text(
124-
ex.originalException.toString(),
125-
textAlign: TextAlign.left,
126-
style: Theme.of(context).textTheme.bodyMedium,
129+
if (widget.exception case CodelesslyException ex)
130+
if (ex.originalException != null) ...[
131+
const SizedBox(height: 16),
132+
Container(
133+
decoration: BoxDecoration(
134+
color: Theme.of(context)
135+
.colorScheme
136+
.tertiaryContainer,
137+
borderRadius: BorderRadius.circular(8),
138+
),
139+
padding: const EdgeInsets.only(
140+
top: 8,
141+
left: 16,
142+
right: 16,
143+
),
144+
child: Column(
145+
crossAxisAlignment: CrossAxisAlignment.start,
146+
children: [
147+
Row(
148+
children: [
149+
Expanded(
150+
child: Text(
151+
'Error Details',
152+
style: Theme.of(context)
153+
.textTheme
154+
.titleMedium,
155+
),
156+
),
157+
IconButton(
158+
icon: const Icon(Icons.copy),
159+
iconSize: 14,
160+
onPressed: () {
161+
Clipboard.setData(
162+
ClipboardData(
163+
text:
164+
'${ex.originalException}\n\n${ex.stacktrace}',
165+
),
166+
);
167+
},
168+
),
169+
IconButton(
170+
icon: Icon(
171+
detailsVisible
172+
? Icons.expand_less
173+
: Icons.expand_more,
174+
),
175+
iconSize: 14,
176+
onPressed: () {
177+
setState(() {
178+
detailsVisible = !detailsVisible;
179+
});
180+
},
181+
)
182+
],
183+
),
184+
const SizedBox(height: 8),
185+
ClipRect(
186+
child: AnimatedAlign(
187+
duration:
188+
const Duration(milliseconds: 300),
189+
curve: Curves.easeOutQuart,
190+
alignment: Alignment.topCenter,
191+
heightFactor: detailsVisible ? 1 : 0,
192+
child: ConstrainedBox(
193+
constraints: const BoxConstraints(
194+
maxHeight: 200,
195+
),
196+
child: SingleChildScrollView(
197+
padding: EdgeInsets.zero,
198+
child: Text(
199+
'${ex.originalException}\n\n${ex.stacktrace}',
200+
textAlign: TextAlign.left,
201+
style: Theme.of(context)
202+
.textTheme
203+
.bodyMedium,
204+
),
205+
),
206+
),
207+
),
208+
),
209+
],
210+
),
127211
),
212+
]
128213
],
129214
),
130215
),

0 commit comments

Comments
 (0)