kasy-cli 1.31.14 → 1.32.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/commands/new.js +15 -1
- package/lib/scaffold/CHANGELOG.json +9 -0
- package/lib/scaffold/backends/api/patch/README.md +87 -2
- package/lib/scaffold/backends/api/patch/lib/features/authentication/api/authentication_api.dart +34 -0
- package/lib/scaffold/backends/api/patch/lib/features/subscriptions/api/stripe_backend_api.dart +12 -2
- package/lib/scaffold/backends/firebase/setup-from-scratch.js +22 -0
- package/lib/scaffold/backends/supabase/edge-functions/send-push-notification/README.md +26 -22
- package/lib/scaffold/backends/supabase/edge-functions/send-push-notification/index.ts +8 -11
- package/lib/scaffold/backends/supabase/edge-functions/stripe-create-checkout-session/index.ts +3 -1
- package/lib/scaffold/backends/supabase/edge-functions/stripe-create-portal-session/index.ts +60 -3
- package/lib/scaffold/backends/supabase/patch/lib/features/authentication/api/authentication_api.dart +12 -0
- package/lib/scaffold/backends/supabase/patch/lib/features/subscriptions/api/stripe_backend_api.dart +12 -2
- package/lib/scaffold/backends/supabase/pubspec.yaml.tpl +3 -2
- package/lib/scaffold/generate.js +1 -1
- package/lib/scaffold/shared/generator-utils.js +22 -3
- package/lib/utils/i18n/messages-en.js +2 -0
- package/lib/utils/i18n/messages-es.js +2 -0
- package/lib/utils/i18n/messages-pt.js +2 -0
- package/package.json +2 -2
- package/templates/firebase/docs/auth-setup.en.md +7 -1
- package/templates/firebase/docs/auth-setup.es.md +7 -1
- package/templates/firebase/docs/auth-setup.pt.md +7 -1
- package/templates/firebase/functions/src/subscriptions/stripe_functions.ts +79 -5
- package/templates/firebase/lib/components/kasy_accordion.dart +2 -2
- package/templates/firebase/lib/components/kasy_alert.dart +1 -1
- package/templates/firebase/lib/components/kasy_app_bar.dart +3 -3
- package/templates/firebase/lib/components/kasy_bottom_sheet.dart +1 -1
- package/templates/firebase/lib/components/kasy_button.dart +8 -8
- package/templates/firebase/lib/components/kasy_chip.dart +1 -1
- package/templates/firebase/lib/components/kasy_date_picker.dart +26 -21
- package/templates/firebase/lib/components/kasy_dialog.dart +2 -2
- package/templates/firebase/lib/components/kasy_sidebar.dart +2 -2
- package/templates/firebase/lib/components/kasy_tabs.dart +2 -2
- package/templates/firebase/lib/components/kasy_text_area.dart +37 -5
- package/templates/firebase/lib/components/kasy_text_field.dart +77 -16
- package/templates/firebase/lib/components/kasy_toast.dart +1 -1
- package/templates/firebase/lib/components/kasy_web_header.dart +4 -3
- package/templates/firebase/lib/core/config/features.dart +13 -0
- package/templates/firebase/lib/core/dev_inspector/dev_inspector.dart +21 -0
- package/templates/firebase/lib/core/rating/widgets/rate_banner.dart +1 -1
- package/templates/firebase/lib/core/rating/widgets/review_popup.dart +1 -1
- package/templates/firebase/lib/core/theme/icon_sizes.dart +47 -0
- package/templates/firebase/lib/core/theme/shadows.dart +13 -0
- package/templates/firebase/lib/core/theme/texts.dart +32 -0
- package/templates/firebase/lib/core/theme/theme.dart +2 -0
- package/templates/firebase/lib/core/web_device_preview/web_device_preview.dart +3 -0
- package/templates/firebase/lib/core/web_viewport_scale.dart +23 -4
- package/templates/firebase/lib/core/widgets/update_bottom_sheet.dart +1 -1
- package/templates/firebase/lib/features/ai_chat/ui/widgets/ai_conversation_tile.dart +1 -1
- package/templates/firebase/lib/features/authentication/ui/components/otp_verification.dart +1 -1
- package/templates/firebase/lib/features/authentication/ui/components/phone_input.dart +2 -1
- package/templates/firebase/lib/features/authentication/ui/recover_password_page.dart +3 -1
- package/templates/firebase/lib/features/authentication/ui/signin_page.dart +36 -14
- package/templates/firebase/lib/features/authentication/ui/signup_page.dart +27 -11
- package/templates/firebase/lib/features/authentication/ui/widgets/recover_password_result.dart +1 -1
- package/templates/firebase/lib/features/feedbacks/ui/feedback_page.dart +1 -1
- package/templates/firebase/lib/features/feedbacks/ui/widgets/add_feature_button.dart +1 -1
- package/templates/firebase/lib/features/feedbacks/ui/widgets/feature_card.dart +1 -1
- package/templates/firebase/lib/features/home/home_components_preview_registry.dart +22 -3
- package/templates/firebase/lib/features/home/home_image_grid.dart +1 -1
- package/templates/firebase/lib/features/local_reminders/ui/reminder_page.dart +2 -2
- package/templates/firebase/lib/features/notifications/ui/widgets/empty_notifications.dart +6 -1
- package/templates/firebase/lib/features/notifications/ui/widgets/notification_tile.dart +35 -38
- package/templates/firebase/lib/features/notifications/ui/widgets/permission_request_view.dart +1 -1
- package/templates/firebase/lib/features/onboarding/ui/components/onboarding_loader.dart +1 -1
- package/templates/firebase/lib/features/onboarding/ui/widgets/onboarding_module_mockups.dart +3 -3
- package/templates/firebase/lib/features/onboarding/ui/widgets/selectable_row_tile.dart +1 -1
- package/templates/firebase/lib/features/settings/settings_page.dart +264 -307
- package/templates/firebase/lib/features/settings/ui/components/avatar_component.dart +2 -2
- package/templates/firebase/lib/features/settings/ui/components/edit_name_sheet.dart +115 -0
- package/templates/firebase/lib/features/settings/ui/components/language_switcher.dart +2 -2
- package/templates/firebase/lib/features/settings/ui/widgets/settings_bottom_sheet_option_tile.dart +1 -1
- package/templates/firebase/lib/features/settings/ui/widgets/settings_tile.dart +13 -5
- package/templates/firebase/lib/features/subscriptions/api/stripe_backend_api.dart +12 -2
- package/templates/firebase/lib/features/subscriptions/api/stripe_payment_api.dart +7 -1
- package/templates/firebase/lib/features/subscriptions/providers/premium_page_provider.dart +11 -3
- package/templates/firebase/lib/features/subscriptions/ui/component/paywall_row.dart +1 -1
- package/templates/firebase/lib/features/subscriptions/ui/widgets/comparison_table.dart +1 -1
- package/templates/firebase/lib/features/subscriptions/ui/widgets/feature_line.dart +2 -2
- package/templates/firebase/lib/features/subscriptions/ui/widgets/premium_close_button.dart +1 -1
- package/templates/firebase/lib/features/subscriptions/ui/widgets/premium_feature.dart +1 -1
- package/templates/firebase/lib/features/subscriptions/ui/widgets/selectable_col.dart +3 -3
- package/templates/firebase/lib/features/subscriptions/ui/widgets/selectable_row.dart +1 -1
- package/templates/firebase/lib/i18n/en.i18n.json +8 -0
- package/templates/firebase/lib/i18n/es.i18n.json +8 -0
- package/templates/firebase/lib/i18n/pt.i18n.json +8 -0
- package/templates/firebase/pubspec.yaml +0 -1
- package/templates/firebase/web/stripe_success.html +64 -26
- package/lib/scaffold/backends/api/patch/lib/features/onboarding/ui/animations/movefade_anim.dart +0 -34
- package/lib/scaffold/backends/api/patch/lib/features/onboarding/ui/components/onboarding_att_setup.dart +0 -67
- package/lib/scaffold/backends/api/patch/lib/features/onboarding/ui/widgets/onboarding_radio_question.dart +0 -183
- package/lib/scaffold/backends/supabase/patch/lib/features/onboarding/ui/animations/movefade_anim.dart +0 -34
- package/lib/scaffold/backends/supabase/patch/lib/features/onboarding/ui/components/onboarding_att_setup.dart +0 -67
- package/lib/scaffold/backends/supabase/patch/lib/features/onboarding/ui/widgets/onboarding_radio_question.dart +0 -183
- package/templates/firebase/lib/features/authentication/ui/components/facebook_signin.dart +0 -19
- package/templates/firebase/login-redesign-preview.png +0 -0
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
import 'package:flutter/material.dart';
|
|
2
|
-
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
3
|
-
import 'package:kasy_kit/components/components.dart';
|
|
4
|
-
import 'package:kasy_kit/core/data/api/analytics_api.dart';
|
|
5
|
-
import 'package:kasy_kit/core/data/api/tracking_api.dart';
|
|
6
|
-
import 'package:kasy_kit/core/states/user_state_notifier.dart';
|
|
7
|
-
import 'package:kasy_kit/features/onboarding/ui/widgets/onboarding_background.dart';
|
|
8
|
-
import 'package:kasy_kit/features/onboarding/ui/widgets/onboarding_illustration_scaffold.dart';
|
|
9
|
-
import 'package:kasy_kit/features/onboarding/ui/widgets/onboarding_module_mockups.dart';
|
|
10
|
-
import 'package:kasy_kit/features/subscriptions/repositories/subscription_repository.dart';
|
|
11
|
-
import 'package:kasy_kit/i18n/translations.g.dart';
|
|
12
|
-
import 'package:permission_handler/permission_handler.dart';
|
|
13
|
-
|
|
14
|
-
/// ATT Permission Step
|
|
15
|
-
/// ATT is only available on iOS
|
|
16
|
-
/// This is the consent screen for iOS 14+ that asks the user to allow the app to access the ATT framework
|
|
17
|
-
/// In a few words you need this to get the IDFA (Identifier for Advertisers) which is used for tracking purposes
|
|
18
|
-
/// So you can create better facebook ads, google ads, etc...
|
|
19
|
-
class AttPermissionStep extends ConsumerWidget {
|
|
20
|
-
final String nextRoute;
|
|
21
|
-
|
|
22
|
-
const AttPermissionStep({
|
|
23
|
-
super.key,
|
|
24
|
-
required this.nextRoute,
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
@override
|
|
28
|
-
Widget build(BuildContext context, WidgetRef ref) {
|
|
29
|
-
final translations = Translations.of(context).onboarding.att;
|
|
30
|
-
|
|
31
|
-
return OnboardingBackground(
|
|
32
|
-
child: OnboardingIllustrationScaffold(
|
|
33
|
-
step: 6,
|
|
34
|
-
title: translations.title,
|
|
35
|
-
description: translations.description,
|
|
36
|
-
image: const TrackingPermissionMockup(),
|
|
37
|
-
footerActions: [
|
|
38
|
-
KasyButton(
|
|
39
|
-
label: translations.continue_button,
|
|
40
|
-
expand: true,
|
|
41
|
-
onPressed: () async {
|
|
42
|
-
final Map<Permission, PermissionStatus> permission = await [
|
|
43
|
-
Permission.appTrackingTransparency,
|
|
44
|
-
].request();
|
|
45
|
-
final isGranted =
|
|
46
|
-
permission.values.first == PermissionStatus.granted;
|
|
47
|
-
ref.read(analyticsApiProvider).logEvent('att_request', {
|
|
48
|
-
'granted': isGranted,
|
|
49
|
-
});
|
|
50
|
-
final userId =
|
|
51
|
-
ref.read(userStateNotifierProvider).user.idOrNull;
|
|
52
|
-
if (userId != null) {
|
|
53
|
-
ref
|
|
54
|
-
.read(subscriptionRepositoryProvider)
|
|
55
|
-
.initUser(userId)
|
|
56
|
-
.ignore();
|
|
57
|
-
ref.read(facebookEventApiProvider).initUser(userId).ignore();
|
|
58
|
-
}
|
|
59
|
-
if (!context.mounted) return;
|
|
60
|
-
Navigator.of(context).pushNamed(nextRoute);
|
|
61
|
-
},
|
|
62
|
-
),
|
|
63
|
-
],
|
|
64
|
-
),
|
|
65
|
-
);
|
|
66
|
-
}
|
|
67
|
-
}
|
|
@@ -1,183 +0,0 @@
|
|
|
1
|
-
import 'package:flutter/material.dart';
|
|
2
|
-
import 'package:flutter_animate/flutter_animate.dart';
|
|
3
|
-
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
4
|
-
import 'package:kasy_kit/components/components.dart';
|
|
5
|
-
import 'package:kasy_kit/core/animations/movefade_anim.dart';
|
|
6
|
-
import 'package:kasy_kit/core/theme/theme.dart';
|
|
7
|
-
import 'package:kasy_kit/core/widgets/responsive_layout.dart';
|
|
8
|
-
import 'package:kasy_kit/features/onboarding/ui/widgets/onboarding_background.dart';
|
|
9
|
-
import 'package:kasy_kit/features/onboarding/ui/widgets/onboarding_progress.dart';
|
|
10
|
-
import 'package:kasy_kit/features/onboarding/ui/widgets/onboarding_step_header.dart';
|
|
11
|
-
import 'package:kasy_kit/features/onboarding/ui/widgets/onboarding_sticky_footer.dart';
|
|
12
|
-
import 'package:kasy_kit/features/onboarding/ui/widgets/selectable_row_tile.dart';
|
|
13
|
-
|
|
14
|
-
typedef OptionBuilder = Widget Function(String key, bool selected);
|
|
15
|
-
|
|
16
|
-
typedef ReassuranceBuilder = Widget? Function(String key);
|
|
17
|
-
|
|
18
|
-
typedef OnOptionIdSelected = void Function(String id);
|
|
19
|
-
|
|
20
|
-
typedef OnValidate = void Function(String? key);
|
|
21
|
-
|
|
22
|
-
/// Single choice question with selectable tiles, matching the clean‑premium
|
|
23
|
-
/// onboarding layout (shared header, left‑aligned title, sticky footer).
|
|
24
|
-
class OnboardingRadioQuestion extends ConsumerStatefulWidget {
|
|
25
|
-
final int step;
|
|
26
|
-
final int totalSteps;
|
|
27
|
-
final String title;
|
|
28
|
-
final String description;
|
|
29
|
-
final String btnText;
|
|
30
|
-
final List<String> optionIds;
|
|
31
|
-
final OptionBuilder optionBuilder;
|
|
32
|
-
final ReassuranceBuilder? reassuranceBuilder;
|
|
33
|
-
final OnOptionIdSelected? onOptionIdSelected;
|
|
34
|
-
final OnValidate? onValidate;
|
|
35
|
-
|
|
36
|
-
const OnboardingRadioQuestion({
|
|
37
|
-
super.key,
|
|
38
|
-
required this.title,
|
|
39
|
-
required this.description,
|
|
40
|
-
required this.btnText,
|
|
41
|
-
required this.optionIds,
|
|
42
|
-
required this.optionBuilder,
|
|
43
|
-
required this.step,
|
|
44
|
-
this.totalSteps = kOnboardingSteps,
|
|
45
|
-
this.onOptionIdSelected,
|
|
46
|
-
this.onValidate,
|
|
47
|
-
this.reassuranceBuilder,
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
@override
|
|
51
|
-
ConsumerState<OnboardingRadioQuestion> createState() =>
|
|
52
|
-
_OnboardingRadioQuestionState();
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
class _OnboardingRadioQuestionState
|
|
56
|
-
extends ConsumerState<OnboardingRadioQuestion> {
|
|
57
|
-
String? selectedChoiceId;
|
|
58
|
-
|
|
59
|
-
@override
|
|
60
|
-
Widget build(BuildContext context) {
|
|
61
|
-
const gutter = KasySpacing.lg;
|
|
62
|
-
|
|
63
|
-
final scrollBody = Column(
|
|
64
|
-
crossAxisAlignment: CrossAxisAlignment.start,
|
|
65
|
-
children: [
|
|
66
|
-
OnboardingStepHeader(step: widget.step, totalSteps: widget.totalSteps),
|
|
67
|
-
Padding(
|
|
68
|
-
padding: const EdgeInsets.fromLTRB(gutter, KasySpacing.lg, gutter, 0),
|
|
69
|
-
child: MoveFadeAnim(
|
|
70
|
-
delayInMs: 120,
|
|
71
|
-
child: Text(
|
|
72
|
-
widget.title,
|
|
73
|
-
textAlign: TextAlign.start,
|
|
74
|
-
style: context.textTheme.headlineMedium?.copyWith(
|
|
75
|
-
color: context.colors.onBackground,
|
|
76
|
-
fontSize: 26,
|
|
77
|
-
fontWeight: FontWeight.w700,
|
|
78
|
-
letterSpacing: -0.2,
|
|
79
|
-
height: 1.2,
|
|
80
|
-
),
|
|
81
|
-
),
|
|
82
|
-
),
|
|
83
|
-
),
|
|
84
|
-
Padding(
|
|
85
|
-
padding: const EdgeInsets.fromLTRB(
|
|
86
|
-
gutter,
|
|
87
|
-
KasySpacing.smd,
|
|
88
|
-
gutter,
|
|
89
|
-
KasySpacing.lg,
|
|
90
|
-
),
|
|
91
|
-
child: MoveFadeAnim(
|
|
92
|
-
delayInMs: 200,
|
|
93
|
-
child: Text(
|
|
94
|
-
widget.description,
|
|
95
|
-
textAlign: TextAlign.start,
|
|
96
|
-
style: context.textTheme.bodyLarge?.copyWith(
|
|
97
|
-
color: context.colors.muted,
|
|
98
|
-
height: 1.45,
|
|
99
|
-
),
|
|
100
|
-
),
|
|
101
|
-
),
|
|
102
|
-
),
|
|
103
|
-
Padding(
|
|
104
|
-
padding: const EdgeInsets.symmetric(horizontal: gutter),
|
|
105
|
-
child: OnboardingSelectableRowGroup(
|
|
106
|
-
physics: const NeverScrollableScrollPhysics(),
|
|
107
|
-
options: widget.optionIds.map(
|
|
108
|
-
(e) {
|
|
109
|
-
final index = widget.optionIds.indexOf(e);
|
|
110
|
-
return Animate(
|
|
111
|
-
effects: [
|
|
112
|
-
FadeEffect(
|
|
113
|
-
delay: Duration(milliseconds: 280 + index * 80),
|
|
114
|
-
duration: const Duration(milliseconds: 450),
|
|
115
|
-
curve: Curves.easeOut,
|
|
116
|
-
),
|
|
117
|
-
MoveEffect(
|
|
118
|
-
delay: Duration(milliseconds: 280 + index * 80),
|
|
119
|
-
duration: const Duration(milliseconds: 450),
|
|
120
|
-
curve: Curves.easeOut,
|
|
121
|
-
begin: const Offset(0, 24),
|
|
122
|
-
end: Offset.zero,
|
|
123
|
-
),
|
|
124
|
-
],
|
|
125
|
-
child: widget.optionBuilder(e, e == selectedChoiceId),
|
|
126
|
-
);
|
|
127
|
-
},
|
|
128
|
-
).toList(),
|
|
129
|
-
onSelect: (index, selected) {
|
|
130
|
-
widget.onOptionIdSelected?.call(widget.optionIds[index]);
|
|
131
|
-
setState(() {
|
|
132
|
-
selectedChoiceId = widget.optionIds[index];
|
|
133
|
-
});
|
|
134
|
-
},
|
|
135
|
-
onSelectInfoWidget: widget.optionIds
|
|
136
|
-
.map((el) => widget.reassuranceBuilder?.call(el))
|
|
137
|
-
.toList(),
|
|
138
|
-
),
|
|
139
|
-
),
|
|
140
|
-
const SizedBox(height: KasySpacing.xl),
|
|
141
|
-
],
|
|
142
|
-
);
|
|
143
|
-
|
|
144
|
-
return OnboardingBackground(
|
|
145
|
-
child: Column(
|
|
146
|
-
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
147
|
-
children: [
|
|
148
|
-
Expanded(
|
|
149
|
-
child: SafeArea(
|
|
150
|
-
bottom: false,
|
|
151
|
-
child: SingleChildScrollView(
|
|
152
|
-
padding: const EdgeInsets.only(bottom: KasySpacing.md),
|
|
153
|
-
child: ResponsiveBuilder(
|
|
154
|
-
small: scrollBody,
|
|
155
|
-
medium: Center(
|
|
156
|
-
child: ConstrainedBox(
|
|
157
|
-
constraints: const BoxConstraints(maxWidth: 600),
|
|
158
|
-
child: scrollBody,
|
|
159
|
-
),
|
|
160
|
-
),
|
|
161
|
-
),
|
|
162
|
-
),
|
|
163
|
-
),
|
|
164
|
-
),
|
|
165
|
-
DeviceSizeBuilder(
|
|
166
|
-
builder: (device) => OnboardingStickyFooter(
|
|
167
|
-
maxContentWidth: device == DeviceType.small ? null : 600,
|
|
168
|
-
children: [
|
|
169
|
-
KasyButton(
|
|
170
|
-
label: widget.btnText,
|
|
171
|
-
expand: true,
|
|
172
|
-
onPressed: selectedChoiceId == null
|
|
173
|
-
? null
|
|
174
|
-
: () => widget.onValidate?.call(selectedChoiceId),
|
|
175
|
-
),
|
|
176
|
-
],
|
|
177
|
-
),
|
|
178
|
-
),
|
|
179
|
-
],
|
|
180
|
-
),
|
|
181
|
-
);
|
|
182
|
-
}
|
|
183
|
-
}
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import 'package:flutter/material.dart';
|
|
2
|
-
import 'package:flutter_animate/flutter_animate.dart';
|
|
3
|
-
|
|
4
|
-
class MoveFadeAnim extends StatelessWidget {
|
|
5
|
-
final int? delayInMs;
|
|
6
|
-
final Widget child;
|
|
7
|
-
|
|
8
|
-
const MoveFadeAnim({
|
|
9
|
-
super.key,
|
|
10
|
-
required this.child,
|
|
11
|
-
this.delayInMs,
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
@override
|
|
15
|
-
Widget build(BuildContext context) {
|
|
16
|
-
return Animate(
|
|
17
|
-
effects: [
|
|
18
|
-
FadeEffect(
|
|
19
|
-
delay: Duration(milliseconds: delayInMs ?? 0),
|
|
20
|
-
duration: const Duration(milliseconds: 200),
|
|
21
|
-
curve: Curves.easeIn,
|
|
22
|
-
),
|
|
23
|
-
MoveEffect(
|
|
24
|
-
delay: Duration(milliseconds: delayInMs ?? 0),
|
|
25
|
-
duration: const Duration(milliseconds: 450),
|
|
26
|
-
curve: Curves.easeOut,
|
|
27
|
-
begin: const Offset(0, 50),
|
|
28
|
-
end: Offset.zero,
|
|
29
|
-
),
|
|
30
|
-
],
|
|
31
|
-
child: child,
|
|
32
|
-
);
|
|
33
|
-
}
|
|
34
|
-
}
|
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
import 'package:flutter/material.dart';
|
|
2
|
-
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
3
|
-
import 'package:kasy_kit/components/components.dart';
|
|
4
|
-
import 'package:kasy_kit/core/data/api/analytics_api.dart';
|
|
5
|
-
import 'package:kasy_kit/core/data/api/tracking_api.dart';
|
|
6
|
-
import 'package:kasy_kit/core/states/user_state_notifier.dart';
|
|
7
|
-
import 'package:kasy_kit/features/onboarding/ui/widgets/onboarding_background.dart';
|
|
8
|
-
import 'package:kasy_kit/features/onboarding/ui/widgets/onboarding_illustration_scaffold.dart';
|
|
9
|
-
import 'package:kasy_kit/features/onboarding/ui/widgets/onboarding_module_mockups.dart';
|
|
10
|
-
import 'package:kasy_kit/features/subscriptions/repositories/subscription_repository.dart';
|
|
11
|
-
import 'package:kasy_kit/i18n/translations.g.dart';
|
|
12
|
-
import 'package:permission_handler/permission_handler.dart';
|
|
13
|
-
|
|
14
|
-
/// ATT Permission Step
|
|
15
|
-
/// ATT is only available on iOS
|
|
16
|
-
/// This is the consent screen for iOS 14+ that asks the user to allow the app to access the ATT framework
|
|
17
|
-
/// In a few words you need this to get the IDFA (Identifier for Advertisers) which is used for tracking purposes
|
|
18
|
-
/// So you can create better facebook ads, google ads, etc...
|
|
19
|
-
class AttPermissionStep extends ConsumerWidget {
|
|
20
|
-
final String nextRoute;
|
|
21
|
-
|
|
22
|
-
const AttPermissionStep({
|
|
23
|
-
super.key,
|
|
24
|
-
required this.nextRoute,
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
@override
|
|
28
|
-
Widget build(BuildContext context, WidgetRef ref) {
|
|
29
|
-
final translations = Translations.of(context).onboarding.att;
|
|
30
|
-
|
|
31
|
-
return OnboardingBackground(
|
|
32
|
-
child: OnboardingIllustrationScaffold(
|
|
33
|
-
step: 6,
|
|
34
|
-
title: translations.title,
|
|
35
|
-
description: translations.description,
|
|
36
|
-
image: const TrackingPermissionMockup(),
|
|
37
|
-
footerActions: [
|
|
38
|
-
KasyButton(
|
|
39
|
-
label: translations.continue_button,
|
|
40
|
-
expand: true,
|
|
41
|
-
onPressed: () async {
|
|
42
|
-
final Map<Permission, PermissionStatus> permission = await [
|
|
43
|
-
Permission.appTrackingTransparency,
|
|
44
|
-
].request();
|
|
45
|
-
final isGranted =
|
|
46
|
-
permission.values.first == PermissionStatus.granted;
|
|
47
|
-
ref.read(analyticsApiProvider).logEvent('att_request', {
|
|
48
|
-
'granted': isGranted,
|
|
49
|
-
});
|
|
50
|
-
final userId =
|
|
51
|
-
ref.read(userStateNotifierProvider).user.idOrNull;
|
|
52
|
-
if (userId != null) {
|
|
53
|
-
ref
|
|
54
|
-
.read(subscriptionRepositoryProvider)
|
|
55
|
-
.initUser(userId)
|
|
56
|
-
.ignore();
|
|
57
|
-
ref.read(facebookEventApiProvider).initUser(userId).ignore();
|
|
58
|
-
}
|
|
59
|
-
if (!context.mounted) return;
|
|
60
|
-
Navigator.of(context).pushNamed(nextRoute);
|
|
61
|
-
},
|
|
62
|
-
),
|
|
63
|
-
],
|
|
64
|
-
),
|
|
65
|
-
);
|
|
66
|
-
}
|
|
67
|
-
}
|
|
@@ -1,183 +0,0 @@
|
|
|
1
|
-
import 'package:flutter/material.dart';
|
|
2
|
-
import 'package:flutter_animate/flutter_animate.dart';
|
|
3
|
-
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
4
|
-
import 'package:kasy_kit/components/components.dart';
|
|
5
|
-
import 'package:kasy_kit/core/animations/movefade_anim.dart';
|
|
6
|
-
import 'package:kasy_kit/core/theme/theme.dart';
|
|
7
|
-
import 'package:kasy_kit/core/widgets/responsive_layout.dart';
|
|
8
|
-
import 'package:kasy_kit/features/onboarding/ui/widgets/onboarding_background.dart';
|
|
9
|
-
import 'package:kasy_kit/features/onboarding/ui/widgets/onboarding_progress.dart';
|
|
10
|
-
import 'package:kasy_kit/features/onboarding/ui/widgets/onboarding_step_header.dart';
|
|
11
|
-
import 'package:kasy_kit/features/onboarding/ui/widgets/onboarding_sticky_footer.dart';
|
|
12
|
-
import 'package:kasy_kit/features/onboarding/ui/widgets/selectable_row_tile.dart';
|
|
13
|
-
|
|
14
|
-
typedef OptionBuilder = Widget Function(String key, bool selected);
|
|
15
|
-
|
|
16
|
-
typedef ReassuranceBuilder = Widget? Function(String key);
|
|
17
|
-
|
|
18
|
-
typedef OnOptionIdSelected = void Function(String id);
|
|
19
|
-
|
|
20
|
-
typedef OnValidate = void Function(String? key);
|
|
21
|
-
|
|
22
|
-
/// Single choice question with selectable tiles, matching the clean‑premium
|
|
23
|
-
/// onboarding layout (shared header, left‑aligned title, sticky footer).
|
|
24
|
-
class OnboardingRadioQuestion extends ConsumerStatefulWidget {
|
|
25
|
-
final int step;
|
|
26
|
-
final int totalSteps;
|
|
27
|
-
final String title;
|
|
28
|
-
final String description;
|
|
29
|
-
final String btnText;
|
|
30
|
-
final List<String> optionIds;
|
|
31
|
-
final OptionBuilder optionBuilder;
|
|
32
|
-
final ReassuranceBuilder? reassuranceBuilder;
|
|
33
|
-
final OnOptionIdSelected? onOptionIdSelected;
|
|
34
|
-
final OnValidate? onValidate;
|
|
35
|
-
|
|
36
|
-
const OnboardingRadioQuestion({
|
|
37
|
-
super.key,
|
|
38
|
-
required this.title,
|
|
39
|
-
required this.description,
|
|
40
|
-
required this.btnText,
|
|
41
|
-
required this.optionIds,
|
|
42
|
-
required this.optionBuilder,
|
|
43
|
-
required this.step,
|
|
44
|
-
this.totalSteps = kOnboardingSteps,
|
|
45
|
-
this.onOptionIdSelected,
|
|
46
|
-
this.onValidate,
|
|
47
|
-
this.reassuranceBuilder,
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
@override
|
|
51
|
-
ConsumerState<OnboardingRadioQuestion> createState() =>
|
|
52
|
-
_OnboardingRadioQuestionState();
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
class _OnboardingRadioQuestionState
|
|
56
|
-
extends ConsumerState<OnboardingRadioQuestion> {
|
|
57
|
-
String? selectedChoiceId;
|
|
58
|
-
|
|
59
|
-
@override
|
|
60
|
-
Widget build(BuildContext context) {
|
|
61
|
-
const gutter = KasySpacing.lg;
|
|
62
|
-
|
|
63
|
-
final scrollBody = Column(
|
|
64
|
-
crossAxisAlignment: CrossAxisAlignment.start,
|
|
65
|
-
children: [
|
|
66
|
-
OnboardingStepHeader(step: widget.step, totalSteps: widget.totalSteps),
|
|
67
|
-
Padding(
|
|
68
|
-
padding: const EdgeInsets.fromLTRB(gutter, KasySpacing.lg, gutter, 0),
|
|
69
|
-
child: MoveFadeAnim(
|
|
70
|
-
delayInMs: 120,
|
|
71
|
-
child: Text(
|
|
72
|
-
widget.title,
|
|
73
|
-
textAlign: TextAlign.start,
|
|
74
|
-
style: context.textTheme.headlineMedium?.copyWith(
|
|
75
|
-
color: context.colors.onBackground,
|
|
76
|
-
fontSize: 26,
|
|
77
|
-
fontWeight: FontWeight.w700,
|
|
78
|
-
letterSpacing: -0.2,
|
|
79
|
-
height: 1.2,
|
|
80
|
-
),
|
|
81
|
-
),
|
|
82
|
-
),
|
|
83
|
-
),
|
|
84
|
-
Padding(
|
|
85
|
-
padding: const EdgeInsets.fromLTRB(
|
|
86
|
-
gutter,
|
|
87
|
-
KasySpacing.smd,
|
|
88
|
-
gutter,
|
|
89
|
-
KasySpacing.lg,
|
|
90
|
-
),
|
|
91
|
-
child: MoveFadeAnim(
|
|
92
|
-
delayInMs: 200,
|
|
93
|
-
child: Text(
|
|
94
|
-
widget.description,
|
|
95
|
-
textAlign: TextAlign.start,
|
|
96
|
-
style: context.textTheme.bodyLarge?.copyWith(
|
|
97
|
-
color: context.colors.muted,
|
|
98
|
-
height: 1.45,
|
|
99
|
-
),
|
|
100
|
-
),
|
|
101
|
-
),
|
|
102
|
-
),
|
|
103
|
-
Padding(
|
|
104
|
-
padding: const EdgeInsets.symmetric(horizontal: gutter),
|
|
105
|
-
child: OnboardingSelectableRowGroup(
|
|
106
|
-
physics: const NeverScrollableScrollPhysics(),
|
|
107
|
-
options: widget.optionIds.map(
|
|
108
|
-
(e) {
|
|
109
|
-
final index = widget.optionIds.indexOf(e);
|
|
110
|
-
return Animate(
|
|
111
|
-
effects: [
|
|
112
|
-
FadeEffect(
|
|
113
|
-
delay: Duration(milliseconds: 280 + index * 80),
|
|
114
|
-
duration: const Duration(milliseconds: 450),
|
|
115
|
-
curve: Curves.easeOut,
|
|
116
|
-
),
|
|
117
|
-
MoveEffect(
|
|
118
|
-
delay: Duration(milliseconds: 280 + index * 80),
|
|
119
|
-
duration: const Duration(milliseconds: 450),
|
|
120
|
-
curve: Curves.easeOut,
|
|
121
|
-
begin: const Offset(0, 24),
|
|
122
|
-
end: Offset.zero,
|
|
123
|
-
),
|
|
124
|
-
],
|
|
125
|
-
child: widget.optionBuilder(e, e == selectedChoiceId),
|
|
126
|
-
);
|
|
127
|
-
},
|
|
128
|
-
).toList(),
|
|
129
|
-
onSelect: (index, selected) {
|
|
130
|
-
widget.onOptionIdSelected?.call(widget.optionIds[index]);
|
|
131
|
-
setState(() {
|
|
132
|
-
selectedChoiceId = widget.optionIds[index];
|
|
133
|
-
});
|
|
134
|
-
},
|
|
135
|
-
onSelectInfoWidget: widget.optionIds
|
|
136
|
-
.map((el) => widget.reassuranceBuilder?.call(el))
|
|
137
|
-
.toList(),
|
|
138
|
-
),
|
|
139
|
-
),
|
|
140
|
-
const SizedBox(height: KasySpacing.xl),
|
|
141
|
-
],
|
|
142
|
-
);
|
|
143
|
-
|
|
144
|
-
return OnboardingBackground(
|
|
145
|
-
child: Column(
|
|
146
|
-
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
147
|
-
children: [
|
|
148
|
-
Expanded(
|
|
149
|
-
child: SafeArea(
|
|
150
|
-
bottom: false,
|
|
151
|
-
child: SingleChildScrollView(
|
|
152
|
-
padding: const EdgeInsets.only(bottom: KasySpacing.md),
|
|
153
|
-
child: ResponsiveBuilder(
|
|
154
|
-
small: scrollBody,
|
|
155
|
-
medium: Center(
|
|
156
|
-
child: ConstrainedBox(
|
|
157
|
-
constraints: const BoxConstraints(maxWidth: 600),
|
|
158
|
-
child: scrollBody,
|
|
159
|
-
),
|
|
160
|
-
),
|
|
161
|
-
),
|
|
162
|
-
),
|
|
163
|
-
),
|
|
164
|
-
),
|
|
165
|
-
DeviceSizeBuilder(
|
|
166
|
-
builder: (device) => OnboardingStickyFooter(
|
|
167
|
-
maxContentWidth: device == DeviceType.small ? null : 600,
|
|
168
|
-
children: [
|
|
169
|
-
KasyButton(
|
|
170
|
-
label: widget.btnText,
|
|
171
|
-
expand: true,
|
|
172
|
-
onPressed: selectedChoiceId == null
|
|
173
|
-
? null
|
|
174
|
-
: () => widget.onValidate?.call(selectedChoiceId),
|
|
175
|
-
),
|
|
176
|
-
],
|
|
177
|
-
),
|
|
178
|
-
),
|
|
179
|
-
],
|
|
180
|
-
),
|
|
181
|
-
);
|
|
182
|
-
}
|
|
183
|
-
}
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import 'package:flutter/material.dart';
|
|
2
|
-
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
3
|
-
import 'package:kasy_kit/features/authentication/providers/models/signin_state.dart';
|
|
4
|
-
import 'package:kasy_kit/features/authentication/providers/signin_state_provider.dart';
|
|
5
|
-
import 'package:kasy_kit/features/authentication/ui/widgets/round_signin.dart';
|
|
6
|
-
|
|
7
|
-
class FacebookSigninComponent extends ConsumerWidget {
|
|
8
|
-
const FacebookSigninComponent({super.key});
|
|
9
|
-
|
|
10
|
-
@override
|
|
11
|
-
Widget build(BuildContext context, WidgetRef ref) {
|
|
12
|
-
// watch keeps the provider alive during async sign-in
|
|
13
|
-
final state = ref.watch(signinStateProvider);
|
|
14
|
-
final isSending = state is SigninStateSending;
|
|
15
|
-
return SocialSigninButton.facebook(
|
|
16
|
-
() { if (!isSending) ref.read(signinStateProvider.notifier).signinWithFacebook(); },
|
|
17
|
-
);
|
|
18
|
-
}
|
|
19
|
-
}
|
|
Binary file
|