feat: Add custom patches sources

This commit is contained in:
Alberto Ponces 2022-09-07 02:37:25 +01:00
parent 10fbbe845e
commit 78317223cc
11 changed files with 259 additions and 206 deletions

View File

@ -57,13 +57,6 @@
"widgetTitle": "Social Media", "widgetTitle": "Social Media",
"widgetSubtitle": "We are online!" "widgetSubtitle": "We are online!"
}, },
"sourcesCard": {
"widgetTitle": "Sources",
"widgetSubtitle": "Add your custom sources",
"organizationLabel": "Organization",
"patchesSourceLabel" : "Patches Source",
"integrationsSourceLabel": "Integrations Source"
},
"appSelectorView": { "appSelectorView": {
"searchBarHint": "Search applications", "searchBarHint": "Search applications",
"storageButton": "Storage", "storageButton": "Storage",
@ -102,6 +95,12 @@
"languageLabel": "Language", "languageLabel": "Language",
"englishOption": "English", "englishOption": "English",
"frenchOption": "French", "frenchOption": "French",
"sourcesLabel": "Sources",
"sourcesLabelHint": "Add your custom sources",
"orgPatchesLabel" : "Patches Org",
"sourcesPatchesLabel" : "Patches Source",
"orgIntegrationsLabel": "Integrations Org",
"sourcesIntegrationsLabel": "Integrations Source",
"contributorsLabel": "Contributors", "contributorsLabel": "Contributors",
"contributorsHint": "A list of contributors of ReVanced", "contributorsHint": "A list of contributors of ReVanced",
"aboutLabel": "About", "aboutLabel": "About",

View File

@ -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';

View File

@ -4,7 +4,6 @@ import 'package:device_apps/device_apps.dart';
import 'package:github/github.dart'; import 'package:github/github.dart';
import 'package:injectable/injectable.dart'; import 'package:injectable/injectable.dart';
import 'package:package_info_plus/package_info_plus.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/models/patched_application.dart';
import 'package:revanced_manager/services/github_api.dart'; import 'package:revanced_manager/services/github_api.dart';
import 'package:revanced_manager/services/root_api.dart'; import 'package:revanced_manager/services/root_api.dart';
@ -14,34 +13,78 @@ import 'package:shared_preferences/shared_preferences.dart';
class ManagerAPI { class ManagerAPI {
final GithubAPI _githubAPI = GithubAPI(); final GithubAPI _githubAPI = GithubAPI();
final RootAPI _rootAPI = RootAPI(); final RootAPI _rootAPI = RootAPI();
final String patcherRepo = 'revanced-patcher';
final String cliRepo = 'revanced-cli';
late SharedPreferences _prefs; 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<void> initialize() async { Future<void> initialize() async {
_prefs = await SharedPreferences.getInstance(); _prefs = await SharedPreferences.getInstance();
} }
String getPatcherRepo() {
return defaultPatcherRepo;
}
String getPatchesRepo() {
return _prefs.getString('patchesRepo') ?? defaultPatchesRepo;
}
Future<void> setPatchesRepo(String value) async {
if (value.isEmpty) {
value = defaultPatchesRepo;
}
await _prefs.setString('patchesRepo', value);
}
String getIntegrationsRepo() {
return _prefs.getString('integrationsRepo') ?? defaultIntegrationsRepo;
}
Future<void> 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<void> setManagerRepo(String value) async {
if (value.isEmpty) {
value = defaultManagerRepo;
}
await _prefs.setString('managerRepo', value);
}
Future<File?> downloadPatches(String extension) async { Future<File?> downloadPatches(String extension) async {
return await _githubAPI.latestReleaseFile(extension, ghOrg, patchesRepo); return await _githubAPI.latestReleaseFile(extension, getPatchesRepo());
} }
Future<File?> downloadIntegrations(String extension) async { Future<File?> downloadIntegrations(String extension) async {
return await _githubAPI.latestReleaseFile( return await _githubAPI.latestReleaseFile(extension, getIntegrationsRepo());
extension,
ghOrg,
integrationsRepo,
);
} }
Future<File?> downloadManager(String extension) async { Future<File?> downloadManager(String extension) async {
return await _githubAPI.latestReleaseFile(extension, ghOrg, managerRepo); return await _githubAPI.latestReleaseFile(extension, getManagerRepo());
} }
Future<String?> getLatestPatchesVersion() async { Future<String?> getLatestPatchesVersion() async {
return await _githubAPI.latestReleaseVersion(ghOrg, patchesRepo); return await _githubAPI.latestReleaseVersion(getPatchesRepo());
} }
Future<String?> getLatestManagerVersion() async { Future<String?> getLatestManagerVersion() async {
return await _githubAPI.latestReleaseVersion(ghOrg, managerRepo); return await _githubAPI.latestReleaseVersion(getManagerRepo());
} }
Future<String> getCurrentManagerVersion() async { Future<String> getCurrentManagerVersion() async {
@ -140,8 +183,10 @@ class ManagerAPI {
} }
Future<bool> hasAppUpdates(String packageName, DateTime patchDate) async { Future<bool> hasAppUpdates(String packageName, DateTime patchDate) async {
List<RepositoryCommit> commits = List<RepositoryCommit> commits = await _githubAPI.getCommits(
await _githubAPI.getCommits(packageName, ghOrg, patchesRepo); packageName,
getPatchesRepo(),
);
return commits.any((c) => return commits.any((c) =>
c.commit != null && c.commit != null &&
c.commit!.author != null && c.commit!.author != null &&
@ -153,8 +198,10 @@ class ManagerAPI {
String packageName, String packageName,
DateTime patchDate, DateTime patchDate,
) async { ) async {
List<RepositoryCommit> commits = List<RepositoryCommit> commits = await _githubAPI.getCommits(
await _githubAPI.getCommits(packageName, ghOrg, patchesRepo); packageName,
getPatchesRepo(),
);
List<String> newCommits = commits List<String> newCommits = commits
.where((c) => .where((c) =>
c.commit != null && c.commit != null &&

View File

@ -1,36 +1,33 @@
import 'package:github/github.dart'; 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/github_api.dart';
import 'package:revanced_manager/services/manager_api.dart';
import 'package:stacked/stacked.dart'; import 'package:stacked/stacked.dart';
class ContributorsViewModel extends BaseViewModel { class ContributorsViewModel extends BaseViewModel {
final ManagerAPI _managerAPI = locator<ManagerAPI>();
final GithubAPI _githubAPI = GithubAPI(); final GithubAPI _githubAPI = GithubAPI();
List<Contributor> patcherContributors = [];
List<Contributor> patchesContributors = []; List<Contributor> patchesContributors = [];
List<Contributor> integrationsContributors = []; List<Contributor> integrationsContributors = [];
List<Contributor> patcherContributors = [];
List<Contributor> cliContributors = []; List<Contributor> cliContributors = [];
List<Contributor> managerContributors = []; List<Contributor> managerContributors = [];
Future<void> getContributors() async { Future<void> getContributors() async {
patcherContributors = await _githubAPI.getContributors(
_managerAPI.getPatcherRepo(),
);
patchesContributors = await _githubAPI.getContributors( patchesContributors = await _githubAPI.getContributors(
ghOrg, _managerAPI.getPatchesRepo(),
patchesRepo,
); );
integrationsContributors = await _githubAPI.getContributors( integrationsContributors = await _githubAPI.getContributors(
ghOrg, _managerAPI.getIntegrationsRepo(),
integrationsRepo,
);
patcherContributors = await _githubAPI.getContributors(
ghOrg,
patcherRepo,
); );
cliContributors = await _githubAPI.getContributors( cliContributors = await _githubAPI.getContributors(
ghOrg, _managerAPI.getCliRepo(),
cliRepo,
); );
managerContributors = await _githubAPI.getContributors( managerContributors = await _githubAPI.getContributors(
ghOrg, _managerAPI.getManagerRepo(),
managerRepo,
); );
notifyListeners(); notifyListeners();
} }

View File

@ -14,7 +14,7 @@ class NavigationViewModel extends IndexTrackingViewModel {
case 1: case 1:
return const PatcherView(); return const PatcherView();
case 2: case 2:
return SettingsView(); return const SettingsView();
default: default:
return const HomeView(); return const HomeView();
} }

View File

@ -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_tile_dialog.dart';
import 'package:revanced_manager/ui/widgets/settingsView/settings_section.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/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:revanced_manager/ui/widgets/shared/custom_sliver_app_bar.dart';
import 'package:stacked/stacked.dart'; import 'package:stacked/stacked.dart';
class SettingsView extends StatelessWidget { class SettingsView extends StatelessWidget {
final TextEditingController organizationController = TextEditingController(); const SettingsView({Key? key}) : super(key: key);
final TextEditingController patchesSourceController = TextEditingController();
final TextEditingController integrationsSourceController =
TextEditingController();
SettingsView({Key? key}) : super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -87,38 +81,18 @@ class SettingsView extends StatelessWidget {
], ],
), ),
SettingsTileDialog( SettingsTileDialog(
title: 'settingsView.languageLabel', title: 'settingsView.languageLabel',
subtitle: 'English', subtitle: 'English',
children: <Widget>[ onTap: () => model.showLanguagesDialog(context),
RadioListTile<String>( ),
title: I18nText('settingsView.englishOption'),
value: 'en',
groupValue: 'en',
onChanged: (value) {
model.updateLanguage(context, value);
Navigator.of(context).pop();
},
),
RadioListTile<String>(
title: I18nText('settingsView.frenchOption'),
value: 'fr',
groupValue: 'en',
onChanged: (value) {
model.updateLanguage(context, value);
Navigator.of(context).pop();
},
),
]),
const Divider(thickness: 1.0), const Divider(thickness: 1.0),
SettingsSection( SettingsSection(
title: 'settingsView.patcherSectionTitle', title: 'settingsView.patcherSectionTitle',
children: <Widget>[ children: <Widget>[
SourcesWidget( SettingsTileDialog(
title: 'settingsView.sourcesLabel', title: 'settingsView.sourcesLabel',
organizationController: organizationController, subtitle: 'settingsView.sourcesLabelHint',
patchesSourceController: patchesSourceController, onTap: () => model.showSourcesDialog(context),
integrationsSourceController:
integrationsSourceController,
), ),
], ],
), ),

View File

@ -3,9 +3,12 @@
import 'package:dynamic_themes/dynamic_themes.dart'; import 'package:dynamic_themes/dynamic_themes.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_i18n/flutter_i18n.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.locator.dart';
import 'package:revanced_manager/app/app.router.dart'; import 'package:revanced_manager/app/app.router.dart';
import 'package:revanced_manager/services/manager_api.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/stacked.dart';
import 'package:stacked_services/stacked_services.dart'; import 'package:stacked_services/stacked_services.dart';
import 'package:timeago/timeago.dart'; import 'package:timeago/timeago.dart';
@ -13,6 +16,10 @@ import 'package:timeago/timeago.dart';
class SettingsViewModel extends BaseViewModel { class SettingsViewModel extends BaseViewModel {
final NavigationService _navigationService = locator<NavigationService>(); final NavigationService _navigationService = locator<NavigationService>();
final ManagerAPI _managerAPI = locator<ManagerAPI>(); final ManagerAPI _managerAPI = locator<ManagerAPI>();
final TextEditingController _orgPatSourceController = TextEditingController();
final TextEditingController _patSourceController = TextEditingController();
final TextEditingController _orgIntSourceController = TextEditingController();
final TextEditingController _intSourceController = TextEditingController();
void setLanguage(String language) { void setLanguage(String language) {
notifyListeners(); notifyListeners();
@ -58,4 +65,115 @@ class SettingsViewModel extends BaseViewModel {
} }
notifyListeners(); notifyListeners();
} }
Future<void> showLanguagesDialog(BuildContext context) {
return showDialog(
context: context,
builder: (context) => SimpleDialog(
title: I18nText('settingsView.languageLabel'),
backgroundColor: Theme.of(context).colorScheme.surface,
children: <Widget>[
RadioListTile<String>(
title: I18nText('settingsView.englishOption'),
value: 'en',
groupValue: 'en',
onChanged: (value) {
updateLanguage(context, value);
Navigator.of(context).pop();
},
),
],
),
);
}
Future<void> 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: <Widget>[
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,
),
);
}
} }

View File

@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_i18n/flutter_i18n.dart'; import 'package:flutter_i18n/flutter_i18n.dart';
import 'package:revanced_manager/app/app.locator.dart'; import 'package:revanced_manager/app/app.locator.dart';
import 'package:revanced_manager/services/github_api.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/views/home/home_viewmodel.dart';
import 'package:revanced_manager/ui/widgets/installerView/custom_material_button.dart'; import 'package:revanced_manager/ui/widgets/installerView/custom_material_button.dart';
import 'package:revanced_manager/ui/widgets/shared/custom_card.dart'; import 'package:revanced_manager/ui/widgets/shared/custom_card.dart';
@ -20,6 +20,7 @@ class LatestCommitCard extends StatefulWidget {
} }
class _LatestCommitCardState extends State<LatestCommitCard> { class _LatestCommitCardState extends State<LatestCommitCard> {
final ManagerAPI _managerAPI = locator<ManagerAPI>();
final GithubAPI _githubAPI = GithubAPI(); final GithubAPI _githubAPI = GithubAPI();
@override @override
@ -41,7 +42,9 @@ class _LatestCommitCardState extends State<LatestCommitCard> {
), ),
), ),
FutureBuilder<String>( FutureBuilder<String>(
future: _githubAPI.latestCommitTime(ghOrg, patcherRepo), future: _githubAPI.latestCommitTime(
_managerAPI.getPatcherRepo(),
),
builder: (context, snapshot) => Text( builder: (context, snapshot) => Text(
snapshot.hasData && snapshot.data!.isNotEmpty snapshot.hasData && snapshot.data!.isNotEmpty
? FlutterI18n.translate( ? FlutterI18n.translate(
@ -68,7 +71,9 @@ class _LatestCommitCardState extends State<LatestCommitCard> {
), ),
), ),
FutureBuilder<String>( FutureBuilder<String>(
future: _githubAPI.latestCommitTime(ghOrg, managerRepo), future: _githubAPI.latestCommitTime(
_managerAPI.getManagerRepo(),
),
builder: (context, snapshot) => builder: (context, snapshot) =>
snapshot.hasData && snapshot.data!.isNotEmpty snapshot.hasData && snapshot.data!.isNotEmpty
? I18nText( ? I18nText(

View File

@ -4,6 +4,7 @@ class CustomTextField extends StatelessWidget {
final TextEditingController inputController; final TextEditingController inputController;
final Widget label; final Widget label;
final String hint; final String hint;
final Widget? leadingIcon;
final Function(String)? onChanged; final Function(String)? onChanged;
const CustomTextField({ const CustomTextField({
@ -11,60 +12,59 @@ class CustomTextField extends StatelessWidget {
required this.inputController, required this.inputController,
required this.label, required this.label,
required this.hint, required this.hint,
this.leadingIcon,
required this.onChanged, required this.onChanged,
}) : super(key: key); }) : super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Column( return Padding(
crossAxisAlignment: CrossAxisAlignment.start, padding: const EdgeInsets.only(top: 4.0),
children: <Widget>[ child: TextField(
const SizedBox(height: 8), controller: inputController,
TextField( onChanged: onChanged,
controller: inputController, keyboardType: TextInputType.text,
onChanged: onChanged, decoration: InputDecoration(
keyboardType: TextInputType.text, icon: leadingIcon,
decoration: InputDecoration( label: label,
label: label, filled: true,
filled: true, fillColor: Theme.of(context).colorScheme.secondaryContainer,
fillColor: Theme.of(context).colorScheme.secondaryContainer, hintText: hint,
hintText: hint, contentPadding: const EdgeInsets.symmetric(
contentPadding: const EdgeInsets.symmetric( vertical: 8.0,
vertical: 0.0, horizontal: 16.0,
horizontal: 20.0, ),
border: OutlineInputBorder(
borderSide: BorderSide(
color: Theme.of(context).colorScheme.primary,
width: 1.0,
), ),
border: OutlineInputBorder( borderRadius: BorderRadius.circular(10),
borderSide: BorderSide( gapPadding: 4.0,
color: Theme.of(context).colorScheme.primary, ),
width: 1.0, focusedBorder: OutlineInputBorder(
), borderSide: BorderSide(
borderRadius: BorderRadius.circular(10), color: Theme.of(context).colorScheme.primary,
gapPadding: 4.0, width: 2.0,
), ),
focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(10),
borderSide: BorderSide( ),
color: Theme.of(context).colorScheme.primary, errorBorder: OutlineInputBorder(
width: 2.0, borderSide: BorderSide(
), color: Theme.of(context).colorScheme.error,
borderRadius: BorderRadius.circular(10), width: 1.0,
), ),
errorBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(10),
borderSide: const BorderSide( ),
color: Colors.red, enabledBorder: OutlineInputBorder(
width: 1.0, borderSide: BorderSide(
), color: Theme.of(context).colorScheme.primary,
borderRadius: BorderRadius.circular(10), width: 1.0,
),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Theme.of(context).colorScheme.primary,
width: 1.0,
),
borderRadius: BorderRadius.circular(10),
), ),
borderRadius: BorderRadius.circular(10),
), ),
), ),
], ),
); );
} }
} }

View File

@ -4,13 +4,13 @@ import 'package:flutter_i18n/flutter_i18n.dart';
class SettingsTileDialog extends StatelessWidget { class SettingsTileDialog extends StatelessWidget {
final String title; final String title;
final String subtitle; final String subtitle;
final List<Widget> children; final Function()? onTap;
const SettingsTileDialog({ const SettingsTileDialog({
Key? key, Key? key,
required this.title, required this.title,
required this.subtitle, required this.subtitle,
required this.children, required this.onTap,
}) : super(key: key); }) : super(key: key);
@override @override
@ -28,14 +28,7 @@ class SettingsTileDialog extends StatelessWidget {
), ),
), ),
subtitle: I18nText(subtitle), subtitle: I18nText(subtitle),
onTap: () => showDialog( onTap: onTap,
context: context,
builder: (context) => SimpleDialog(
title: I18nText(title),
backgroundColor: Theme.of(context).colorScheme.surface,
children: children,
),
),
); );
} }
} }

View File

@ -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: <Widget>[
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(),
);
}
}