mirror of
https://github.com/JGeek00/adguard-home-manager.git
synced 2025-04-21 22:39:11 +00:00
Added screen to update server
This commit is contained in:
parent
4e65f80baf
commit
e02b598be9
13 changed files with 819 additions and 34 deletions
|
@ -3,4 +3,5 @@ class Urls {
|
|||
static const String gitHub = "https://github.com/JGeek00/adguard-home-manager";
|
||||
static const String customRuleDocs = "https://kb.adguard.com/en/general/how-to-create-your-own-ad-filters";
|
||||
static const String checkLatestReleaseUrl = "https://api.github.com/repos/JGeek00/adguard-home-manager/releases/latest";
|
||||
static const String adGuardHomeLatestRelease = "https://api.github.com/repos/AdGuardTeam/AdGuardHome/releases/latest";
|
||||
}
|
47
lib/functions/compare_versions.dart
Normal file
47
lib/functions/compare_versions.dart
Normal file
|
@ -0,0 +1,47 @@
|
|||
bool compareVersions({
|
||||
required String currentVersion,
|
||||
required String newVersion
|
||||
}) {
|
||||
final currentSplit = currentVersion.split('.').map((e) => int.parse(e)).toList();
|
||||
final newSplit = newVersion.split('.').map((e) => int.parse(e)).toList();
|
||||
|
||||
if (newSplit[0] > currentSplit[0]) {
|
||||
return true;
|
||||
}
|
||||
else if (newSplit[1] > currentSplit[1]) {
|
||||
return true;
|
||||
}
|
||||
else if (newSplit[2] > currentSplit[2]) {
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool compareBetaVersions({
|
||||
required String currentVersion,
|
||||
required String newVersion
|
||||
}) {
|
||||
final currentSplit = currentVersion.split('-')[0].split('.').map((e) => int.parse(e)).toList();
|
||||
final newSplit = newVersion.split('-')[0].split('.').map((e) => int.parse(e)).toList();
|
||||
|
||||
final currentBeta = int.parse(currentVersion.split('-')[1].replaceAll('b.', ''));
|
||||
final newBeta = int.parse(newVersion.split('-')[1].replaceAll('b.', ''));
|
||||
|
||||
if (newSplit[0] > currentSplit[0]) {
|
||||
return true;
|
||||
}
|
||||
else if (newSplit[1] > currentSplit[1]) {
|
||||
return true;
|
||||
}
|
||||
else if (newSplit[2] > currentSplit[2]) {
|
||||
return true;
|
||||
}
|
||||
else if (newBeta > currentBeta) {
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
17
lib/functions/open_url.dart
Normal file
17
lib/functions/open_url.dart
Normal file
|
@ -0,0 +1,17 @@
|
|||
import 'package:flutter_web_browser/flutter_web_browser.dart';
|
||||
|
||||
void openUrl(String url) {
|
||||
FlutterWebBrowser.openWebPage(
|
||||
url: url,
|
||||
customTabsOptions: const CustomTabsOptions(
|
||||
instantAppsEnabled: true,
|
||||
showTitle: true,
|
||||
urlBarHidingEnabled: false,
|
||||
),
|
||||
safariVCOptions: const SafariViewControllerOptions(
|
||||
barCollapsingEnabled: true,
|
||||
dismissButtonStyle: SafariViewControllerDismissButtonStyle.close,
|
||||
modalPresentationCapturesStatusBarAppearance: true,
|
||||
)
|
||||
);
|
||||
}
|
|
@ -585,5 +585,16 @@
|
|||
"invalidDomain": "Invalid domain",
|
||||
"loadingBlockedServicesList": "Loading blocked services list...",
|
||||
"blockedServicesListNotLoaded": "The blocked services list could not be loaded",
|
||||
"error": "Error"
|
||||
"error": "Error",
|
||||
"updates": "Updates",
|
||||
"updatesDescription": "Update the AdGuard Home server",
|
||||
"updateNow": "Update now",
|
||||
"currentVersion": "Current version",
|
||||
"requestStartUpdateFailed": "Request to start update failed",
|
||||
"requestStartUpdateSuccessful": "Request to start update successfull",
|
||||
"serverUpdated": "Server is updated",
|
||||
"unknownStatus": "Unknown status",
|
||||
"checkingUpdates": "Checking updates...",
|
||||
"checkUpdates": "Check updates",
|
||||
"requestingUpdate": "Requesting update..."
|
||||
}
|
|
@ -585,5 +585,16 @@
|
|||
"invalidDomain": "Dominio no válido",
|
||||
"loadingBlockedServicesList": "Cargando lista de servicios bloqueados...",
|
||||
"blockedServicesListNotLoaded": "No se ha podido cargar la lista de servicios bloqueados",
|
||||
"error": "Error"
|
||||
"error": "Error",
|
||||
"updates": "Actualizaciones",
|
||||
"updatesDescription": "Actualiza el servidor AdGuard Home",
|
||||
"updateNow": "Actualizar ahora",
|
||||
"currentVersion": "Versión actual",
|
||||
"requestStartUpdateFailed": "Petición para iniciar la actualización fallida",
|
||||
"requestStartUpdateSuccessful": "Petición para iniciar la actualización satisfactoria",
|
||||
"serverUpdated": "Servidor actualizado",
|
||||
"unknownStatus": "Estado desconocido",
|
||||
"checkingUpdates": "Comprobando actualizaciones...",
|
||||
"checkUpdates": "Comprobar actualizaciones",
|
||||
"requestingUpdate": "Solicitando actualización..."
|
||||
}
|
55
lib/models/update_available.dart
Normal file
55
lib/models/update_available.dart
Normal file
|
@ -0,0 +1,55 @@
|
|||
import 'package:adguard_home_manager/constants/enums.dart';
|
||||
|
||||
class UpdateAvailable {
|
||||
LoadStatus loadStatus = LoadStatus.loading;
|
||||
UpdateAvailableData? data;
|
||||
|
||||
UpdateAvailable({
|
||||
required this.loadStatus,
|
||||
required this.data
|
||||
});
|
||||
}
|
||||
|
||||
class UpdateAvailableData {
|
||||
String currentVersion;
|
||||
final String newVersion;
|
||||
final String announcement;
|
||||
final String announcementUrl;
|
||||
final bool canAutoupdate;
|
||||
final bool disabled;
|
||||
String? changelog;
|
||||
bool? updateAvailable;
|
||||
|
||||
UpdateAvailableData({
|
||||
required this.currentVersion,
|
||||
required this.newVersion,
|
||||
required this.announcement,
|
||||
required this.announcementUrl,
|
||||
required this.canAutoupdate,
|
||||
required this.disabled,
|
||||
this.changelog,
|
||||
this.updateAvailable
|
||||
});
|
||||
|
||||
factory UpdateAvailableData.fromJson(Map<String, dynamic> json) => UpdateAvailableData(
|
||||
currentVersion: json["current_version"],
|
||||
newVersion: json["new_version"],
|
||||
announcement: json["announcement"],
|
||||
announcementUrl: json["announcement_url"],
|
||||
canAutoupdate: json["can_autoupdate"],
|
||||
disabled: json["disabled"],
|
||||
changelog: json["changelog"],
|
||||
updateAvailable: json['update_available']
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
"current_version": currentVersion,
|
||||
"new_version": newVersion,
|
||||
"announcement": announcement,
|
||||
"announcement_url": announcementUrl,
|
||||
"can_autoupdate": canAutoupdate,
|
||||
"disabled": disabled,
|
||||
"changelog": changelog,
|
||||
"update_available": updateAvailable
|
||||
};
|
||||
}
|
|
@ -1,5 +1,3 @@
|
|||
import 'package:adguard_home_manager/constants/enums.dart';
|
||||
import 'package:adguard_home_manager/models/blocked_services.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:sqflite/sqflite.dart';
|
||||
|
||||
|
@ -9,11 +7,15 @@ import 'package:adguard_home_manager/models/dns_info.dart';
|
|||
import 'package:adguard_home_manager/models/rewrite_rules.dart';
|
||||
import 'package:adguard_home_manager/models/filtering_status.dart';
|
||||
import 'package:adguard_home_manager/models/clients_allowed_blocked.dart';
|
||||
import 'package:adguard_home_manager/models/update_available.dart';
|
||||
import 'package:adguard_home_manager/models/blocked_services.dart';
|
||||
import 'package:adguard_home_manager/models/clients.dart';
|
||||
import 'package:adguard_home_manager/models/server_status.dart';
|
||||
import 'package:adguard_home_manager/models/server.dart';
|
||||
import 'package:adguard_home_manager/services/http_requests.dart';
|
||||
import 'package:adguard_home_manager/functions/conversions.dart';
|
||||
import 'package:adguard_home_manager/functions/compare_versions.dart';
|
||||
import 'package:adguard_home_manager/constants/enums.dart';
|
||||
|
||||
class ServersProvider with ChangeNotifier {
|
||||
Database? _dbInstance;
|
||||
|
@ -56,6 +58,11 @@ class ServersProvider with ChangeNotifier {
|
|||
services: null
|
||||
);
|
||||
|
||||
final UpdateAvailable _updateAvailable = UpdateAvailable(
|
||||
loadStatus: LoadStatus.loading,
|
||||
data: null,
|
||||
);
|
||||
|
||||
FilteringStatus? _filteringStatus;
|
||||
|
||||
List<Server> get serversList {
|
||||
|
@ -102,6 +109,10 @@ class ServersProvider with ChangeNotifier {
|
|||
return _blockedServicesList;
|
||||
}
|
||||
|
||||
UpdateAvailable get updateAvailable {
|
||||
return _updateAvailable;
|
||||
}
|
||||
|
||||
void setDbInstance(Database db) {
|
||||
_dbInstance = db;
|
||||
}
|
||||
|
@ -224,6 +235,18 @@ class ServersProvider with ChangeNotifier {
|
|||
}
|
||||
}
|
||||
|
||||
void setUpdateAvailableLoadStatus(LoadStatus status, bool notify) {
|
||||
_updateAvailable.loadStatus = status;
|
||||
if (notify == true) {
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
void setUpdateAvailableData(UpdateAvailableData data) {
|
||||
_updateAvailable.data = data;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Future<dynamic> createServer(Server server) async {
|
||||
final saved = await saveServerIntoDb(server);
|
||||
if (saved == null) {
|
||||
|
@ -460,6 +483,40 @@ class ServersProvider with ChangeNotifier {
|
|||
}
|
||||
}
|
||||
|
||||
void checkServerUpdatesAvailable(Server server) async {
|
||||
setUpdateAvailableLoadStatus(LoadStatus.loading, true);
|
||||
final result = await Future.wait([
|
||||
checkServerUpdates(server: server),
|
||||
getUpdateChangelog(server: server)
|
||||
]);
|
||||
if (result[0]['result'] == 'success') {
|
||||
UpdateAvailableData data = result[0]['data'];
|
||||
data.changelog = result[1]['body'];
|
||||
data.updateAvailable = data.newVersion.contains('b')
|
||||
? compareBetaVersions(
|
||||
currentVersion: data.currentVersion.replaceAll('v', ''),
|
||||
newVersion: data.newVersion.replaceAll('v', ''),
|
||||
)
|
||||
: compareVersions(
|
||||
currentVersion: data.currentVersion.replaceAll('v', ''),
|
||||
newVersion: data.newVersion.replaceAll('v', ''),
|
||||
);
|
||||
setUpdateAvailableData(data);
|
||||
setUpdateAvailableLoadStatus(LoadStatus.loaded, true);
|
||||
}
|
||||
else {
|
||||
setUpdateAvailableLoadStatus(LoadStatus.error, true);
|
||||
}
|
||||
}
|
||||
|
||||
void clearUpdateAvailable(Server server, String newCurrentVersion) {
|
||||
if (_updateAvailable.data != null) {
|
||||
_updateAvailable.data!.updateAvailable = null;
|
||||
_updateAvailable.data!.currentVersion = newCurrentVersion;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
void saveFromDb(List<Map<String, dynamic>>? data) async {
|
||||
if (data != null) {
|
||||
for (var server in data) {
|
||||
|
@ -484,6 +541,7 @@ class ServersProvider with ChangeNotifier {
|
|||
if (serverStatus['result'] == 'success') {
|
||||
_serverStatus.data = serverStatus['data'];
|
||||
_serverStatus.loadStatus = 1;
|
||||
checkServerUpdatesAvailable(serverObj); // Do not await
|
||||
}
|
||||
else {
|
||||
_serverStatus.loadStatus = 2;
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:flutter_web_browser/flutter_web_browser.dart';
|
||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
|
||||
import 'package:adguard_home_manager/screens/settings/server_info/server_info.dart';
|
||||
|
@ -10,9 +9,9 @@ import 'package:adguard_home_manager/screens/settings/access_settings/access_set
|
|||
import 'package:adguard_home_manager/screens/settings/customization/customization.dart';
|
||||
import 'package:adguard_home_manager/screens/settings/dhcp/dhcp.dart';
|
||||
import 'package:adguard_home_manager/widgets/section_label.dart';
|
||||
import 'package:adguard_home_manager/screens/settings/update.dart';
|
||||
import 'package:adguard_home_manager/screens/settings/dns/dns.dart';
|
||||
import 'package:adguard_home_manager/screens/settings/dns_rewrites/dns_rewrites.dart';
|
||||
import 'package:adguard_home_manager/screens/settings/appbar.dart';
|
||||
import 'package:adguard_home_manager/screens/servers/servers.dart';
|
||||
import 'package:adguard_home_manager/screens/settings/advanced_setings.dart';
|
||||
import 'package:adguard_home_manager/screens/settings/general_settings.dart';
|
||||
|
@ -20,6 +19,7 @@ import 'package:adguard_home_manager/screens/settings/general_settings.dart';
|
|||
import 'package:adguard_home_manager/widgets/custom_list_tile.dart';
|
||||
|
||||
import 'package:adguard_home_manager/constants/strings.dart';
|
||||
import 'package:adguard_home_manager/functions/open_url.dart';
|
||||
import 'package:adguard_home_manager/constants/urls.dart';
|
||||
import 'package:adguard_home_manager/providers/servers_provider.dart';
|
||||
import 'package:adguard_home_manager/providers/app_config_provider.dart';
|
||||
|
@ -40,22 +40,6 @@ class Settings extends StatelessWidget {
|
|||
}));
|
||||
}
|
||||
|
||||
void openWeb(String url) {
|
||||
FlutterWebBrowser.openWebPage(
|
||||
url: url,
|
||||
customTabsOptions: const CustomTabsOptions(
|
||||
instantAppsEnabled: true,
|
||||
showTitle: true,
|
||||
urlBarHidingEnabled: false,
|
||||
),
|
||||
safariVCOptions: const SafariViewControllerOptions(
|
||||
barCollapsingEnabled: true,
|
||||
dismissButtonStyle: SafariViewControllerDismissButtonStyle.close,
|
||||
modalPresentationCapturesStatusBarAppearance: true,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(AppLocalizations.of(context)!.settings),
|
||||
|
@ -125,6 +109,31 @@ class Settings extends StatelessWidget {
|
|||
)
|
||||
},
|
||||
),
|
||||
CustomListTile(
|
||||
icon: Icons.system_update_rounded,
|
||||
title: AppLocalizations.of(context)!.updates,
|
||||
subtitle: AppLocalizations.of(context)!.updatesDescription,
|
||||
trailing: serversProvider.updateAvailable.data != null &&
|
||||
serversProvider.updateAvailable.data!.updateAvailable != null &&
|
||||
serversProvider.updateAvailable.data!.updateAvailable == true
|
||||
? Container(
|
||||
width: 10,
|
||||
height: 10,
|
||||
margin: const EdgeInsets.only(right: 12),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
color: Colors.red
|
||||
),
|
||||
)
|
||||
: null,
|
||||
onTap: () => {
|
||||
Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder: (context) => const UpdateScreen()
|
||||
)
|
||||
)
|
||||
},
|
||||
),
|
||||
CustomListTile(
|
||||
icon: Icons.info_rounded,
|
||||
title: AppLocalizations.of(context)!.serverInformation,
|
||||
|
@ -196,7 +205,7 @@ class Settings extends StatelessWidget {
|
|||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
IconButton(
|
||||
onPressed: () => openWeb(Urls.playStore),
|
||||
onPressed: () => openUrl(Urls.playStore),
|
||||
icon: SvgPicture.asset(
|
||||
'assets/resources/google-play.svg',
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
|
@ -206,7 +215,7 @@ class Settings extends StatelessWidget {
|
|||
tooltip: AppLocalizations.of(context)!.visitGooglePlay,
|
||||
),
|
||||
IconButton(
|
||||
onPressed: () => openWeb(Urls.gitHub),
|
||||
onPressed: () => openUrl(Urls.gitHub),
|
||||
icon: SvgPicture.asset(
|
||||
'assets/resources/github.svg',
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
|
|
350
lib/screens/settings/update.dart
Normal file
350
lib/screens/settings/update.dart
Normal file
|
@ -0,0 +1,350 @@
|
|||
// ignore_for_file: use_build_context_synchronously
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_html/flutter_html.dart';
|
||||
import 'package:html/parser.dart' as html;
|
||||
import 'package:markdown/markdown.dart' as md;
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
|
||||
import 'package:adguard_home_manager/functions/compare_versions.dart';
|
||||
|
||||
import 'package:adguard_home_manager/providers/app_config_provider.dart';
|
||||
import 'package:adguard_home_manager/services/http_requests.dart';
|
||||
import 'package:adguard_home_manager/functions/snackbar.dart';
|
||||
import 'package:adguard_home_manager/classes/process_modal.dart';
|
||||
import 'package:adguard_home_manager/functions/open_url.dart';
|
||||
import 'package:adguard_home_manager/constants/enums.dart';
|
||||
import 'package:adguard_home_manager/providers/servers_provider.dart';
|
||||
|
||||
class UpdateScreen extends StatelessWidget {
|
||||
const UpdateScreen({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final serversProvider = Provider.of<ServersProvider>(context);
|
||||
final appConfigProvider = Provider.of<AppConfigProvider>(context);
|
||||
|
||||
void update() async {
|
||||
ProcessModal processModal = ProcessModal(context: context);
|
||||
processModal.open(AppLocalizations.of(context)!.requestingUpdate);
|
||||
|
||||
final result = await requestUpdateServer(server: serversProvider.selectedServer!);
|
||||
|
||||
processModal.close();
|
||||
|
||||
if (result['result'] == 'success') {
|
||||
serversProvider.clearUpdateAvailable(serversProvider.selectedServer!, serversProvider.updateAvailable.data!.newVersion);
|
||||
showSnacbkar(
|
||||
context: context,
|
||||
appConfigProvider: appConfigProvider,
|
||||
label: AppLocalizations.of(context)!.requestStartUpdateSuccessful,
|
||||
color: Colors.green,
|
||||
labelColor: Colors.white,
|
||||
);
|
||||
}
|
||||
else {
|
||||
showSnacbkar(
|
||||
context: context,
|
||||
appConfigProvider: appConfigProvider,
|
||||
label: AppLocalizations.of(context)!.requestStartUpdateFailed,
|
||||
color: Colors.red,
|
||||
labelColor: Colors.white,
|
||||
);
|
||||
appConfigProvider.addLog(result['log']);
|
||||
}
|
||||
}
|
||||
|
||||
Widget headerPortrait() {
|
||||
return Column(
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
IconButton(
|
||||
icon: Icon(
|
||||
Icons.arrow_back,
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
onPressed: () => Navigator.pop(context),
|
||||
),
|
||||
IconButton(
|
||||
icon: Icon(
|
||||
Icons.refresh_rounded,
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
tooltip: AppLocalizations.of(context)!.checkUpdates,
|
||||
onPressed: () => serversProvider.checkServerUpdatesAvailable(serversProvider.selectedServer!)
|
||||
),
|
||||
],
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
top: 8, bottom: 16, left: 16, right: 16
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
serversProvider.updateAvailable.loadStatus == LoadStatus.loading
|
||||
? Column(
|
||||
children: const [
|
||||
CircularProgressIndicator(),
|
||||
SizedBox(height: 4)
|
||||
],
|
||||
)
|
||||
: Icon(
|
||||
serversProvider.updateAvailable.data!.updateAvailable != null
|
||||
? serversProvider.updateAvailable.data!.updateAvailable == true
|
||||
? Icons.system_update_rounded
|
||||
: Icons.system_security_update_good_rounded
|
||||
: Icons.system_security_update_warning_rounded,
|
||||
size: 40,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
serversProvider.updateAvailable.loadStatus == LoadStatus.loading
|
||||
? AppLocalizations.of(context)!.checkingUpdates
|
||||
: serversProvider.updateAvailable.data!.updateAvailable != null
|
||||
? serversProvider.updateAvailable.data!.updateAvailable == true
|
||||
? AppLocalizations.of(context)!.updateAvailable
|
||||
: AppLocalizations.of(context)!.serverUpdated
|
||||
: AppLocalizations.of(context)!.unknownStatus,
|
||||
style: const TextStyle(
|
||||
fontSize: 24,
|
||||
fontWeight: FontWeight.w400
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 40),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
if (serversProvider.updateAvailable.loadStatus == LoadStatus.loaded) Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
serversProvider.updateAvailable.data!.updateAvailable != null && serversProvider.updateAvailable.data!.updateAvailable == true
|
||||
? AppLocalizations.of(context)!.newVersion
|
||||
: AppLocalizations.of(context)!.currentVersion,
|
||||
style: const TextStyle(
|
||||
fontSize: 16,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
serversProvider.updateAvailable.data!.updateAvailable != null
|
||||
? serversProvider.updateAvailable.data!.updateAvailable == true
|
||||
? serversProvider.updateAvailable.data!.newVersion
|
||||
: serversProvider.updateAvailable.data!.currentVersion
|
||||
: "N/A",
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
if (serversProvider.updateAvailable.loadStatus != LoadStatus.loaded) const SizedBox(),
|
||||
FilledButton.icon(
|
||||
icon: const Icon(Icons.download_rounded),
|
||||
label: Text(AppLocalizations.of(context)!.updateNow),
|
||||
onPressed: serversProvider.updateAvailable.data!.updateAvailable != null && serversProvider.updateAvailable.data!.updateAvailable == true
|
||||
? () => update()
|
||||
: null
|
||||
)
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget headerLandscape() {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
IconButton(
|
||||
icon: Icon(
|
||||
Icons.arrow_back,
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
onPressed: () => Navigator.pop(context),
|
||||
),
|
||||
IconButton(
|
||||
icon: Icon(
|
||||
Icons.refresh_rounded,
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
tooltip: AppLocalizations.of(context)!.checkUpdates,
|
||||
onPressed: () => serversProvider.checkServerUpdatesAvailable(serversProvider.selectedServer!)
|
||||
),
|
||||
],
|
||||
),
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
top: 8, bottom: 16, left: 16, right: 16
|
||||
),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
serversProvider.updateAvailable.loadStatus == LoadStatus.loading
|
||||
? Column(
|
||||
children: const [
|
||||
CircularProgressIndicator(),
|
||||
SizedBox(height: 4)
|
||||
],
|
||||
)
|
||||
: Icon(
|
||||
serversProvider.updateAvailable.data!.updateAvailable != null
|
||||
? serversProvider.updateAvailable.data!.updateAvailable == true
|
||||
? Icons.system_update_rounded
|
||||
: Icons.system_security_update_good_rounded
|
||||
: Icons.system_security_update_warning_rounded,
|
||||
size: 40,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
serversProvider.updateAvailable.loadStatus == LoadStatus.loading
|
||||
? AppLocalizations.of(context)!.checkingUpdates
|
||||
: serversProvider.updateAvailable.data!.updateAvailable != null
|
||||
? serversProvider.updateAvailable.data!.updateAvailable == true
|
||||
? AppLocalizations.of(context)!.updateAvailable
|
||||
: AppLocalizations.of(context)!.serverUpdated
|
||||
: AppLocalizations.of(context)!.unknownStatus,
|
||||
style: const TextStyle(
|
||||
fontSize: 24,
|
||||
fontWeight: FontWeight.w400
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 40),
|
||||
Expanded(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
if (serversProvider.updateAvailable.loadStatus == LoadStatus.loaded) Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
serversProvider.updateAvailable.data!.updateAvailable != null && serversProvider.updateAvailable.data!.updateAvailable == true
|
||||
? AppLocalizations.of(context)!.newVersion
|
||||
: AppLocalizations.of(context)!.currentVersion,
|
||||
style: const TextStyle(
|
||||
fontSize: 16,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
serversProvider.updateAvailable.data!.updateAvailable != null
|
||||
? serversProvider.updateAvailable.data!.updateAvailable == true
|
||||
? serversProvider.updateAvailable.data!.newVersion
|
||||
: serversProvider.updateAvailable.data!.currentVersion
|
||||
: "N/A",
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
if (serversProvider.updateAvailable.loadStatus != LoadStatus.loaded) const SizedBox(),
|
||||
FilledButton.icon(
|
||||
icon: const Icon(Icons.download_rounded),
|
||||
label: Text(AppLocalizations.of(context)!.updateNow),
|
||||
onPressed: serversProvider.updateAvailable.data!.updateAvailable != null && serversProvider.updateAvailable.data!.updateAvailable == true
|
||||
? () => update()
|
||||
: null
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
final changelog = serversProvider.updateAvailable.loadStatus == LoadStatus.loaded && serversProvider.updateAvailable.data!.changelog != null
|
||||
? ListView(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
child: Text(
|
||||
"Changelog ${serversProvider.updateAvailable.data!.newVersion}",
|
||||
style: TextStyle(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
child: Html(
|
||||
data: html.parse(md.markdownToHtml(serversProvider.updateAvailable.data!.changelog!)).outerHtml,
|
||||
onLinkTap: (url, context, attributes, element) => url != null ? openUrl(url) : null,
|
||||
)
|
||||
)
|
||||
],
|
||||
)
|
||||
: null;
|
||||
|
||||
return Scaffold(
|
||||
body: MediaQuery.of(context).size.width > 700
|
||||
? Row(
|
||||
children: [
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: Container(
|
||||
color: Theme.of(context).colorScheme.surfaceVariant,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: [
|
||||
Container(
|
||||
height: MediaQuery.of(context).size.height,
|
||||
padding: EdgeInsets.only(
|
||||
top: MediaQuery.of(context).viewPadding.top
|
||||
),
|
||||
child: headerLandscape(),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
flex: 3,
|
||||
child: SafeArea(
|
||||
child: SizedBox(
|
||||
width: MediaQuery.of(context).size.width*0.6,
|
||||
child: changelog ?? const SizedBox(),
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
)
|
||||
: Column(
|
||||
children: [
|
||||
Container(
|
||||
color: Theme.of(context).colorScheme.surfaceVariant,
|
||||
child: SafeArea(
|
||||
child: headerPortrait()
|
||||
)
|
||||
),
|
||||
changelog != null
|
||||
? Expanded(child: changelog)
|
||||
: const SizedBox(),
|
||||
]
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
|
@ -20,6 +20,7 @@ import 'package:adguard_home_manager/models/clients.dart';
|
|||
import 'package:adguard_home_manager/models/clients_allowed_blocked.dart';
|
||||
import 'package:adguard_home_manager/models/server.dart';
|
||||
import 'package:adguard_home_manager/constants/urls.dart';
|
||||
import 'package:adguard_home_manager/models/update_available.dart';
|
||||
|
||||
|
||||
Future<Map<String, dynamic>> apiRequest({
|
||||
|
@ -1960,3 +1961,170 @@ Future checkAppUpdatesGitHub() async {
|
|||
};
|
||||
}
|
||||
}
|
||||
|
||||
Future checkServerUpdates({
|
||||
required Server server,
|
||||
}) async {
|
||||
final result = await Future.wait([
|
||||
apiRequest(
|
||||
urlPath: '/version.json',
|
||||
method: 'get',
|
||||
server: server,
|
||||
type: 'check_server_updates',
|
||||
body: json.encode({
|
||||
"recheck_now": true
|
||||
})
|
||||
),
|
||||
apiRequest(
|
||||
urlPath: '/status',
|
||||
method: 'get',
|
||||
server: server,
|
||||
type: 'check_server_updates',
|
||||
body: json.encode({
|
||||
"recheck_now": true
|
||||
})
|
||||
),
|
||||
]);
|
||||
|
||||
if (result[0]['hasResponse'] == true && result[0]['hasResponse'] == true) {
|
||||
if (result[0]['statusCode'] == 200 && result[0]['statusCode'] == 200) {
|
||||
final Map<String, dynamic> obj = {
|
||||
...jsonDecode(result[0]['body']),
|
||||
'current_version': jsonDecode(result[1]['body'])['version']
|
||||
};
|
||||
return {
|
||||
'result': 'success',
|
||||
'data': UpdateAvailableData.fromJson(obj)
|
||||
};
|
||||
}
|
||||
else {
|
||||
return {
|
||||
'result': 'error',
|
||||
'log': AppLog(
|
||||
type: 'get_filtering_status',
|
||||
dateTime: DateTime.now(),
|
||||
message: 'error_code_not_expected',
|
||||
statusCode: result.map((res) => res['statusCode'] ?? 'null').toString(),
|
||||
resBody: result.map((res) => res['body'] ?? 'null').toString(),
|
||||
)
|
||||
};
|
||||
}
|
||||
}
|
||||
else {
|
||||
return {
|
||||
'result': 'error',
|
||||
'log': AppLog(
|
||||
type: 'get_filtering_status',
|
||||
dateTime: DateTime.now(),
|
||||
message: 'no_response',
|
||||
statusCode: result.map((res) => res['statusCode'] ?? 'null').toString(),
|
||||
resBody: result.map((res) => res['body'] ?? 'null').toString(),
|
||||
)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Future getUpdateChangelog({
|
||||
required Server server,
|
||||
}) async {
|
||||
try {
|
||||
HttpClient httpClient = HttpClient();
|
||||
HttpClientRequest request = await httpClient.getUrl(Uri.parse(Urls.adGuardHomeLatestRelease));
|
||||
HttpClientResponse response = await request.close();
|
||||
String reply = await response.transform(utf8.decoder).join();
|
||||
httpClient.close();
|
||||
if (response.statusCode == 200) {
|
||||
return {
|
||||
'result': 'success',
|
||||
'hasResponse': true,
|
||||
'error': false,
|
||||
'statusCode': response.statusCode,
|
||||
'body': jsonDecode(reply)['body']
|
||||
};
|
||||
}
|
||||
else {
|
||||
return {
|
||||
'result': 'error',
|
||||
'log': AppLog(
|
||||
type: 'update_encryption_settings',
|
||||
dateTime: DateTime.now(),
|
||||
message: 'error_code_not_expected',
|
||||
statusCode: response.statusCode.toString(),
|
||||
resBody: reply,
|
||||
)
|
||||
};
|
||||
}
|
||||
} on SocketException {
|
||||
return {
|
||||
'result': 'no_connection',
|
||||
'message': 'SocketException',
|
||||
'log': AppLog(
|
||||
type: 'check_latest_release_github',
|
||||
dateTime: DateTime.now(),
|
||||
message: 'SocketException'
|
||||
)
|
||||
};
|
||||
} on TimeoutException {
|
||||
return {
|
||||
'result': 'no_connection',
|
||||
'message': 'TimeoutException',
|
||||
'log': AppLog(
|
||||
type: 'check_latest_release_github',
|
||||
dateTime: DateTime.now(),
|
||||
message: 'TimeoutException'
|
||||
)
|
||||
};
|
||||
} on HandshakeException {
|
||||
return {
|
||||
'result': 'ssl_error',
|
||||
'message': 'HandshakeException',
|
||||
'log': AppLog(
|
||||
type: 'check_latest_release_github',
|
||||
dateTime: DateTime.now(),
|
||||
message: 'HandshakeException'
|
||||
)
|
||||
};
|
||||
} catch (e) {
|
||||
return {
|
||||
'result': 'error',
|
||||
'message': e.toString(),
|
||||
'log': AppLog(
|
||||
type: 'check_latest_release_github',
|
||||
dateTime: DateTime.now(),
|
||||
message: e.toString()
|
||||
)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Future requestUpdateServer({
|
||||
required Server server,
|
||||
}) async {
|
||||
final result = await apiRequest(
|
||||
urlPath: '/update',
|
||||
method: 'post',
|
||||
server: server,
|
||||
type: 'update_server'
|
||||
);
|
||||
|
||||
if (result['hasResponse'] == true) {
|
||||
if (result['statusCode'] == 200) {
|
||||
return { 'result': 'success' };
|
||||
}
|
||||
else {
|
||||
return {
|
||||
'result': 'error',
|
||||
'log': AppLog(
|
||||
type: 'update_server',
|
||||
dateTime: DateTime.now(),
|
||||
message: 'error_code_not_expected',
|
||||
statusCode: result['statusCode'].toString(),
|
||||
resBody: result['body'],
|
||||
)
|
||||
};
|
||||
}
|
||||
}
|
||||
else {
|
||||
return result;
|
||||
}
|
||||
}
|
|
@ -50,12 +50,34 @@ class BottomNavBar extends StatelessWidget {
|
|||
return NavigationBar(
|
||||
selectedIndex: appConfigProvider.selectedScreen,
|
||||
destinations: screens.map((screen) => NavigationDestination(
|
||||
icon: Icon(
|
||||
icon: Stack(
|
||||
children: [
|
||||
Icon(
|
||||
screen.icon,
|
||||
color: screens[appConfigProvider.selectedScreen] == screen
|
||||
? Theme.of(context).colorScheme.onSecondaryContainer
|
||||
: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
if (
|
||||
screen.name == 'settings' &&
|
||||
serversProvider.updateAvailable.data != null &&
|
||||
serversProvider.updateAvailable.data!.updateAvailable != null &&
|
||||
serversProvider.updateAvailable.data!.updateAvailable == true
|
||||
) Positioned(
|
||||
bottom: 0,
|
||||
right: -12,
|
||||
child: Container(
|
||||
width: 10,
|
||||
height: 10,
|
||||
margin: const EdgeInsets.only(right: 12),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
color: Colors.red
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
label: translatedName(screen.name)
|
||||
)).toList(),
|
||||
onDestinationSelected: (value) {
|
||||
|
|
40
pubspec.lock
40
pubspec.lock
|
@ -246,6 +246,14 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.4.1"
|
||||
flutter_html:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: flutter_html
|
||||
sha256: "342c7908f0a67bcec62b6e0f7cf23e23bafe7f64693665dd35be98d5e783bdfd"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.0-alpha.6"
|
||||
flutter_launcher_icons:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
|
@ -267,6 +275,14 @@ packages:
|
|||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
flutter_markdown:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: flutter_markdown
|
||||
sha256: "7b25c10de1fea883f3c4f9b8389506b54053cd00807beab69fd65c8653a2711f"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.6.14"
|
||||
flutter_native_splash:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
|
@ -302,13 +318,13 @@ packages:
|
|||
source: sdk
|
||||
version: "0.0.0"
|
||||
html:
|
||||
dependency: transitive
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: html
|
||||
sha256: d9793e10dbe0e6c364f4c59bf3e01fb33a9b2a674bc7a1081693dba0614b6269
|
||||
sha256: "79d498e6d6761925a34ee5ea8fa6dfef38607781d2fa91e37523474282af55cb"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.15.1"
|
||||
version: "0.15.2"
|
||||
http:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -365,6 +381,14 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.1"
|
||||
markdown:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: markdown
|
||||
sha256: d95a9d12954aafc97f984ca29baaa7690ed4d9ec4140a23ad40580bcdb6c87f5
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "7.0.2"
|
||||
matcher:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -397,6 +421,14 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.0"
|
||||
numerus:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: numerus
|
||||
sha256: "436759d84f233b40107d0cc31cfa92d24e0960afeb2e506be70926d4cddffd9e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.0"
|
||||
package_info_plus:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -651,5 +683,5 @@ packages:
|
|||
source: hosted
|
||||
version: "3.1.1"
|
||||
sdks:
|
||||
dart: ">=2.18.1 <3.0.0"
|
||||
dart: ">=2.19.0 <3.0.0"
|
||||
flutter: ">=3.3.0"
|
||||
|
|
|
@ -50,6 +50,10 @@ dependencies:
|
|||
bottom_sheet: ^3.1.2
|
||||
percent_indicator: ^4.2.2
|
||||
store_checker: ^1.1.0
|
||||
flutter_markdown: ^0.6.14
|
||||
markdown: ^7.0.2
|
||||
html: ^0.15.2
|
||||
flutter_html: ^3.0.0-alpha.6
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
|
Loading…
Add table
Reference in a new issue