From 2d42a8ee9bf228fbc16ce0979fd7d94ca4697a27 Mon Sep 17 00:00:00 2001 From: Mihail Varbanov Date: Fri, 22 Mar 2024 21:03:58 +0200 Subject: [PATCH] Release/v5.1.34+1934 (#4016) * Filter sport events with the correct sport names [#3986] * Make the event2 favorite [#3986] * Update CHANGELOG.md [#3986] * Accessibility improvements * Accessibility improvements * Acknowledged "Assistant Managed" group for enabling Assistant UI [#4002]. * Trim URL links before launching them, report URL parse failure when confirming URLs [#4001]. * Created HomeEmptyContentWidget displayed when there is no favorites content [#3999]. * version: 5.1.33+1933 * version: 5.1.33+1933 * Add event image description when creating / modifying event [#4010] * Update CHANGELOG.md [#4010] * Use registration label for event if available [#4012] * Update CHANGELOG.md [#4012] * Fix deep link key for notifyProfileLoginNotification to profile.login * version: 5.1.34+1934 --------- Co-authored-by: Dobromir Dobrev Co-authored-by: Todor Bachvarov --- CHANGELOG.md | 16 +++++ SECURITY.md | 4 +- assets/flexUI.json | 2 +- assets/strings.en.json | 4 ++ assets/strings.es.json | 4 ++ assets/strings.zh.json | 4 ++ lib/ext/Favorite.dart | 2 +- lib/service/FirebaseMessaging.dart | 14 ++-- lib/ui/BrowsePanel.dart | 2 +- lib/ui/RootPanel.dart | 14 ++-- .../AthleticsEventsContentWidget.dart | 21 ++++-- .../athletics/AthleticsGameDetailHeading.dart | 12 ++-- .../athletics/AthleticsGameDetailPanel.dart | 2 +- lib/ui/attributes/ContentAttributesPanel.dart | 24 ++++--- lib/ui/events2/Event2CreatePanel.dart | 15 ++++ lib/ui/events2/Event2DetailPanel.dart | 4 +- .../events2/Event2SetupRegistrationPanel.dart | 19 ++--- lib/ui/groups/GroupDetailPanel.dart | 26 +++---- lib/ui/groups/GroupMemberPanel.dart | 5 +- lib/ui/groups/GroupMembersPanel.dart | 25 ++++--- lib/ui/groups/GroupSettingsPanel.dart | 23 ++++--- lib/ui/groups/GroupWidgets.dart | 6 +- lib/ui/home/HomeAthleticsEventsWidget.dart | 2 +- lib/ui/home/HomeEmptyContentWidget.dart | 69 +++++++++++++++++++ lib/ui/home/HomePanel.dart | 13 ++-- lib/ui/home/HomeWelcomeWidget.dart | 7 -- lib/ui/home/HomeWidgets.dart | 10 ++- plugin | 2 +- pubspec.yaml | 2 +- 29 files changed, 251 insertions(+), 102 deletions(-) create mode 100644 lib/ui/home/HomeEmptyContentWidget.dart diff --git a/CHANGELOG.md b/CHANGELOG.md index bbf6b5a8a..19ec75790 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +## [5.1.34] - 2024-03-15 +### Added +- Event cover image description [#4010](https://github.com/rokwire/illinois-app/issues/4010). +### Fixed +- Show event registration label if available [#4012](https://github.com/rokwire/illinois-app/issues/4012). +- Fix Settings and Profile deep links [#4014](https://github.com/rokwire/illinois-app/issues/4014). + +## [5.1.33] - 2024-03-14 +### Added +- Created HomeEmptyContentWidget displayed when there is no favorites content [#3999](https://github.com/rokwire/illinois-app/issues/3999). +### Changed +- Acknowledged "Assistant Managed" group for enabling Assistant UI [#4002](https://github.com/rokwire/illinois-app/issues/4002). +### Fixed +- Correct filtering of sport events [#3986](https://github.com/rokwire/illinois-app/issues/3986). +- Trim URL links before launching them, report URL parse failure when confirming URLs [#4001](https://github.com/rokwire/illinois-app/issues/4001). + ## [5.1.32] - 2024-02-19 ### Removed - Android: background location permission [#3989](https://github.com/rokwire/illinois-app/issues/3989). diff --git a/SECURITY.md b/SECURITY.md index c3359b479..4d651c2ec 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -6,8 +6,8 @@ Patches for [ **illinois-app** ] will only be applied to the following versions: | Version | Supported | |----------| ------------------ | -| 5.1.32 | :white_check_mark: | -| < 5.1.32 | :x: | +| 5.1.34 | :white_check_mark: | +| < 5.1.34 | :x: | | 5.0.78 | :white_check_mark: | | < 5.0.78 | :x: | | 4.3.61 | :white_check_mark: | diff --git a/assets/flexUI.json b/assets/flexUI.json index 8269f8a6a..d45204134 100644 --- a/assets/flexUI.json +++ b/assets/flexUI.json @@ -120,7 +120,7 @@ "canvas_courses" : ["${config.settings.groups.canvas}"], "medicine_courses" : ["${config.settings.groups.canvas}"], "mtd_bus_pass" : ["${config.settings.groups.mtd_bus_pass}"], - "assistant" : ["${config.settings.groups.assistant}"], + "assistant" : ["${config.settings.groups.assistant}", "OR", "${config.settings.groups.assistant_managed}"], "essential_skills_coach" : ["${config.settings.groups.essential_skills_coach}"] }, "locations": { diff --git a/assets/strings.en.json b/assets/strings.en.json index da2aa83f7..5e929eced 100644 --- a/assets/strings.en.json +++ b/assets/strings.en.json @@ -136,6 +136,7 @@ "panel.home.header.title": "Favorites", "panel.home.header.editing.title": "Customize", + "panel.home.connect.not_logged_in.title": "Connect to Illinois", "panel.home.connect.not_logged_in.netid.description.part_1": "Are you a ", "panel.home.connect.not_logged_in.netid.description.part_2": "university student", "panel.home.connect.not_logged_in.netid.description.part_3": " or ", @@ -1213,6 +1214,7 @@ "panel.event2.create.header.title": "Create an Event", "panel.event2.update.header.title": "Update Event", + "panel.event2.create.section.image.description": "The event image displays a 16:9 or 1000px x 615px jpg, png, or gif (not animated). Larger images are automatically positioned within the frame and can be tapped to view in their entirety within the Illinois app.", "panel.event2.create.section.title.title": "EVENT TITLE", "panel.event2.create.section.title.field.title": "TITLE FIELD", "panel.event2.create.section.description.title": "EVENT DESCRIPTION", @@ -3530,6 +3532,8 @@ "widget.home.welcome.text.title": "Welcome to {{app_title}} {{app_version}}", "widget.home.welcome.button.close.label": "Close", + "widget.home.empty.content.text": "Tap the \u2606s under Browse to add shortcuts to Favorites. Note that some features require specific privacy settings and signing in with your NetID, phone number, or email address.", + "widget.home.athletics_news.text.title": "Big 10 News", "widget.home.athletics_news.text.empty.description": "Big 10 News is not available right now.", "widget.home.athletics_news.text.offline": "Big 10 News is not available while offline.", diff --git a/assets/strings.es.json b/assets/strings.es.json index 846bf0dac..acd1a4281 100644 --- a/assets/strings.es.json +++ b/assets/strings.es.json @@ -136,6 +136,7 @@ "panel.home.header.title": "Favoritas", "panel.home.header.editing.title": "Personalizar", + "panel.home.connect.not_logged_in.title": "Connect to Illinois", "panel.home.connect.not_logged_in.netid.description.part_1": "¿Eres un/a ", "panel.home.connect.not_logged_in.netid.description.part_2": "estudiante universitario/a", "panel.home.connect.not_logged_in.netid.description.part_3": " o ", @@ -1212,6 +1213,7 @@ "panel.event2.create.header.title": "Crear un Evento", "panel.event2.update.header.title": "Actualizar un Evento", + "panel.event2.create.section.image.description": "La imagen del evento muestra un formato jpg, png o gif de 16:9 o 1000 px x 615 px (no animado). Las imágenes más grandes se colocan automáticamente dentro del marco y se pueden tocar para verlas en su totalidad dentro de la aplicación de Illinois.", "panel.event2.create.section.title.title": "TÍTULO DEL EVENTO", "panel.event2.create.section.title.field.title": "TITLE FIELD", "panel.event2.create.section.description.title": "DESCRIPCIÓN DEL EVENTO", @@ -3523,6 +3525,8 @@ "widget.home.welcome.text.title": "Bienvenido a {{app_title}} {{app_version}}", "widget.home.welcome.button.close.label": "Cerrar", + "widget.home.empty.content.text": "Tap the \u2606s under Browse to add shortcuts to Favorites. Note that some features require specific privacy settings and signing in with your NetID, phone number, or email address.", + "widget.home.athletics_news.text.title": "10 grandes noticias", "widget.home.athletics_news.text.empty.description": "Las 10 grandes noticias no están disponibles en este momento.", "widget.home.athletics_news.text.offline": "Big 10 News no está disponible sin conexión.", diff --git a/assets/strings.zh.json b/assets/strings.zh.json index a5816ae61..205dcf823 100644 --- a/assets/strings.zh.json +++ b/assets/strings.zh.json @@ -136,6 +136,7 @@ "panel.home.header.title": "收藏夾", "panel.home.header.editing.title": "定制", + "panel.home.connect.not_logged_in.title": "Connect to Illinois", "panel.home.connect.not_logged_in.netid.description.part_1": "您是 ", "panel.home.connect.not_logged_in.netid.description.part_2": "學生", "panel.home.connect.not_logged_in.netid.description.part_3": " 或 ", @@ -1212,6 +1213,7 @@ "panel.event2.create.header.title": "举办活动", "panel.event2.update.header.title": "活动更新", + "panel.event2.create.section.image.description": "事件圖像顯示 16:9 或 1000px x 615px jpg、png 或 gif(非動畫)。 較大的圖像會自動定位在框架內,並且可以點擊以在伊利諾伊州應用程式中完整查看。", "panel.event2.create.section.title.title": "活动名称", "panel.event2.create.section.description.title": "活动细节", "panel.event2.create.section.description.description": " (在此栏新增的超链接无法使用。请将网址输入至正确的字段,如在线活动的活动网址、活动网页链接、或是外部的活动注册链接。)", @@ -3508,6 +3510,8 @@ "widget.home.welcome.text.title": "“歡迎來到{{app_title}} {{app_version}}", "widget.home.welcome.button.close.label": "關", + "widget.home.empty.content.text": "Tap the \u2606s under Browse to add shortcuts to Favorites. Note that some features require specific privacy settings and signing in with your NetID, phone number, or email address.", + "widget.home.athletics_news.text.title": "十大新聞", "widget.home.athletics_news.text.empty.description": "目前無法查看十大新聞。", "widget.home.athletics_news.text.offline": "離線時無法查看十大新聞。", diff --git a/lib/ext/Favorite.dart b/lib/ext/Favorite.dart index 54457e3a3..147933419 100644 --- a/lib/ext/Favorite.dart +++ b/lib/ext/Favorite.dart @@ -192,7 +192,7 @@ extension FavoriteExt on Favorite { else if (this is Event2) { Event2 event2 = (this as Event2); if (event2.hasGame) { - Navigator.push(context, CupertinoPageRoute(builder: (context) => AthleticsGameDetailPanel(game: event2.game))); + Navigator.push(context, CupertinoPageRoute(builder: (context) => AthleticsGameDetailPanel(game: event2.game, event: event2))); } else { Navigator.push(context, CupertinoPageRoute(builder: (context) => Event2DetailPanel(event: event2))); } diff --git a/lib/service/FirebaseMessaging.dart b/lib/service/FirebaseMessaging.dart index 020fcef9e..011ec9ba7 100644 --- a/lib/service/FirebaseMessaging.dart +++ b/lib/service/FirebaseMessaging.dart @@ -98,8 +98,8 @@ class FirebaseMessaging extends rokwire.FirebaseMessaging implements Notificatio static const String notifyWellnessToDoItemNotification = "$notifyBase.wellness.to_do"; static const String notifyProfileMyNotification = "$notifyBase.profile.my"; static const String notifyProfileWhoAreYouNotification = "$notifyBase.profile.who_are_you"; - static const String notifyProfileLoginNotification = "$notifyBase.profile.privacy"; //TBD profile.login? - static const String notifySettingsSectionsNotification = "$notifyBase.settings.sections"; //TBD deprecate. Use payloadTypeProfileLogin instead + static const String notifyProfileLoginNotification = "$notifyBase.profile.login"; + static const String notifySettingsSectionsNotification = "$notifyBase.settings.sections"; //TBD deprecate and delete. Use profile.login instead static const String notifySettingsInterestsNotification = "$notifyBase.settings.interests"; static const String notifySettingsFoodFiltersNotification = "$notifyBase.settings.food_filters"; static const String notifySettingsSportsNotification = "$notifyBase.settings.sports"; @@ -107,11 +107,11 @@ class FirebaseMessaging extends rokwire.FirebaseMessaging implements Notificatio static const String notifySettingsAssessmentsNotification = "$notifyBase.settings.assessments"; static const String notifySettingsCalendarNotification = "$notifyBase.settings.calendar"; static const String notifySettingsAppointmentsNotification = "$notifyBase.settings.appointments"; - static const String notifySettingsMapsNotification = "$notifyBase.settings.maps"; //TBD do we need this? Are we declaring these somewhere in the documentation? - static const String notifySettingsContactsNotification = "$notifyBase.settings.contacts"; //TBD do we need this? - static const String notifySettingsResearchNotification = "$notifyBase.settings.research"; //TBD do we need this? - static const String notifySettingsPrivacyNotification = "$notifyBase.settings.privacy"; //TBD do we need this? - static const String notifySettingsNotificationsNotification = "$notifyBase.settings.notifications"; //TBD do we need this? + static const String notifySettingsMapsNotification = "$notifyBase.settings.maps"; + static const String notifySettingsContactsNotification = "$notifyBase.settings.contacts"; + static const String notifySettingsResearchNotification = "$notifyBase.settings.research"; + static const String notifySettingsPrivacyNotification = "$notifyBase.settings.privacy"; + static const String notifySettingsNotificationsNotification = "$notifyBase.settings.notifications"; static const String notifyGuideArticleDetailNotification = "$notifyBase.guide.article.detail"; // Topic names diff --git a/lib/ui/BrowsePanel.dart b/lib/ui/BrowsePanel.dart index f51545acf..cafbec8b2 100644 --- a/lib/ui/BrowsePanel.dart +++ b/lib/ui/BrowsePanel.dart @@ -75,8 +75,8 @@ import 'package:rokwire_plugin/utils/utils.dart'; import 'package:url_launcher/url_launcher.dart'; class BrowsePanel extends StatefulWidget { - static const String notifyRefresh = "edu.illinois.rokwire.browse.refresh"; + static const String notifySelect = "edu.illinois.rokwire.browse.select"; BrowsePanel(); diff --git a/lib/ui/RootPanel.dart b/lib/ui/RootPanel.dart index 480fdfe7c..cdb87c7b0 100644 --- a/lib/ui/RootPanel.dart +++ b/lib/ui/RootPanel.dart @@ -190,6 +190,7 @@ class _RootPanelState extends State with TickerProviderStateMixin imp Polls.notifyPresentResult, uiuc.TabBar.notifySelectionChanged, HomePanel.notifySelect, + BrowsePanel.notifySelect, ExploreMapPanel.notifySelect, ]); @@ -475,7 +476,10 @@ class _RootPanelState extends State with TickerProviderStateMixin imp _onFirebaseGuideArticleNotification(param); } else if (name == HomePanel.notifySelect) { - _onSelectHome(); + _onSelectTab(RootTab.Favorites); + } + else if (name == BrowsePanel.notifySelect) { + _onSelectTab(RootTab.Browse); } else if (name == ExploreMapPanel.notifySelect) { _onSelectMaps(param); @@ -493,11 +497,11 @@ class _RootPanelState extends State with TickerProviderStateMixin imp } } - void _onSelectHome() { - int? homeIndex = _getIndexByRootTab(RootTab.Favorites); - if (mounted && (homeIndex != null)) { + void _onSelectTab(RootTab tab) { + int? tabIndex = _getIndexByRootTab(tab); + if (mounted && (tabIndex != null)) { Navigator.of(context, rootNavigator: true).popUntil((route) => route.isFirst); - _selectTab(homeIndex); + _selectTab(tabIndex); } } diff --git a/lib/ui/athletics/AthleticsEventsContentWidget.dart b/lib/ui/athletics/AthleticsEventsContentWidget.dart index ebd698f3e..9aabd9eb5 100644 --- a/lib/ui/athletics/AthleticsEventsContentWidget.dart +++ b/lib/ui/athletics/AthleticsEventsContentWidget.dart @@ -106,7 +106,7 @@ class _AthleticsEventsContentWidgetState extends State _onTapGame(game), showImage: true))); + child: AthleticsEventCard(sportEvent: event, onTap: () => _onTapGame(event), showImage: true))); } } if (_extendingEvents) { @@ -153,9 +153,9 @@ class _AthleticsEventsContentWidgetState extends State(Styles().colors.fillColorSecondary))))); } - void _onTapGame(Game game) { + void _onTapGame(Event2 event) { Analytics().logSelect(target: 'Athletics Event'); - Navigator.push(context, CupertinoPageRoute(builder: (context) => AthleticsGameDetailPanel(game: game))); + Navigator.push(context, CupertinoPageRoute(builder: (context) => AthleticsGameDetailPanel(game: event.game, event: event))); } Events2Query _queryParam({int offset = 0, int limit = _eventsPageLength}) { @@ -301,16 +301,27 @@ class _AthleticsEventsContentWidgetState extends State[]; - sportAttribute = List.from(_teamsFilter!.map((sport) => sport.name)); + sportAttribute = List.from(_teamsFilter!.map((sport) { + return _getSportFilterKey(sport); + })); } attributes.addAll({'sport': sportAttribute}); } return attributes; } + String? _getSportFilterKey(SportDefinition? sport) { + // "Manually" select different property name for these sports because they do not match with labels in Calendar and Sports BB + if ((sport?.shortName == 'wrestling') || (sport?.shortName == 'wswim')) { + return sport?.customName; + } else { + return sport?.name; + } + } + bool get _favoritesMode => (widget.showFavorites == true); String get _emptyMessage { diff --git a/lib/ui/athletics/AthleticsGameDetailHeading.dart b/lib/ui/athletics/AthleticsGameDetailHeading.dart index 8584897e2..9cc99a281 100644 --- a/lib/ui/athletics/AthleticsGameDetailHeading.dart +++ b/lib/ui/athletics/AthleticsGameDetailHeading.dart @@ -23,6 +23,7 @@ import 'package:illinois/utils/AppUtils.dart'; import 'package:rokwire_plugin/model/auth2.dart'; import 'package:illinois/model/livestats/LiveGame.dart'; import 'package:illinois/model/sport/SportDetails.dart'; +import 'package:rokwire_plugin/model/event2.dart'; import 'package:rokwire_plugin/service/notification_service.dart'; import 'package:illinois/service/Auth2.dart'; import 'package:illinois/service/Sports.dart'; @@ -45,9 +46,10 @@ import 'package:url_launcher/url_launcher.dart'; class AthleticsGameDetailHeading extends StatefulWidget { final Game? game; + final Event2? sportEvent; final bool showImageTout; - AthleticsGameDetailHeading({this.game, this.showImageTout = true}); + AthleticsGameDetailHeading({this.game, this.sportEvent, this.showImageTout = true}); _AthleticsGameDetailHeadingState createState() => _AthleticsGameDetailHeadingState(); } @@ -98,7 +100,7 @@ class _AthleticsGameDetailHeadingState extends State bool hasScores = sportDefinition?.hasScores ?? false; bool hasLiveGame = (Storage().debugDisableLiveGameCheck == true) || LiveStats().hasLiveGame(widget.game?.id); bool showScore = hasScores && (widget.game?.isGameDay ?? false) && hasLiveGame; - bool isGameFavorite = Auth2().isFavorite(widget.game); + bool isSportEventFavorite = Auth2().isFavorite(widget.sportEvent); bool isUpcomingGame = widget.game?.isUpcoming ?? false; String? liveStatsUrl = widget.game?.links?.liveStats; String? audioUrl = widget.game?.links?.audio; @@ -158,10 +160,10 @@ class _AthleticsGameDetailHeadingState extends State label: Localization().getStringEx("widget.game_detail_heading.button.save_game.title", "Save Game"), hint: Localization().getStringEx("widget.game_detail_heading.button.save_game.hint", "Tap to save"), button: true, - checked: isGameFavorite, + checked: isSportEventFavorite, child: GestureDetector( child: Container(padding: EdgeInsets.only(right: 24, left: 10, bottom: 20, top: 20), - child: Styles().images.getImage(isGameFavorite ? 'star-filled' : 'star-outline-gray', excludeFromSemantics: true + child: Styles().images.getImage(isSportEventFavorite ? 'star-filled' : 'star-outline-gray', excludeFromSemantics: true )), onTap: _onTapSwitchFavorite), ), @@ -439,7 +441,7 @@ class _AthleticsGameDetailHeadingState extends State void _onTapSwitchFavorite() { Analytics().logSelect(target: "Favorite: ${widget.game?.title}"); - Auth2().prefs?.toggleFavorite(widget.game); + Auth2().prefs?.toggleFavorite(widget.sportEvent); } void _onTapGetTickets() { diff --git a/lib/ui/athletics/AthleticsGameDetailPanel.dart b/lib/ui/athletics/AthleticsGameDetailPanel.dart index 67621d705..38a67b065 100644 --- a/lib/ui/athletics/AthleticsGameDetailPanel.dart +++ b/lib/ui/athletics/AthleticsGameDetailPanel.dart @@ -131,7 +131,7 @@ class _AthleticsGameDetailPanelState extends Event2Selector2State[ - AthleticsGameDetailHeading(game: game, showImageTout: false, ), + AthleticsGameDetailHeading(game: game, sportEvent: widget.event, showImageTout: false, ), Padding( padding: EdgeInsets.symmetric(horizontal: 24, vertical: 16), child: Column( diff --git a/lib/ui/attributes/ContentAttributesPanel.dart b/lib/ui/attributes/ContentAttributesPanel.dart index afc3604c7..88e81f8e1 100644 --- a/lib/ui/attributes/ContentAttributesPanel.dart +++ b/lib/ui/attributes/ContentAttributesPanel.dart @@ -297,19 +297,21 @@ class _ContentAttributesPanelState extends State { borderRadius: BorderRadius.all(Radius.circular(4)) ), //padding: const EdgeInsets.only(left: 12, right: 8), - child: InkWell(onTap: () => enabled ? _onAttributeCheckbox(attribute) : null, - child: Row(crossAxisAlignment: CrossAxisAlignment.start, children: [ - Expanded(child: - Padding(padding: EdgeInsets.only(left: 12, top: 16, bottom: 16), child: - Text(text ?? '', style: textStyle,) + child: Semantics(enabled: enabled, checked: displayValue?? false, child: + InkWell(onTap: () => enabled ? _onAttributeCheckbox(attribute) : null, + child: Row(crossAxisAlignment: CrossAxisAlignment.start, children: [ + Expanded(child: + Padding(padding: EdgeInsets.only(left: 12, top: 16, bottom: 16), child: + Text(text ?? '', style: textStyle,) + ), ), - ), - Padding(padding: EdgeInsets.only(left: 16, right: 16, top: 16, bottom: 16), child: - Styles().images.getImage(imageAsset, excludeFromSemantics: true,) ?? Container(), - ), - ]), + Padding(padding: EdgeInsets.only(left: 16, right: 16, top: 16, bottom: 16), child: + Styles().images.getImage(imageAsset, excludeFromSemantics: true,) ?? Container(), + ), + ]), + ), ), - ), + ) ]), ); } diff --git a/lib/ui/events2/Event2CreatePanel.dart b/lib/ui/events2/Event2CreatePanel.dart index cf93946c8..a4ff61e49 100644 --- a/lib/ui/events2/Event2CreatePanel.dart +++ b/lib/ui/events2/Event2CreatePanel.dart @@ -631,6 +631,7 @@ class _Event2CreatePanelState extends State { return SingleChildScrollView(child: Column(children: [ _buildImageWidget(), + _buildImageDescriptionSection(), Padding(padding: EdgeInsets.symmetric(horizontal: 16, vertical: 24), child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ _buildTitleSection(), @@ -705,6 +706,20 @@ class _Event2CreatePanelState extends State { } + Widget _buildImageDescriptionSection() { + return Padding( + padding: EdgeInsets.only(left: 16, right: 16, top: 20), + child: Row(children: [ + Expanded( + child: Text( + Localization().getStringEx('panel.event2.create.section.image.description', + "The event image displays a 16:9 or 1000px x 615px jpg, png, or gif (not animated). Larger images are automatically positioned within the frame and can be tapped to view in their entirety within the Illinois app."), + overflow: TextOverflow.ellipsis, + maxLines: 8, + style: Styles().textStyles.getTextStyle('widget.message.small'))) + ])); + } + // Title and Description Widget _buildTitleSection() => Event2CreatePanel.buildSectionWidget( diff --git a/lib/ui/events2/Event2DetailPanel.dart b/lib/ui/events2/Event2DetailPanel.dart index e66af0eb0..4fb3f5623 100644 --- a/lib/ui/events2/Event2DetailPanel.dart +++ b/lib/ui/events2/Event2DetailPanel.dart @@ -654,7 +654,7 @@ class _Event2DetailPanelState extends Event2Selector2State im } else if (StringUtils.isNotEmpty(_event?.registrationDetails?.externalLink)) { // else external registration // if (_event?.userRole == null){ //TBD check if this is correct check or we don't know if the user is registered externally return [_buildButtonWidget( - title: Localization().getStringEx('panel.event2.detail.button.register.title', 'Register'), + title: StringUtils.ensureNotEmpty(_event?.registrationDetails?.label, defaultValue: Localization().getStringEx('panel.event2.detail.button.register.title', 'Register')), onTap: _onExternalRegistration, progress: _registrationLaunching, externalLink: true @@ -888,7 +888,7 @@ class _Event2DetailPanelState extends Event2Selector2State im } void _launchUrl(String? url, { BuildContext? context, void Function(bool progress)? updateProgress }) { - Uri? uri = UrlUtils.parseUri(url); + Uri? uri = UrlUtils.parseUri(url?.trim()); if (uri != null) { if (updateProgress != null) { updateProgress(true); diff --git a/lib/ui/events2/Event2SetupRegistrationPanel.dart b/lib/ui/events2/Event2SetupRegistrationPanel.dart index 3162117c8..3da12ef8b 100644 --- a/lib/ui/events2/Event2SetupRegistrationPanel.dart +++ b/lib/ui/events2/Event2SetupRegistrationPanel.dart @@ -327,16 +327,17 @@ class _Event2SetupRegistrationPanelState extends State implements Notifica } Widget _buildImageHeader(){ - return StringUtils.isNotEmpty(_group?.imageURL) ? Container(height: 200, color: Styles().colors.background, child: - Stack(alignment: Alignment.bottomCenter, children: [ - Positioned.fill(child: ModalImageHolder(child: Image.network(_group!.imageURL!, excludeFromSemantics: true, fit: BoxFit.cover, headers: Config().networkAuthHeaders))), - CustomPaint(painter: TrianglePainter(painterColor: Styles().colors.fillColorSecondaryTransparent05, horzDir: TriangleHorzDirection.leftToRight), child: - Container(height: 53,), - ), - CustomPaint(painter: TrianglePainter(painterColor: Styles().colors.white), child: - Container(height: 30,), - ), - ], - ), - ) : Container(); + return StringUtils.isNotEmpty(_group?.imageURL) ? Semantics(label: "group image", hint: "Double tap to zoom", child: + Container(height: 200, color: Styles().colors.background, child: + Stack(alignment: Alignment.bottomCenter, children: [ + Positioned.fill(child: ModalImageHolder(child: Image.network(_group!.imageURL!, excludeFromSemantics: true, fit: BoxFit.cover, headers: Config().networkAuthHeaders))), + CustomPaint(painter: TrianglePainter(painterColor: Styles().colors.fillColorSecondaryTransparent05, horzDir: TriangleHorzDirection.leftToRight), child: + Container(height: 53,), + ), + CustomPaint(painter: TrianglePainter(painterColor: Styles().colors.white), child: + Container(height: 30,), + ), + ], + ), + ) + ): Container(); } Widget _buildGroupInfo() { diff --git a/lib/ui/groups/GroupMemberPanel.dart b/lib/ui/groups/GroupMemberPanel.dart index ae88d2cb9..50dd1591f 100644 --- a/lib/ui/groups/GroupMemberPanel.dart +++ b/lib/ui/groups/GroupMemberPanel.dart @@ -155,7 +155,10 @@ class _GroupMemberPanelState extends State { padding: const EdgeInsets.symmetric(vertical: 16), child: ClipRRect( borderRadius: BorderRadius.circular(65), - child: Container(width: 65, height: 65, child: GroupMemberProfileImage(userId: _member?.userId)), + child: Container(width: 65, height: 65, child: + Semantics(label: "user image", hint: "Double tap to zoom", child: + GroupMemberProfileImage(userId: _member?.userId)) + ), ), ), Container(width: 16,), diff --git a/lib/ui/groups/GroupMembersPanel.dart b/lib/ui/groups/GroupMembersPanel.dart index ce360f824..29bd9a263 100644 --- a/lib/ui/groups/GroupMembersPanel.dart +++ b/lib/ui/groups/GroupMembersPanel.dart @@ -372,15 +372,17 @@ class _GroupMembersPanelState extends State implements Notifi return Visibility(visible: showSynced || showUpdated, child: Container(child: Padding(padding: EdgeInsets.symmetric(vertical: 10, horizontal: 16), child: Column(crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: [ Visibility(visible: showSynced, - child: Row(mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.center, children: [ - Padding(padding: EdgeInsets.only(right: 5), child: Text(Localization().getStringEx('panel.group_detail.date.updated.managed.membership.label', 'Last sync:'), style: Styles().textStyles.getTextStyle('panel.group.detail.fat'))), - Text(StringUtils.ensureNotEmpty(_group?.displayManagedMembershipUpdateTime, defaultValue: 'N/A'), style: Styles().textStyles.getTextStyle('panel.group.detail.fat')) - ])), + child: Semantics(container: true, child: + Row(mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.center, children: [ + Padding(padding: EdgeInsets.only(right: 5), child: Text(Localization().getStringEx('panel.group_detail.date.updated.managed.membership.label', 'Last sync:'), style: Styles().textStyles.getTextStyle('panel.group.detail.fat'))), + Text(StringUtils.ensureNotEmpty(_group?.displayManagedMembershipUpdateTime, defaultValue: 'N/A'), style: Styles().textStyles.getTextStyle('panel.group.detail.fat')) + ]))), Visibility(visible: showUpdated, - child: Padding(padding: EdgeInsets.only(top: 5), child: Row(mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.center, children: [ - Padding(padding: EdgeInsets.only(right: 5), child: Text(Localization().getStringEx('panel.group_detail.date.updated.membership.label', 'Last updated:'), style: Styles().textStyles.getTextStyle('panel.group.detail.fat'))), - Text(StringUtils.ensureNotEmpty(_group?.displayMembershipUpdateTime, defaultValue: 'N/A'), style: Styles().textStyles.getTextStyle('panel.group.detail.fat')) - ]))) + child: Semantics(container: true, + child: Padding(padding: EdgeInsets.only(top: 5), child: Row(mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.center, children: [ + Padding(padding: EdgeInsets.only(right: 5), child: Text(Localization().getStringEx('panel.group_detail.date.updated.membership.label', 'Last updated:'), style: Styles().textStyles.getTextStyle('panel.group.detail.fat'))), + Text(StringUtils.ensureNotEmpty(_group?.displayMembershipUpdateTime, defaultValue: 'N/A'), style: Styles().textStyles.getTextStyle('panel.group.detail.fat')) + ])))) ])))); } @@ -442,6 +444,7 @@ class _GroupMembersPanelState extends State implements Notifi Container( constraints: BoxConstraints(minHeight: MediaQuery.of(context).size.height), child: BlockSemantics(child: + Semantics(label: "dismiss", child: GestureDetector( onTap: () { Analytics().logSelect(target: 'Close Dropdown'); @@ -452,7 +455,8 @@ class _GroupMembersPanelState extends State implements Notifi child: Container(color: Styles().colors.blackTransparent06) ) ) - ); + ) + ); } Widget _buildStatusValuesWidget() { @@ -649,7 +653,8 @@ class _GroupMemberCard extends StatelessWidget { children: [ ClipRRect( borderRadius: BorderRadius.circular(65), - child: Container(width: 65, height: 65, child: GroupMemberProfileImage(userId: member?.userId))), + child: Semantics(label: "user image", hint: "Double tap to zoom", child: + Container(width: 65, height: 65, child: GroupMemberProfileImage(userId: member?.userId)))), Expanded( child: Padding( padding: const EdgeInsets.only(left: 11), diff --git a/lib/ui/groups/GroupSettingsPanel.dart b/lib/ui/groups/GroupSettingsPanel.dart index c819585d7..453d7a9bf 100644 --- a/lib/ui/groups/GroupSettingsPanel.dart +++ b/lib/ui/groups/GroupSettingsPanel.dart @@ -205,7 +205,7 @@ class _GroupSettingsPanelState extends State { child: Stack( alignment: Alignment.bottomCenter, children: [ - StringUtils.isNotEmpty(_group?.imageURL) ? Positioned.fill(child: ModalImageHolder(child: Image.network(_group!.imageURL!, excludeFromSemantics: true, fit: BoxFit.cover, headers: Config().networkAuthHeaders))) : Container(), + StringUtils.isNotEmpty(_group?.imageURL) ? Positioned.fill(child: Semantics(label: "Group Image", child: ModalImageHolder(child: Image.network(_group!.imageURL!, excludeFromSemantics: true, fit: BoxFit.cover, headers: Config().networkAuthHeaders)))) : Container(), CustomPaint( painter: TrianglePainter(painterColor: Styles().colors.fillColorSecondaryTransparent05, horzDir: TriangleHorzDirection.leftToRight), child: Container( @@ -489,16 +489,18 @@ class _GroupSettingsPanelState extends State { for (ContentAttribute attribute in attributes) { List? displayAttributeValues = attribute.displaySelectedLabelsFromSelection(groupAttributes, complete: true); if ((displayAttributeValues != null) && displayAttributeValues.isNotEmpty) { - attributesList.add(Row(crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text("${attribute.displayTitle}: ", overflow: TextOverflow.ellipsis, maxLines: 1, style: - Styles().textStyles.getTextStyle("widget.card.detail.small.fat") - ), - Expanded(child: - Text(displayAttributeValues.join(', '), /*overflow: TextOverflow.ellipsis, maxLines: 1,*/ style: - Styles().textStyles.getTextStyle("widget.card.detail.small.regular") + attributesList.add( + Semantics(container:true, child: + Row(crossAxisAlignment: CrossAxisAlignment.start, children: [ + Text("${attribute.displayTitle}: ", overflow: TextOverflow.ellipsis, maxLines: 1, style: + Styles().textStyles.getTextStyle("widget.card.detail.small.fat") + ), + Expanded(child: + Text(displayAttributeValues.join(', '), /*overflow: TextOverflow.ellipsis, maxLines: 1,*/ style: + Styles().textStyles.getTextStyle("widget.card.detail.small.regular") + ), ), - ), - ],),); + ],)),); } } } @@ -543,6 +545,7 @@ class _GroupSettingsPanelState extends State { child:Column(children: [ Semantics( explicitChildNodes: true, + container: true, child: Container( child: GroupDropDownButton( emptySelectionText: Localization().getStringEx("panel.groups_settings.privacy.hint.default","Select privacy setting.."), diff --git a/lib/ui/groups/GroupWidgets.dart b/lib/ui/groups/GroupWidgets.dart index b67dbaa2f..f7a357041 100644 --- a/lib/ui/groups/GroupWidgets.dart +++ b/lib/ui/groups/GroupWidgets.dart @@ -72,7 +72,7 @@ class GroupSectionTitle extends StatelessWidget { Widget build(BuildContext context) { return Container(padding: margin, child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ - Semantics(label: title, hint: description, header: true, excludeSemantics: true, child: + Semantics(label: _semanticsLabel, hint: description, header: true, excludeSemantics: true, child: RichText(text: TextSpan(text: title, style: titleTextStyle ?? Styles().textStyles.getTextStyle("widget.title.tiny"), children: [ @@ -87,6 +87,8 @@ class GroupSectionTitle extends StatelessWidget { ],) ); } + + String? get _semanticsLabel => "$title ${requiredMark == true ? ", required" : ""}"; } ///////////////////////////////////// @@ -853,7 +855,7 @@ class _GroupCardState extends State implements NotificationsListener // flex: 1, // child: Semantics( - label: "post image", + label: "Group image", button: true, hint: "Double tap to zoom the image", child: GestureDetector( diff --git a/lib/ui/home/HomeAthleticsEventsWidget.dart b/lib/ui/home/HomeAthleticsEventsWidget.dart index 8063dd4dd..e97efb585 100644 --- a/lib/ui/home/HomeAthleticsEventsWidget.dart +++ b/lib/ui/home/HomeAthleticsEventsWidget.dart @@ -202,7 +202,7 @@ class _HomeAthleticsEventsWidgetState extends State i Analytics().logSelect(target: "Event: '${event.name}'" , source: widget.runtimeType.toString()); if (Connectivity().isNotOffline) { if (event.hasGame) { - Navigator.push(context, CupertinoPageRoute( builder: (context) => AthleticsGameDetailPanel(game: event.game))); + Navigator.push(context, CupertinoPageRoute( builder: (context) => AthleticsGameDetailPanel(game: event.game, event: event))); } else { Navigator.push(context, CupertinoPageRoute(builder: (context) => Event2DetailPanel(event: event))); } diff --git a/lib/ui/home/HomeEmptyContentWidget.dart b/lib/ui/home/HomeEmptyContentWidget.dart new file mode 100644 index 000000000..6a9721ad7 --- /dev/null +++ b/lib/ui/home/HomeEmptyContentWidget.dart @@ -0,0 +1,69 @@ + +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:illinois/service/Analytics.dart'; +import 'package:illinois/ui/BrowsePanel.dart'; +import 'package:illinois/ui/home/HomeWidgets.dart'; +import 'package:illinois/ui/profile/ProfileHomePanel.dart'; +import 'package:illinois/ui/settings/SettingsHomeContentPanel.dart'; +import 'package:rokwire_plugin/service/localization.dart'; +import 'package:rokwire_plugin/service/notification_service.dart'; + +class HomeEmptyContentWidget extends StatelessWidget { + final String? favoriteId; + final StreamController? updateController; + + static const String _localScheme = 'local'; + + static const String _browseLocalUrlMacro = '{{browse_local_url}}'; + static const String _browseLocalUrl = 'browse'; + + static const String _signInLocalUrlMacro = '{{sign_in_local_url}}'; + static const String _signInLocalUrl = 'sign_in'; + + static const String _privacySettingsLocalUrlMacro = '{{privacy_settings_local_url}}'; + static const String _privacySettingsLocalUrl = 'privacy_settings'; + + HomeEmptyContentWidget({ super.key, this.favoriteId, this.updateController}); + + @override + Widget build(BuildContext context) { + String htmlContent = Localization().getStringEx("widget.home.empty.content.text", "Tap the \u2606s under Browse to add shortcuts to Favorites. Note that some features require specific privacy settings and signing in with your NetID, phone number, or email address.") + .replaceAll(_browseLocalUrlMacro, '$_localScheme://$_browseLocalUrl') + .replaceAll(_signInLocalUrlMacro, '$_localScheme://$_signInLocalUrl') + .replaceAll(_privacySettingsLocalUrlMacro, '$_localScheme://$_privacySettingsLocalUrl'); + + return HomeSlantWidget(favoriteId: null /*widget.favoriteId*/, + title: Localization().getStringEx("panel.home.header.title", "Favorites"), + childPadding: HomeSlantWidget.defaultChildPadding, + child: Column(children: [ + HomeMessageHtmlCard( + message: htmlContent, + margin: EdgeInsets.only(bottom: 16), + onTapLink : (url) => _onTapLink(context, url), + ) + ],), + ); + } + + void _onTapLink(BuildContext context, String? url) { + Uri? uri = (url != null) ? Uri.tryParse(url) : null; + if (uri?.scheme == _localScheme) { + if (uri?.host == _browseLocalUrl) { + Analytics().logSelect(target: 'Browse', source: runtimeType.toString()); + NotificationService().notify(BrowsePanel.notifySelect); + } + else if (uri?.host == _signInLocalUrl) { + Analytics().logSelect(target: 'Sign In', source: runtimeType.toString()); + ProfileHomePanel.present(context, content: ProfileContent.login); + } + else if (uri?.host == _privacySettingsLocalUrl) { + Analytics().logSelect(target: 'Privacy Settings', source: runtimeType.toString()); + SettingsHomeContentPanel.present(context, content: SettingsContent.privacy); + } + } + } +} + + diff --git a/lib/ui/home/HomePanel.dart b/lib/ui/home/HomePanel.dart index fd272ea6c..bae91eaa7 100644 --- a/lib/ui/home/HomePanel.dart +++ b/lib/ui/home/HomePanel.dart @@ -39,6 +39,7 @@ import 'package:illinois/ui/home/HomeCheckListWidget.dart'; import 'package:illinois/ui/home/HomeCustomizeFavoritesPanel.dart'; import 'package:illinois/ui/home/HomeDailyIlliniWidget.dart'; import 'package:illinois/ui/home/HomeDiningWidget.dart'; +import 'package:illinois/ui/home/HomeEmptyContentWidget.dart'; import 'package:illinois/ui/home/HomeEvent2Widget.dart'; import 'package:illinois/ui/home/HomeFavoritesWidget.dart'; import 'package:illinois/ui/home/HomeInboxWidget.dart'; @@ -566,10 +567,8 @@ class _HomePanelState extends State with AutomaticKeepAliveClientMixi Widget build(BuildContext context) { super.build(context); - String title = Localization().getStringEx('panel.home.header.title', 'Favorites'); - return Scaffold( - appBar: RootHeaderBar(title: title), + appBar: RootHeaderBar(title: Localization().getStringEx('panel.home.header.title', 'Favorites')), body: RefreshIndicator(onRefresh: _onPullToRefresh, child: Column(key: _contentWrapperKey, children: [ Expanded(child: @@ -587,7 +586,11 @@ class _HomePanelState extends State with AutomaticKeepAliveClientMixi List _buildRegularContentList() { List widgets = []; widgets.addAll(_buildWidgetsFromCodes(_systemCodes)); - widgets.addAll(_buildWidgetsFromCodes(_favoriteCodes?.reversed, availableCodes: _availableCodes)); + List favWidgets = _buildWidgetsFromCodes(_favoriteCodes?.reversed, availableCodes: _availableCodes); + if (favWidgets.isEmpty) { + favWidgets.add(HomeEmptyContentWidget()); + } + widgets.addAll(favWidgets); return widgets; } @@ -620,7 +623,7 @@ class _HomePanelState extends State with AutomaticKeepAliveClientMixi return HomeLoginWidget(key: _widgetKey(code), favoriteId: code, updateController: _updateController,); } else if (code == 'welcome') { - return HomeWelcomeWidget(key: _widgetKey(code), favoriteId: code, updateController: _updateController,); //TBD + return HomeWelcomeWidget(key: _widgetKey(code), favoriteId: code, updateController: _updateController,); } else { dynamic data = HomePanel.dataFromCode(code, diff --git a/lib/ui/home/HomeWelcomeWidget.dart b/lib/ui/home/HomeWelcomeWidget.dart index bc2bbed22..d6f610f73 100644 --- a/lib/ui/home/HomeWelcomeWidget.dart +++ b/lib/ui/home/HomeWelcomeWidget.dart @@ -6,8 +6,6 @@ import 'package:illinois/model/Video.dart'; import 'package:illinois/service/Analytics.dart'; import 'package:illinois/service/Content.dart'; import 'package:illinois/service/Storage.dart'; -import 'package:illinois/ui/home/HomePanel.dart'; -import 'package:illinois/ui/home/HomeWidgets.dart'; import 'package:illinois/ui/settings/SettingsVideoTutorialPanel.dart'; import 'package:illinois/ui/widgets/VideoPlayButton.dart'; import 'package:illinois/utils/AppUtils.dart'; @@ -23,11 +21,6 @@ class HomeWelcomeWidget extends StatefulWidget { HomeWelcomeWidget({Key? key, this.favoriteId, this.updateController}) : super(key: key); - static Widget handle({Key? key, String? favoriteId, HomeDragAndDropHost? dragAndDropHost, int? position}) => - HomeHandleWidget(key: key, favoriteId: favoriteId, dragAndDropHost: dragAndDropHost, position: position, - title: Localization().getStringEx("widget.home_create_poll.heading.title", "Polls"), - ); - @override State createState() => _HomeWelcomeWidgetState(); } diff --git a/lib/ui/home/HomeWidgets.dart b/lib/ui/home/HomeWidgets.dart index 4f1eb4a05..fe69fcb87 100644 --- a/lib/ui/home/HomeWidgets.dart +++ b/lib/ui/home/HomeWidgets.dart @@ -269,6 +269,11 @@ class HomeSlantWidget extends StatelessWidget { @override Widget build(BuildContext context) { + EdgeInsetsGeometry titleTextPadding = EdgeInsets.only(top: 12, bottom: 12, + left: (titleIconKey == null) ? 16 : 0, + right: ((actions == null) && (favoriteId == null)) ? 16 : 0, + ); + return Column(children: [ // Title Row @@ -277,10 +282,11 @@ class HomeSlantWidget extends StatelessWidget { child: Container(color: Styles().colors.fillColorPrimary, child: Row(crossAxisAlignment: headerAxisAlignment, children: [ - HomeTitleIcon(image: Styles().images.getImage(titleIconKey, excludeFromSemantics: true)), + if (titleIconKey != null) + HomeTitleIcon(image: Styles().images.getImage(titleIconKey, excludeFromSemantics: true)), Expanded(child: - Padding(padding: EdgeInsets.symmetric(vertical: 12), child: + Padding(padding: titleTextPadding, child: Semantics(label: title, header: true, excludeSemantics: true, child: Text(title ?? '', style: Styles().textStyles.getTextStyle("widget.title.light.large.extra_fat")) ) diff --git a/plugin b/plugin index 0227ff995..c0815bea2 160000 --- a/plugin +++ b/plugin @@ -1 +1 @@ -Subproject commit 0227ff995c2aecdc8a6e9245f61a1177d389f9a2 +Subproject commit c0815bea272f25d421079a72be981427b3cd85a7 diff --git a/pubspec.yaml b/pubspec.yaml index 7609c1463..6e8ecc205 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -11,7 +11,7 @@ description: Illinois client application. # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 5.1.32+1932 +version: 5.1.34+1934 publish_to: none