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.
- package/bin/kasy.js +25 -4
- package/lib/commands/check.js +40 -50
- package/lib/commands/deploy.js +25 -25
- package/lib/commands/splash.js +220 -0
- package/lib/scaffold/CHANGELOG.json +9 -0
- package/lib/scaffold/backends/api/patch/lib/main.dart +29 -10
- package/lib/scaffold/backends/supabase/patch/lib/main.dart +29 -10
- package/lib/scaffold/features/README.md +15 -139
- package/lib/scaffold/shared/generator-utils.js +16 -15
- package/lib/utils/i18n.js +292 -43
- package/package.json +2 -2
- package/templates/firebase/android/app/src/main/res/drawable-hdpi/android12splash.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-hdpi/splash.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-mdpi/android12splash.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-mdpi/splash.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-night/background.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-night/launch_background.xml +9 -0
- package/templates/firebase/android/app/src/main/res/drawable-night-hdpi/android12splash.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-night-hdpi/splash.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-night-mdpi/android12splash.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-night-mdpi/splash.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-night-v21/background.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-night-v21/launch_background.xml +9 -0
- package/templates/firebase/android/app/src/main/res/drawable-night-xhdpi/android12splash.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-night-xhdpi/splash.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-night-xxhdpi/android12splash.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-night-xxhdpi/splash.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-night-xxxhdpi/android12splash.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-night-xxxhdpi/splash.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-xhdpi/android12splash.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-xhdpi/splash.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-xxhdpi/android12splash.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-xxhdpi/splash.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-xxxhdpi/android12splash.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-xxxhdpi/splash.png +0 -0
- package/templates/firebase/android/app/src/main/res/values-night-v31/styles.xml +2 -1
- package/templates/firebase/android/app/src/main/res/values-v31/styles.xml +1 -0
- package/templates/firebase/assets/images/splash_logo_dark.png +0 -0
- package/templates/firebase/assets/images/splash_logo_light.png +0 -0
- package/templates/firebase/ios/Runner/Assets.xcassets/LaunchBackground.imageset/Contents.json +9 -8
- package/templates/firebase/ios/Runner/Assets.xcassets/LaunchBackground.imageset/darkbackground.png +0 -0
- package/templates/firebase/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json +33 -0
- package/templates/firebase/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png +0 -0
- package/templates/firebase/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png +0 -0
- package/templates/firebase/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png +0 -0
- package/templates/firebase/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImageDark.png +0 -0
- package/templates/firebase/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImageDark@2x.png +0 -0
- package/templates/firebase/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImageDark@3x.png +0 -0
- package/templates/firebase/ios/Runner/Base.lproj/LaunchScreen.storyboard +1 -1
- package/templates/firebase/lib/core/initializer/onstart_widget.dart +7 -1
- package/templates/firebase/lib/core/theme/providers/theme_provider.dart +48 -24
- package/templates/firebase/lib/features/onboarding/ui/components/onboarding_features.dart +4 -0
- package/templates/firebase/lib/features/onboarding/ui/onboarding_page.dart +1 -0
- package/templates/firebase/lib/features/onboarding/ui/widgets/onboarding_feature.dart +13 -0
- package/templates/firebase/lib/features/settings/settings_page.dart +158 -18
- package/templates/firebase/lib/i18n/en.i18n.json +6 -2
- package/templates/firebase/lib/i18n/es.i18n.json +6 -2
- package/templates/firebase/lib/i18n/pt.i18n.json +6 -2
- package/templates/firebase/lib/main.dart +29 -10
- package/templates/firebase/pubspec.yaml +4 -5
- package/templates/firebase/test/core/data/repositories/user_repository_test.dart +1 -1
- package/templates/firebase/web/index.html +47 -39
- package/templates/firebase/web/splash/img/dark-1x.png +0 -0
- package/templates/firebase/web/splash/img/dark-2x.png +0 -0
- package/templates/firebase/web/splash/img/dark-3x.png +0 -0
- package/templates/firebase/web/splash/img/dark-4x.png +0 -0
- package/templates/firebase/web/splash/img/light-1x.png +0 -0
- package/templates/firebase/web/splash/img/light-2x.png +0 -0
- package/templates/firebase/web/splash/img/light-3x.png +0 -0
- package/templates/firebase/web/splash/img/light-4x.png +0 -0
- package/lib/scaffold/features/analytics/lib/core/data/api/analytics_api.dart +0 -124
- package/lib/scaffold/features/ci/.github/ISSUE_TEMPLATE/BUG.es.md +0 -35
- package/lib/scaffold/features/ci/.github/ISSUE_TEMPLATE/BUG.md +0 -35
- package/lib/scaffold/features/ci/.github/ISSUE_TEMPLATE/BUG.pt.md +0 -35
- package/lib/scaffold/features/ci/.github/ISSUE_TEMPLATE/feature_request.es.md +0 -12
- package/lib/scaffold/features/ci/.github/ISSUE_TEMPLATE/feature_request.md +0 -12
- package/lib/scaffold/features/ci/.github/ISSUE_TEMPLATE/feature_request.pt.md +0 -12
- package/lib/scaffold/features/ci/.github/PULL_REQUEST_TEMPLATE.es.md +0 -17
- package/lib/scaffold/features/ci/.github/PULL_REQUEST_TEMPLATE.md +0 -17
- package/lib/scaffold/features/ci/.github/PULL_REQUEST_TEMPLATE.pt.md +0 -17
- package/lib/scaffold/features/ci/.github/dependabot.yml +0 -16
- package/lib/scaffold/features/ci/.github/workflows/app.yml +0 -20
- package/lib/scaffold/features/ci/.gitlab/templates/deploy.yaml +0 -14
- package/lib/scaffold/features/ci/.gitlab/templates/dropbox.yaml +0 -19
- package/lib/scaffold/features/ci/.gitlab/templates/flutter.yaml +0 -163
- package/lib/scaffold/features/ci/.gitlab/templates/mailgun.yaml +0 -28
- package/lib/scaffold/features/ci/.gitlab-ci.yml +0 -37
- package/lib/scaffold/features/ci/codemagic.yaml +0 -157
- package/lib/scaffold/features/facebook/lib/core/data/api/tracking_api.dart +0 -111
- package/lib/scaffold/features/feedback/lib/features/feedbacks/api/entities/feature_request_entity.dart +0 -27
- package/lib/scaffold/features/feedback/lib/features/feedbacks/api/entities/feature_vote_entity.dart +0 -27
- package/lib/scaffold/features/feedback/lib/features/feedbacks/api/feature_request_api.dart +0 -50
- package/lib/scaffold/features/feedback/lib/features/feedbacks/api/feature_vote_api.dart +0 -79
- package/lib/scaffold/features/feedback/lib/features/feedbacks/models/feature_requests.dart +0 -48
- package/lib/scaffold/features/feedback/lib/features/feedbacks/models/feedback_state.dart +0 -42
- package/lib/scaffold/features/feedback/lib/features/feedbacks/providers/feedback_page_notifier.dart +0 -147
- package/lib/scaffold/features/feedback/lib/features/feedbacks/repositories/feature_request_repository.dart +0 -95
- package/lib/scaffold/features/feedback/lib/features/feedbacks/ui/component/add_feature_form.dart +0 -199
- package/lib/scaffold/features/feedback/lib/features/feedbacks/ui/feedback_page.dart +0 -175
- package/lib/scaffold/features/feedback/lib/features/feedbacks/ui/widgets/add_feature_button.dart +0 -76
- package/lib/scaffold/features/feedback/lib/features/feedbacks/ui/widgets/feature_card.dart +0 -279
- package/lib/scaffold/features/ios-release/.kasy/apple.env.example +0 -8
- package/lib/scaffold/features/ios-release/.kasy/codemagic.env.example +0 -7
- package/lib/scaffold/features/ios-release/docs/codemagic-release.en.md +0 -50
- package/lib/scaffold/features/ios-release/docs/codemagic-release.es.md +0 -50
- package/lib/scaffold/features/ios-release/docs/codemagic-release.pt.md +0 -50
- package/lib/scaffold/features/ios-release/docs/ios-release.en.md +0 -41
- package/lib/scaffold/features/ios-release/docs/ios-release.es.md +0 -41
- package/lib/scaffold/features/ios-release/docs/ios-release.pt.md +0 -41
- package/lib/scaffold/features/ios-release/scripts/bump-ios-version.js +0 -38
- package/lib/scaffold/features/ios-release/scripts/release-ios.sh +0 -137
- package/lib/scaffold/features/llm_chat/lib/features/llm_chat/llm_chat_page.dart +0 -301
- package/lib/scaffold/features/local_notifications/lib/features/local_reminder/providers/reminder_notifier.dart +0 -81
- package/lib/scaffold/features/local_notifications/lib/features/local_reminder/repositories/reminder_preferences.dart +0 -76
- package/lib/scaffold/features/local_notifications/lib/features/local_reminder/ui/reminder_page.dart +0 -282
- package/lib/scaffold/features/onboarding/lib/features/onboarding/api/entities/user_info_entity.dart +0 -24
- package/lib/scaffold/features/onboarding/lib/features/onboarding/api/user_infos_api.dart +0 -71
- package/lib/scaffold/features/onboarding/lib/features/onboarding/models/user_info.dart +0 -92
- package/lib/scaffold/features/onboarding/lib/features/onboarding/providers/onboarding_model.dart +0 -15
- package/lib/scaffold/features/onboarding/lib/features/onboarding/providers/onboarding_provider.dart +0 -78
- package/lib/scaffold/features/onboarding/lib/features/onboarding/repositories/user_infos_repository.dart +0 -29
- package/lib/scaffold/features/onboarding/lib/features/onboarding/ui/animations/page_transitions.dart +0 -30
- package/lib/scaffold/features/onboarding/lib/features/onboarding/ui/components/onboarding_att_setup.dart +0 -66
- package/lib/scaffold/features/onboarding/lib/features/onboarding/ui/components/onboarding_features.dart +0 -72
- package/lib/scaffold/features/onboarding/lib/features/onboarding/ui/components/onboarding_loader.dart +0 -92
- package/lib/scaffold/features/onboarding/lib/features/onboarding/ui/components/onboarding_notifications_setup.dart +0 -73
- package/lib/scaffold/features/onboarding/lib/features/onboarding/ui/components/onboarding_questions.dart +0 -89
- package/lib/scaffold/features/onboarding/lib/features/onboarding/ui/onboarding_page.dart +0 -94
- package/lib/scaffold/features/onboarding/lib/features/onboarding/ui/widgets/onboarding_background.dart +0 -80
- package/lib/scaffold/features/onboarding/lib/features/onboarding/ui/widgets/onboarding_feature.dart +0 -139
- package/lib/scaffold/features/onboarding/lib/features/onboarding/ui/widgets/onboarding_illustration_scaffold.dart +0 -110
- package/lib/scaffold/features/onboarding/lib/features/onboarding/ui/widgets/onboarding_progress.dart +0 -84
- package/lib/scaffold/features/onboarding/lib/features/onboarding/ui/widgets/onboarding_radio_question.dart +0 -173
- package/lib/scaffold/features/onboarding/lib/features/onboarding/ui/widgets/onboarding_reassurance.dart +0 -45
- package/lib/scaffold/features/onboarding/lib/features/onboarding/ui/widgets/onboarding_sticky_footer.dart +0 -77
- package/lib/scaffold/features/onboarding/lib/features/onboarding/ui/widgets/selectable_row_tile.dart +0 -392
- package/lib/scaffold/features/revenuecat/lib/core/data/api/tracking_api.dart +0 -116
- package/lib/scaffold/features/revenuecat/lib/core/data/models/subscription.dart +0 -322
- package/lib/scaffold/features/revenuecat/lib/core/home_widgets/home_widget_background_task.dart +0 -41
- package/lib/scaffold/features/revenuecat/lib/core/states/user_state_notifier.dart +0 -305
- 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
|
-
}
|
package/lib/scaffold/features/feedback/lib/features/feedbacks/providers/feedback_page_notifier.dart
DELETED
|
@@ -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
|
-
}
|
package/lib/scaffold/features/feedback/lib/features/feedbacks/ui/component/add_feature_form.dart
DELETED
|
@@ -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
|
-
}
|