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
package/bin/kasy.js
CHANGED
|
@@ -15,6 +15,7 @@ const { runRemove } = require('../lib/commands/remove');
|
|
|
15
15
|
const { runUpdate } = require('../lib/commands/update');
|
|
16
16
|
const { runDocs } = require('../lib/commands/docs');
|
|
17
17
|
const { runNotificationsText } = require('../lib/commands/notifications');
|
|
18
|
+
const { runSplash } = require('../lib/commands/splash');
|
|
18
19
|
const {
|
|
19
20
|
runConfigure: runIosConfigure,
|
|
20
21
|
runBuild: runIosBuild,
|
|
@@ -510,6 +511,26 @@ function buildProgram(language) {
|
|
|
510
511
|
t
|
|
511
512
|
);
|
|
512
513
|
|
|
514
|
+
applyLocalizedHelp(
|
|
515
|
+
program
|
|
516
|
+
.command('splash')
|
|
517
|
+
.argument('[directory]', 'Project folder (default: current directory)', '.')
|
|
518
|
+
.requiredOption('--light <path>', 'PNG with dark logo (shown in light mode)')
|
|
519
|
+
.requiredOption('--dark <path>', 'PNG with light logo (shown in dark mode)')
|
|
520
|
+
.option('--skip-generate', 'Only copy files, do not run flutter_native_splash:create', false)
|
|
521
|
+
.description(t('cli.command.splash.description'))
|
|
522
|
+
.action(async (directory, options) => {
|
|
523
|
+
const dir = directory || '.';
|
|
524
|
+
await runSplash(dir, {
|
|
525
|
+
language,
|
|
526
|
+
light: options.light,
|
|
527
|
+
dark: options.dark,
|
|
528
|
+
skipGenerate: options.skipGenerate,
|
|
529
|
+
});
|
|
530
|
+
}),
|
|
531
|
+
t
|
|
532
|
+
);
|
|
533
|
+
|
|
513
534
|
const notificationsCmd = program
|
|
514
535
|
.command('notifications')
|
|
515
536
|
.description(t('cli.command.notifications.description'));
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
const path = require('node:path');
|
|
2
|
+
const fs = require('fs-extra');
|
|
3
|
+
const fsp = require('node:fs/promises');
|
|
4
|
+
const { exec } = require('node:child_process');
|
|
5
|
+
const { promisify } = require('node:util');
|
|
6
|
+
const kleur = require('kleur');
|
|
7
|
+
const ui = require('../utils/ui');
|
|
8
|
+
const { printCompactHeader } = require('../utils/brand');
|
|
9
|
+
const { createTranslator, detectDefaultLanguage } = require('../utils/i18n');
|
|
10
|
+
|
|
11
|
+
const execAsync = promisify(exec);
|
|
12
|
+
|
|
13
|
+
const ASSETS_DIR = path.join('assets', 'images');
|
|
14
|
+
const LIGHT_NAME = 'splash_logo_light.png';
|
|
15
|
+
const DARK_NAME = 'splash_logo_dark.png';
|
|
16
|
+
|
|
17
|
+
const PNG_SIGNATURE = Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]);
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Parse the PNG IHDR chunk to read color type and detect transparency.
|
|
21
|
+
*
|
|
22
|
+
* PNG layout: 8-byte signature + chunks. The IHDR chunk is always first,
|
|
23
|
+
* placed at byte offset 8. It holds: length(4) + "IHDR"(4) + data(13) + crc(4).
|
|
24
|
+
* Color type is the 10th byte of data → absolute offset 25.
|
|
25
|
+
*
|
|
26
|
+
* Color types with alpha or transparency support:
|
|
27
|
+
* 4 = grayscale + alpha
|
|
28
|
+
* 6 = truecolor + alpha
|
|
29
|
+
* 3 = indexed (transparency only if a tRNS chunk is present)
|
|
30
|
+
*
|
|
31
|
+
* @param {string} filePath
|
|
32
|
+
* @returns {Promise<{ valid: boolean, hasAlpha: boolean, colorType: number, width: number, height: number }>}
|
|
33
|
+
*/
|
|
34
|
+
async function inspectPng(filePath) {
|
|
35
|
+
const handle = await fsp.open(filePath, 'r');
|
|
36
|
+
try {
|
|
37
|
+
const header = Buffer.alloc(33);
|
|
38
|
+
await handle.read(header, 0, 33, 0);
|
|
39
|
+
const sig = header.subarray(0, 8);
|
|
40
|
+
if (!sig.equals(PNG_SIGNATURE)) {
|
|
41
|
+
return { valid: false, hasAlpha: false, colorType: -1, width: 0, height: 0 };
|
|
42
|
+
}
|
|
43
|
+
const width = header.readUInt32BE(16);
|
|
44
|
+
const height = header.readUInt32BE(20);
|
|
45
|
+
const colorType = header.readUInt8(25);
|
|
46
|
+
|
|
47
|
+
let hasAlpha = colorType === 4 || colorType === 6;
|
|
48
|
+
|
|
49
|
+
if (!hasAlpha && colorType === 3) {
|
|
50
|
+
const { size } = await handle.stat();
|
|
51
|
+
const remaining = Math.min(size - 33, 256 * 1024);
|
|
52
|
+
if (remaining > 0) {
|
|
53
|
+
const tail = Buffer.alloc(remaining);
|
|
54
|
+
await handle.read(tail, 0, remaining, 33);
|
|
55
|
+
if (tail.includes(Buffer.from('tRNS'))) {
|
|
56
|
+
hasAlpha = true;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return { valid: true, hasAlpha, colorType, width, height };
|
|
62
|
+
} finally {
|
|
63
|
+
await handle.close();
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
async function assertKasyProject(projectDir, t) {
|
|
68
|
+
const kitSetupPath = path.join(projectDir, 'kit_setup.json');
|
|
69
|
+
const pubspecPath = path.join(projectDir, 'pubspec.yaml');
|
|
70
|
+
if (!(await fs.pathExists(kitSetupPath)) && !(await fs.pathExists(pubspecPath))) {
|
|
71
|
+
throw new Error(t('splash.error.notKasyProject'));
|
|
72
|
+
}
|
|
73
|
+
const assetsDir = path.join(projectDir, ASSETS_DIR);
|
|
74
|
+
await fs.ensureDir(assetsDir);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* @param {string} flagValue
|
|
79
|
+
* @param {string} role 'light' | 'dark'
|
|
80
|
+
*/
|
|
81
|
+
function resolveInputPath(flagValue, role) {
|
|
82
|
+
if (!flagValue) return null;
|
|
83
|
+
const expanded = flagValue.startsWith('~')
|
|
84
|
+
? path.join(require('node:os').homedir(), flagValue.slice(1))
|
|
85
|
+
: flagValue;
|
|
86
|
+
return path.resolve(expanded);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* @param {string} projectDir
|
|
91
|
+
* @param {{ light?: string, dark?: string, skipGenerate?: boolean, language?: string }} options
|
|
92
|
+
*/
|
|
93
|
+
async function runSplash(projectDir, options = {}) {
|
|
94
|
+
const language = options.language || detectDefaultLanguage();
|
|
95
|
+
const t = createTranslator(language);
|
|
96
|
+
|
|
97
|
+
printCompactHeader();
|
|
98
|
+
ui.intro(kleur.bold().cyan(t('splash.intro')));
|
|
99
|
+
|
|
100
|
+
await assertKasyProject(projectDir, t);
|
|
101
|
+
|
|
102
|
+
const lightPath = resolveInputPath(options.light, 'light');
|
|
103
|
+
const darkPath = resolveInputPath(options.dark, 'dark');
|
|
104
|
+
|
|
105
|
+
if (!lightPath || !darkPath) {
|
|
106
|
+
ui.log.error(t('splash.error.bothRequired'));
|
|
107
|
+
ui.log.message(kleur.dim('kasy splash --light <light.png> --dark <dark.png>'));
|
|
108
|
+
process.exit(1);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
for (const [role, p] of [['light', lightPath], ['dark', darkPath]]) {
|
|
112
|
+
if (!(await fs.pathExists(p))) {
|
|
113
|
+
ui.log.error(t('splash.error.fileNotFound', { path: p }));
|
|
114
|
+
process.exit(1);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const inspectSpinner = ui.spinner();
|
|
119
|
+
inspectSpinner.start(t('splash.validating'));
|
|
120
|
+
|
|
121
|
+
let lightInfo;
|
|
122
|
+
let darkInfo;
|
|
123
|
+
try {
|
|
124
|
+
lightInfo = await inspectPng(lightPath);
|
|
125
|
+
darkInfo = await inspectPng(darkPath);
|
|
126
|
+
} catch (err) {
|
|
127
|
+
inspectSpinner.stop(`✖ ${err.message || t('splash.error.notPng')}`);
|
|
128
|
+
process.exit(1);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (!lightInfo.valid || !darkInfo.valid) {
|
|
132
|
+
inspectSpinner.stop(`✖ ${t('splash.error.notPng')}`);
|
|
133
|
+
process.exit(1);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
inspectSpinner.stop(t('splash.validated'));
|
|
137
|
+
|
|
138
|
+
const warnings = [];
|
|
139
|
+
if (!lightInfo.hasAlpha) {
|
|
140
|
+
warnings.push(t('splash.warn.noAlphaLight', { path: path.basename(lightPath) }));
|
|
141
|
+
}
|
|
142
|
+
if (!darkInfo.hasAlpha) {
|
|
143
|
+
warnings.push(t('splash.warn.noAlphaDark', { path: path.basename(darkPath) }));
|
|
144
|
+
}
|
|
145
|
+
if (lightInfo.width < 768 || lightInfo.height < 768) {
|
|
146
|
+
warnings.push(t('splash.warn.smallLight', {
|
|
147
|
+
w: lightInfo.width,
|
|
148
|
+
h: lightInfo.height,
|
|
149
|
+
}));
|
|
150
|
+
}
|
|
151
|
+
if (darkInfo.width < 768 || darkInfo.height < 768) {
|
|
152
|
+
warnings.push(t('splash.warn.smallDark', {
|
|
153
|
+
w: darkInfo.width,
|
|
154
|
+
h: darkInfo.height,
|
|
155
|
+
}));
|
|
156
|
+
}
|
|
157
|
+
if (warnings.length > 0) {
|
|
158
|
+
ui.note(warnings.map((w) => `${kleur.yellow('⚠')} ${w}`).join('\n'), t('splash.warn.title'));
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const destLight = path.join(projectDir, ASSETS_DIR, LIGHT_NAME);
|
|
162
|
+
const destDark = path.join(projectDir, ASSETS_DIR, DARK_NAME);
|
|
163
|
+
|
|
164
|
+
const copySpinner = ui.spinner();
|
|
165
|
+
copySpinner.start(t('splash.copying'));
|
|
166
|
+
await fs.copy(lightPath, destLight, { overwrite: true });
|
|
167
|
+
await fs.copy(darkPath, destDark, { overwrite: true });
|
|
168
|
+
copySpinner.stop(t('splash.copied'));
|
|
169
|
+
|
|
170
|
+
if (options.skipGenerate) {
|
|
171
|
+
ui.note(t('splash.skipGenerate.hint'), t('splash.skipGenerate.title'));
|
|
172
|
+
ui.outro(t('splash.done'));
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const genSpinner = ui.spinner();
|
|
177
|
+
genSpinner.start(t('splash.generating'));
|
|
178
|
+
const result = await runFlutterNativeSplash(projectDir);
|
|
179
|
+
if (result.ok) {
|
|
180
|
+
genSpinner.stop(t('splash.generated'));
|
|
181
|
+
} else {
|
|
182
|
+
genSpinner.stop(`⚠ ${t('splash.error.generateFailed')}`);
|
|
183
|
+
if (result.stderr) {
|
|
184
|
+
ui.log.message(kleur.dim(result.stderr.split('\n').slice(0, 8).join('\n')));
|
|
185
|
+
}
|
|
186
|
+
ui.log.message(kleur.dim('dart run flutter_native_splash:create'));
|
|
187
|
+
process.exit(1);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const summary = [
|
|
191
|
+
`${kleur.bold(t('splash.summary.light'))}: ${kleur.white(LIGHT_NAME)} (${lightInfo.width}x${lightInfo.height})`,
|
|
192
|
+
`${kleur.bold(t('splash.summary.dark'))}: ${kleur.white(DARK_NAME)} (${darkInfo.width}x${darkInfo.height})`,
|
|
193
|
+
].join('\n');
|
|
194
|
+
ui.note(summary, t('splash.summary.title'));
|
|
195
|
+
|
|
196
|
+
ui.outro(t('splash.done'));
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
async function runFlutterNativeSplash(projectDir) {
|
|
200
|
+
try {
|
|
201
|
+
const { stdout, stderr } = await execAsync(
|
|
202
|
+
'dart run flutter_native_splash:create',
|
|
203
|
+
{
|
|
204
|
+
cwd: projectDir,
|
|
205
|
+
maxBuffer: 16 * 1024 * 1024,
|
|
206
|
+
timeout: 240_000,
|
|
207
|
+
},
|
|
208
|
+
);
|
|
209
|
+
return { ok: true, stdout, stderr };
|
|
210
|
+
} catch (err) {
|
|
211
|
+
return {
|
|
212
|
+
ok: false,
|
|
213
|
+
error: err.message,
|
|
214
|
+
stdout: err.stdout || '',
|
|
215
|
+
stderr: err.stderr || '',
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
module.exports = { runSplash, inspectPng };
|
|
@@ -1,4 +1,13 @@
|
|
|
1
1
|
{
|
|
2
|
+
"1.13.0": {
|
|
3
|
+
"modules": {
|
|
4
|
+
"onboarding": {
|
|
5
|
+
"pt": "Botão \"Já tem conta? Entrar\" na primeira tela do onboarding — quem já tem conta entra direto sem passar pelo fluxo todo, sem precisar criar usuário anônimo antes",
|
|
6
|
+
"en": "\"Already have an account? Log in\" button on the first onboarding screen — returning users go straight to sign-in instead of going through the full flow as an anonymous user first",
|
|
7
|
+
"es": "Botón \"¿Ya tienes cuenta? Iniciar sesión\" en la primera pantalla del onboarding — quien ya tiene cuenta entra directo sin pasar por todo el flujo como usuario anónimo"
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
},
|
|
2
11
|
"1.10.0": {
|
|
3
12
|
"modules": {
|
|
4
13
|
"widget": {
|
|
@@ -108,7 +108,7 @@ void run(SharedPreferences prefs) => runApp(
|
|
|
108
108
|
// mode: ThemeMode.dark,
|
|
109
109
|
// ),
|
|
110
110
|
// See ./docs/theme.md for more details
|
|
111
|
-
class MyApp extends
|
|
111
|
+
class MyApp extends ConsumerStatefulWidget {
|
|
112
112
|
final SharedPreferences sharedPreferences;
|
|
113
113
|
|
|
114
114
|
const MyApp({
|
|
@@ -116,23 +116,42 @@ class MyApp extends ConsumerWidget {
|
|
|
116
116
|
required this.sharedPreferences,
|
|
117
117
|
});
|
|
118
118
|
|
|
119
|
+
@override
|
|
120
|
+
ConsumerState<MyApp> createState() => _MyAppState();
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
class _MyAppState extends ConsumerState<MyApp> {
|
|
124
|
+
late final AppTheme _appTheme;
|
|
125
|
+
|
|
126
|
+
@override
|
|
127
|
+
void initState() {
|
|
128
|
+
super.initState();
|
|
129
|
+
_appTheme = AppTheme.uniform(
|
|
130
|
+
sharedPreferences: widget.sharedPreferences,
|
|
131
|
+
themeFactory: const UniversalThemeFactory(),
|
|
132
|
+
lightColors: KasyColors.light(),
|
|
133
|
+
darkColors: KasyColors.dark(),
|
|
134
|
+
textTheme: KasyTextTheme.build(),
|
|
135
|
+
defaultMode: ThemeMode.system,
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
@override
|
|
140
|
+
void dispose() {
|
|
141
|
+
_appTheme.dispose();
|
|
142
|
+
super.dispose();
|
|
143
|
+
}
|
|
144
|
+
|
|
119
145
|
// This widget is the root of your application.
|
|
120
146
|
@override
|
|
121
|
-
Widget build(BuildContext context
|
|
147
|
+
Widget build(BuildContext context) {
|
|
122
148
|
ErrorWidget.builder = (FlutterErrorDetails details) {
|
|
123
149
|
return AppErrorWidget(error: details);
|
|
124
150
|
};
|
|
125
151
|
final goRouter = ref.watch(goRouterProvider);
|
|
126
152
|
|
|
127
153
|
return ThemeProvider(
|
|
128
|
-
notifier:
|
|
129
|
-
sharedPreferences: sharedPreferences,
|
|
130
|
-
themeFactory: const UniversalThemeFactory(),
|
|
131
|
-
lightColors: KasyColors.light(),
|
|
132
|
-
darkColors: KasyColors.dark(),
|
|
133
|
-
textTheme: KasyTextTheme.build(),
|
|
134
|
-
defaultMode: ThemeMode.light,
|
|
135
|
-
),
|
|
154
|
+
notifier: _appTheme,
|
|
136
155
|
child: Builder(builder: (context) {
|
|
137
156
|
return WebDevicePreview.wrap(
|
|
138
157
|
child: DevInspector.wrap(
|
|
@@ -138,7 +138,7 @@ void run(SharedPreferences prefs) => runApp(
|
|
|
138
138
|
// mode: ThemeMode.dark,
|
|
139
139
|
// ),
|
|
140
140
|
// See ./docs/theme.md for more details
|
|
141
|
-
class MyApp extends
|
|
141
|
+
class MyApp extends ConsumerStatefulWidget {
|
|
142
142
|
final SharedPreferences sharedPreferences;
|
|
143
143
|
|
|
144
144
|
const MyApp({
|
|
@@ -146,23 +146,42 @@ class MyApp extends ConsumerWidget {
|
|
|
146
146
|
required this.sharedPreferences,
|
|
147
147
|
});
|
|
148
148
|
|
|
149
|
+
@override
|
|
150
|
+
ConsumerState<MyApp> createState() => _MyAppState();
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
class _MyAppState extends ConsumerState<MyApp> {
|
|
154
|
+
late final AppTheme _appTheme;
|
|
155
|
+
|
|
156
|
+
@override
|
|
157
|
+
void initState() {
|
|
158
|
+
super.initState();
|
|
159
|
+
_appTheme = AppTheme.uniform(
|
|
160
|
+
sharedPreferences: widget.sharedPreferences,
|
|
161
|
+
themeFactory: const UniversalThemeFactory(),
|
|
162
|
+
lightColors: KasyColors.light(),
|
|
163
|
+
darkColors: KasyColors.dark(),
|
|
164
|
+
textTheme: KasyTextTheme.build(),
|
|
165
|
+
defaultMode: ThemeMode.system,
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
@override
|
|
170
|
+
void dispose() {
|
|
171
|
+
_appTheme.dispose();
|
|
172
|
+
super.dispose();
|
|
173
|
+
}
|
|
174
|
+
|
|
149
175
|
// This widget is the root of your application.
|
|
150
176
|
@override
|
|
151
|
-
Widget build(BuildContext context
|
|
177
|
+
Widget build(BuildContext context) {
|
|
152
178
|
ErrorWidget.builder = (FlutterErrorDetails details) {
|
|
153
179
|
return AppErrorWidget(error: details);
|
|
154
180
|
};
|
|
155
181
|
final goRouter = ref.watch(goRouterProvider);
|
|
156
182
|
|
|
157
183
|
return ThemeProvider(
|
|
158
|
-
notifier:
|
|
159
|
-
sharedPreferences: sharedPreferences,
|
|
160
|
-
themeFactory: const UniversalThemeFactory(),
|
|
161
|
-
lightColors: KasyColors.light(),
|
|
162
|
-
darkColors: KasyColors.dark(),
|
|
163
|
-
textTheme: KasyTextTheme.build(),
|
|
164
|
-
defaultMode: ThemeMode.light,
|
|
165
|
-
),
|
|
184
|
+
notifier: _appTheme,
|
|
166
185
|
child: Builder(builder: (context) {
|
|
167
186
|
return WebDevicePreview.wrap(
|
|
168
187
|
child: DevInspector.wrap(
|
|
@@ -1,151 +1,27 @@
|
|
|
1
1
|
# Feature Patches
|
|
2
2
|
|
|
3
|
-
Este diretório
|
|
3
|
+
Este diretório guarda **patches por feature**: arquivos extras que são copiados para o projeto gerado quando o usuário seleciona um módulo específico no wizard do CLI.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Estado atual
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
features/
|
|
9
|
-
ci/ ← patch aplicado quando o usuário seleciona o módulo "ci"
|
|
10
|
-
.github/
|
|
11
|
-
workflows/
|
|
12
|
-
app.yml
|
|
13
|
-
.gitlab-ci.yml
|
|
14
|
-
...
|
|
15
|
-
web/ ← patch aplicado quando "web" é selecionado
|
|
16
|
-
web/
|
|
17
|
-
index.html
|
|
18
|
-
...
|
|
19
|
-
widget/ ← patch aplicado quando "widget" é selecionado
|
|
20
|
-
android/
|
|
21
|
-
...
|
|
22
|
-
```
|
|
7
|
+
Este diretório está intencionalmente vazio (só este README). Todas as features já vivem dentro de `Firebase/` e são incluídas em `cli/templates/firebase/` pelo `cli/scripts/bundle-template.js` no momento do publish. Features opcionais são controladas por `removeModuleDirs()` em `cli/lib/scaffold/shared/generator-utils.js` — quando a feature não é selecionada, sua pasta é removida do projeto gerado.
|
|
23
8
|
|
|
24
|
-
|
|
9
|
+
## Quando criar um patch aqui
|
|
25
10
|
|
|
26
|
-
|
|
11
|
+
Apenas quando a feature precisar de arquivos que **não existem** em `Firebase/`. Se o mesmo caminho já existe em `Firebase/`, **não crie patch** — o patch vai sobrescrever silenciosamente o template atualizado pela versão (provavelmente antiga) que estiver no patch, causando regressão no projeto do cliente (perda de integrações, validações, UI nova, etc.).
|
|
27
12
|
|
|
28
|
-
##
|
|
13
|
+
## Como adicionar um patch
|
|
29
14
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
| `widget` | Não | Os arquivos do widget (iOS + Android + Dart) já vão no template base. Quando o módulo não é selecionado, `removeAndroidWidgetArtifacts` + `writeNoOpAdminHomeWidgets` em `generate.js` limpam o que sobra. |
|
|
35
|
-
| `llm_chat` | Não | Apenas `LLM_CHAT_ENDPOINT` via dart-define. A chave da API LLM fica no servidor (Firebase Secret / Supabase Secret) — nunca no app. |
|
|
36
|
-
| `sentry` | Não | Apenas dart-define (`SENTRY_DSN`) |
|
|
37
|
-
| `analytics` | Não | Apenas dart-define |
|
|
38
|
-
| `facebook` | Não | Token já substituído via `buildTokens` |
|
|
39
|
-
| `revenuecat`| Não | Apenas dart-define (`REVENUECAT_KEY_*`) |
|
|
40
|
-
| `onboarding`| Não | Habilitado via dart-define / `features_config.json` |
|
|
41
|
-
| `feedback` | Não | Habilitado via dart-define / `features_config.json` |
|
|
42
|
-
| `local_notifications` | ✅ Sim | Copia `lib/features/local_reminder/` com UI, provider e repositório |
|
|
15
|
+
1. Crie `features/{modulo}/`.
|
|
16
|
+
2. Coloque os arquivos exatamente como devem aparecer no projeto gerado (caminhos relativos à raiz do projeto).
|
|
17
|
+
3. Adicione o nome do módulo a `ALLOWED_FEATURE_PATCHES` em `cli/scripts/check-feature-patches.js`, com um comentário de uma linha explicando por que o patch é necessário.
|
|
18
|
+
4. O engine aplica os patches depois de copiar o template base e o patch do backend.
|
|
43
19
|
|
|
44
|
-
##
|
|
20
|
+
## Drift guard
|
|
45
21
|
|
|
46
|
-
|
|
47
|
-
2. Coloque dentro dela os arquivos **exatamente como devem aparecer** no projeto gerado (caminhos relativos à raiz do projeto).
|
|
48
|
-
3. O engine aplica os patches após copiar o template base e após aplicar o patch do backend.
|
|
22
|
+
`cli/scripts/check-feature-patches.js` roda no `npm prepack` (antes de qualquer `npm publish` ou `npm pack`). O build falha se:
|
|
49
23
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
> precisam ser atualizados para chamar essa etapa quando a unificação for concluída.
|
|
24
|
+
- alguma pasta dentro de `features/` não estiver listada em `ALLOWED_FEATURE_PATCHES`, ou
|
|
25
|
+
- algum arquivo dentro de um patch permitido tiver caminho equivalente em `Firebase/` (significa que o patch é duplicata desatualizada).
|
|
53
26
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
## Feature: `local_notifications` — Notificações Locais Agendadas
|
|
57
|
-
|
|
58
|
-
> **Plataformas:** iOS e Android apenas (sem suporte Web/Desktop).
|
|
59
|
-
|
|
60
|
-
### O que esta feature faz
|
|
61
|
-
|
|
62
|
-
Permite ao usuário configurar um lembrete recorrente diretamente no app, sem servidor. A notificação é agendada localmente via `flutter_local_notifications` e persiste entre sessões via `SharedPreferences`.
|
|
63
|
-
|
|
64
|
-
### Tipos de lembrete suportados
|
|
65
|
-
|
|
66
|
-
| Tipo | Comportamento |
|
|
67
|
-
|-----------------|------------------------------------------------------|
|
|
68
|
-
| `daily` | Dispara todo dia no horário escolhido |
|
|
69
|
-
| `weekly` | Dispara uma vez por semana no dia + horário escolhido |
|
|
70
|
-
| `specificDate` | Dispara uma única vez na data + hora escolhida |
|
|
71
|
-
|
|
72
|
-
### Entrada do usuário
|
|
73
|
-
|
|
74
|
-
Acesso via **Settings → Lembretes** (`/reminder`):
|
|
75
|
-
|
|
76
|
-
1. **Toggle "Ativar lembrete"** — liga/desliga (cancela notificação pendente ao desligar)
|
|
77
|
-
2. **Segmented button "Repetir"** — escolhe o tipo: `Todo dia / Toda semana / Data específica`
|
|
78
|
-
3. **Horário** — `TimePicker` nativo do sistema
|
|
79
|
-
4. **Dia da semana** — `ChoiceChip` (seg–dom), aparece somente no modo semanal
|
|
80
|
-
5. **Data e hora** — `DatePicker` + `TimePicker` em sequência, aparece somente no modo data específica
|
|
81
|
-
|
|
82
|
-
### Estrutura de arquivos gerados no projeto
|
|
83
|
-
|
|
84
|
-
```
|
|
85
|
-
lib/features/local_reminder/
|
|
86
|
-
repositories/
|
|
87
|
-
reminder_preferences.dart ← SharedPreferences (load/save ReminderState)
|
|
88
|
-
providers/
|
|
89
|
-
reminder_notifier.dart ← AsyncNotifier (keepAlive) + agenda/cancela
|
|
90
|
-
reminder_notifier.g.dart ← gerado pelo build_runner (provider: reminderProvider)
|
|
91
|
-
ui/
|
|
92
|
-
reminder_page.dart ← ReminderPage + _ReminderForm + subwidgets
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
### Integrações automáticas pelo CLI
|
|
96
|
-
|
|
97
|
-
| Arquivo | O que é adicionado |
|
|
98
|
-
|--------------------------------|---------------------------------------------------------|
|
|
99
|
-
| `lib/core/config/features.dart` | `const bool withLocalNotifications = true/false;` |
|
|
100
|
-
| `lib/router.dart` | Import + `GoRoute(path: '/reminder', ...)` |
|
|
101
|
-
| `lib/features/settings/settings_page.dart` | Tile "Lembretes" → `context.push('/reminder')` |
|
|
102
|
-
|
|
103
|
-
### Model — `ReminderState`
|
|
104
|
-
|
|
105
|
-
```dart
|
|
106
|
-
class ReminderState {
|
|
107
|
-
final bool enabled;
|
|
108
|
-
final ReminderType type; // daily | weekly | specificDate
|
|
109
|
-
final int hour;
|
|
110
|
-
final int minute;
|
|
111
|
-
final int dayOfWeek; // 1=Segunda … 7=Domingo
|
|
112
|
-
final DateTime? date; // usado somente para specificDate
|
|
113
|
-
}
|
|
114
|
-
```
|
|
115
|
-
|
|
116
|
-
### Notificação agendada
|
|
117
|
-
|
|
118
|
-
ID fixo `42`. Texto configurado via i18n (`dailyReminder.title` / `dailyReminder.body`).
|
|
119
|
-
Ao salvar qualquer mudança, o provider cancela o ID 42 e re-agenda com os novos parâmetros.
|
|
120
|
-
|
|
121
|
-
### Dependências (já no core — não adicionadas pelo módulo)
|
|
122
|
-
|
|
123
|
-
- `flutter_local_notifications`
|
|
124
|
-
- `flutter_timezone` + `timezone`
|
|
125
|
-
- `shared_preferences`
|
|
126
|
-
- `riverpod_annotation`
|
|
127
|
-
|
|
128
|
-
### Traduções adicionadas
|
|
129
|
-
|
|
130
|
-
Chaves em `lib/i18n/{pt,en,es}.i18n.json`:
|
|
131
|
-
|
|
132
|
-
```json
|
|
133
|
-
"reminderPage": {
|
|
134
|
-
"title": "Lembretes",
|
|
135
|
-
"toggleLabel": "Ativar lembrete",
|
|
136
|
-
"typeLabel": "Repetir",
|
|
137
|
-
"daily": "Todo dia",
|
|
138
|
-
"weekly": "Toda semana",
|
|
139
|
-
"specificDate": "Data específica",
|
|
140
|
-
"timeLabel": "Horário",
|
|
141
|
-
"dayLabel": "Dia da semana",
|
|
142
|
-
"dateLabel": "Data e hora",
|
|
143
|
-
"selectDate": "Selecionar data e hora"
|
|
144
|
-
},
|
|
145
|
-
"dailyReminder": { "title": "Lembrete", "body": "Está na hora de beber água." },
|
|
146
|
-
"settings": { "reminders": "Lembretes" }
|
|
147
|
-
```
|
|
148
|
-
|
|
149
|
-
### Bug corrigido durante a implementação
|
|
150
|
-
|
|
151
|
-
`local_notifier.dart` tinha um método `scheduleFromNow()` com lógica invertida (agendava apenas quando **não** havia notificações pendentes) e usava `Future.delayed` que não sobrevive ao restart do app. O método foi **removido** completamente.
|
|
27
|
+
Esse é o mecanismo que impede o cenário em que patches divergem de `Firebase/` ao longo do tempo e o CLI passa a entregar código velho para os clientes.
|
|
@@ -273,7 +273,6 @@ async function writeRouter(projectDir, modules, packageName, defaultPaywall = 'b
|
|
|
273
273
|
lines.push(`import 'package:${pkg}/features/authentication/ui/signin_page.dart';`);
|
|
274
274
|
lines.push(`import 'package:${pkg}/features/authentication/ui/signup_page.dart';`);
|
|
275
275
|
if (withFeedback) {
|
|
276
|
-
lines.push(`import 'package:${pkg}/features/feedbacks/ui/component/add_feature_form.dart';`);
|
|
277
276
|
lines.push(`import 'package:${pkg}/features/feedbacks/ui/feedback_page.dart';`);
|
|
278
277
|
}
|
|
279
278
|
if (withLlmChat) {
|
|
@@ -374,11 +373,6 @@ async function writeRouter(projectDir, modules, packageName, defaultPaywall = 'b
|
|
|
374
373
|
lines.push(` path: '/feedback',`);
|
|
375
374
|
lines.push(` builder: (context, state) => const FeedbackPage(),`);
|
|
376
375
|
lines.push(` ),`);
|
|
377
|
-
lines.push(` GoRoute(`);
|
|
378
|
-
lines.push(` name: 'feedback_new',`);
|
|
379
|
-
lines.push(` path: '/feedback/new',`);
|
|
380
|
-
lines.push(` builder: (context, state) => const AddFeatureComponent(),`);
|
|
381
|
-
lines.push(` ),`);
|
|
382
376
|
}
|
|
383
377
|
|
|
384
378
|
// LLM Chat
|
|
@@ -596,6 +590,13 @@ class NoOpAnalyticsApi implements AnalyticsApi {
|
|
|
596
590
|
@override Future<void> identify(User user) async {}
|
|
597
591
|
}
|
|
598
592
|
|
|
593
|
+
// Stub kept for source-compat with code that still calls MixpanelAnalyticsApi.instance().
|
|
594
|
+
// Run: kasy add analytics to swap this file for the real Mixpanel-backed implementation.
|
|
595
|
+
class MixpanelAnalyticsApi extends NoOpAnalyticsApi {
|
|
596
|
+
const MixpanelAnalyticsApi._() : super();
|
|
597
|
+
factory MixpanelAnalyticsApi.instance() => const MixpanelAnalyticsApi._();
|
|
598
|
+
}
|
|
599
|
+
|
|
599
600
|
class AnalyticsObserver extends RouteObserver<ModalRoute<dynamic>> {
|
|
600
601
|
final AnalyticsApi analyticsApi;
|
|
601
602
|
final String? prefix;
|
|
@@ -1186,11 +1187,8 @@ async function stripPubspecDeps(projectDir, modules) {
|
|
|
1186
1187
|
}
|
|
1187
1188
|
|
|
1188
1189
|
/**
|
|
1189
|
-
* Patches
|
|
1190
|
+
* Patches files that always import sentry_flutter, removing the dependency
|
|
1190
1191
|
* when neither sentry, revenuecat, nor facebook modules are selected.
|
|
1191
|
-
* Affected files:
|
|
1192
|
-
* - lib/core/initializer/onstart_widget.dart
|
|
1193
|
-
* - lib/core/data/api/remote_config_api.dart
|
|
1194
1192
|
*
|
|
1195
1193
|
* @param {string} projectDir
|
|
1196
1194
|
*/
|
|
@@ -1200,15 +1198,16 @@ async function writeNoOpSentryUsages(projectDir) {
|
|
|
1200
1198
|
const files = [
|
|
1201
1199
|
path.join(projectDir, 'lib', 'core', 'initializer', 'onstart_widget.dart'),
|
|
1202
1200
|
path.join(projectDir, 'lib', 'core', 'data', 'api', 'remote_config_api.dart'),
|
|
1201
|
+
path.join(projectDir, 'lib', 'features', 'notifications', 'api', 'local_notifier.dart'),
|
|
1202
|
+
path.join(projectDir, 'lib', 'features', 'notifications', 'providers', 'models', 'notification.dart'),
|
|
1203
|
+
path.join(projectDir, 'lib', 'features', 'notifications', 'shared', 'notification_permission_bottom_sheet.dart'),
|
|
1203
1204
|
];
|
|
1204
1205
|
|
|
1205
1206
|
for (const filePath of files) {
|
|
1206
1207
|
if (!(await fs.pathExists(filePath))) continue;
|
|
1207
1208
|
let content = await fs.readFile(filePath, 'utf8');
|
|
1208
|
-
// Remove sentry import line
|
|
1209
1209
|
content = content.replace(sentryImport, '');
|
|
1210
|
-
|
|
1211
|
-
content = content.replace(/^[ \t]*Sentry\.captureException\([^)]+\);\n/gm, '');
|
|
1210
|
+
content = content.replace(/^[ \t]*Sentry\.captureException\([^)]*\);\n/gm, '');
|
|
1212
1211
|
await fs.outputFile(filePath, content, 'utf8');
|
|
1213
1212
|
}
|
|
1214
1213
|
}
|
|
@@ -1469,11 +1468,13 @@ class PremiumPageArgs {
|
|
|
1469
1468
|
'utf8',
|
|
1470
1469
|
);
|
|
1471
1470
|
|
|
1472
|
-
// 4. No-op premium_page_factory.dart (used by admin_paywalls)
|
|
1471
|
+
// 4. No-op premium_page_factory.dart (used by admin_paywalls).
|
|
1472
|
+
// Mirror the constants declared in Firebase/.../premium_page_factory.dart so that
|
|
1473
|
+
// admin_routes.dart compiles even without revenuecat selected.
|
|
1473
1474
|
await fs.outputFile(
|
|
1474
1475
|
path.join(projectDir, 'lib', 'features', 'subscription', 'ui', 'component', 'premium_page_factory.dart'),
|
|
1475
1476
|
`// No-op paywall factory. Run: kasy add revenuecat to activate.
|
|
1476
|
-
enum PaywallFactory { basic }
|
|
1477
|
+
enum PaywallFactory { basic, basicRow, minimal, withSwitch }
|
|
1477
1478
|
`,
|
|
1478
1479
|
'utf8',
|
|
1479
1480
|
);
|