From e692350a9ee01b75f1c280a121ed51ff1fe867e5 Mon Sep 17 00:00:00 2001 From: Tamas Kozmer <72397075+tamaskozmer@users.noreply.github.com> Date: Mon, 7 Oct 2024 16:31:30 +0200 Subject: [PATCH] [MBL-17888][Parent] Fixed help menu serialization #2586 refs: MBL-17888 affects: Parent release note: Fixed an issue where help links wouldn't load in some cases --- apps/flutter_parent/lib/models/help_link.dart | 8 +- .../lib/models/help_link.g.dart | 73 +++++++++++-------- .../lib/screens/help/help_screen.dart | 24 +++--- .../screens/help/help_screen_interactor.dart | 1 + .../help/help_screen_interactor_test.dart | 23 ++++++ 5 files changed, 81 insertions(+), 48 deletions(-) diff --git a/apps/flutter_parent/lib/models/help_link.dart b/apps/flutter_parent/lib/models/help_link.dart index 467ed5b648..af8241783a 100644 --- a/apps/flutter_parent/lib/models/help_link.dart +++ b/apps/flutter_parent/lib/models/help_link.dart @@ -28,18 +28,18 @@ abstract class HelpLink implements Built { factory HelpLink([void Function(HelpLinkBuilder) updates]) = _$HelpLink; - String get id; + String? get id; String get type; @BuiltValueField(wireName: 'available_to') BuiltList get availableTo; - String get url; + String? get url; - String get text; + String? get text; - String get subtext; + String? get subtext; } class AvailableTo extends EnumClass { diff --git a/apps/flutter_parent/lib/models/help_link.g.dart b/apps/flutter_parent/lib/models/help_link.g.dart index a596b2cb39..e9bd162ddf 100644 --- a/apps/flutter_parent/lib/models/help_link.g.dart +++ b/apps/flutter_parent/lib/models/help_link.g.dart @@ -55,22 +55,38 @@ class _$HelpLinkSerializer implements StructuredSerializer { Iterable serialize(Serializers serializers, HelpLink object, {FullType specifiedType = FullType.unspecified}) { final result = [ - 'id', - serializers.serialize(object.id, specifiedType: const FullType(String)), 'type', serializers.serialize(object.type, specifiedType: const FullType(String)), 'available_to', serializers.serialize(object.availableTo, specifiedType: const FullType(BuiltList, const [const FullType(AvailableTo)])), - 'url', - serializers.serialize(object.url, specifiedType: const FullType(String)), - 'text', - serializers.serialize(object.text, specifiedType: const FullType(String)), - 'subtext', - serializers.serialize(object.subtext, - specifiedType: const FullType(String)), ]; + Object? value; + value = object.id; + + result + ..add('id') + ..add( + serializers.serialize(value, specifiedType: const FullType(String))); + value = object.url; + + result + ..add('url') + ..add( + serializers.serialize(value, specifiedType: const FullType(String))); + value = object.text; + + result + ..add('text') + ..add( + serializers.serialize(value, specifiedType: const FullType(String))); + value = object.subtext; + + result + ..add('subtext') + ..add( + serializers.serialize(value, specifiedType: const FullType(String))); return result; } @@ -88,7 +104,7 @@ class _$HelpLinkSerializer implements StructuredSerializer { switch (key) { case 'id': result.id = serializers.deserialize(value, - specifiedType: const FullType(String))! as String; + specifiedType: const FullType(String)) as String?; break; case 'type': result.type = serializers.deserialize(value, @@ -102,15 +118,15 @@ class _$HelpLinkSerializer implements StructuredSerializer { break; case 'url': result.url = serializers.deserialize(value, - specifiedType: const FullType(String))! as String; + specifiedType: const FullType(String)) as String?; break; case 'text': result.text = serializers.deserialize(value, - specifiedType: const FullType(String))! as String; + specifiedType: const FullType(String)) as String?; break; case 'subtext': result.subtext = serializers.deserialize(value, - specifiedType: const FullType(String))! as String; + specifiedType: const FullType(String)) as String?; break; } } @@ -138,36 +154,32 @@ class _$AvailableToSerializer implements PrimitiveSerializer { class _$HelpLink extends HelpLink { @override - final String id; + final String? id; @override final String type; @override final BuiltList availableTo; @override - final String url; + final String? url; @override - final String text; + final String? text; @override - final String subtext; + final String? subtext; factory _$HelpLink([void Function(HelpLinkBuilder)? updates]) => (new HelpLinkBuilder()..update(updates))._build(); _$HelpLink._( - {required this.id, + {this.id, required this.type, required this.availableTo, - required this.url, - required this.text, - required this.subtext}) + this.url, + this.text, + this.subtext}) : super._() { - BuiltValueNullFieldError.checkNotNull(id, r'HelpLink', 'id'); BuiltValueNullFieldError.checkNotNull(type, r'HelpLink', 'type'); BuiltValueNullFieldError.checkNotNull( availableTo, r'HelpLink', 'availableTo'); - BuiltValueNullFieldError.checkNotNull(url, r'HelpLink', 'url'); - BuiltValueNullFieldError.checkNotNull(text, r'HelpLink', 'text'); - BuiltValueNullFieldError.checkNotNull(subtext, r'HelpLink', 'subtext'); } @override @@ -279,16 +291,13 @@ class HelpLinkBuilder implements Builder { try { _$result = _$v ?? new _$HelpLink._( - id: BuiltValueNullFieldError.checkNotNull(id, r'HelpLink', 'id'), + id: id, type: BuiltValueNullFieldError.checkNotNull( type, r'HelpLink', 'type'), availableTo: availableTo.build(), - url: BuiltValueNullFieldError.checkNotNull( - url, r'HelpLink', 'url'), - text: BuiltValueNullFieldError.checkNotNull( - text, r'HelpLink', 'text'), - subtext: BuiltValueNullFieldError.checkNotNull( - subtext, r'HelpLink', 'subtext')); + url: url, + text: text, + subtext: subtext); } catch (_) { late String _$failedField; try { diff --git a/apps/flutter_parent/lib/screens/help/help_screen.dart b/apps/flutter_parent/lib/screens/help/help_screen.dart index 4e468a5720..4d2ca0edf1 100644 --- a/apps/flutter_parent/lib/screens/help/help_screen.dart +++ b/apps/flutter_parent/lib/screens/help/help_screen.dart @@ -65,8 +65,8 @@ class _HelpScreenState extends State { List _generateLinks(List? links) { List helpLinks = List.from(links?.map( (l) => ListTile( - title: Text(l.text, style: Theme.of(context).textTheme.titleMedium), - subtitle: Text(l.subtext, style: Theme.of(context).textTheme.bodySmall), + title: Text(l.text ?? '', style: Theme.of(context).textTheme.titleMedium), + subtitle: Text(l.subtext ?? '', style: Theme.of(context).textTheme.bodySmall), onTap: () => _linkClick(l), ), ) ?? []); @@ -84,7 +84,7 @@ class _HelpScreenState extends State { } void _linkClick(HelpLink link) { - String url = link.url; + String url = link.url ?? ''; if (url[0] == '#') { // Internal link if (url.contains('#create_ticket')) { @@ -93,24 +93,24 @@ class _HelpScreenState extends State { // Custom for Android _showShareLove(); } - } else if (link.id.contains('submit_feature_idea')) { + } else if (link.id?.contains('submit_feature_idea') == true) { _showRequestFeature(); - } else if (link.url.startsWith('tel:+')) { + } else if (url.startsWith('tel:+')) { // Support phone links: https://community.canvaslms.com/docs/DOC-12664-4214610054 - locator().launchPhone(link.url); - } else if (link.url.startsWith('mailto:')) { + locator().launchPhone(url); + } else if (url.startsWith('mailto:')) { // Support mailto links: https://community.canvaslms.com/docs/DOC-12664-4214610054 - locator().launchEmail(link.url); - } else if (link.url.contains('cases.canvaslms.com/liveagentchat')) { + locator().launchEmail(url); + } else if (url.contains('cases.canvaslms.com/liveagentchat')) { // Chat with Canvas Support - Doesn't seem work properly with WebViews, so we kick it out // to the external browser - locator().launch(link.url); - } else if (link.id.contains('search_the_canvas_guides')) { + locator().launch(url); + } else if (link.id?.contains('search_the_canvas_guides') == true) { // Send them to the mobile Canvas guides _showSearch(); } else { // External url - locator().launch(link.url); + locator().launch(url); } } diff --git a/apps/flutter_parent/lib/screens/help/help_screen_interactor.dart b/apps/flutter_parent/lib/screens/help/help_screen_interactor.dart index cc143cd67d..3fdc29789b 100644 --- a/apps/flutter_parent/lib/screens/help/help_screen_interactor.dart +++ b/apps/flutter_parent/lib/screens/help/help_screen_interactor.dart @@ -34,6 +34,7 @@ class HelpScreenInteractor { link.availableTo.contains(AvailableTo.user)); List filterObserverLinks(BuiltList list) => list + .where((link) => link.url != null && link.text != null) .where((link) => link.availableTo.contains(AvailableTo.observer) || link.availableTo.contains(AvailableTo.user)) diff --git a/apps/flutter_parent/test/screens/help/help_screen_interactor_test.dart b/apps/flutter_parent/test/screens/help/help_screen_interactor_test.dart index 00178d1d4a..2b827e1103 100644 --- a/apps/flutter_parent/test/screens/help/help_screen_interactor_test.dart +++ b/apps/flutter_parent/test/screens/help/help_screen_interactor_test.dart @@ -96,6 +96,21 @@ void main() { observerLinks); }); + test('filterObserverLinks only returns links that has text and url', () async { + var validLinks = [ + createHelpLink(availableTo: [AvailableTo.observer]), + createHelpLink(availableTo: [AvailableTo.user]), + ]; + + var invalidLinks = [ + createNullableHelpLink(url: 'url', availableTo: [AvailableTo.observer]), + createNullableHelpLink(text: 'text', availableTo: [AvailableTo.observer]), + ]; + + expect(HelpScreenInteractor().filterObserverLinks(BuiltList.from([...validLinks, ...invalidLinks])), + validLinks); + }); + test('custom list is returned if there are any custom lists', () async { var api = MockHelpLinksApi(); var customLinks = [ @@ -144,3 +159,11 @@ HelpLink createHelpLink({String? id, String? text, String? url, List? availableTo}) => HelpLink((b) => b + ..id = id + ..type = '' + ..availableTo = ListBuilder(availableTo != null ? availableTo : []) + ..url = url + ..text = text + ..subtext = 'subtext'); \ No newline at end of file