From 3da59245720378cd0a823035bcf4f6c046b77fa7 Mon Sep 17 00:00:00 2001 From: phuoc Date: Tue, 5 Nov 2024 14:24:10 +0700 Subject: [PATCH 01/20] support daily notification Signed-off-by: phuoc --- .../additional_data/additional_data.dart | 34 +++- lib/model/additional_data/cs_view_thread.dart | 3 +- .../daily_notification_data.dart | 152 ++++++++++++++++++ .../jg_crystalline_work_generated.dart | 3 +- .../navigate_additional_data.dart | 1 + .../additional_data/view_collection.dart | 3 +- .../additional_data/view_exhibition.dart | 1 + .../additional_data/view_new_message.dart | 3 +- lib/model/additional_data/view_postcard.dart | 3 +- lib/screen/app_router.dart | 1 + .../dailies_work/dailies_work_page.dart | 65 +++++++- lib/screen/home/home_navigation_page.dart | 18 ++- lib/util/inapp_notifications.dart | 28 ++-- lib/util/metric_helper.dart | 6 +- lib/util/notification_type.dart | 10 +- lib/view/cast_button.dart | 27 ++-- 16 files changed, 320 insertions(+), 38 deletions(-) create mode 100644 lib/model/additional_data/daily_notification_data.dart diff --git a/lib/model/additional_data/additional_data.dart b/lib/model/additional_data/additional_data.dart index 65cd7acb9..10714e2e7 100644 --- a/lib/model/additional_data/additional_data.dart +++ b/lib/model/additional_data/additional_data.dart @@ -1,4 +1,5 @@ import 'package:autonomy_flutter/model/additional_data/cs_view_thread.dart'; +import 'package:autonomy_flutter/model/additional_data/daily_notification_data.dart'; import 'package:autonomy_flutter/model/additional_data/jg_crystalline_work_generated.dart'; import 'package:autonomy_flutter/model/additional_data/navigate_additional_data.dart'; import 'package:autonomy_flutter/model/additional_data/view_collection.dart' @@ -14,10 +15,12 @@ import 'package:flutter/cupertino.dart'; class AdditionalData { final NotificationType notificationType; final String? announcementContentId; + final String? linkText; AdditionalData({ required this.notificationType, this.announcementContentId, + this.linkText, }); bool get isTappable => false; @@ -27,10 +30,13 @@ class AdditionalData { try { final notificationType = NotificationType.fromString(type ?? json['notification_type']); + final String? linkText = json['link_text']; final defaultAdditionalData = AdditionalData( - notificationType: notificationType, - announcementContentId: announcementContentId); + notificationType: notificationType, + announcementContentId: announcementContentId, + linkText: linkText, + ); switch (notificationType) { case NotificationType.customerSupportNewMessage: @@ -44,6 +50,7 @@ class AdditionalData { issueId: issueId, notificationType: notificationType, announcementContentId: announcementContentId, + linkText: linkText, ); case NotificationType.artworkCreated: case NotificationType.artworkReceived: @@ -51,6 +58,7 @@ class AdditionalData { return view_collection_handler.ViewCollection( notificationType: notificationType, announcementContentId: announcementContentId, + linkText: linkText, ); case NotificationType.newMessage: final groupId = json['group_id']; @@ -62,6 +70,7 @@ class AdditionalData { groupId: groupId, notificationType: notificationType, announcementContentId: announcementContentId, + linkText: linkText, ); case NotificationType.newPostcardTrip: case NotificationType.postcardShareExpired: @@ -74,6 +83,7 @@ class AdditionalData { indexID: indexID, notificationType: notificationType, announcementContentId: announcementContentId, + linkText: linkText, ); case NotificationType.jgCrystallineWorkHasArrived: final jgExhibitionId = JohnGerrardHelper.exhibitionID; @@ -81,6 +91,7 @@ class AdditionalData { exhibitionId: jgExhibitionId ?? '', notificationType: notificationType, announcementContentId: announcementContentId, + linkText: linkText, ); case NotificationType.jgCrystallineWorkGenerated: final tokenId = json['token_id']; @@ -92,6 +103,7 @@ class AdditionalData { tokenId: tokenId, notificationType: notificationType, announcementContentId: announcementContentId, + linkText: linkText, ); case NotificationType.exhibitionViewingOpening: case NotificationType.exhibitionSalesOpening: @@ -105,6 +117,7 @@ class AdditionalData { exhibitionId: exhibitionId, notificationType: notificationType, announcementContentId: announcementContentId, + linkText: linkText, ); case NotificationType.navigate: final navigationRoute = json['navigation_route']; @@ -114,7 +127,22 @@ class AdditionalData { notificationType: notificationType, announcementContentId: announcementContentId, homeIndex: homeIndex, + linkText: linkText, ); + case NotificationType.daily: + final subType = json['notification_sub_type']; + final dailyType = DailyNotificationType.fromString(subType?? ''); + if (dailyType == null) { + log.warning('AdditionalData: dailyType is null'); + return defaultAdditionalData; + } + return DailyNotificationData( + dailyNotificationType: dailyType, + notificationType: notificationType, + announcementContentId: announcementContentId, + linkText: linkText, + ); + default: return defaultAdditionalData; } @@ -130,5 +158,5 @@ class AdditionalData { log.info('AdditionalData: handle tap: $notificationType'); } - bool prepareAndDidSuccess() => true; + Future prepareAndDidSuccess() => Future.value(true); } diff --git a/lib/model/additional_data/cs_view_thread.dart b/lib/model/additional_data/cs_view_thread.dart index ae4e4ceaa..513f6b23f 100644 --- a/lib/model/additional_data/cs_view_thread.dart +++ b/lib/model/additional_data/cs_view_thread.dart @@ -16,6 +16,7 @@ class CsViewThread extends AdditionalData { required this.issueId, required super.notificationType, super.announcementContentId, + super.linkText, }); final CustomerSupportService _customerSupportService = @@ -37,7 +38,7 @@ class CsViewThread extends AdditionalData { } @override - bool prepareAndDidSuccess() { + Future prepareAndDidSuccess() async { _customerSupportService.triggerReloadMessages.value += 1; unawaited(_customerSupportService.getChatThreads()); if (issueId == memoryValues.viewingSupportThreadIssueID) { diff --git a/lib/model/additional_data/daily_notification_data.dart b/lib/model/additional_data/daily_notification_data.dart new file mode 100644 index 000000000..045e9b731 --- /dev/null +++ b/lib/model/additional_data/daily_notification_data.dart @@ -0,0 +1,152 @@ +import 'dart:async'; + +import 'package:autonomy_flutter/common/injector.dart'; +import 'package:autonomy_flutter/model/additional_data/additional_data.dart'; +import 'package:autonomy_flutter/model/dailies.dart'; +import 'package:autonomy_flutter/screen/app_router.dart'; +import 'package:autonomy_flutter/screen/exhibition_details/exhibition_detail_page.dart'; +import 'package:autonomy_flutter/screen/feralfile_series/feralfile_series_page.dart'; +import 'package:autonomy_flutter/service/feralfile_service.dart'; +import 'package:autonomy_flutter/service/navigation_service.dart'; +import 'package:autonomy_flutter/util/log.dart'; +import 'package:flutter/material.dart'; +import 'package:sentry_flutter/sentry_flutter.dart'; + +enum DailyNotificationType { + viewDaily, + revisitDaily, + viewDailySeries, + viewDailyExhibition, + meetDailyArtist, + displayDailyOnTV, + ; + + static DailyNotificationType? fromString(String value) { + switch (value) { + case 'view_daily': + return DailyNotificationType.viewDaily; + case 'revisit_daily': + return DailyNotificationType.revisitDaily; + case 'view_daily_series': + return DailyNotificationType.viewDailySeries; + case 'view_daily_exhibition': + return DailyNotificationType.viewDailyExhibition; + case 'meet_daily_artist': + return DailyNotificationType.meetDailyArtist; + case 'display_daily_on_tv': + return DailyNotificationType.displayDailyOnTV; + default: + log.info('DailyNotificationType: fromString: unknown value: $value'); + return null; + } + } +} + +class DailyNotificationData extends AdditionalData { + final DailyNotificationType dailyNotificationType; + + final _navigationService = injector(); + final _feralFileService = injector(); + DailyToken? _dailyToken; + + DailyNotificationData({ + required this.dailyNotificationType, + required super.notificationType, + super.announcementContentId, + super.linkText, + }); + + @override + bool get isTappable => true; + + @override + Future handleTap(BuildContext context) async { + log.info('DailyNotificationData: handle tap'); + bool isDailyTokenAvailable = await prepareAndDidSuccess(); + if (!isDailyTokenAvailable) { + log.warning('Tapped at daily notification: dailyToken is null'); + unawaited(Sentry.captureMessage( + 'Tapped at daily notification: dailyToken is null')); + return; + } + switch (dailyNotificationType) { + case DailyNotificationType.viewDaily: + case DailyNotificationType.revisitDaily: + await _navigationService.navigatePath(AppRouter.dailyWorkPage); + dailyWorkKey.currentState?.trackInterest(); + case DailyNotificationType.viewDailySeries: + final artwork = _dailyToken!.artwork; + if (artwork == null) { + _logAndSendSentryForNullData('artwork'); + return; + } + final series = await _feralFileService.getSeries(artwork.seriesID); + if (!context.mounted) { + return; + } + await Navigator.of(context).pushNamed( + AppRouter.feralFileSeriesPage, + arguments: FeralFileSeriesPagePayload( + seriesId: artwork.seriesID, + exhibitionId: series.exhibitionID, + ), + ); + case DailyNotificationType.viewDailyExhibition: + final artwork = _dailyToken!.artwork; + if (artwork == null) { + _logAndSendSentryForNullData('artwork'); + return; + } + final series = await _feralFileService.getSeries(artwork.seriesID); + final exhibition = series.exhibition; + if (exhibition == null) { + _logAndSendSentryForNullData('exhibition'); + return; + } + if (!context.mounted) { + return; + } + await Navigator.of(context).pushNamed( + AppRouter.exhibitionDetailPage, + arguments: + ExhibitionDetailPayload(exhibitions: [exhibition], index: 0), + ); + case DailyNotificationType.meetDailyArtist: + final artwork = _dailyToken!.artwork; + if (artwork == null) { + _logAndSendSentryForNullData('artwork'); + return; + } + final series = await _feralFileService.getSeries(artwork.seriesID); + final alumniID = series.artistAlumniAccountID; + await injector().openFeralFileArtistPage(alumniID); + case DailyNotificationType.displayDailyOnTV: + await _navigationService.navigatePath(AppRouter.dailyWorkPage); + await Future.delayed(const Duration(milliseconds: 300), () { + dailyWorkKey.currentState?.openDisplayDialog(); + }); + + default: + } + } + + void _logAndSendSentryForNullData(String data) { + log.warning('DailyNotificationData: $data is null'); + unawaited(Sentry.captureMessage('DailyNotificationData: $data is null')); + } + + @override + Future prepareAndDidSuccess() async { + if (_dailyToken != null) { + return true; + } + _dailyToken = await _feralFileService.getCurrentDailiesToken(); + if (_dailyToken == null) { + log.warning('DailyNotificationData: dailyToken is null'); + unawaited( + Sentry.captureMessage('DailyNotificationData: dailyToken is null')); + return false; + } + return true; + } +} diff --git a/lib/model/additional_data/jg_crystalline_work_generated.dart b/lib/model/additional_data/jg_crystalline_work_generated.dart index c11946dfc..47bb0f033 100644 --- a/lib/model/additional_data/jg_crystalline_work_generated.dart +++ b/lib/model/additional_data/jg_crystalline_work_generated.dart @@ -15,6 +15,7 @@ class JgCrystallineWorkGenerated extends AdditionalData { required this.tokenId, required super.notificationType, super.announcementContentId, + super.linkText, }); @override @@ -29,7 +30,7 @@ class JgCrystallineWorkGenerated extends AdditionalData { } @override - bool prepareAndDidSuccess() { + Future prepareAndDidSuccess() async { unawaited(injector().refreshTokens()); return true; } diff --git a/lib/model/additional_data/navigate_additional_data.dart b/lib/model/additional_data/navigate_additional_data.dart index 83b17257a..857172407 100644 --- a/lib/model/additional_data/navigate_additional_data.dart +++ b/lib/model/additional_data/navigate_additional_data.dart @@ -15,6 +15,7 @@ class NavigateAdditionalData extends AdditionalData { required this.navigationRoute, required super.notificationType, super.announcementContentId, + super.linkText, this.homeIndex, }); diff --git a/lib/model/additional_data/view_collection.dart b/lib/model/additional_data/view_collection.dart index 194016069..c748e3151 100644 --- a/lib/model/additional_data/view_collection.dart +++ b/lib/model/additional_data/view_collection.dart @@ -12,6 +12,7 @@ class ViewCollection extends AdditionalData { ViewCollection({ required super.notificationType, super.announcementContentId, + super.linkText, }); @override @@ -29,7 +30,7 @@ class ViewCollection extends AdditionalData { } @override - bool prepareAndDidSuccess() { + Future prepareAndDidSuccess() async { unawaited(injector().refreshTokens()); return true; } diff --git a/lib/model/additional_data/view_exhibition.dart b/lib/model/additional_data/view_exhibition.dart index 62ed57921..9f6a50c7b 100644 --- a/lib/model/additional_data/view_exhibition.dart +++ b/lib/model/additional_data/view_exhibition.dart @@ -11,6 +11,7 @@ class ViewExhibitionData extends AdditionalData { required this.exhibitionId, required super.notificationType, super.announcementContentId, + super.linkText, }); @override diff --git a/lib/model/additional_data/view_new_message.dart b/lib/model/additional_data/view_new_message.dart index fce200abc..1c806f5ae 100644 --- a/lib/model/additional_data/view_new_message.dart +++ b/lib/model/additional_data/view_new_message.dart @@ -21,6 +21,7 @@ class ViewNewMessage extends AdditionalData { required this.groupId, required super.notificationType, super.announcementContentId, + super.linkText, }); final RemoteConfigService _remoteConfigService = @@ -66,7 +67,7 @@ class ViewNewMessage extends AdditionalData { } @override - bool prepareAndDidSuccess() { + Future prepareAndDidSuccess() async { if (!_remoteConfigService.getBool(ConfigGroup.viewDetail, ConfigKey.chat)) { return false; } diff --git a/lib/model/additional_data/view_postcard.dart b/lib/model/additional_data/view_postcard.dart index 19b2d4c87..189faa9db 100644 --- a/lib/model/additional_data/view_postcard.dart +++ b/lib/model/additional_data/view_postcard.dart @@ -17,6 +17,7 @@ class ViewPostcard extends AdditionalData { required this.indexID, required super.notificationType, super.announcementContentId, + super.linkText, }); @override @@ -49,7 +50,7 @@ class ViewPostcard extends AdditionalData { } @override - bool prepareAndDidSuccess() { + Future prepareAndDidSuccess() async { unawaited(injector().refreshTokens()); return true; } diff --git a/lib/screen/app_router.dart b/lib/screen/app_router.dart index 155bd801c..19cc15151 100644 --- a/lib/screen/app_router.dart +++ b/lib/screen/app_router.dart @@ -133,6 +133,7 @@ import 'package:page_transition/page_transition.dart'; GlobalKey homePageKey = GlobalKey(); GlobalKey homePageNoTransactionKey = GlobalKey(); GlobalKey feralFileHomeKey = GlobalKey(); +final GlobalKey dailyWorkKey = GlobalKey(); class AppRouter { static const createPlayListPage = 'create_playlist_page'; diff --git a/lib/screen/dailies_work/dailies_work_page.dart b/lib/screen/dailies_work/dailies_work_page.dart index 8115a8f80..6ab7b71c2 100644 --- a/lib/screen/dailies_work/dailies_work_page.dart +++ b/lib/screen/dailies_work/dailies_work_page.dart @@ -58,6 +58,15 @@ class DailyWorkPageState extends State late int _currentIndex; ScrollController? _scrollController; final _artworkKey = GlobalKey(); + final _displayButtonKey = GlobalKey(); + + DailyToken? get _currentDailyToken => + context.read().state.currentDailyToken; + + bool _trackingInterest = false; + Timer? _trackingInterestTimer; + static const _scrollLikingThreshold = 100.0; + static const _stayDurationLikingThreshold = Duration(seconds: 5); @override void initState() { @@ -65,7 +74,7 @@ class DailyWorkPageState extends State context.read().add(GetDailyAssetTokenEvent()); _pageController = PageController(); _pageController!.addListener(() { - _pageControllerListenser(); + _pageControllerListener(); }); _currentIndex = _pageController!.initialPage; _scrollController = ScrollController(); @@ -81,7 +90,7 @@ class DailyWorkPageState extends State super.dispose(); } - void _pageControllerListenser() { + void _pageControllerListener() { if (_pageController!.page != 0) { pauseDailyWork(); } else { @@ -89,6 +98,51 @@ class DailyWorkPageState extends State } } + void openDisplayDialog() { + // ignore isSubscribed because it's not necessary + unawaited(_displayButtonKey.currentState?.onTap(context, true)); + } + + void didPushed() { + _stopTrackingInterest(); + } + + void trackInterest() { + if (_trackingInterest) { + return; + } + log.info('start trackingInterest in Daily'); + _trackingInterest = true; + _trackingInterestTimer = Timer(_stayDurationLikingThreshold, () { + _setUserInterested(); + }); + } + + void _stopTrackingInterest() { + log.info('stopTrackingInterest in Daily'); + _trackingInterest = false; + _trackingInterestTimer?.cancel(); + } + + void _setUserInterested() { + if (!_trackingInterest) { + return; + } + log.info('Set User Interested in Daily'); + _stopTrackingInterest(); + if (_currentDailyToken == null) { + return; + } + final data = { + MetricParameter.tokenId: _currentDailyToken!.tokenID, + }; + unawaited(injector() + .addEvent(MetricEventName.playlistView, data: data)); + unawaited(injector + .get() + .addEvent(MetricEventName.dailyLiked, data: data)); + } + Future scheduleNextDailyWork(BuildContext context) async { setState(() { _remainingDuration = _calcRemainingDuration; @@ -225,12 +279,16 @@ class DailyWorkPageState extends State textAlign: TextAlign.left), ), FFCastButton( + key: _displayButtonKey, displayKey: CastDailyWorkRequest.displayKey, onDeviceSelected: (device) { context.read().add( CanvasDeviceCastDailyWorkEvent( device, CastDailyWorkRequest())); }, + onTap: () { + _setUserInterested(); + }, text: 'display'.tr(), shouldCheckSubscription: false, ), @@ -422,6 +480,9 @@ class DailyWorkPageState extends State duration: const Duration(milliseconds: 300), curve: Curves.easeInOut)); } + if (_scrollController!.offset > _scrollLikingThreshold) { + _setUserInterested(); + } return true; }, child: CustomScrollView( diff --git a/lib/screen/home/home_navigation_page.dart b/lib/screen/home/home_navigation_page.dart index 5f3abc312..a9efeaba6 100644 --- a/lib/screen/home/home_navigation_page.dart +++ b/lib/screen/home/home_navigation_page.dart @@ -86,7 +86,6 @@ class HomeNavigationPageState extends State late int _selectedIndex; PageController? _pageController; late List _pages; - final GlobalKey _dailyWorkKey = GlobalKey(); final GlobalKey _organizeHomeKey = GlobalKey(); final _configurationService = injector(); late Timer? _timer; @@ -110,14 +109,21 @@ class HomeNavigationPageState extends State await onItemTapped(HomeNavigatorTab.explore.index); } + void _notifyMoveOutDaily() { + dailyWorkKey.currentState?.didPushed(); + } + Future onItemTapped(int index) async { + if (index != HomeNavigatorTab.daily.index) { + _notifyMoveOutDaily(); + } if (index < _pages.length) { // handle scroll to top when tap on the same tab if (_selectedIndex == index) { if (index == HomeNavigatorTab.explore.index) { feralFileHomeKey.currentState?.scrollToTop(); } else if (index == HomeNavigatorTab.daily.index) { - _dailyWorkKey.currentState?.scrollToTop(); + dailyWorkKey.currentState?.scrollToTop(); } else if (index == HomeNavigatorTab.collection.index) { _organizeHomeKey.currentState?.scrollToTop(); } @@ -127,9 +133,9 @@ class HomeNavigationPageState extends State // if tap on daily, resume daily work, // otherwise pause daily work if (index == HomeNavigatorTab.daily.index) { - _dailyWorkKey.currentState?.resumeDailyWork(); + dailyWorkKey.currentState?.resumeDailyWork(); } else { - _dailyWorkKey.currentState?.pauseDailyWork(); + dailyWorkKey.currentState?.pauseDailyWork(); } } setState(() { @@ -252,7 +258,7 @@ class HomeNavigationPageState extends State BlocProvider.value(value: injector()), ], child: DailyWorkPage( - key: _dailyWorkKey, + key: dailyWorkKey, )), MultiBlocProvider( providers: [ @@ -358,7 +364,7 @@ class HomeNavigationPageState extends State injector() .add(ListPlaylistLoadPlaylist(refreshDefaultPlaylist: true)); if (_selectedIndex == HomeNavigatorTab.daily.index) { - _dailyWorkKey.currentState?.resumeDailyWork(); + dailyWorkKey.currentState?.resumeDailyWork(); } } diff --git a/lib/util/inapp_notifications.dart b/lib/util/inapp_notifications.dart index 1496bc559..ee3b54249 100644 --- a/lib/util/inapp_notifications.dart +++ b/lib/util/inapp_notifications.dart @@ -17,11 +17,12 @@ import 'package:feralfile_app_theme/feral_file_app_theme.dart'; import 'package:flutter/material.dart'; import 'package:flutter_vibrate/flutter_vibrate.dart'; import 'package:overlay_support/overlay_support.dart'; + // ignore: implementation_imports import 'package:overlay_support/src/overlay_state_finder.dart'; Widget _notificationToast(BuildContext context, String id, - {Function? handler, String? body}) => + {Function? handler, String? body, String? tappingText}) => _SimpleNotificationToast( notification: body ?? '', key: Key(id), @@ -29,10 +30,13 @@ Widget _notificationToast(BuildContext context, String id, handler?.call(); }, addOnTextSpan: [ - if (handler != null) + if (tappingText != null) TextSpan( - text: ' ${'tap_to_view'.tr()}', - style: Theme.of(context).textTheme.ppMori400FFYellow14, + text: ' $tappingText', + style: Theme.of(context) + .textTheme + .ppMori400FFYellow14 + .copyWith(color: AppColor.feralFileLightBlue), ) ], ); @@ -196,12 +200,18 @@ Future showNotifications( configurationService.showingNotification.value = true; final notification = showSimpleNotification( - _notificationToast(context, id, handler: () async { - /// this is how to detect user tap on notification - /// this must put before handler?.call() to make sure it's called first + _notificationToast( + context, + id, + handler: () async { + /// this is how to detect user tap on notification + /// this must put before handler?.call() to make sure it's called first - handler?.call(); - }, body: body), + handler?.call(); + }, + body: body, + tappingText: additionalData?.linkText, + ), background: Colors.transparent, elevation: 0, duration: const Duration(days: 1), diff --git a/lib/util/metric_helper.dart b/lib/util/metric_helper.dart index 108464fe7..8c91721f9 100644 --- a/lib/util/metric_helper.dart +++ b/lib/util/metric_helper.dart @@ -2,7 +2,9 @@ enum MetricEventName { openApp, dailyView, playlistView, - exhibitionView; + exhibitionView, + dailyLiked, + ; String get name { switch (this) { @@ -14,6 +16,8 @@ enum MetricEventName { return 'PLAYLIST_VIEW'; case MetricEventName.exhibitionView: return 'EXHIBITION_VIEW'; + case MetricEventName.dailyLiked: + return 'DAILY_LIKED'; } } } diff --git a/lib/util/notification_type.dart b/lib/util/notification_type.dart index a5ab32642..16411fc7f 100644 --- a/lib/util/notification_type.dart +++ b/lib/util/notification_type.dart @@ -29,7 +29,9 @@ enum NotificationType { exhibitionSalesOpening, exhibitionSaleClosing, navigate, - general; + general, + daily, + ; // toString method @override @@ -65,6 +67,8 @@ enum NotificationType { return 'navigate'; case NotificationType.general: return 'general'; + case NotificationType.daily: + return 'daily'; } } @@ -99,6 +103,8 @@ enum NotificationType { return NotificationType.exhibitionSaleClosing; case 'navigate': return NotificationType.navigate; + case 'daily': + return NotificationType.daily; default: return NotificationType.general; } @@ -138,7 +144,7 @@ class NotificationHandler { return; } // prepare for handling notification - final shouldShow = additionalData.prepareAndDidSuccess(); + final shouldShow = await additionalData.prepareAndDidSuccess(); if (!shouldShow || !context.mounted) { return; } diff --git a/lib/view/cast_button.dart b/lib/view/cast_button.dart index db29f4cbd..f6d4b5a7a 100644 --- a/lib/view/cast_button.dart +++ b/lib/view/cast_button.dart @@ -24,6 +24,7 @@ class FFCastButton extends StatefulWidget { final String? text; final String? type; final bool shouldCheckSubscription; + final VoidCallback? onTap; const FFCastButton({ required this.displayKey, @@ -32,15 +33,16 @@ class FFCastButton extends StatefulWidget { this.onDeviceSelected, this.text, this.shouldCheckSubscription = true, + this.onTap, }); @override - State createState() => _FFCastButtonState(); + State createState() => FFCastButtonState(); } final keyboardManagerKey = GlobalKey(); -class _FFCastButtonState extends State { +class FFCastButtonState extends State { late CanvasDeviceBloc _canvasDeviceBloc; final _upgradesBloc = injector.get(); @@ -65,14 +67,8 @@ class _FFCastButtonState extends State { final isSubscribed = subscriptionState.isSubscribed; return GestureDetector( onTap: () async { - if (!widget.shouldCheckSubscription || isSubscribed) { - await injector().showStreamAction( - widget.displayKey, - widget.onDeviceSelected, - ); - } else { - await _showUpgradeDialog(context); - } + widget.onTap?.call(); + await onTap(context, isSubscribed); }, child: Semantics( label: 'cast_icon', @@ -131,6 +127,17 @@ class _FFCastButtonState extends State { ); } + Future onTap(BuildContext context ,bool isSubscribed) async { + if (!widget.shouldCheckSubscription || isSubscribed) { + await injector().showStreamAction( + widget.displayKey, + widget.onDeviceSelected, + ); + } else { + await _showUpgradeDialog(context); + } + } + Future _showUpgradeDialog(BuildContext context) async { await UIHelper.showDialog( context, From 2944e7f07f57d1061bd2e99b6d62c150ac23dc7b Mon Sep 17 00:00:00 2001 From: phuoc Date: Wed, 6 Nov 2024 11:11:22 +0700 Subject: [PATCH 02/20] fix announcement api and model Signed-off-by: phuoc --- lib/gateway/iap_api.dart | 2 +- lib/gateway/iap_api.g.dart | 2 +- lib/model/announcement/announcement.dart | 11 ++++++++++- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/lib/gateway/iap_api.dart b/lib/gateway/iap_api.dart index b68e4d8a0..6651b4d48 100644 --- a/lib/gateway/iap_api.dart +++ b/lib/gateway/iap_api.dart @@ -30,7 +30,7 @@ abstract class IAPApi { Future generateIdentityHash( @Body() Map body); - @GET('/apis/v2/announcements') + @GET('/apis/v2/notifications') Future> getAnnouncements(@Body() AnnouncementRequest body); @POST('/apis/v2/gift-code/{id}/redeem') diff --git a/lib/gateway/iap_api.g.dart b/lib/gateway/iap_api.g.dart index 7bbbc0514..072b55b5f 100644 --- a/lib/gateway/iap_api.g.dart +++ b/lib/gateway/iap_api.g.dart @@ -115,7 +115,7 @@ class _IAPApi implements IAPApi { ) .compose( _dio.options, - '/apis/v2/announcements', + '/apis/v2/notifications', queryParameters: queryParameters, data: _data, ) diff --git a/lib/model/announcement/announcement.dart b/lib/model/announcement/announcement.dart index f27dfc7d2..706716d7d 100644 --- a/lib/model/announcement/announcement.dart +++ b/lib/model/announcement/announcement.dart @@ -7,6 +7,9 @@ class Announcement extends ChatThread { final Map additionalData; final DateTime startedAt; final DateTime endedAt; + final String? imageURL; + final String? notificationType; + final String? deliveryTimeOfDay; Announcement({ required this.announcementContentId, @@ -14,15 +17,21 @@ class Announcement extends ChatThread { required this.additionalData, required this.startedAt, required this.endedAt, + this.imageURL, + this.notificationType, + this.deliveryTimeOfDay, }); factory Announcement.fromJson(Map json) => Announcement( - announcementContentId: json['announcementContentID'], + announcementContentId: json['notificationContentID'], content: json['content'], additionalData: (json['additionalData'] ?? {}) as Map, startedAt: DateTime.parse(json['startedAt']), endedAt: DateTime.parse(json['endedAt']), + imageURL: json['imageURL'], + notificationType: json['notificationType'], + deliveryTimeOfDay: json['deliveryTimeOfDay'], ); bool get isExpired => DateTime.now().isAfter(endedAt); From bdc55a70219bdac743517319fd7f82f7eb2e2f71 Mon Sep 17 00:00:00 2001 From: phuoc Date: Wed, 6 Nov 2024 11:26:58 +0700 Subject: [PATCH 03/20] fix time Signed-off-by: phuoc --- lib/model/announcement/announcement.dart | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/model/announcement/announcement.dart b/lib/model/announcement/announcement.dart index 706716d7d..3486fbc85 100644 --- a/lib/model/announcement/announcement.dart +++ b/lib/model/announcement/announcement.dart @@ -27,8 +27,9 @@ class Announcement extends ChatThread { content: json['content'], additionalData: (json['additionalData'] ?? {}) as Map, - startedAt: DateTime.parse(json['startedAt']), - endedAt: DateTime.parse(json['endedAt']), + startedAt: DateTime.tryParse(json['startedAt'] ?? '') ?? DateTime.now(), + endedAt: DateTime.tryParse(json['endedAt'] ?? '') ?? + DateTime.now().add(const Duration(days: 365)), imageURL: json['imageURL'], notificationType: json['notificationType'], deliveryTimeOfDay: json['deliveryTimeOfDay'], From aed7453f49d23c2add1c42ab12c8f656ce57681e Mon Sep 17 00:00:00 2001 From: phuoc Date: Wed, 6 Nov 2024 16:24:03 +0700 Subject: [PATCH 04/20] add localTime for metric Signed-off-by: phuoc --- lib/screen/dailies_work/dailies_work_page.dart | 5 ++--- lib/service/user_interactivity_service.dart | 0 2 files changed, 2 insertions(+), 3 deletions(-) create mode 100644 lib/service/user_interactivity_service.dart diff --git a/lib/screen/dailies_work/dailies_work_page.dart b/lib/screen/dailies_work/dailies_work_page.dart index fa7c202ff..f0dfbbed3 100644 --- a/lib/screen/dailies_work/dailies_work_page.dart +++ b/lib/screen/dailies_work/dailies_work_page.dart @@ -67,7 +67,7 @@ class DailyWorkPageState extends State bool _trackingInterest = false; Timer? _trackingInterestTimer; static const _scrollLikingThreshold = 100.0; - static const _stayDurationLikingThreshold = Duration(seconds: 5); + static const _stayDurationLikingThreshold = Duration(seconds: 10); @override void initState() { @@ -136,9 +136,8 @@ class DailyWorkPageState extends State } final data = { MetricParameter.tokenId: _currentDailyToken!.tokenID, + MetricParameter.localTime: DateTime.now().toIso8601String(), }; - unawaited(injector() - .addEvent(MetricEventName.playlistView, data: data)); unawaited(injector .get() .addEvent(MetricEventName.dailyLiked, data: data)); diff --git a/lib/service/user_interactivity_service.dart b/lib/service/user_interactivity_service.dart new file mode 100644 index 000000000..e69de29bb From 5b83f4921bd05e84e92b54fcb3a639bf4c8c2828 Mon Sep 17 00:00:00 2001 From: phuoc Date: Wed, 6 Nov 2024 16:53:18 +0700 Subject: [PATCH 05/20] enable notification prompt Signed-off-by: phuoc --- assets | 2 +- lib/common/injector.dart | 4 + .../dailies_work/dailies_work_page.dart | 10 +-- lib/service/configuration_service.dart | 13 +++ lib/service/user_interactivity_service.dart | 81 +++++++++++++++++++ lib/util/log.dart | 1 + lib/util/metric_helper.dart | 6 +- lib/util/ui_helper.dart | 27 +++++++ 8 files changed, 135 insertions(+), 9 deletions(-) diff --git a/assets b/assets index 8981babdc..cedaf2a47 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 8981babdce1dd1af67c985bc4951d14ad7fa27c1 +Subproject commit cedaf2a477689e0332025040ea552b29b6976806 diff --git a/lib/common/injector.dart b/lib/common/injector.dart index 7a9e2c676..aad5a617f 100644 --- a/lib/common/injector.dart +++ b/lib/common/injector.dart @@ -78,6 +78,7 @@ import 'package:autonomy_flutter/service/remote_config_service.dart'; import 'package:autonomy_flutter/service/settings_data_service.dart'; import 'package:autonomy_flutter/service/tezos_beacon_service.dart'; import 'package:autonomy_flutter/service/tezos_service.dart'; +import 'package:autonomy_flutter/service/user_interactivity_service.dart'; import 'package:autonomy_flutter/service/versions_service.dart'; import 'package:autonomy_flutter/service/wc2_service.dart'; import 'package:autonomy_flutter/util/au_file_service.dart'; @@ -222,6 +223,9 @@ Future setupInjector() async { injector.registerLazySingleton( () => UserApi(dio, baseUrl: Environment.autonomyAuthURL)); + injector.registerLazySingleton( + () => UserInteractivityServiceImpl(injector(), injector())); + final tzktUrl = Environment.appTestnetConfig ? Environment.tzktTestnetURL : Environment.tzktMainnetURL; diff --git a/lib/screen/dailies_work/dailies_work_page.dart b/lib/screen/dailies_work/dailies_work_page.dart index f0dfbbed3..67a6e0846 100644 --- a/lib/screen/dailies_work/dailies_work_page.dart +++ b/lib/screen/dailies_work/dailies_work_page.dart @@ -18,6 +18,7 @@ import 'package:autonomy_flutter/screen/exhibition_details/exhibition_detail_pag import 'package:autonomy_flutter/service/metric_client_service.dart'; import 'package:autonomy_flutter/service/navigation_service.dart'; import 'package:autonomy_flutter/service/remote_config_service.dart'; +import 'package:autonomy_flutter/service/user_interactivity_service.dart'; import 'package:autonomy_flutter/util/asset_token_ext.dart'; import 'package:autonomy_flutter/util/log.dart'; import 'package:autonomy_flutter/util/metric_helper.dart'; @@ -134,13 +135,8 @@ class DailyWorkPageState extends State if (_currentDailyToken == null) { return; } - final data = { - MetricParameter.tokenId: _currentDailyToken!.tokenID, - MetricParameter.localTime: DateTime.now().toIso8601String(), - }; - unawaited(injector - .get() - .addEvent(MetricEventName.dailyLiked, data: data)); + unawaited(injector() + .likeDailyWork(_currentDailyToken!)); } Future scheduleNextDailyWork(BuildContext context) async { diff --git a/lib/service/configuration_service.dart b/lib/service/configuration_service.dart index c84cdf460..43512d94d 100644 --- a/lib/service/configuration_service.dart +++ b/lib/service/configuration_service.dart @@ -24,6 +24,10 @@ import 'package:uuid/uuid.dart'; //ignore_for_file: constant_identifier_names abstract class ConfigurationService { + int getDailyLikedCount(); + + Future setDailyLikedCount(int count); + String? getAnonymousDeviceId(); Future createAnonymousDeviceId(); @@ -214,6 +218,7 @@ abstract class ConfigurationService { } class ConfigurationServiceImpl implements ConfigurationService { + static const String keyDailyLikedCount = 'daily_liked_count'; static const String keyAnonymousDeviceId = 'anonymous_device_id'; static const String keyAnonymousIssueIds = 'anonymous_issue_ids'; static const String keyDidMigrateToAccountSetting = @@ -963,6 +968,14 @@ class ConfigurationServiceImpl implements ConfigurationService { @override List getAnonymousIssueIds() => _preferences.getStringList(keyAnonymousIssueIds) ?? []; + + @override + int getDailyLikedCount() => _preferences.getInt(keyDailyLikedCount) ?? 0; + + @override + Future setDailyLikedCount(int count) async { + await _preferences.setInt(keyDailyLikedCount, count); + } } enum ConflictAction { diff --git a/lib/service/user_interactivity_service.dart b/lib/service/user_interactivity_service.dart index e69de29bb..1eb349e88 100644 --- a/lib/service/user_interactivity_service.dart +++ b/lib/service/user_interactivity_service.dart @@ -0,0 +1,81 @@ +import 'dart:async'; +import 'dart:math'; + +import 'package:autonomy_flutter/model/dailies.dart'; +import 'package:autonomy_flutter/service/configuration_service.dart'; +import 'package:autonomy_flutter/service/metric_client_service.dart'; +import 'package:autonomy_flutter/util/metric_helper.dart'; +import 'package:autonomy_flutter/util/ui_helper.dart'; +import 'package:easy_localization/easy_localization.dart'; + +abstract class UserInteractivityService { + Future likeDailyWork(DailyToken dailyToken); +} + +class UserInteractivityServiceImpl implements UserInteractivityService { + final ConfigurationService _configurationService; + final MetricClientService _metricClientService; + + UserInteractivityServiceImpl( + this._configurationService, this._metricClientService); + + @override + Future likeDailyWork(DailyToken dailyToken) async { + final data = { + MetricParameter.tokenId: dailyToken.tokenID, + MetricParameter.localTime: DateTime.now().toIso8601String(), + }; + unawaited( + _metricClientService.addEvent(MetricEventName.dailyLiked, data: data)); + + final isNotificationEnabled = _configurationService.isNotificationEnabled(); + if (!isNotificationEnabled) { + final likedCount = _configurationService.getDailyLikedCount(); + if (likedCount >= 3) { + await _showEnableNotificationDialog(); + } else { + await _configurationService.setDailyLikedCount(likedCount + 1); + } + } + } + + Future _showEnableNotificationDialog() async { + final type = EnableNotificationPromptType.getRandomType(); + await UIHelper.showNotificationPrompt(type); + await _configurationService.setDailyLikedCount(0); + } +} + +enum EnableNotificationPromptType { + stayUpdate, + getUpdate, + neverMiss, + ; + + String get title { + switch (this) { + case EnableNotificationPromptType.stayUpdate: + return 'stay_updated_new_art'.tr(); + case EnableNotificationPromptType.getUpdate: + return 'get_daily_updated'.tr(); + case EnableNotificationPromptType.neverMiss: + return 'never_miss_tomorrow_art'.tr(); + } + } + + String get description { + switch (this) { + case EnableNotificationPromptType.stayUpdate: + return 'stay_updated_new_art_desc'.tr(); + case EnableNotificationPromptType.getUpdate: + return 'get_daily_updated_desc'.tr(); + case EnableNotificationPromptType.neverMiss: + return 'never_miss_tomorrow_art_desc'.tr(); + } + } + + static EnableNotificationPromptType getRandomType() { + const values = EnableNotificationPromptType.values; + return values[Random().nextInt(values.length)]; + } +} diff --git a/lib/util/log.dart b/lib/util/log.dart index e9e9157d0..ac0a6cd03 100644 --- a/lib/util/log.dart +++ b/lib/util/log.dart @@ -151,6 +151,7 @@ class FileLogger { if (logText.contains(LoggingInterceptor.errorLogPrefix)) { return logText; } + return logText; String filteredLog = logText; RegExp combinedRegex = RegExp('("message":".*?")|' diff --git a/lib/util/metric_helper.dart b/lib/util/metric_helper.dart index 8c91721f9..69d4a9a1b 100644 --- a/lib/util/metric_helper.dart +++ b/lib/util/metric_helper.dart @@ -25,7 +25,9 @@ enum MetricEventName { enum MetricParameter { tokenId, section, - exhibitionId; + exhibitionId, + localTime, + ; String get name { switch (this) { @@ -35,6 +37,8 @@ enum MetricParameter { return 'section'; case MetricParameter.exhibitionId: return 'exhibitionID'; + case MetricParameter.localTime: + return 'localTime'; } } } diff --git a/lib/util/ui_helper.dart b/lib/util/ui_helper.dart index 99f9422d4..baefb1836 100644 --- a/lib/util/ui_helper.dart +++ b/lib/util/ui_helper.dart @@ -20,6 +20,7 @@ import 'package:autonomy_flutter/screen/customer_support/support_thread_page.dar import 'package:autonomy_flutter/service/configuration_service.dart'; import 'package:autonomy_flutter/service/metric_client_service.dart'; import 'package:autonomy_flutter/service/navigation_service.dart'; +import 'package:autonomy_flutter/service/user_interactivity_service.dart'; import 'package:autonomy_flutter/util/au_icons.dart'; import 'package:autonomy_flutter/util/constants.dart'; import 'package:autonomy_flutter/util/distance_formater.dart'; @@ -1894,6 +1895,32 @@ class UIHelper { 'subscription_upgraded', ); } + + static Future showNotificationPrompt( + EnableNotificationPromptType type) async { + final context = injector().context; + if (!context.mounted) { + return null; + } + return await showDialog( + context, + type.title, + Column( + children: [ + Text(type.description, + style: Theme.of(context).textTheme.ppMori400White14), + const SizedBox(height: 20), + PrimaryButton( + onTap: () async { + Navigator.of(context).pop(true); + await Navigator.of(context).pushNamed(AppRouter.preferencesPage); + }, + text: 'enable'.tr(), + ), + ], + ), + ); + } } Widget loadingScreen(ThemeData theme, String text) => Scaffold( From 93e8c467b0200e79946898307a16902cb5435789 Mon Sep 17 00:00:00 2001 From: phuoc Date: Thu, 7 Nov 2024 10:36:09 +0700 Subject: [PATCH 06/20] fix comment, bug Signed-off-by: phuoc --- assets | 2 +- .../additional_data/additional_data.dart | 28 +++++----- lib/model/additional_data/cs_view_thread.dart | 2 +- .../daily_notification_data.dart | 33 ++++++------ .../jg_crystalline_work_generated.dart | 2 +- .../additional_data/view_collection.dart | 2 +- .../additional_data/view_new_message.dart | 2 +- lib/model/additional_data/view_postcard.dart | 2 +- lib/screen/app_router.dart | 2 +- lib/screen/home/home_navigation_page.dart | 6 ++- lib/service/user_interactivity_service.dart | 4 ++ lib/util/log.dart | 1 - lib/util/ui_helper.dart | 53 +++++++++++++++++-- 13 files changed, 96 insertions(+), 43 deletions(-) diff --git a/assets b/assets index cedaf2a47..23f3ad682 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit cedaf2a477689e0332025040ea552b29b6976806 +Subproject commit 23f3ad682320475922d7f1aabe993ff0f94329ee diff --git a/lib/model/additional_data/additional_data.dart b/lib/model/additional_data/additional_data.dart index b96101f20..e9bf41213 100644 --- a/lib/model/additional_data/additional_data.dart +++ b/lib/model/additional_data/additional_data.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + import 'package:autonomy_flutter/model/additional_data/cs_view_thread.dart'; import 'package:autonomy_flutter/model/additional_data/daily_notification_data.dart'; import 'package:autonomy_flutter/model/additional_data/jg_crystalline_work_generated.dart'; @@ -26,7 +28,7 @@ class AdditionalData { bool get isTappable => false; static AdditionalData fromJson(Map json, {String? type}) { - final announcementContentId = json['announcementContentID']; + final notificationContentId = json['notificationContentID']; try { final notificationType = NotificationType.fromString(type ?? json['notification_type']); @@ -34,7 +36,7 @@ class AdditionalData { final defaultAdditionalData = AdditionalData( notificationType: notificationType, - announcementContentId: announcementContentId, + announcementContentId: notificationContentId, linkText: linkText, ); @@ -49,7 +51,7 @@ class AdditionalData { return CsViewThread( issueId: issueId.toString(), notificationType: notificationType, - announcementContentId: announcementContentId, + announcementContentId: notificationContentId, linkText: linkText, ); case NotificationType.artworkCreated: @@ -57,7 +59,7 @@ class AdditionalData { case NotificationType.galleryNewNft: return view_collection_handler.ViewCollection( notificationType: notificationType, - announcementContentId: announcementContentId, + announcementContentId: notificationContentId, linkText: linkText, ); case NotificationType.newMessage: @@ -69,7 +71,7 @@ class AdditionalData { return ViewNewMessage( groupId: groupId, notificationType: notificationType, - announcementContentId: announcementContentId, + announcementContentId: notificationContentId, linkText: linkText, ); case NotificationType.newPostcardTrip: @@ -82,7 +84,7 @@ class AdditionalData { return ViewPostcard( indexID: indexID, notificationType: notificationType, - announcementContentId: announcementContentId, + announcementContentId: notificationContentId, linkText: linkText, ); case NotificationType.jgCrystallineWorkHasArrived: @@ -90,7 +92,7 @@ class AdditionalData { return ViewExhibitionData( exhibitionId: jgExhibitionId ?? '', notificationType: notificationType, - announcementContentId: announcementContentId, + announcementContentId: notificationContentId, linkText: linkText, ); case NotificationType.jgCrystallineWorkGenerated: @@ -102,7 +104,7 @@ class AdditionalData { return JgCrystallineWorkGenerated( tokenId: tokenId, notificationType: notificationType, - announcementContentId: announcementContentId, + announcementContentId: notificationContentId, linkText: linkText, ); case NotificationType.exhibitionViewingOpening: @@ -116,7 +118,7 @@ class AdditionalData { return ViewExhibitionData( exhibitionId: exhibitionId, notificationType: notificationType, - announcementContentId: announcementContentId, + announcementContentId: notificationContentId, linkText: linkText, ); case NotificationType.navigate: @@ -125,7 +127,7 @@ class AdditionalData { return NavigateAdditionalData( navigationRoute: navigationRoute, notificationType: notificationType, - announcementContentId: announcementContentId, + announcementContentId: notificationContentId, homeIndex: homeIndex, linkText: linkText, ); @@ -139,7 +141,7 @@ class AdditionalData { return DailyNotificationData( dailyNotificationType: dailyType, notificationType: notificationType, - announcementContentId: announcementContentId, + announcementContentId: notificationContentId, linkText: linkText, ); @@ -150,7 +152,7 @@ class AdditionalData { log.info('AdditionalData: error parsing additional data'); return AdditionalData( notificationType: NotificationType.general, - announcementContentId: announcementContentId); + announcementContentId: notificationContentId); } } @@ -158,5 +160,5 @@ class AdditionalData { log.info('AdditionalData: handle tap: $notificationType'); } - Future prepareAndDidSuccess() => Future.value(true); + FutureOr prepareAndDidSuccess() => Future.value(true); } diff --git a/lib/model/additional_data/cs_view_thread.dart b/lib/model/additional_data/cs_view_thread.dart index 513f6b23f..d016ce168 100644 --- a/lib/model/additional_data/cs_view_thread.dart +++ b/lib/model/additional_data/cs_view_thread.dart @@ -38,7 +38,7 @@ class CsViewThread extends AdditionalData { } @override - Future prepareAndDidSuccess() async { + bool prepareAndDidSuccess() { _customerSupportService.triggerReloadMessages.value += 1; unawaited(_customerSupportService.getChatThreads()); if (issueId == memoryValues.viewingSupportThreadIssueID) { diff --git a/lib/model/additional_data/daily_notification_data.dart b/lib/model/additional_data/daily_notification_data.dart index 045e9b731..c4cebdc5f 100644 --- a/lib/model/additional_data/daily_notification_data.dart +++ b/lib/model/additional_data/daily_notification_data.dart @@ -4,6 +4,7 @@ import 'package:autonomy_flutter/common/injector.dart'; import 'package:autonomy_flutter/model/additional_data/additional_data.dart'; import 'package:autonomy_flutter/model/dailies.dart'; import 'package:autonomy_flutter/screen/app_router.dart'; +import 'package:autonomy_flutter/screen/dailies_work/dailies_work_bloc.dart'; import 'package:autonomy_flutter/screen/exhibition_details/exhibition_detail_page.dart'; import 'package:autonomy_flutter/screen/feralfile_series/feralfile_series_page.dart'; import 'package:autonomy_flutter/service/feralfile_service.dart'; @@ -47,7 +48,7 @@ class DailyNotificationData extends AdditionalData { final _navigationService = injector(); final _feralFileService = injector(); - DailyToken? _dailyToken; + final _dailyWorkBloc = injector(); DailyNotificationData({ required this.dailyNotificationType, @@ -64,18 +65,17 @@ class DailyNotificationData extends AdditionalData { log.info('DailyNotificationData: handle tap'); bool isDailyTokenAvailable = await prepareAndDidSuccess(); if (!isDailyTokenAvailable) { - log.warning('Tapped at daily notification: dailyToken is null'); - unawaited(Sentry.captureMessage( - 'Tapped at daily notification: dailyToken is null')); + _logAndSendSentryForNullData('dailyToken'); return; } + final dailyToken = injector().state.currentDailyToken; switch (dailyNotificationType) { case DailyNotificationType.viewDaily: case DailyNotificationType.revisitDaily: await _navigationService.navigatePath(AppRouter.dailyWorkPage); dailyWorkKey.currentState?.trackInterest(); case DailyNotificationType.viewDailySeries: - final artwork = _dailyToken!.artwork; + final artwork = dailyToken!.artwork; if (artwork == null) { _logAndSendSentryForNullData('artwork'); return; @@ -92,7 +92,7 @@ class DailyNotificationData extends AdditionalData { ), ); case DailyNotificationType.viewDailyExhibition: - final artwork = _dailyToken!.artwork; + final artwork = dailyToken!.artwork; if (artwork == null) { _logAndSendSentryForNullData('artwork'); return; @@ -112,14 +112,13 @@ class DailyNotificationData extends AdditionalData { ExhibitionDetailPayload(exhibitions: [exhibition], index: 0), ); case DailyNotificationType.meetDailyArtist: - final artwork = _dailyToken!.artwork; - if (artwork == null) { - _logAndSendSentryForNullData('artwork'); + final artistID = _dailyWorkBloc.state.assetTokens.firstOrNull?.artistID; + + if (artistID == null) { + _logAndSendSentryForNullData('artistID'); return; } - final series = await _feralFileService.getSeries(artwork.seriesID); - final alumniID = series.artistAlumniAccountID; - await injector().openFeralFileArtistPage(alumniID); + await injector().openFeralFileArtistPage(artistID); case DailyNotificationType.displayDailyOnTV: await _navigationService.navigatePath(AppRouter.dailyWorkPage); await Future.delayed(const Duration(milliseconds: 300), () { @@ -137,11 +136,13 @@ class DailyNotificationData extends AdditionalData { @override Future prepareAndDidSuccess() async { - if (_dailyToken != null) { - return true; + DailyToken? dailyToken = _dailyWorkBloc.state.currentDailyToken; + if (dailyToken == null) { + log.warning('DailyNotificationData: dailyToken is null, retrying'); + await Future.delayed(const Duration(milliseconds: 1000)); + dailyToken = _dailyWorkBloc.state.currentDailyToken; } - _dailyToken = await _feralFileService.getCurrentDailiesToken(); - if (_dailyToken == null) { + if (dailyToken == null) { log.warning('DailyNotificationData: dailyToken is null'); unawaited( Sentry.captureMessage('DailyNotificationData: dailyToken is null')); diff --git a/lib/model/additional_data/jg_crystalline_work_generated.dart b/lib/model/additional_data/jg_crystalline_work_generated.dart index 47bb0f033..e02693712 100644 --- a/lib/model/additional_data/jg_crystalline_work_generated.dart +++ b/lib/model/additional_data/jg_crystalline_work_generated.dart @@ -30,7 +30,7 @@ class JgCrystallineWorkGenerated extends AdditionalData { } @override - Future prepareAndDidSuccess() async { + bool prepareAndDidSuccess() { unawaited(injector().refreshTokens()); return true; } diff --git a/lib/model/additional_data/view_collection.dart b/lib/model/additional_data/view_collection.dart index c748e3151..acf645cf3 100644 --- a/lib/model/additional_data/view_collection.dart +++ b/lib/model/additional_data/view_collection.dart @@ -30,7 +30,7 @@ class ViewCollection extends AdditionalData { } @override - Future prepareAndDidSuccess() async { + bool prepareAndDidSuccess() { unawaited(injector().refreshTokens()); return true; } diff --git a/lib/model/additional_data/view_new_message.dart b/lib/model/additional_data/view_new_message.dart index 1c806f5ae..8359620f7 100644 --- a/lib/model/additional_data/view_new_message.dart +++ b/lib/model/additional_data/view_new_message.dart @@ -67,7 +67,7 @@ class ViewNewMessage extends AdditionalData { } @override - Future prepareAndDidSuccess() async { + bool prepareAndDidSuccess() { if (!_remoteConfigService.getBool(ConfigGroup.viewDetail, ConfigKey.chat)) { return false; } diff --git a/lib/model/additional_data/view_postcard.dart b/lib/model/additional_data/view_postcard.dart index 189faa9db..aa89fa86b 100644 --- a/lib/model/additional_data/view_postcard.dart +++ b/lib/model/additional_data/view_postcard.dart @@ -50,7 +50,7 @@ class ViewPostcard extends AdditionalData { } @override - Future prepareAndDidSuccess() async { + bool prepareAndDidSuccess() { unawaited(injector().refreshTokens()); return true; } diff --git a/lib/screen/app_router.dart b/lib/screen/app_router.dart index 19cc15151..c65f3d2a5 100644 --- a/lib/screen/app_router.dart +++ b/lib/screen/app_router.dart @@ -1079,7 +1079,7 @@ class AppRouter { settings: settings, builder: (context) => MultiBlocProvider(providers: [ BlocProvider( - create: (_) => DailyWorkBloc(injector(), injector()), + create: (_) => injector(), ), BlocProvider.value(value: canvasDeviceBloc), ], child: const DailyWorkPage()), diff --git a/lib/screen/home/home_navigation_page.dart b/lib/screen/home/home_navigation_page.dart index a9efeaba6..574fa0518 100644 --- a/lib/screen/home/home_navigation_page.dart +++ b/lib/screen/home/home_navigation_page.dart @@ -36,6 +36,7 @@ import 'package:autonomy_flutter/service/navigation_service.dart'; import 'package:autonomy_flutter/service/notification_service.dart' as nc; import 'package:autonomy_flutter/service/remote_config_service.dart'; import 'package:autonomy_flutter/service/tezos_beacon_service.dart'; +import 'package:autonomy_flutter/service/user_interactivity_service.dart'; import 'package:autonomy_flutter/service/versions_service.dart'; import 'package:autonomy_flutter/service/wc2_service.dart'; import 'package:autonomy_flutter/shared.dart'; @@ -253,7 +254,7 @@ class HomeNavigationPageState extends State MultiBlocProvider( providers: [ BlocProvider( - create: (_) => DailyWorkBloc(injector(), injector()), + create: (_) => injector(), ), BlocProvider.value(value: injector()), ], @@ -539,7 +540,8 @@ class HomeNavigationPageState extends State } void _triggerShowAnnouncement() { - unawaited(Future.delayed(const Duration(milliseconds: 1000), () { + unawaited(Future.delayed(const Duration(milliseconds: 2000), () { + UIHelper.showNotificationPrompt(EnableNotificationPromptType.getUpdate); _announcementService.fetchAnnouncements().then( (_) async { await _announcementService.showOldestAnnouncement(); diff --git a/lib/service/user_interactivity_service.dart b/lib/service/user_interactivity_service.dart index 1eb349e88..babdaf30d 100644 --- a/lib/service/user_interactivity_service.dart +++ b/lib/service/user_interactivity_service.dart @@ -28,6 +28,10 @@ class UserInteractivityServiceImpl implements UserInteractivityService { unawaited( _metricClientService.addEvent(MetricEventName.dailyLiked, data: data)); + await _likeDailyWorkActionResponse(); + } + + Future _likeDailyWorkActionResponse() async { final isNotificationEnabled = _configurationService.isNotificationEnabled(); if (!isNotificationEnabled) { final likedCount = _configurationService.getDailyLikedCount(); diff --git a/lib/util/log.dart b/lib/util/log.dart index ac0a6cd03..e9e9157d0 100644 --- a/lib/util/log.dart +++ b/lib/util/log.dart @@ -151,7 +151,6 @@ class FileLogger { if (logText.contains(LoggingInterceptor.errorLogPrefix)) { return logText; } - return logText; String filteredLog = logText; RegExp combinedRegex = RegExp('("message":".*?")|' diff --git a/lib/util/ui_helper.dart b/lib/util/ui_helper.dart index baefb1836..1a487b591 100644 --- a/lib/util/ui_helper.dart +++ b/lib/util/ui_helper.dart @@ -1245,6 +1245,52 @@ class UIHelper { )); } + static Future showCenterDialog(BuildContext context, + {required Widget content}) async { + UIHelper.hideInfoDialog(context); + final theme = Theme.of(context); + return await showCupertinoModalPopup( + context: context, + builder: (context) => Scaffold( + backgroundColor: Colors.transparent, + body: Stack( + children: [ + GestureDetector( + child: Container( + color: AppColor.primaryBlack.withOpacity(0.5), + ), + onTap: () { + Navigator.pop(context); + }, + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 20), + child: Center( + child: Container( + decoration: BoxDecoration( + color: theme.auGreyBackground, + borderRadius: BorderRadius.circular(5), + ), + constraints: const BoxConstraints( + maxHeight: 600, + ), + padding: const EdgeInsets.symmetric( + vertical: 20, horizontal: 15), + child: Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + content, + ], + ), + ), + ), + ), + ], + ), + )); + } + static Future showCenterMenu(BuildContext context, {required List options}) async { final theme = Theme.of(context); @@ -1902,10 +1948,9 @@ class UIHelper { if (!context.mounted) { return null; } - return await showDialog( + return await showCenterDialog( context, - type.title, - Column( + content: Column( children: [ Text(type.description, style: Theme.of(context).textTheme.ppMori400White14), @@ -1915,7 +1960,7 @@ class UIHelper { Navigator.of(context).pop(true); await Navigator.of(context).pushNamed(AppRouter.preferencesPage); }, - text: 'enable'.tr(), + text: 'go_to_notification'.tr(), ), ], ), From 0cb510e1c9676fa2aae88a7bbfcd82cc35bbc6dc Mon Sep 17 00:00:00 2001 From: phuoc Date: Thu, 7 Nov 2024 10:46:09 +0700 Subject: [PATCH 07/20] fix ui Signed-off-by: phuoc --- lib/screen/home/home_navigation_page.dart | 2 -- lib/util/ui_helper.dart | 20 +++++++++++++------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/lib/screen/home/home_navigation_page.dart b/lib/screen/home/home_navigation_page.dart index 574fa0518..ae14e5a82 100644 --- a/lib/screen/home/home_navigation_page.dart +++ b/lib/screen/home/home_navigation_page.dart @@ -36,7 +36,6 @@ import 'package:autonomy_flutter/service/navigation_service.dart'; import 'package:autonomy_flutter/service/notification_service.dart' as nc; import 'package:autonomy_flutter/service/remote_config_service.dart'; import 'package:autonomy_flutter/service/tezos_beacon_service.dart'; -import 'package:autonomy_flutter/service/user_interactivity_service.dart'; import 'package:autonomy_flutter/service/versions_service.dart'; import 'package:autonomy_flutter/service/wc2_service.dart'; import 'package:autonomy_flutter/shared.dart'; @@ -541,7 +540,6 @@ class HomeNavigationPageState extends State void _triggerShowAnnouncement() { unawaited(Future.delayed(const Duration(milliseconds: 2000), () { - UIHelper.showNotificationPrompt(EnableNotificationPromptType.getUpdate); _announcementService.fetchAnnouncements().then( (_) async { await _announcementService.showOldestAnnouncement(); diff --git a/lib/util/ui_helper.dart b/lib/util/ui_helper.dart index 1a487b591..9be4b5435 100644 --- a/lib/util/ui_helper.dart +++ b/lib/util/ui_helper.dart @@ -1276,12 +1276,14 @@ class UIHelper { ), padding: const EdgeInsets.symmetric( vertical: 20, horizontal: 15), - child: Column( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - content, - ], + child: SingleChildScrollView( + child: Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + content, + ], + ), ), ), ), @@ -1951,7 +1953,11 @@ class UIHelper { return await showCenterDialog( context, content: Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ + Text(type.title, + style: Theme.of(context).textTheme.ppMori700White24), + const SizedBox(height: 20), Text(type.description, style: Theme.of(context).textTheme.ppMori400White14), const SizedBox(height: 20), @@ -1960,7 +1966,7 @@ class UIHelper { Navigator.of(context).pop(true); await Navigator.of(context).pushNamed(AppRouter.preferencesPage); }, - text: 'go_to_notification'.tr(), + text: 'go_to_notifications'.tr(), ), ], ), From 2c352b18894d9e7a7ffd3a5a30ed2282e0a0ed3e Mon Sep 17 00:00:00 2001 From: phuoc Date: Thu, 7 Nov 2024 12:58:33 +0700 Subject: [PATCH 08/20] fix mark ad read announcement Signed-off-by: phuoc --- lib/model/additional_data/additional_data.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/model/additional_data/additional_data.dart b/lib/model/additional_data/additional_data.dart index e9bf41213..b6942797f 100644 --- a/lib/model/additional_data/additional_data.dart +++ b/lib/model/additional_data/additional_data.dart @@ -28,7 +28,7 @@ class AdditionalData { bool get isTappable => false; static AdditionalData fromJson(Map json, {String? type}) { - final notificationContentId = json['notificationContentID']; + final notificationContentId = json['notification_content_id']; try { final notificationType = NotificationType.fromString(type ?? json['notification_type']); From 91f172cd0410739da49aa7c3f73bc9494df51618 Mon Sep 17 00:00:00 2001 From: phuoc Date: Thu, 7 Nov 2024 13:21:35 +0700 Subject: [PATCH 09/20] apply in-app enable Signed-off-by: phuoc --- .../additional_data/additional_data.dart | 2 +- lib/model/announcement/announcement.dart | 3 +++ .../announcement/announcement_adapter.dart | 4 +++- .../announcement/announcement_local.dart | 20 ++++++++++++++++ .../announcement/announcement_service.dart | 24 +++++++------------ lib/view/cast_button.dart | 2 +- 6 files changed, 36 insertions(+), 19 deletions(-) diff --git a/lib/model/additional_data/additional_data.dart b/lib/model/additional_data/additional_data.dart index b6942797f..aa17fb65e 100644 --- a/lib/model/additional_data/additional_data.dart +++ b/lib/model/additional_data/additional_data.dart @@ -133,7 +133,7 @@ class AdditionalData { ); case NotificationType.daily: final subType = json['notification_sub_type']; - final dailyType = DailyNotificationType.fromString(subType?? ''); + final dailyType = DailyNotificationType.fromString(subType ?? ''); if (dailyType == null) { log.warning('AdditionalData: dailyType is null'); return defaultAdditionalData; diff --git a/lib/model/announcement/announcement.dart b/lib/model/announcement/announcement.dart index 3486fbc85..44f2396b7 100644 --- a/lib/model/announcement/announcement.dart +++ b/lib/model/announcement/announcement.dart @@ -10,6 +10,7 @@ class Announcement extends ChatThread { final String? imageURL; final String? notificationType; final String? deliveryTimeOfDay; + final bool inAppEnabled; Announcement({ required this.announcementContentId, @@ -17,6 +18,7 @@ class Announcement extends ChatThread { required this.additionalData, required this.startedAt, required this.endedAt, + required this.inAppEnabled, this.imageURL, this.notificationType, this.deliveryTimeOfDay, @@ -33,6 +35,7 @@ class Announcement extends ChatThread { imageURL: json['imageURL'], notificationType: json['notificationType'], deliveryTimeOfDay: json['deliveryTimeOfDay'], + inAppEnabled: json['inAppEnabled'], ); bool get isExpired => DateTime.now().isAfter(endedAt); diff --git a/lib/model/announcement/announcement_adapter.dart b/lib/model/announcement/announcement_adapter.dart index 0d2d90208..7ede5ac95 100644 --- a/lib/model/announcement/announcement_adapter.dart +++ b/lib/model/announcement/announcement_adapter.dart @@ -6,7 +6,8 @@ class AnnouncementLocalAdapter extends TypeAdapter { final int typeId = 10; @override - AnnouncementLocal read(BinaryReader reader) => AnnouncementLocal( + AnnouncementLocal read(BinaryReader reader) => + AnnouncementLocal.addFromAdditionalData( announcementContentId: reader.readString(), content: reader.readString(), additionalData: reader.readMap().cast(), @@ -17,6 +18,7 @@ class AnnouncementLocalAdapter extends TypeAdapter { @override void write(BinaryWriter writer, AnnouncementLocal obj) { + obj.additionalData['~inAppEnabled'] = obj.inAppEnabled; writer ..writeString(obj.announcementContentId) ..writeString(obj.content) diff --git a/lib/model/announcement/announcement_local.dart b/lib/model/announcement/announcement_local.dart index 4f3437ae4..50a802515 100644 --- a/lib/model/announcement/announcement_local.dart +++ b/lib/model/announcement/announcement_local.dart @@ -9,9 +9,28 @@ class AnnouncementLocal extends Announcement { required super.additionalData, required super.startedAt, required super.endedAt, + required super.inAppEnabled, this.read = false, }); + static AnnouncementLocal addFromAdditionalData({ + required String announcementContentId, + required String content, + required Map additionalData, + required DateTime startedAt, + required DateTime endedAt, + required bool read, + }) => + AnnouncementLocal( + announcementContentId: announcementContentId, + content: content, + additionalData: additionalData, + startedAt: startedAt, + endedAt: endedAt, + inAppEnabled: additionalData['~inAppEnabled'] ?? true, + read: read, + ); + AnnouncementLocal markAsRead() { read = true; return this; @@ -24,6 +43,7 @@ class AnnouncementLocal extends Announcement { additionalData: announcement.additionalData, startedAt: announcement.startedAt, endedAt: announcement.endedAt, + inAppEnabled: announcement.inAppEnabled, ); @override diff --git a/lib/service/announcement/announcement_service.dart b/lib/service/announcement/announcement_service.dart index 7ea2529c5..cb639ced0 100644 --- a/lib/service/announcement/announcement_service.dart +++ b/lib/service/announcement/announcement_service.dart @@ -22,12 +22,8 @@ abstract class AnnouncementService { Future markAsRead(String? announcementContentId); - List getUnreadAnnouncements(); - AnnouncementLocal? getAnnouncement(String? announcementContentId); - AnnouncementLocal? getOldestUnreadAnnouncement(); - Future showOldestAnnouncement({bool shouldRepeat = true}); void linkAnnouncementToIssue(String announcementContentId, String issueId); @@ -42,9 +38,6 @@ class AnnouncementServiceImpl implements AnnouncementService { final AnnouncementStore _announcementStore; final ConfigurationService _configurationService; - // Map - final Map _announcementToIssueMap = {}; - AnnouncementServiceImpl( this._iapApi, this._announcementStore, @@ -118,13 +111,13 @@ class AnnouncementServiceImpl implements AnnouncementService { await _saveAnnouncement(announcement.markAsRead()); } - @override - List getUnreadAnnouncements() { - _queue.removeWhere((element) => element.read); + List _getAnnouncementsToShow() { + _queue.removeWhere((element) => element.read || !element.inAppEnabled); if (_queue.isEmpty) { final allAnnouncements = getLocalAnnouncements(); - _queue - .addAll(allAnnouncements.where((element) => !element.read).toList()); + _queue.addAll(allAnnouncements + .where((element) => !element.read && element.inAppEnabled) + .toList()); } _updateBadger(_queue.length); return _queue; @@ -138,15 +131,14 @@ class AnnouncementServiceImpl implements AnnouncementService { return _announcementStore.get(announcementContentId); } - @override - AnnouncementLocal? getOldestUnreadAnnouncement() { - final announcements = getUnreadAnnouncements(); + AnnouncementLocal? _getOldestUnreadAnnouncement() { + final announcements = _getAnnouncementsToShow(); return announcements.firstOrNull; } @override Future showOldestAnnouncement({bool shouldRepeat = true}) async { - final announcement = getOldestUnreadAnnouncement(); + final announcement = _getOldestUnreadAnnouncement(); if (announcement != null) { final context = injector().context; final data = announcement.additionalData; diff --git a/lib/view/cast_button.dart b/lib/view/cast_button.dart index f6d4b5a7a..4f01475f2 100644 --- a/lib/view/cast_button.dart +++ b/lib/view/cast_button.dart @@ -127,7 +127,7 @@ class FFCastButtonState extends State { ); } - Future onTap(BuildContext context ,bool isSubscribed) async { + Future onTap(BuildContext context, bool isSubscribed) async { if (!widget.shouldCheckSubscription || isSubscribed) { await injector().showStreamAction( widget.displayKey, From 74594009f9fa1d5b960843ea98f6d7506385cc55 Mon Sep 17 00:00:00 2001 From: phuoc Date: Thu, 7 Nov 2024 13:28:20 +0700 Subject: [PATCH 10/20] support notification type Signed-off-by: phuoc --- lib/model/announcement/announcement.dart | 2 +- lib/model/announcement/announcement_adapter.dart | 1 + lib/model/announcement/announcement_local.dart | 3 +++ lib/util/ui_helper.dart | 3 +-- 4 files changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/model/announcement/announcement.dart b/lib/model/announcement/announcement.dart index 44f2396b7..4b0756aa5 100644 --- a/lib/model/announcement/announcement.dart +++ b/lib/model/announcement/announcement.dart @@ -19,8 +19,8 @@ class Announcement extends ChatThread { required this.startedAt, required this.endedAt, required this.inAppEnabled, + required this.notificationType, this.imageURL, - this.notificationType, this.deliveryTimeOfDay, }); diff --git a/lib/model/announcement/announcement_adapter.dart b/lib/model/announcement/announcement_adapter.dart index 7ede5ac95..48c038c07 100644 --- a/lib/model/announcement/announcement_adapter.dart +++ b/lib/model/announcement/announcement_adapter.dart @@ -19,6 +19,7 @@ class AnnouncementLocalAdapter extends TypeAdapter { @override void write(BinaryWriter writer, AnnouncementLocal obj) { obj.additionalData['~inAppEnabled'] = obj.inAppEnabled; + obj.additionalData['~notificationType'] = obj.notificationType; writer ..writeString(obj.announcementContentId) ..writeString(obj.content) diff --git a/lib/model/announcement/announcement_local.dart b/lib/model/announcement/announcement_local.dart index 50a802515..f6bf4d55d 100644 --- a/lib/model/announcement/announcement_local.dart +++ b/lib/model/announcement/announcement_local.dart @@ -10,6 +10,7 @@ class AnnouncementLocal extends Announcement { required super.startedAt, required super.endedAt, required super.inAppEnabled, + required super.notificationType, this.read = false, }); @@ -28,6 +29,7 @@ class AnnouncementLocal extends Announcement { startedAt: startedAt, endedAt: endedAt, inAppEnabled: additionalData['~inAppEnabled'] ?? true, + notificationType: additionalData['~notificationType'], read: read, ); @@ -44,6 +46,7 @@ class AnnouncementLocal extends Announcement { startedAt: announcement.startedAt, endedAt: announcement.endedAt, inAppEnabled: announcement.inAppEnabled, + notificationType: announcement.notificationType, ); @override diff --git a/lib/util/ui_helper.dart b/lib/util/ui_helper.dart index 9be4b5435..4c60ba9ef 100644 --- a/lib/util/ui_helper.dart +++ b/lib/util/ui_helper.dart @@ -1955,8 +1955,7 @@ class UIHelper { content: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text(type.title, - style: Theme.of(context).textTheme.ppMori700White24), + Text(type.title, style: Theme.of(context).textTheme.ppMori700White24), const SizedBox(height: 20), Text(type.description, style: Theme.of(context).textTheme.ppMori400White14), From ac6ed736d027232a502f8eefbe4b838d8469ce94 Mon Sep 17 00:00:00 2001 From: phuoc Date: Thu, 7 Nov 2024 15:59:13 +0700 Subject: [PATCH 11/20] mark as read new issue announcement Signed-off-by: phuoc --- lib/screen/customer_support/support_thread_page.dart | 10 ++++++++++ lib/screen/home/home_navigation_page.dart | 1 - 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/lib/screen/customer_support/support_thread_page.dart b/lib/screen/customer_support/support_thread_page.dart index 31bec184c..a8475f468 100644 --- a/lib/screen/customer_support/support_thread_page.dart +++ b/lib/screen/customer_support/support_thread_page.dart @@ -261,6 +261,16 @@ class _SupportThreadPageState extends State { if (_issueID != null && !_issueID!.startsWith('TEMP')) { unawaited(_loadIssueDetails()); } + _markAnnouncementAsRead(); + } + + void _markAnnouncementAsRead() { + if (widget.payload is NewIssueFromAnnouncementPayload) { + final announcement = + (widget.payload as NewIssueFromAnnouncementPayload).announcement; + unawaited(injector() + .markAsRead(announcement.announcementContentId)); + } } Future _getUserId(String issueId) async { diff --git a/lib/screen/home/home_navigation_page.dart b/lib/screen/home/home_navigation_page.dart index ae14e5a82..e5d37bb0f 100644 --- a/lib/screen/home/home_navigation_page.dart +++ b/lib/screen/home/home_navigation_page.dart @@ -299,7 +299,6 @@ class HomeNavigationPageState extends State /// should complete event after getting all data needed /// and before calling async function Future.delayed(const Duration(milliseconds: 500), () async { - await injector().fetchAnnouncements(); if (!mounted) { return; } From 008c2912608203b9098d56ffdce09d812a84ae5c Mon Sep 17 00:00:00 2001 From: phuoc Date: Thu, 7 Nov 2024 17:36:45 +0700 Subject: [PATCH 12/20] delete notification Signed-off-by: phuoc --- .../settings/preferences/preferences_view.dart | 14 -------------- lib/service/user_interactivity_service.dart | 3 ++- 2 files changed, 2 insertions(+), 15 deletions(-) diff --git a/lib/screen/settings/preferences/preferences_view.dart b/lib/screen/settings/preferences/preferences_view.dart index 29c9003d9..020ec2f91 100644 --- a/lib/screen/settings/preferences/preferences_view.dart +++ b/lib/screen/settings/preferences/preferences_view.dart @@ -54,20 +54,6 @@ class PreferenceView extends StatelessWidget { ), ), addDivider(), - Padding( - padding: padding, - child: _preferenceItem( - context, - 'notifications'.tr(), - 'receive_notification'.tr(), - state.isNotificationEnabled, (value) { - final newState = state.copyWith(isNotificationEnabled: value); - context - .read() - .add(PreferenceUpdateEvent(newState)); - }), - ), - addDivider(), Padding( padding: padding, child: _preferenceItemWithBuilder( diff --git a/lib/service/user_interactivity_service.dart b/lib/service/user_interactivity_service.dart index babdaf30d..b9afccbac 100644 --- a/lib/service/user_interactivity_service.dart +++ b/lib/service/user_interactivity_service.dart @@ -32,7 +32,8 @@ class UserInteractivityServiceImpl implements UserInteractivityService { } Future _likeDailyWorkActionResponse() async { - final isNotificationEnabled = _configurationService.isNotificationEnabled(); + /// temporary implementation + const isNotificationEnabled = true; if (!isNotificationEnabled) { final likedCount = _configurationService.getDailyLikedCount(); if (likedCount >= 3) { From 302d06a09bd0927806fb12491bd9dd7602af18f0 Mon Sep 17 00:00:00 2001 From: phuoc Date: Thu, 7 Nov 2024 17:41:17 +0700 Subject: [PATCH 13/20] fix link color Signed-off-by: phuoc --- lib/util/inapp_notifications.dart | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/util/inapp_notifications.dart b/lib/util/inapp_notifications.dart index ee3b54249..3d444d1f4 100644 --- a/lib/util/inapp_notifications.dart +++ b/lib/util/inapp_notifications.dart @@ -51,7 +51,10 @@ Widget _inAppNotificationToast(BuildContext context, String body, String key, if (notificationOpenedHandler != null) TextSpan( text: ' ${'tap_to_view'.tr()}', - style: Theme.of(context).textTheme.ppMori400FFYellow14, + style: Theme.of(context) + .textTheme + .ppMori400FFYellow14 + .copyWith(color: AppColor.feralFileLightBlue), ) ], ); From 89006a6443e1e9d7d16dcde28ea503c296492cde Mon Sep 17 00:00:00 2001 From: phuoc Date: Thu, 7 Nov 2024 17:55:46 +0700 Subject: [PATCH 14/20] auto register Signed-off-by: phuoc --- lib/screen/onboarding_page.dart | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/lib/screen/onboarding_page.dart b/lib/screen/onboarding_page.dart index c43a8bdcc..ba793429b 100644 --- a/lib/screen/onboarding_page.dart +++ b/lib/screen/onboarding_page.dart @@ -126,11 +126,7 @@ class _OnboardingPageState extends State Future _registerPushNotifications() async { try { - final isNotificationEnabled = - injector().isNotificationEnabled(); - if (isNotificationEnabled) { - await registerPushNotifications(); - } + await registerPushNotifications(); } catch (e, s) { log.info('registerPushNotifications error: $e'); unawaited(Sentry.captureException('registerPushNotifications error: $e', From 695fb336f257b53139ced0cbb0feb9aa7dd588e7 Mon Sep 17 00:00:00 2001 From: phuoc Date: Fri, 8 Nov 2024 09:10:02 +0700 Subject: [PATCH 15/20] remove awesome notification pub Signed-off-by: phuoc --- lib/common/injector.dart | 4 - lib/screen/home/home_navigation_page.dart | 8 -- lib/screen/onboarding_page.dart | 9 -- lib/service/notification_service.dart | 168 ---------------------- pubspec.lock | 8 -- pubspec.yaml | 1 - 6 files changed, 198 deletions(-) delete mode 100644 lib/service/notification_service.dart diff --git a/lib/common/injector.dart b/lib/common/injector.dart index aad5a617f..c77b23752 100644 --- a/lib/common/injector.dart +++ b/lib/common/injector.dart @@ -69,7 +69,6 @@ import 'package:autonomy_flutter/service/metric_client_service.dart'; import 'package:autonomy_flutter/service/navigation_service.dart'; import 'package:autonomy_flutter/service/network_issue_manager.dart'; import 'package:autonomy_flutter/service/network_service.dart'; -import 'package:autonomy_flutter/service/notification_service.dart'; import 'package:autonomy_flutter/service/passkey_service.dart'; import 'package:autonomy_flutter/service/pending_token_service.dart'; import 'package:autonomy_flutter/service/playlist_service.dart'; @@ -375,9 +374,6 @@ Future setupInjector() async { injector(), )); - injector - .registerLazySingleton(() => NotificationService()); - injector.registerLazySingleton(() => FeralFileServiceImpl( injector(), injector(), diff --git a/lib/screen/home/home_navigation_page.dart b/lib/screen/home/home_navigation_page.dart index e5d37bb0f..31301182c 100644 --- a/lib/screen/home/home_navigation_page.dart +++ b/lib/screen/home/home_navigation_page.dart @@ -33,7 +33,6 @@ import 'package:autonomy_flutter/service/customer_support_service.dart'; import 'package:autonomy_flutter/service/deeplink_service.dart'; import 'package:autonomy_flutter/service/locale_service.dart'; import 'package:autonomy_flutter/service/navigation_service.dart'; -import 'package:autonomy_flutter/service/notification_service.dart' as nc; import 'package:autonomy_flutter/service/remote_config_service.dart'; import 'package:autonomy_flutter/service/tezos_beacon_service.dart'; import 'package:autonomy_flutter/service/versions_service.dart'; @@ -90,7 +89,6 @@ class HomeNavigationPageState extends State final _configurationService = injector(); late Timer? _timer; final _clientTokenService = injector(); - final _notificationService = injector(); final _remoteConfig = injector(); final _announcementService = injector(); late HomeNavigatorTab _initialTab; @@ -296,8 +294,6 @@ class HomeNavigationPageState extends State event.notification.notificationId; final body = event.notification.body; - /// should complete event after getting all data needed - /// and before calling async function Future.delayed(const Duration(milliseconds: 500), () async { if (!mounted) { return; @@ -576,9 +572,5 @@ class HomeNavigationPageState extends State if (widget.payload.startedTab != _initialTab) { await onItemTapped(widget.payload.startedTab.index); } - final initialAction = _notificationService.initialAction; - if (initialAction != null) { - await nc.NotificationService.onActionReceivedMethod(initialAction); - } } } diff --git a/lib/screen/onboarding_page.dart b/lib/screen/onboarding_page.dart index ba793429b..ceb8bf3dd 100644 --- a/lib/screen/onboarding_page.dart +++ b/lib/screen/onboarding_page.dart @@ -17,7 +17,6 @@ import 'package:autonomy_flutter/service/configuration_service.dart'; import 'package:autonomy_flutter/service/deeplink_service.dart'; import 'package:autonomy_flutter/service/device_info_service.dart'; import 'package:autonomy_flutter/service/metric_client_service.dart'; -import 'package:autonomy_flutter/service/notification_service.dart'; import 'package:autonomy_flutter/service/passkey_service.dart'; import 'package:autonomy_flutter/service/remote_config_service.dart'; import 'package:autonomy_flutter/util/dailies_helper.dart'; @@ -103,14 +102,6 @@ class _OnboardingPageState extends State await injector() .setVersionInfo(packageInfo.version); - final notificationService = injector(); - unawaited( - notificationService.initNotification().then( - (_) { - notificationService.startListeningNotificationEvents(); - }, - ), - ); await disableLandscapeMode(); unawaited(JohnGerrardHelper.updateJohnGerrardLatestRevealIndex()); DailiesHelper.updateDailies([]); diff --git a/lib/service/notification_service.dart b/lib/service/notification_service.dart deleted file mode 100644 index c43d68123..000000000 --- a/lib/service/notification_service.dart +++ /dev/null @@ -1,168 +0,0 @@ -import 'dart:async'; -import 'dart:convert'; - -import 'package:autonomy_flutter/common/injector.dart'; -import 'package:autonomy_flutter/screen/app_router.dart'; -import 'package:autonomy_flutter/screen/detail/artwork_detail_page.dart'; -import 'package:autonomy_flutter/screen/interactive_postcard/postcard_detail_page.dart'; -import 'package:autonomy_flutter/service/navigation_service.dart'; -import 'package:autonomy_flutter/util/log.dart'; -import 'package:awesome_notifications/awesome_notifications.dart'; - -enum NotificationType { - postcard; - - Map toJson() => { - 'name': name, - }; - - static NotificationType fromJson(Map map) { - final value = NotificationType.values - .firstWhere((element) => element.name == map['name']); - return value; - } -} - -class NotificationPayload { - int notificationId; - NotificationType notificationType; - String metadata; - - NotificationPayload( - {required this.notificationId, - required this.notificationType, - required this.metadata}); - - factory NotificationPayload.fromJson(Map map) => - NotificationPayload( - notificationId: int.tryParse(map['notificationId'] ?? '') ?? 0, - notificationType: - NotificationType.fromJson(jsonDecode(map['notificationType'])), - metadata: map['metadata'], - ); - - Map toJson() => { - 'notificationId': notificationId.toString(), - 'notificationType': jsonEncode(notificationType.toJson()), - 'metadata': metadata, - }; -} - -class NotificationService { - final postcardChannelKey = 'autonomy.postcard.notification.key'; - final postcardChannelName = 'postcard_channel_name'; - final postcardChannelDescription = 'postcard_channel_description'; - ReceivedAction? _initialAction; - - ReceivedAction? get initialAction => _initialAction; - - NotificationService(); - - Future initNotification() async { - log.info('NotificationService: initNotification'); - try { - await AwesomeNotifications().initialize( - null, //'resource://drawable/res_app_icon',// - [ - NotificationChannel( - channelKey: postcardChannelKey, - channelName: postcardChannelName, - channelDescription: postcardChannelDescription, - playSound: true, - onlyAlertOnce: false, - groupAlertBehavior: GroupAlertBehavior.Children, - importance: NotificationImportance.High, - defaultPrivacy: NotificationPrivacy.Private, - ) - ], - debug: true); - - _initialAction = - await AwesomeNotifications().getInitialNotificationAction(); - } catch (e) { - log.warning('NotificationService: initNotification error: $e'); - } - } - - Future startListeningNotificationEvents() async { - log.info('NotificationService: startListeningNotificationEvents'); - try { - await AwesomeNotifications().setListeners( - onActionReceivedMethod: onActionReceivedMethod, - onNotificationCreatedMethod: onNotificationCreatedMethod, - onNotificationDisplayedMethod: onNotificationDisplayedMethod, - onDismissActionReceivedMethod: onDismissActionReceivedMethod, - ); - } catch (e) { - log.warning( - 'NotificationService: startListeningNotificationEvents error: $e'); - } - } - - @pragma('vm:entry-point') - static Future onActionReceivedMethod( - ReceivedAction receivedAction) async { - final navigationService = injector(); - if (receivedAction.actionType == ActionType.SilentAction || - receivedAction.actionType == ActionType.SilentBackgroundAction) { - return; - } else { - try { - final payload = receivedAction.payload; - final notificationPayload = NotificationPayload.fromJson(payload ?? {}); - switch (notificationPayload.notificationType) { - case NotificationType.postcard: - final postcardIdentity = PostcardIdentity.fromJson( - jsonDecode(notificationPayload.metadata)); - navigationService.popUntilHome(); - await navigationService.navigateTo( - AppRouter.claimedPostcardDetailsPage, - arguments: PostcardDetailPagePayload(ArtworkIdentity( - postcardIdentity.id, postcardIdentity.owner))); - } - } catch (e) { - log.info('[NotificationService] onActionReceivedMethod error: $e]'); - } - } - } - - @pragma('vm:entry-point') - static Future onNotificationCreatedMethod( - ReceivedNotification receivedNotification) async { - log.info('[NotificationService] onNotificationCreatedMethod:' - ' $receivedNotification'); - } - - @pragma('vm:entry-point') - static Future onNotificationDisplayedMethod( - ReceivedNotification receivedNotification) async { - log.info('[NotificationService] onNotificationDisplayedMethod:' - ' $receivedNotification'); - } - - @pragma('vm:entry-point') - static Future onDismissActionReceivedMethod( - ReceivedNotification receivedNotification) async { - log.info('[NotificationService] onDismissActionReceivedMethod:' - ' $receivedNotification'); - } - - Future showNotification( - {required String title, - required String channelKey, - int id = 0, - String? body, - Map? payload}) async { - bool isAllowed = await AwesomeNotifications().isNotificationAllowed(); - if (!isAllowed) { - return; - } - await AwesomeNotifications().createNotification( - content: NotificationContent( - id: id, - channelKey: channelKey, - title: title, - body: body, - payload: payload)); - } -} diff --git a/pubspec.lock b/pubspec.lock index b06fd9751..4598d92d6 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -97,14 +97,6 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.0" - awesome_notifications: - dependency: "direct main" - description: - name: awesome_notifications - sha256: "3eeb9e0cdfc72c7b4b83e9924da38ea86edd65a2af02bd5429aa41dce634ee90" - url: "https://pub.dev" - source: hosted - version: "0.8.3" backdrop: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index 7ba9eed9c..d87e1ec01 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -147,7 +147,6 @@ dependencies: html: ^0.15.1 video_player: ^2.9.2 walletconnect_flutter_v2: ^2.1.11 - awesome_notifications: ^0.8.3 shimmer: ^3.0.0 graphql_flutter: card_swiper: ^3.0.1 From 9ba8710fc87d31887d8f5b0b4b39ecd89ce6ffe1 Mon Sep 17 00:00:00 2001 From: phuoc Date: Fri, 8 Nov 2024 10:11:15 +0700 Subject: [PATCH 16/20] fix daily bloc instance Signed-off-by: phuoc --- lib/screen/dailies_work/dailies_work_page.dart | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/lib/screen/dailies_work/dailies_work_page.dart b/lib/screen/dailies_work/dailies_work_page.dart index 67a6e0846..663d9a3a6 100644 --- a/lib/screen/dailies_work/dailies_work_page.dart +++ b/lib/screen/dailies_work/dailies_work_page.dart @@ -61,9 +61,9 @@ class DailyWorkPageState extends State ScrollController? _scrollController; final _artworkKey = GlobalKey(); final _displayButtonKey = GlobalKey(); + late final DailyWorkBloc _dailyWorkBloc; - DailyToken? get _currentDailyToken => - context.read().state.currentDailyToken; + DailyToken? get _currentDailyToken => _dailyWorkBloc.state.currentDailyToken; bool _trackingInterest = false; Timer? _trackingInterestTimer; @@ -73,7 +73,8 @@ class DailyWorkPageState extends State @override void initState() { super.initState(); - context.read().add(GetDailyAssetTokenEvent()); + _dailyWorkBloc = injector(); + _dailyWorkBloc.add(GetDailyAssetTokenEvent()); _pageController = PageController(); _pageController!.addListener(() { _pageControllerListener(); @@ -151,7 +152,7 @@ class DailyWorkPageState extends State _timer?.cancel(); _timer = Timer(duration, () { log.info('Get Daily Asset Token'); - context.read().add(GetDailyAssetTokenEvent()); + _dailyWorkBloc.add(GetDailyAssetTokenEvent()); }); } @@ -235,6 +236,7 @@ class DailyWorkPageState extends State } Widget _buildBody() => BlocConsumer( + bloc: _dailyWorkBloc, builder: (context, state) => PageView( controller: _pageController, scrollDirection: Axis.vertical, @@ -366,6 +368,7 @@ class DailyWorkPageState extends State ), Expanded( child: BlocConsumer( + bloc: _dailyWorkBloc, listener: (context, state) { if (state.assetTokens.isNotEmpty) { // get identity @@ -461,6 +464,7 @@ class DailyWorkPageState extends State ) { final theme = Theme.of(context); return BlocBuilder( + bloc: _dailyWorkBloc, builder: (context, state) { final assetToken = state.assetTokens.firstOrNull; if (assetToken == null) { From 80c1ea606b6cedf8118c5872e38ad1bd6ef49757 Mon Sep 17 00:00:00 2001 From: phuoc Date: Fri, 8 Nov 2024 12:02:01 +0700 Subject: [PATCH 17/20] fix comment Signed-off-by: phuoc --- lib/model/additional_data/additional_data.dart | 2 +- lib/model/additional_data/daily_notification_data.dart | 2 +- lib/model/announcement/announcement_local.dart | 6 +++--- lib/service/customer_support_service.dart | 6 ++++++ 4 files changed, 11 insertions(+), 5 deletions(-) diff --git a/lib/model/additional_data/additional_data.dart b/lib/model/additional_data/additional_data.dart index aa17fb65e..d0a1c3acc 100644 --- a/lib/model/additional_data/additional_data.dart +++ b/lib/model/additional_data/additional_data.dart @@ -160,5 +160,5 @@ class AdditionalData { log.info('AdditionalData: handle tap: $notificationType'); } - FutureOr prepareAndDidSuccess() => Future.value(true); + FutureOr prepareAndDidSuccess() => true; } diff --git a/lib/model/additional_data/daily_notification_data.dart b/lib/model/additional_data/daily_notification_data.dart index c4cebdc5f..b8b042634 100644 --- a/lib/model/additional_data/daily_notification_data.dart +++ b/lib/model/additional_data/daily_notification_data.dart @@ -68,7 +68,7 @@ class DailyNotificationData extends AdditionalData { _logAndSendSentryForNullData('dailyToken'); return; } - final dailyToken = injector().state.currentDailyToken; + final dailyToken = _dailyWorkBloc.state.currentDailyToken; switch (dailyNotificationType) { case DailyNotificationType.viewDaily: case DailyNotificationType.revisitDaily: diff --git a/lib/model/announcement/announcement_local.dart b/lib/model/announcement/announcement_local.dart index f6bf4d55d..2c998e8eb 100644 --- a/lib/model/announcement/announcement_local.dart +++ b/lib/model/announcement/announcement_local.dart @@ -3,7 +3,7 @@ import 'package:autonomy_flutter/model/announcement/announcement.dart'; class AnnouncementLocal extends Announcement { bool read; - AnnouncementLocal({ + AnnouncementLocal._({ required super.announcementContentId, required super.content, required super.additionalData, @@ -22,7 +22,7 @@ class AnnouncementLocal extends Announcement { required DateTime endedAt, required bool read, }) => - AnnouncementLocal( + AnnouncementLocal._( announcementContentId: announcementContentId, content: content, additionalData: additionalData, @@ -39,7 +39,7 @@ class AnnouncementLocal extends Announcement { } static AnnouncementLocal fromAnnouncement(Announcement announcement) => - AnnouncementLocal( + AnnouncementLocal._( announcementContentId: announcement.announcementContentId, content: announcement.content, additionalData: announcement.additionalData, diff --git a/lib/service/customer_support_service.dart b/lib/service/customer_support_service.dart index 6551dd4ca..727e30000 100644 --- a/lib/service/customer_support_service.dart +++ b/lib/service/customer_support_service.dart @@ -101,6 +101,8 @@ class CustomerSupportServiceImpl extends CustomerSupportService { bool _isProcessingDraftMessages = false; + static const _supportChatNotificationTypes = []; + Future> _getIssues() async { final issues = []; try { @@ -179,6 +181,10 @@ class CustomerSupportServiceImpl extends CustomerSupportService { // add announcement final announcement = injector().getLocalAnnouncements() ..removeWhere((element) { + final type = element.notificationType ?? ''; + if (!_supportChatNotificationTypes.contains(type)) { + return true; + } final issueId = injector() .findIssueIdByAnnouncement(element.announcementContentId); final issue = From 9d01ab0bb71ddbb4dfcbb44d9b30efeca7140549 Mon Sep 17 00:00:00 2001 From: phuoc Date: Fri, 8 Nov 2024 13:40:55 +0700 Subject: [PATCH 18/20] fix feedback Signed-off-by: phuoc --- .../dailies_work/dailies_work_page.dart | 30 +++++++++---------- lib/service/user_interactivity_service.dart | 4 +-- lib/util/ui_helper.dart | 3 +- 3 files changed, 19 insertions(+), 18 deletions(-) diff --git a/lib/screen/dailies_work/dailies_work_page.dart b/lib/screen/dailies_work/dailies_work_page.dart index 663d9a3a6..6b6bca489 100644 --- a/lib/screen/dailies_work/dailies_work_page.dart +++ b/lib/screen/dailies_work/dailies_work_page.dart @@ -65,8 +65,8 @@ class DailyWorkPageState extends State DailyToken? get _currentDailyToken => _dailyWorkBloc.state.currentDailyToken; - bool _trackingInterest = false; - Timer? _trackingInterestTimer; + bool _trackingDailyLiked = false; + Timer? _trackingDailyLikedTimer; static const _scrollLikingThreshold = 100.0; static const _stayDurationLikingThreshold = Duration(seconds: 10); @@ -107,32 +107,32 @@ class DailyWorkPageState extends State } void didPushed() { - _stopTrackingInterest(); + _stopTrackingLiked(); } void trackInterest() { - if (_trackingInterest) { + if (_trackingDailyLiked) { return; } log.info('start trackingInterest in Daily'); - _trackingInterest = true; - _trackingInterestTimer = Timer(_stayDurationLikingThreshold, () { - _setUserInterested(); + _trackingDailyLiked = true; + _trackingDailyLikedTimer = Timer(_stayDurationLikingThreshold, () { + _setUserLiked(); }); } - void _stopTrackingInterest() { + void _stopTrackingLiked() { log.info('stopTrackingInterest in Daily'); - _trackingInterest = false; - _trackingInterestTimer?.cancel(); + _trackingDailyLiked = false; + _trackingDailyLikedTimer?.cancel(); } - void _setUserInterested() { - if (!_trackingInterest) { + void _setUserLiked() { + if (!_trackingDailyLiked) { return; } log.info('Set User Interested in Daily'); - _stopTrackingInterest(); + _stopTrackingLiked(); if (_currentDailyToken == null) { return; } @@ -285,7 +285,7 @@ class DailyWorkPageState extends State device, CastDailyWorkRequest())); }, onTap: () { - _setUserInterested(); + _setUserLiked(); }, text: 'display'.tr(), shouldCheckSubscription: false, @@ -484,7 +484,7 @@ class DailyWorkPageState extends State curve: Curves.easeInOut)); } if (_scrollController!.offset > _scrollLikingThreshold) { - _setUserInterested(); + _setUserLiked(); } return true; }, diff --git a/lib/service/user_interactivity_service.dart b/lib/service/user_interactivity_service.dart index b9afccbac..b36e5b406 100644 --- a/lib/service/user_interactivity_service.dart +++ b/lib/service/user_interactivity_service.dart @@ -7,6 +7,7 @@ import 'package:autonomy_flutter/service/metric_client_service.dart'; import 'package:autonomy_flutter/util/metric_helper.dart'; import 'package:autonomy_flutter/util/ui_helper.dart'; import 'package:easy_localization/easy_localization.dart'; +import 'package:onesignal_flutter/onesignal_flutter.dart'; abstract class UserInteractivityService { Future likeDailyWork(DailyToken dailyToken); @@ -32,8 +33,7 @@ class UserInteractivityServiceImpl implements UserInteractivityService { } Future _likeDailyWorkActionResponse() async { - /// temporary implementation - const isNotificationEnabled = true; + final isNotificationEnabled = OneSignal.Notifications.permission; if (!isNotificationEnabled) { final likedCount = _configurationService.getDailyLikedCount(); if (likedCount >= 3) { diff --git a/lib/util/ui_helper.dart b/lib/util/ui_helper.dart index 08c8877b6..ff1b05f4a 100644 --- a/lib/util/ui_helper.dart +++ b/lib/util/ui_helper.dart @@ -54,6 +54,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_vibrate/flutter_vibrate.dart'; import 'package:jiffy/jiffy.dart'; +import 'package:permission_handler/permission_handler.dart'; import 'package:share_plus/share_plus.dart'; import 'package:walletconnect_flutter_v2/apis/core/pairing/utils/pairing_models.dart'; @@ -1968,7 +1969,7 @@ class UIHelper { PrimaryButton( onTap: () async { Navigator.of(context).pop(true); - await Navigator.of(context).pushNamed(AppRouter.preferencesPage); + openAppSettings(); }, text: 'go_to_notifications'.tr(), ), From e2b10a788318cbb860b6a64f5a0e31b838dda8c3 Mon Sep 17 00:00:00 2001 From: phuoc Date: Fri, 8 Nov 2024 14:17:22 +0700 Subject: [PATCH 19/20] fix track intertest daily Signed-off-by: phuoc --- lib/model/additional_data/additional_data.dart | 2 +- lib/model/additional_data/daily_notification_data.dart | 1 - lib/screen/dailies_work/dailies_work_page.dart | 5 +++++ lib/screen/home/home_navigation_page.dart | 1 + lib/service/user_interactivity_service.dart | 7 ++++--- lib/util/notification_type.dart | 10 +++++----- 6 files changed, 16 insertions(+), 10 deletions(-) diff --git a/lib/model/additional_data/additional_data.dart b/lib/model/additional_data/additional_data.dart index d0a1c3acc..d5a08c56a 100644 --- a/lib/model/additional_data/additional_data.dart +++ b/lib/model/additional_data/additional_data.dart @@ -131,7 +131,7 @@ class AdditionalData { homeIndex: homeIndex, linkText: linkText, ); - case NotificationType.daily: + case NotificationType.dailyArtworkReminders: final subType = json['notification_sub_type']; final dailyType = DailyNotificationType.fromString(subType ?? ''); if (dailyType == null) { diff --git a/lib/model/additional_data/daily_notification_data.dart b/lib/model/additional_data/daily_notification_data.dart index b8b042634..d23af0e1c 100644 --- a/lib/model/additional_data/daily_notification_data.dart +++ b/lib/model/additional_data/daily_notification_data.dart @@ -73,7 +73,6 @@ class DailyNotificationData extends AdditionalData { case DailyNotificationType.viewDaily: case DailyNotificationType.revisitDaily: await _navigationService.navigatePath(AppRouter.dailyWorkPage); - dailyWorkKey.currentState?.trackInterest(); case DailyNotificationType.viewDailySeries: final artwork = dailyToken!.artwork; if (artwork == null) { diff --git a/lib/screen/dailies_work/dailies_work_page.dart b/lib/screen/dailies_work/dailies_work_page.dart index 6b6bca489..118b3bbcd 100644 --- a/lib/screen/dailies_work/dailies_work_page.dart +++ b/lib/screen/dailies_work/dailies_work_page.dart @@ -110,6 +110,10 @@ class DailyWorkPageState extends State _stopTrackingLiked(); } + void didTapDaily() { + trackInterest(); + } + void trackInterest() { if (_trackingDailyLiked) { return; @@ -260,6 +264,7 @@ class DailyWorkPageState extends State .addEvent(MetricEventName.dailyView, data: { MetricParameter.tokenId: current.assetTokens.first.id, })); + trackInterest(); } } return true; diff --git a/lib/screen/home/home_navigation_page.dart b/lib/screen/home/home_navigation_page.dart index 31301182c..8f4a9aac7 100644 --- a/lib/screen/home/home_navigation_page.dart +++ b/lib/screen/home/home_navigation_page.dart @@ -132,6 +132,7 @@ class HomeNavigationPageState extends State // otherwise pause daily work if (index == HomeNavigatorTab.daily.index) { dailyWorkKey.currentState?.resumeDailyWork(); + dailyWorkKey.currentState?.trackInterest(); } else { dailyWorkKey.currentState?.pauseDailyWork(); } diff --git a/lib/service/user_interactivity_service.dart b/lib/service/user_interactivity_service.dart index b36e5b406..c048a5619 100644 --- a/lib/service/user_interactivity_service.dart +++ b/lib/service/user_interactivity_service.dart @@ -1,9 +1,10 @@ import 'dart:async'; -import 'dart:math'; +import 'dart:math' as math; import 'package:autonomy_flutter/model/dailies.dart'; import 'package:autonomy_flutter/service/configuration_service.dart'; import 'package:autonomy_flutter/service/metric_client_service.dart'; +import 'package:autonomy_flutter/util/log.dart'; import 'package:autonomy_flutter/util/metric_helper.dart'; import 'package:autonomy_flutter/util/ui_helper.dart'; import 'package:easy_localization/easy_localization.dart'; @@ -28,7 +29,7 @@ class UserInteractivityServiceImpl implements UserInteractivityService { }; unawaited( _metricClientService.addEvent(MetricEventName.dailyLiked, data: data)); - + log.info('Liked daily work: ${dailyToken.tokenID}'); await _likeDailyWorkActionResponse(); } @@ -81,6 +82,6 @@ enum EnableNotificationPromptType { static EnableNotificationPromptType getRandomType() { const values = EnableNotificationPromptType.values; - return values[Random().nextInt(values.length)]; + return values[math.Random().nextInt(values.length)]; } } diff --git a/lib/util/notification_type.dart b/lib/util/notification_type.dart index 16411fc7f..010c2f36f 100644 --- a/lib/util/notification_type.dart +++ b/lib/util/notification_type.dart @@ -30,7 +30,7 @@ enum NotificationType { exhibitionSaleClosing, navigate, general, - daily, + dailyArtworkReminders, ; // toString method @@ -67,8 +67,8 @@ enum NotificationType { return 'navigate'; case NotificationType.general: return 'general'; - case NotificationType.daily: - return 'daily'; + case NotificationType.dailyArtworkReminders: + return 'daily_artwork_reminders'; } } @@ -103,8 +103,8 @@ enum NotificationType { return NotificationType.exhibitionSaleClosing; case 'navigate': return NotificationType.navigate; - case 'daily': - return NotificationType.daily; + case 'daily_artwork_reminders': + return NotificationType.dailyArtworkReminders; default: return NotificationType.general; } From 84fe4fceb135334d05606c87e0f405a84902c6d4 Mon Sep 17 00:00:00 2001 From: phuoc Date: Fri, 8 Nov 2024 14:52:22 +0700 Subject: [PATCH 20/20] add comment, rename Signed-off-by: phuoc --- lib/service/announcement/announcement_service.dart | 1 + lib/service/customer_support_service.dart | 1 + lib/service/user_interactivity_service.dart | 4 ++-- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/service/announcement/announcement_service.dart b/lib/service/announcement/announcement_service.dart index cb639ced0..4b90d3c7a 100644 --- a/lib/service/announcement/announcement_service.dart +++ b/lib/service/announcement/announcement_service.dart @@ -111,6 +111,7 @@ class AnnouncementServiceImpl implements AnnouncementService { await _saveAnnouncement(announcement.markAsRead()); } + /// unread and in-app enabled announcements List _getAnnouncementsToShow() { _queue.removeWhere((element) => element.read || !element.inAppEnabled); if (_queue.isEmpty) { diff --git a/lib/service/customer_support_service.dart b/lib/service/customer_support_service.dart index 727e30000..bb0870008 100644 --- a/lib/service/customer_support_service.dart +++ b/lib/service/customer_support_service.dart @@ -101,6 +101,7 @@ class CustomerSupportServiceImpl extends CustomerSupportService { bool _isProcessingDraftMessages = false; + /// will add this after backend support static const _supportChatNotificationTypes = []; Future> _getIssues() async { diff --git a/lib/service/user_interactivity_service.dart b/lib/service/user_interactivity_service.dart index c048a5619..514b252ba 100644 --- a/lib/service/user_interactivity_service.dart +++ b/lib/service/user_interactivity_service.dart @@ -30,10 +30,10 @@ class UserInteractivityServiceImpl implements UserInteractivityService { unawaited( _metricClientService.addEvent(MetricEventName.dailyLiked, data: data)); log.info('Liked daily work: ${dailyToken.tokenID}'); - await _likeDailyWorkActionResponse(); + await _countDailyLiked(); } - Future _likeDailyWorkActionResponse() async { + Future _countDailyLiked() async { final isNotificationEnabled = OneSignal.Notifications.permission; if (!isNotificationEnabled) { final likedCount = _configurationService.getDailyLikedCount();