kasy-cli 1.12.1 → 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 +21 -0
- 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 +78 -0
- 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/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,111 +0,0 @@
|
|
|
1
|
-
import 'dart:async';
|
|
2
|
-
|
|
3
|
-
import 'package:facebook_app_events/facebook_app_events.dart';
|
|
4
|
-
import 'package:flutter/foundation.dart';
|
|
5
|
-
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
6
|
-
import 'package:logger/logger.dart';
|
|
7
|
-
import 'package:permission_handler/permission_handler.dart';
|
|
8
|
-
import 'package:kasy_kit/core/initializer/onstart_service.dart';
|
|
9
|
-
import 'package:kasy_kit/core/states/models/user_state.dart';
|
|
10
|
-
import 'package:kasy_kit/core/states/user_state_notifier.dart';
|
|
11
|
-
import 'package:kasy_kit/features/notifications/repositories/device_repository.dart';
|
|
12
|
-
import 'package:sentry_flutter/sentry_flutter.dart';
|
|
13
|
-
|
|
14
|
-
final facebookEventApiProvider = Provider(
|
|
15
|
-
(ref) => FacebookEventApi(
|
|
16
|
-
deviceRepository: ref.watch(deviceRepositoryProvider),
|
|
17
|
-
userState: ref.watch(userStateNotifierProvider),
|
|
18
|
-
),
|
|
19
|
-
);
|
|
20
|
-
|
|
21
|
-
class FacebookEventApi implements OnStartService {
|
|
22
|
-
final DeviceRepository deviceRepository;
|
|
23
|
-
final UserState userState;
|
|
24
|
-
|
|
25
|
-
FacebookEventApi({
|
|
26
|
-
required this.deviceRepository,
|
|
27
|
-
required this.userState,
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
@override
|
|
31
|
-
Future<void> init() async {
|
|
32
|
-
if (kIsWeb) {
|
|
33
|
-
return;
|
|
34
|
-
}
|
|
35
|
-
// We must init the facebook app events
|
|
36
|
-
final facebookAppEvents = FacebookAppEvents();
|
|
37
|
-
// This is required by RevenueCat
|
|
38
|
-
await facebookAppEvents.setAutoLogAppEventsEnabled(false);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
Future<void> initUser(String userId) async {
|
|
42
|
-
if (kIsWeb) {
|
|
43
|
-
return;
|
|
44
|
-
}
|
|
45
|
-
final facebookAppEvents = FacebookAppEvents();
|
|
46
|
-
await facebookAppEvents.setUserID(userId);
|
|
47
|
-
|
|
48
|
-
if (defaultTargetPlatform != TargetPlatform.iOS) {
|
|
49
|
-
return;
|
|
50
|
-
}
|
|
51
|
-
final isGranted = await Permission.appTrackingTransparency.isGranted;
|
|
52
|
-
if (!isGranted) {
|
|
53
|
-
return;
|
|
54
|
-
}
|
|
55
|
-
await facebookAppEvents.setAdvertiserTracking(enabled: true);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
Future<void> requestIDFA() async {
|
|
59
|
-
if (kIsWeb) {
|
|
60
|
-
return;
|
|
61
|
-
}
|
|
62
|
-
final facebookAppEvents = FacebookAppEvents();
|
|
63
|
-
if (defaultTargetPlatform == TargetPlatform.iOS) {
|
|
64
|
-
final status = await Permission.appTrackingTransparency.request();
|
|
65
|
-
// analyticsApi?.logEvent('att_request', {
|
|
66
|
-
// 'granted': true,
|
|
67
|
-
// });
|
|
68
|
-
|
|
69
|
-
debugPrint("IDFA status: $status");
|
|
70
|
-
if (status.isGranted) {
|
|
71
|
-
await facebookAppEvents.setAdvertiserTracking(enabled: true);
|
|
72
|
-
await _initMetaUser();
|
|
73
|
-
unawaited(facebookAppEvents.flush());
|
|
74
|
-
await deviceRepository.refreshExtraData();
|
|
75
|
-
} else {
|
|
76
|
-
await facebookAppEvents.setAdvertiserTracking(enabled: false);
|
|
77
|
-
}
|
|
78
|
-
} else if (defaultTargetPlatform == TargetPlatform.android) {
|
|
79
|
-
await _initMetaUser();
|
|
80
|
-
await facebookAppEvents.setAutoLogAppEventsEnabled(true);
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
Future<void> _initMetaUser() async {
|
|
85
|
-
final facebookAppEvents = FacebookAppEvents();
|
|
86
|
-
final userId = userState.user.idOrNull;
|
|
87
|
-
if (userId != null) {
|
|
88
|
-
await facebookAppEvents.setUserID(userId);
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
Future<void> logMetaStartTrial(String orderId, double price, String currency) async {
|
|
93
|
-
try {
|
|
94
|
-
final facebookAppEvents = FacebookAppEvents();
|
|
95
|
-
await facebookAppEvents.logStartTrial(orderId: orderId, price: price, currency: currency);
|
|
96
|
-
} catch (e) {
|
|
97
|
-
Logger().e("Error logging StartTrial (facebook): $e");
|
|
98
|
-
Sentry.captureException(e, stackTrace: StackTrace.current);
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
Future<void> logMetaSubscribe(String orderId, double price, String currency) async {
|
|
103
|
-
try {
|
|
104
|
-
final facebookAppEvents = FacebookAppEvents();
|
|
105
|
-
await facebookAppEvents.logSubscribe(orderId: orderId, price: price, currency: currency);
|
|
106
|
-
} catch (e) {
|
|
107
|
-
Logger().e("Error logging Subscribe (facebook): $e");
|
|
108
|
-
Sentry.captureException(e, stackTrace: StackTrace.current);
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
}
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import 'package:cloud_firestore/cloud_firestore.dart';
|
|
2
|
-
import 'package:freezed_annotation/freezed_annotation.dart';
|
|
3
|
-
// ignore: depend_on_referenced_packages
|
|
4
|
-
// ignore_for_file: invalid_annotation_target
|
|
5
|
-
|
|
6
|
-
import 'package:kasy_kit/core/data/entities/json_converters.dart';
|
|
7
|
-
|
|
8
|
-
part 'feature_request_entity.freezed.dart';
|
|
9
|
-
part 'feature_request_entity.g.dart';
|
|
10
|
-
|
|
11
|
-
@freezed
|
|
12
|
-
sealed class FeatureRequestEntity with _$FeatureRequestEntity {
|
|
13
|
-
const factory FeatureRequestEntity({
|
|
14
|
-
@JsonKey(includeIfNull: false, toJson: Converters.id) String? id,
|
|
15
|
-
@TimestampConverter() required DateTime creationDate,
|
|
16
|
-
@TimestampConverter() required DateTime lastUpdateDate,
|
|
17
|
-
required Map<String, String> title,
|
|
18
|
-
required Map<String, String> description,
|
|
19
|
-
required int votes,
|
|
20
|
-
required bool active, // votes are enabled for this feature
|
|
21
|
-
}) = FeatureRequestEntityData;
|
|
22
|
-
|
|
23
|
-
const FeatureRequestEntity._();
|
|
24
|
-
|
|
25
|
-
factory FeatureRequestEntity.fromJson(String id, Map<String, dynamic> json) =>
|
|
26
|
-
_$FeatureRequestEntityFromJson(json..['id'] = id);
|
|
27
|
-
}
|
package/lib/scaffold/features/feedback/lib/features/feedbacks/api/entities/feature_vote_entity.dart
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import 'package:cloud_firestore/cloud_firestore.dart';
|
|
2
|
-
import 'package:freezed_annotation/freezed_annotation.dart';
|
|
3
|
-
// ignore: depend_on_referenced_packages
|
|
4
|
-
// ignore_for_file: invalid_annotation_target
|
|
5
|
-
|
|
6
|
-
import 'package:kasy_kit/core/data/entities/json_converters.dart';
|
|
7
|
-
|
|
8
|
-
part 'feature_vote_entity.freezed.dart';
|
|
9
|
-
part 'feature_vote_entity.g.dart';
|
|
10
|
-
|
|
11
|
-
@freezed
|
|
12
|
-
abstract class UserFeatureVoteEntity with _$UserFeatureVoteEntity {
|
|
13
|
-
const factory UserFeatureVoteEntity({
|
|
14
|
-
@JsonKey(includeIfNull: false, toJson: Converters.id) String? id,
|
|
15
|
-
@JsonKey(name: 'creation_date')
|
|
16
|
-
@TimestampConverter()
|
|
17
|
-
required DateTime creationDate,
|
|
18
|
-
required String userId,
|
|
19
|
-
required String featureId,
|
|
20
|
-
}) = UserFeatureVoteEntityData;
|
|
21
|
-
|
|
22
|
-
const UserFeatureVoteEntity._();
|
|
23
|
-
|
|
24
|
-
factory UserFeatureVoteEntity.fromJson(
|
|
25
|
-
String id, Map<String, dynamic> json) =>
|
|
26
|
-
_$UserFeatureVoteEntityFromJson(json..['id'] = id);
|
|
27
|
-
}
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
import 'package:cloud_firestore/cloud_firestore.dart';
|
|
2
|
-
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
3
|
-
import 'package:kasy_kit/features/feedbacks/api/entities/feature_request_entity.dart';
|
|
4
|
-
|
|
5
|
-
final featureRequestApiProvider = Provider<FeatureRequestApi>(
|
|
6
|
-
(ref) => FeatureRequestApi(
|
|
7
|
-
client: FirebaseFirestore.instance,
|
|
8
|
-
),
|
|
9
|
-
);
|
|
10
|
-
|
|
11
|
-
class FeatureRequestApi {
|
|
12
|
-
final FirebaseFirestore _client;
|
|
13
|
-
|
|
14
|
-
FeatureRequestApi({
|
|
15
|
-
required FirebaseFirestore client,
|
|
16
|
-
}) : _client = client;
|
|
17
|
-
|
|
18
|
-
CollectionReference<FeatureRequestEntity?> get _collection => _client
|
|
19
|
-
.collection('feature_requests')
|
|
20
|
-
.withConverter(
|
|
21
|
-
fromFirestore: (snapshot, _) {
|
|
22
|
-
if (snapshot.exists) {
|
|
23
|
-
return FeatureRequestEntity.fromJson(snapshot.id, snapshot.data()!);
|
|
24
|
-
}
|
|
25
|
-
return null;
|
|
26
|
-
},
|
|
27
|
-
toFirestore: (data, _) => data!.toJson(),
|
|
28
|
-
);
|
|
29
|
-
|
|
30
|
-
Future<List<FeatureRequestEntity>> getAllActive() async {
|
|
31
|
-
final snapshot = await _collection.where('active', isEqualTo: true).get();
|
|
32
|
-
return snapshot.docs.map((e) => e.data()!).toList();
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
Future<String> create({
|
|
36
|
-
required String title,
|
|
37
|
-
required String description,
|
|
38
|
-
}) async {
|
|
39
|
-
final now = DateTime.now().toIso8601String();
|
|
40
|
-
final docRef = await _client.collection('feature_requests').add({
|
|
41
|
-
'title': {'en': title, 'pt': title, 'es': title},
|
|
42
|
-
'description': {'en': description, 'pt': description, 'es': description},
|
|
43
|
-
'votes': 0,
|
|
44
|
-
'active': false,
|
|
45
|
-
'creation_date': now,
|
|
46
|
-
'last_update_date': now,
|
|
47
|
-
});
|
|
48
|
-
return docRef.id;
|
|
49
|
-
}
|
|
50
|
-
}
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
import 'package:cloud_firestore/cloud_firestore.dart';
|
|
2
|
-
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
3
|
-
import 'package:kasy_kit/features/feedbacks/api/entities/feature_vote_entity.dart';
|
|
4
|
-
|
|
5
|
-
final featureVoteApiProvider = Provider<FeatureVoteApi>(
|
|
6
|
-
(ref) => FeatureVoteApi(
|
|
7
|
-
client: FirebaseFirestore.instance,
|
|
8
|
-
),
|
|
9
|
-
);
|
|
10
|
-
|
|
11
|
-
class FeatureVoteApi {
|
|
12
|
-
final FirebaseFirestore _client;
|
|
13
|
-
|
|
14
|
-
FeatureVoteApi({
|
|
15
|
-
required FirebaseFirestore client,
|
|
16
|
-
}) : _client = client;
|
|
17
|
-
|
|
18
|
-
CollectionReference<UserFeatureVoteEntity?> _featureVoteCollection(
|
|
19
|
-
String featureId) =>
|
|
20
|
-
_client
|
|
21
|
-
.collection('feature_requests')
|
|
22
|
-
.doc(featureId)
|
|
23
|
-
.collection('votes')
|
|
24
|
-
.withConverter(
|
|
25
|
-
fromFirestore: (snapshot, _) {
|
|
26
|
-
if (snapshot.exists) {
|
|
27
|
-
return UserFeatureVoteEntity.fromJson(
|
|
28
|
-
snapshot.id, snapshot.data()!);
|
|
29
|
-
}
|
|
30
|
-
return null;
|
|
31
|
-
},
|
|
32
|
-
toFirestore: (data, _) => data!.toJson(),
|
|
33
|
-
);
|
|
34
|
-
|
|
35
|
-
Query<UserFeatureVoteEntity?> get collectionGroup =>
|
|
36
|
-
_client.collectionGroup('votes').withConverter(
|
|
37
|
-
fromFirestore: (snapshot, _) {
|
|
38
|
-
if (snapshot.exists) {
|
|
39
|
-
return UserFeatureVoteEntity.fromJson(
|
|
40
|
-
snapshot.id, snapshot.data()!);
|
|
41
|
-
}
|
|
42
|
-
return null;
|
|
43
|
-
},
|
|
44
|
-
toFirestore: (data, _) => data!.toJson(),
|
|
45
|
-
);
|
|
46
|
-
|
|
47
|
-
Future<List<UserFeatureVoteEntity>> getUserVotes(String userId) async {
|
|
48
|
-
final snapshot =
|
|
49
|
-
await collectionGroup.where('userId', isEqualTo: userId).get();
|
|
50
|
-
return snapshot.docs.map((e) => e.data()!).toList();
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
Future<UserFeatureVoteEntity> create(String userId, String featureId) async {
|
|
54
|
-
final result = UserFeatureVoteEntity(
|
|
55
|
-
id: userId,
|
|
56
|
-
userId: userId,
|
|
57
|
-
creationDate: DateTime.now(),
|
|
58
|
-
featureId: featureId,
|
|
59
|
-
);
|
|
60
|
-
final batch = _client.batch();
|
|
61
|
-
batch.set(_featureVoteCollection(featureId).doc(userId), result);
|
|
62
|
-
batch.update(
|
|
63
|
-
_client.collection('feature_requests').doc(featureId),
|
|
64
|
-
{'votes': FieldValue.increment(1), 'last_update_date': FieldValue.serverTimestamp()},
|
|
65
|
-
);
|
|
66
|
-
await batch.commit();
|
|
67
|
-
return result;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
Future<void> delete(String featureId, String voteId) async {
|
|
71
|
-
final batch = _client.batch();
|
|
72
|
-
batch.delete(_featureVoteCollection(featureId).doc(voteId));
|
|
73
|
-
batch.update(
|
|
74
|
-
_client.collection('feature_requests').doc(featureId),
|
|
75
|
-
{'votes': FieldValue.increment(-1), 'last_update_date': FieldValue.serverTimestamp()},
|
|
76
|
-
);
|
|
77
|
-
await batch.commit();
|
|
78
|
-
}
|
|
79
|
-
}
|
|
@@ -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
|
-
}
|