subscription_common_widgets.dart 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. import 'package:flutter/material.dart';
  2. import "package:intl/intl.dart";
  3. import 'package:photos/ente_theme_data.dart';
  4. import "package:photos/generated/l10n.dart";
  5. import "package:photos/models/api/storage_bonus/bonus.dart";
  6. import 'package:photos/models/subscription.dart';
  7. import "package:photos/services/update_service.dart";
  8. import "package:photos/theme/ente_theme.dart";
  9. import "package:photos/ui/components/captioned_text_widget.dart";
  10. import "package:photos/ui/components/menu_item_widget/menu_item_widget.dart";
  11. import 'package:photos/ui/payment/billing_questions_widget.dart';
  12. import 'package:photos/utils/data_util.dart';
  13. class SubscriptionHeaderWidget extends StatefulWidget {
  14. final bool? isOnboarding;
  15. final int? currentUsage;
  16. const SubscriptionHeaderWidget({
  17. Key? key,
  18. this.isOnboarding,
  19. this.currentUsage,
  20. }) : super(key: key);
  21. @override
  22. State<StatefulWidget> createState() {
  23. return _SubscriptionHeaderWidgetState();
  24. }
  25. }
  26. class _SubscriptionHeaderWidgetState extends State<SubscriptionHeaderWidget> {
  27. @override
  28. Widget build(BuildContext context) {
  29. if (widget.isOnboarding!) {
  30. return Padding(
  31. padding: const EdgeInsets.fromLTRB(20, 20, 20, 24),
  32. child: Column(
  33. crossAxisAlignment: CrossAxisAlignment.start,
  34. children: [
  35. Row(
  36. children: [
  37. Text(
  38. S.of(context).selectYourPlan,
  39. style: Theme.of(context).textTheme.headlineMedium,
  40. ),
  41. ],
  42. ),
  43. const SizedBox(height: 10),
  44. Text(
  45. S.of(context).enteSubscriptionPitch,
  46. style: Theme.of(context).textTheme.bodySmall,
  47. ),
  48. const SizedBox(height: 4),
  49. Text(
  50. S.of(context).enteSubscriptionShareWithFamily,
  51. style: Theme.of(context).textTheme.bodySmall,
  52. ),
  53. ],
  54. ),
  55. );
  56. } else {
  57. return SizedBox(
  58. height: 72,
  59. width: double.infinity,
  60. child: Padding(
  61. padding: const EdgeInsets.all(24.0),
  62. child: RichText(
  63. text: TextSpan(
  64. children: [
  65. TextSpan(
  66. text: S.of(context).currentUsageIs,
  67. style: Theme.of(context).textTheme.titleMedium,
  68. ),
  69. TextSpan(
  70. text: formatBytes(widget.currentUsage!),
  71. style: Theme.of(context)
  72. .textTheme
  73. .titleMedium!
  74. .copyWith(fontWeight: FontWeight.bold),
  75. ),
  76. ],
  77. ),
  78. ),
  79. ),
  80. );
  81. }
  82. }
  83. }
  84. class ValidityWidget extends StatelessWidget {
  85. final Subscription? currentSubscription;
  86. final BonusData? bonusData;
  87. const ValidityWidget({Key? key, this.currentSubscription, this.bonusData})
  88. : super(key: key);
  89. @override
  90. Widget build(BuildContext context) {
  91. if (currentSubscription == null) {
  92. return const SizedBox.shrink();
  93. }
  94. final List<Bonus> addOnBonus = bonusData?.getAddOnBonuses() ?? <Bonus>[];
  95. final bool isFreeTrialSub = currentSubscription!.productID == freeProductID;
  96. bool hideSubValidityView = false;
  97. if (isFreeTrialSub && addOnBonus.isNotEmpty) {
  98. hideSubValidityView = true;
  99. }
  100. if (!currentSubscription!.isValid()) {
  101. hideSubValidityView = true;
  102. }
  103. final endDate =
  104. DateFormat.yMMMd(Localizations.localeOf(context).languageCode).format(
  105. DateTime.fromMicrosecondsSinceEpoch(currentSubscription!.expiryTime),
  106. );
  107. var message = S.of(context).renewsOn(endDate);
  108. if (isFreeTrialSub) {
  109. message = UpdateService.instance.isPlayStoreFlavor()
  110. ? S.of(context).playStoreFreeTrialValidTill(endDate)
  111. : S.of(context).freeTrialValidTill(endDate);
  112. } else if (currentSubscription!.attributes?.isCancelled ?? false) {
  113. message = S.of(context).subWillBeCancelledOn(endDate);
  114. if (addOnBonus.isNotEmpty) {
  115. hideSubValidityView = true;
  116. }
  117. }
  118. return Padding(
  119. padding: const EdgeInsets.only(top: 0),
  120. child: Column(
  121. children: [
  122. if (!hideSubValidityView)
  123. Text(
  124. message,
  125. style: Theme.of(context).textTheme.bodySmall,
  126. textAlign: TextAlign.center,
  127. ),
  128. if (addOnBonus.isNotEmpty)
  129. ...addOnBonus.map((bonus) => AddOnBonusValidity(bonus)).toList(),
  130. ],
  131. ),
  132. );
  133. }
  134. }
  135. class AddOnBonusValidity extends StatelessWidget {
  136. final Bonus bonus;
  137. const AddOnBonusValidity(this.bonus, {super.key});
  138. @override
  139. Widget build(BuildContext context) {
  140. final endDate =
  141. DateFormat.yMMMd(Localizations.localeOf(context).languageCode).format(
  142. DateTime.fromMicrosecondsSinceEpoch(bonus.validTill),
  143. );
  144. final String storage = convertBytesToReadableFormat(bonus.storage);
  145. return Padding(
  146. padding: const EdgeInsets.only(top: 8, bottom: 8),
  147. child: Text(
  148. S.of(context).addOnValidTill(storage, endDate),
  149. style: Theme.of(context).textTheme.bodySmall,
  150. textAlign: TextAlign.center,
  151. ),
  152. );
  153. }
  154. }
  155. class SubFaqWidget extends StatelessWidget {
  156. final bool isOnboarding;
  157. const SubFaqWidget({Key? key, this.isOnboarding = false}) : super(key: key);
  158. @override
  159. Widget build(BuildContext context) {
  160. final colorScheme = getEnteColorScheme(context);
  161. return Padding(
  162. padding: EdgeInsets.fromLTRB(16, 40, 16, isOnboarding ? 40 : 4),
  163. child: MenuItemWidget(
  164. captionedTextWidget: CaptionedTextWidget(
  165. title: S.of(context).faqs,
  166. ),
  167. menuItemColor: colorScheme.fillFaint,
  168. trailingWidget: Icon(
  169. Icons.chevron_right_outlined,
  170. color: colorScheme.strokeBase,
  171. ),
  172. singleBorderRadius: 4,
  173. alignCaptionedTextToLeft: true,
  174. onTap: () async {
  175. showModalBottomSheet<void>(
  176. backgroundColor: Theme.of(context).colorScheme.bgColorForQuestions,
  177. barrierColor: Colors.black87,
  178. context: context,
  179. builder: (context) {
  180. return const BillingQuestionsWidget();
  181. },
  182. );
  183. },
  184. ),
  185. );
  186. }
  187. }