settings: changed layout for hidden filters and access grants
This commit is contained in:
parent
cadd2b4d1c
commit
f16d98ba2b
4 changed files with 151 additions and 94 deletions
|
@ -6,7 +6,7 @@ class EmptyContent extends StatelessWidget {
|
||||||
final AlignmentGeometry alignment;
|
final AlignmentGeometry alignment;
|
||||||
|
|
||||||
const EmptyContent({
|
const EmptyContent({
|
||||||
@required this.icon,
|
this.icon,
|
||||||
@required this.text,
|
@required this.text,
|
||||||
this.alignment = const FractionalOffset(.5, .35),
|
this.alignment = const FractionalOffset(.5, .35),
|
||||||
});
|
});
|
||||||
|
@ -19,12 +19,14 @@ class EmptyContent extends StatelessWidget {
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
Icon(
|
if (icon != null) ...[
|
||||||
icon,
|
Icon(
|
||||||
size: 64,
|
icon,
|
||||||
color: color,
|
size: 64,
|
||||||
),
|
color: color,
|
||||||
SizedBox(height: 16),
|
),
|
||||||
|
SizedBox(height: 16)
|
||||||
|
],
|
||||||
Text(
|
Text(
|
||||||
text,
|
text,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
|
|
|
@ -1,13 +1,34 @@
|
||||||
import 'package:aves/services/android_file_service.dart';
|
import 'package:aves/services/android_file_service.dart';
|
||||||
|
import 'package:aves/theme/icons.dart';
|
||||||
|
import 'package:aves/widgets/collection/empty.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:intl/intl.dart';
|
|
||||||
|
|
||||||
class GrantedDirectories extends StatefulWidget {
|
class StorageAccessTile extends StatelessWidget {
|
||||||
@override
|
@override
|
||||||
_GrantedDirectoriesState createState() => _GrantedDirectoriesState();
|
Widget build(BuildContext context) {
|
||||||
|
return ListTile(
|
||||||
|
title: Text('Storage Access'),
|
||||||
|
onTap: () {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
settings: RouteSettings(name: StorageAccessPage.routeName),
|
||||||
|
builder: (context) => StorageAccessPage(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _GrantedDirectoriesState extends State<GrantedDirectories> {
|
class StorageAccessPage extends StatefulWidget {
|
||||||
|
static const routeName = '/settings/storage_access';
|
||||||
|
|
||||||
|
@override
|
||||||
|
_StorageAccessPageState createState() => _StorageAccessPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _StorageAccessPageState extends State<StorageAccessPage> {
|
||||||
Future<List<String>> _pathLoader;
|
Future<List<String>> _pathLoader;
|
||||||
List<String> _lastPaths;
|
List<String> _lastPaths;
|
||||||
|
|
||||||
|
@ -21,44 +42,64 @@ class _GrantedDirectoriesState extends State<GrantedDirectories> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final textTheme = Theme.of(context).textTheme;
|
return Scaffold(
|
||||||
return Padding(
|
appBar: AppBar(
|
||||||
padding: EdgeInsets.symmetric(horizontal: 16),
|
title: Text('Storage Access'),
|
||||||
child: FutureBuilder<List<String>>(
|
),
|
||||||
future: _pathLoader,
|
body: SafeArea(
|
||||||
builder: (context, snapshot) {
|
child: Column(
|
||||||
if (snapshot.hasError) {
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
return Text(snapshot.error.toString());
|
children: [
|
||||||
}
|
Padding(
|
||||||
if (snapshot.connectionState != ConnectionState.done && _lastPaths == null) {
|
padding: EdgeInsets.symmetric(vertical: 8, horizontal: 16),
|
||||||
return SizedBox.shrink();
|
child: Row(
|
||||||
}
|
children: [
|
||||||
_lastPaths = snapshot.data..sort();
|
Icon(AIcons.info),
|
||||||
final count = _lastPaths.length;
|
SizedBox(width: 16),
|
||||||
return Column(
|
Expanded(child: Text('Some directories require an explicit access grant to modify files in them. You can review here directories to which you previously gave access.')),
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
],
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
'Aves has access to ${Intl.plural(count, zero: 'no directories.', one: 'one directory:', other: '$count directories:')}',
|
|
||||||
style: textTheme.subtitle1,
|
|
||||||
),
|
),
|
||||||
..._lastPaths.map((path) => Row(
|
),
|
||||||
children: [
|
Divider(),
|
||||||
Expanded(child: Text(path, style: textTheme.caption)),
|
Expanded(
|
||||||
SizedBox(width: 8),
|
child: FutureBuilder<List<String>>(
|
||||||
OutlinedButton(
|
future: _pathLoader,
|
||||||
onPressed: () async {
|
builder: (context, snapshot) {
|
||||||
await AndroidFileService.revokeDirectoryAccess(path);
|
if (snapshot.hasError) {
|
||||||
_load();
|
return Text(snapshot.error.toString());
|
||||||
setState(() {});
|
}
|
||||||
},
|
if (snapshot.connectionState != ConnectionState.done && _lastPaths == null) {
|
||||||
child: Text('Revoke'.toUpperCase()),
|
return SizedBox.shrink();
|
||||||
),
|
}
|
||||||
],
|
_lastPaths = snapshot.data..sort();
|
||||||
)),
|
if (_lastPaths.isEmpty) {
|
||||||
],
|
return EmptyContent(
|
||||||
);
|
text: 'No access grants',
|
||||||
},
|
);
|
||||||
|
}
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: _lastPaths
|
||||||
|
.map((path) => ListTile(
|
||||||
|
title: Text(path),
|
||||||
|
dense: true,
|
||||||
|
trailing: IconButton(
|
||||||
|
icon: Icon(AIcons.clear),
|
||||||
|
onPressed: () async {
|
||||||
|
await AndroidFileService.revokeDirectoryAccess(path);
|
||||||
|
_load();
|
||||||
|
setState(() {});
|
||||||
|
},
|
||||||
|
tooltip: 'Revoke',
|
||||||
|
),
|
||||||
|
))
|
||||||
|
.toList(),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,34 +1,26 @@
|
||||||
import 'package:aves/model/filters/filters.dart';
|
|
||||||
import 'package:aves/model/settings/settings.dart';
|
import 'package:aves/model/settings/settings.dart';
|
||||||
import 'package:aves/model/source/collection_source.dart';
|
import 'package:aves/model/source/collection_source.dart';
|
||||||
|
import 'package:aves/theme/icons.dart';
|
||||||
|
import 'package:aves/widgets/collection/empty.dart';
|
||||||
import 'package:aves/widgets/common/identity/aves_filter_chip.dart';
|
import 'package:aves/widgets/common/identity/aves_filter_chip.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
class HiddenFilters extends StatelessWidget {
|
class HiddenFilterTile extends StatelessWidget {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Selector<Settings, Set<CollectionFilter>>(
|
return ListTile(
|
||||||
selector: (context, s) => s.hiddenFilters,
|
title: Text('Hidden filters'),
|
||||||
builder: (context, hiddenFilters, child) {
|
onTap: () {
|
||||||
return ListTile(
|
Navigator.push(
|
||||||
title: hiddenFilters.isEmpty ? Text('There are no hidden filters') : Text('Hidden filters'),
|
context,
|
||||||
trailing: hiddenFilters.isEmpty
|
MaterialPageRoute(
|
||||||
? null
|
settings: RouteSettings(name: HiddenFilterPage.routeName),
|
||||||
: OutlinedButton(
|
builder: (context) => HiddenFilterPage(),
|
||||||
onPressed: () {
|
),
|
||||||
Navigator.push(
|
);
|
||||||
context,
|
},
|
||||||
MaterialPageRoute(
|
);
|
||||||
settings: RouteSettings(name: HiddenFilterPage.routeName),
|
|
||||||
builder: (context) => HiddenFilterPage(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
child: Text('Edit'.toUpperCase()),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,24 +34,49 @@ class HiddenFilterPage extends StatelessWidget {
|
||||||
title: Text('Hidden Filters'),
|
title: Text('Hidden Filters'),
|
||||||
),
|
),
|
||||||
body: SafeArea(
|
body: SafeArea(
|
||||||
child: Padding(
|
child: Column(
|
||||||
padding: EdgeInsets.all(8),
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
child: Consumer<Settings>(
|
children: [
|
||||||
builder: (context, settings, child) {
|
Padding(
|
||||||
final filterList = settings.hiddenFilters.toList()..sort();
|
padding: EdgeInsets.symmetric(vertical: 8, horizontal: 16),
|
||||||
return Wrap(
|
child: Row(
|
||||||
spacing: 8,
|
children: [
|
||||||
runSpacing: 8,
|
Icon(AIcons.info),
|
||||||
children: filterList
|
SizedBox(width: 16),
|
||||||
.map((filter) => AvesFilterChip(
|
Expanded(child: Text('Photos and videos matching hidden filters will not appear in your collection.')),
|
||||||
filter: filter,
|
],
|
||||||
removable: true,
|
),
|
||||||
onTap: (filter) => context.read<CollectionSource>().changeFilterVisibility(filter, true),
|
),
|
||||||
))
|
Divider(),
|
||||||
.toList(),
|
Expanded(
|
||||||
);
|
child: Padding(
|
||||||
},
|
padding: EdgeInsets.all(8),
|
||||||
),
|
child: Consumer<Settings>(
|
||||||
|
builder: (context, settings, child) {
|
||||||
|
final hiddenFilters = settings.hiddenFilters;
|
||||||
|
final filterList = hiddenFilters.toList()..sort();
|
||||||
|
if (hiddenFilters.isEmpty) {
|
||||||
|
return EmptyContent(
|
||||||
|
icon: AIcons.hide,
|
||||||
|
text: 'No hidden filters',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return Wrap(
|
||||||
|
spacing: 8,
|
||||||
|
runSpacing: 8,
|
||||||
|
children: filterList
|
||||||
|
.map((filter) => AvesFilterChip(
|
||||||
|
filter: filter,
|
||||||
|
removable: true,
|
||||||
|
onTap: (filter) => context.read<CollectionSource>().changeFilterVisibility(filter, true),
|
||||||
|
))
|
||||||
|
.toList(),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
@ -242,11 +242,8 @@ class _SettingsPageState extends State<SettingsPage> {
|
||||||
onChanged: (v) => settings.isCrashlyticsEnabled = v,
|
onChanged: (v) => settings.isCrashlyticsEnabled = v,
|
||||||
title: Text('Allow anonymous analytics and crash reporting'),
|
title: Text('Allow anonymous analytics and crash reporting'),
|
||||||
),
|
),
|
||||||
HiddenFilters(),
|
HiddenFilterTile(),
|
||||||
Padding(
|
StorageAccessTile(),
|
||||||
padding: EdgeInsets.only(top: 8, bottom: 16),
|
|
||||||
child: GrantedDirectories(),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue