diff --git a/lib/features/profile/views/profile_page.dart b/lib/features/profile/views/profile_page.dart index d7a047f..96032eb 100644 --- a/lib/features/profile/views/profile_page.dart +++ b/lib/features/profile/views/profile_page.dart @@ -35,6 +35,9 @@ import '../../../shared/widgets/model_avatar.dart'; /// Profile page (You tab) showing user info and main actions /// Enhanced with production-grade design tokens for better cohesion class ProfilePage extends ConsumerWidget { + static const _githubSponsorsUrl = 'https://github.com/sponsors/cogwheel0'; + static const _buyMeACoffeeUrl = 'https://www.buymeacoffee.com/cogwheel0'; + const ProfilePage({super.key}); @override @@ -161,11 +164,123 @@ class ProfilePage extends ConsumerWidget { end: 0, curve: AnimationCurves.pageTransition, ), + const SizedBox(height: Spacing.sectionGap), + _buildSupportSection(context) + .animate() + .fadeIn( + delay: AnimationDelay.medium, + duration: AnimationDuration.pageTransition, + ) + .slideY( + begin: 0.08, + end: 0, + curve: AnimationCurves.pageTransition, + ), ], ), ); } + Widget _buildSupportSection(BuildContext context) { + final theme = context.conduitTheme; + final textTheme = + theme.bodySmall?.copyWith(color: theme.textSecondary) ?? + TextStyle(color: theme.textSecondary); + + final supportTiles = [ + _buildSupportOption( + context, + icon: UiUtils.platformIcon( + ios: CupertinoIcons.heart, + android: Icons.favorite_border, + ), + title: AppLocalizations.of(context)!.githubSponsorsTitle, + subtitle: AppLocalizations.of(context)!.githubSponsorsSubtitle, + url: _githubSponsorsUrl, + color: theme.success, + ), + _buildSupportOption( + context, + icon: UiUtils.platformIcon( + ios: CupertinoIcons.gift, + android: Icons.coffee, + ), + title: AppLocalizations.of(context)!.buyMeACoffeeTitle, + subtitle: AppLocalizations.of(context)!.buyMeACoffeeSubtitle, + url: _buyMeACoffeeUrl, + color: theme.warning, + ), + ]; + + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + AppLocalizations.of(context)!.supportConduit, + style: theme.headingSmall?.copyWith(color: theme.textPrimary), + ), + const SizedBox(height: Spacing.xs), + Text( + AppLocalizations.of(context)!.supportConduitSubtitle, + style: textTheme, + ), + const SizedBox(height: Spacing.sm), + for (var i = 0; i < supportTiles.length; i++) ...[ + supportTiles[i], + if (i != supportTiles.length - 1) const SizedBox(height: Spacing.md), + ], + ], + ); + } + + Widget _buildSupportOption( + BuildContext context, { + required IconData icon, + required String title, + required String subtitle, + required String url, + required Color color, + }) { + final theme = context.conduitTheme; + return _ProfileSettingTile( + onTap: () => _openExternalLink(context, url), + isDestructive: false, + leading: _buildIconBadge(context, icon, color: color), + title: title, + subtitle: subtitle, + trailing: Icon( + UiUtils.platformIcon( + ios: CupertinoIcons.arrow_up_right, + android: Icons.open_in_new, + ), + color: theme.iconSecondary, + size: IconSize.small, + ), + ); + } + + Future _openExternalLink(BuildContext context, String url) async { + try { + final launched = await launchUrlString( + url, + mode: LaunchMode.externalApplication, + ); + + if (!launched && context.mounted) { + UiUtils.showMessage( + context, + AppLocalizations.of(context)!.errorMessage, + ); + } + } on PlatformException catch (_) { + if (!context.mounted) return; + UiUtils.showMessage(context, AppLocalizations.of(context)!.errorMessage); + } catch (_) { + if (!context.mounted) return; + UiUtils.showMessage(context, AppLocalizations.of(context)!.errorMessage); + } + } + Widget _buildProfileHeader( BuildContext context, dynamic user, diff --git a/lib/l10n/app_de.arb b/lib/l10n/app_de.arb index adf0c17..6f619b4 100644 --- a/lib/l10n/app_de.arb +++ b/lib/l10n/app_de.arb @@ -21,6 +21,12 @@ "description": "Statusnachricht nach einem erneuten Versuch ohne wiederhergestellte Verbindung" }, "account": "Konto", + "supportConduit": "Conduit unterstützen", + "supportConduitSubtitle": "Hilf, die Weiterentwicklung und neue Funktionen zu finanzieren.", + "githubSponsorsTitle": "GitHub Sponsors", + "githubSponsorsSubtitle": "Werde monatliche*r Sponsor*in und unterstütze die Roadmap.", + "buyMeACoffeeTitle": "Buy Me a Coffee", + "buyMeACoffeeSubtitle": "Bedanke dich mit einer einmaligen Spende.", "signOut": "Abmelden", "endYourSession": "Sitzung beenden", "defaultModel": "Standardmodell", diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index dd116bf..7fd27be 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -30,6 +30,30 @@ "@pleaseCheckConnection": {"description": "Generic connectivity hint after an error."}, "account": "Account", "@account": {"description": "Section header for account-related options."}, + "supportConduit": "Support Conduit", + "@supportConduit": { + "description": "Section header inviting the user to financially support the project." + }, + "supportConduitSubtitle": "Keep Conduit independent by funding ongoing development.", + "@supportConduitSubtitle": { + "description": "Subtitle explaining why donations are helpful." + }, + "githubSponsorsTitle": "GitHub Sponsors", + "@githubSponsorsTitle": { + "description": "Tile title linking to the GitHub Sponsors page." + }, + "githubSponsorsSubtitle": "Become a recurring sponsor to fund roadmap items.", + "@githubSponsorsSubtitle": { + "description": "Subtitle explaining the impact of recurring sponsorship." + }, + "buyMeACoffeeTitle": "Buy Me a Coffee", + "@buyMeACoffeeTitle": { + "description": "Tile title linking to the Buy Me a Coffee page." + }, + "buyMeACoffeeSubtitle": "Make a one-time donation to say thanks.", + "@buyMeACoffeeSubtitle": { + "description": "Subtitle encouraging one-time donations via Buy Me a Coffee." + }, "signOut": "Sign Out", "@signOut": {"description": "Button/title for signing out of the app."}, "endYourSession": "End your session", diff --git a/lib/l10n/app_fr.arb b/lib/l10n/app_fr.arb index 0d8b417..7a4eae4 100644 --- a/lib/l10n/app_fr.arb +++ b/lib/l10n/app_fr.arb @@ -21,6 +21,12 @@ "description": "Message d'état après une tentative de reconnexion sans succès" }, "account": "Compte", + "supportConduit": "Soutenir Conduit", + "supportConduitSubtitle": "Financez le développement continu et les nouvelles fonctionnalités.", + "githubSponsorsTitle": "GitHub Sponsors", + "githubSponsorsSubtitle": "Devenez sponsor récurrent pour soutenir la feuille de route.", + "buyMeACoffeeTitle": "Buy Me a Coffee", + "buyMeACoffeeSubtitle": "Faites un don ponctuel pour nous encourager.", "signOut": "Se déconnecter", "endYourSession": "Terminer votre session", "defaultModel": "Modèle par défaut", diff --git a/lib/l10n/app_it.arb b/lib/l10n/app_it.arb index 2b4876d..050066b 100644 --- a/lib/l10n/app_it.arb +++ b/lib/l10n/app_it.arb @@ -21,6 +21,12 @@ "description": "Messaggio di stato dopo un tentativo di riconnessione senza successo" }, "account": "Account", + "supportConduit": "Sostieni Conduit", + "supportConduitSubtitle": "Mantieni Conduit indipendente finanziando lo sviluppo continuo.", + "githubSponsorsTitle": "GitHub Sponsors", + "githubSponsorsSubtitle": "Diventa sponsor ricorrente per supportare la roadmap.", + "buyMeACoffeeTitle": "Buy Me a Coffee", + "buyMeACoffeeSubtitle": "Fai una donazione una tantum per dire grazie.", "signOut": "Esci", "endYourSession": "Termina la sessione", "defaultModel": "Modello predefinito", diff --git a/lib/l10n/app_localizations.dart b/lib/l10n/app_localizations.dart index d8acd28..0e29cbd 100644 --- a/lib/l10n/app_localizations.dart +++ b/lib/l10n/app_localizations.dart @@ -174,6 +174,42 @@ abstract class AppLocalizations { /// **'Account'** String get account; + /// Section header inviting the user to financially support the project. + /// + /// In en, this message translates to: + /// **'Support Conduit'** + String get supportConduit; + + /// Subtitle explaining why donations are helpful. + /// + /// In en, this message translates to: + /// **'Keep Conduit independent by funding ongoing development.'** + String get supportConduitSubtitle; + + /// Tile title linking to the GitHub Sponsors page. + /// + /// In en, this message translates to: + /// **'GitHub Sponsors'** + String get githubSponsorsTitle; + + /// Subtitle explaining the impact of recurring sponsorship. + /// + /// In en, this message translates to: + /// **'Become a recurring sponsor to fund roadmap items.'** + String get githubSponsorsSubtitle; + + /// Tile title linking to the Buy Me a Coffee page. + /// + /// In en, this message translates to: + /// **'Buy Me a Coffee'** + String get buyMeACoffeeTitle; + + /// Subtitle encouraging one-time donations via Buy Me a Coffee. + /// + /// In en, this message translates to: + /// **'Make a one-time donation to say thanks.'** + String get buyMeACoffeeSubtitle; + /// Button/title for signing out of the app. /// /// In en, this message translates to: diff --git a/lib/l10n/app_localizations_de.dart b/lib/l10n/app_localizations_de.dart index 776c2bd..6868c99 100644 --- a/lib/l10n/app_localizations_de.dart +++ b/lib/l10n/app_localizations_de.dart @@ -47,6 +47,27 @@ class AppLocalizationsDe extends AppLocalizations { @override String get account => 'Konto'; + @override + String get supportConduit => 'Conduit unterstützen'; + + @override + String get supportConduitSubtitle => + 'Hilf, die Weiterentwicklung und neue Funktionen zu finanzieren.'; + + @override + String get githubSponsorsTitle => 'GitHub Sponsors'; + + @override + String get githubSponsorsSubtitle => + 'Werde monatliche*r Sponsor*in und unterstütze die Roadmap.'; + + @override + String get buyMeACoffeeTitle => 'Buy Me a Coffee'; + + @override + String get buyMeACoffeeSubtitle => + 'Bedanke dich mit einer einmaligen Spende.'; + @override String get signOut => 'Abmelden'; diff --git a/lib/l10n/app_localizations_en.dart b/lib/l10n/app_localizations_en.dart index 644ec66..7d30344 100644 --- a/lib/l10n/app_localizations_en.dart +++ b/lib/l10n/app_localizations_en.dart @@ -47,6 +47,26 @@ class AppLocalizationsEn extends AppLocalizations { @override String get account => 'Account'; + @override + String get supportConduit => 'Support Conduit'; + + @override + String get supportConduitSubtitle => + 'Keep Conduit independent by funding ongoing development.'; + + @override + String get githubSponsorsTitle => 'GitHub Sponsors'; + + @override + String get githubSponsorsSubtitle => + 'Become a recurring sponsor to fund roadmap items.'; + + @override + String get buyMeACoffeeTitle => 'Buy Me a Coffee'; + + @override + String get buyMeACoffeeSubtitle => 'Make a one-time donation to say thanks.'; + @override String get signOut => 'Sign Out'; diff --git a/lib/l10n/app_localizations_fr.dart b/lib/l10n/app_localizations_fr.dart index 172552b..58dff0e 100644 --- a/lib/l10n/app_localizations_fr.dart +++ b/lib/l10n/app_localizations_fr.dart @@ -47,6 +47,27 @@ class AppLocalizationsFr extends AppLocalizations { @override String get account => 'Compte'; + @override + String get supportConduit => 'Soutenir Conduit'; + + @override + String get supportConduitSubtitle => + 'Financez le développement continu et les nouvelles fonctionnalités.'; + + @override + String get githubSponsorsTitle => 'GitHub Sponsors'; + + @override + String get githubSponsorsSubtitle => + 'Devenez sponsor récurrent pour soutenir la feuille de route.'; + + @override + String get buyMeACoffeeTitle => 'Buy Me a Coffee'; + + @override + String get buyMeACoffeeSubtitle => + 'Faites un don ponctuel pour nous encourager.'; + @override String get signOut => 'Se déconnecter'; diff --git a/lib/l10n/app_localizations_it.dart b/lib/l10n/app_localizations_it.dart index 5dfc1f2..785c9af 100644 --- a/lib/l10n/app_localizations_it.dart +++ b/lib/l10n/app_localizations_it.dart @@ -46,6 +46,27 @@ class AppLocalizationsIt extends AppLocalizations { @override String get account => 'Account'; + @override + String get supportConduit => 'Sostieni Conduit'; + + @override + String get supportConduitSubtitle => + 'Mantieni Conduit indipendente finanziando lo sviluppo continuo.'; + + @override + String get githubSponsorsTitle => 'GitHub Sponsors'; + + @override + String get githubSponsorsSubtitle => + 'Diventa sponsor ricorrente per supportare la roadmap.'; + + @override + String get buyMeACoffeeTitle => 'Buy Me a Coffee'; + + @override + String get buyMeACoffeeSubtitle => + 'Fai una donazione una tantum per dire grazie.'; + @override String get signOut => 'Esci';