From bfe932c05312938de58923e26ad265afbea9424b Mon Sep 17 00:00:00 2001 From: Pierre HUBERT Date: Thu, 30 Dec 2021 12:02:18 +0100 Subject: [PATCH] Start to display banner --- lib/helpers/server_config_helper.dart | 12 +++ lib/models/server_config.dart | 39 ++++++++ .../routes/main_route/smartphone_route.dart | 10 ++- lib/ui/widgets/banner_widget.dart | 89 +++++++++++++++++++ lib/utils/intl_utils.dart | 5 ++ 5 files changed, 154 insertions(+), 1 deletion(-) create mode 100644 lib/ui/widgets/banner_widget.dart diff --git a/lib/helpers/server_config_helper.dart b/lib/helpers/server_config_helper.dart index b38ea01..821ffd9 100644 --- a/lib/helpers/server_config_helper.dart +++ b/lib/helpers/server_config_helper.dart @@ -17,6 +17,7 @@ class ServerConfigurationHelper { (await APIRequest.withoutLogin("server/config").execWithThrow()) .getObject(); + final banner = response["banner"]; final pushNotificationsPolicy = response["push_notifications"]; final passwordPolicy = response["password_policy"]; final dataConservationPolicy = response["data_conservation_policy"]; @@ -31,6 +32,15 @@ class ServerConfigurationHelper { contactEmail: response["contact_email"], playStoreURL: response["play_store_url"], androidDirectDownloadURL: response["android_direct_download_url"], + banner: banner == null + ? null + : Banner( + enabled: banner["enabled"], + expire: banner["expire"], + nature: BannerNatureExt.fromStr(banner["nature"]), + message: Map.from(banner["message"]) + .map((key, value) => MapEntry(key, value.toString())), + link: banner["link"]), notificationsPolicy: NotificationsPolicy( hasFirebase: pushNotificationsPolicy["has_firebase"], hasIndependent: pushNotificationsPolicy["has_independent"], @@ -98,3 +108,5 @@ class ServerConfigurationHelper { /// Shortcut for server configuration ServerConfig get srvConfig => ServerConfigurationHelper.config; + +bool get showBanner => srvConfig.banner != null && srvConfig.banner.visible; \ No newline at end of file diff --git a/lib/models/server_config.dart b/lib/models/server_config.dart index 5618269..b60983e 100644 --- a/lib/models/server_config.dart +++ b/lib/models/server_config.dart @@ -1,3 +1,4 @@ +import 'package:comunic/utils/date_utils.dart'; import 'package:flutter/widgets.dart'; import 'package:version/version.dart'; @@ -132,6 +133,42 @@ class AccountInformationPolicy { assert(maxLocationLength != null); } +enum BannerNature { Information, Warning, Success } + +extension BannerNatureExt on BannerNature { + static BannerNature fromStr(String s) { + switch (s) { + case "information": + return BannerNature.Information; + case "success": + return BannerNature.Success; + case "warning": + default: + return BannerNature.Warning; + } + } +} + +class Banner { + final bool enabled; + final int expire; + final BannerNature nature; + final Map message; + final String link; + + const Banner({ + @required this.enabled, + @required this.expire, + @required this.nature, + @required this.message, + @required this.link, + }) : assert(enabled != null), + assert(nature != null), + assert(message != null); + + bool get visible => enabled && (expire == null || expire > time()); +} + class ServerConfig { final Version minSupportedMobileVersion; final String termsURL; @@ -139,6 +176,7 @@ class ServerConfig { final String contactEmail; final String playStoreURL; final String androidDirectDownloadURL; + final Banner banner; final NotificationsPolicy notificationsPolicy; final PasswordPolicy passwordPolicy; final ServerDataConservationPolicy dataConservationPolicy; @@ -152,6 +190,7 @@ class ServerConfig { @required this.contactEmail, @required this.playStoreURL, @required this.androidDirectDownloadURL, + @required this.banner, @required this.notificationsPolicy, @required this.passwordPolicy, @required this.dataConservationPolicy, diff --git a/lib/ui/routes/main_route/smartphone_route.dart b/lib/ui/routes/main_route/smartphone_route.dart index 00b9ea5..41b05e5 100644 --- a/lib/ui/routes/main_route/smartphone_route.dart +++ b/lib/ui/routes/main_route/smartphone_route.dart @@ -1,6 +1,7 @@ import 'package:comunic/ui/routes/main_route/main_route.dart'; import 'package:comunic/ui/routes/main_route/page_info.dart'; import 'package:comunic/ui/screens/notifications_screen.dart'; +import 'package:comunic/ui/widgets/banner_widget.dart'; import 'package:comunic/ui/widgets/mobile_mode/mobile_appbar_widget.dart'; import 'package:flutter/material.dart'; @@ -33,7 +34,14 @@ class _MainRouteState extends MainController { appBar: currentPage.hideNavBar ? null : ComunicMobileAppBar(currentPage: currentPage), - body: SafeArea(key: currentPage.key, child: currentPage.child), + body: SafeArea( + key: currentPage.key, + child: Column( + children: [ + BannerWidget(), + Expanded(child: currentPage.child) + ], + )), ), ), ), diff --git a/lib/ui/widgets/banner_widget.dart b/lib/ui/widgets/banner_widget.dart new file mode 100644 index 0000000..36a5002 --- /dev/null +++ b/lib/ui/widgets/banner_widget.dart @@ -0,0 +1,89 @@ +import 'package:comunic/helpers/server_config_helper.dart'; +import 'package:comunic/models/server_config.dart'; +import 'package:comunic/utils/intl_utils.dart'; +import 'package:flutter/material.dart'; +import 'package:url_launcher/url_launcher.dart'; + +bool _bannerDismissed = false; + +class BannerWidget extends StatefulWidget { + const BannerWidget({Key key}) : super(key: key); + + @override + _BannerWidgetState createState() => _BannerWidgetState(); +} + +class _BannerWidgetState extends State { + void _hideBanner() { + setState(() => _bannerDismissed = true); + } + + void _openLink() { + launch(srvConfig.banner.link); + } + + @override + Widget build(BuildContext context) { + if (!showBanner || _bannerDismissed) return Container(); + + final banner = srvConfig.banner; + return Card( + color: banner.nature == BannerNature.Information + ? Colors.blue + : banner.nature == BannerNature.Success + ? Colors.green + : Colors.red, + child: Padding( + padding: const EdgeInsets.all(2.0), + child: Row( + children: [ + BannerButton( + icon: Icon( + banner.nature == BannerNature.Information + ? Icons.info + : banner.nature == BannerNature.Success + ? Icons.check + : Icons.warning, + ), + ), + Flexible( + child: Text( + banner.message.containsKey(lang()) + ? banner.message[lang()] + : banner.message["en"], + style: TextStyle(color: Colors.white), + ), + ), + banner.link == null + ? Container() + : BannerButton( + onPressed: _openLink, + icon: Icon(Icons.open_in_new), + ), + BannerButton( + onPressed: _hideBanner, + icon: Icon(Icons.close), + ) + ], + ), + ), + ); + } +} + +class BannerButton extends StatelessWidget { + final Function() onPressed; + final Widget icon; + + const BannerButton({this.onPressed, this.icon, Key key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return IconButton( + onPressed: onPressed, + icon: icon, + disabledColor: Colors.white, + padding: EdgeInsets.all(1.0), + ); + } +} diff --git a/lib/utils/intl_utils.dart b/lib/utils/intl_utils.dart index 65f07f0..070841d 100644 --- a/lib/utils/intl_utils.dart +++ b/lib/utils/intl_utils.dart @@ -48,3 +48,8 @@ String tr(String string, {Map args}) { return string; } + +/// Get current lang +String lang() { + return _currLang != null ? _currLang : "en"; +}