diff --git a/packages/app/lib/branding.dart b/packages/app/lib/branding.dart index 0a13a7cd633..8a3acf75d10 100644 --- a/packages/app/lib/branding.dart +++ b/packages/app/lib/branding.dart @@ -17,6 +17,8 @@ const branding = Branding( 'assets/logo.svg.vec', ), ), + sourceCodeURL: 'https://github.com/nextcloud/neon', + issueTrackerURL: 'https://github.com/nextcloud/neon/issues', legalese: 'Copyright © 2023, provokateurin\nUnder GPLv3 license', ); diff --git a/packages/neon/neon/lib/l10n/en.arb b/packages/neon/neon/lib/l10n/en.arb index 17af0ba0bed..d8c3f2b313f 100644 --- a/packages/neon/neon/lib/l10n/en.arb +++ b/packages/neon/neon/lib/l10n/en.arb @@ -180,5 +180,7 @@ }, "accountOptionsInitialApp": "App to show initially", "accountOptionsAutomatic": "Automatic", - "licenses": "Licenses" + "licenses": "Licenses", + "sourceCode": "Source code", + "issueTracker": "Report a bug or request a feature" } diff --git a/packages/neon/neon/lib/l10n/localizations.dart b/packages/neon/neon/lib/l10n/localizations.dart index 1152702d3e3..c9720a385dc 100644 --- a/packages/neon/neon/lib/l10n/localizations.dart +++ b/packages/neon/neon/lib/l10n/localizations.dart @@ -688,6 +688,18 @@ abstract class AppLocalizations { /// In en, this message translates to: /// **'Licenses'** String get licenses; + + /// No description provided for @sourceCode. + /// + /// In en, this message translates to: + /// **'Source code'** + String get sourceCode; + + /// No description provided for @issueTracker. + /// + /// In en, this message translates to: + /// **'Report a bug or request a feature'** + String get issueTracker; } class _AppLocalizationsDelegate extends LocalizationsDelegate { diff --git a/packages/neon/neon/lib/l10n/localizations_en.dart b/packages/neon/neon/lib/l10n/localizations_en.dart index bd75edc46c1..34c2cef8096 100644 --- a/packages/neon/neon/lib/l10n/localizations_en.dart +++ b/packages/neon/neon/lib/l10n/localizations_en.dart @@ -344,4 +344,10 @@ class AppLocalizationsEn extends AppLocalizations { @override String get licenses => 'Licenses'; + + @override + String get sourceCode => 'Source code'; + + @override + String get issueTracker => 'Report a bug or request a feature'; } diff --git a/packages/neon/neon/lib/src/pages/settings.dart b/packages/neon/neon/lib/src/pages/settings.dart index 8923f7d4ea0..a9278e7faa7 100644 --- a/packages/neon/neon/lib/src/pages/settings.dart +++ b/packages/neon/neon/lib/src/pages/settings.dart @@ -24,6 +24,7 @@ import 'package:neon/src/utils/save_file.dart'; import 'package:neon/src/widgets/exception.dart'; import 'package:package_info_plus/package_info_plus.dart'; import 'package:provider/provider.dart'; +import 'package:url_launcher/url_launcher_string.dart'; enum SettingsCategories { apps, @@ -54,6 +55,7 @@ class _SettingsPageState extends State { final globalOptions = Provider.of(context); final accountsBloc = Provider.of(context, listen: false); final appImplementations = Provider.of>(context); + final branding = Branding.of(context); final appBar = AppBar( title: Text(AppLocalizations.of(context).settings), @@ -220,6 +222,34 @@ class _SettingsPageState extends State { title: Text(AppLocalizations.of(context).optionsCategoryOther), key: ValueKey(SettingsCategories.other.name), tiles: [ + if (branding.sourceCodeURL != null) + CustomSettingsTile( + leading: Icon( + Icons.code, + color: Theme.of(context).colorScheme.primary, + ), + title: Text(AppLocalizations.of(context).sourceCode), + onTap: () async { + await launchUrlString( + branding.sourceCodeURL!, + mode: LaunchMode.externalApplication, + ); + }, + ), + if (branding.issueTrackerURL != null) + CustomSettingsTile( + leading: Icon( + MdiIcons.textBoxEditOutline, + color: Theme.of(context).colorScheme.primary, + ), + title: Text(AppLocalizations.of(context).issueTracker), + onTap: () async { + await launchUrlString( + branding.issueTrackerURL!, + mode: LaunchMode.externalApplication, + ); + }, + ), CustomSettingsTile( leading: Icon( MdiIcons.scriptText, @@ -227,7 +257,6 @@ class _SettingsPageState extends State { ), title: Text(AppLocalizations.of(context).licenses), onTap: () async { - final branding = Branding.of(context); showLicensePage( context: context, applicationName: branding.name, diff --git a/packages/neon/neon/lib/src/theme/branding.dart b/packages/neon/neon/lib/src/theme/branding.dart index 02844f0b734..17b654a11d8 100644 --- a/packages/neon/neon/lib/src/theme/branding.dart +++ b/packages/neon/neon/lib/src/theme/branding.dart @@ -12,6 +12,8 @@ class Branding { const Branding({ required this.name, required this.logo, + this.sourceCodeURL, + this.issueTrackerURL, this.legalese, this.showLoginWithNextcloud = true, }); @@ -22,6 +24,12 @@ class Branding { /// Logo of the app shown on various places in the app. final Widget logo; + /// The URL where the source code can be seen + final String? sourceCodeURL; + + /// The URL where users can report bugs and request features + final String? issueTrackerURL; + /// A string to show in small print. /// /// Typically this is a copyright notice shown as the [AboutDialog.applicationLegalese].