apply_code_screen.dart 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. import "package:flutter/material.dart";
  2. import "package:logging/logging.dart";
  3. import "package:photos/extensions/input_formatter.dart";
  4. import "package:photos/models/api/storage_bonus/storage_bonus.dart";
  5. import "package:photos/models/user_details.dart";
  6. import "package:photos/services/storage_bonus_service.dart";
  7. import "package:photos/theme/ente_theme.dart";
  8. import 'package:photos/ui/components/buttons/button_widget.dart';
  9. import 'package:photos/ui/components/buttons/icon_button_widget.dart';
  10. import "package:photos/ui/components/models/button_type.dart";
  11. import "package:photos/ui/components/title_bar_title_widget.dart";
  12. import "package:photos/ui/components/title_bar_widget.dart";
  13. import "package:photos/ui/growth/code_success_screen.dart";
  14. import "package:photos/utils/dialog_util.dart";
  15. class ApplyCodeScreen extends StatefulWidget {
  16. // referrerView and userDetails used to render code_success_screen
  17. final ReferralView referralView;
  18. final UserDetails userDetails;
  19. const ApplyCodeScreen(
  20. this.referralView,
  21. this.userDetails, {
  22. super.key,
  23. });
  24. @override
  25. State<ApplyCodeScreen> createState() => _ApplyCodeScreenState();
  26. }
  27. class _ApplyCodeScreenState extends State<ApplyCodeScreen> {
  28. late TextEditingController _textController;
  29. late FocusNode textFieldFocusNode;
  30. String code = "";
  31. @override
  32. void initState() {
  33. _textController = TextEditingController();
  34. textFieldFocusNode = FocusNode();
  35. super.initState();
  36. }
  37. @override
  38. void dispose() {
  39. _textController.dispose();
  40. textFieldFocusNode.dispose();
  41. super.dispose();
  42. }
  43. @override
  44. Widget build(BuildContext context) {
  45. final colorScheme = getEnteColorScheme(context);
  46. final textStyle = getEnteTextTheme(context);
  47. textFieldFocusNode.requestFocus();
  48. return Scaffold(
  49. body: CustomScrollView(
  50. primary: false,
  51. slivers: <Widget>[
  52. TitleBarWidget(
  53. flexibleSpaceTitle: const TitleBarTitleWidget(
  54. title: "Apply code",
  55. ),
  56. actionIcons: [
  57. IconButtonWidget(
  58. icon: Icons.close_outlined,
  59. iconButtonType: IconButtonType.secondary,
  60. onTap: () {
  61. // Go three screen back, similar to pop thrice
  62. Navigator.of(context)
  63. ..pop()
  64. ..pop()
  65. ..pop();
  66. },
  67. ),
  68. ],
  69. ),
  70. SliverList(
  71. delegate: SliverChildBuilderDelegate(
  72. (delegateBuildContext, index) {
  73. return Padding(
  74. padding: const EdgeInsets.symmetric(horizontal: 16),
  75. child: Padding(
  76. padding: const EdgeInsets.symmetric(vertical: 20),
  77. child: Column(
  78. mainAxisSize: MainAxisSize.min,
  79. children: [
  80. Column(
  81. children: [
  82. Text(
  83. "Enter the code provided by your friend to "
  84. "claim free storage for both of you",
  85. style: textStyle.small
  86. .copyWith(color: colorScheme.textMuted),
  87. ),
  88. const SizedBox(height: 24),
  89. _getInputField(),
  90. // Container with 8 border radius and red color
  91. ],
  92. ),
  93. ],
  94. ),
  95. ),
  96. );
  97. },
  98. childCount: 1,
  99. ),
  100. ),
  101. SliverFillRemaining(
  102. child: SafeArea(
  103. child: Padding(
  104. padding: const EdgeInsets.all(12.0),
  105. child: Column(
  106. mainAxisAlignment: MainAxisAlignment.end,
  107. children: [
  108. ButtonWidget(
  109. buttonType: ButtonType.neutral,
  110. buttonSize: ButtonSize.large,
  111. labelText: "Apply",
  112. isDisabled: code.trim().length < 4,
  113. onTap: () async {
  114. try {
  115. await StorageBonusService.instance
  116. .getGateway()
  117. .claimReferralCode(code.trim().toUpperCase());
  118. Navigator.of(context).pushReplacement(
  119. MaterialPageRoute(
  120. builder: (context) => CodeSuccessScreen(
  121. widget.referralView,
  122. widget.userDetails,
  123. ),
  124. ),
  125. );
  126. } catch (e) {
  127. Logger('$runtimeType')
  128. .severe("failed to apply referral", e);
  129. showErrorDialogForException(
  130. context: context,
  131. exception: e as Exception,
  132. apiErrorPrefix: "Failed to apply code");
  133. }
  134. },
  135. )
  136. ],
  137. ),
  138. ),
  139. ),
  140. ),
  141. ],
  142. ),
  143. );
  144. }
  145. Widget _getInputField() {
  146. return TextFormField(
  147. controller: _textController,
  148. focusNode: textFieldFocusNode,
  149. style: getEnteTextTheme(context).body,
  150. inputFormatters: [UpperCaseTextFormatter()],
  151. textCapitalization: TextCapitalization.sentences,
  152. decoration: InputDecoration(
  153. focusedBorder: OutlineInputBorder(
  154. borderRadius: const BorderRadius.all(Radius.circular(4.0)),
  155. borderSide:
  156. BorderSide(color: getEnteColorScheme(context).strokeMuted),
  157. ),
  158. fillColor: getEnteColorScheme(context).fillFaint,
  159. filled: true,
  160. hintText: 'Enter referral code',
  161. contentPadding: const EdgeInsets.symmetric(
  162. horizontal: 16,
  163. vertical: 14,
  164. ),
  165. border: UnderlineInputBorder(
  166. borderSide: BorderSide.none,
  167. borderRadius: BorderRadius.circular(8),
  168. ),
  169. ),
  170. onChanged: (value) {
  171. code = value.trim();
  172. setState(() {});
  173. },
  174. autocorrect: false,
  175. keyboardType: TextInputType.emailAddress,
  176. textInputAction: TextInputAction.next,
  177. );
  178. }
  179. }