-
-
Notifications
You must be signed in to change notification settings - Fork 21
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #2100 from acterglobal/ben-better-errors-for-images
Inline Error Button with Retry for chat images
- Loading branch information
Showing
9 changed files
with
297 additions
and
42 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
import 'package:acter/common/toolkit/errors/error_dialog.dart'; | ||
import 'package:flutter/material.dart'; | ||
import 'package:flutter_gen/gen_l10n/l10n.dart'; | ||
|
||
/// InlineErrorButton for text inlined actions | ||
/// | ||
/// This is a ErrorButton that highlights the given text using the | ||
/// `theme.inlineErrorTheme`. Thus this is super useful if you have some text | ||
/// and want a specific part of it to be highlighted to the user indicating | ||
/// it has an action. See [ErrorButton] for options. | ||
class ActerInlineErrorButton extends StatelessWidget { | ||
final Object error; | ||
final StackTrace? stack; | ||
final VoidCallback? onRetryTap; | ||
final Icon? icon; | ||
|
||
final String? dialogTitle; | ||
final String? text; | ||
final String Function(Object error)? textBuilder; | ||
final bool includeBugReportButton; | ||
|
||
const ActerInlineErrorButton({ | ||
super.key, | ||
required this.error, | ||
this.icon, | ||
this.stack, | ||
this.dialogTitle, | ||
this.text, | ||
this.textBuilder, | ||
this.onRetryTap, | ||
this.includeBugReportButton = true, | ||
}); | ||
|
||
@override | ||
Widget build(BuildContext context) { | ||
if (icon != null) { | ||
return _buildWithIcon(context); | ||
} | ||
return TextButton( | ||
onPressed: () async { | ||
await ActerErrorDialog.show( | ||
context: context, | ||
error: error, | ||
stack: stack, | ||
title: dialogTitle, | ||
text: text, | ||
textBuilder: textBuilder, | ||
onRetryTap: onRetryTap != null | ||
? () { | ||
onRetryTap!(); | ||
Navigator.pop(context); | ||
} | ||
: null, | ||
includeBugReportButton: includeBugReportButton, | ||
); | ||
}, | ||
child: Text(L10n.of(context).fatalError), | ||
); | ||
} | ||
|
||
const ActerInlineErrorButton.icon({ | ||
super.key, | ||
required this.error, | ||
this.stack, | ||
required this.icon, | ||
this.dialogTitle, | ||
this.text, | ||
this.textBuilder, | ||
this.onRetryTap, | ||
this.includeBugReportButton = true, | ||
}); | ||
|
||
Widget _buildWithIcon(BuildContext context) { | ||
return IconButton( | ||
icon: icon!, | ||
onPressed: () async { | ||
await ActerErrorDialog.show( | ||
context: context, | ||
error: error, | ||
stack: stack, | ||
title: dialogTitle, | ||
text: text, | ||
textBuilder: textBuilder, | ||
onRetryTap: onRetryTap != null | ||
? () { | ||
onRetryTap!(); | ||
Navigator.pop(context); | ||
} | ||
: null, | ||
includeBugReportButton: includeBugReportButton, | ||
); | ||
}, | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
import 'package:acter/common/providers/chat_providers.dart'; | ||
import 'package:acter/common/toolkit/errors/inline_error_button.dart'; | ||
import 'package:acter/features/chat/providers/chat_providers.dart'; | ||
import 'package:acter/features/chat/widgets/image_message_builder.dart'; | ||
import 'package:acter_flutter_sdk/acter_flutter_sdk_ffi.dart'; | ||
import 'package:convenient_test_dev/convenient_test_dev.dart'; | ||
import 'package:flutter_chat_types/flutter_chat_types.dart'; | ||
import 'package:flutter_test/flutter_test.dart'; | ||
import 'package:mockito/mockito.dart'; | ||
|
||
import '../../../helpers/error_helpers.dart'; | ||
import '../../../helpers/mock_chat_providers.dart'; | ||
import '../../../helpers/test_util.dart'; | ||
|
||
class FixedOptionStringMock extends Mock implements OptionString { | ||
final String? result; | ||
|
||
FixedOptionStringMock(this.result); | ||
|
||
@override | ||
String? text() => result; | ||
} | ||
|
||
/// Mocked version of ActerSdk for test purposes | ||
class RetryMediaConvoMock extends Mock implements Convo { | ||
bool shouldFail = true; | ||
@override | ||
Future<OptionString> mediaPath(String eventId, bool isThumb) async { | ||
if (shouldFail) { | ||
shouldFail = false; | ||
return FixedOptionStringMock('bad_path'); | ||
} | ||
return FixedOptionStringMock(null); | ||
} | ||
} | ||
|
||
void main() { | ||
group('Image Fails to Load Error', () { | ||
testWidgets('shows error and retries', (tester) async { | ||
const imageMessage = ImageMessage( | ||
author: User( | ||
id: 'sender', | ||
firstName: 'userName', | ||
), | ||
remoteId: 'eventItem.uniqueId()', | ||
createdAt: 1234567, | ||
height: 20, | ||
id: 'eventId', | ||
name: 'msgContent.body()', | ||
size: 30, | ||
uri: 'msgContent.source()!.url()', | ||
width: 30, | ||
); | ||
|
||
await tester.pumpProviderWidget( | ||
overrides: [ | ||
// Provider first provides a broken path to trigger the error | ||
// then null, so it would check for auto-download but not attempt | ||
chatProvider.overrideWith( | ||
() => MockAsyncConvoNotifier(retVal: RetryMediaConvoMock()), | ||
), | ||
autoDownloadMediaProvider.overrideWith((a, b) => false), | ||
], | ||
child: const ImageMessageBuilder( | ||
message: imageMessage, | ||
roomId: '!roomId', | ||
messageWidth: 100, | ||
), | ||
); | ||
|
||
await tester.pumpWithRunAsyncUntil( | ||
() => findsOne.matches(find.byType(ActerInlineErrorButton), {}), | ||
); | ||
await tester.ensureInlineErrorWithRetryWorks(); | ||
}); | ||
}); | ||
} |
Oops, something went wrong.