mirror of
https://github.com/JGeek00/adguard-home-manager.git
synced 2025-04-25 08:16:06 +00:00
Adapted clients screen
This commit is contained in:
parent
5660aefdc5
commit
3c92457040
14 changed files with 1249 additions and 373 deletions
100
lib/screens/clients/active_client_tile.dart
Normal file
100
lib/screens/clients/active_client_tile.dart
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
import 'package:adguard_home_manager/widgets/custom_list_tile.dart';
|
||||||
|
|
||||||
|
import 'package:adguard_home_manager/models/clients.dart';
|
||||||
|
|
||||||
|
class ActiveClientTile extends StatelessWidget {
|
||||||
|
final AutoClient client;
|
||||||
|
final void Function(AutoClient) onTap;
|
||||||
|
final bool splitView;
|
||||||
|
final AutoClient? selectedClient;
|
||||||
|
|
||||||
|
const ActiveClientTile({
|
||||||
|
Key? key,
|
||||||
|
required this.client,
|
||||||
|
required this.onTap,
|
||||||
|
required this.splitView,
|
||||||
|
this.selectedClient
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
if (splitView == true) {
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 12),
|
||||||
|
child: Material(
|
||||||
|
color: Colors.transparent,
|
||||||
|
borderRadius: BorderRadius.circular(28),
|
||||||
|
child: InkWell(
|
||||||
|
borderRadius: BorderRadius.circular(28),
|
||||||
|
onTap: () => onTap(client),
|
||||||
|
child: Container(
|
||||||
|
width: double.maxFinite,
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.circular(28),
|
||||||
|
color: client == selectedClient
|
||||||
|
? Theme.of(context).colorScheme.primaryContainer
|
||||||
|
: null
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Flexible(
|
||||||
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Flexible(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
client.name != ''
|
||||||
|
? client.name!
|
||||||
|
: client.ip,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.w400,
|
||||||
|
color: Theme.of(context).colorScheme.onSurface,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (client.name != '') Text(client.ip)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
client.source,
|
||||||
|
style: TextStyle(
|
||||||
|
color: Theme.of(context).colorScheme.onSurface
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return CustomListTile(
|
||||||
|
title: client.name != ''
|
||||||
|
? client.name!
|
||||||
|
: client.ip,
|
||||||
|
subtitle: client.name != ''
|
||||||
|
? client.ip
|
||||||
|
: null,
|
||||||
|
trailing: Text(
|
||||||
|
client.source,
|
||||||
|
style: TextStyle(
|
||||||
|
color: Theme.of(context).colorScheme.onSurface
|
||||||
|
),
|
||||||
|
),
|
||||||
|
onTap: () => onTap(client),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
250
lib/screens/clients/added_client_tile.dart
Normal file
250
lib/screens/clients/added_client_tile.dart
Normal file
|
@ -0,0 +1,250 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
|
import 'package:adguard_home_manager/widgets/custom_list_tile.dart';
|
||||||
|
|
||||||
|
import 'package:adguard_home_manager/functions/compare_versions.dart';
|
||||||
|
import 'package:adguard_home_manager/models/clients.dart';
|
||||||
|
import 'package:adguard_home_manager/providers/servers_provider.dart';
|
||||||
|
import 'package:adguard_home_manager/providers/app_config_provider.dart';
|
||||||
|
|
||||||
|
class AddedClientTile extends StatefulWidget {
|
||||||
|
final Client client;
|
||||||
|
final void Function(Client) onTap;
|
||||||
|
final void Function(Client) onLongPress;
|
||||||
|
final void Function(Client) onEdit;
|
||||||
|
final Client? selectedClient;
|
||||||
|
final bool? splitView;
|
||||||
|
|
||||||
|
const AddedClientTile({
|
||||||
|
Key? key,
|
||||||
|
required this.client,
|
||||||
|
required this.onTap,
|
||||||
|
required this.onLongPress,
|
||||||
|
required this.onEdit,
|
||||||
|
this.selectedClient,
|
||||||
|
required this.splitView
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<AddedClientTile> createState() => _AddedClientTileState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _AddedClientTileState extends State<AddedClientTile> {
|
||||||
|
bool hover = false;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final serversProvider = Provider.of<ServersProvider>(context);
|
||||||
|
final appConfigProvider = Provider.of<AppConfigProvider>(context);
|
||||||
|
|
||||||
|
if (widget.splitView == true) {
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 12),
|
||||||
|
child: Material(
|
||||||
|
color: Colors.transparent,
|
||||||
|
borderRadius: BorderRadius.circular(28),
|
||||||
|
child: InkWell(
|
||||||
|
borderRadius: BorderRadius.circular(28),
|
||||||
|
onTap: () => widget.onTap(widget.client),
|
||||||
|
onHover: (v) => setState(() => hover = v),
|
||||||
|
child: Container(
|
||||||
|
width: double.maxFinite,
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.circular(28),
|
||||||
|
color: widget.client == widget.selectedClient
|
||||||
|
? Theme.of(context).colorScheme.primaryContainer
|
||||||
|
: null
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Flexible(
|
||||||
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Flexible(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
widget.client.ids.toString().replaceAll(RegExp(r'^\[|\]$'), ''),
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.w400,
|
||||||
|
color: Theme.of(context).colorScheme.onSurface,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Icon(
|
||||||
|
Icons.filter_list_rounded,
|
||||||
|
size: 19,
|
||||||
|
color: widget.client.filteringEnabled == true
|
||||||
|
? appConfigProvider.useThemeColorForStatus == true
|
||||||
|
? Theme.of(context).colorScheme.primary
|
||||||
|
: Colors.green
|
||||||
|
: appConfigProvider.useThemeColorForStatus == true
|
||||||
|
? Colors.grey
|
||||||
|
: Colors.red,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 10),
|
||||||
|
Icon(
|
||||||
|
Icons.vpn_lock_rounded,
|
||||||
|
size: 18,
|
||||||
|
color: widget.client.safebrowsingEnabled == true
|
||||||
|
? appConfigProvider.useThemeColorForStatus == true
|
||||||
|
? Theme.of(context).colorScheme.primary
|
||||||
|
: Colors.green
|
||||||
|
: appConfigProvider.useThemeColorForStatus == true
|
||||||
|
? Colors.grey
|
||||||
|
: Colors.red,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 10),
|
||||||
|
Icon(
|
||||||
|
Icons.block,
|
||||||
|
size: 18,
|
||||||
|
color: widget.client.parentalEnabled == true
|
||||||
|
? appConfigProvider.useThemeColorForStatus == true
|
||||||
|
? Theme.of(context).colorScheme.primary
|
||||||
|
: Colors.green
|
||||||
|
: appConfigProvider.useThemeColorForStatus == true
|
||||||
|
? Colors.grey
|
||||||
|
: Colors.red,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 10),
|
||||||
|
Icon(
|
||||||
|
Icons.search_rounded,
|
||||||
|
size: 19,
|
||||||
|
color: serverVersionIsAhead(
|
||||||
|
currentVersion: serversProvider.serverStatus.data!.serverVersion,
|
||||||
|
referenceVersion: 'v0.107.28',
|
||||||
|
referenceVersionBeta: 'v0.108.0-b.33'
|
||||||
|
) == true
|
||||||
|
? widget.client.safeSearch != null && widget.client.safeSearch!.enabled == true
|
||||||
|
? appConfigProvider.useThemeColorForStatus == true
|
||||||
|
? Theme.of(context).colorScheme.primary
|
||||||
|
: Colors.green
|
||||||
|
: appConfigProvider.useThemeColorForStatus == true
|
||||||
|
? Colors.grey
|
||||||
|
: Colors.red
|
||||||
|
: widget.client.safesearchEnabled == true
|
||||||
|
? appConfigProvider.useThemeColorForStatus == true
|
||||||
|
? Theme.of(context).colorScheme.primary
|
||||||
|
: Colors.green
|
||||||
|
: appConfigProvider.useThemeColorForStatus == true
|
||||||
|
? Colors.grey
|
||||||
|
: Colors.red,
|
||||||
|
)
|
||||||
|
],
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (hover == true) IconButton(
|
||||||
|
onPressed: () => widget.onEdit(widget.client),
|
||||||
|
icon: const Icon(Icons.edit_rounded)
|
||||||
|
)
|
||||||
|
],
|
||||||
|
)
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return CustomListTile(
|
||||||
|
onLongPress: () => widget.onLongPress(widget.client),
|
||||||
|
onTap: () => widget.onTap(widget.client),
|
||||||
|
onHover: (v) => setState(() => hover = v),
|
||||||
|
title: widget.client.name,
|
||||||
|
subtitleWidget: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
widget.client.ids.toString().replaceAll(RegExp(r'^\[|\]$'), ''),
|
||||||
|
style: TextStyle(
|
||||||
|
color: Theme.of(context).listTileTheme.textColor
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Icon(
|
||||||
|
Icons.filter_list_rounded,
|
||||||
|
size: 19,
|
||||||
|
color: widget.client.filteringEnabled == true
|
||||||
|
? appConfigProvider.useThemeColorForStatus == true
|
||||||
|
? Theme.of(context).colorScheme.primary
|
||||||
|
: Colors.green
|
||||||
|
: appConfigProvider.useThemeColorForStatus == true
|
||||||
|
? Colors.grey
|
||||||
|
: Colors.red,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 10),
|
||||||
|
Icon(
|
||||||
|
Icons.vpn_lock_rounded,
|
||||||
|
size: 18,
|
||||||
|
color: widget.client.safebrowsingEnabled == true
|
||||||
|
? appConfigProvider.useThemeColorForStatus == true
|
||||||
|
? Theme.of(context).colorScheme.primary
|
||||||
|
: Colors.green
|
||||||
|
: appConfigProvider.useThemeColorForStatus == true
|
||||||
|
? Colors.grey
|
||||||
|
: Colors.red,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 10),
|
||||||
|
Icon(
|
||||||
|
Icons.block,
|
||||||
|
size: 18,
|
||||||
|
color: widget.client.parentalEnabled == true
|
||||||
|
? appConfigProvider.useThemeColorForStatus == true
|
||||||
|
? Theme.of(context).colorScheme.primary
|
||||||
|
: Colors.green
|
||||||
|
: appConfigProvider.useThemeColorForStatus == true
|
||||||
|
? Colors.grey
|
||||||
|
: Colors.red,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 10),
|
||||||
|
Icon(
|
||||||
|
Icons.search_rounded,
|
||||||
|
size: 19,
|
||||||
|
color: serverVersionIsAhead(
|
||||||
|
currentVersion: serversProvider.serverStatus.data!.serverVersion,
|
||||||
|
referenceVersion: 'v0.107.28',
|
||||||
|
referenceVersionBeta: 'v0.108.0-b.33'
|
||||||
|
) == true
|
||||||
|
? widget.client.safeSearch != null && widget.client.safeSearch!.enabled == true
|
||||||
|
? appConfigProvider.useThemeColorForStatus == true
|
||||||
|
? Theme.of(context).colorScheme.primary
|
||||||
|
: Colors.green
|
||||||
|
: appConfigProvider.useThemeColorForStatus == true
|
||||||
|
? Colors.grey
|
||||||
|
: Colors.red
|
||||||
|
: widget.client.safesearchEnabled == true
|
||||||
|
? appConfigProvider.useThemeColorForStatus == true
|
||||||
|
? Theme.of(context).colorScheme.primary
|
||||||
|
: Colors.green
|
||||||
|
: appConfigProvider.useThemeColorForStatus == true
|
||||||
|
? Colors.grey
|
||||||
|
: Colors.red,
|
||||||
|
)
|
||||||
|
],
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
trailing: hover == true
|
||||||
|
? IconButton(
|
||||||
|
onPressed: () => widget.onEdit(widget.client),
|
||||||
|
icon: const Icon(Icons.edit_rounded)
|
||||||
|
)
|
||||||
|
: null,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,12 +1,16 @@
|
||||||
// ignore_for_file: use_build_context_synchronously
|
// ignore_for_file: use_build_context_synchronously
|
||||||
|
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:animations/animations.dart';
|
import 'package:animations/animations.dart';
|
||||||
import 'package:flutter/rendering.dart';
|
import 'package:flutter/rendering.dart';
|
||||||
|
import 'package:flutter_split_view/flutter_split_view.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||||
|
|
||||||
import 'package:adguard_home_manager/screens/clients/client_screen.dart';
|
import 'package:adguard_home_manager/screens/clients/client_screen.dart';
|
||||||
|
import 'package:adguard_home_manager/screens/clients/added_client_tile.dart';
|
||||||
import 'package:adguard_home_manager/screens/clients/remove_client_modal.dart';
|
import 'package:adguard_home_manager/screens/clients/remove_client_modal.dart';
|
||||||
import 'package:adguard_home_manager/screens/clients/fab.dart';
|
import 'package:adguard_home_manager/screens/clients/fab.dart';
|
||||||
import 'package:adguard_home_manager/screens/clients/options_modal.dart';
|
import 'package:adguard_home_manager/screens/clients/options_modal.dart';
|
||||||
|
@ -27,13 +31,19 @@ class AddedList extends StatefulWidget {
|
||||||
final LoadStatus loadStatus;
|
final LoadStatus loadStatus;
|
||||||
final List<Client> data;
|
final List<Client> data;
|
||||||
final Future Function() fetchClients;
|
final Future Function() fetchClients;
|
||||||
|
final void Function(Client) onClientSelected;
|
||||||
|
final Client? selectedClient;
|
||||||
|
final bool splitView;
|
||||||
|
|
||||||
const AddedList({
|
const AddedList({
|
||||||
Key? key,
|
Key? key,
|
||||||
required this.scrollController,
|
required this.scrollController,
|
||||||
required this.loadStatus,
|
required this.loadStatus,
|
||||||
required this.data,
|
required this.data,
|
||||||
required this.fetchClients
|
required this.fetchClients,
|
||||||
|
required this.onClientSelected,
|
||||||
|
this.selectedClient,
|
||||||
|
required this.splitView
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -69,6 +79,8 @@ class _AddedListState extends State<AddedList> {
|
||||||
final serversProvider = Provider.of<ServersProvider>(context);
|
final serversProvider = Provider.of<ServersProvider>(context);
|
||||||
final appConfigProvider = Provider.of<AppConfigProvider>(context);
|
final appConfigProvider = Provider.of<AppConfigProvider>(context);
|
||||||
|
|
||||||
|
final width = MediaQuery.of(context).size.width;
|
||||||
|
|
||||||
void confirmEditClient(Client client) async {
|
void confirmEditClient(Client client) async {
|
||||||
ProcessModal processModal = ProcessModal(context: context);
|
ProcessModal processModal = ProcessModal(context: context);
|
||||||
processModal.open(AppLocalizations.of(context)!.addingClient);
|
processModal.open(AppLocalizations.of(context)!.addingClient);
|
||||||
|
@ -130,6 +142,10 @@ class _AddedListState extends State<AddedList> {
|
||||||
clientsData.clients = clientsData.clients.where((c) => c.name != client.name).toList();
|
clientsData.clients = clientsData.clients.where((c) => c.name != client.name).toList();
|
||||||
serversProvider.setClientsData(clientsData);
|
serversProvider.setClientsData(clientsData);
|
||||||
|
|
||||||
|
if (widget.splitView == true) {
|
||||||
|
SplitView.of(context).popUntil(0);
|
||||||
|
}
|
||||||
|
|
||||||
showSnacbkar(
|
showSnacbkar(
|
||||||
context: context,
|
context: context,
|
||||||
appConfigProvider: appConfigProvider,
|
appConfigProvider: appConfigProvider,
|
||||||
|
@ -150,6 +166,20 @@ class _AddedListState extends State<AddedList> {
|
||||||
}
|
}
|
||||||
|
|
||||||
void openClientModal(Client client) {
|
void openClientModal(Client client) {
|
||||||
|
if (width > 900 || !(Platform.isAndroid | Platform.isIOS)) {
|
||||||
|
showDialog(
|
||||||
|
barrierDismissible: false,
|
||||||
|
context: context,
|
||||||
|
builder: (BuildContext context) => ClientScreen(
|
||||||
|
onConfirm: confirmEditClient,
|
||||||
|
serverVersion: serversProvider.serverStatus.data!.serverVersion,
|
||||||
|
onDelete: deleteClient,
|
||||||
|
client: client,
|
||||||
|
dialog: true,
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
Navigator.push(context, MaterialPageRoute(
|
Navigator.push(context, MaterialPageRoute(
|
||||||
fullscreenDialog: true,
|
fullscreenDialog: true,
|
||||||
builder: (BuildContext context) => ClientScreen(
|
builder: (BuildContext context) => ClientScreen(
|
||||||
|
@ -157,9 +187,11 @@ class _AddedListState extends State<AddedList> {
|
||||||
serverVersion: serversProvider.serverStatus.data!.serverVersion,
|
serverVersion: serversProvider.serverStatus.data!.serverVersion,
|
||||||
onDelete: deleteClient,
|
onDelete: deleteClient,
|
||||||
client: client,
|
client: client,
|
||||||
|
dialog: false,
|
||||||
)
|
)
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void openDeleteModal(Client client) {
|
void openDeleteModal(Client client) {
|
||||||
showModal(
|
showModal(
|
||||||
|
@ -181,6 +213,10 @@ class _AddedListState extends State<AddedList> {
|
||||||
}
|
}
|
||||||
|
|
||||||
return CustomTabContentList(
|
return CustomTabContentList(
|
||||||
|
noSliver: !(Platform.isAndroid || Platform.isIOS),
|
||||||
|
listPadding: widget.splitView == true
|
||||||
|
? const EdgeInsets.only(top: 8)
|
||||||
|
: null,
|
||||||
loadingGenerator: () => SizedBox(
|
loadingGenerator: () => SizedBox(
|
||||||
width: double.maxFinite,
|
width: double.maxFinite,
|
||||||
height: MediaQuery.of(context).size.height-171,
|
height: MediaQuery.of(context).size.height-171,
|
||||||
|
@ -202,97 +238,13 @@ class _AddedListState extends State<AddedList> {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
itemsCount: widget.data.length,
|
itemsCount: widget.data.length,
|
||||||
contentWidget: (index) => ListTile(
|
contentWidget: (index) => AddedClientTile(
|
||||||
contentPadding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12),
|
selectedClient: widget.selectedClient,
|
||||||
isThreeLine: true,
|
client: widget.data[index],
|
||||||
onLongPress: () => openOptionsModal(widget.data[index]),
|
onTap: widget.onClientSelected,
|
||||||
onTap: () => openClientModal(widget.data[index]),
|
onLongPress: openOptionsModal,
|
||||||
title: Padding(
|
onEdit: openClientModal,
|
||||||
padding: const EdgeInsets.only(bottom: 5),
|
splitView: widget.splitView,
|
||||||
child: Text(
|
|
||||||
widget.data[index].name,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 18,
|
|
||||||
fontWeight: FontWeight.normal,
|
|
||||||
color: Theme.of(context).colorScheme.onSurface
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
subtitle: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
widget.data[index].ids.toString().replaceAll(RegExp(r'^\[|\]$'), ''),
|
|
||||||
style: TextStyle(
|
|
||||||
color: Theme.of(context).listTileTheme.textColor
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 7),
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
Icon(
|
|
||||||
Icons.filter_list_rounded,
|
|
||||||
size: 19,
|
|
||||||
color: widget.data[index].filteringEnabled == true
|
|
||||||
? appConfigProvider.useThemeColorForStatus == true
|
|
||||||
? Theme.of(context).colorScheme.primary
|
|
||||||
: Colors.green
|
|
||||||
: appConfigProvider.useThemeColorForStatus == true
|
|
||||||
? Colors.grey
|
|
||||||
: Colors.red,
|
|
||||||
),
|
|
||||||
const SizedBox(width: 10),
|
|
||||||
Icon(
|
|
||||||
Icons.vpn_lock_rounded,
|
|
||||||
size: 18,
|
|
||||||
color: widget.data[index].safebrowsingEnabled == true
|
|
||||||
? appConfigProvider.useThemeColorForStatus == true
|
|
||||||
? Theme.of(context).colorScheme.primary
|
|
||||||
: Colors.green
|
|
||||||
: appConfigProvider.useThemeColorForStatus == true
|
|
||||||
? Colors.grey
|
|
||||||
: Colors.red,
|
|
||||||
),
|
|
||||||
const SizedBox(width: 10),
|
|
||||||
Icon(
|
|
||||||
Icons.block,
|
|
||||||
size: 18,
|
|
||||||
color: widget.data[index].parentalEnabled == true
|
|
||||||
? appConfigProvider.useThemeColorForStatus == true
|
|
||||||
? Theme.of(context).colorScheme.primary
|
|
||||||
: Colors.green
|
|
||||||
: appConfigProvider.useThemeColorForStatus == true
|
|
||||||
? Colors.grey
|
|
||||||
: Colors.red,
|
|
||||||
),
|
|
||||||
const SizedBox(width: 10),
|
|
||||||
Icon(
|
|
||||||
Icons.search_rounded,
|
|
||||||
size: 19,
|
|
||||||
color: serverVersionIsAhead(
|
|
||||||
currentVersion: serversProvider.serverStatus.data!.serverVersion,
|
|
||||||
referenceVersion: 'v0.107.28',
|
|
||||||
referenceVersionBeta: 'v0.108.0-b.33'
|
|
||||||
) == true
|
|
||||||
? widget.data[index].safeSearch != null && widget.data[index].safeSearch!.enabled == true
|
|
||||||
? appConfigProvider.useThemeColorForStatus == true
|
|
||||||
? Theme.of(context).colorScheme.primary
|
|
||||||
: Colors.green
|
|
||||||
: appConfigProvider.useThemeColorForStatus == true
|
|
||||||
? Colors.grey
|
|
||||||
: Colors.red
|
|
||||||
: widget.data[index].safesearchEnabled == true
|
|
||||||
? appConfigProvider.useThemeColorForStatus == true
|
|
||||||
? Theme.of(context).colorScheme.primary
|
|
||||||
: Colors.green
|
|
||||||
: appConfigProvider.useThemeColorForStatus == true
|
|
||||||
? Colors.grey
|
|
||||||
: Colors.red,
|
|
||||||
)
|
|
||||||
],
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
noData: SizedBox(
|
noData: SizedBox(
|
||||||
width: double.maxFinite,
|
width: double.maxFinite,
|
||||||
|
|
|
@ -19,6 +19,7 @@ class ClientScreen extends StatefulWidget {
|
||||||
final String serverVersion;
|
final String serverVersion;
|
||||||
final void Function(Client) onConfirm;
|
final void Function(Client) onConfirm;
|
||||||
final void Function(Client)? onDelete;
|
final void Function(Client)? onDelete;
|
||||||
|
final bool dialog;
|
||||||
|
|
||||||
const ClientScreen({
|
const ClientScreen({
|
||||||
Key? key,
|
Key? key,
|
||||||
|
@ -26,6 +27,7 @@ class ClientScreen extends StatefulWidget {
|
||||||
required this.serverVersion,
|
required this.serverVersion,
|
||||||
required this.onConfirm,
|
required this.onConfirm,
|
||||||
this.onDelete,
|
this.onDelete,
|
||||||
|
required this.dialog
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -301,50 +303,12 @@ class _ClientScreenState extends State<ClientScreen> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Scaffold(
|
Widget content(bool withPaddingTop) {
|
||||||
appBar: AppBar(
|
return ListView(
|
||||||
leading: IconButton(
|
padding: const EdgeInsets.only(top: 0),
|
||||||
onPressed: () => Navigator.pop(context),
|
|
||||||
icon: const Icon(Icons.close)
|
|
||||||
),
|
|
||||||
title: Text(
|
|
||||||
widget.client != null
|
|
||||||
? AppLocalizations.of(context)!.client
|
|
||||||
: AppLocalizations.of(context)!.addClient
|
|
||||||
),
|
|
||||||
actions: [
|
|
||||||
if (widget.client == null || (widget.client != null && editMode == true)) IconButton(
|
|
||||||
onPressed: validValues == true
|
|
||||||
? () {
|
|
||||||
createClient();
|
|
||||||
Navigator.pop(context);
|
|
||||||
}
|
|
||||||
: null,
|
|
||||||
icon: Icon(
|
|
||||||
widget.client != null && editMode == true
|
|
||||||
? Icons.save_rounded
|
|
||||||
: Icons.check_rounded
|
|
||||||
),
|
|
||||||
tooltip: widget.client != null && editMode == true
|
|
||||||
? AppLocalizations.of(context)!.save
|
|
||||||
: AppLocalizations.of(context)!.confirm,
|
|
||||||
),
|
|
||||||
if (widget.client != null && editMode == false) IconButton(
|
|
||||||
onPressed: () => setState(() => editMode = true),
|
|
||||||
icon: const Icon(Icons.edit_rounded),
|
|
||||||
tooltip: AppLocalizations.of(context)!.edit,
|
|
||||||
),
|
|
||||||
if (widget.client != null) IconButton(
|
|
||||||
onPressed: openDeleteClientScreen,
|
|
||||||
icon: const Icon(Icons.delete_rounded),
|
|
||||||
tooltip: AppLocalizations.of(context)!.delete,
|
|
||||||
),
|
|
||||||
const SizedBox(width: 10),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
body: ListView(
|
|
||||||
children: [
|
children: [
|
||||||
const SizedBox(height: 24),
|
if (withPaddingTop == true) const SizedBox(height: 24),
|
||||||
|
if (withPaddingTop == false) const SizedBox(height: 6),
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 24),
|
padding: const EdgeInsets.symmetric(horizontal: 24),
|
||||||
child: TextFormField(
|
child: TextFormField(
|
||||||
|
@ -693,10 +657,7 @@ class _ClientScreenState extends State<ClientScreen> {
|
||||||
child: Row(
|
child: Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
SizedBox(
|
Expanded(
|
||||||
width: editMode == true
|
|
||||||
? MediaQuery.of(context).size.width - 108
|
|
||||||
: MediaQuery.of(context).size.width - 40,
|
|
||||||
child: TextFormField(
|
child: TextFormField(
|
||||||
enabled: editMode,
|
enabled: editMode,
|
||||||
controller: controller['controller'],
|
controller: controller['controller'],
|
||||||
|
@ -751,7 +712,125 @@ class _ClientScreenState extends State<ClientScreen> {
|
||||||
),
|
),
|
||||||
const SizedBox(height: 20)
|
const SizedBox(height: 20)
|
||||||
],
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (widget.dialog == true) {
|
||||||
|
return Dialog(
|
||||||
|
child: ConstrainedBox(
|
||||||
|
constraints: const BoxConstraints(
|
||||||
|
maxWidth: 500
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
IconButton(
|
||||||
|
onPressed: () => Navigator.pop(context),
|
||||||
|
icon: const Icon(Icons.clear_rounded)
|
||||||
|
),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
Text(
|
||||||
|
widget.client != null
|
||||||
|
? AppLocalizations.of(context)!.client
|
||||||
|
: AppLocalizations.of(context)!.addClient,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 22
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
if (widget.client == null || (widget.client != null && editMode == true)) IconButton(
|
||||||
|
onPressed: validValues == true
|
||||||
|
? () {
|
||||||
|
createClient();
|
||||||
|
Navigator.pop(context);
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
icon: Icon(
|
||||||
|
widget.client != null && editMode == true
|
||||||
|
? Icons.save_rounded
|
||||||
|
: Icons.check_rounded
|
||||||
|
),
|
||||||
|
tooltip: widget.client != null && editMode == true
|
||||||
|
? AppLocalizations.of(context)!.save
|
||||||
|
: AppLocalizations.of(context)!.confirm,
|
||||||
|
),
|
||||||
|
if (widget.client != null && editMode == false) IconButton(
|
||||||
|
onPressed: () => setState(() => editMode = true),
|
||||||
|
icon: const Icon(Icons.edit_rounded),
|
||||||
|
tooltip: AppLocalizations.of(context)!.edit,
|
||||||
|
),
|
||||||
|
if (widget.client != null) IconButton(
|
||||||
|
onPressed: openDeleteClientScreen,
|
||||||
|
icon: const Icon(Icons.delete_rounded),
|
||||||
|
tooltip: AppLocalizations.of(context)!.delete,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 10),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Flexible(
|
||||||
|
child: content(false)
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
leading: IconButton(
|
||||||
|
onPressed: () => Navigator.pop(context),
|
||||||
|
icon: const Icon(Icons.close)
|
||||||
|
),
|
||||||
|
title: Text(
|
||||||
|
widget.client != null
|
||||||
|
? AppLocalizations.of(context)!.client
|
||||||
|
: AppLocalizations.of(context)!.addClient
|
||||||
|
),
|
||||||
|
actions: [
|
||||||
|
if (widget.client == null || (widget.client != null && editMode == true)) IconButton(
|
||||||
|
onPressed: validValues == true
|
||||||
|
? () {
|
||||||
|
createClient();
|
||||||
|
Navigator.pop(context);
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
icon: Icon(
|
||||||
|
widget.client != null && editMode == true
|
||||||
|
? Icons.save_rounded
|
||||||
|
: Icons.check_rounded
|
||||||
|
),
|
||||||
|
tooltip: widget.client != null && editMode == true
|
||||||
|
? AppLocalizations.of(context)!.save
|
||||||
|
: AppLocalizations.of(context)!.confirm,
|
||||||
|
),
|
||||||
|
if (widget.client != null && editMode == false) IconButton(
|
||||||
|
onPressed: () => setState(() => editMode = true),
|
||||||
|
icon: const Icon(Icons.edit_rounded),
|
||||||
|
tooltip: AppLocalizations.of(context)!.edit,
|
||||||
|
),
|
||||||
|
if (widget.client != null) IconButton(
|
||||||
|
onPressed: openDeleteClientScreen,
|
||||||
|
icon: const Icon(Icons.delete_rounded),
|
||||||
|
tooltip: AppLocalizations.of(context)!.delete,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 10),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
body: content(true)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -1,9 +1,14 @@
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_split_view/flutter_split_view.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||||
|
|
||||||
import 'package:adguard_home_manager/screens/clients/clients_list.dart';
|
import 'package:adguard_home_manager/screens/clients/clients_list.dart';
|
||||||
import 'package:adguard_home_manager/screens/clients/search_clients.dart';
|
import 'package:adguard_home_manager/screens/clients/search_clients.dart';
|
||||||
|
import 'package:adguard_home_manager/screens/clients/logs_list_client.dart';
|
||||||
|
import 'package:adguard_home_manager/screens/clients/clients_desktop_view.dart';
|
||||||
import 'package:adguard_home_manager/screens/clients/added_list.dart';
|
import 'package:adguard_home_manager/screens/clients/added_list.dart';
|
||||||
|
|
||||||
import 'package:adguard_home_manager/models/app_log.dart';
|
import 'package:adguard_home_manager/models/app_log.dart';
|
||||||
|
@ -90,7 +95,118 @@ class _ClientsWidgetState extends State<ClientsWidget> with TickerProviderStateM
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final serversProvider = Provider.of<ServersProvider>(context);
|
final serversProvider = Provider.of<ServersProvider>(context);
|
||||||
|
final appConfigProvider = Provider.of<AppConfigProvider>(context);
|
||||||
|
|
||||||
|
final width = MediaQuery.of(context).size.width;
|
||||||
|
|
||||||
|
PreferredSizeWidget tabBar() {
|
||||||
|
return TabBar(
|
||||||
|
controller: tabController,
|
||||||
|
unselectedLabelColor: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||||
|
tabs: [
|
||||||
|
Tab(
|
||||||
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
const Icon(Icons.devices),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
Text(AppLocalizations.of(context)!.activeClients)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Tab(
|
||||||
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
const Icon(Icons.add_rounded),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
Text(AppLocalizations.of(context)!.added)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget tabBarView() {
|
||||||
|
return TabBarView(
|
||||||
|
controller: tabController,
|
||||||
|
children: [
|
||||||
|
ClientsList(
|
||||||
|
scrollController: scrollController,
|
||||||
|
loadStatus: serversProvider.clients.loadStatus,
|
||||||
|
data: serversProvider.clients.loadStatus == LoadStatus.loaded
|
||||||
|
? serversProvider.clients.data!.autoClientsData : [],
|
||||||
|
fetchClients: fetchClients,
|
||||||
|
onClientSelected: (client) => Navigator.push(context, MaterialPageRoute(
|
||||||
|
builder: (context) => LogsListClient(
|
||||||
|
ip: client.ip,
|
||||||
|
serversProvider: serversProvider,
|
||||||
|
appConfigProvider: appConfigProvider
|
||||||
|
)
|
||||||
|
)),
|
||||||
|
splitView: false,
|
||||||
|
),
|
||||||
|
AddedList(
|
||||||
|
scrollController: scrollController,
|
||||||
|
loadStatus: serversProvider.clients.loadStatus,
|
||||||
|
data: serversProvider.clients.loadStatus == LoadStatus.loaded
|
||||||
|
? serversProvider.clients.data!.clients : [],
|
||||||
|
fetchClients: fetchClients,
|
||||||
|
onClientSelected: (client) => Navigator.push(context, MaterialPageRoute(
|
||||||
|
builder: (context) => LogsListClient(
|
||||||
|
ip: client.ids[0],
|
||||||
|
serversProvider: serversProvider,
|
||||||
|
appConfigProvider: appConfigProvider
|
||||||
|
)
|
||||||
|
)),
|
||||||
|
splitView: false,
|
||||||
|
),
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(Platform.isAndroid || Platform.isIOS)) {
|
||||||
|
if (width > 900) {
|
||||||
|
return SplitView.material(
|
||||||
|
breakpoint: 900,
|
||||||
|
hideDivider: true,
|
||||||
|
child: ClientsDesktopView(
|
||||||
|
serversProvider: serversProvider,
|
||||||
|
appConfigProvider: appConfigProvider,
|
||||||
|
fetchClients: fetchClients,
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return DefaultTabController(
|
||||||
|
length: 2,
|
||||||
|
child: Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: Text(AppLocalizations.of(context)!.clients),
|
||||||
|
centerTitle: false,
|
||||||
|
actions: [
|
||||||
|
if (serversProvider.clients.loadStatus == LoadStatus.loaded) ...[
|
||||||
|
IconButton(
|
||||||
|
onPressed: () => {
|
||||||
|
Navigator.push(context, MaterialPageRoute(
|
||||||
|
builder: (context) => const SearchClients()
|
||||||
|
))
|
||||||
|
},
|
||||||
|
icon: const Icon(Icons.search),
|
||||||
|
tooltip: AppLocalizations.of(context)!.searchClients,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 10),
|
||||||
|
]
|
||||||
|
],
|
||||||
|
bottom: tabBar()
|
||||||
|
),
|
||||||
|
body: tabBarView(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
return DefaultTabController(
|
return DefaultTabController(
|
||||||
length: 2,
|
length: 2,
|
||||||
child: NestedScrollView(
|
child: NestedScrollView(
|
||||||
|
@ -119,54 +235,14 @@ class _ClientsWidgetState extends State<ClientsWidget> with TickerProviderStateM
|
||||||
const SizedBox(width: 10),
|
const SizedBox(width: 10),
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
bottom: TabBar(
|
bottom: tabBar()
|
||||||
controller: tabController,
|
|
||||||
unselectedLabelColor: Theme.of(context).colorScheme.onSurfaceVariant,
|
|
||||||
tabs: [
|
|
||||||
Tab(
|
|
||||||
icon: const Icon(Icons.devices),
|
|
||||||
text: AppLocalizations.of(context)!.activeClients,
|
|
||||||
),
|
|
||||||
Tab(
|
|
||||||
icon: const Icon(Icons.add_rounded),
|
|
||||||
text: AppLocalizations.of(context)!.added,
|
|
||||||
),
|
|
||||||
]
|
|
||||||
)
|
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
];
|
];
|
||||||
}),
|
}),
|
||||||
body: Container(
|
body: tabBarView()
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Theme.of(context).scaffoldBackgroundColor,
|
|
||||||
border: Border(
|
|
||||||
top: BorderSide(
|
|
||||||
color: Theme.of(context).colorScheme.onSurface.withOpacity(0.1)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
child: TabBarView(
|
|
||||||
controller: tabController,
|
|
||||||
children: [
|
|
||||||
ClientsList(
|
|
||||||
scrollController: scrollController,
|
|
||||||
loadStatus: serversProvider.clients.loadStatus,
|
|
||||||
data: serversProvider.clients.loadStatus == LoadStatus.loaded
|
|
||||||
? serversProvider.clients.data!.autoClientsData : [],
|
|
||||||
fetchClients: fetchClients,
|
|
||||||
),
|
|
||||||
AddedList(
|
|
||||||
scrollController: scrollController,
|
|
||||||
loadStatus: serversProvider.clients.loadStatus,
|
|
||||||
data: serversProvider.clients.loadStatus == LoadStatus.loaded
|
|
||||||
? serversProvider.clients.data!.clients : [],
|
|
||||||
fetchClients: fetchClients,
|
|
||||||
),
|
|
||||||
]
|
|
||||||
)
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
161
lib/screens/clients/clients_desktop_view.dart
Normal file
161
lib/screens/clients/clients_desktop_view.dart
Normal file
|
@ -0,0 +1,161 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_split_view/flutter_split_view.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||||
|
|
||||||
|
import 'package:adguard_home_manager/screens/clients/logs_list_client.dart';
|
||||||
|
import 'package:adguard_home_manager/screens/clients/added_list.dart';
|
||||||
|
import 'package:adguard_home_manager/screens/clients/clients_list.dart';
|
||||||
|
|
||||||
|
import 'package:adguard_home_manager/providers/app_config_provider.dart';
|
||||||
|
import 'package:adguard_home_manager/constants/enums.dart';
|
||||||
|
import 'package:adguard_home_manager/models/clients.dart';
|
||||||
|
import 'package:adguard_home_manager/providers/servers_provider.dart';
|
||||||
|
import 'package:adguard_home_manager/screens/clients/search_clients.dart';
|
||||||
|
|
||||||
|
|
||||||
|
class ClientsDesktopView extends StatefulWidget {
|
||||||
|
final ServersProvider serversProvider;
|
||||||
|
final AppConfigProvider appConfigProvider;
|
||||||
|
final Future Function() fetchClients;
|
||||||
|
|
||||||
|
const ClientsDesktopView({
|
||||||
|
Key? key,
|
||||||
|
required this.serversProvider,
|
||||||
|
required this.appConfigProvider,
|
||||||
|
required this.fetchClients
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<ClientsDesktopView> createState() => _ClientsDesktopViewState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ClientsDesktopViewState extends State<ClientsDesktopView> with TickerProviderStateMixin {
|
||||||
|
late TabController tabController;
|
||||||
|
final ScrollController scrollController = ScrollController();
|
||||||
|
|
||||||
|
AutoClient? selectedActiveClient;
|
||||||
|
Client? selectedAddedClient;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
tabController = TabController(
|
||||||
|
initialIndex: 0,
|
||||||
|
length: 2,
|
||||||
|
vsync: this,
|
||||||
|
);
|
||||||
|
tabController.addListener(() => widget.appConfigProvider.setSelectedClientsTab(tabController.index));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final serversProvider = Provider.of<ServersProvider>(context);
|
||||||
|
final appConfigProvider = Provider.of<AppConfigProvider>(context);
|
||||||
|
|
||||||
|
PreferredSizeWidget tabBar() {
|
||||||
|
return TabBar(
|
||||||
|
controller: tabController,
|
||||||
|
unselectedLabelColor: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||||
|
tabs: [
|
||||||
|
Tab(
|
||||||
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
const Icon(Icons.devices),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
Text(AppLocalizations.of(context)!.activeClients)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Tab(
|
||||||
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
const Icon(Icons.add_rounded),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
Text(AppLocalizations.of(context)!.added)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget tabBarView() {
|
||||||
|
return TabBarView(
|
||||||
|
controller: tabController,
|
||||||
|
children: [
|
||||||
|
ClientsList(
|
||||||
|
scrollController: scrollController,
|
||||||
|
loadStatus: serversProvider.clients.loadStatus,
|
||||||
|
data: serversProvider.clients.loadStatus == LoadStatus.loaded
|
||||||
|
? serversProvider.clients.data!.autoClientsData : [],
|
||||||
|
fetchClients: widget.fetchClients,
|
||||||
|
onClientSelected: (client) => setState(() {
|
||||||
|
selectedAddedClient = null;
|
||||||
|
selectedActiveClient = client;
|
||||||
|
SplitView.of(context).setSecondary(
|
||||||
|
LogsListClient(
|
||||||
|
ip: client.ip,
|
||||||
|
name: client.name,
|
||||||
|
serversProvider: serversProvider,
|
||||||
|
appConfigProvider: appConfigProvider,
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
selectedClient: selectedActiveClient,
|
||||||
|
splitView: true
|
||||||
|
),
|
||||||
|
AddedList(
|
||||||
|
scrollController: scrollController,
|
||||||
|
loadStatus: serversProvider.clients.loadStatus,
|
||||||
|
data: serversProvider.clients.loadStatus == LoadStatus.loaded
|
||||||
|
? serversProvider.clients.data!.clients : [],
|
||||||
|
fetchClients: widget.fetchClients,
|
||||||
|
onClientSelected: (client) => setState(() {
|
||||||
|
selectedActiveClient = null;
|
||||||
|
selectedAddedClient = client;
|
||||||
|
SplitView.of(context).setSecondary(
|
||||||
|
LogsListClient(
|
||||||
|
ip: client.ids[0],
|
||||||
|
name: client.name,
|
||||||
|
serversProvider: serversProvider,
|
||||||
|
appConfigProvider: appConfigProvider,
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
selectedClient: selectedAddedClient,
|
||||||
|
splitView: true,
|
||||||
|
),
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return DefaultTabController(
|
||||||
|
length: 2,
|
||||||
|
child: Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: Text(AppLocalizations.of(context)!.clients),
|
||||||
|
centerTitle: false,
|
||||||
|
actions: [
|
||||||
|
if (serversProvider.clients.loadStatus == LoadStatus.loaded) ...[
|
||||||
|
IconButton(
|
||||||
|
onPressed: () => {
|
||||||
|
Navigator.push(context, MaterialPageRoute(
|
||||||
|
builder: (context) => const SearchClients()
|
||||||
|
))
|
||||||
|
},
|
||||||
|
icon: const Icon(Icons.search),
|
||||||
|
tooltip: AppLocalizations.of(context)!.searchClients,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 10),
|
||||||
|
]
|
||||||
|
],
|
||||||
|
bottom: tabBar()
|
||||||
|
),
|
||||||
|
body: tabBarView(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,36 +1,42 @@
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:provider/provider.dart';
|
|
||||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||||
|
|
||||||
import 'package:adguard_home_manager/widgets/custom_list_tile.dart';
|
import 'package:adguard_home_manager/screens/clients/active_client_tile.dart';
|
||||||
|
|
||||||
import 'package:adguard_home_manager/widgets/tab_content_list.dart';
|
import 'package:adguard_home_manager/widgets/tab_content_list.dart';
|
||||||
|
|
||||||
import 'package:adguard_home_manager/models/clients.dart';
|
import 'package:adguard_home_manager/models/clients.dart';
|
||||||
import 'package:adguard_home_manager/models/applied_filters.dart';
|
|
||||||
import 'package:adguard_home_manager/providers/app_config_provider.dart';
|
|
||||||
import 'package:adguard_home_manager/constants/enums.dart';
|
import 'package:adguard_home_manager/constants/enums.dart';
|
||||||
import 'package:adguard_home_manager/providers/logs_provider.dart';
|
|
||||||
|
|
||||||
class ClientsList extends StatelessWidget {
|
class ClientsList extends StatelessWidget {
|
||||||
final ScrollController scrollController;
|
final ScrollController scrollController;
|
||||||
final LoadStatus loadStatus;
|
final LoadStatus loadStatus;
|
||||||
final List<AutoClient> data;
|
final List<AutoClient> data;
|
||||||
final Future Function() fetchClients;
|
final Future Function() fetchClients;
|
||||||
|
final void Function(AutoClient) onClientSelected;
|
||||||
|
final AutoClient? selectedClient;
|
||||||
|
final bool splitView;
|
||||||
|
|
||||||
const ClientsList({
|
const ClientsList({
|
||||||
Key? key,
|
Key? key,
|
||||||
required this.scrollController,
|
required this.scrollController,
|
||||||
required this.loadStatus,
|
required this.loadStatus,
|
||||||
required this.data,
|
required this.data,
|
||||||
required this.fetchClients
|
required this.fetchClients,
|
||||||
|
required this.onClientSelected,
|
||||||
|
this.selectedClient,
|
||||||
|
required this.splitView
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final appConfigProvider = Provider.of<AppConfigProvider>(context);
|
|
||||||
final logsProvider = Provider.of<LogsProvider>(context);
|
|
||||||
|
|
||||||
return CustomTabContentList(
|
return CustomTabContentList(
|
||||||
|
listPadding: splitView == true
|
||||||
|
? const EdgeInsets.only(top: 8)
|
||||||
|
: null,
|
||||||
|
noSliver: !(Platform.isAndroid || Platform.isIOS),
|
||||||
loadingGenerator: () => SizedBox(
|
loadingGenerator: () => SizedBox(
|
||||||
width: double.maxFinite,
|
width: double.maxFinite,
|
||||||
height: MediaQuery.of(context).size.height-171,
|
height: MediaQuery.of(context).size.height-171,
|
||||||
|
@ -52,31 +58,11 @@ class ClientsList extends StatelessWidget {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
itemsCount: data.length,
|
itemsCount: data.length,
|
||||||
contentWidget: (index) => CustomListTile(
|
contentWidget: (index) => ActiveClientTile(
|
||||||
title: data[index].name != ''
|
client: data[index],
|
||||||
? data[index].name!
|
onTap: onClientSelected,
|
||||||
: data[index].ip,
|
splitView: splitView,
|
||||||
subtitle: data[index].name != ''
|
selectedClient: selectedClient,
|
||||||
? data[index].ip
|
|
||||||
: null,
|
|
||||||
trailing: Text(
|
|
||||||
data[index].source,
|
|
||||||
style: TextStyle(
|
|
||||||
color: Theme.of(context).colorScheme.onSurface
|
|
||||||
),
|
|
||||||
),
|
|
||||||
onTap: () {
|
|
||||||
logsProvider.setSearchText(null);
|
|
||||||
logsProvider.setSelectedClients([data[index].ip]);
|
|
||||||
logsProvider.setAppliedFilters(
|
|
||||||
AppliedFiters(
|
|
||||||
selectedResultStatus: 'all',
|
|
||||||
searchText: null,
|
|
||||||
clients: [data[index].ip]
|
|
||||||
)
|
|
||||||
);
|
|
||||||
appConfigProvider.setSelectedScreen(2);
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
noData: SizedBox(
|
noData: SizedBox(
|
||||||
width: double.maxFinite,
|
width: double.maxFinite,
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
// ignore_for_file: use_build_context_synchronously
|
// ignore_for_file: use_build_context_synchronously
|
||||||
|
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||||
|
@ -23,6 +25,8 @@ class ClientsFab extends StatelessWidget {
|
||||||
final serversProvider = Provider.of<ServersProvider>(context);
|
final serversProvider = Provider.of<ServersProvider>(context);
|
||||||
final appConfigProvider = Provider.of<AppConfigProvider>(context);
|
final appConfigProvider = Provider.of<AppConfigProvider>(context);
|
||||||
|
|
||||||
|
final width = MediaQuery.of(context).size.width;
|
||||||
|
|
||||||
void confirmAddClient(Client client) async {
|
void confirmAddClient(Client client) async {
|
||||||
ProcessModal processModal = ProcessModal(context: context);
|
ProcessModal processModal = ProcessModal(context: context);
|
||||||
processModal.open(AppLocalizations.of(context)!.addingClient);
|
processModal.open(AppLocalizations.of(context)!.addingClient);
|
||||||
|
@ -65,14 +69,28 @@ class ClientsFab extends StatelessWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
void openAddClient() {
|
void openAddClient() {
|
||||||
|
if (width > 900 || !(Platform.isAndroid | Platform.isIOS)) {
|
||||||
|
showDialog(
|
||||||
|
barrierDismissible: false,
|
||||||
|
context: context,
|
||||||
|
builder: (BuildContext context) => ClientScreen(
|
||||||
|
onConfirm: confirmAddClient,
|
||||||
|
serverVersion: serversProvider.serverStatus.data!.serverVersion,
|
||||||
|
dialog: true,
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
Navigator.push(context, MaterialPageRoute(
|
Navigator.push(context, MaterialPageRoute(
|
||||||
fullscreenDialog: true,
|
fullscreenDialog: true,
|
||||||
builder: (BuildContext context) => ClientScreen(
|
builder: (BuildContext context) => ClientScreen(
|
||||||
onConfirm: confirmAddClient,
|
onConfirm: confirmAddClient,
|
||||||
serverVersion: serversProvider.serverStatus.data!.serverVersion,
|
serverVersion: serversProvider.serverStatus.data!.serverVersion,
|
||||||
|
dialog: false,
|
||||||
)
|
)
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return FloatingActionButton(
|
return FloatingActionButton(
|
||||||
onPressed: openAddClient,
|
onPressed: openAddClient,
|
||||||
|
|
223
lib/screens/clients/logs_list_client.dart
Normal file
223
lib/screens/clients/logs_list_client.dart
Normal file
|
@ -0,0 +1,223 @@
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||||
|
|
||||||
|
import 'package:adguard_home_manager/screens/logs/log_tile.dart';
|
||||||
|
|
||||||
|
import 'package:adguard_home_manager/models/logs.dart';
|
||||||
|
import 'package:adguard_home_manager/providers/app_config_provider.dart';
|
||||||
|
import 'package:adguard_home_manager/providers/servers_provider.dart';
|
||||||
|
import 'package:adguard_home_manager/services/http_requests.dart';
|
||||||
|
|
||||||
|
class LogsListClient extends StatefulWidget {
|
||||||
|
final String ip;
|
||||||
|
final String? name;
|
||||||
|
final ServersProvider serversProvider;
|
||||||
|
final AppConfigProvider appConfigProvider;
|
||||||
|
|
||||||
|
const LogsListClient({
|
||||||
|
Key? key,
|
||||||
|
required this.ip,
|
||||||
|
this.name,
|
||||||
|
required this.serversProvider,
|
||||||
|
required this.appConfigProvider
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<LogsListClient> createState() => _LogsListClientState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _LogsListClientState extends State<LogsListClient> {
|
||||||
|
late ScrollController scrollController;
|
||||||
|
|
||||||
|
bool isLoadingMore = false;
|
||||||
|
|
||||||
|
int logsQuantity = 100;
|
||||||
|
int offset = 0;
|
||||||
|
|
||||||
|
int loadStatus = 0;
|
||||||
|
LogsData? logsData;
|
||||||
|
|
||||||
|
String previousIp = "";
|
||||||
|
|
||||||
|
bool showDivider = true;
|
||||||
|
|
||||||
|
Future fetchLogs({
|
||||||
|
int? inOffset,
|
||||||
|
bool? loadingMore,
|
||||||
|
String? responseStatus,
|
||||||
|
String? searchText,
|
||||||
|
}) async {
|
||||||
|
int offst = inOffset ?? offset;
|
||||||
|
|
||||||
|
if (loadingMore != null && loadingMore == true) {
|
||||||
|
setState(() => isLoadingMore = true);
|
||||||
|
}
|
||||||
|
|
||||||
|
final result = await getLogs(
|
||||||
|
server: widget.serversProvider.selectedServer!,
|
||||||
|
count: logsQuantity,
|
||||||
|
offset: offst,
|
||||||
|
search: '"${widget.ip}"'
|
||||||
|
);
|
||||||
|
|
||||||
|
if (loadingMore != null && loadingMore == true) {
|
||||||
|
setState(() => isLoadingMore = false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mounted) {
|
||||||
|
if (result['result'] == 'success') {
|
||||||
|
setState(() => offset = inOffset != null ? inOffset+logsQuantity : offset+logsQuantity);
|
||||||
|
if (loadingMore != null && loadingMore == true && logsData != null) {
|
||||||
|
LogsData newLogsData = result['data'];
|
||||||
|
newLogsData.data = [...logsData!.data, ...result['data'].data];
|
||||||
|
setState(() => logsData = newLogsData);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
LogsData newLogsData = result['data'];
|
||||||
|
setState(() => logsData = newLogsData);
|
||||||
|
}
|
||||||
|
setState(() => loadStatus = 1);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
setState(() => loadStatus = 2);
|
||||||
|
widget.appConfigProvider.addLog(result['log']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void scrollListener() {
|
||||||
|
if (scrollController.position.extentAfter < 500 && isLoadingMore == false) {
|
||||||
|
fetchLogs(loadingMore: true);
|
||||||
|
}
|
||||||
|
if (scrollController.position.pixels > 0) {
|
||||||
|
setState(() => showDivider = false);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
setState(() => showDivider = true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
scrollController = ScrollController()..addListener(scrollListener);
|
||||||
|
fetchLogs(inOffset: 0);
|
||||||
|
setState(() => previousIp = widget.ip);
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
if (widget.ip != previousIp) {
|
||||||
|
setState(() => loadStatus = 0);
|
||||||
|
if (scrollController.hasClients) scrollController.animateTo(0, duration: const Duration(milliseconds: 1), curve: Curves.ease);
|
||||||
|
fetchLogs(inOffset: 0);
|
||||||
|
setState(() => previousIp = widget.ip);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget status() {
|
||||||
|
switch (loadStatus) {
|
||||||
|
case 0:
|
||||||
|
return SizedBox(
|
||||||
|
width: double.maxFinite,
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
const CircularProgressIndicator(),
|
||||||
|
const SizedBox(height: 30),
|
||||||
|
Text(
|
||||||
|
AppLocalizations.of(context)!.loadingLogs,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 22,
|
||||||
|
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
case 1:
|
||||||
|
return RefreshIndicator(
|
||||||
|
onRefresh: fetchLogs,
|
||||||
|
child: ListView.builder(
|
||||||
|
controller: scrollController,
|
||||||
|
padding: const EdgeInsets.only(top: 0),
|
||||||
|
itemCount: isLoadingMore == true
|
||||||
|
? logsData!.data.length+1
|
||||||
|
: logsData!.data.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
if (isLoadingMore == true && index == logsData!.data.length) {
|
||||||
|
return const Padding(
|
||||||
|
padding: EdgeInsets.symmetric(vertical: 20),
|
||||||
|
child: Center(
|
||||||
|
child: CircularProgressIndicator(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return LogTile(
|
||||||
|
log: logsData!.data[index],
|
||||||
|
index: index,
|
||||||
|
length: logsData!.data.length,
|
||||||
|
onLogTap: (log) {
|
||||||
|
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
return SizedBox(
|
||||||
|
width: double.maxFinite,
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
const Icon(
|
||||||
|
Icons.error,
|
||||||
|
color: Colors.red,
|
||||||
|
size: 50,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 30),
|
||||||
|
Text(
|
||||||
|
AppLocalizations.of(context)!.logsNotLoaded,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 22,
|
||||||
|
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
default:
|
||||||
|
return const SizedBox();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: Text(widget.name != null && widget.name != '' ? widget.name! : widget.ip),
|
||||||
|
centerTitle: true,
|
||||||
|
actions: [
|
||||||
|
if (!(Platform.isAndroid || Platform.isIOS)) ...[
|
||||||
|
IconButton(
|
||||||
|
onPressed: fetchLogs,
|
||||||
|
icon: const Icon(Icons.refresh_rounded),
|
||||||
|
tooltip: AppLocalizations.of(context)!.refresh,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 8)
|
||||||
|
]
|
||||||
|
],
|
||||||
|
),
|
||||||
|
body: status(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -64,7 +64,11 @@ class _SafeSearchModalState extends State<SafeSearchModal> {
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
content: Wrap(
|
content: ConstrainedBox(
|
||||||
|
constraints: const BoxConstraints(
|
||||||
|
maxWidth: 400
|
||||||
|
),
|
||||||
|
child: Wrap(
|
||||||
children: [
|
children: [
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||||
|
@ -168,6 +172,7 @@ class _SafeSearchModalState extends State<SafeSearchModal> {
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
),
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () => Navigator.pop(context),
|
onPressed: () => Navigator.pop(context),
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
// ignore_for_file: use_build_context_synchronously
|
// ignore_for_file: use_build_context_synchronously
|
||||||
|
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:animations/animations.dart';
|
import 'package:animations/animations.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
@ -99,6 +101,8 @@ class _SearchClientsWidgetState extends State<SearchClientsWidget> {
|
||||||
final serversProvider = Provider.of<ServersProvider>(context);
|
final serversProvider = Provider.of<ServersProvider>(context);
|
||||||
final appConfigProvider = Provider.of<AppConfigProvider>(context);
|
final appConfigProvider = Provider.of<AppConfigProvider>(context);
|
||||||
|
|
||||||
|
final width = MediaQuery.of(context).size.width;
|
||||||
|
|
||||||
void deleteClient(Client client) async {
|
void deleteClient(Client client) async {
|
||||||
ProcessModal processModal = ProcessModal(context: context);
|
ProcessModal processModal = ProcessModal(context: context);
|
||||||
processModal.open(AppLocalizations.of(context)!.removingClient);
|
processModal.open(AppLocalizations.of(context)!.removingClient);
|
||||||
|
@ -183,16 +187,32 @@ class _SearchClientsWidgetState extends State<SearchClientsWidget> {
|
||||||
}
|
}
|
||||||
|
|
||||||
void openClientModal(Client client) {
|
void openClientModal(Client client) {
|
||||||
|
if (width > 900 || !(Platform.isAndroid | Platform.isIOS)) {
|
||||||
|
showDialog(
|
||||||
|
barrierDismissible: false,
|
||||||
|
context: context,
|
||||||
|
builder: (BuildContext context) => ClientScreen(
|
||||||
|
onConfirm: confirmEditClient,
|
||||||
|
serverVersion: serversProvider.serverStatus.data!.serverVersion,
|
||||||
|
onDelete: deleteClient,
|
||||||
|
client: client,
|
||||||
|
dialog: true,
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
Navigator.push(context, MaterialPageRoute(
|
Navigator.push(context, MaterialPageRoute(
|
||||||
fullscreenDialog: true,
|
fullscreenDialog: true,
|
||||||
builder: (BuildContext context) => ClientScreen(
|
builder: (BuildContext context) => ClientScreen(
|
||||||
onConfirm: confirmEditClient,
|
onConfirm: confirmEditClient,
|
||||||
|
serverVersion: serversProvider.serverStatus.data!.serverVersion,
|
||||||
onDelete: deleteClient,
|
onDelete: deleteClient,
|
||||||
client: client,
|
client: client,
|
||||||
serverVersion: serversProvider.serverStatus.data!.serverVersion,
|
dialog: false,
|
||||||
)
|
)
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void openDeleteModal(Client client) {
|
void openDeleteModal(Client client) {
|
||||||
showModal(
|
showModal(
|
||||||
|
|
|
@ -42,7 +42,7 @@ class LogTile extends StatelessWidget {
|
||||||
required String text
|
required String text
|
||||||
}) {
|
}) {
|
||||||
return SizedBox(
|
return SizedBox(
|
||||||
width: 70,
|
width: 80,
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
Icon(
|
Icon(
|
||||||
|
|
|
@ -10,6 +10,7 @@ class CustomListTile extends StatelessWidget {
|
||||||
final EdgeInsets? padding;
|
final EdgeInsets? padding;
|
||||||
final void Function()? onLongPress;
|
final void Function()? onLongPress;
|
||||||
final bool? disabled;
|
final bool? disabled;
|
||||||
|
final void Function(bool)? onHover;
|
||||||
|
|
||||||
const CustomListTile({
|
const CustomListTile({
|
||||||
Key? key,
|
Key? key,
|
||||||
|
@ -21,7 +22,8 @@ class CustomListTile extends StatelessWidget {
|
||||||
this.trailing,
|
this.trailing,
|
||||||
this.padding,
|
this.padding,
|
||||||
this.onLongPress,
|
this.onLongPress,
|
||||||
this.disabled
|
this.disabled,
|
||||||
|
this.onHover
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -30,6 +32,7 @@ class CustomListTile extends StatelessWidget {
|
||||||
color: Colors.transparent,
|
color: Colors.transparent,
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
onTap: onTap,
|
onTap: onTap,
|
||||||
|
onHover: onHover,
|
||||||
onLongPress: onLongPress,
|
onLongPress: onLongPress,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: padding ?? const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
padding: padding ?? const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
||||||
|
|
|
@ -16,6 +16,7 @@ class CustomTabContentList extends StatelessWidget {
|
||||||
final Widget? fab;
|
final Widget? fab;
|
||||||
final bool? fabVisible;
|
final bool? fabVisible;
|
||||||
final bool? noSliver;
|
final bool? noSliver;
|
||||||
|
final EdgeInsets? listPadding;
|
||||||
|
|
||||||
const CustomTabContentList({
|
const CustomTabContentList({
|
||||||
Key? key,
|
Key? key,
|
||||||
|
@ -29,7 +30,8 @@ class CustomTabContentList extends StatelessWidget {
|
||||||
this.refreshIndicatorOffset,
|
this.refreshIndicatorOffset,
|
||||||
this.fab,
|
this.fab,
|
||||||
this.fabVisible,
|
this.fabVisible,
|
||||||
this.noSliver
|
this.noSliver,
|
||||||
|
this.listPadding
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -73,6 +75,7 @@ class CustomTabContentList extends StatelessWidget {
|
||||||
return Stack(
|
return Stack(
|
||||||
children: [
|
children: [
|
||||||
ListView.builder(
|
ListView.builder(
|
||||||
|
padding: listPadding,
|
||||||
itemCount: itemsCount,
|
itemCount: itemsCount,
|
||||||
itemBuilder: (context, index) => contentWidget(index),
|
itemBuilder: (context, index) => contentWidget(index),
|
||||||
),
|
),
|
||||||
|
@ -117,7 +120,7 @@ class CustomTabContentList extends StatelessWidget {
|
||||||
builder: (BuildContext context) {
|
builder: (BuildContext context) {
|
||||||
return RefreshIndicator(
|
return RefreshIndicator(
|
||||||
onRefresh: onRefresh,
|
onRefresh: onRefresh,
|
||||||
edgeOffset: refreshIndicatorOffset ?? 95,
|
edgeOffset: refreshIndicatorOffset ?? 70,
|
||||||
child: CustomScrollView(
|
child: CustomScrollView(
|
||||||
slivers: <Widget>[
|
slivers: <Widget>[
|
||||||
SliverOverlapInjector(
|
SliverOverlapInjector(
|
||||||
|
|
Loading…
Add table
Reference in a new issue