From ee34cb17649ff32899e35fc75d76979917b5e3fb Mon Sep 17 00:00:00 2001 From: ameera-keyvalue Date: Tue, 9 Jul 2024 14:34:03 +0530 Subject: [PATCH 01/10] fix: Remove the extra padding for app header --- lib/src/widgets/app_bar.dart | 90 ++++++++++++++++++------------------ 1 file changed, 45 insertions(+), 45 deletions(-) diff --git a/lib/src/widgets/app_bar.dart b/lib/src/widgets/app_bar.dart index 5f8db01..da5df3a 100644 --- a/lib/src/widgets/app_bar.dart +++ b/lib/src/widgets/app_bar.dart @@ -48,13 +48,13 @@ class SirenAppBar extends StatelessWidget implements PreferredSizeWidget { : null, ), height: preferredSize.height, - child: Padding( - padding: const EdgeInsets.only(right: 16, left: 20), - child: headerParams?.customHeader ?? - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Row( + child: headerParams?.customHeader ?? + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Padding( + padding: const EdgeInsets.only(right: 16, left: 20), + child: Row( children: [ if (headerParams?.showBackButton ?? false) Semantics( @@ -93,49 +93,49 @@ class SirenAppBar extends StatelessWidget implements PreferredSizeWidget { ), ], ), - if (!(headerParams?.hideClearAll ?? false)) - Semantics( - label: 'siren-header-clear-all', - hint: 'Tap to clear all notifications', - child: GestureDetector( - key: const Key('siren-header-clear-all'), - onTap: () { - if (isNonEmptyNotifications && - onClearAllPressed != null) { - onClearAllPressed!(); - } - }, - child: Opacity( - opacity: isNonEmptyNotifications ? 1 : 0.4, - child: Row( - children: [ - Padding( - padding: const EdgeInsets.only(right: 4), - child: Icon( - Icons.clear_all, - size: styles?.clearAllIconStyle?.size ?? 24, - color: colors?.clearAllIcon ?? - defaultColors.appBarActionText, - ), + ), + if (!(headerParams?.hideClearAll ?? false)) + Semantics( + label: 'siren-header-clear-all', + hint: 'Tap to clear all notifications', + child: GestureDetector( + key: const Key('siren-header-clear-all'), + onTap: () { + if (isNonEmptyNotifications && + onClearAllPressed != null) { + onClearAllPressed!(); + } + }, + child: Opacity( + opacity: isNonEmptyNotifications ? 1 : 0.4, + child: Row( + children: [ + Padding( + padding: const EdgeInsets.only(right: 4), + child: Icon( + Icons.clear_all, + size: styles?.clearAllIconStyle?.size ?? 24, + color: colors?.clearAllIcon ?? + defaultColors.appBarActionText, ), - Text( - Strings.clear_all, - style: TextStyle( - fontSize: 14, - fontWeight: FontWeight.w500, - color: colors?.inboxHeaderColors - ?.headerActionColor ?? - defaultColors.appBarActionText, - ), + ), + Text( + Strings.clear_all, + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.w500, + color: colors + ?.inboxHeaderColors?.headerActionColor ?? + defaultColors.appBarActionText, ), - ], - ), + ), + ], ), ), ), - ], - ), - ), + ), + ], + ), ); } } From e22d258a7dd5b74d63aee125f32e1db9bd7e1b18 Mon Sep 17 00:00:00 2001 From: ameera-keyvalue Date: Tue, 9 Jul 2024 15:10:02 +0530 Subject: [PATCH 02/10] fix: Map additional data --- lib/src/models/notification_model.dart | 39 ++++++++++++++++---------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/lib/src/models/notification_model.dart b/lib/src/models/notification_model.dart index 68f56f7..014433f 100644 --- a/lib/src/models/notification_model.dart +++ b/lib/src/models/notification_model.dart @@ -1,3 +1,5 @@ +import 'dart:convert'; + import 'package:flutter/material.dart'; /// Class representing the data structure of a notification. @@ -63,24 +65,31 @@ class MessageData { this.subHeader, }); - /// Factory method to create MessageData from JSON. - factory MessageData.fromJson(Map? json) { + /// Factory method to create MessageData from data. + factory MessageData.fromJson(Map? data) { + var dataDecoded; + Map? additionalData; + if (data?['additionalData'] != null) { + try { + dataDecoded = json.decode(data?['additionalData'] as String); + additionalData = dataDecoded as Map?; + } catch (error) { + additionalData = null; + } + } return MessageData( - channel: json?['channel'] as String?, - header: json?['header'] as String?, - subHeader: json?['subHeader'] as String?, - body: json?['body'] as String?, - actionUrl: json?['actionUrl'] as String?, - avatar: json?['avatar'] != null - ? AvatarData.fromJson(json?['avatar'] as Map) + channel: data?['channel'] as String?, + header: data?['header'] as String?, + subHeader: data?['subHeader'] as String?, + body: data?['body'] as String?, + actionUrl: data?['actionUrl'] as String?, + avatar: data?['avatar'] != null + ? AvatarData.fromJson(data?['avatar'] as Map) : null, - thumbnailUrl: json?['thumbnailUrl'] != null - ? (json?['thumbnailUrl'] as String?) + thumbnailUrl: data?['thumbnailUrl'] != null + ? (data?['thumbnailUrl'] as String?) : '', - additionalData: - (json?['additionalData'] != null && json?['additionalData'] is Map) - ? json!['additionalData'] as Map? - : null, + additionalData: additionalData, ); } From 9a7eeaca58ab0802838ec4ca540a8ec06f8ecd70 Mon Sep 17 00:00:00 2001 From: ameera-keyvalue Date: Tue, 9 Jul 2024 16:15:11 +0530 Subject: [PATCH 03/10] fix: Map additional data lint errror --- lib/src/models/notification_model.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/src/models/notification_model.dart b/lib/src/models/notification_model.dart index 014433f..582921e 100644 --- a/lib/src/models/notification_model.dart +++ b/lib/src/models/notification_model.dart @@ -67,6 +67,7 @@ class MessageData { /// Factory method to create MessageData from data. factory MessageData.fromJson(Map? data) { + // ignore: prefer_typing_uninitialized_variables var dataDecoded; Map? additionalData; if (data?['additionalData'] != null) { From 6292e77e6f9047cd4aff7a61456bdad9174135bc Mon Sep 17 00:00:00 2001 From: ameera-keyvalue Date: Tue, 9 Jul 2024 16:39:46 +0530 Subject: [PATCH 04/10] fix: Map additional data and update tests --- lib/src/models/notification_model.dart | 15 ++++++--------- test/models/notification_model_test.dart | 8 ++++---- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/lib/src/models/notification_model.dart b/lib/src/models/notification_model.dart index 582921e..4cc31a6 100644 --- a/lib/src/models/notification_model.dart +++ b/lib/src/models/notification_model.dart @@ -67,17 +67,14 @@ class MessageData { /// Factory method to create MessageData from data. factory MessageData.fromJson(Map? data) { - // ignore: prefer_typing_uninitialized_variables - var dataDecoded; Map? additionalData; - if (data?['additionalData'] != null) { - try { - dataDecoded = json.decode(data?['additionalData'] as String); - additionalData = dataDecoded as Map?; - } catch (error) { - additionalData = null; - } + if (data?['additionalData'] != null) { + try { + additionalData = json.decode(data?['additionalData'] as String) as Map?; + } catch (error) { + additionalData = null; } + } return MessageData( channel: data?['channel'] as String?, header: data?['header'] as String?, diff --git a/test/models/notification_model_test.dart b/test/models/notification_model_test.dart index 84dc69b..343f592 100644 --- a/test/models/notification_model_test.dart +++ b/test/models/notification_model_test.dart @@ -15,7 +15,7 @@ void main() { 'body': 'body', 'actionUrl': 'actionUrl', 'avatar': {'imageUrl': 'avatarUrl', 'altText': 'altText'}, - 'additionalData': {}, + 'additionalData': '{}', }, 'requestId': 'requestId', 'isRead': true, @@ -37,7 +37,7 @@ void main() { expect(notification.message.actionUrl, 'actionUrl'); expect(notification.message.avatar?.url, 'avatarUrl'); expect(notification.message.avatar?.altText, 'altText'); - expect(notification.message.additionalData, {}); + expect(notification.message.additionalData, isA>()); }); test('markAsRead() should mark the notification as read', () { @@ -77,7 +77,7 @@ void main() { 'body': 'body', 'actionUrl': 'actionUrl', 'avatar': {'imageUrl': 'avatarUrl', 'altText': 'altText'}, - 'additionalData': {}, + 'additionalData': '{}', }; final message = MessageData.fromJson(Map.from(json)); @@ -89,7 +89,7 @@ void main() { expect(message.actionUrl, 'actionUrl'); expect(message.avatar?.url, 'avatarUrl'); expect(message.avatar?.altText, 'altText'); - expect(message.additionalData, {}); + expect(message.additionalData, isA>()); }); }); From 11722ce167eb7d2830f0c18459a3a072d88fe7c0 Mon Sep 17 00:00:00 2001 From: ameera-keyvalue Date: Tue, 9 Jul 2024 16:47:47 +0530 Subject: [PATCH 05/10] refact: Update code format --- lib/src/models/notification_model.dart | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/lib/src/models/notification_model.dart b/lib/src/models/notification_model.dart index 4cc31a6..73ce92d 100644 --- a/lib/src/models/notification_model.dart +++ b/lib/src/models/notification_model.dart @@ -68,13 +68,14 @@ class MessageData { /// Factory method to create MessageData from data. factory MessageData.fromJson(Map? data) { Map? additionalData; - if (data?['additionalData'] != null) { - try { - additionalData = json.decode(data?['additionalData'] as String) as Map?; - } catch (error) { - additionalData = null; + if (data?['additionalData'] != null) { + try { + additionalData = json.decode(data?['additionalData'] as String) + as Map?; + } catch (error) { + additionalData = null; + } } - } return MessageData( channel: data?['channel'] as String?, header: data?['header'] as String?, From 5fb3daa10aded60b29028902b1d62f66e46992cd Mon Sep 17 00:00:00 2001 From: Ameera Sherin <89637967+Ameera-Sherin@users.noreply.github.com> Date: Tue, 9 Jul 2024 17:21:15 +0530 Subject: [PATCH 06/10] doc: update Changelog --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 78c841a..8051a78 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,13 @@ All notable changes to this project will be documented in this file. +## 1.3.0 + +### Added +- Added a fix for the extra padding issue in the app bar when a custom header is passed. +- Added a fix for parsing additional data to JSON when received as a string. + + ## 1.2.0 ### Added From 9979e3de6e7986d7b16e58a7bf34e5fca23d8096 Mon Sep 17 00:00:00 2001 From: ameera-keyvalue Date: Fri, 12 Jul 2024 10:14:09 +0530 Subject: [PATCH 07/10] doc: Update the version number --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8051a78..1918b23 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ All notable changes to this project will be documented in this file. -## 1.3.0 +## 1.2.1 ### Added - Added a fix for the extra padding issue in the app bar when a custom header is passed. From 5cf939109793c5d20496f6eebe0f77cfff587673 Mon Sep 17 00:00:00 2001 From: anitta-keyvalue Date: Wed, 21 May 2025 09:13:02 +0530 Subject: [PATCH 08/10] feat: Update pubspec lock files --- example/pubspec.lock | 2 +- pubspec.lock | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/example/pubspec.lock b/example/pubspec.lock index a4e44bf..15087ac 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -169,7 +169,7 @@ packages: path: ".." relative: true source: path - version: "1.1.0" + version: "1.2.1" sky_engine: dependency: transitive description: flutter diff --git a/pubspec.lock b/pubspec.lock index 5e06fd1..678763c 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -556,18 +556,18 @@ packages: dependency: transitive description: name: web - sha256: "97da13628db363c635202ad97068d47c5b8aa555808e7a9411963c533b449b27" + sha256: "4188706108906f002b3a293509234588823c8c979dc83304e229ff400c996b05" url: "https://pub.dev" source: hosted - version: "0.5.1" + version: "0.4.2" web_socket_channel: dependency: transitive description: name: web_socket_channel - sha256: "58c6666b342a38816b2e7e50ed0f1e261959630becd4c879c4f26bfa14aa5a42" + sha256: "939ab60734a4f8fa95feacb55804fa278de28bdeef38e616dc08e44a84adea23" url: "https://pub.dev" source: hosted - version: "2.4.5" + version: "2.4.3" yaml: dependency: transitive description: @@ -577,5 +577,5 @@ packages: source: hosted version: "3.1.2" sdks: - dart: ">=3.3.0 <4.0.0" + dart: ">=3.2.0 <4.0.0" flutter: ">=3.0.0" From 1c9910ef2a994be598dd456708c6623da2f9d0e4 Mon Sep 17 00:00:00 2001 From: Anitta <99161914+anitta-keyvalue@users.noreply.github.com> Date: Wed, 28 May 2025 18:49:07 +0530 Subject: [PATCH 09/10] fix: Add fix for build errors (#107) --- .github/workflows/test.yml | 4 +- example/pubspec.lock | 114 +++--- lib/src/api/delete_notification_by_id.dart | 4 +- lib/src/api/fetch_all_notification.dart | 4 +- lib/src/api/read_notification_by_id.dart | 4 +- lib/src/models/notification_model.dart | 2 +- lib/src/utils/common_utils.dart | 4 +- lib/src/widgets/loader_widget.dart | 21 +- lib/src/widgets/media_error_widget.dart | 8 +- lib/src/widgets/siren_inbox.dart | 6 +- lib/src/widgets/siren_inbox_icon.dart | 2 +- pubspec.lock | 270 +++++++------ pubspec.yaml | 6 +- test/data/siren_data_provider_test.dart | 49 ++- test/data/siren_data_provider_test.mocks.dart | 260 +++++++++--- test/services/api_client_test.mocks.dart | 369 ++++++++++-------- test/services/network_service_test.mocks.dart | 3 +- test/widgets/siren_inbox_icon_test.mocks.dart | 57 +-- test/widgets/siren_inbox_test.mocks.dart | 57 +-- 19 files changed, 768 insertions(+), 476 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 8b75340..78d030b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -19,10 +19,10 @@ jobs: channel: 'stable' - name: Exclude Example application run: rm -rf example - - name: Verify formatting - run: dart format --output=none --set-exit-if-changed . - name: Install dependencies run: flutter pub get + - name: Verify formatting + run: dart format --output=none --set-exit-if-changed . - name: Analyze project source run: flutter analyze lib/ - name: Run tests diff --git a/example/pubspec.lock b/example/pubspec.lock index 15087ac..d3c3527 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -5,42 +5,42 @@ packages: dependency: transitive description: name: async - sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" + sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb" url: "https://pub.dev" source: hosted - version: "2.11.0" + version: "2.13.0" boolean_selector: dependency: transitive description: name: boolean_selector - sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" + sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" characters: dependency: transitive description: name: characters - sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" + sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 url: "https://pub.dev" source: hosted - version: "1.3.0" + version: "1.4.0" clock: dependency: transitive description: name: clock - sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf + sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b url: "https://pub.dev" source: hosted - version: "1.1.1" + version: "1.1.2" collection: dependency: transitive description: name: collection - sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a + sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" url: "https://pub.dev" source: hosted - version: "1.18.0" + version: "1.19.1" cupertino_icons: dependency: "direct main" description: @@ -53,18 +53,26 @@ packages: dependency: transitive description: name: dio - sha256: "639179e1cc0957779e10dd5b786ce180c477c4c0aca5aaba5d1700fa2e834801" + sha256: "253a18bbd4851fecba42f7343a1df3a9a4c1d31a2c1b37e221086b4fa8c8dbc9" url: "https://pub.dev" source: hosted - version: "5.4.3" + version: "5.8.0+1" + dio_web_adapter: + dependency: transitive + description: + name: dio_web_adapter + sha256: "7586e476d70caecaf1686d21eee7247ea43ef5c345eab9e0cc3583ff13378d78" + url: "https://pub.dev" + source: hosted + version: "2.1.1" fake_async: dependency: transitive description: name: fake_async - sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" + sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44" url: "https://pub.dev" source: hosted - version: "1.3.1" + version: "1.3.3" flutter: dependency: "direct main" description: flutter @@ -87,34 +95,34 @@ packages: dependency: transitive description: name: http_parser - sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" + sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571" url: "https://pub.dev" source: hosted - version: "4.0.2" + version: "4.1.2" leak_tracker: dependency: transitive description: name: leak_tracker - sha256: "78eb209deea09858f5269f5a5b02be4049535f568c07b275096836f01ea323fa" + sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0" url: "https://pub.dev" source: hosted - version: "10.0.0" + version: "10.0.9" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: b46c5e37c19120a8a01918cfaf293547f47269f7cb4b0058f21531c2465d6ef0 + sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573 url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "3.0.9" leak_tracker_testing: dependency: transitive description: name: leak_tracker_testing - sha256: a597f72a664dbd293f3bfc51f9ba69816f84dcd403cdac7066cb3f6003f3ab47 + sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "3.0.1" lints: dependency: transitive description: @@ -127,26 +135,26 @@ packages: dependency: transitive description: name: matcher - sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb + sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 url: "https://pub.dev" source: hosted - version: "0.12.16+1" + version: "0.12.17" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" + sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec url: "https://pub.dev" source: hosted - version: "0.8.0" + version: "0.11.1" meta: dependency: transitive description: name: meta - sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 + sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c url: "https://pub.dev" source: hosted - version: "1.11.0" + version: "1.16.0" network_logger: dependency: "direct main" description: @@ -159,10 +167,10 @@ packages: dependency: transitive description: name: path - sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" + sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" url: "https://pub.dev" source: hosted - version: "1.9.0" + version: "1.9.1" sirenapp_flutter_inbox: dependency: "direct main" description: @@ -174,63 +182,63 @@ packages: dependency: transitive description: flutter source: sdk - version: "0.0.99" + version: "0.0.0" source_span: dependency: transitive description: name: source_span - sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" + sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c" url: "https://pub.dev" source: hosted - version: "1.10.0" + version: "1.10.1" stack_trace: dependency: transitive description: name: stack_trace - sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" + sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" url: "https://pub.dev" source: hosted - version: "1.11.1" + version: "1.12.1" stream_channel: dependency: transitive description: name: stream_channel - sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 + sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.4" string_scanner: dependency: transitive description: name: string_scanner - sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" + sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" url: "https://pub.dev" source: hosted - version: "1.2.0" + version: "1.4.1" term_glyph: dependency: transitive description: name: term_glyph - sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 + sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" url: "https://pub.dev" source: hosted - version: "1.2.1" + version: "1.2.2" test_api: dependency: transitive description: name: test_api - sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" + sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd url: "https://pub.dev" source: hosted - version: "0.6.1" + version: "0.7.4" typed_data: dependency: transitive description: name: typed_data - sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c + sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006 url: "https://pub.dev" source: hosted - version: "1.3.2" + version: "1.4.0" vector_math: dependency: transitive description: @@ -243,10 +251,18 @@ packages: dependency: transitive description: name: vm_service - sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957 + sha256: ddfa8d30d89985b96407efce8acbdd124701f96741f2d981ca860662f1c0dc02 + url: "https://pub.dev" + source: hosted + version: "15.0.0" + web: + dependency: transitive + description: + name: web + sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a" url: "https://pub.dev" source: hosted - version: "13.0.0" + version: "1.1.1" sdks: - dart: ">=3.2.0-0 <4.0.0" - flutter: ">=3.0.0" + dart: ">=3.7.0-0 <4.0.0" + flutter: ">=3.18.0-18.0.pre.54" diff --git a/lib/src/api/delete_notification_by_id.dart b/lib/src/api/delete_notification_by_id.dart index cc6d356..b73c2e4 100644 --- a/lib/src/api/delete_notification_by_id.dart +++ b/lib/src/api/delete_notification_by_id.dart @@ -22,7 +22,7 @@ class DeleteNotificationById { Future deleteNotificationById({ required String notificationId, }) async { - final _apiPath = + final apiPath = '${Generics.V2}${Generics.BASE_URL}${SirenDataProvider.instance.recipientId}/notifications'; final result = ApiResponse()..isLoading = true; var apiError = Errors.deleteFailedError; @@ -39,7 +39,7 @@ class DeleteNotificationById { } final apiResponse = await api.delete( - path: '$_apiPath/$notificationId', + path: '$apiPath/$notificationId', ); if (apiResponse.statusCode != 0 && apiResponse.data != null) { final deletionStatus = convertJsonToDeletionStatus(apiResponse.data); diff --git a/lib/src/api/fetch_all_notification.dart b/lib/src/api/fetch_all_notification.dart index 584a393..b14f496 100644 --- a/lib/src/api/fetch_all_notification.dart +++ b/lib/src/api/fetch_all_notification.dart @@ -30,7 +30,7 @@ class FetchAllNotifications { String? start, String? end, }) async { - final _apiPath = + final apiPath = '${Generics.V2}${Generics.BASE_URL}${SirenDataProvider.instance.recipientId}/notifications'; final result = ApiResponse()..isLoading = true; var apiError = Errors.notificationFetchFailedError; @@ -68,7 +68,7 @@ class FetchAllNotifications { } final apiResponse = await api.get( - path: '$_apiPath?$queryString', + path: '$apiPath?$queryString', ); if (apiResponse.statusCode != 0 && apiResponse.data != null) { diff --git a/lib/src/api/read_notification_by_id.dart b/lib/src/api/read_notification_by_id.dart index 5c65dd7..6acc8d9 100644 --- a/lib/src/api/read_notification_by_id.dart +++ b/lib/src/api/read_notification_by_id.dart @@ -16,7 +16,7 @@ class ReadNotificationById { }) async { final result = ApiResponse()..isLoading = true; var apiError = Errors.markAsReadFailedError; - final _apiPath = + final apiPath = '${Generics.V2}${Generics.BASE_URL}${SirenDataProvider.instance.recipientId}/notifications'; if (SirenDataProvider.instance.tokenVerificationStatus != Status.SUCCESS) { apiError = SirenDataProvider.instance.getVerificationErrorType(); @@ -30,7 +30,7 @@ class ReadNotificationById { } final apiResponse = await api.patch( - path: '$_apiPath/$notificationId', + path: '$apiPath/$notificationId', data: { 'isRead': true, 'isDelivered': true, diff --git a/lib/src/models/notification_model.dart b/lib/src/models/notification_model.dart index 73ce92d..c0b43cb 100644 --- a/lib/src/models/notification_model.dart +++ b/lib/src/models/notification_model.dart @@ -72,7 +72,7 @@ class MessageData { try { additionalData = json.decode(data?['additionalData'] as String) as Map?; - } catch (error) { + } on FormatException { additionalData = null; } } diff --git a/lib/src/utils/common_utils.dart b/lib/src/utils/common_utils.dart index 04809e2..2b205b7 100644 --- a/lib/src/utils/common_utils.dart +++ b/lib/src/utils/common_utils.dart @@ -1,4 +1,4 @@ -import 'package:flutter/services.dart' show rootBundle; +import 'package:flutter/services.dart' show PlatformException, rootBundle; import 'package:sirenapp_flutter_inbox/src/constants/generics.dart'; /// Generates elapsed time text based on the difference between the target time and the current time. @@ -64,7 +64,7 @@ Future> loadEnv() async { } return envVariables; - } catch (e) { + } on PlatformException { return {}; } } diff --git a/lib/src/widgets/loader_widget.dart b/lib/src/widgets/loader_widget.dart index bc13ffa..2bb364a 100644 --- a/lib/src/widgets/loader_widget.dart +++ b/lib/src/widgets/loader_widget.dart @@ -99,7 +99,7 @@ class CardLoaderWidgetState extends State decoration: BoxDecoration( shape: BoxShape.circle, color: defaultColors.skeletonLoaderColor - .withOpacity(0.5 + 0.5 * _controller.value), + .withValues(alpha: 0.5 + 0.5 * _controller.value), ), ), ), @@ -117,7 +117,7 @@ class CardLoaderWidgetState extends State height: 18, decoration: BoxDecoration( color: defaultColors.skeletonLoaderColor - .withOpacity(0.5 + 0.5 * _controller.value), + .withValues(alpha: 0.5 + 0.5 * _controller.value), borderRadius: BorderRadius.circular(4), ), ), @@ -128,7 +128,7 @@ class CardLoaderWidgetState extends State height: 18, decoration: BoxDecoration( color: defaultColors.skeletonLoaderColor - .withOpacity(0.5 + 0.5 * _controller.value), + .withValues(alpha: 0.5 + 0.5 * _controller.value), borderRadius: BorderRadius.circular(4), ), ), @@ -139,7 +139,7 @@ class CardLoaderWidgetState extends State height: 18, decoration: BoxDecoration( color: defaultColors.skeletonLoaderColor - .withOpacity(0.5 + 0.5 * _controller.value), + .withValues(alpha: 0.5 + 0.5 * _controller.value), borderRadius: BorderRadius.circular(4), ), ), @@ -153,8 +153,9 @@ class CardLoaderWidgetState extends State height: 10, decoration: BoxDecoration( shape: BoxShape.circle, - color: defaultColors.skeletonLoaderColor - .withOpacity(0.5 + 0.5 * _controller.value), + color: defaultColors.skeletonLoaderColor.withValues( + alpha: 0.5 + 0.5 * _controller.value, + ), ), ), ), @@ -164,8 +165,10 @@ class CardLoaderWidgetState extends State builder: (context, child) => Container( height: 12, decoration: BoxDecoration( - color: defaultColors.skeletonLoaderColor - .withOpacity(0.5 + 0.5 * _controller.value), + color: + defaultColors.skeletonLoaderColor.withValues( + alpha: 0.5 + 0.5 * _controller.value, + ), borderRadius: BorderRadius.circular(4), ), ), @@ -183,7 +186,7 @@ class CardLoaderWidgetState extends State height: 16, decoration: BoxDecoration( color: defaultColors.skeletonLoaderColor - .withOpacity(0.5 + 0.5 * _controller.value), + .withValues(alpha: 0.5 + 0.5 * _controller.value), borderRadius: BorderRadius.circular(4), ), ), diff --git a/lib/src/widgets/media_error_widget.dart b/lib/src/widgets/media_error_widget.dart index 4696233..cbb6c11 100644 --- a/lib/src/widgets/media_error_widget.dart +++ b/lib/src/widgets/media_error_widget.dart @@ -1,5 +1,3 @@ -// ignore_for_file: cascade_invocations - import 'package:flutter/material.dart'; import 'package:sirenapp_flutter_inbox/src/theme/app_theme.dart'; @@ -75,16 +73,14 @@ class DiagonalPainter extends CustomPainter { ..style = PaintingStyle.stroke ..strokeCap = StrokeCap.round; - final lightColorPath = Path(); - lightColorPath + final lightColorPath = Path() ..moveTo( size.width + 2, size.height + 2, ) ..lineTo(-2, -2); - final darkColorPath = Path(); - darkColorPath + final darkColorPath = Path() ..moveTo( size.width + gap, size.height, diff --git a/lib/src/widgets/siren_inbox.dart b/lib/src/widgets/siren_inbox.dart index 84b12b7..78139f1 100644 --- a/lib/src/widgets/siren_inbox.dart +++ b/lib/src/widgets/siren_inbox.dart @@ -200,7 +200,7 @@ class _SirenInboxState extends State _initialize(); break; - // ignore: no_default_cases + // ignore: no_default_cases, reason: All cases are handled above default: } } else if (streamResponse.response?.isError ?? false) { @@ -501,7 +501,7 @@ class _SirenInboxState extends State } Widget _buildInboxBody( - ScrollController _controller, + ScrollController controller, List data, bool isTabInactive, ) { @@ -527,7 +527,7 @@ class _SirenInboxState extends State onCardClick: widget.onCardClick, onEndReached: onEndReached, onRefresh: onRefresh, - scrollController: _controller, + scrollController: controller, ); } diff --git a/lib/src/widgets/siren_inbox_icon.dart b/lib/src/widgets/siren_inbox_icon.dart index 84f221e..3d3a390 100644 --- a/lib/src/widgets/siren_inbox_icon.dart +++ b/lib/src/widgets/siren_inbox_icon.dart @@ -96,7 +96,7 @@ class _SirenInboxIconState extends State { await _initialize(); break; } - // ignore: no_default_cases + // ignore: no_default_cases, reason: All cases are handled above default: } } else if (streamResponse.response?.isError ?? false) { diff --git a/pubspec.lock b/pubspec.lock index 678763c..f372df4 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -5,90 +5,90 @@ packages: dependency: transitive description: name: _fe_analyzer_shared - sha256: "0b2f2bd91ba804e53a61d757b986f89f1f9eaed5b11e4b2f5a2468d86d6c9fc7" + sha256: e55636ed79578b9abca5fecf9437947798f5ef7456308b5cb85720b793eac92f url: "https://pub.dev" source: hosted - version: "67.0.0" + version: "82.0.0" analyzer: dependency: transitive description: name: analyzer - sha256: "37577842a27e4338429a1cbc32679d508836510b056f1eedf0c8d20e39c1383d" + sha256: "904ae5bb474d32c38fb9482e2d925d5454cda04ddd0e55d2e6826bc72f6ba8c0" url: "https://pub.dev" source: hosted - version: "6.4.1" + version: "7.4.5" args: dependency: transitive description: name: args - sha256: "7cf60b9f0cc88203c5a190b4cd62a99feea42759a7fa695010eb5de1c0b2252a" + sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04 url: "https://pub.dev" source: hosted - version: "2.5.0" + version: "2.7.0" async: dependency: transitive description: name: async - sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" + sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb" url: "https://pub.dev" source: hosted - version: "2.11.0" + version: "2.13.0" boolean_selector: dependency: transitive description: name: boolean_selector - sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" + sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" build: dependency: transitive description: name: build - sha256: "80184af8b6cb3e5c1c4ec6d8544d27711700bc3e6d2efad04238c7b5290889f0" + sha256: cef23f1eda9b57566c81e2133d196f8e3df48f244b317368d65c5943d91148f0 url: "https://pub.dev" source: hosted - version: "2.4.1" + version: "2.4.2" build_config: dependency: transitive description: name: build_config - sha256: bf80fcfb46a29945b423bd9aad884590fb1dc69b330a4d4700cac476af1708d1 + sha256: "4ae2de3e1e67ea270081eaee972e1bd8f027d459f249e0f1186730784c2e7e33" url: "https://pub.dev" source: hosted - version: "1.1.1" + version: "1.1.2" build_daemon: dependency: transitive description: name: build_daemon - sha256: "0343061a33da9c5810b2d6cee51945127d8f4c060b7fbdd9d54917f0a3feaaa1" + sha256: "8e928697a82be082206edb0b9c99c5a4ad6bc31c9e9b8b2f291ae65cd4a25daa" url: "https://pub.dev" source: hosted - version: "4.0.1" + version: "4.0.4" build_resolvers: dependency: transitive description: name: build_resolvers - sha256: "339086358431fa15d7eca8b6a36e5d783728cf025e559b834f4609a1fcfb7b0a" + sha256: b9e4fda21d846e192628e7a4f6deda6888c36b5b69ba02ff291a01fd529140f0 url: "https://pub.dev" source: hosted - version: "2.4.2" + version: "2.4.4" build_runner: dependency: "direct dev" description: name: build_runner - sha256: "3ac61a79bfb6f6cc11f693591063a7f19a7af628dc52f141743edac5c16e8c22" + sha256: "058fe9dce1de7d69c4b84fada934df3e0153dd000758c4d65964d0166779aa99" url: "https://pub.dev" source: hosted - version: "2.4.9" + version: "2.4.15" build_runner_core: dependency: transitive description: name: build_runner_core - sha256: "4ae8ffe5ac758da294ecf1802f2aff01558d8b1b00616aa7538ea9a8a5d50799" + sha256: "22e3aa1c80e0ada3722fe5b63fd43d9c8990759d0a2cf489c8c5d7b2bdebc021" url: "https://pub.dev" source: hosted - version: "7.3.0" + version: "8.0.0" built_collection: dependency: transitive description: @@ -101,18 +101,18 @@ packages: dependency: transitive description: name: built_value - sha256: c7913a9737ee4007efedaffc968c049fd0f3d0e49109e778edc10de9426005cb + sha256: ea90e81dc4a25a043d9bee692d20ed6d1c4a1662a28c03a96417446c093ed6b4 url: "https://pub.dev" source: hosted - version: "8.9.2" + version: "8.9.5" characters: dependency: transitive description: name: characters - sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" + sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 url: "https://pub.dev" source: hosted - version: "1.3.0" + version: "1.4.0" checked_yaml: dependency: transitive description: @@ -125,82 +125,90 @@ packages: dependency: transitive description: name: clock - sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf + sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b url: "https://pub.dev" source: hosted - version: "1.1.1" + version: "1.1.2" code_builder: dependency: transitive description: name: code_builder - sha256: f692079e25e7869c14132d39f223f8eec9830eb76131925143b2129c4bb01b37 + sha256: "0ec10bf4a89e4c613960bf1e8b42c64127021740fb21640c29c909826a5eea3e" url: "https://pub.dev" source: hosted - version: "4.10.0" + version: "4.10.1" collection: dependency: transitive description: name: collection - sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a + sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" url: "https://pub.dev" source: hosted - version: "1.18.0" + version: "1.19.1" convert: dependency: transitive description: name: convert - sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592" + sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68 url: "https://pub.dev" source: hosted - version: "3.1.1" + version: "3.1.2" crypto: dependency: transitive description: name: crypto - sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab + sha256: "1e445881f28f22d6140f181e07737b22f1e099a5e1ff94b0af2f9e4a463f4855" url: "https://pub.dev" source: hosted - version: "3.0.3" + version: "3.0.6" dart_style: dependency: transitive description: name: dart_style - sha256: "99e066ce75c89d6b29903d788a7bb9369cf754f7b24bf70bf4b6d6d6b26853b9" + sha256: "5b236382b47ee411741447c1f1e111459c941ea1b3f2b540dde54c210a3662af" url: "https://pub.dev" source: hosted - version: "2.3.6" + version: "3.1.0" dio: dependency: "direct main" description: name: dio - sha256: "49af28382aefc53562459104f64d16b9dfd1e8ef68c862d5af436cc8356ce5a8" + sha256: "253a18bbd4851fecba42f7343a1df3a9a4c1d31a2c1b37e221086b4fa8c8dbc9" + url: "https://pub.dev" + source: hosted + version: "5.8.0+1" + dio_web_adapter: + dependency: transitive + description: + name: dio_web_adapter + sha256: "7586e476d70caecaf1686d21eee7247ea43ef5c345eab9e0cc3583ff13378d78" url: "https://pub.dev" source: hosted - version: "5.4.1" + version: "2.1.1" fake_async: dependency: transitive description: name: fake_async - sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" + sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44" url: "https://pub.dev" source: hosted - version: "1.3.1" + version: "1.3.3" file: dependency: transitive description: name: file - sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c" + sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4 url: "https://pub.dev" source: hosted - version: "7.0.0" + version: "7.0.1" fixnum: dependency: transitive description: name: fixnum - sha256: "25517a4deb0c03aa0f32fd12db525856438902d9c16536311e76cdc57b31d7d1" + sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "1.1.1" flutter: dependency: "direct main" description: flutter @@ -231,34 +239,42 @@ packages: dependency: transitive description: name: glob - sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63" + sha256: c3f1ee72c96f8f78935e18aa8cecced9ab132419e8625dc187e1c2408efc20de url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.3" graphs: dependency: transitive description: name: graphs - sha256: aedc5a15e78fc65a6e23bcd927f24c64dd995062bcd1ca6eda65a3cff92a4d19 + sha256: "741bbf84165310a68ff28fe9e727332eef1407342fca52759cb21ad8177bb8d0" url: "https://pub.dev" source: hosted - version: "2.3.1" + version: "2.3.2" + http: + dependency: transitive + description: + name: http + sha256: "2c11f3f94c687ee9bad77c171151672986360b2b001d109814ee7140b2cf261b" + url: "https://pub.dev" + source: hosted + version: "1.4.0" http_multi_server: dependency: transitive description: name: http_multi_server - sha256: "97486f20f9c2f7be8f514851703d0119c3596d14ea63227af6f7a481ef2b2f8b" + sha256: aa6199f908078bb1c5efb8d8638d4ae191aac11b311132c3ef48ce352fb52ef8 url: "https://pub.dev" source: hosted - version: "3.2.1" + version: "3.2.2" http_parser: dependency: transitive description: name: http_parser - sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" + sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571" url: "https://pub.dev" source: hosted - version: "4.0.2" + version: "4.1.2" husky: dependency: "direct dev" description: @@ -271,18 +287,18 @@ packages: dependency: transitive description: name: io - sha256: "2ec25704aba361659e10e3e5f5d672068d332fc8ac516421d483a11e5cbd061e" + sha256: dfd5a80599cf0165756e3181807ed3e77daf6dd4137caaad72d0b7931597650b url: "https://pub.dev" source: hosted - version: "1.0.4" + version: "1.0.5" js: dependency: transitive description: name: js - sha256: c1b2e9b5ea78c45e1a0788d29606ba27dc5f71f019f32ca5140f61ef071838cf + sha256: "53385261521cc4a0c4658fd0ad07a7d14591cf8fc33abbceae306ddb974888dc" url: "https://pub.dev" source: hosted - version: "0.7.1" + version: "0.7.2" json_annotation: dependency: transitive description: @@ -295,26 +311,26 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "78eb209deea09858f5269f5a5b02be4049535f568c07b275096836f01ea323fa" + sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0" url: "https://pub.dev" source: hosted - version: "10.0.0" + version: "10.0.9" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: b46c5e37c19120a8a01918cfaf293547f47269f7cb4b0058f21531c2465d6ef0 + sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573 url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "3.0.9" leak_tracker_testing: dependency: transitive description: name: leak_tracker_testing - sha256: a597f72a664dbd293f3bfc51f9ba69816f84dcd403cdac7066cb3f6003f3ab47 + sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "3.0.1" lints: dependency: transitive description: @@ -327,50 +343,50 @@ packages: dependency: transitive description: name: logging - sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340" + sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61 url: "https://pub.dev" source: hosted - version: "1.2.0" + version: "1.3.0" matcher: dependency: transitive description: name: matcher - sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb + sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 url: "https://pub.dev" source: hosted - version: "0.12.16+1" + version: "0.12.17" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" + sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec url: "https://pub.dev" source: hosted - version: "0.8.0" + version: "0.11.1" meta: dependency: transitive description: name: meta - sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 + sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c url: "https://pub.dev" source: hosted - version: "1.11.0" + version: "1.16.0" mime: dependency: transitive description: name: mime - sha256: "2e123074287cc9fd6c09de8336dae606d1ddb88d9ac47358826db698c176a1f2" + sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6" url: "https://pub.dev" source: hosted - version: "1.0.5" + version: "2.0.0" mockito: dependency: "direct dev" description: name: mockito - sha256: "6841eed20a7befac0ce07df8116c8b8233ed1f4486a7647c7fc5a02ae6163917" + sha256: "4546eac99e8967ea91bae633d2ca7698181d008e95fa4627330cf903d573277a" url: "https://pub.dev" source: hosted - version: "5.4.4" + version: "5.4.6" network_image_mock: dependency: "direct dev" description: @@ -383,18 +399,18 @@ packages: dependency: transitive description: name: package_config - sha256: "1c5b77ccc91e4823a5af61ee74e6b972db1ef98c2ff5a18d3161c982a55448bd" + sha256: f096c55ebb7deb7e384101542bfba8c52696c1b56fca2eb62827989ef2353bbc url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.2.0" path: dependency: transitive description: name: path - sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" + sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" url: "https://pub.dev" source: hosted - version: "1.9.0" + version: "1.9.1" pool: dependency: transitive description: @@ -407,119 +423,119 @@ packages: dependency: transitive description: name: pub_semver - sha256: "40d3ab1bbd474c4c2328c91e3a7df8c6dd629b79ece4c4bd04bee496a224fb0c" + sha256: "5bfcf68ca79ef689f8990d1160781b4bad40a3bd5e5218ad4076ddb7f4081585" url: "https://pub.dev" source: hosted - version: "2.1.4" + version: "2.2.0" pubspec_parse: dependency: transitive description: name: pubspec_parse - sha256: c63b2876e58e194e4b0828fcb080ad0e06d051cb607a6be51a9e084f47cb9367 + sha256: "0560ba233314abbed0a48a2956f7f022cce7c3e1e73df540277da7544cad4082" url: "https://pub.dev" source: hosted - version: "1.2.3" + version: "1.5.0" shelf: dependency: transitive description: name: shelf - sha256: ad29c505aee705f41a4d8963641f91ac4cee3c8fad5947e033390a7bd8180fa4 + sha256: e7dd780a7ffb623c57850b33f43309312fc863fb6aa3d276a754bb299839ef12 url: "https://pub.dev" source: hosted - version: "1.4.1" + version: "1.4.2" shelf_web_socket: dependency: transitive description: name: shelf_web_socket - sha256: "9ca081be41c60190ebcb4766b2486a7d50261db7bd0f5d9615f2d653637a84c1" + sha256: "3632775c8e90d6c9712f883e633716432a27758216dfb61bd86a8321c0580925" url: "https://pub.dev" source: hosted - version: "1.0.4" + version: "3.0.0" sky_engine: dependency: transitive description: flutter source: sdk - version: "0.0.99" + version: "0.0.0" source_gen: dependency: transitive description: name: source_gen - sha256: "14658ba5f669685cd3d63701d01b31ea748310f7ab854e471962670abcf57832" + sha256: "35c8150ece9e8c8d263337a265153c3329667640850b9304861faea59fc98f6b" url: "https://pub.dev" source: hosted - version: "1.5.0" + version: "2.0.0" source_span: dependency: transitive description: name: source_span - sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" + sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c" url: "https://pub.dev" source: hosted - version: "1.10.0" + version: "1.10.1" stack_trace: dependency: transitive description: name: stack_trace - sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" + sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" url: "https://pub.dev" source: hosted - version: "1.11.1" + version: "1.12.1" stream_channel: dependency: transitive description: name: stream_channel - sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 + sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.4" stream_transform: dependency: transitive description: name: stream_transform - sha256: "14a00e794c7c11aa145a170587321aedce29769c08d7f58b1d141da75e3b1c6f" + sha256: ad47125e588cfd37a9a7f86c7d6356dde8dfe89d071d293f80ca9e9273a33871 url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.1.1" string_scanner: dependency: transitive description: name: string_scanner - sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" + sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" url: "https://pub.dev" source: hosted - version: "1.2.0" + version: "1.4.1" term_glyph: dependency: transitive description: name: term_glyph - sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 + sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" url: "https://pub.dev" source: hosted - version: "1.2.1" + version: "1.2.2" test_api: dependency: transitive description: name: test_api - sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" + sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd url: "https://pub.dev" source: hosted - version: "0.6.1" + version: "0.7.4" timing: dependency: transitive description: name: timing - sha256: "70a3b636575d4163c477e6de42f247a23b315ae20e86442bebe32d3cabf61c32" + sha256: "62ee18aca144e4a9f29d212f5a4c6a053be252b895ab14b5821996cff4ed90fe" url: "https://pub.dev" source: hosted - version: "1.0.1" + version: "1.0.2" typed_data: dependency: transitive description: name: typed_data - sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c + sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006 url: "https://pub.dev" source: hosted - version: "1.3.2" + version: "1.4.0" vector_math: dependency: transitive description: @@ -532,50 +548,58 @@ packages: dependency: "direct dev" description: name: very_good_analysis - sha256: cecd7a0e92978dbece97c255502c8965f2db3439cde5a11f4b2c65f1955911ee + sha256: "62d2b86d183fb81b2edc22913d9f155d26eb5cf3855173adb1f59fac85035c63" url: "https://pub.dev" source: hosted - version: "2.4.0" + version: "7.0.0" vm_service: dependency: transitive description: name: vm_service - sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957 + sha256: ddfa8d30d89985b96407efce8acbdd124701f96741f2d981ca860662f1c0dc02 url: "https://pub.dev" source: hosted - version: "13.0.0" + version: "15.0.0" watcher: dependency: transitive description: name: watcher - sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8" + sha256: "69da27e49efa56a15f8afe8f4438c4ec02eff0a117df1b22ea4aad194fe1c104" url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "1.1.1" web: dependency: transitive description: name: web - sha256: "4188706108906f002b3a293509234588823c8c979dc83304e229ff400c996b05" + sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a" url: "https://pub.dev" source: hosted - version: "0.4.2" + version: "1.1.1" + web_socket: + dependency: transitive + description: + name: web_socket + sha256: "34d64019aa8e36bf9842ac014bb5d2f5586ca73df5e4d9bf5c936975cae6982c" + url: "https://pub.dev" + source: hosted + version: "1.0.1" web_socket_channel: dependency: transitive description: name: web_socket_channel - sha256: "939ab60734a4f8fa95feacb55804fa278de28bdeef38e616dc08e44a84adea23" + sha256: d645757fb0f4773d602444000a8131ff5d48c9e47adfe9772652dd1a4f2d45c8 url: "https://pub.dev" source: hosted - version: "2.4.3" + version: "3.0.3" yaml: dependency: transitive description: name: yaml - sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5" + sha256: b9da305ac7c39faa3f030eccd175340f968459dae4af175130b3fc47e40d76ce url: "https://pub.dev" source: hosted - version: "3.1.2" + version: "3.1.3" sdks: - dart: ">=3.2.0 <4.0.0" - flutter: ">=3.0.0" + dart: ">=3.7.0 <4.0.0" + flutter: ">=3.18.0-18.0.pre.54" diff --git a/pubspec.yaml b/pubspec.yaml index 6ba0d33..9c287b1 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -11,19 +11,19 @@ keywords: - notifications environment: - sdk: '>=2.17.0 <4.0.0' + sdk: '>=2.17.0' flutter: ">=3.0.0" dependencies: flutter: sdk: flutter - dio: '>=5.2.0 <=5.4.3+1' + dio: '>=5.2.0' dev_dependencies: flutter_test: sdk: flutter flutter_lints: ^2.0.0 - very_good_analysis: ^2.4.0 + very_good_analysis: ^7.0.0 husky: ^0.1.7 mockito: ^5.0.0 network_image_mock: ^2.0.1 diff --git a/test/data/siren_data_provider_test.dart b/test/data/siren_data_provider_test.dart index 2b2fe6d..b613030 100644 --- a/test/data/siren_data_provider_test.dart +++ b/test/data/siren_data_provider_test.dart @@ -5,23 +5,38 @@ import 'package:sirenapp_flutter_inbox/sirenapp_flutter_inbox.dart'; import 'package:sirenapp_flutter_inbox/src/api/verify_token.dart'; import 'package:sirenapp_flutter_inbox/src/constants/generics.dart'; import 'package:sirenapp_flutter_inbox/src/data/siren_data_provider.dart'; +import 'package:sirenapp_flutter_inbox/src/models/api_response.dart'; +import 'package:sirenapp_flutter_inbox/src/services/api_client.dart'; import 'siren_data_provider_test.mocks.dart'; @GenerateNiceMocks([ - MockSpec(), + MockSpec(), ]) void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + late SirenDataProvider sirenDataProvider; - late MockVerifyToken mockVerifyToken; + late MockApiClient mockApiClient; setUp(() { - sirenDataProvider = SirenDataProvider.instance..initialize(); - mockVerifyToken = MockVerifyToken(); + mockApiClient = MockApiClient(); + sirenDataProvider = SirenDataProvider.instance; + VerifyToken().api = mockApiClient; + sirenDataProvider.initialize(); }); group('SirenDataProvider', () { test('UpdateParams updates user token and recipient ID', () async { + when(mockApiClient.get(path: anyNamed('path'))).thenAnswer( + (_) async => DioResponse( + data: { + 'data': {'status': 'SUCCESS'} + }, + statusCode: 200, + ), + ); + sirenDataProvider.updateParams( userToken: 'token', recipientId: 'recipientId', @@ -32,25 +47,31 @@ void main() { }); test('IconDispose closes icon controller', () { + final controller = sirenDataProvider.iconController; sirenDataProvider.iconDispose(); - - expect(sirenDataProvider.iconController.isClosed, false); + expect(controller.isClosed, true); }); test('InboxDispose closes inbox controller', () { + final controller = sirenDataProvider.inboxController; sirenDataProvider.inboxDispose(); - - expect(sirenDataProvider.inboxController.isClosed, false); + expect(controller.isClosed, true); }); test('Handles retry logic on token verification failure', () async { - final failedResponse = ApiResponse(data: false); - when(mockVerifyToken.verifyToken()) - .thenAnswer((_) async => failedResponse); - - await sirenDataProvider.initialize(); + when(mockApiClient.get(path: anyNamed('path'))).thenAnswer( + (_) async => DioResponse( + data: { + 'data': {'status': 'FAILED'} + }, + statusCode: 401, + ), + ); - sirenDataProvider.updateParams(userToken: 'token', recipientId: '123'); + sirenDataProvider.updateParams( + userToken: 'token', + recipientId: 'recipientId', + ); await Future.delayed( const Duration(seconds: Generics.DATA_FETCH_INTERVAL) * diff --git a/test/data/siren_data_provider_test.mocks.dart b/test/data/siren_data_provider_test.mocks.dart index f4a1d01..91a7927 100644 --- a/test/data/siren_data_provider_test.mocks.dart +++ b/test/data/siren_data_provider_test.mocks.dart @@ -1,15 +1,14 @@ -// Mocks generated by Mockito 5.4.4 from annotations +// Mocks generated by Mockito 5.4.6 from annotations // in sirenapp_flutter_inbox/test/data/siren_data_provider_test.dart. // Do not manually edit this file. // ignore_for_file: no_leading_underscores_for_library_prefixes -import 'dart:async' as _i6; +import 'dart:async' as _i5; +import 'package:dio/dio.dart' as _i4; import 'package:mockito/mockito.dart' as _i1; -import 'package:sirenapp_flutter_inbox/src/api/verify_token.dart' as _i4; -import 'package:sirenapp_flutter_inbox/src/constants/generics.dart' as _i5; -import 'package:sirenapp_flutter_inbox/src/models/api_response.dart' as _i3; -import 'package:sirenapp_flutter_inbox/src/services/api_client.dart' as _i2; +import 'package:sirenapp_flutter_inbox/src/models/api_response.dart' as _i2; +import 'package:sirenapp_flutter_inbox/src/services/api_client.dart' as _i3; // ignore_for_file: type=lint // ignore_for_file: avoid_redundant_argument_values @@ -19,13 +18,14 @@ import 'package:sirenapp_flutter_inbox/src/services/api_client.dart' as _i2; // ignore_for_file: deprecated_member_use_from_same_package // ignore_for_file: implementation_imports // ignore_for_file: invalid_use_of_visible_for_testing_member +// ignore_for_file: must_be_immutable // ignore_for_file: prefer_const_constructors // ignore_for_file: unnecessary_parenthesis // ignore_for_file: camel_case_types // ignore_for_file: subtype_of_sealed_class -class _FakeApiClient_0 extends _i1.SmartFake implements _i2.ApiClient { - _FakeApiClient_0( +class _FakeDioResponse_0 extends _i1.SmartFake implements _i2.DioResponse { + _FakeDioResponse_0( Object parent, Invocation parentInvocation, ) : super( @@ -34,73 +34,237 @@ class _FakeApiClient_0 extends _i1.SmartFake implements _i2.ApiClient { ); } -class _FakeApiResponse_1 extends _i1.SmartFake implements _i3.ApiResponse { - _FakeApiResponse_1( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -/// A class which mocks [VerifyToken]. +/// A class which mocks [ApiClient]. /// /// See the documentation for Mockito's code generation for more information. -class MockVerifyToken extends _i1.Mock implements _i4.VerifyToken { +class MockApiClient extends _i1.Mock implements _i3.ApiClient { @override - _i2.ApiClient get api => (super.noSuchMethod( - Invocation.getter(#api), - returnValue: _FakeApiClient_0( - this, - Invocation.getter(#api), + bool isServerError(_i4.Response? response) => (super.noSuchMethod( + Invocation.method( + #isServerError, + [response], ), - returnValueForMissingStub: _FakeApiClient_0( - this, - Invocation.getter(#api), + returnValue: false, + returnValueForMissingStub: false, + ) as bool); + + @override + _i5.Future<_i2.DioResponse> get({ + String? path, + Map? queryParameters, + _i4.Options? options, + _i4.CancelToken? cancelToken, + _i4.ProgressCallback? onReceiveProgress, + }) => + (super.noSuchMethod( + Invocation.method( + #get, + [], + { + #path: path, + #queryParameters: queryParameters, + #options: options, + #cancelToken: cancelToken, + #onReceiveProgress: onReceiveProgress, + }, ), - ) as _i2.ApiClient); + returnValue: _i5.Future<_i2.DioResponse>.value(_FakeDioResponse_0( + this, + Invocation.method( + #get, + [], + { + #path: path, + #queryParameters: queryParameters, + #options: options, + #cancelToken: cancelToken, + #onReceiveProgress: onReceiveProgress, + }, + ), + )), + returnValueForMissingStub: + _i5.Future<_i2.DioResponse>.value(_FakeDioResponse_0( + this, + Invocation.method( + #get, + [], + { + #path: path, + #queryParameters: queryParameters, + #options: options, + #cancelToken: cancelToken, + #onReceiveProgress: onReceiveProgress, + }, + ), + )), + ) as _i5.Future<_i2.DioResponse>); @override - set api(_i2.ApiClient? _api) => super.noSuchMethod( - Invocation.setter( - #api, - _api, + _i5.Future<_i2.DioResponse> post({ + String? path, + dynamic data, + Map? queryParameters, + _i4.Options? options, + _i4.CancelToken? cancelToken, + _i4.ProgressCallback? onSendProgress, + _i4.ProgressCallback? onReceiveProgress, + }) => + (super.noSuchMethod( + Invocation.method( + #post, + [], + { + #path: path, + #data: data, + #queryParameters: queryParameters, + #options: options, + #cancelToken: cancelToken, + #onSendProgress: onSendProgress, + #onReceiveProgress: onReceiveProgress, + }, ), - returnValueForMissingStub: null, - ); + returnValue: _i5.Future<_i2.DioResponse>.value(_FakeDioResponse_0( + this, + Invocation.method( + #post, + [], + { + #path: path, + #data: data, + #queryParameters: queryParameters, + #options: options, + #cancelToken: cancelToken, + #onSendProgress: onSendProgress, + #onReceiveProgress: onReceiveProgress, + }, + ), + )), + returnValueForMissingStub: + _i5.Future<_i2.DioResponse>.value(_FakeDioResponse_0( + this, + Invocation.method( + #post, + [], + { + #path: path, + #data: data, + #queryParameters: queryParameters, + #options: options, + #cancelToken: cancelToken, + #onSendProgress: onSendProgress, + #onReceiveProgress: onReceiveProgress, + }, + ), + )), + ) as _i5.Future<_i2.DioResponse>); @override - _i5.Status convertJsonToVerificationStatus(dynamic response) => + _i5.Future<_i2.DioResponse> patch({ + String? path, + dynamic data, + Map? queryParameters, + _i4.Options? options, + _i4.CancelToken? cancelToken, + _i4.ProgressCallback? onSendProgress, + _i4.ProgressCallback? onReceiveProgress, + }) => (super.noSuchMethod( Invocation.method( - #convertJsonToVerificationStatus, - [response], + #patch, + [], + { + #path: path, + #data: data, + #queryParameters: queryParameters, + #options: options, + #cancelToken: cancelToken, + #onSendProgress: onSendProgress, + #onReceiveProgress: onReceiveProgress, + }, ), - returnValue: _i5.Status.PENDING, - returnValueForMissingStub: _i5.Status.PENDING, - ) as _i5.Status); + returnValue: _i5.Future<_i2.DioResponse>.value(_FakeDioResponse_0( + this, + Invocation.method( + #patch, + [], + { + #path: path, + #data: data, + #queryParameters: queryParameters, + #options: options, + #cancelToken: cancelToken, + #onSendProgress: onSendProgress, + #onReceiveProgress: onReceiveProgress, + }, + ), + )), + returnValueForMissingStub: + _i5.Future<_i2.DioResponse>.value(_FakeDioResponse_0( + this, + Invocation.method( + #patch, + [], + { + #path: path, + #data: data, + #queryParameters: queryParameters, + #options: options, + #cancelToken: cancelToken, + #onSendProgress: onSendProgress, + #onReceiveProgress: onReceiveProgress, + }, + ), + )), + ) as _i5.Future<_i2.DioResponse>); @override - _i6.Future<_i3.ApiResponse> verifyToken() => (super.noSuchMethod( + _i5.Future<_i2.DioResponse> delete({ + String? path, + Map? queryParameters, + _i4.Options? options, + _i4.CancelToken? cancelToken, + _i4.ProgressCallback? onReceiveProgress, + }) => + (super.noSuchMethod( Invocation.method( - #verifyToken, + #delete, [], + { + #path: path, + #queryParameters: queryParameters, + #options: options, + #cancelToken: cancelToken, + #onReceiveProgress: onReceiveProgress, + }, ), - returnValue: _i6.Future<_i3.ApiResponse>.value(_FakeApiResponse_1( + returnValue: _i5.Future<_i2.DioResponse>.value(_FakeDioResponse_0( this, Invocation.method( - #verifyToken, + #delete, [], + { + #path: path, + #queryParameters: queryParameters, + #options: options, + #cancelToken: cancelToken, + #onReceiveProgress: onReceiveProgress, + }, ), )), returnValueForMissingStub: - _i6.Future<_i3.ApiResponse>.value(_FakeApiResponse_1( + _i5.Future<_i2.DioResponse>.value(_FakeDioResponse_0( this, Invocation.method( - #verifyToken, + #delete, [], + { + #path: path, + #queryParameters: queryParameters, + #options: options, + #cancelToken: cancelToken, + #onReceiveProgress: onReceiveProgress, + }, ), )), - ) as _i6.Future<_i3.ApiResponse>); + ) as _i5.Future<_i2.DioResponse>); } diff --git a/test/services/api_client_test.mocks.dart b/test/services/api_client_test.mocks.dart index b2d0ecc..df124b4 100644 --- a/test/services/api_client_test.mocks.dart +++ b/test/services/api_client_test.mocks.dart @@ -1,20 +1,20 @@ -// Mocks generated by Mockito 5.4.4 from annotations +// Mocks generated by Mockito 5.4.6 from annotations // in sirenapp_flutter_inbox/test/services/api_client_test.dart. // Do not manually edit this file. // ignore_for_file: no_leading_underscores_for_library_prefixes -import 'dart:async' as _i7; +import 'dart:async' as _i8; import 'package:dio/src/adapter.dart' as _i3; import 'package:dio/src/cancel_token.dart' as _i10; -import 'package:dio/src/dio.dart' as _i9; +import 'package:dio/src/dio.dart' as _i7; import 'package:dio/src/dio_mixin.dart' as _i5; import 'package:dio/src/options.dart' as _i2; import 'package:dio/src/response.dart' as _i6; import 'package:dio/src/transformer.dart' as _i4; import 'package:mockito/mockito.dart' as _i1; import 'package:mockito/src/dummies.dart' as _i12; -import 'package:sirenapp_flutter_inbox/sirenapp_flutter_inbox.dart' as _i8; +import 'package:sirenapp_flutter_inbox/sirenapp_flutter_inbox.dart' as _i9; import 'package:sirenapp_flutter_inbox/src/constants/generics.dart' as _i13; import 'package:sirenapp_flutter_inbox/src/data/siren_data_provider.dart' as _i11; @@ -27,6 +27,7 @@ import 'package:sirenapp_flutter_inbox/src/data/siren_data_provider.dart' // ignore_for_file: deprecated_member_use_from_same_package // ignore_for_file: implementation_imports // ignore_for_file: invalid_use_of_visible_for_testing_member +// ignore_for_file: must_be_immutable // ignore_for_file: prefer_const_constructors // ignore_for_file: unnecessary_parenthesis // ignore_for_file: camel_case_types @@ -83,9 +84,8 @@ class _FakeResponse_4 extends _i1.SmartFake implements _i6.Response { ); } -class _FakeStreamController_5 extends _i1.SmartFake - implements _i7.StreamController { - _FakeStreamController_5( +class _FakeDio_5 extends _i1.SmartFake implements _i7.Dio { + _FakeDio_5( Object parent, Invocation parentInvocation, ) : super( @@ -94,9 +94,20 @@ class _FakeStreamController_5 extends _i1.SmartFake ); } -class _FakeSirenErrorType_6 extends _i1.SmartFake - implements _i8.SirenErrorType { - _FakeSirenErrorType_6( +class _FakeStreamController_6 extends _i1.SmartFake + implements _i8.StreamController { + _FakeStreamController_6( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeSirenErrorType_7 extends _i1.SmartFake + implements _i9.SirenErrorType { + _FakeSirenErrorType_7( Object parent, Invocation parentInvocation, ) : super( @@ -108,7 +119,7 @@ class _FakeSirenErrorType_6 extends _i1.SmartFake /// A class which mocks [Dio]. /// /// See the documentation for Mockito's code generation for more information. -class MockDio extends _i1.Mock implements _i9.Dio { +class MockDio extends _i1.Mock implements _i7.Dio { @override _i2.BaseOptions get options => (super.noSuchMethod( Invocation.getter(#options), @@ -122,15 +133,6 @@ class MockDio extends _i1.Mock implements _i9.Dio { ), ) as _i2.BaseOptions); - @override - set options(_i2.BaseOptions? _options) => super.noSuchMethod( - Invocation.setter( - #options, - _options, - ), - returnValueForMissingStub: null, - ); - @override _i3.HttpClientAdapter get httpClientAdapter => (super.noSuchMethod( Invocation.getter(#httpClientAdapter), @@ -144,16 +146,6 @@ class MockDio extends _i1.Mock implements _i9.Dio { ), ) as _i3.HttpClientAdapter); - @override - set httpClientAdapter(_i3.HttpClientAdapter? _httpClientAdapter) => - super.noSuchMethod( - Invocation.setter( - #httpClientAdapter, - _httpClientAdapter, - ), - returnValueForMissingStub: null, - ); - @override _i4.Transformer get transformer => (super.noSuchMethod( Invocation.getter(#transformer), @@ -167,15 +159,6 @@ class MockDio extends _i1.Mock implements _i9.Dio { ), ) as _i4.Transformer); - @override - set transformer(_i4.Transformer? _transformer) => super.noSuchMethod( - Invocation.setter( - #transformer, - _transformer, - ), - returnValueForMissingStub: null, - ); - @override _i5.Interceptors get interceptors => (super.noSuchMethod( Invocation.getter(#interceptors), @@ -189,6 +172,34 @@ class MockDio extends _i1.Mock implements _i9.Dio { ), ) as _i5.Interceptors); + @override + set options(_i2.BaseOptions? _options) => super.noSuchMethod( + Invocation.setter( + #options, + _options, + ), + returnValueForMissingStub: null, + ); + + @override + set httpClientAdapter(_i3.HttpClientAdapter? _httpClientAdapter) => + super.noSuchMethod( + Invocation.setter( + #httpClientAdapter, + _httpClientAdapter, + ), + returnValueForMissingStub: null, + ); + + @override + set transformer(_i4.Transformer? _transformer) => super.noSuchMethod( + Invocation.setter( + #transformer, + _transformer, + ), + returnValueForMissingStub: null, + ); + @override void close({bool? force = false}) => super.noSuchMethod( Invocation.method( @@ -200,7 +211,7 @@ class MockDio extends _i1.Mock implements _i9.Dio { ); @override - _i7.Future<_i6.Response> head( + _i8.Future<_i6.Response> head( String? path, { Object? data, Map? queryParameters, @@ -218,7 +229,7 @@ class MockDio extends _i1.Mock implements _i9.Dio { #cancelToken: cancelToken, }, ), - returnValue: _i7.Future<_i6.Response>.value(_FakeResponse_4( + returnValue: _i8.Future<_i6.Response>.value(_FakeResponse_4( this, Invocation.method( #head, @@ -232,7 +243,7 @@ class MockDio extends _i1.Mock implements _i9.Dio { ), )), returnValueForMissingStub: - _i7.Future<_i6.Response>.value(_FakeResponse_4( + _i8.Future<_i6.Response>.value(_FakeResponse_4( this, Invocation.method( #head, @@ -245,10 +256,10 @@ class MockDio extends _i1.Mock implements _i9.Dio { }, ), )), - ) as _i7.Future<_i6.Response>); + ) as _i8.Future<_i6.Response>); @override - _i7.Future<_i6.Response> headUri( + _i8.Future<_i6.Response> headUri( Uri? uri, { Object? data, _i2.Options? options, @@ -264,7 +275,7 @@ class MockDio extends _i1.Mock implements _i9.Dio { #cancelToken: cancelToken, }, ), - returnValue: _i7.Future<_i6.Response>.value(_FakeResponse_4( + returnValue: _i8.Future<_i6.Response>.value(_FakeResponse_4( this, Invocation.method( #headUri, @@ -277,7 +288,7 @@ class MockDio extends _i1.Mock implements _i9.Dio { ), )), returnValueForMissingStub: - _i7.Future<_i6.Response>.value(_FakeResponse_4( + _i8.Future<_i6.Response>.value(_FakeResponse_4( this, Invocation.method( #headUri, @@ -289,10 +300,10 @@ class MockDio extends _i1.Mock implements _i9.Dio { }, ), )), - ) as _i7.Future<_i6.Response>); + ) as _i8.Future<_i6.Response>); @override - _i7.Future<_i6.Response> get( + _i8.Future<_i6.Response> get( String? path, { Object? data, Map? queryParameters, @@ -312,7 +323,7 @@ class MockDio extends _i1.Mock implements _i9.Dio { #onReceiveProgress: onReceiveProgress, }, ), - returnValue: _i7.Future<_i6.Response>.value(_FakeResponse_4( + returnValue: _i8.Future<_i6.Response>.value(_FakeResponse_4( this, Invocation.method( #get, @@ -327,7 +338,7 @@ class MockDio extends _i1.Mock implements _i9.Dio { ), )), returnValueForMissingStub: - _i7.Future<_i6.Response>.value(_FakeResponse_4( + _i8.Future<_i6.Response>.value(_FakeResponse_4( this, Invocation.method( #get, @@ -341,10 +352,10 @@ class MockDio extends _i1.Mock implements _i9.Dio { }, ), )), - ) as _i7.Future<_i6.Response>); + ) as _i8.Future<_i6.Response>); @override - _i7.Future<_i6.Response> getUri( + _i8.Future<_i6.Response> getUri( Uri? uri, { Object? data, _i2.Options? options, @@ -362,7 +373,7 @@ class MockDio extends _i1.Mock implements _i9.Dio { #onReceiveProgress: onReceiveProgress, }, ), - returnValue: _i7.Future<_i6.Response>.value(_FakeResponse_4( + returnValue: _i8.Future<_i6.Response>.value(_FakeResponse_4( this, Invocation.method( #getUri, @@ -376,7 +387,7 @@ class MockDio extends _i1.Mock implements _i9.Dio { ), )), returnValueForMissingStub: - _i7.Future<_i6.Response>.value(_FakeResponse_4( + _i8.Future<_i6.Response>.value(_FakeResponse_4( this, Invocation.method( #getUri, @@ -389,10 +400,10 @@ class MockDio extends _i1.Mock implements _i9.Dio { }, ), )), - ) as _i7.Future<_i6.Response>); + ) as _i8.Future<_i6.Response>); @override - _i7.Future<_i6.Response> post( + _i8.Future<_i6.Response> post( String? path, { Object? data, Map? queryParameters, @@ -414,7 +425,7 @@ class MockDio extends _i1.Mock implements _i9.Dio { #onReceiveProgress: onReceiveProgress, }, ), - returnValue: _i7.Future<_i6.Response>.value(_FakeResponse_4( + returnValue: _i8.Future<_i6.Response>.value(_FakeResponse_4( this, Invocation.method( #post, @@ -430,7 +441,7 @@ class MockDio extends _i1.Mock implements _i9.Dio { ), )), returnValueForMissingStub: - _i7.Future<_i6.Response>.value(_FakeResponse_4( + _i8.Future<_i6.Response>.value(_FakeResponse_4( this, Invocation.method( #post, @@ -445,10 +456,10 @@ class MockDio extends _i1.Mock implements _i9.Dio { }, ), )), - ) as _i7.Future<_i6.Response>); + ) as _i8.Future<_i6.Response>); @override - _i7.Future<_i6.Response> postUri( + _i8.Future<_i6.Response> postUri( Uri? uri, { Object? data, _i2.Options? options, @@ -468,7 +479,7 @@ class MockDio extends _i1.Mock implements _i9.Dio { #onReceiveProgress: onReceiveProgress, }, ), - returnValue: _i7.Future<_i6.Response>.value(_FakeResponse_4( + returnValue: _i8.Future<_i6.Response>.value(_FakeResponse_4( this, Invocation.method( #postUri, @@ -483,7 +494,7 @@ class MockDio extends _i1.Mock implements _i9.Dio { ), )), returnValueForMissingStub: - _i7.Future<_i6.Response>.value(_FakeResponse_4( + _i8.Future<_i6.Response>.value(_FakeResponse_4( this, Invocation.method( #postUri, @@ -497,10 +508,10 @@ class MockDio extends _i1.Mock implements _i9.Dio { }, ), )), - ) as _i7.Future<_i6.Response>); + ) as _i8.Future<_i6.Response>); @override - _i7.Future<_i6.Response> put( + _i8.Future<_i6.Response> put( String? path, { Object? data, Map? queryParameters, @@ -522,7 +533,7 @@ class MockDio extends _i1.Mock implements _i9.Dio { #onReceiveProgress: onReceiveProgress, }, ), - returnValue: _i7.Future<_i6.Response>.value(_FakeResponse_4( + returnValue: _i8.Future<_i6.Response>.value(_FakeResponse_4( this, Invocation.method( #put, @@ -538,7 +549,7 @@ class MockDio extends _i1.Mock implements _i9.Dio { ), )), returnValueForMissingStub: - _i7.Future<_i6.Response>.value(_FakeResponse_4( + _i8.Future<_i6.Response>.value(_FakeResponse_4( this, Invocation.method( #put, @@ -553,10 +564,10 @@ class MockDio extends _i1.Mock implements _i9.Dio { }, ), )), - ) as _i7.Future<_i6.Response>); + ) as _i8.Future<_i6.Response>); @override - _i7.Future<_i6.Response> putUri( + _i8.Future<_i6.Response> putUri( Uri? uri, { Object? data, _i2.Options? options, @@ -576,7 +587,7 @@ class MockDio extends _i1.Mock implements _i9.Dio { #onReceiveProgress: onReceiveProgress, }, ), - returnValue: _i7.Future<_i6.Response>.value(_FakeResponse_4( + returnValue: _i8.Future<_i6.Response>.value(_FakeResponse_4( this, Invocation.method( #putUri, @@ -591,7 +602,7 @@ class MockDio extends _i1.Mock implements _i9.Dio { ), )), returnValueForMissingStub: - _i7.Future<_i6.Response>.value(_FakeResponse_4( + _i8.Future<_i6.Response>.value(_FakeResponse_4( this, Invocation.method( #putUri, @@ -605,10 +616,10 @@ class MockDio extends _i1.Mock implements _i9.Dio { }, ), )), - ) as _i7.Future<_i6.Response>); + ) as _i8.Future<_i6.Response>); @override - _i7.Future<_i6.Response> patch( + _i8.Future<_i6.Response> patch( String? path, { Object? data, Map? queryParameters, @@ -630,7 +641,7 @@ class MockDio extends _i1.Mock implements _i9.Dio { #onReceiveProgress: onReceiveProgress, }, ), - returnValue: _i7.Future<_i6.Response>.value(_FakeResponse_4( + returnValue: _i8.Future<_i6.Response>.value(_FakeResponse_4( this, Invocation.method( #patch, @@ -646,7 +657,7 @@ class MockDio extends _i1.Mock implements _i9.Dio { ), )), returnValueForMissingStub: - _i7.Future<_i6.Response>.value(_FakeResponse_4( + _i8.Future<_i6.Response>.value(_FakeResponse_4( this, Invocation.method( #patch, @@ -661,10 +672,10 @@ class MockDio extends _i1.Mock implements _i9.Dio { }, ), )), - ) as _i7.Future<_i6.Response>); + ) as _i8.Future<_i6.Response>); @override - _i7.Future<_i6.Response> patchUri( + _i8.Future<_i6.Response> patchUri( Uri? uri, { Object? data, _i2.Options? options, @@ -684,7 +695,7 @@ class MockDio extends _i1.Mock implements _i9.Dio { #onReceiveProgress: onReceiveProgress, }, ), - returnValue: _i7.Future<_i6.Response>.value(_FakeResponse_4( + returnValue: _i8.Future<_i6.Response>.value(_FakeResponse_4( this, Invocation.method( #patchUri, @@ -699,7 +710,7 @@ class MockDio extends _i1.Mock implements _i9.Dio { ), )), returnValueForMissingStub: - _i7.Future<_i6.Response>.value(_FakeResponse_4( + _i8.Future<_i6.Response>.value(_FakeResponse_4( this, Invocation.method( #patchUri, @@ -713,10 +724,10 @@ class MockDio extends _i1.Mock implements _i9.Dio { }, ), )), - ) as _i7.Future<_i6.Response>); + ) as _i8.Future<_i6.Response>); @override - _i7.Future<_i6.Response> delete( + _i8.Future<_i6.Response> delete( String? path, { Object? data, Map? queryParameters, @@ -734,7 +745,7 @@ class MockDio extends _i1.Mock implements _i9.Dio { #cancelToken: cancelToken, }, ), - returnValue: _i7.Future<_i6.Response>.value(_FakeResponse_4( + returnValue: _i8.Future<_i6.Response>.value(_FakeResponse_4( this, Invocation.method( #delete, @@ -748,7 +759,7 @@ class MockDio extends _i1.Mock implements _i9.Dio { ), )), returnValueForMissingStub: - _i7.Future<_i6.Response>.value(_FakeResponse_4( + _i8.Future<_i6.Response>.value(_FakeResponse_4( this, Invocation.method( #delete, @@ -761,10 +772,10 @@ class MockDio extends _i1.Mock implements _i9.Dio { }, ), )), - ) as _i7.Future<_i6.Response>); + ) as _i8.Future<_i6.Response>); @override - _i7.Future<_i6.Response> deleteUri( + _i8.Future<_i6.Response> deleteUri( Uri? uri, { Object? data, _i2.Options? options, @@ -780,7 +791,7 @@ class MockDio extends _i1.Mock implements _i9.Dio { #cancelToken: cancelToken, }, ), - returnValue: _i7.Future<_i6.Response>.value(_FakeResponse_4( + returnValue: _i8.Future<_i6.Response>.value(_FakeResponse_4( this, Invocation.method( #deleteUri, @@ -793,7 +804,7 @@ class MockDio extends _i1.Mock implements _i9.Dio { ), )), returnValueForMissingStub: - _i7.Future<_i6.Response>.value(_FakeResponse_4( + _i8.Future<_i6.Response>.value(_FakeResponse_4( this, Invocation.method( #deleteUri, @@ -805,17 +816,18 @@ class MockDio extends _i1.Mock implements _i9.Dio { }, ), )), - ) as _i7.Future<_i6.Response>); + ) as _i8.Future<_i6.Response>); @override - _i7.Future<_i6.Response> download( + _i8.Future<_i6.Response> download( String? urlPath, dynamic savePath, { _i2.ProgressCallback? onReceiveProgress, Map? queryParameters, _i10.CancelToken? cancelToken, bool? deleteOnError = true, - String? lengthHeader = r'content-length', + _i2.FileAccessMode? fileAccessMode = _i2.FileAccessMode.write, + String? lengthHeader = 'content-length', Object? data, _i2.Options? options, }) => @@ -831,13 +843,14 @@ class MockDio extends _i1.Mock implements _i9.Dio { #queryParameters: queryParameters, #cancelToken: cancelToken, #deleteOnError: deleteOnError, + #fileAccessMode: fileAccessMode, #lengthHeader: lengthHeader, #data: data, #options: options, }, ), returnValue: - _i7.Future<_i6.Response>.value(_FakeResponse_4( + _i8.Future<_i6.Response>.value(_FakeResponse_4( this, Invocation.method( #download, @@ -850,6 +863,7 @@ class MockDio extends _i1.Mock implements _i9.Dio { #queryParameters: queryParameters, #cancelToken: cancelToken, #deleteOnError: deleteOnError, + #fileAccessMode: fileAccessMode, #lengthHeader: lengthHeader, #data: data, #options: options, @@ -857,7 +871,7 @@ class MockDio extends _i1.Mock implements _i9.Dio { ), )), returnValueForMissingStub: - _i7.Future<_i6.Response>.value(_FakeResponse_4( + _i8.Future<_i6.Response>.value(_FakeResponse_4( this, Invocation.method( #download, @@ -870,22 +884,24 @@ class MockDio extends _i1.Mock implements _i9.Dio { #queryParameters: queryParameters, #cancelToken: cancelToken, #deleteOnError: deleteOnError, + #fileAccessMode: fileAccessMode, #lengthHeader: lengthHeader, #data: data, #options: options, }, ), )), - ) as _i7.Future<_i6.Response>); + ) as _i8.Future<_i6.Response>); @override - _i7.Future<_i6.Response> downloadUri( + _i8.Future<_i6.Response> downloadUri( Uri? uri, dynamic savePath, { _i2.ProgressCallback? onReceiveProgress, _i10.CancelToken? cancelToken, bool? deleteOnError = true, - String? lengthHeader = r'content-length', + _i2.FileAccessMode? fileAccessMode = _i2.FileAccessMode.write, + String? lengthHeader = 'content-length', Object? data, _i2.Options? options, }) => @@ -900,13 +916,14 @@ class MockDio extends _i1.Mock implements _i9.Dio { #onReceiveProgress: onReceiveProgress, #cancelToken: cancelToken, #deleteOnError: deleteOnError, + #fileAccessMode: fileAccessMode, #lengthHeader: lengthHeader, #data: data, #options: options, }, ), returnValue: - _i7.Future<_i6.Response>.value(_FakeResponse_4( + _i8.Future<_i6.Response>.value(_FakeResponse_4( this, Invocation.method( #downloadUri, @@ -918,6 +935,7 @@ class MockDio extends _i1.Mock implements _i9.Dio { #onReceiveProgress: onReceiveProgress, #cancelToken: cancelToken, #deleteOnError: deleteOnError, + #fileAccessMode: fileAccessMode, #lengthHeader: lengthHeader, #data: data, #options: options, @@ -925,7 +943,7 @@ class MockDio extends _i1.Mock implements _i9.Dio { ), )), returnValueForMissingStub: - _i7.Future<_i6.Response>.value(_FakeResponse_4( + _i8.Future<_i6.Response>.value(_FakeResponse_4( this, Invocation.method( #downloadUri, @@ -937,16 +955,17 @@ class MockDio extends _i1.Mock implements _i9.Dio { #onReceiveProgress: onReceiveProgress, #cancelToken: cancelToken, #deleteOnError: deleteOnError, + #fileAccessMode: fileAccessMode, #lengthHeader: lengthHeader, #data: data, #options: options, }, ), )), - ) as _i7.Future<_i6.Response>); + ) as _i8.Future<_i6.Response>); @override - _i7.Future<_i6.Response> request( + _i8.Future<_i6.Response> request( String? url, { Object? data, Map? queryParameters, @@ -968,7 +987,7 @@ class MockDio extends _i1.Mock implements _i9.Dio { #onReceiveProgress: onReceiveProgress, }, ), - returnValue: _i7.Future<_i6.Response>.value(_FakeResponse_4( + returnValue: _i8.Future<_i6.Response>.value(_FakeResponse_4( this, Invocation.method( #request, @@ -984,7 +1003,7 @@ class MockDio extends _i1.Mock implements _i9.Dio { ), )), returnValueForMissingStub: - _i7.Future<_i6.Response>.value(_FakeResponse_4( + _i8.Future<_i6.Response>.value(_FakeResponse_4( this, Invocation.method( #request, @@ -999,10 +1018,10 @@ class MockDio extends _i1.Mock implements _i9.Dio { }, ), )), - ) as _i7.Future<_i6.Response>); + ) as _i8.Future<_i6.Response>); @override - _i7.Future<_i6.Response> requestUri( + _i8.Future<_i6.Response> requestUri( Uri? uri, { Object? data, _i10.CancelToken? cancelToken, @@ -1022,7 +1041,7 @@ class MockDio extends _i1.Mock implements _i9.Dio { #onReceiveProgress: onReceiveProgress, }, ), - returnValue: _i7.Future<_i6.Response>.value(_FakeResponse_4( + returnValue: _i8.Future<_i6.Response>.value(_FakeResponse_4( this, Invocation.method( #requestUri, @@ -1037,7 +1056,7 @@ class MockDio extends _i1.Mock implements _i9.Dio { ), )), returnValueForMissingStub: - _i7.Future<_i6.Response>.value(_FakeResponse_4( + _i8.Future<_i6.Response>.value(_FakeResponse_4( this, Invocation.method( #requestUri, @@ -1051,16 +1070,16 @@ class MockDio extends _i1.Mock implements _i9.Dio { }, ), )), - ) as _i7.Future<_i6.Response>); + ) as _i8.Future<_i6.Response>); @override - _i7.Future<_i6.Response> fetch(_i2.RequestOptions? requestOptions) => + _i8.Future<_i6.Response> fetch(_i2.RequestOptions? requestOptions) => (super.noSuchMethod( Invocation.method( #fetch, [requestOptions], ), - returnValue: _i7.Future<_i6.Response>.value(_FakeResponse_4( + returnValue: _i8.Future<_i6.Response>.value(_FakeResponse_4( this, Invocation.method( #fetch, @@ -1068,14 +1087,60 @@ class MockDio extends _i1.Mock implements _i9.Dio { ), )), returnValueForMissingStub: - _i7.Future<_i6.Response>.value(_FakeResponse_4( + _i8.Future<_i6.Response>.value(_FakeResponse_4( this, Invocation.method( #fetch, [requestOptions], ), )), - ) as _i7.Future<_i6.Response>); + ) as _i8.Future<_i6.Response>); + + @override + _i7.Dio clone({ + _i2.BaseOptions? options, + _i5.Interceptors? interceptors, + _i3.HttpClientAdapter? httpClientAdapter, + _i4.Transformer? transformer, + }) => + (super.noSuchMethod( + Invocation.method( + #clone, + [], + { + #options: options, + #interceptors: interceptors, + #httpClientAdapter: httpClientAdapter, + #transformer: transformer, + }, + ), + returnValue: _FakeDio_5( + this, + Invocation.method( + #clone, + [], + { + #options: options, + #interceptors: interceptors, + #httpClientAdapter: httpClientAdapter, + #transformer: transformer, + }, + ), + ), + returnValueForMissingStub: _FakeDio_5( + this, + Invocation.method( + #clone, + [], + { + #options: options, + #interceptors: interceptors, + #httpClientAdapter: httpClientAdapter, + #transformer: transformer, + }, + ), + ), + ) as _i7.Dio); } /// A class which mocks [SirenDataProvider]. @@ -1095,15 +1160,6 @@ class MockSirenDataProvider extends _i1.Mock implements _i11.SirenDataProvider { ), ) as String); - @override - set userToken(String? _userToken) => super.noSuchMethod( - Invocation.setter( - #userToken, - _userToken, - ), - returnValueForMissingStub: null, - ); - @override String get recipientId => (super.noSuchMethod( Invocation.getter(#recipientId), @@ -1117,15 +1173,6 @@ class MockSirenDataProvider extends _i1.Mock implements _i11.SirenDataProvider { ), ) as String); - @override - set recipientId(String? _recipientId) => super.noSuchMethod( - Invocation.setter( - #recipientId, - _recipientId, - ), - returnValueForMissingStub: null, - ); - @override String get apiDomain => (super.noSuchMethod( Invocation.getter(#apiDomain), @@ -1140,41 +1187,32 @@ class MockSirenDataProvider extends _i1.Mock implements _i11.SirenDataProvider { ) as String); @override - set apiDomain(String? _apiDomain) => super.noSuchMethod( - Invocation.setter( - #apiDomain, - _apiDomain, - ), - returnValueForMissingStub: null, - ); - - @override - _i7.StreamController<_i8.StreamResponse> get inboxController => + _i8.StreamController<_i9.StreamResponse> get inboxController => (super.noSuchMethod( Invocation.getter(#inboxController), - returnValue: _FakeStreamController_5<_i8.StreamResponse>( + returnValue: _FakeStreamController_6<_i9.StreamResponse>( this, Invocation.getter(#inboxController), ), - returnValueForMissingStub: _FakeStreamController_5<_i8.StreamResponse>( + returnValueForMissingStub: _FakeStreamController_6<_i9.StreamResponse>( this, Invocation.getter(#inboxController), ), - ) as _i7.StreamController<_i8.StreamResponse>); + ) as _i8.StreamController<_i9.StreamResponse>); @override - _i7.StreamController<_i8.StreamResponse> get iconController => + _i8.StreamController<_i9.StreamResponse> get iconController => (super.noSuchMethod( Invocation.getter(#iconController), - returnValue: _FakeStreamController_5<_i8.StreamResponse>( + returnValue: _FakeStreamController_6<_i9.StreamResponse>( this, Invocation.getter(#iconController), ), - returnValueForMissingStub: _FakeStreamController_5<_i8.StreamResponse>( + returnValueForMissingStub: _FakeStreamController_6<_i9.StreamResponse>( this, Invocation.getter(#iconController), ), - ) as _i7.StreamController<_i8.StreamResponse>); + ) as _i8.StreamController<_i9.StreamResponse>); @override _i13.Status get tokenVerificationStatus => (super.noSuchMethod( @@ -1191,14 +1229,41 @@ class MockSirenDataProvider extends _i1.Mock implements _i11.SirenDataProvider { ) as bool); @override - _i7.Future initialize() => (super.noSuchMethod( + set userToken(String? _userToken) => super.noSuchMethod( + Invocation.setter( + #userToken, + _userToken, + ), + returnValueForMissingStub: null, + ); + + @override + set recipientId(String? _recipientId) => super.noSuchMethod( + Invocation.setter( + #recipientId, + _recipientId, + ), + returnValueForMissingStub: null, + ); + + @override + set apiDomain(String? _apiDomain) => super.noSuchMethod( + Invocation.setter( + #apiDomain, + _apiDomain, + ), + returnValueForMissingStub: null, + ); + + @override + _i8.Future initialize() => (super.noSuchMethod( Invocation.method( #initialize, [], ), - returnValue: _i7.Future.value(), - returnValueForMissingStub: _i7.Future.value(), - ) as _i7.Future); + returnValue: _i8.Future.value(), + returnValueForMissingStub: _i8.Future.value(), + ) as _i8.Future); @override void updateParams({ @@ -1227,26 +1292,26 @@ class MockSirenDataProvider extends _i1.Mock implements _i11.SirenDataProvider { ); @override - _i8.SirenErrorType getVerificationErrorType() => (super.noSuchMethod( + _i9.SirenErrorType getVerificationErrorType() => (super.noSuchMethod( Invocation.method( #getVerificationErrorType, [], ), - returnValue: _FakeSirenErrorType_6( + returnValue: _FakeSirenErrorType_7( this, Invocation.method( #getVerificationErrorType, [], ), ), - returnValueForMissingStub: _FakeSirenErrorType_6( + returnValueForMissingStub: _FakeSirenErrorType_7( this, Invocation.method( #getVerificationErrorType, [], ), ), - ) as _i8.SirenErrorType); + ) as _i9.SirenErrorType); @override void iconDispose() => super.noSuchMethod( diff --git a/test/services/network_service_test.mocks.dart b/test/services/network_service_test.mocks.dart index ee1e372..142127c 100644 --- a/test/services/network_service_test.mocks.dart +++ b/test/services/network_service_test.mocks.dart @@ -1,4 +1,4 @@ -// Mocks generated by Mockito 5.4.4 from annotations +// Mocks generated by Mockito 5.4.6 from annotations // in sirenapp_flutter_inbox/test/services/network_service_test.dart. // Do not manually edit this file. @@ -18,6 +18,7 @@ import 'package:sirenapp_flutter_inbox/src/services/api_client.dart' as _i3; // ignore_for_file: deprecated_member_use_from_same_package // ignore_for_file: implementation_imports // ignore_for_file: invalid_use_of_visible_for_testing_member +// ignore_for_file: must_be_immutable // ignore_for_file: prefer_const_constructors // ignore_for_file: unnecessary_parenthesis // ignore_for_file: camel_case_types diff --git a/test/widgets/siren_inbox_icon_test.mocks.dart b/test/widgets/siren_inbox_icon_test.mocks.dart index cc7c558..dd05da8 100644 --- a/test/widgets/siren_inbox_icon_test.mocks.dart +++ b/test/widgets/siren_inbox_icon_test.mocks.dart @@ -1,4 +1,4 @@ -// Mocks generated by Mockito 5.4.4 from annotations +// Mocks generated by Mockito 5.4.6 from annotations // in sirenapp_flutter_inbox/test/widgets/siren_inbox_icon_test.dart. // Do not manually edit this file. @@ -23,6 +23,7 @@ import 'package:sirenapp_flutter_inbox/src/services/api_client.dart' as _i4; // ignore_for_file: deprecated_member_use_from_same_package // ignore_for_file: implementation_imports // ignore_for_file: invalid_use_of_visible_for_testing_member +// ignore_for_file: must_be_immutable // ignore_for_file: prefer_const_constructors // ignore_for_file: unnecessary_parenthesis // ignore_for_file: camel_case_types @@ -87,15 +88,6 @@ class MockSirenDataProvider extends _i1.Mock implements _i5.SirenDataProvider { ), ) as String); - @override - set userToken(String? _userToken) => super.noSuchMethod( - Invocation.setter( - #userToken, - _userToken, - ), - returnValueForMissingStub: null, - ); - @override String get recipientId => (super.noSuchMethod( Invocation.getter(#recipientId), @@ -105,15 +97,6 @@ class MockSirenDataProvider extends _i1.Mock implements _i5.SirenDataProvider { ), ) as String); - @override - set recipientId(String? _recipientId) => super.noSuchMethod( - Invocation.setter( - #recipientId, - _recipientId, - ), - returnValueForMissingStub: null, - ); - @override String get apiDomain => (super.noSuchMethod( Invocation.getter(#apiDomain), @@ -123,15 +106,6 @@ class MockSirenDataProvider extends _i1.Mock implements _i5.SirenDataProvider { ), ) as String); - @override - set apiDomain(String? _apiDomain) => super.noSuchMethod( - Invocation.setter( - #apiDomain, - _apiDomain, - ), - returnValueForMissingStub: null, - ); - @override _i2.StreamController<_i3.StreamResponse> get inboxController => (super.noSuchMethod( @@ -164,6 +138,33 @@ class MockSirenDataProvider extends _i1.Mock implements _i5.SirenDataProvider { returnValue: false, ) as bool); + @override + set userToken(String? _userToken) => super.noSuchMethod( + Invocation.setter( + #userToken, + _userToken, + ), + returnValueForMissingStub: null, + ); + + @override + set recipientId(String? _recipientId) => super.noSuchMethod( + Invocation.setter( + #recipientId, + _recipientId, + ), + returnValueForMissingStub: null, + ); + + @override + set apiDomain(String? _apiDomain) => super.noSuchMethod( + Invocation.setter( + #apiDomain, + _apiDomain, + ), + returnValueForMissingStub: null, + ); + @override _i2.Future initialize() => (super.noSuchMethod( Invocation.method( diff --git a/test/widgets/siren_inbox_test.mocks.dart b/test/widgets/siren_inbox_test.mocks.dart index 6f46027..49279b6 100644 --- a/test/widgets/siren_inbox_test.mocks.dart +++ b/test/widgets/siren_inbox_test.mocks.dart @@ -1,4 +1,4 @@ -// Mocks generated by Mockito 5.4.4 from annotations +// Mocks generated by Mockito 5.4.6 from annotations // in sirenapp_flutter_inbox/test/widgets/siren_inbox_test.dart. // Do not manually edit this file. @@ -23,6 +23,7 @@ import 'package:sirenapp_flutter_inbox/src/services/api_client.dart' as _i4; // ignore_for_file: deprecated_member_use_from_same_package // ignore_for_file: implementation_imports // ignore_for_file: invalid_use_of_visible_for_testing_member +// ignore_for_file: must_be_immutable // ignore_for_file: prefer_const_constructors // ignore_for_file: unnecessary_parenthesis // ignore_for_file: camel_case_types @@ -87,15 +88,6 @@ class MockSirenDataProvider extends _i1.Mock implements _i5.SirenDataProvider { ), ) as String); - @override - set userToken(String? _userToken) => super.noSuchMethod( - Invocation.setter( - #userToken, - _userToken, - ), - returnValueForMissingStub: null, - ); - @override String get recipientId => (super.noSuchMethod( Invocation.getter(#recipientId), @@ -109,15 +101,6 @@ class MockSirenDataProvider extends _i1.Mock implements _i5.SirenDataProvider { ), ) as String); - @override - set recipientId(String? _recipientId) => super.noSuchMethod( - Invocation.setter( - #recipientId, - _recipientId, - ), - returnValueForMissingStub: null, - ); - @override String get apiDomain => (super.noSuchMethod( Invocation.getter(#apiDomain), @@ -131,15 +114,6 @@ class MockSirenDataProvider extends _i1.Mock implements _i5.SirenDataProvider { ), ) as String); - @override - set apiDomain(String? _apiDomain) => super.noSuchMethod( - Invocation.setter( - #apiDomain, - _apiDomain, - ), - returnValueForMissingStub: null, - ); - @override _i2.StreamController<_i3.StreamResponse> get inboxController => (super.noSuchMethod( @@ -182,6 +156,33 @@ class MockSirenDataProvider extends _i1.Mock implements _i5.SirenDataProvider { returnValueForMissingStub: false, ) as bool); + @override + set userToken(String? _userToken) => super.noSuchMethod( + Invocation.setter( + #userToken, + _userToken, + ), + returnValueForMissingStub: null, + ); + + @override + set recipientId(String? _recipientId) => super.noSuchMethod( + Invocation.setter( + #recipientId, + _recipientId, + ), + returnValueForMissingStub: null, + ); + + @override + set apiDomain(String? _apiDomain) => super.noSuchMethod( + Invocation.setter( + #apiDomain, + _apiDomain, + ), + returnValueForMissingStub: null, + ); + @override _i2.Future initialize() => (super.noSuchMethod( Invocation.method( From 51a22da4de3a964b2e9cb74b7cbe150a40e676de Mon Sep 17 00:00:00 2001 From: Anitta <99161914+anitta-keyvalue@users.noreply.github.com> Date: Thu, 29 May 2025 17:24:02 +0530 Subject: [PATCH 10/10] feat: Add filter for categories (#108) --- CHANGELOG.md | 5 + README.md | 231 +++++++++++--- lib/src/api/fetch_all_notification.dart | 19 +- lib/src/api/fetch_categories.dart | 64 ++++ lib/src/models/ui_models.dart | 108 +++++++ lib/src/theme/app_colors.dart | 16 + lib/src/theme/colors.dart | 18 ++ lib/src/theme/dark_colors.dart | 8 + lib/src/theme/light_colors.dart | 8 + lib/src/widgets/app_bar.dart | 395 +++++++++++++++++++++--- lib/src/widgets/siren_inbox.dart | 75 ++++- test/models/ui_models_test.dart | 48 +++ test/widgets/app_bar_test.dart | 141 +++++++++ 13 files changed, 1053 insertions(+), 83 deletions(-) create mode 100644 lib/src/api/fetch_categories.dart diff --git a/CHANGELOG.md b/CHANGELOG.md index 1918b23..a987aec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,11 @@ All notable changes to this project will be documented in this file. +## 1.3.0 + +### Added +- Added support to filter notifications by category. + ## 1.2.1 ### Added diff --git a/README.md b/README.md index 6745f4f..ee9f72b 100644 --- a/README.md +++ b/README.md @@ -102,74 +102,225 @@ Given below are the arguments of Siren Inbox Widget. | ----------------- | -------------------------------------------------------------------- | -------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | darkMode | Toggle to enable dark mode when custom theme is not passed | bool | false | | hideTab | Toggle to enable all and unread tabs | bool | false | -| itemsPerFetch | Number of notifications fetch per api request (have a max cap of 50) | int | 20 | +| itemsPerFetch | Number of notifications fetch per api request (max 50) | int | 20 | | listEmptyWidget | Custom widget for empty notification list | Widget | null | -| customCard | Custom widget to display the notification cards | Widget | null | +| customCard | Custom builder for notification cards | Widget Function(NotificationType) | null | | customLoader | Custom widget to display the initial loading state | Widget | null | | customErrorWidget | Custom error widget | Widget | null | | cardParams | Properties of notification card | CardParams | CardParams(hideAvatar: false, disableAutoMarkAsRead: false, hideDelete: false, deleteIcon: Icon(Icons.close), onAvatarClick: Function(NotificationType), hideMediaThumbnail: false, onMediaThumbnailClick: Function(NotificationType)) | -| headerParams | Properties of notification window header | HeaderParams | HeaderParams(hideHeader: false, hideClearAll: false,title: 'Notifications', customHeader: null showBackButton:false, backButton: null, onBackPress: ()=> null ) | -| tabParams | Properties of tab bar | TabParams | TabParams(tabs: [TabItem(key: 'ALL', title: 'All'), TabItem(key: 'UNREAD', title: 'Unread')], activeTabIndex:0,) | +| headerParams | Properties of notification window header | HeaderParams | HeaderParams(hideHeader: false, hideClearAll: false, title: 'Notifications', customHeader: null, showBackButton: false, backButton: null, onBackPress: null) | +| tabParams | Properties of tab bar | TabParams | TabParams(tabs: [TabItem(key: 'ALL', title: 'All'), TabItem(key: 'UNREAD', title: 'Unread')], activeTabIndex: 0) | | onCardClick | Custom click handler for notification cards | Function(NotificationType) | null | | onError | Callback for handling errors | Function(SirenErrorType) | null | | theme | Theme properties for custom color theme | CustomThemeColors | null | | customStyles | Style properties for custom styling | CustomStyles | null | +| customTabIndicator| Custom decoration for tab indicator | BoxDecoration | null | +| filterParams | Properties for configuring the filter dropdown | FilterParams | FilterParams(categoryFilterParams: CategoryFilterParams(showFilters: true, filterIconWidget: null, hideBadge: false, categoryFilterStyles: CategoryFilterStyles(dropdownTextStyle: null))) | #### Theme customization -Here are some of the available theme options: +Here are the available theme options: ```dart theme: CustomThemeColors( - primary: Colors.blue, - highlightedCardColor: Colors.blueAccent, - textColor: Colors.green, - cardColors: CardColors( - titleColor: Colors.grey, - subtitleColor: Colors.grey, - ), - inboxHeaderColors: InboxHeaderColors( - titleColor: Colors.redAccent, - headerActionColor: Colors.purpleAccent, - borderColor: Colors.cyanAccent - ), - ), + backgroundColor: Colors.blue, + primary: Colors.blueAccent, + highlightedCardColor: Colors.blue.shade100, + borderColor: Colors.grey.shade300, + deleteIcon: Colors.red, + clearAllIcon: Colors.grey, + textColor: Colors.black87, + dateColor: Colors.grey, + timerIcon: Colors.blue, + notificationIconColor: Colors.blue, + loaderColor: Colors.blue, + inboxHeaderColors: InboxHeaderColors( + background: Colors.white, + titleColor: Colors.black87, + headerActionColor: Colors.blue, + borderColor: Colors.grey.shade300 + ), + badgeColors: BadgeColors( + backgroundColor: Colors.red, + color: Colors.white + ), + cardColors: CardColors( + borderColor: Colors.grey.shade300, + background: Colors.white, + titleColor: Colors.black87, + subtitleColor: Colors.grey, + descriptionColor: Colors.black54 + ), + tabColors: TabColors( + containerBackgroundColor: Colors.white, + activeTabBackgroundColor: Colors.blue.shade50, + activeTabTextColor: Colors.blue, + inactiveTabTextColor: Colors.grey, + indicatorColor: Colors.blue + ), + filterColors: FilterColors( + categoryFilterColors: CategoryFilterColors( + filterIconBorderColor: Colors.grey.shade300, + filterBadgeColor: Colors.blue, + filterDropdownBackgroundColor: Colors.white, + filterCheckboxCheckedColor: Colors.blue, + filterCheckboxUncheckedColor: Colors.grey.shade300, + filterActionTextColor: Colors.black87, + filterIconColor: Colors.blue, + checkIconColor: Colors.white + ) + ) +) ``` -#### Style options +#### Style customization -Here are some of the custom style options for the notification inbox: +Here are the custom style options for the notification inbox: ```dart customStyles: CustomStyles( container: ContainerStyle( - padding: EdgeInsets.all(20), - decoration: BoxDecoration(color: Colors.yellow)), + padding: EdgeInsets.all(16), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(8) + ), + margin: EdgeInsets.all(8) + ), cardStyle: CardStyle( cardContainer: ContainerStyle( - padding: EdgeInsets.all(20), + padding: EdgeInsets.all(16), decoration: BoxDecoration( - color: Colors.yellow, - border: Border.all(color: Colors.red))), - cardTitle: TextStyle(fontSize: 22, fontWeight: FontWeight.w800), - cardSubtitle: - TextStyle(fontSize: 20, fontWeight: FontWeight.w700), - cardDescription: - TextStyle(fontSize: 18, fontWeight: FontWeight.w600), - dateStyle: TextStyle(fontSize: 16, fontWeight: FontWeight.w500), - avatarSize: 30, + color: Colors.white, + border: Border.all(color: Colors.grey.shade300), + borderRadius: BorderRadius.circular(8) + ) + ), + cardTitle: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w600, + color: Colors.black87 + ), + cardSubtitle: TextStyle( + fontSize: 14, + fontWeight: FontWeight.w500, + color: Colors.grey + ), + cardDescription: TextStyle( + fontSize: 14, + color: Colors.black54 ), + dateStyle: TextStyle( + fontSize: 12, + color: Colors.grey + ), + avatarSize: 40 + ), appBarStyle: InboxHeaderStyle( - headerTextStyle: - TextStyle(fontSize: 20, fontWeight: FontWeight.w900), - titlePadding: EdgeInsets.symmetric(horizontal: 30), - borderWidth: 5), - timerIconStyle: TimerIconStyle(size: 30), - deleteIconStyle: DeleteIconStyle(size: 30), - clearAllIconStyle: ClearAllIconStyle(size: 30), -), + headerTextStyle: TextStyle( + fontSize: 18, + fontWeight: FontWeight.w600, + color: Colors.black87 + ), + titlePadding: EdgeInsets.symmetric(horizontal: 16), + borderWidth: 1 + ), + notificationIconStyle: NotificationIconStyle( + size: 24 + ), + badgeStyle: BadgeStyle( + fontSize: 12, + size: 20, + top: 0, + right: 2 + ), + timerIconStyle: TimerIconStyle( + size: 20 + ), + deleteIconStyle: DeleteIconStyle( + size: 20 + ), + clearAllIconStyle: ClearAllIconStyle( + size: 20 + ), + tabStyles: TabStyles( + containerStyle: ContainerStyle( + padding: EdgeInsets.symmetric(horizontal: 16), + margin: EdgeInsets.only(bottom: 8) + ), + activeTabTextStyle: TextStyle( + fontSize: 14, + fontWeight: FontWeight.w600, + color: Colors.blue + ), + inActiveTabTextStyle: TextStyle( + fontSize: 14, + fontWeight: FontWeight.w500, + color: Colors.grey + ), + indicatorSize: 2, + indicatorPadding: EdgeInsets.symmetric(horizontal: 16) + ), + hideTabMargin: HideTabMargin( + upper: false, + lower: false + ), + filterStyles: FilterStyles( + categoryFilterStyles: CategoryFilterStyles( + dropdownTextStyle: TextStyle( + fontSize: 14, + color: Colors.black87 + ) + ) + ) +) +``` + +### 2.4. Filter Configuration + +The filter functionality allows users to filter notifications by categories. Here's how to configure it: + +```dart +SirenInbox( + filterParams: FilterParams( + categoryFilterParams: CategoryFilterParams( + showFilters: true, + filterIconWidget: Icon(Icons.filter_list), // Optional custom filter icon + hideBadge: false, // Optional hide badge showing number of selected filters + categoryFilterStyles: CategoryFilterStyles( // Optional custom styles for category filter + dropdownTextStyle: TextStyle( + fontSize: 14, + color: Colors.black87 + ) + ) + ) + ) +) +``` + +#### Filter Features: +- Custom filter icon support +- Badge showing number of selected filters (99+ for more than 99 selections) +- Dropdown with checkbox selection +- Customizable colors and styles for all filter components + +#### Category Filter Styles +You can customize the appearance of the category filter dropdown using `CategoryFilterStyles`: + +```dart +CategoryFilterStyles( + dropdownTextStyle: TextStyle( + fontSize: 14, + color: Colors.black87, + fontWeight: FontWeight.w500 + ) +) ``` +| Style Property | Description | Type | Default Value | +|------------------|------------------------------------------------|-----------|----------------------------------| +| dropdownTextStyle| Style for the category text in dropdown | TextStyle | fontSize: 14, color: Colors.black87 | + ## 3. Siren Class The `Siren Class` provides utility functions for modifying notifications. diff --git a/lib/src/api/fetch_all_notification.dart b/lib/src/api/fetch_all_notification.dart index b14f496..70b79fa 100644 --- a/lib/src/api/fetch_all_notification.dart +++ b/lib/src/api/fetch_all_notification.dart @@ -29,6 +29,7 @@ class FetchAllNotifications { bool? isRead, String? start, String? end, + List? categories, }) async { final apiPath = '${Generics.V2}${Generics.BASE_URL}${SirenDataProvider.instance.recipientId}/notifications'; @@ -53,8 +54,22 @@ class FetchAllNotifications { queryParams['isRead'] = isRead.toString(); } - final queryString = - queryParams.entries.map((e) => '${e.key}=${e.value}').join('&'); + // Build the query string + final queryParts = []; + + // Add all non-category parameters + queryParams.forEach((key, value) { + queryParts.add('$key=${Uri.encodeComponent(value)}'); + }); + + // Add each category as a separate parameter + if (categories != null && categories.isNotEmpty) { + for (final category in categories) { + queryParts.add('category=${Uri.encodeComponent(category)}'); + } + } + + final queryString = queryParts.join('&'); if (SirenDataProvider.instance.tokenVerificationStatus != Status.SUCCESS) { apiError = SirenDataProvider.instance.getVerificationErrorType(); diff --git a/lib/src/api/fetch_categories.dart b/lib/src/api/fetch_categories.dart new file mode 100644 index 0000000..d636211 --- /dev/null +++ b/lib/src/api/fetch_categories.dart @@ -0,0 +1,64 @@ +import 'package:sirenapp_flutter_inbox/src/constants/generics.dart'; +import 'package:sirenapp_flutter_inbox/src/data/siren_data_provider.dart'; +import 'package:sirenapp_flutter_inbox/src/errors/errors.dart'; +import 'package:sirenapp_flutter_inbox/src/models/api_response.dart'; +import 'package:sirenapp_flutter_inbox/src/services/api_client.dart'; +import 'package:sirenapp_flutter_inbox/src/services/api_provider.dart'; + +class FetchCategories { + FetchCategories._internal(); + static final FetchCategories instance = FetchCategories._internal(); + final ApiClient api = ApiClient(apiProvider()); + + List convertJsonToCategoryList(List dataList) { + return dataList.map((dynamic json) { + if (json is String) { + return json; + } + throw const FormatException('Invalid JSON format'); + }).toList(); + } + + Future fetchCategories() async { + final apiPath = + '${Generics.V2}${Generics.BASE_URL}${SirenDataProvider.instance.recipientId}/categories'; + final result = ApiResponse()..isLoading = true; + var apiError = Errors.notificationFetchFailedError; + + if (SirenDataProvider.instance.tokenVerificationStatus != Status.SUCCESS) { + apiError = SirenDataProvider.instance.getVerificationErrorType(); + result + ..isLoading = false + ..isError = true + ..data = null + ..rawResponse = Errors.rawResponseError + ..error = apiError; + return result; + } + + final apiResponse = await api.get( + path: apiPath, + ); + + if (apiResponse.statusCode != 0 && apiResponse.data != null) { + final dataList = + ApiResponse.fromJson(apiResponse.data).data as List?; + result + ..isLoading = false + ..isSuccess = apiResponse.statusCode == 200 + ..isError = apiResponse.statusCode != 200 + ..data = convertJsonToCategoryList(dataList ?? []) + ..rawResponse = apiResponse + ..error = apiError; + } else { + result + ..isLoading = false + ..isSuccess = false + ..isError = true + ..rawResponse = apiResponse + ..error = Errors.defaultError; + } + + return result; + } +} diff --git a/lib/src/models/ui_models.dart b/lib/src/models/ui_models.dart index 5631392..4853183 100644 --- a/lib/src/models/ui_models.dart +++ b/lib/src/models/ui_models.dart @@ -100,6 +100,7 @@ class CustomStyles { this.clearAllIconStyle, this.tabStyles, this.hideTabMargin, + this.filterStyles, }); /// The decoration for the Siren inbox list. @@ -126,9 +127,14 @@ class CustomStyles { /// Style of clear all icon in inbox default header final ClearAllIconStyle? clearAllIconStyle; + /// Styles for customizing the appearance of tabs in the Siren inbox final TabStyles? tabStyles; + /// Controls whether to hide margins above and below the tabs final HideTabMargin? hideTabMargin; + + /// Styles for customizing the appearance of filters + final FilterStyles? filterStyles; } class HideTabMargin { @@ -187,6 +193,7 @@ class CustomThemeColors { this.badgeColors, this.cardColors, this.tabColors, + this.filterColors, }); /// The background color for Siren inbox. @@ -233,6 +240,56 @@ class CustomThemeColors { /// The colors for tab bar final TabColors? tabColors; + + /// The colors for category dropdown + final FilterColors? filterColors; +} + +/// Custom theme colors to configure the appearance of filters +class FilterColors { + const FilterColors({ + this.categoryFilterColors, + }); + + /// The colors for category dropdown + final CategoryFilterColors? categoryFilterColors; +} + +class CategoryFilterColors { + CategoryFilterColors({ + this.filterIconBorderColor, + this.filterBadgeColor, + this.filterDropdownBackgroundColor, + this.filterCheckboxCheckedColor, + this.filterCheckboxUncheckedColor, + this.filterActionTextColor, + this.filterIconColor, + this.checkIconColor, + }); + + /// The border color for the filter icon button + final Color? filterIconBorderColor; + + /// The badge color for the filter icon + final Color? filterBadgeColor; + + /// The background color for the filter dropdown + final Color? filterDropdownBackgroundColor; + + /// The checked color for the filter checkbox + final Color? filterCheckboxCheckedColor; + + /// The unchecked color for the filter checkbox border + final Color? filterCheckboxUncheckedColor; + + /// The menu action text color + final Color? filterActionTextColor; + + /// The text color for the filter icon + final Color? filterIconColor; + + /// The color for the check icon in the filter dropdown + final Color? checkIconColor; } /// Custom theme colors to configure the appearance inbox list item. @@ -468,3 +525,54 @@ class TabColors { /// The color of the tab indicator. final Color? indicatorColor; } + +/// Styles for customizing the appearance of filters +class FilterStyles { + const FilterStyles({ + this.categoryFilterStyles, + }); + final CategoryFilterStyles? categoryFilterStyles; +} + +/// Properties for configuring the appearance of the category dropdown. +class CategoryFilterStyles { + /// Constructs a [CategoryFilterStyles] with optional parameters. + const CategoryFilterStyles({ + this.dropdownTextStyle, + }); + + /// The text style for dropdown items. + final TextStyle? dropdownTextStyle; +} + +class FilterParams { + const FilterParams({ + this.categoryFilterParams, + }); + + final CategoryFilterParams? categoryFilterParams; +} + +/// Properties for configuring the appearance and behavior of the category dropdown. +class CategoryFilterParams { + /// Constructs a [CategoryFilterParams] with optional parameters. + const CategoryFilterParams({ + this.showFilters = true, + this.filterIconWidget, + this.style, + this.hideBadge = false, + }); + + /// Flag to show the categories dropdown in the app bar. + final bool showFilters; + + /// Custom widget to display the filter UI. + /// If not provided, a default filter UI will be used. + final Widget? filterIconWidget; + + /// Style properties for the category dropdown. + final CategoryFilterStyles? style; + + /// Flag to hide the badge showing number of selected filters. + final bool hideBadge; +} diff --git a/lib/src/theme/app_colors.dart b/lib/src/theme/app_colors.dart index 02e95f3..66661b0 100644 --- a/lib/src/theme/app_colors.dart +++ b/lib/src/theme/app_colors.dart @@ -44,6 +44,14 @@ class AppColors { required this.tabBarInActiveColor, required this.textColor, required this.timerIcon, + required this.filterIconBorderColor, + required this.filterBadgeColor, + required this.filterDropdownBackgroundColor, + required this.filterCheckboxCheckedColor, + required this.filterCheckboxUncheckedColor, + required this.filterActionTextColor, + required this.filterIconColor, + required this.checkIconColor, }); factory AppColors.lightColorTheme() => lightColors; @@ -89,4 +97,12 @@ class AppColors { Color tabBarInActiveColor; Color textColor; Color timerIcon; + Color filterIconBorderColor; + Color filterBadgeColor; + Color filterDropdownBackgroundColor; + Color filterCheckboxCheckedColor; + Color filterCheckboxUncheckedColor; + Color filterActionTextColor; + Color filterIconColor; + Color checkIconColor; } diff --git a/lib/src/theme/colors.dart b/lib/src/theme/colors.dart index 39245a9..648d3a3 100644 --- a/lib/src/theme/colors.dart +++ b/lib/src/theme/colors.dart @@ -34,4 +34,22 @@ class SirenAppColors { static const Color avatarIconLight = Color(0xFF98A2B3); static const Color avatarPlaceholderBgDark = Color(0xFF4C4C4C); static const Color avatarIconDark = Color(0xFF999999); + + // Filter (category) dropdown and badge colors + static const Color filterIconBorderLight = Color(0xFFE0E0E0); + static const Color filterBadgeLight = Color(0xFFD32F2F); + static const Color filterDropdownBackgroundLight = Color(0xFFFFFFFF); + static const Color filterCheckboxCheckedLight = Color(0xFFFF7043); + static const Color filterCheckboxUncheckedLight = Color(0xFFBDBDBD); + static const Color filterTextColorLight = Color(0xFF344054); + static const Color checkIconColorLight = Colors.white; + + static const Color filterIconBorderDark = Color(0xFF444444); + static const Color filterBadgeDark = Color(0xFFD32F2F); + static const Color filterDropdownBackgroundDark = Color(0xFF2F2F2F); + static const Color filterCheckboxCheckedDark = Color(0xFFFF7043); + static const Color filterCheckboxUncheckedDark = Color(0xFF888888); + static const Color menuActionTextColorLight = Color(0xFF101928); + static const Color filterTextColorDark = Colors.white; + static const Color checkIconColorDark = Colors.white; } diff --git a/lib/src/theme/dark_colors.dart b/lib/src/theme/dark_colors.dart index 387fd93..a43a892 100644 --- a/lib/src/theme/dark_colors.dart +++ b/lib/src/theme/dark_colors.dart @@ -43,4 +43,12 @@ final darkColors = AppColors( tabBarInActiveColor: SirenAppColors.grey300, textColor: SirenAppColors.grey700Complementary, timerIcon: SirenAppColors.grey400, + filterIconBorderColor: SirenAppColors.filterIconBorderDark, + filterBadgeColor: SirenAppColors.filterBadgeDark, + filterDropdownBackgroundColor: SirenAppColors.filterDropdownBackgroundDark, + filterCheckboxCheckedColor: SirenAppColors.filterCheckboxCheckedDark, + filterCheckboxUncheckedColor: SirenAppColors.filterCheckboxUncheckedDark, + filterActionTextColor: SirenAppColors.avatarPlaceholderBgLight, + filterIconColor: SirenAppColors.grey500Complementary, + checkIconColor: SirenAppColors.checkIconColorDark, ); diff --git a/lib/src/theme/light_colors.dart b/lib/src/theme/light_colors.dart index 17eb429..12d7cd9 100644 --- a/lib/src/theme/light_colors.dart +++ b/lib/src/theme/light_colors.dart @@ -43,4 +43,12 @@ final lightColors = AppColors( tabBarInActiveColor: SirenAppColors.grey700, textColor: SirenAppColors.grey700, timerIcon: SirenAppColors.grey500, + filterIconBorderColor: SirenAppColors.filterIconBorderLight, + filterBadgeColor: SirenAppColors.filterBadgeLight, + filterDropdownBackgroundColor: SirenAppColors.filterDropdownBackgroundLight, + filterCheckboxCheckedColor: SirenAppColors.filterCheckboxCheckedLight, + filterCheckboxUncheckedColor: SirenAppColors.filterCheckboxUncheckedLight, + filterActionTextColor: SirenAppColors.menuActionTextColorLight, + filterIconColor: SirenAppColors.grey500, + checkIconColor: SirenAppColors.checkIconColorLight, ); diff --git a/lib/src/widgets/app_bar.dart b/lib/src/widgets/app_bar.dart index da5df3a..79b76ae 100644 --- a/lib/src/widgets/app_bar.dart +++ b/lib/src/widgets/app_bar.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:sirenapp_flutter_inbox/src/constants/strings.dart'; import 'package:sirenapp_flutter_inbox/src/models/ui_models.dart'; +import 'package:sirenapp_flutter_inbox/src/theme/app_colors.dart'; import 'package:sirenapp_flutter_inbox/src/theme/app_theme.dart'; class SirenAppBar extends StatelessWidget implements PreferredSizeWidget { @@ -11,14 +12,25 @@ class SirenAppBar extends StatelessWidget implements PreferredSizeWidget { this.styles, this.colors, this.isDarkMode, + this.categories = const [], + this.selectedValues = const [], + this.onCategorySelected, + this.filterIconWidget, + this.hideBadge = false, super.key, }); + final VoidCallback? onClearAllPressed; final bool isNonEmptyNotifications; final HeaderParams? headerParams; final CustomStyles? styles; final CustomThemeColors? colors; final bool? isDarkMode; + final List categories; + final List selectedValues; + final void Function(String)? onCategorySelected; + final Widget? filterIconWidget; + final bool hideBadge; @override Size get preferredSize { @@ -48,13 +60,13 @@ class SirenAppBar extends StatelessWidget implements PreferredSizeWidget { : null, ), height: preferredSize.height, - child: headerParams?.customHeader ?? - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Padding( - padding: const EdgeInsets.only(right: 16, left: 20), - child: Row( + child: Container( + margin: const EdgeInsets.only(right: 16, left: 20), + child: headerParams?.customHeader ?? + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( children: [ if (headerParams?.showBackButton ?? false) Semantics( @@ -93,49 +105,352 @@ class SirenAppBar extends StatelessWidget implements PreferredSizeWidget { ), ], ), - ), - if (!(headerParams?.hideClearAll ?? false)) - Semantics( - label: 'siren-header-clear-all', - hint: 'Tap to clear all notifications', - child: GestureDetector( - key: const Key('siren-header-clear-all'), - onTap: () { - if (isNonEmptyNotifications && - onClearAllPressed != null) { - onClearAllPressed!(); - } - }, - child: Opacity( - opacity: isNonEmptyNotifications ? 1 : 0.4, - child: Row( - children: [ - Padding( - padding: const EdgeInsets.only(right: 4), - child: Icon( - Icons.clear_all, - size: styles?.clearAllIconStyle?.size ?? 24, - color: colors?.clearAllIcon ?? - defaultColors.appBarActionText, + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + if (categories.isNotEmpty) + Semantics( + label: 'siren-filter', + hint: 'Tap to filter notifications by categories', + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: SizedBox( + key: const Key('siren-filter'), + width: 44, + height: 44, + child: _CategoryFilter( + categories: categories, + selectedValues: selectedValues, + onSelectionChanged: onCategorySelected, + colors: colors, + defaultColors: defaultColors, + styles: styles, + filterIconWidget: filterIconWidget, + hideBadge: hideBadge, ), ), - Text( - Strings.clear_all, - style: TextStyle( - fontSize: 14, - fontWeight: FontWeight.w500, - color: colors - ?.inboxHeaderColors?.headerActionColor ?? - defaultColors.appBarActionText, + ), + ), + if (!(headerParams?.hideClearAll ?? false)) + Semantics( + label: 'siren-header-clear-all', + hint: 'Tap to clear all notifications', + child: GestureDetector( + key: const Key('siren-header-clear-all'), + onTap: () { + if (isNonEmptyNotifications && + onClearAllPressed != null) { + onClearAllPressed!(); + } + }, + child: Opacity( + opacity: isNonEmptyNotifications ? 1 : 0.4, + child: Row( + children: [ + Padding( + padding: const EdgeInsets.only(right: 4), + child: Icon( + Icons.clear_all, + size: styles?.clearAllIconStyle?.size ?? 24, + color: colors?.clearAllIcon ?? + defaultColors.appBarActionText, + ), + ), + Text( + Strings.clear_all, + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.w500, + color: colors?.inboxHeaderColors + ?.headerActionColor ?? + defaultColors.appBarActionText, + ), + ), + ], ), ), - ], + ), + ), + ], + ), + ], + ), + ), + ); + } +} + +class _CategoryFilter extends StatefulWidget { + const _CategoryFilter({ + required this.categories, + required this.selectedValues, + required this.onSelectionChanged, + required this.colors, + required this.defaultColors, + required this.styles, + this.filterIconWidget, + this.hideBadge = false, + }); + + final List categories; + final List selectedValues; + final void Function(String)? onSelectionChanged; + final CustomThemeColors? colors; + final AppColors defaultColors; + final Widget? filterIconWidget; + final CustomStyles? styles; + final bool hideBadge; + + @override + State<_CategoryFilter> createState() => _CategoryFilterState(); +} + +class _CategoryFilterState extends State<_CategoryFilter> { + final LayerLink _layerLink = LayerLink(); + OverlayEntry? _overlayEntry; + bool _isDropdownOpen = false; + + void _toggleDropdown() { + if (_isDropdownOpen) { + _removeOverlay(); + } else { + _showOverlay(); + } + } + + void _showOverlay() { + _overlayEntry = _createOverlayEntry(); + Overlay.of(context).insert(_overlayEntry!); + _isDropdownOpen = true; + } + + void _removeOverlay() { + _overlayEntry?.remove(); + _overlayEntry = null; + _isDropdownOpen = false; + } + + void _handleItemSelection(String category) { + if (widget.onSelectionChanged != null) { + widget.onSelectionChanged?.call(category); + // Update the overlay to reflect the new selection + _overlayEntry?.markNeedsBuild(); + } + } + + OverlayEntry _createOverlayEntry() { + final renderBox = context.findRenderObject()! as RenderBox; + final size = renderBox.size; + final offset = renderBox.localToGlobal(Offset.zero); + + return OverlayEntry( + builder: (context) { + return GestureDetector( + behavior: HitTestBehavior.translucent, + onTap: _removeOverlay, + child: Stack( + children: [ + Positioned( + left: offset.dx, + top: offset.dy + size.height + 8, + width: 220, + child: CompositedTransformFollower( + link: _layerLink, + showWhenUnlinked: false, + offset: Offset(-220 + size.width, size.height), + child: Material( + color: widget.colors?.filterColors?.categoryFilterColors + ?.filterDropdownBackgroundColor ?? + widget.defaultColors.filterDropdownBackgroundColor, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + elevation: 4, + child: Padding( + padding: const EdgeInsets.only(bottom: 8), + child: ConstrainedBox( + constraints: const BoxConstraints(maxHeight: 300), + child: NotificationListener( + onNotification: (_) => true, + child: ListView.builder( + shrinkWrap: true, + itemCount: widget.categories.length, + itemBuilder: (context, index) { + final category = widget.categories[index]; + final isSelected = + widget.selectedValues.contains(category); + + return GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: () => _handleItemSelection(category), + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: 14, + vertical: 8, + ), + child: Row( + children: [ + Container( + width: 24, + height: 24, + decoration: BoxDecoration( + color: isSelected + ? widget + .colors + ?.filterColors + ?.categoryFilterColors + ?.filterCheckboxCheckedColor ?? + widget.defaultColors + .filterCheckboxCheckedColor + : Colors.transparent, + border: Border.all( + color: isSelected + ? widget + .colors + ?.filterColors + ?.categoryFilterColors + ?.filterCheckboxCheckedColor ?? + widget.defaultColors + .filterCheckboxCheckedColor + : widget + .colors + ?.filterColors + ?.categoryFilterColors + ?.filterCheckboxUncheckedColor ?? + widget.defaultColors + .filterCheckboxUncheckedColor, + width: 1.5, + ), + borderRadius: + BorderRadius.circular(6), + ), + child: isSelected + ? Center( + child: Icon( + Icons.check, + color: widget + .colors + ?.filterColors + ?.categoryFilterColors + ?.checkIconColor ?? + widget.defaultColors + .checkIconColor, + size: 16, + ), + ) + : null, + ), + const SizedBox(width: 12), + Expanded( + child: Text( + category.isEmpty + ? 'Others' + : category, + overflow: TextOverflow.ellipsis, + style: widget + .styles + ?.filterStyles + ?.categoryFilterStyles + ?.dropdownTextStyle + ?.copyWith( + color: widget + .colors + ?.filterColors + ?.categoryFilterColors + ?.filterActionTextColor ?? + widget.defaultColors + .filterActionTextColor, + ) ?? + TextStyle( + fontSize: 14, + color: widget + .colors + ?.filterColors + ?.categoryFilterColors + ?.filterActionTextColor ?? + widget.defaultColors + .filterActionTextColor, + ), + ), + ), + ], + ), + ), + ); + }, + ), + ), ), ), ), ), + ), ], ), + ); + }, + ); + } + + @override + void dispose() { + _removeOverlay(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return CompositedTransformTarget( + link: _layerLink, + child: Stack( + clipBehavior: Clip.none, + children: [ + GestureDetector( + onTap: _toggleDropdown, + child: Container( + width: 44, + height: 44, + decoration: BoxDecoration( + border: Border.all( + color: widget.colors?.filterColors?.categoryFilterColors + ?.filterIconBorderColor ?? + widget.defaultColors.filterIconBorderColor, + ), + borderRadius: BorderRadius.circular(8), + ), + child: widget.filterIconWidget ?? + Icon( + Icons.filter_alt_outlined, + color: widget.colors?.filterColors?.categoryFilterColors + ?.filterIconColor ?? + widget.defaultColors.filterIconColor, + size: 24, + ), + ), + ), + if (widget.selectedValues.isNotEmpty && !widget.hideBadge) + Positioned( + right: -11, + top: -4, + child: Container( + width: 22, + height: 22, + decoration: BoxDecoration( + color: widget.colors?.filterColors?.categoryFilterColors + ?.filterBadgeColor ?? + widget.defaultColors.filterBadgeColor, + shape: BoxShape.circle, + ), + child: Center( + child: Text( + '${widget.selectedValues.length > 99 ? '99+' : widget.selectedValues.length}', + style: const TextStyle(color: Colors.white, fontSize: 10), + ), + ), + ), + ), + ], + ), ); } } diff --git a/lib/src/widgets/siren_inbox.dart b/lib/src/widgets/siren_inbox.dart index 78139f1..44f767b 100644 --- a/lib/src/widgets/siren_inbox.dart +++ b/lib/src/widgets/siren_inbox.dart @@ -5,6 +5,7 @@ import 'package:flutter/material.dart'; import 'package:sirenapp_flutter_inbox/sirenapp_flutter_inbox.dart'; import 'package:sirenapp_flutter_inbox/src/api/delete_notification_by_id.dart'; import 'package:sirenapp_flutter_inbox/src/api/fetch_all_notification.dart'; +import 'package:sirenapp_flutter_inbox/src/api/fetch_categories.dart'; import 'package:sirenapp_flutter_inbox/src/api/mark_all_notifications_as_viewed.dart'; import 'package:sirenapp_flutter_inbox/src/api/notifications_bulk_update.dart'; import 'package:sirenapp_flutter_inbox/src/api/read_notification_by_id.dart'; @@ -35,6 +36,7 @@ class SirenInbox extends StatefulWidget { this.theme, this.customStyles, this.customTabIndicator, + this.filterParams, }); /// Flag for enabling dark mode. @@ -81,6 +83,9 @@ class SirenInbox extends StatefulWidget { final BoxDecoration? customTabIndicator; + /// Properties for configuring the category dropdown. + final FilterParams? filterParams; + @override State createState() => _SirenInboxState(); } @@ -98,8 +103,11 @@ class _SirenInboxState extends State bool _enableClearAll = true; List notifications = []; + List allCategories = []; + List selectedCategories = []; late final DeleteNotificationById _deleteNotificationById; late final ReadNotificationById _readNotificationById; + late final FetchCategories _fetchCategories; late Timer? _periodicUpdateRef; late StreamSubscription _subscription; late ScrollController _inboxScrollController; @@ -135,7 +143,10 @@ class _SirenInboxState extends State Future _initialize() async { if (SirenDataProvider.instance.tokenVerificationStatus == Status.SUCCESS) { - await initialFetchNotification(); + await Future.wait([ + initialFetchNotification(), + _fetchAllCategories(), + ]); } else if (SirenDataProvider.instance.tokenVerificationStatus == Status.FAILED || !SirenDataProvider.instance.isProviderInitialized) { @@ -146,6 +157,19 @@ class _SirenInboxState extends State } } + Future _fetchAllCategories() async { + final response = await _fetchCategories.fetchCategories(); + if (response.isSuccess && response.data != null) { + safeSetState(() { + allCategories = (response.data as List) + ..removeWhere((e) => e.isEmpty) + ..addAll(['']); + }); + } else { + widget.onError?.call(response.error ?? SirenErrorType()); + } + } + void _initializeVariables() { pageSize = max(min(widget.itemsPerFetch ?? Generics.PAGE_SIZE, 50), 0); _periodicUpdateRef = Timer(const Duration(days: 1), () {}); @@ -163,6 +187,7 @@ class _SirenInboxState extends State _deleteNotificationById = DeleteNotificationById.instance; _readNotificationById = ReadNotificationById.instance; + _fetchCategories = FetchCategories.instance; _tabController = TabController( length: InboxTabs.values.length, vsync: this, @@ -309,6 +334,7 @@ class _SirenInboxState extends State notifications[0].createdAt, ) : null, + categories: selectedCategories, ); if (fetchedNotifications.isSuccess) { final newNotifications = @@ -343,6 +369,7 @@ class _SirenInboxState extends State end: DateTime.now().toUtc().toIso8601String(), size: pageSize, isRead: getIsRead(), + categories: selectedCategories, ); if (fetchedNotifications.isSuccess) { @@ -447,6 +474,7 @@ class _SirenInboxState extends State ), size: pageSize, isRead: getIsRead(), + categories: selectedCategories, ); if (fetchedNotifications.isSuccess) { final newNotifications = @@ -500,6 +528,19 @@ class _SirenInboxState extends State } } + void _updateSelectedCategories(String category) { + setState(() { + if (selectedCategories.contains(category)) { + selectedCategories.remove(category); + } else { + selectedCategories.add(category); + } + }); + // Reset and fetch notifications with new category selection + _reset(cancelFetch: true); + initialFetchNotification(); + } + Widget _buildInboxBody( ScrollController controller, List data, @@ -547,6 +588,22 @@ class _SirenInboxState extends State isNonEmptyNotifications: _enableClearAll, headerParams: widget.headerParams, styles: widget.customStyles, + categories: + widget.filterParams?.categoryFilterParams?.showFilters ?? false + ? allCategories + : const [], + selectedValues: + widget.filterParams?.categoryFilterParams?.showFilters ?? false + ? selectedCategories + : const [], + onCategorySelected: + widget.filterParams?.categoryFilterParams?.showFilters ?? false + ? _updateSelectedCategories + : null, + filterIconWidget: + widget.filterParams?.categoryFilterParams?.filterIconWidget, + hideBadge: + widget.filterParams?.categoryFilterParams?.hideBadge ?? false, ), body: Column( children: [ @@ -652,6 +709,22 @@ class _SirenInboxState extends State isNonEmptyNotifications: _enableClearAll, headerParams: widget.headerParams, styles: widget.customStyles, + categories: + widget.filterParams?.categoryFilterParams?.showFilters ?? false + ? allCategories + : const [], + selectedValues: + widget.filterParams?.categoryFilterParams?.showFilters ?? false + ? selectedCategories + : const [], + onCategorySelected: + widget.filterParams?.categoryFilterParams?.showFilters ?? false + ? _updateSelectedCategories + : null, + filterIconWidget: + widget.filterParams?.categoryFilterParams?.filterIconWidget, + hideBadge: + widget.filterParams?.categoryFilterParams?.hideBadge ?? false, ), body: _buildInboxBody(_inboxScrollController, notifications, false), ); diff --git a/test/models/ui_models_test.dart b/test/models/ui_models_test.dart index 7ab5d81..46dd342 100644 --- a/test/models/ui_models_test.dart +++ b/test/models/ui_models_test.dart @@ -189,4 +189,52 @@ void main() { expect(tabColors.inactiveTabTextColor, Colors.black); }); }); + + group('FilterParams', () { + test('constructor should initialize properties with default values', () { + const filterParams = CategoryFilterParams(); + + expect(filterParams.showFilters, true); + expect(filterParams.filterIconWidget, null); + expect(filterParams.style, null); + expect(filterParams.hideBadge, false); + }); + + test('constructor should initialize properties with provided values', () { + const customIcon = Icon(Icons.tune); + const filterParams = CategoryFilterParams( + showFilters: false, + filterIconWidget: customIcon, + hideBadge: true, + ); + + expect(filterParams.showFilters, false); + expect(filterParams.filterIconWidget, customIcon); + expect(filterParams.hideBadge, true); + }); + }); + + group('FilterColors', () { + test('constructor should initialize properties with provided values', () { + final filterColors = CategoryFilterColors( + filterIconBorderColor: Colors.red, + filterBadgeColor: Colors.blue, + filterDropdownBackgroundColor: Colors.green, + filterCheckboxCheckedColor: Colors.yellow, + filterCheckboxUncheckedColor: Colors.purple, + filterActionTextColor: Colors.orange, + filterIconColor: Colors.pink, + checkIconColor: Colors.brown, + ); + + expect(filterColors.filterIconBorderColor, Colors.red); + expect(filterColors.filterBadgeColor, Colors.blue); + expect(filterColors.filterDropdownBackgroundColor, Colors.green); + expect(filterColors.filterCheckboxCheckedColor, Colors.yellow); + expect(filterColors.filterCheckboxUncheckedColor, Colors.purple); + expect(filterColors.filterActionTextColor, Colors.orange); + expect(filterColors.filterIconColor, Colors.pink); + expect(filterColors.checkIconColor, Colors.brown); + }); + }); } diff --git a/test/widgets/app_bar_test.dart b/test/widgets/app_bar_test.dart index 5e2af73..0cd9a92 100644 --- a/test/widgets/app_bar_test.dart +++ b/test/widgets/app_bar_test.dart @@ -132,4 +132,145 @@ void main() { await tester.tap(find.text('Clear All')); expect(clearAllPressed, true); }); + + // New tests for filter functionality + testWidgets('SirenAppBar displays filter icon when categories are provided', + (WidgetTester tester) async { + await tester.pumpWidget( + MaterialApp( + home: Scaffold( + appBar: SirenAppBar( + headerParams: HeaderParams( + title: 'Title', + showBackButton: false, + ), + isNonEmptyNotifications: false, + categories: const ['Category 1', 'Category 2'], + ), + ), + ), + ); + + expect(find.byIcon(Icons.filter_alt_outlined), findsOneWidget); + }); + + testWidgets( + 'SirenAppBar does not display filter icon when no categories are provided', + (WidgetTester tester) async { + await tester.pumpWidget( + MaterialApp( + home: Scaffold( + appBar: SirenAppBar( + headerParams: HeaderParams( + title: 'Title', + showBackButton: false, + ), + isNonEmptyNotifications: false, + ), + ), + ), + ); + + expect(find.byIcon(Icons.filter_alt_outlined), findsNothing); + }); + + testWidgets('SirenAppBar displays filter badge with selected count', + (WidgetTester tester) async { + await tester.pumpWidget( + MaterialApp( + home: Scaffold( + appBar: SirenAppBar( + headerParams: HeaderParams( + title: 'Title', + showBackButton: false, + ), + isNonEmptyNotifications: false, + categories: const ['Category 1', 'Category 2'], + selectedValues: const ['Category 1'], + ), + ), + ), + ); + + expect(find.text('1'), findsOneWidget); + }); + + testWidgets( + 'SirenAppBar does not display filter badge when hideBadge is true', + (WidgetTester tester) async { + await tester.pumpWidget( + MaterialApp( + home: Scaffold( + appBar: SirenAppBar( + headerParams: HeaderParams( + title: 'Title', + showBackButton: false, + ), + isNonEmptyNotifications: false, + categories: const ['Category 1', 'Category 2'], + selectedValues: const ['Category 1'], + hideBadge: true, + ), + ), + ), + ); + + expect(find.text('1'), findsNothing); + }); + + testWidgets( + 'SirenAppBar calls onCategorySelected when filter item is selected', + (WidgetTester tester) async { + String? selectedCategory; + await tester.pumpWidget( + MaterialApp( + home: Scaffold( + appBar: SirenAppBar( + headerParams: HeaderParams( + title: 'Title', + showBackButton: false, + ), + isNonEmptyNotifications: false, + categories: const ['Category 1', 'Category 2'], + onCategorySelected: (category) { + selectedCategory = category; + }, + ), + ), + ), + ); + + // Open filter dropdown + await tester.tap(find.byIcon(Icons.filter_alt_outlined)); + await tester.pumpAndSettle(); + + // Select a category + await tester.tap(find.text('Category 1')); + await tester.pumpAndSettle(); + + expect(selectedCategory, 'Category 1'); + }); + + testWidgets('SirenAppBar uses custom filter icon widget when provided', + (WidgetTester tester) async { + const customIcon = Icon(Icons.tune); + await tester.pumpWidget( + MaterialApp( + home: Scaffold( + appBar: SirenAppBar( + headerParams: HeaderParams( + title: 'Title', + showBackButton: false, + ), + isNonEmptyNotifications: false, + categories: const ['Category 1', 'Category 2'], + filterIconWidget: customIcon, + ), + ), + ), + ); + + expect(find.byIcon(Icons.tune), findsOneWidget); + expect(find.byIcon(Icons.filter_alt_outlined), findsNothing); + }); }