diff --git a/lib/generated/intl/messages_en.dart b/lib/generated/intl/messages_en.dart index e683466d4..ef3b802a9 100644 --- a/lib/generated/intl/messages_en.dart +++ b/lib/generated/intl/messages_en.dart @@ -347,6 +347,8 @@ class MessageLookup extends MessageLookupByLibrary { "backupSettings": MessageLookupByLibrary.simpleMessage("Backup settings"), "backupVideos": MessageLookupByLibrary.simpleMessage("Backup videos"), + "blackFridaySale": + MessageLookupByLibrary.simpleMessage("Black Friday Sale"), "blog": MessageLookupByLibrary.simpleMessage("Blog"), "cachedData": MessageLookupByLibrary.simpleMessage("Cached data"), "calculating": MessageLookupByLibrary.simpleMessage("Calculating..."), @@ -1338,6 +1340,8 @@ class MessageLookup extends MessageLookupByLibrary { "upgrade": MessageLookupByLibrary.simpleMessage("Upgrade"), "uploadingFilesToAlbum": MessageLookupByLibrary.simpleMessage("Uploading files to album..."), + "upto50OffUntil4thDec": MessageLookupByLibrary.simpleMessage( + "Upto 50% off, until 4th Dec."), "usableReferralStorageInfo": MessageLookupByLibrary.simpleMessage( "Usable storage is limited by your current plan. Excess claimed storage will automatically become usable when you upgrade your plan."), "usePublicLinksForPeopleNotOnEnte": diff --git a/lib/generated/l10n.dart b/lib/generated/l10n.dart index ee0a68d7d..bae3b877e 100644 --- a/lib/generated/l10n.dart +++ b/lib/generated/l10n.dart @@ -7895,6 +7895,16 @@ class S { ); } + /// `Black Friday Sale` + String get blackFridaySale { + return Intl.message( + 'Black Friday Sale', + name: 'blackFridaySale', + desc: '', + args: [], + ); + } + /// `Modify your query, or try searching for` String get modifyYourQueryOrTrySearchingFor { return Intl.message( @@ -7904,6 +7914,16 @@ class S { args: [], ); } + + /// `Upto 50% off, until 4th Dec.` + String get upto50OffUntil4thDec { + return Intl.message( + 'Upto 50% off, until 4th Dec.', + name: 'upto50OffUntil4thDec', + desc: '', + args: [], + ); + } } class AppLocalizationDelegate extends LocalizationsDelegate { diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb index 4c00753b6..cc538a1fa 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -1122,5 +1122,7 @@ "addOns": "Add-ons", "addOnPageSubtitle": "Details of add-ons", "yourMap": "Your map", - "modifyYourQueryOrTrySearchingFor": "Modify your query, or try searching for" -} \ No newline at end of file + "modifyYourQueryOrTrySearchingFor": "Modify your query, or try searching for", + "blackFridaySale": "Black Friday Sale", + "upto50OffUntil4thDec": "Upto 50% off, until 4th Dec." +} diff --git a/lib/models/user_details.dart b/lib/models/user_details.dart index ef1fdbbd7..f107a2e6b 100644 --- a/lib/models/user_details.dart +++ b/lib/models/user_details.dart @@ -33,6 +33,10 @@ class UserDetails { return familyData?.members?.isNotEmpty ?? false; } + bool hasPaidAddon() { + return bonusData?.getAddOnBonuses().isNotEmpty ?? false; + } + bool isFamilyAdmin() { assert(isPartOfFamily(), "verify user is part of family before calling"); final FamilyMember currentUserMember = familyData!.members! diff --git a/lib/services/billing_service.dart b/lib/services/billing_service.dart index 24ec308fb..6891089e3 100644 --- a/lib/services/billing_service.dart +++ b/lib/services/billing_service.dart @@ -166,7 +166,8 @@ class BillingService { BuildContext context, UserDetails userDetails, ) async { - if (userDetails.subscription.productID == freeProductID) { + if (userDetails.subscription.productID == freeProductID && + !userDetails.hasPaidAddon()) { await showErrorDialog( context, S.of(context).familyPlans, diff --git a/lib/ui/components/notification_widget.dart b/lib/ui/components/notification_widget.dart index 6b0691238..6779a58fa 100644 --- a/lib/ui/components/notification_widget.dart +++ b/lib/ui/components/notification_widget.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import "package:flutter_animate/flutter_animate.dart"; import "package:photos/ente_theme_data.dart"; import 'package:photos/theme/colors.dart'; import "package:photos/theme/ente_theme.dart"; @@ -20,6 +21,7 @@ class NotificationWidget extends StatelessWidget { final String? subText; final GestureTapCallback onTap; final NotificationType type; + final bool isBlackFriday; const NotificationWidget({ Key? key, @@ -27,6 +29,7 @@ class NotificationWidget extends StatelessWidget { required this.actionIcon, required this.text, required this.onTap, + this.isBlackFriday = false, this.subText, this.type = NotificationType.warning, }) : super(key: key); @@ -89,11 +92,34 @@ class NotificationWidget extends StatelessWidget { child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Icon( - startIcon, - size: 36, - color: strokeColorScheme.strokeBase, - ), + isBlackFriday + ? Icon( + startIcon, + size: 36, + color: strokeColorScheme.strokeBase, + ) + .animate( + onPlay: (controller) => + controller.repeat(reverse: true), + delay: 2000.ms, + ) + .shake( + duration: 500.ms, + hz: 6, + delay: 1600.ms, + ) + .scale( + duration: 500.ms, + begin: const Offset(0.9, 0.9), + end: const Offset(1.1, 1.1), + delay: 1600.ms, + // curve: Curves.easeInOut, + ) + : Icon( + startIcon, + size: 36, + color: strokeColorScheme.strokeBase, + ), const SizedBox(width: 12), Expanded( child: Column( diff --git a/lib/ui/payment/stripe_subscription_page.dart b/lib/ui/payment/stripe_subscription_page.dart index 506e597da..54a551b7d 100644 --- a/lib/ui/payment/stripe_subscription_page.dart +++ b/lib/ui/payment/stripe_subscription_page.dart @@ -211,10 +211,12 @@ class _StripeSubscriptionPageState extends State { widgets.add(_showSubscriptionToggle()); if (_hasActiveSubscription) { - widgets.add(ValidityWidget( - currentSubscription: _currentSubscription, - bonusData: _userDetails.bonusData, - )); + widgets.add( + ValidityWidget( + currentSubscription: _currentSubscription, + bonusData: _userDetails.bonusData, + ), + ); } if (_currentSubscription!.productID == freeProductID) { diff --git a/lib/ui/settings_page.dart b/lib/ui/settings_page.dart index da99ad320..044a70975 100644 --- a/lib/ui/settings_page.dart +++ b/lib/ui/settings_page.dart @@ -9,6 +9,7 @@ import 'package:photos/events/opened_settings_event.dart'; import "package:photos/generated/l10n.dart"; import 'package:photos/services/feature_flag_service.dart'; import "package:photos/services/storage_bonus_service.dart"; +import "package:photos/services/user_service.dart"; import 'package:photos/theme/colors.dart'; import 'package:photos/theme/ente_theme.dart'; import "package:photos/ui/components/notification_widget.dart"; @@ -28,6 +29,7 @@ import 'package:photos/ui/settings/support_section_widget.dart'; import 'package:photos/ui/settings/theme_switch_widget.dart'; import "package:photos/ui/sharing/verify_identity_dialog.dart"; import "package:photos/utils/navigation_util.dart"; +import "package:url_launcher/url_launcher_string.dart"; class SettingsPage extends StatelessWidget { final ValueNotifier emailNotifier; @@ -84,23 +86,42 @@ class SettingsPage extends StatelessWidget { const sectionSpacing = SizedBox(height: 8); contents.add(const SizedBox(height: 8)); if (hasLoggedIn) { + final shouldShowBFBanner = shouldShowBfBanner(); + final showStorageBonusBanner = + StorageBonusService.instance.shouldShowStorageBonus(); contents.addAll([ const StorageCardWidget(), - StorageBonusService.instance.shouldShowStorageBonus() + (shouldShowBFBanner || showStorageBonusBanner) ? RepaintBoundary( child: Padding( padding: const EdgeInsets.symmetric(vertical: 8.0), - child: NotificationWidget( - startIcon: Icons.auto_awesome, - actionIcon: Icons.arrow_forward_outlined, - text: S.of(context).doubleYourStorage, - subText: S.of(context).referFriendsAnd2xYourPlan, - type: NotificationType.goldenBanner, - onTap: () async { - StorageBonusService.instance.markStorageBonusAsDone(); - routeToPage(context, const ReferralScreen()); - }, - ), + child: shouldShowBFBanner + ? NotificationWidget( + isBlackFriday: true, + startIcon: Icons.celebration, + actionIcon: Icons.arrow_forward_outlined, + text: S.of(context).blackFridaySale, + subText: S.of(context).upto50OffUntil4thDec, + type: NotificationType.goldenBanner, + onTap: () async { + launchUrlString( + "https://ente.io/blackfriday", + mode: LaunchMode.platformDefault, + ); + }, + ) + : NotificationWidget( + startIcon: Icons.auto_awesome, + actionIcon: Icons.arrow_forward_outlined, + text: S.of(context).doubleYourStorage, + subText: S.of(context).referFriendsAnd2xYourPlan, + type: NotificationType.goldenBanner, + onTap: () async { + StorageBonusService.instance + .markStorageBonusAsDone(); + routeToPage(context, const ReferralScreen()); + }, + ), ).animate(onPlay: (controller) => controller.repeat()).shimmer( duration: 1000.ms, delay: 3200.ms, @@ -167,6 +188,23 @@ class SettingsPage extends StatelessWidget { ); } + bool shouldShowBfBanner() { + if (!Platform.isAndroid && !kDebugMode) { + return false; + } + // if date is after 5th of December 2023, 00:00:00, hide banner + if (DateTime.now().isAfter(DateTime(2023, 12, 5))) { + return false; + } + // if coupon is already applied, can hide the banner + return (UserService.instance + .getCachedUserDetails() + ?.bonusData + ?.getAddOnBonuses() + .isEmpty ?? + true); + } + Future _showVerifyIdentityDialog(BuildContext context) async { await showDialog( context: context, diff --git a/pubspec.yaml b/pubspec.yaml index 315b70db6..9d2349252 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -12,7 +12,7 @@ description: ente photos application # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 0.7.108+508 +version: 0.7.116+516 environment: sdk: ">=3.0.0 <4.0.0"