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,9 +1,11 @@
|
|
|
1
1
|
import 'dart:ui';
|
|
2
2
|
|
|
3
|
+
import 'package:flutter/foundation.dart';
|
|
3
4
|
import 'package:flutter/material.dart';
|
|
4
5
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
5
6
|
import 'package:go_router/go_router.dart';
|
|
6
7
|
import 'package:kasy_kit/components/components.dart';
|
|
8
|
+
import 'package:kasy_kit/core/config/features.dart';
|
|
7
9
|
import 'package:kasy_kit/core/data/models/user.dart';
|
|
8
10
|
import 'package:kasy_kit/core/haptics/kasy_haptics.dart';
|
|
9
11
|
import 'package:kasy_kit/core/states/user_state_notifier.dart';
|
|
@@ -64,6 +66,7 @@ class SigninPage extends ConsumerWidget {
|
|
|
64
66
|
subtitle: t.auth.signin.subtitle,
|
|
65
67
|
children: [
|
|
66
68
|
KasyTextField(
|
|
69
|
+
variant: KasyTextFieldVariant.flat,
|
|
67
70
|
key: const Key('email_input'),
|
|
68
71
|
onChanged: (value) => ref
|
|
69
72
|
.read(signinStateProvider.notifier)
|
|
@@ -83,6 +86,7 @@ class SigninPage extends ConsumerWidget {
|
|
|
83
86
|
),
|
|
84
87
|
const SizedBox(height: _authFieldSpacing),
|
|
85
88
|
KasyTextField(
|
|
89
|
+
variant: KasyTextFieldVariant.flat,
|
|
86
90
|
key: const Key('password_input'),
|
|
87
91
|
onChanged: (newValue) => ref
|
|
88
92
|
.read(signinStateProvider.notifier)
|
|
@@ -98,10 +102,15 @@ class SigninPage extends ConsumerWidget {
|
|
|
98
102
|
FocusScope.of(context).unfocus();
|
|
99
103
|
ref.read(signinStateProvider.notifier).signin();
|
|
100
104
|
},
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
105
|
+
// Secondary link: intentionally NOT a Tab stop, so
|
|
106
|
+
// Tab/next flows email → password → submit → social
|
|
107
|
+
// buttons. Still tappable by mouse/touch and announced
|
|
108
|
+
// to screen readers as a button.
|
|
109
|
+
labelTrailing: Semantics(
|
|
110
|
+
button: true,
|
|
111
|
+
label: t.auth.signin.forgot_password,
|
|
104
112
|
child: GestureDetector(
|
|
113
|
+
behavior: HitTestBehavior.opaque,
|
|
105
114
|
onTap: () => context.push('/recover_password'),
|
|
106
115
|
child: Text(
|
|
107
116
|
t.auth.signin.forgot_password,
|
|
@@ -195,7 +204,9 @@ class _SignupPrompt extends StatelessWidget {
|
|
|
195
204
|
KasyPressableDepth(
|
|
196
205
|
semanticLabel: t.auth.signin.signup_link,
|
|
197
206
|
onPressed: () => context.pushReplacement('/signup'),
|
|
198
|
-
|
|
207
|
+
// Secondary link: kept out of Tab traversal (focusable defaults to
|
|
208
|
+
// false) so keyboard/next jumps straight to the social sign-in
|
|
209
|
+
// buttons (the most-used path).
|
|
199
210
|
child: Text(
|
|
200
211
|
t.auth.signin.signup_link,
|
|
201
212
|
style: context.textTheme.bodyMedium?.copyWith(
|
|
@@ -218,6 +229,14 @@ class _SocialSigninRow extends ConsumerWidget {
|
|
|
218
229
|
Widget build(BuildContext context, WidgetRef ref) {
|
|
219
230
|
final state = ref.watch(signinStateProvider);
|
|
220
231
|
final isSending = state is SigninStateSending;
|
|
232
|
+
// Apple sign-in is reliable only on Apple-native platforms (iOS/macOS). On web
|
|
233
|
+
// it needs a paid Apple Service ID + manual console setup (see auth docs), gated
|
|
234
|
+
// by withAppleWebSignin (off by default). On Android it needs the same Service ID
|
|
235
|
+
// and the Supabase/API native flow throws, so Apple is hidden there.
|
|
236
|
+
final bool showApple = kIsWeb
|
|
237
|
+
? withAppleWebSignin
|
|
238
|
+
: (defaultTargetPlatform == TargetPlatform.iOS ||
|
|
239
|
+
defaultTargetPlatform == TargetPlatform.macOS);
|
|
221
240
|
return Row(
|
|
222
241
|
children: [
|
|
223
242
|
Expanded(
|
|
@@ -230,17 +249,20 @@ class _SocialSigninRow extends ConsumerWidget {
|
|
|
230
249
|
ref.read(signinStateProvider.notifier).signinWithGoogle(),
|
|
231
250
|
),
|
|
232
251
|
),
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
252
|
+
if (showApple) ...[
|
|
253
|
+
const SizedBox(width: KasySpacing.sm),
|
|
254
|
+
Expanded(
|
|
255
|
+
child: _SocialSigninTile(
|
|
256
|
+
label: t.auth.signin.apple,
|
|
257
|
+
icon: Image.asset('assets/icons/apple.png', width: 20, height: 20),
|
|
258
|
+
onPressed: isSending
|
|
259
|
+
? null
|
|
260
|
+
: () => ref
|
|
261
|
+
.read(signinStateProvider.notifier)
|
|
262
|
+
.signinWithApple(),
|
|
263
|
+
),
|
|
242
264
|
),
|
|
243
|
-
|
|
265
|
+
],
|
|
244
266
|
const SizedBox(width: KasySpacing.sm),
|
|
245
267
|
Expanded(
|
|
246
268
|
child: _SocialSigninTile(
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import 'dart:ui';
|
|
2
2
|
|
|
3
|
+
import 'package:flutter/foundation.dart';
|
|
3
4
|
import 'package:flutter/material.dart';
|
|
4
5
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
5
6
|
import 'package:go_router/go_router.dart';
|
|
6
7
|
import 'package:kasy_kit/components/components.dart';
|
|
8
|
+
import 'package:kasy_kit/core/config/features.dart';
|
|
7
9
|
import 'package:kasy_kit/core/data/models/user.dart';
|
|
8
10
|
import 'package:kasy_kit/core/haptics/kasy_haptics.dart';
|
|
9
11
|
import 'package:kasy_kit/core/states/user_state_notifier.dart';
|
|
@@ -60,6 +62,7 @@ class SignupPage extends ConsumerWidget {
|
|
|
60
62
|
subtitle: t.auth.signup.subtitle,
|
|
61
63
|
children: [
|
|
62
64
|
KasyTextField(
|
|
65
|
+
variant: KasyTextFieldVariant.flat,
|
|
63
66
|
key: const Key('email_input'),
|
|
64
67
|
onChanged: (value) => ref
|
|
65
68
|
.read(signupStateProvider.notifier)
|
|
@@ -79,6 +82,7 @@ class SignupPage extends ConsumerWidget {
|
|
|
79
82
|
),
|
|
80
83
|
const SizedBox(height: _authFieldSpacing),
|
|
81
84
|
KasyTextField(
|
|
85
|
+
variant: KasyTextFieldVariant.flat,
|
|
82
86
|
key: const Key('password_input'),
|
|
83
87
|
onChanged: (newValue) => ref
|
|
84
88
|
.read(signupStateProvider.notifier)
|
|
@@ -180,7 +184,8 @@ class _SigninPrompt extends StatelessWidget {
|
|
|
180
184
|
KasyPressableDepth(
|
|
181
185
|
semanticLabel: t.auth.signup.signin_link,
|
|
182
186
|
onPressed: () => context.pushReplacement('/signin'),
|
|
183
|
-
|
|
187
|
+
// Secondary link: kept out of Tab traversal (focusable defaults to
|
|
188
|
+
// false) so keyboard/next jumps straight to the social buttons.
|
|
184
189
|
child: Text(
|
|
185
190
|
t.auth.signup.signin_link,
|
|
186
191
|
style: context.textTheme.bodyMedium?.copyWith(
|
|
@@ -203,6 +208,14 @@ class _SocialSignupRow extends ConsumerWidget {
|
|
|
203
208
|
Widget build(BuildContext context, WidgetRef ref) {
|
|
204
209
|
final state = ref.watch(signinStateProvider);
|
|
205
210
|
final isSending = state is SigninStateSending;
|
|
211
|
+
// Apple sign-in is reliable only on Apple-native platforms (iOS/macOS). On web
|
|
212
|
+
// it needs a paid Apple Service ID + manual console setup (see auth docs), gated
|
|
213
|
+
// by withAppleWebSignin (off by default). On Android it needs the same Service ID
|
|
214
|
+
// and the Supabase/API native flow throws, so Apple is hidden there.
|
|
215
|
+
final bool showApple = kIsWeb
|
|
216
|
+
? withAppleWebSignin
|
|
217
|
+
: (defaultTargetPlatform == TargetPlatform.iOS ||
|
|
218
|
+
defaultTargetPlatform == TargetPlatform.macOS);
|
|
206
219
|
return Row(
|
|
207
220
|
children: [
|
|
208
221
|
Expanded(
|
|
@@ -215,17 +228,20 @@ class _SocialSignupRow extends ConsumerWidget {
|
|
|
215
228
|
ref.read(signinStateProvider.notifier).signinWithGoogle(),
|
|
216
229
|
),
|
|
217
230
|
),
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
231
|
+
if (showApple) ...[
|
|
232
|
+
const SizedBox(width: KasySpacing.sm),
|
|
233
|
+
Expanded(
|
|
234
|
+
child: _SocialSignupTile(
|
|
235
|
+
label: t.auth.signin.apple,
|
|
236
|
+
icon: Image.asset('assets/icons/apple.png', width: 20, height: 20),
|
|
237
|
+
onPressed: isSending
|
|
238
|
+
? null
|
|
239
|
+
: () => ref
|
|
240
|
+
.read(signinStateProvider.notifier)
|
|
241
|
+
.signinWithApple(),
|
|
242
|
+
),
|
|
227
243
|
),
|
|
228
|
-
|
|
244
|
+
],
|
|
229
245
|
const SizedBox(width: KasySpacing.sm),
|
|
230
246
|
Expanded(
|
|
231
247
|
child: _SocialSignupTile(
|
|
@@ -167,7 +167,7 @@ class _AddChipButton extends StatelessWidget {
|
|
|
167
167
|
child: Row(
|
|
168
168
|
mainAxisSize: MainAxisSize.min,
|
|
169
169
|
children: [
|
|
170
|
-
Icon(KasyIcons.add, size:
|
|
170
|
+
Icon(KasyIcons.add, size: KasyIconSize.xs, color: context.colors.primary),
|
|
171
171
|
const SizedBox(width: KasySpacing.xs),
|
|
172
172
|
Text(
|
|
173
173
|
label,
|
|
@@ -2714,18 +2714,26 @@ Widget _buildTextFieldVariantsVariant(BuildContext context) {
|
|
|
2714
2714
|
children: [
|
|
2715
2715
|
KasyTextField(
|
|
2716
2716
|
label: 'Primary Variant',
|
|
2717
|
-
hint: 'Primary style
|
|
2717
|
+
hint: 'Primary style text field',
|
|
2718
2718
|
description: 'Default variant with primary styling',
|
|
2719
2719
|
contentType: KasyTextFieldContentType.email,
|
|
2720
2720
|
),
|
|
2721
2721
|
SizedBox(height: KasySpacing.lg),
|
|
2722
2722
|
KasyTextField(
|
|
2723
2723
|
label: 'Secondary Variant',
|
|
2724
|
-
hint: 'Secondary style
|
|
2724
|
+
hint: 'Secondary style text field',
|
|
2725
2725
|
description: 'Secondary variant for surfaces',
|
|
2726
2726
|
variant: KasyTextFieldVariant.secondary,
|
|
2727
2727
|
contentType: KasyTextFieldContentType.email,
|
|
2728
2728
|
),
|
|
2729
|
+
SizedBox(height: KasySpacing.lg),
|
|
2730
|
+
KasyTextField(
|
|
2731
|
+
label: 'Flat Variant',
|
|
2732
|
+
hint: 'Flat style text field',
|
|
2733
|
+
description: 'Primary fill, hairline border, no shadow',
|
|
2734
|
+
variant: KasyTextFieldVariant.flat,
|
|
2735
|
+
contentType: KasyTextFieldContentType.email,
|
|
2736
|
+
),
|
|
2729
2737
|
],
|
|
2730
2738
|
);
|
|
2731
2739
|
}
|
|
@@ -2738,7 +2746,7 @@ Widget _buildTextFieldStates(BuildContext context) {
|
|
|
2738
2746
|
KasyTextField(
|
|
2739
2747
|
label: 'Default State',
|
|
2740
2748
|
hint: 'Enter your email',
|
|
2741
|
-
description: 'Normal
|
|
2749
|
+
description: 'Normal text field state',
|
|
2742
2750
|
contentType: KasyTextFieldContentType.email,
|
|
2743
2751
|
),
|
|
2744
2752
|
SizedBox(height: KasySpacing.lg),
|
|
@@ -9013,6 +9021,7 @@ class _DatePickerVariantsPreview extends StatefulWidget {
|
|
|
9013
9021
|
class _DatePickerVariantsPreviewState
|
|
9014
9022
|
extends State<_DatePickerVariantsPreview> {
|
|
9015
9023
|
DateTime? _filledDate;
|
|
9024
|
+
DateTime? _flatDate;
|
|
9016
9025
|
DateTime? _noBarrierDate;
|
|
9017
9026
|
DateTime? _noSuffixDate;
|
|
9018
9027
|
DateTime? _noFocusDate;
|
|
@@ -9035,6 +9044,16 @@ class _DatePickerVariantsPreviewState
|
|
|
9035
9044
|
locale: locale,
|
|
9036
9045
|
),
|
|
9037
9046
|
const SizedBox(height: KasySpacing.lg),
|
|
9047
|
+
// Flat — surface fill + hairline border, no shadow (KasyTextField.flat).
|
|
9048
|
+
KasyDatePicker(
|
|
9049
|
+
label: 'Flat (no shadow)',
|
|
9050
|
+
placeholder: 'Choose a date',
|
|
9051
|
+
value: _flatDate,
|
|
9052
|
+
onChanged: (d) => setState(() => _flatDate = d),
|
|
9053
|
+
variant: KasyTextFieldVariant.flat,
|
|
9054
|
+
locale: locale,
|
|
9055
|
+
),
|
|
9056
|
+
const SizedBox(height: KasySpacing.lg),
|
|
9038
9057
|
// Popover without the dimmed backdrop — feels lighter / tooltip-like.
|
|
9039
9058
|
KasyDatePicker(
|
|
9040
9059
|
label: 'No backdrop',
|
|
@@ -194,7 +194,7 @@ class _TimeTile extends StatelessWidget {
|
|
|
194
194
|
const Spacer(),
|
|
195
195
|
Icon(
|
|
196
196
|
KasyIcons.arrowForwardIos,
|
|
197
|
-
size:
|
|
197
|
+
size: KasyIconSize.xs,
|
|
198
198
|
color: context.colors.muted,
|
|
199
199
|
),
|
|
200
200
|
],
|
|
@@ -299,7 +299,7 @@ class _DateTile extends StatelessWidget {
|
|
|
299
299
|
const Spacer(),
|
|
300
300
|
Icon(
|
|
301
301
|
KasyIcons.arrowForwardIos,
|
|
302
|
-
size:
|
|
302
|
+
size: KasyIconSize.xs,
|
|
303
303
|
color: context.colors.muted,
|
|
304
304
|
),
|
|
305
305
|
],
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import 'package:flutter/foundation.dart' show kIsWeb;
|
|
1
2
|
import 'package:flutter/material.dart';
|
|
2
3
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
3
4
|
import 'package:kasy_kit/components/components.dart';
|
|
@@ -52,7 +53,7 @@ class _EmptyNotificationsState extends ConsumerState<EmptyNotifications> {
|
|
|
52
53
|
),
|
|
53
54
|
child: Icon(
|
|
54
55
|
KasyIcons.notificationOff,
|
|
55
|
-
size:
|
|
56
|
+
size: KasyIconSize.display,
|
|
56
57
|
color: context.colors.primary.withValues(alpha: 0.6),
|
|
57
58
|
),
|
|
58
59
|
),
|
|
@@ -79,6 +80,10 @@ class _EmptyNotificationsState extends ConsumerState<EmptyNotifications> {
|
|
|
79
80
|
builder: (context, snapshot) {
|
|
80
81
|
final permission = snapshot.data;
|
|
81
82
|
if (permission == null) return const SizedBox.shrink();
|
|
83
|
+
// Web has no usable notification-permission prompt (askPermission is a
|
|
84
|
+
// no-op on web and permission_handler has no web support), so the CTA
|
|
85
|
+
// would be a dead button. Show the empty state without it.
|
|
86
|
+
if (kIsWeb) return const SizedBox.shrink();
|
|
82
87
|
if (permission is NotificationPermissionGranted) {
|
|
83
88
|
return const SizedBox.shrink();
|
|
84
89
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import 'package:better_skeleton/better_skeleton.dart';
|
|
2
1
|
import 'package:flutter/material.dart';
|
|
3
2
|
import 'package:jiffy/jiffy.dart';
|
|
3
|
+
import 'package:kasy_kit/components/kasy_skeleton.dart';
|
|
4
4
|
import 'package:kasy_kit/core/theme/theme.dart';
|
|
5
5
|
import 'package:kasy_kit/core/widgets/kasy_focus_ring.dart';
|
|
6
6
|
import 'package:kasy_kit/features/notifications/providers/models/notification.dart'
|
|
@@ -307,54 +307,51 @@ class TileNotificationIcon extends StatelessWidget {
|
|
|
307
307
|
),
|
|
308
308
|
child: Icon(
|
|
309
309
|
active ? KasyIcons.notificationActive : KasyIcons.notification,
|
|
310
|
-
size:
|
|
310
|
+
size: KasyIconSize.lg,
|
|
311
311
|
color: color,
|
|
312
312
|
),
|
|
313
313
|
);
|
|
314
314
|
}
|
|
315
315
|
}
|
|
316
316
|
|
|
317
|
-
///
|
|
318
|
-
|
|
317
|
+
/// Skeleton placeholder for [NotificationTile], built from the design-system
|
|
318
|
+
/// [KasySkeleton] bones so the loading shimmer matches the kit everywhere and
|
|
319
|
+
/// mirrors the real tile's layout (leading badge + title / body / date lines).
|
|
320
|
+
class NotificationSkeletonTile extends StatelessWidget {
|
|
319
321
|
const NotificationSkeletonTile({super.key});
|
|
320
322
|
|
|
321
|
-
@override
|
|
322
|
-
State<NotificationSkeletonTile> createState() =>
|
|
323
|
-
_NotificationSkeletonTileState();
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
class _NotificationSkeletonTileState extends State<NotificationSkeletonTile>
|
|
327
|
-
with SingleTickerProviderStateMixin {
|
|
328
|
-
late final AnimationController animationController;
|
|
329
|
-
|
|
330
|
-
@override
|
|
331
|
-
void initState() {
|
|
332
|
-
super.initState();
|
|
333
|
-
animationController = AnimationController(
|
|
334
|
-
vsync: this,
|
|
335
|
-
duration: const Duration(milliseconds: 1000),
|
|
336
|
-
)..repeat();
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
@override
|
|
340
|
-
void deactivate() {
|
|
341
|
-
animationController.dispose();
|
|
342
|
-
super.deactivate();
|
|
343
|
-
}
|
|
344
|
-
|
|
345
323
|
@override
|
|
346
324
|
Widget build(BuildContext context) {
|
|
347
|
-
return Padding(
|
|
348
|
-
padding:
|
|
349
|
-
child:
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
325
|
+
return const Padding(
|
|
326
|
+
padding: EdgeInsets.only(top: KasySpacing.sm),
|
|
327
|
+
child: KasySkeletonGroup(
|
|
328
|
+
child: Padding(
|
|
329
|
+
padding: EdgeInsets.all(KasySpacing.md),
|
|
330
|
+
child: Row(
|
|
331
|
+
crossAxisAlignment: CrossAxisAlignment.start,
|
|
332
|
+
children: [
|
|
333
|
+
KasySkeleton(
|
|
334
|
+
width: 40,
|
|
335
|
+
height: 40,
|
|
336
|
+
borderRadius: BorderRadius.all(Radius.circular(10)),
|
|
337
|
+
),
|
|
338
|
+
SizedBox(width: KasySpacing.smd),
|
|
339
|
+
Expanded(
|
|
340
|
+
child: Column(
|
|
341
|
+
crossAxisAlignment: CrossAxisAlignment.start,
|
|
342
|
+
children: [
|
|
343
|
+
KasySkeleton(width: 150, height: 13),
|
|
344
|
+
SizedBox(height: KasySpacing.sm),
|
|
345
|
+
KasySkeleton(width: double.infinity, height: 11),
|
|
346
|
+
SizedBox(height: KasySpacing.xs),
|
|
347
|
+
KasySkeleton(width: 210, height: 11),
|
|
348
|
+
SizedBox(height: KasySpacing.smd),
|
|
349
|
+
KasySkeleton(width: 70, height: 10),
|
|
350
|
+
],
|
|
351
|
+
),
|
|
352
|
+
),
|
|
353
|
+
],
|
|
356
354
|
),
|
|
357
|
-
padding: const EdgeInsets.all(KasySpacing.md),
|
|
358
355
|
),
|
|
359
356
|
),
|
|
360
357
|
);
|
package/templates/firebase/lib/features/notifications/ui/widgets/permission_request_view.dart
CHANGED
|
@@ -76,7 +76,7 @@ class PermissionRequestView extends StatelessWidget {
|
|
|
76
76
|
child: Center(
|
|
77
77
|
child:
|
|
78
78
|
illustration ??
|
|
79
|
-
KasyBrandBadge(icon: icon, size: 76, glyphSize:
|
|
79
|
+
KasyBrandBadge(icon: icon, size: 76, glyphSize: KasyIconSize.display),
|
|
80
80
|
),
|
|
81
81
|
),
|
|
82
82
|
const SizedBox(height: KasySpacing.xl),
|
|
@@ -102,7 +102,7 @@ class _PulsingOrb extends StatelessWidget {
|
|
|
102
102
|
),
|
|
103
103
|
],
|
|
104
104
|
),
|
|
105
|
-
child: Icon(KasyIcons.flash, color: colors.onPrimary, size:
|
|
105
|
+
child: Icon(KasyIcons.flash, color: colors.onPrimary, size: KasyIconSize.display),
|
|
106
106
|
)
|
|
107
107
|
.animate(onPlay: (c) => c.repeat(reverse: true))
|
|
108
108
|
.scaleXY(
|
package/templates/firebase/lib/features/onboarding/ui/widgets/onboarding_module_mockups.dart
CHANGED
|
@@ -283,7 +283,7 @@ class _PlanRow extends StatelessWidget {
|
|
|
283
283
|
),
|
|
284
284
|
),
|
|
285
285
|
child: selected
|
|
286
|
-
? Icon(KasyIcons.check, size:
|
|
286
|
+
? Icon(KasyIcons.check, size: KasyIconSize.xxs, color: colors.onPrimary)
|
|
287
287
|
: null,
|
|
288
288
|
),
|
|
289
289
|
const SizedBox(width: KasySpacing.smd),
|
|
@@ -421,7 +421,7 @@ class _FakeField extends StatelessWidget {
|
|
|
421
421
|
),
|
|
422
422
|
child: Row(
|
|
423
423
|
children: [
|
|
424
|
-
Icon(icon, size:
|
|
424
|
+
Icon(icon, size: KasyIconSize.sm, color: colors.muted),
|
|
425
425
|
const SizedBox(width: KasySpacing.sm),
|
|
426
426
|
Text(
|
|
427
427
|
hint,
|
|
@@ -452,7 +452,7 @@ class _SocialChip extends StatelessWidget {
|
|
|
452
452
|
borderRadius: KasyRadius.mdBorderRadius,
|
|
453
453
|
border: Border.all(color: colors.outline),
|
|
454
454
|
),
|
|
455
|
-
child: Icon(icon, size:
|
|
455
|
+
child: Icon(icon, size: KasyIconSize.md, color: colors.onSurface),
|
|
456
456
|
);
|
|
457
457
|
}
|
|
458
458
|
}
|
|
@@ -391,7 +391,7 @@ class RoundRadioBox extends StatelessWidget {
|
|
|
391
391
|
opacity: iconOpacity,
|
|
392
392
|
child: Transform.scale(
|
|
393
393
|
scale: iconSize,
|
|
394
|
-
child: Icon(icon, color: context.colors.onPrimary, size:
|
|
394
|
+
child: Icon(icon, color: context.colors.onPrimary, size: KasyIconSize.sm),
|
|
395
395
|
),
|
|
396
396
|
)
|
|
397
397
|
: const SizedBox.shrink(),
|