kasy-cli 1.10.0 → 1.13.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.
Files changed (141) hide show
  1. package/bin/kasy.js +25 -4
  2. package/lib/commands/check.js +40 -50
  3. package/lib/commands/deploy.js +25 -25
  4. package/lib/commands/splash.js +220 -0
  5. package/lib/scaffold/CHANGELOG.json +9 -0
  6. package/lib/scaffold/backends/api/patch/lib/main.dart +29 -10
  7. package/lib/scaffold/backends/supabase/patch/lib/main.dart +29 -10
  8. package/lib/scaffold/features/README.md +15 -139
  9. package/lib/scaffold/shared/generator-utils.js +16 -15
  10. package/lib/utils/i18n.js +292 -43
  11. package/package.json +2 -2
  12. package/templates/firebase/android/app/src/main/res/drawable-hdpi/android12splash.png +0 -0
  13. package/templates/firebase/android/app/src/main/res/drawable-hdpi/splash.png +0 -0
  14. package/templates/firebase/android/app/src/main/res/drawable-mdpi/android12splash.png +0 -0
  15. package/templates/firebase/android/app/src/main/res/drawable-mdpi/splash.png +0 -0
  16. package/templates/firebase/android/app/src/main/res/drawable-night/background.png +0 -0
  17. package/templates/firebase/android/app/src/main/res/drawable-night/launch_background.xml +9 -0
  18. package/templates/firebase/android/app/src/main/res/drawable-night-hdpi/android12splash.png +0 -0
  19. package/templates/firebase/android/app/src/main/res/drawable-night-hdpi/splash.png +0 -0
  20. package/templates/firebase/android/app/src/main/res/drawable-night-mdpi/android12splash.png +0 -0
  21. package/templates/firebase/android/app/src/main/res/drawable-night-mdpi/splash.png +0 -0
  22. package/templates/firebase/android/app/src/main/res/drawable-night-v21/background.png +0 -0
  23. package/templates/firebase/android/app/src/main/res/drawable-night-v21/launch_background.xml +9 -0
  24. package/templates/firebase/android/app/src/main/res/drawable-night-xhdpi/android12splash.png +0 -0
  25. package/templates/firebase/android/app/src/main/res/drawable-night-xhdpi/splash.png +0 -0
  26. package/templates/firebase/android/app/src/main/res/drawable-night-xxhdpi/android12splash.png +0 -0
  27. package/templates/firebase/android/app/src/main/res/drawable-night-xxhdpi/splash.png +0 -0
  28. package/templates/firebase/android/app/src/main/res/drawable-night-xxxhdpi/android12splash.png +0 -0
  29. package/templates/firebase/android/app/src/main/res/drawable-night-xxxhdpi/splash.png +0 -0
  30. package/templates/firebase/android/app/src/main/res/drawable-xhdpi/android12splash.png +0 -0
  31. package/templates/firebase/android/app/src/main/res/drawable-xhdpi/splash.png +0 -0
  32. package/templates/firebase/android/app/src/main/res/drawable-xxhdpi/android12splash.png +0 -0
  33. package/templates/firebase/android/app/src/main/res/drawable-xxhdpi/splash.png +0 -0
  34. package/templates/firebase/android/app/src/main/res/drawable-xxxhdpi/android12splash.png +0 -0
  35. package/templates/firebase/android/app/src/main/res/drawable-xxxhdpi/splash.png +0 -0
  36. package/templates/firebase/android/app/src/main/res/values-night-v31/styles.xml +2 -1
  37. package/templates/firebase/android/app/src/main/res/values-v31/styles.xml +1 -0
  38. package/templates/firebase/assets/images/splash_logo_dark.png +0 -0
  39. package/templates/firebase/assets/images/splash_logo_light.png +0 -0
  40. package/templates/firebase/ios/Runner/Assets.xcassets/LaunchBackground.imageset/Contents.json +9 -8
  41. package/templates/firebase/ios/Runner/Assets.xcassets/LaunchBackground.imageset/darkbackground.png +0 -0
  42. package/templates/firebase/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json +33 -0
  43. package/templates/firebase/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png +0 -0
  44. package/templates/firebase/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png +0 -0
  45. package/templates/firebase/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png +0 -0
  46. package/templates/firebase/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImageDark.png +0 -0
  47. package/templates/firebase/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImageDark@2x.png +0 -0
  48. package/templates/firebase/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImageDark@3x.png +0 -0
  49. package/templates/firebase/ios/Runner/Base.lproj/LaunchScreen.storyboard +1 -1
  50. package/templates/firebase/lib/core/initializer/onstart_widget.dart +7 -1
  51. package/templates/firebase/lib/core/theme/providers/theme_provider.dart +48 -24
  52. package/templates/firebase/lib/features/onboarding/ui/components/onboarding_features.dart +4 -0
  53. package/templates/firebase/lib/features/onboarding/ui/onboarding_page.dart +1 -0
  54. package/templates/firebase/lib/features/onboarding/ui/widgets/onboarding_feature.dart +13 -0
  55. package/templates/firebase/lib/features/settings/settings_page.dart +158 -18
  56. package/templates/firebase/lib/i18n/en.i18n.json +6 -2
  57. package/templates/firebase/lib/i18n/es.i18n.json +6 -2
  58. package/templates/firebase/lib/i18n/pt.i18n.json +6 -2
  59. package/templates/firebase/lib/main.dart +29 -10
  60. package/templates/firebase/pubspec.yaml +4 -5
  61. package/templates/firebase/test/core/data/repositories/user_repository_test.dart +1 -1
  62. package/templates/firebase/web/index.html +47 -39
  63. package/templates/firebase/web/splash/img/dark-1x.png +0 -0
  64. package/templates/firebase/web/splash/img/dark-2x.png +0 -0
  65. package/templates/firebase/web/splash/img/dark-3x.png +0 -0
  66. package/templates/firebase/web/splash/img/dark-4x.png +0 -0
  67. package/templates/firebase/web/splash/img/light-1x.png +0 -0
  68. package/templates/firebase/web/splash/img/light-2x.png +0 -0
  69. package/templates/firebase/web/splash/img/light-3x.png +0 -0
  70. package/templates/firebase/web/splash/img/light-4x.png +0 -0
  71. package/lib/scaffold/features/analytics/lib/core/data/api/analytics_api.dart +0 -124
  72. package/lib/scaffold/features/ci/.github/ISSUE_TEMPLATE/BUG.es.md +0 -35
  73. package/lib/scaffold/features/ci/.github/ISSUE_TEMPLATE/BUG.md +0 -35
  74. package/lib/scaffold/features/ci/.github/ISSUE_TEMPLATE/BUG.pt.md +0 -35
  75. package/lib/scaffold/features/ci/.github/ISSUE_TEMPLATE/feature_request.es.md +0 -12
  76. package/lib/scaffold/features/ci/.github/ISSUE_TEMPLATE/feature_request.md +0 -12
  77. package/lib/scaffold/features/ci/.github/ISSUE_TEMPLATE/feature_request.pt.md +0 -12
  78. package/lib/scaffold/features/ci/.github/PULL_REQUEST_TEMPLATE.es.md +0 -17
  79. package/lib/scaffold/features/ci/.github/PULL_REQUEST_TEMPLATE.md +0 -17
  80. package/lib/scaffold/features/ci/.github/PULL_REQUEST_TEMPLATE.pt.md +0 -17
  81. package/lib/scaffold/features/ci/.github/dependabot.yml +0 -16
  82. package/lib/scaffold/features/ci/.github/workflows/app.yml +0 -20
  83. package/lib/scaffold/features/ci/.gitlab/templates/deploy.yaml +0 -14
  84. package/lib/scaffold/features/ci/.gitlab/templates/dropbox.yaml +0 -19
  85. package/lib/scaffold/features/ci/.gitlab/templates/flutter.yaml +0 -163
  86. package/lib/scaffold/features/ci/.gitlab/templates/mailgun.yaml +0 -28
  87. package/lib/scaffold/features/ci/.gitlab-ci.yml +0 -37
  88. package/lib/scaffold/features/ci/codemagic.yaml +0 -157
  89. package/lib/scaffold/features/facebook/lib/core/data/api/tracking_api.dart +0 -111
  90. package/lib/scaffold/features/feedback/lib/features/feedbacks/api/entities/feature_request_entity.dart +0 -27
  91. package/lib/scaffold/features/feedback/lib/features/feedbacks/api/entities/feature_vote_entity.dart +0 -27
  92. package/lib/scaffold/features/feedback/lib/features/feedbacks/api/feature_request_api.dart +0 -50
  93. package/lib/scaffold/features/feedback/lib/features/feedbacks/api/feature_vote_api.dart +0 -79
  94. package/lib/scaffold/features/feedback/lib/features/feedbacks/models/feature_requests.dart +0 -48
  95. package/lib/scaffold/features/feedback/lib/features/feedbacks/models/feedback_state.dart +0 -42
  96. package/lib/scaffold/features/feedback/lib/features/feedbacks/providers/feedback_page_notifier.dart +0 -147
  97. package/lib/scaffold/features/feedback/lib/features/feedbacks/repositories/feature_request_repository.dart +0 -95
  98. package/lib/scaffold/features/feedback/lib/features/feedbacks/ui/component/add_feature_form.dart +0 -199
  99. package/lib/scaffold/features/feedback/lib/features/feedbacks/ui/feedback_page.dart +0 -175
  100. package/lib/scaffold/features/feedback/lib/features/feedbacks/ui/widgets/add_feature_button.dart +0 -76
  101. package/lib/scaffold/features/feedback/lib/features/feedbacks/ui/widgets/feature_card.dart +0 -279
  102. package/lib/scaffold/features/ios-release/.kasy/apple.env.example +0 -8
  103. package/lib/scaffold/features/ios-release/.kasy/codemagic.env.example +0 -7
  104. package/lib/scaffold/features/ios-release/docs/codemagic-release.en.md +0 -50
  105. package/lib/scaffold/features/ios-release/docs/codemagic-release.es.md +0 -50
  106. package/lib/scaffold/features/ios-release/docs/codemagic-release.pt.md +0 -50
  107. package/lib/scaffold/features/ios-release/docs/ios-release.en.md +0 -41
  108. package/lib/scaffold/features/ios-release/docs/ios-release.es.md +0 -41
  109. package/lib/scaffold/features/ios-release/docs/ios-release.pt.md +0 -41
  110. package/lib/scaffold/features/ios-release/scripts/bump-ios-version.js +0 -38
  111. package/lib/scaffold/features/ios-release/scripts/release-ios.sh +0 -137
  112. package/lib/scaffold/features/llm_chat/lib/features/llm_chat/llm_chat_page.dart +0 -301
  113. package/lib/scaffold/features/local_notifications/lib/features/local_reminder/providers/reminder_notifier.dart +0 -81
  114. package/lib/scaffold/features/local_notifications/lib/features/local_reminder/repositories/reminder_preferences.dart +0 -76
  115. package/lib/scaffold/features/local_notifications/lib/features/local_reminder/ui/reminder_page.dart +0 -282
  116. package/lib/scaffold/features/onboarding/lib/features/onboarding/api/entities/user_info_entity.dart +0 -24
  117. package/lib/scaffold/features/onboarding/lib/features/onboarding/api/user_infos_api.dart +0 -71
  118. package/lib/scaffold/features/onboarding/lib/features/onboarding/models/user_info.dart +0 -92
  119. package/lib/scaffold/features/onboarding/lib/features/onboarding/providers/onboarding_model.dart +0 -15
  120. package/lib/scaffold/features/onboarding/lib/features/onboarding/providers/onboarding_provider.dart +0 -78
  121. package/lib/scaffold/features/onboarding/lib/features/onboarding/repositories/user_infos_repository.dart +0 -29
  122. package/lib/scaffold/features/onboarding/lib/features/onboarding/ui/animations/page_transitions.dart +0 -30
  123. package/lib/scaffold/features/onboarding/lib/features/onboarding/ui/components/onboarding_att_setup.dart +0 -66
  124. package/lib/scaffold/features/onboarding/lib/features/onboarding/ui/components/onboarding_features.dart +0 -72
  125. package/lib/scaffold/features/onboarding/lib/features/onboarding/ui/components/onboarding_loader.dart +0 -92
  126. package/lib/scaffold/features/onboarding/lib/features/onboarding/ui/components/onboarding_notifications_setup.dart +0 -73
  127. package/lib/scaffold/features/onboarding/lib/features/onboarding/ui/components/onboarding_questions.dart +0 -89
  128. package/lib/scaffold/features/onboarding/lib/features/onboarding/ui/onboarding_page.dart +0 -94
  129. package/lib/scaffold/features/onboarding/lib/features/onboarding/ui/widgets/onboarding_background.dart +0 -80
  130. package/lib/scaffold/features/onboarding/lib/features/onboarding/ui/widgets/onboarding_feature.dart +0 -139
  131. package/lib/scaffold/features/onboarding/lib/features/onboarding/ui/widgets/onboarding_illustration_scaffold.dart +0 -110
  132. package/lib/scaffold/features/onboarding/lib/features/onboarding/ui/widgets/onboarding_progress.dart +0 -84
  133. package/lib/scaffold/features/onboarding/lib/features/onboarding/ui/widgets/onboarding_radio_question.dart +0 -173
  134. package/lib/scaffold/features/onboarding/lib/features/onboarding/ui/widgets/onboarding_reassurance.dart +0 -45
  135. package/lib/scaffold/features/onboarding/lib/features/onboarding/ui/widgets/onboarding_sticky_footer.dart +0 -77
  136. package/lib/scaffold/features/onboarding/lib/features/onboarding/ui/widgets/selectable_row_tile.dart +0 -392
  137. package/lib/scaffold/features/revenuecat/lib/core/data/api/tracking_api.dart +0 -116
  138. package/lib/scaffold/features/revenuecat/lib/core/data/models/subscription.dart +0 -322
  139. package/lib/scaffold/features/revenuecat/lib/core/home_widgets/home_widget_background_task.dart +0 -41
  140. package/lib/scaffold/features/revenuecat/lib/core/states/user_state_notifier.dart +0 -305
  141. package/templates/firebase/assets/images/splashscreen.png +0 -0
@@ -1,48 +0,0 @@
1
- import 'package:freezed_annotation/freezed_annotation.dart';
2
- import 'package:kasy_kit/features/feedbacks/api/entities/feature_request_entity.dart';
3
- import 'package:kasy_kit/features/feedbacks/api/entities/feature_vote_entity.dart';
4
-
5
- part 'feature_requests.freezed.dart';
6
-
7
- @freezed
8
- abstract class FeatureRequest with _$FeatureRequest {
9
- const factory FeatureRequest({
10
- required String id,
11
- required DateTime creationDate,
12
- required String title,
13
- required String description,
14
- required int votes,
15
- }) = FeatureRequestData;
16
-
17
- const FeatureRequest._();
18
-
19
- factory FeatureRequest.from(FeatureRequestEntity entity, String language) {
20
- return FeatureRequest(
21
- id: entity.id!,
22
- creationDate: entity.creationDate,
23
- title: entity.title.containsKey(language)
24
- ? entity.title[language]!
25
- : entity.title['en']!,
26
- description: entity.description.containsKey(language)
27
- ? entity.description[language]!
28
- : entity.description['en']!,
29
- votes: entity.votes,
30
- );
31
- }
32
- }
33
-
34
- @freezed
35
- abstract class FeatureRequestVote with _$FeatureRequestVote {
36
- const factory FeatureRequestVote({
37
- required String id,
38
- required String featureId,
39
- }) = FeatureRequestVoteData;
40
-
41
- const FeatureRequestVote._();
42
-
43
- factory FeatureRequestVote.from(UserFeatureVoteEntity entity) =>
44
- FeatureRequestVote(
45
- id: entity.id!,
46
- featureId: entity.featureId,
47
- );
48
- }
@@ -1,42 +0,0 @@
1
- import 'package:kasy_kit/features/feedbacks/models/feature_requests.dart';
2
-
3
- class FeebackPageState {
4
- List<FeatureRequestVote> userVotes;
5
- List<FeatureRequest> featureRequests;
6
-
7
- FeebackPageState({
8
- required this.featureRequests,
9
- required this.userVotes,
10
- });
11
-
12
- FeebackPageState copyWith({
13
- List<FeatureRequest>? featureRequests,
14
- List<FeatureRequestVote>? userVotes,
15
- }) {
16
- return FeebackPageState(
17
- featureRequests: featureRequests ?? this.featureRequests,
18
- userVotes: userVotes ?? this.userVotes,
19
- );
20
- }
21
-
22
- FeebackPageState addVote(FeatureRequest feature, int vote) {
23
- final index =
24
- featureRequests.indexWhere((element) => element.id == feature.id);
25
- if (index == -1) {
26
- return this;
27
- }
28
- final newFeature =
29
- featureRequests[index].copyWith(votes: feature.votes + vote);
30
- final newFeatureRequests = List<FeatureRequest>.from(featureRequests);
31
- newFeatureRequests[index] = newFeature;
32
- return copyWith(featureRequests: newFeatureRequests);
33
- }
34
-
35
- bool hasVoted(FeatureRequest feature) {
36
- return userVotes.any((element) => element.featureId == feature.id);
37
- }
38
-
39
- bool canVote() {
40
- return userVotes.isEmpty;
41
- }
42
- }
@@ -1,147 +0,0 @@
1
- import 'package:flutter/foundation.dart';
2
- import 'package:kasy_kit/components/kasy_toast.dart';
3
- import 'package:kasy_kit/core/states/translations.dart';
4
- import 'package:kasy_kit/core/states/user_state_notifier.dart';
5
- import 'package:kasy_kit/features/feedbacks/models/feature_requests.dart';
6
- import 'package:kasy_kit/features/feedbacks/models/feedback_state.dart';
7
- import 'package:kasy_kit/features/feedbacks/repositories/feature_request_repository.dart';
8
- import 'package:riverpod_annotation/riverpod_annotation.dart';
9
-
10
- part 'feedback_page_notifier.g.dart';
11
-
12
- @Riverpod()
13
- class FeedbackPageNotifier extends _$FeedbackPageNotifier {
14
- bool _isVoting = false;
15
-
16
- @override
17
- Future<FeebackPageState> build() async {
18
- final featureRequestRepository = ref.read(featureRequestRepositoryProvider);
19
- final userId = ref.read(userStateNotifierProvider).user.idOrNull;
20
-
21
- final userVotes = userId != null
22
- ? await featureRequestRepository.getUserVotes(userId)
23
- : <FeatureRequestVote>[];
24
- final features = await featureRequestRepository.getActiveFeatureRequests();
25
- features.sort((a, b) => b.votes.compareTo(a.votes));
26
-
27
- return FeebackPageState(
28
- featureRequests: features,
29
- userVotes: userVotes,
30
- );
31
- }
32
-
33
- Future<void> vote(FeatureRequest featureRequest) async {
34
- if (_isVoting) return;
35
- _isVoting = true;
36
- final translations = ref.read(translationsProvider).feature_requests;
37
-
38
- if (state.value!.hasVoted(featureRequest)) {
39
- await _unVote(featureRequest);
40
- _isVoting = false;
41
- return;
42
- }
43
-
44
- final oldState = state.value!;
45
- final featureRequestRepository = ref.read(featureRequestRepositoryProvider);
46
- final userState = ref.read(userStateNotifierProvider);
47
-
48
- try {
49
- final userId = userState.user.idOrNull;
50
- if (userId == null) return;
51
-
52
- // Optimistic update: count +1 AND mark as voted immediately so
53
- // hasVoted returns true right away (prevents inverted toggle).
54
- final optimisticVote = FeatureRequestVote(
55
- id: '_optimistic',
56
- featureId: featureRequest.id,
57
- );
58
- var newState = state.value!
59
- .addVote(featureRequest, 1)
60
- .copyWith(
61
- userVotes: [...state.value!.userVotes, optimisticVote],
62
- );
63
- state = AsyncData(newState);
64
-
65
- final userVote = await featureRequestRepository.vote(
66
- userId,
67
- featureRequest,
68
- );
69
- // Replace optimistic entry with real vote from server.
70
- newState = newState.copyWith(userVotes: [
71
- ...newState.userVotes.where((v) => v.id != '_optimistic'),
72
- userVote,
73
- ]);
74
- state = AsyncData(newState);
75
- ref.read(toastProvider).success(
76
- title: translations.vote_success.title,
77
- text: translations.vote_success.description,
78
- );
79
- } catch (e) {
80
- state = AsyncData(oldState);
81
- ref.read(toastProvider).error(
82
- title: translations.vote_error.title,
83
- text: translations.vote_error.description,
84
- );
85
- } finally {
86
- _isVoting = false;
87
- }
88
- }
89
-
90
- Future<void> _unVote(FeatureRequest featureRequest) async {
91
- final oldState = state.value!;
92
- final featureRequestRepository = ref.read(featureRequestRepositoryProvider);
93
- final userState = ref.read(userStateNotifierProvider);
94
-
95
- try {
96
- final userId = userState.user.idOrNull;
97
- if (userId == null) return;
98
- final voteToDelete = oldState.userVotes
99
- .where((e) => e.featureId == featureRequest.id)
100
- .firstOrNull;
101
- if (voteToDelete == null) return;
102
-
103
- var newState = oldState.addVote(featureRequest, -1);
104
- newState = newState.copyWith(userVotes: [
105
- ...newState.userVotes.where((e) => e.featureId != featureRequest.id),
106
- ]);
107
- state = AsyncData(newState);
108
-
109
- // If the vote was only optimistic (never reached the server), skip delete.
110
- if (voteToDelete.id == '_optimistic') return;
111
- await featureRequestRepository.removeVote(userId, voteToDelete);
112
- } catch (e) {
113
- debugPrint('Error while unvoting: $e');
114
- state = AsyncData(oldState);
115
- }
116
- }
117
-
118
- Future<void> createFeatureSuggestion({
119
- required String title,
120
- required String description,
121
- }) async {
122
- final userId = ref.read(userStateNotifierProvider).user.idOrNull;
123
- if (userId == null) return;
124
- if (title.isEmpty || description.isEmpty) {
125
- final tr = ref.read(translationsProvider).feature_requests.add_feature;
126
- ref.read(toastProvider).error(
127
- title: tr.error_title,
128
- text: tr.error_required,
129
- );
130
- return;
131
- }
132
- if (description.length < 40) {
133
- final tr = ref.read(translationsProvider).feature_requests.add_feature;
134
- ref.read(toastProvider).error(
135
- title: tr.error_title,
136
- text: tr.error_too_short,
137
- );
138
- return;
139
- }
140
- final featureRequestRepository = ref.read(featureRequestRepositoryProvider);
141
- await featureRequestRepository.createNewFeatureSuggestion(
142
- userId: userId,
143
- title: title,
144
- description: description,
145
- );
146
- }
147
- }
@@ -1,95 +0,0 @@
1
- import 'package:flutter_riverpod/flutter_riverpod.dart';
2
- import 'package:logger/logger.dart';
3
- import 'package:kasy_kit/i18n/translations.g.dart';
4
- import 'package:kasy_kit/features/feedbacks/api/feature_request_api.dart';
5
- import 'package:kasy_kit/features/feedbacks/api/feature_vote_api.dart';
6
- import 'package:kasy_kit/features/feedbacks/models/feature_requests.dart';
7
-
8
- final featureRequestRepositoryProvider = Provider<FeatureRequestRepository>(
9
- (ref) => FeatureRequestRepository(
10
- featureVoteApi: ref.read(featureVoteApiProvider),
11
- featureRequestApi: ref.read(featureRequestApiProvider),
12
- logger: Logger(),
13
- ),
14
- );
15
-
16
- class FeatureRequestRepository {
17
- final FeatureVoteApi _featureVoteApi;
18
- final FeatureRequestApi _featureRequestApi;
19
- final Logger _logger;
20
-
21
- FeatureRequestRepository({
22
- required FeatureVoteApi featureVoteApi,
23
- required FeatureRequestApi featureRequestApi,
24
- required Logger logger,
25
- }) : _featureVoteApi = featureVoteApi,
26
- _logger = logger,
27
- _featureRequestApi = featureRequestApi;
28
-
29
- Future<List<FeatureRequest>> getActiveFeatureRequests() async {
30
- try {
31
- final featureRequestsEntities = await _featureRequestApi.getAllActive();
32
- final currentLocale = LocaleSettings.currentLocale;
33
- return featureRequestsEntities
34
- .map(
35
- (entity) => FeatureRequest.from(entity, currentLocale.languageCode),
36
- )
37
- .toList();
38
- } catch (e, s) {
39
- _logger.e('Error while fetching active feature requests: $e : $s');
40
- rethrow;
41
- }
42
- }
43
-
44
- Future<List<FeatureRequestVote>> getUserVotes(String userId) async {
45
- try {
46
- final uservotes = await _featureVoteApi.getUserVotes(userId);
47
- return uservotes.map((e) => FeatureRequestVote.from(e)).toList();
48
- } catch (e, s) {
49
- _logger.e('Error while fetching user votes: $e : $s');
50
- rethrow;
51
- }
52
- }
53
-
54
- Future<FeatureRequestVote> vote(
55
- String userId,
56
- FeatureRequest featureRequest,
57
- ) async {
58
- try {
59
- final result = await _featureVoteApi.create(
60
- userId,
61
- featureRequest.id,
62
- );
63
- return FeatureRequestVote.from(result);
64
- } catch (e, s) {
65
- _logger.e('Error while voting for feature request: $e : $s');
66
- rethrow;
67
- }
68
- }
69
-
70
- Future<void> removeVote(String userId, FeatureRequestVote vote) async {
71
- try {
72
- await _featureVoteApi.delete(vote.featureId, vote.id);
73
- } catch (e, s) {
74
- _logger.e('Error while deleting vote for feature request: $e : $s');
75
- rethrow;
76
- }
77
- }
78
-
79
- Future<void> createNewFeatureSuggestion({
80
- required String userId,
81
- required String title,
82
- required String description,
83
- }) async {
84
- try {
85
- final featureId = await _featureRequestApi.create(
86
- title: title,
87
- description: description,
88
- );
89
- await _featureVoteApi.create(userId, featureId);
90
- } catch (e, s) {
91
- _logger.e('Error while creating feature suggestion: $e : $s');
92
- rethrow;
93
- }
94
- }
95
- }
@@ -1,199 +0,0 @@
1
- import 'package:flutter/material.dart';
2
- import 'package:flutter_riverpod/flutter_riverpod.dart';
3
- import 'package:go_router/go_router.dart';
4
- import 'package:kasy_kit/components/components.dart';
5
- import 'package:kasy_kit/core/theme/theme.dart';
6
- import 'package:kasy_kit/core/widgets/kasy_scroll_behavior.dart';
7
- import 'package:kasy_kit/features/feedbacks/providers/feedback_page_notifier.dart';
8
- import 'package:kasy_kit/i18n/translations.g.dart';
9
-
10
- Future<void> showAddFeatureBottomSheet(BuildContext context) {
11
- return showModalBottomSheet<bool?>(
12
- context: context,
13
- isScrollControlled: true,
14
- builder: (context) => const FractionallySizedBox(
15
- heightFactor: .8,
16
- child: AddFeatureComponent(),
17
- ),
18
- ).then((res) {
19
- if (!context.mounted) return;
20
- final translations = Translations.of(context).feature_requests.add_feature;
21
- if (res == true) {
22
- showSuccessToast(
23
- context: context,
24
- title: translations.toast_success.title,
25
- text: translations.toast_success.description,
26
- );
27
- }
28
- });
29
- }
30
-
31
- class AddFeatureComponent extends ConsumerStatefulWidget {
32
- const AddFeatureComponent({super.key});
33
-
34
- @override
35
- ConsumerState<ConsumerStatefulWidget> createState() =>
36
- _AddFeatureComponentState();
37
- }
38
-
39
- class _AddFeatureComponentState extends ConsumerState<AddFeatureComponent> {
40
- String title = '';
41
- String description = '';
42
- bool locked = false;
43
-
44
- final titleFocusNode = FocusNode();
45
- final textFocusNode = FocusNode();
46
-
47
- bool get _titleFilled => title.trim().isNotEmpty;
48
- bool get _descriptionValid => description.length >= 40;
49
- bool get canSubmit => _titleFilled && _descriptionValid && !locked;
50
-
51
- void _onSubmitAttempt() {
52
- final tr = Translations.of(context).feature_requests.add_feature;
53
- if (!_titleFilled || description.trim().isEmpty) {
54
- ref.read(toastProvider).error(
55
- title: tr.error_title,
56
- text: tr.error_required,
57
- );
58
- return;
59
- }
60
- if (!_descriptionValid) {
61
- ref.read(toastProvider).error(
62
- title: tr.error_title,
63
- text: tr.error_too_short,
64
- );
65
- return;
66
- }
67
- }
68
-
69
- void _submit() {
70
- if (locked) return;
71
- FocusScope.of(context).unfocus();
72
- locked = true;
73
- final tr = Translations.of(context).feature_requests.add_feature;
74
- ref
75
- .read(feedbackPageProvider.notifier)
76
- .createFeatureSuggestion(
77
- title: title,
78
- description: description,
79
- )
80
- .then(
81
- (_) {
82
- locked = false;
83
- if (!mounted) return;
84
- context.pop(true);
85
- },
86
- onError: (_) {
87
- locked = false;
88
- ref.read(toastProvider).error(
89
- title: tr.error_title,
90
- text: tr.error_sending,
91
- );
92
- },
93
- );
94
- }
95
-
96
- @override
97
- Widget build(BuildContext context) {
98
- final translations = Translations.of(context).feature_requests.add_feature;
99
-
100
- final Widget submitButton = KasyButton(
101
- label: translations.save_button,
102
- expand: true,
103
- onPressed: canSubmit ? _submit : null,
104
- );
105
-
106
- return Scaffold(
107
- body: Stack(
108
- fit: StackFit.expand,
109
- children: [
110
- Positioned.fill(
111
- child: ScrollConfiguration(
112
- behavior: const KasyKitScrollBehavior(),
113
- child: CustomScrollView(
114
- slivers: kasyOverlayPaddedSlivers(
115
- context,
116
- contentPadding: EdgeInsets.fromLTRB(
117
- KasySpacing.pageHorizontalGutter,
118
- KasySpacing.belowChromeContentGap,
119
- KasySpacing.pageHorizontalGutter,
120
- MediaQuery.paddingOf(context).bottom + KasySpacing.xxxl,
121
- ),
122
- slivers: [
123
- SliverToBoxAdapter(
124
- child: Column(
125
- crossAxisAlignment: CrossAxisAlignment.stretch,
126
- children: [
127
- const SizedBox(height: KasySpacing.md),
128
- Text(
129
- translations.description,
130
- style: context.textTheme.bodyMedium?.copyWith(
131
- color: context.colors.muted,
132
- height: 1.5,
133
- ),
134
- ),
135
- const SizedBox(height: KasySpacing.xl),
136
- KasyTextField(
137
- label: translations.title_label,
138
- hint: translations.title_hint,
139
- showRequiredIndicator: true,
140
- variant: KasyTextFieldVariant.secondary,
141
- onChanged: (value) =>
142
- setState(() => title = value),
143
- focusNode: titleFocusNode,
144
- textInputAction: TextInputAction.next,
145
- onSubmitted: (_) {
146
- titleFocusNode.unfocus();
147
- textFocusNode.requestFocus();
148
- },
149
- maxLength: 40,
150
- ),
151
- const SizedBox(height: KasySpacing.md),
152
- KasyTextField(
153
- label: translations.description_label,
154
- hint: translations.description_hint,
155
- showRequiredIndicator: true,
156
- variant: KasyTextFieldVariant.secondary,
157
- minLines: 6,
158
- maxLines: 6,
159
- onChanged: (value) =>
160
- setState(() => description = value),
161
- textInputAction: TextInputAction.done,
162
- onSubmitted: (_) => textFocusNode.unfocus(),
163
- maxLength: 1000,
164
- focusNode: textFocusNode,
165
- ),
166
- const SizedBox(height: KasySpacing.xl),
167
- ],
168
- ),
169
- ),
170
- ],
171
- ),
172
- ),
173
- ),
174
- ),
175
- Positioned(
176
- top: 0,
177
- left: 0,
178
- right: 0,
179
- child: KasyAppBar(
180
- title: translations.title,
181
- onBack: () => context.pop(),
182
- ),
183
- ),
184
- Positioned(
185
- left: KasySpacing.pageHorizontalGutter,
186
- right: KasySpacing.pageHorizontalGutter,
187
- bottom: MediaQuery.paddingOf(context).bottom + KasySpacing.md,
188
- child: canSubmit
189
- ? submitButton
190
- : GestureDetector(
191
- onTap: _onSubmitAttempt,
192
- child: submitButton,
193
- ),
194
- ),
195
- ],
196
- ),
197
- );
198
- }
199
- }