diff --git a/assets/i18n/en.json b/assets/i18n/en.json index 5e611243..c48cf627 100644 --- a/assets/i18n/en.json +++ b/assets/i18n/en.json @@ -57,13 +57,6 @@ "widgetTitle": "Social Media", "widgetSubtitle": "We are online!" }, - "sourcesCard": { - "widgetTitle": "Sources", - "widgetSubtitle": "Add your custom sources", - "organizationLabel": "Organization", - "patchesSourceLabel" : "Patches Source", - "integrationsSourceLabel": "Integrations Source" - }, "appSelectorView": { "searchBarHint": "Search applications", "storageButton": "Storage", @@ -102,6 +95,12 @@ "languageLabel": "Language", "englishOption": "English", "frenchOption": "French", + "sourcesLabel": "Sources", + "sourcesLabelHint": "Add your custom sources", + "orgPatchesLabel" : "Patches Org", + "sourcesPatchesLabel" : "Patches Source", + "orgIntegrationsLabel": "Integrations Org", + "sourcesIntegrationsLabel": "Integrations Source", "contributorsLabel": "Contributors", "contributorsHint": "A list of contributors of ReVanced", "aboutLabel": "About", diff --git a/lib/constants.dart b/lib/constants.dart deleted file mode 100644 index 744fa04d..00000000 --- a/lib/constants.dart +++ /dev/null @@ -1,6 +0,0 @@ -String ghOrg = 'revanced'; -String patchesRepo = 'revanced-patches'; -String integrationsRepo = 'revanced-integrations'; -const String patcherRepo = 'revanced-patcher'; -const String cliRepo = 'revanced-cli'; -const String managerRepo = 'revanced-manager'; diff --git a/lib/services/manager_api.dart b/lib/services/manager_api.dart index 31fc7f8a..1fde1cd5 100644 --- a/lib/services/manager_api.dart +++ b/lib/services/manager_api.dart @@ -4,7 +4,6 @@ import 'package:device_apps/device_apps.dart'; import 'package:github/github.dart'; import 'package:injectable/injectable.dart'; import 'package:package_info_plus/package_info_plus.dart'; -import 'package:revanced_manager/constants.dart'; import 'package:revanced_manager/models/patched_application.dart'; import 'package:revanced_manager/services/github_api.dart'; import 'package:revanced_manager/services/root_api.dart'; @@ -14,34 +13,78 @@ import 'package:shared_preferences/shared_preferences.dart'; class ManagerAPI { final GithubAPI _githubAPI = GithubAPI(); final RootAPI _rootAPI = RootAPI(); + final String patcherRepo = 'revanced-patcher'; + final String cliRepo = 'revanced-cli'; late SharedPreferences _prefs; + String defaultPatcherRepo = 'revanced/revanced-patcher'; + String defaultPatchesRepo = 'revanced/revanced-patches'; + String defaultIntegrationsRepo = 'revanced/revanced-integrations'; + String defaultCliRepo = 'revanced/revanced-cli'; + String defaultManagerRepo = 'revanced/revanced-manager'; Future initialize() async { _prefs = await SharedPreferences.getInstance(); } + String getPatcherRepo() { + return defaultPatcherRepo; + } + + String getPatchesRepo() { + return _prefs.getString('patchesRepo') ?? defaultPatchesRepo; + } + + Future setPatchesRepo(String value) async { + if (value.isEmpty) { + value = defaultPatchesRepo; + } + await _prefs.setString('patchesRepo', value); + } + + String getIntegrationsRepo() { + return _prefs.getString('integrationsRepo') ?? defaultIntegrationsRepo; + } + + Future setIntegrationsRepo(String value) async { + if (value.isEmpty) { + value = defaultIntegrationsRepo; + } + await _prefs.setString('integrationsRepo', value); + } + + String getCliRepo() { + return defaultCliRepo; + } + + String getManagerRepo() { + return _prefs.getString('managerRepo') ?? defaultManagerRepo; + } + + Future setManagerRepo(String value) async { + if (value.isEmpty) { + value = defaultManagerRepo; + } + await _prefs.setString('managerRepo', value); + } + Future downloadPatches(String extension) async { - return await _githubAPI.latestReleaseFile(extension, ghOrg, patchesRepo); + return await _githubAPI.latestReleaseFile(extension, getPatchesRepo()); } Future downloadIntegrations(String extension) async { - return await _githubAPI.latestReleaseFile( - extension, - ghOrg, - integrationsRepo, - ); + return await _githubAPI.latestReleaseFile(extension, getIntegrationsRepo()); } Future downloadManager(String extension) async { - return await _githubAPI.latestReleaseFile(extension, ghOrg, managerRepo); + return await _githubAPI.latestReleaseFile(extension, getManagerRepo()); } Future getLatestPatchesVersion() async { - return await _githubAPI.latestReleaseVersion(ghOrg, patchesRepo); + return await _githubAPI.latestReleaseVersion(getPatchesRepo()); } Future getLatestManagerVersion() async { - return await _githubAPI.latestReleaseVersion(ghOrg, managerRepo); + return await _githubAPI.latestReleaseVersion(getManagerRepo()); } Future getCurrentManagerVersion() async { @@ -140,8 +183,10 @@ class ManagerAPI { } Future hasAppUpdates(String packageName, DateTime patchDate) async { - List commits = - await _githubAPI.getCommits(packageName, ghOrg, patchesRepo); + List commits = await _githubAPI.getCommits( + packageName, + getPatchesRepo(), + ); return commits.any((c) => c.commit != null && c.commit!.author != null && @@ -153,8 +198,10 @@ class ManagerAPI { String packageName, DateTime patchDate, ) async { - List commits = - await _githubAPI.getCommits(packageName, ghOrg, patchesRepo); + List commits = await _githubAPI.getCommits( + packageName, + getPatchesRepo(), + ); List newCommits = commits .where((c) => c.commit != null && diff --git a/lib/ui/views/contributors/contributors_viewmodel.dart b/lib/ui/views/contributors/contributors_viewmodel.dart index 38865c82..d3c4b4f4 100644 --- a/lib/ui/views/contributors/contributors_viewmodel.dart +++ b/lib/ui/views/contributors/contributors_viewmodel.dart @@ -1,36 +1,33 @@ import 'package:github/github.dart'; -import 'package:revanced_manager/constants.dart'; +import 'package:revanced_manager/app/app.locator.dart'; import 'package:revanced_manager/services/github_api.dart'; +import 'package:revanced_manager/services/manager_api.dart'; import 'package:stacked/stacked.dart'; class ContributorsViewModel extends BaseViewModel { + final ManagerAPI _managerAPI = locator(); final GithubAPI _githubAPI = GithubAPI(); + List patcherContributors = []; List patchesContributors = []; List integrationsContributors = []; - List patcherContributors = []; List cliContributors = []; List managerContributors = []; Future getContributors() async { + patcherContributors = await _githubAPI.getContributors( + _managerAPI.getPatcherRepo(), + ); patchesContributors = await _githubAPI.getContributors( - ghOrg, - patchesRepo, + _managerAPI.getPatchesRepo(), ); integrationsContributors = await _githubAPI.getContributors( - ghOrg, - integrationsRepo, - ); - patcherContributors = await _githubAPI.getContributors( - ghOrg, - patcherRepo, + _managerAPI.getIntegrationsRepo(), ); cliContributors = await _githubAPI.getContributors( - ghOrg, - cliRepo, + _managerAPI.getCliRepo(), ); managerContributors = await _githubAPI.getContributors( - ghOrg, - managerRepo, + _managerAPI.getManagerRepo(), ); notifyListeners(); } diff --git a/lib/ui/views/navigation/navigation_viewmodel.dart b/lib/ui/views/navigation/navigation_viewmodel.dart index 247f0b34..85974626 100644 --- a/lib/ui/views/navigation/navigation_viewmodel.dart +++ b/lib/ui/views/navigation/navigation_viewmodel.dart @@ -14,7 +14,7 @@ class NavigationViewModel extends IndexTrackingViewModel { case 1: return const PatcherView(); case 2: - return SettingsView(); + return const SettingsView(); default: return const HomeView(); } diff --git a/lib/ui/views/settings/settings_view.dart b/lib/ui/views/settings/settings_view.dart index cef8871e..ca48808f 100644 --- a/lib/ui/views/settings/settings_view.dart +++ b/lib/ui/views/settings/settings_view.dart @@ -7,17 +7,11 @@ import 'package:revanced_manager/ui/widgets/settingsView/custom_switch_tile.dart import 'package:revanced_manager/ui/widgets/settingsView/settings_tile_dialog.dart'; import 'package:revanced_manager/ui/widgets/settingsView/settings_section.dart'; import 'package:revanced_manager/ui/widgets/settingsView/social_media_widget.dart'; -import 'package:revanced_manager/ui/widgets/settingsView/sources_widget.dart'; import 'package:revanced_manager/ui/widgets/shared/custom_sliver_app_bar.dart'; import 'package:stacked/stacked.dart'; class SettingsView extends StatelessWidget { - final TextEditingController organizationController = TextEditingController(); - final TextEditingController patchesSourceController = TextEditingController(); - final TextEditingController integrationsSourceController = - TextEditingController(); - - SettingsView({Key? key}) : super(key: key); + const SettingsView({Key? key}) : super(key: key); @override Widget build(BuildContext context) { @@ -87,38 +81,18 @@ class SettingsView extends StatelessWidget { ], ), SettingsTileDialog( - title: 'settingsView.languageLabel', - subtitle: 'English', - children: [ - RadioListTile( - title: I18nText('settingsView.englishOption'), - value: 'en', - groupValue: 'en', - onChanged: (value) { - model.updateLanguage(context, value); - Navigator.of(context).pop(); - }, - ), - RadioListTile( - title: I18nText('settingsView.frenchOption'), - value: 'fr', - groupValue: 'en', - onChanged: (value) { - model.updateLanguage(context, value); - Navigator.of(context).pop(); - }, - ), - ]), + title: 'settingsView.languageLabel', + subtitle: 'English', + onTap: () => model.showLanguagesDialog(context), + ), const Divider(thickness: 1.0), SettingsSection( title: 'settingsView.patcherSectionTitle', children: [ - SourcesWidget( + SettingsTileDialog( title: 'settingsView.sourcesLabel', - organizationController: organizationController, - patchesSourceController: patchesSourceController, - integrationsSourceController: - integrationsSourceController, + subtitle: 'settingsView.sourcesLabelHint', + onTap: () => model.showSourcesDialog(context), ), ], ), diff --git a/lib/ui/views/settings/settings_viewmodel.dart b/lib/ui/views/settings/settings_viewmodel.dart index bec01446..05e8a0a7 100644 --- a/lib/ui/views/settings/settings_viewmodel.dart +++ b/lib/ui/views/settings/settings_viewmodel.dart @@ -3,9 +3,12 @@ import 'package:dynamic_themes/dynamic_themes.dart'; import 'package:flutter/material.dart'; import 'package:flutter_i18n/flutter_i18n.dart'; +import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:revanced_manager/app/app.locator.dart'; import 'package:revanced_manager/app/app.router.dart'; import 'package:revanced_manager/services/manager_api.dart'; +import 'package:revanced_manager/ui/widgets/installerView/custom_material_button.dart'; +import 'package:revanced_manager/ui/widgets/settingsView/custom_text_field.dart'; import 'package:stacked/stacked.dart'; import 'package:stacked_services/stacked_services.dart'; import 'package:timeago/timeago.dart'; @@ -13,6 +16,10 @@ import 'package:timeago/timeago.dart'; class SettingsViewModel extends BaseViewModel { final NavigationService _navigationService = locator(); final ManagerAPI _managerAPI = locator(); + final TextEditingController _orgPatSourceController = TextEditingController(); + final TextEditingController _patSourceController = TextEditingController(); + final TextEditingController _orgIntSourceController = TextEditingController(); + final TextEditingController _intSourceController = TextEditingController(); void setLanguage(String language) { notifyListeners(); @@ -58,4 +65,115 @@ class SettingsViewModel extends BaseViewModel { } notifyListeners(); } + + Future showLanguagesDialog(BuildContext context) { + return showDialog( + context: context, + builder: (context) => SimpleDialog( + title: I18nText('settingsView.languageLabel'), + backgroundColor: Theme.of(context).colorScheme.surface, + children: [ + RadioListTile( + title: I18nText('settingsView.englishOption'), + value: 'en', + groupValue: 'en', + onChanged: (value) { + updateLanguage(context, value); + Navigator.of(context).pop(); + }, + ), + ], + ), + ); + } + + Future showSourcesDialog(BuildContext context) async { + String patchesRepo = _managerAPI.getPatchesRepo(); + String integrationsRepo = _managerAPI.getIntegrationsRepo(); + _orgPatSourceController.text = patchesRepo.split('/')[0]; + _patSourceController.text = patchesRepo.split('/')[1]; + _orgIntSourceController.text = integrationsRepo.split('/')[0]; + _intSourceController.text = integrationsRepo.split('/')[1]; + return showDialog( + context: context, + builder: (context) => AlertDialog( + title: I18nText('settingsView.sourcesLabel'), + content: SingleChildScrollView( + child: Column( + children: [ + CustomTextField( + leadingIcon: Icon( + Icons.extension_outlined, + color: Theme.of(context).colorScheme.primary, + ), + inputController: _orgPatSourceController, + label: I18nText('settingsView.orgPatchesLabel'), + hint: patchesRepo.split('/')[0], + onChanged: (value) => notifyListeners(), + ), + const SizedBox(height: 8), + CustomTextField( + leadingIcon: const Icon( + Icons.extension_outlined, + color: Colors.transparent, + ), + inputController: _patSourceController, + label: I18nText('settingsView.sourcesPatchesLabel'), + hint: patchesRepo.split('/')[1], + onChanged: (value) => notifyListeners(), + ), + const SizedBox(height: 20), + CustomTextField( + leadingIcon: Icon( + Icons.merge_outlined, + color: Theme.of(context).colorScheme.primary, + ), + inputController: _orgIntSourceController, + label: I18nText('settingsView.orgIntegrationsLabel'), + hint: integrationsRepo.split('/')[0], + onChanged: (value) => notifyListeners(), + ), + const SizedBox(height: 8), + CustomTextField( + leadingIcon: const Icon( + Icons.merge_outlined, + color: Colors.transparent, + ), + inputController: _intSourceController, + label: I18nText('settingsView.sourcesIntegrationsLabel'), + hint: integrationsRepo.split('/')[1], + onChanged: (value) => notifyListeners(), + ), + ], + ), + ), + actions: [ + CustomMaterialButton( + isFilled: false, + label: I18nText('cancelButton'), + onPressed: () { + _orgPatSourceController.clear(); + _patSourceController.clear(); + _orgIntSourceController.clear(); + _intSourceController.clear(); + Navigator.of(context).pop(); + }, + ), + CustomMaterialButton( + label: I18nText('okButton'), + onPressed: () { + _managerAPI.setPatchesRepo( + '${_orgPatSourceController.text}/${_patSourceController.text}', + ); + _managerAPI.setIntegrationsRepo( + '${_orgIntSourceController.text}/${_intSourceController.text}', + ); + Navigator.of(context).pop(); + }, + ) + ], + backgroundColor: Theme.of(context).colorScheme.secondaryContainer, + ), + ); + } } diff --git a/lib/ui/widgets/homeView/latest_commit_card.dart b/lib/ui/widgets/homeView/latest_commit_card.dart index e78260ec..57f4cb52 100644 --- a/lib/ui/widgets/homeView/latest_commit_card.dart +++ b/lib/ui/widgets/homeView/latest_commit_card.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_i18n/flutter_i18n.dart'; import 'package:revanced_manager/app/app.locator.dart'; import 'package:revanced_manager/services/github_api.dart'; -import 'package:revanced_manager/constants.dart'; +import 'package:revanced_manager/services/manager_api.dart'; import 'package:revanced_manager/ui/views/home/home_viewmodel.dart'; import 'package:revanced_manager/ui/widgets/installerView/custom_material_button.dart'; import 'package:revanced_manager/ui/widgets/shared/custom_card.dart'; @@ -20,6 +20,7 @@ class LatestCommitCard extends StatefulWidget { } class _LatestCommitCardState extends State { + final ManagerAPI _managerAPI = locator(); final GithubAPI _githubAPI = GithubAPI(); @override @@ -41,7 +42,9 @@ class _LatestCommitCardState extends State { ), ), FutureBuilder( - future: _githubAPI.latestCommitTime(ghOrg, patcherRepo), + future: _githubAPI.latestCommitTime( + _managerAPI.getPatcherRepo(), + ), builder: (context, snapshot) => Text( snapshot.hasData && snapshot.data!.isNotEmpty ? FlutterI18n.translate( @@ -68,7 +71,9 @@ class _LatestCommitCardState extends State { ), ), FutureBuilder( - future: _githubAPI.latestCommitTime(ghOrg, managerRepo), + future: _githubAPI.latestCommitTime( + _managerAPI.getManagerRepo(), + ), builder: (context, snapshot) => snapshot.hasData && snapshot.data!.isNotEmpty ? I18nText( diff --git a/lib/ui/widgets/settingsView/custom_text_field.dart b/lib/ui/widgets/settingsView/custom_text_field.dart index 6ee7fcab..340f2318 100644 --- a/lib/ui/widgets/settingsView/custom_text_field.dart +++ b/lib/ui/widgets/settingsView/custom_text_field.dart @@ -4,6 +4,7 @@ class CustomTextField extends StatelessWidget { final TextEditingController inputController; final Widget label; final String hint; + final Widget? leadingIcon; final Function(String)? onChanged; const CustomTextField({ @@ -11,60 +12,59 @@ class CustomTextField extends StatelessWidget { required this.inputController, required this.label, required this.hint, + this.leadingIcon, required this.onChanged, }) : super(key: key); @override Widget build(BuildContext context) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const SizedBox(height: 8), - TextField( - controller: inputController, - onChanged: onChanged, - keyboardType: TextInputType.text, - decoration: InputDecoration( - label: label, - filled: true, - fillColor: Theme.of(context).colorScheme.secondaryContainer, - hintText: hint, - contentPadding: const EdgeInsets.symmetric( - vertical: 0.0, - horizontal: 20.0, + return Padding( + padding: const EdgeInsets.only(top: 4.0), + child: TextField( + controller: inputController, + onChanged: onChanged, + keyboardType: TextInputType.text, + decoration: InputDecoration( + icon: leadingIcon, + label: label, + filled: true, + fillColor: Theme.of(context).colorScheme.secondaryContainer, + hintText: hint, + contentPadding: const EdgeInsets.symmetric( + vertical: 8.0, + horizontal: 16.0, + ), + border: OutlineInputBorder( + borderSide: BorderSide( + color: Theme.of(context).colorScheme.primary, + width: 1.0, ), - border: OutlineInputBorder( - borderSide: BorderSide( - color: Theme.of(context).colorScheme.primary, - width: 1.0, - ), - borderRadius: BorderRadius.circular(10), - gapPadding: 4.0, + borderRadius: BorderRadius.circular(10), + gapPadding: 4.0, + ), + focusedBorder: OutlineInputBorder( + borderSide: BorderSide( + color: Theme.of(context).colorScheme.primary, + width: 2.0, ), - focusedBorder: OutlineInputBorder( - borderSide: BorderSide( - color: Theme.of(context).colorScheme.primary, - width: 2.0, - ), - borderRadius: BorderRadius.circular(10), + borderRadius: BorderRadius.circular(10), + ), + errorBorder: OutlineInputBorder( + borderSide: BorderSide( + color: Theme.of(context).colorScheme.error, + width: 1.0, ), - errorBorder: OutlineInputBorder( - borderSide: const BorderSide( - color: Colors.red, - width: 1.0, - ), - borderRadius: BorderRadius.circular(10), - ), - enabledBorder: OutlineInputBorder( - borderSide: BorderSide( - color: Theme.of(context).colorScheme.primary, - width: 1.0, - ), - borderRadius: BorderRadius.circular(10), + borderRadius: BorderRadius.circular(10), + ), + enabledBorder: OutlineInputBorder( + borderSide: BorderSide( + color: Theme.of(context).colorScheme.primary, + width: 1.0, ), + borderRadius: BorderRadius.circular(10), ), ), - ], + ), ); } } diff --git a/lib/ui/widgets/settingsView/settings_tile_dialog.dart b/lib/ui/widgets/settingsView/settings_tile_dialog.dart index 011e1599..565fb670 100644 --- a/lib/ui/widgets/settingsView/settings_tile_dialog.dart +++ b/lib/ui/widgets/settingsView/settings_tile_dialog.dart @@ -4,13 +4,13 @@ import 'package:flutter_i18n/flutter_i18n.dart'; class SettingsTileDialog extends StatelessWidget { final String title; final String subtitle; - final List children; + final Function()? onTap; const SettingsTileDialog({ Key? key, required this.title, required this.subtitle, - required this.children, + required this.onTap, }) : super(key: key); @override @@ -28,14 +28,7 @@ class SettingsTileDialog extends StatelessWidget { ), ), subtitle: I18nText(subtitle), - onTap: () => showDialog( - context: context, - builder: (context) => SimpleDialog( - title: I18nText(title), - backgroundColor: Theme.of(context).colorScheme.surface, - children: children, - ), - ), + onTap: onTap, ); } } diff --git a/lib/ui/widgets/settingsView/sources_widget.dart b/lib/ui/widgets/settingsView/sources_widget.dart deleted file mode 100644 index 1b4ea677..00000000 --- a/lib/ui/widgets/settingsView/sources_widget.dart +++ /dev/null @@ -1,74 +0,0 @@ -import 'package:expandable/expandable.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_i18n/flutter_i18n.dart'; -import 'package:revanced_manager/constants.dart'; -import 'package:revanced_manager/ui/widgets/settingsView/custom_text_field.dart'; -import 'package:revanced_manager/ui/widgets/shared/custom_card.dart'; - -class SourcesWidget extends StatelessWidget { - final String title; - final TextEditingController organizationController; - final TextEditingController patchesSourceController; - final TextEditingController integrationsSourceController; - - const SourcesWidget({ - Key? key, - required this.title, - required this.organizationController, - required this.patchesSourceController, - required this.integrationsSourceController, - }) : super(key: key); - - @override - Widget build(BuildContext context) { - return ExpandablePanel( - theme: ExpandableThemeData( - hasIcon: true, - iconColor: Theme.of(context).iconTheme.color, - iconPadding: const EdgeInsets.symmetric(vertical: 16.0), - animationDuration: const Duration(milliseconds: 400), - ), - header: ListTile( - contentPadding: EdgeInsets.zero, - title: I18nText( - 'sourcesCard.widgetTitle', - child: const Text( - '', - style: TextStyle( - fontSize: 20, - fontWeight: FontWeight.w500, - ), - ), - ), - subtitle: I18nText('sourcesCard.widgetSubtitle'), - ), - expanded: CustomCard( - child: Column( - children: [ - CustomTextField( - inputController: organizationController, - label: I18nText('sourcesCard.organizationLabel'), - hint: ghOrg, - onChanged: (value) => ghOrg = value, - ), - const SizedBox(height: 8), - CustomTextField( - inputController: patchesSourceController, - label: I18nText('sourcesCard.patchesSourceLabel'), - hint: patchesRepo, - onChanged: (value) => patchesRepo = value, - ), - const SizedBox(height: 8), - CustomTextField( - inputController: integrationsSourceController, - label: I18nText('sourcesCard.integrationsSourceLabel'), - hint: integrationsRepo, - onChanged: (value) => integrationsRepo = value, - ), - ], - ), - ), - collapsed: Container(), - ); - } -}