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;
|
||||
|
||||
const EmptyContent({
|
||||
@required this.icon,
|
||||
this.icon,
|
||||
@required this.text,
|
||||
this.alignment = const FractionalOffset(.5, .35),
|
||||
});
|
||||
|
@ -19,12 +19,14 @@ class EmptyContent extends StatelessWidget {
|
|||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(
|
||||
icon,
|
||||
size: 64,
|
||||
color: color,
|
||||
),
|
||||
SizedBox(height: 16),
|
||||
if (icon != null) ...[
|
||||
Icon(
|
||||
icon,
|
||||
size: 64,
|
||||
color: color,
|
||||
),
|
||||
SizedBox(height: 16)
|
||||
],
|
||||
Text(
|
||||
text,
|
||||
style: TextStyle(
|
||||
|
|
|
@ -1,13 +1,34 @@
|
|||
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:intl/intl.dart';
|
||||
|
||||
class GrantedDirectories extends StatefulWidget {
|
||||
class StorageAccessTile extends StatelessWidget {
|
||||
@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;
|
||||
List<String> _lastPaths;
|
||||
|
||||
|
@ -21,44 +42,64 @@ class _GrantedDirectoriesState extends State<GrantedDirectories> {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final textTheme = Theme.of(context).textTheme;
|
||||
return Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 16),
|
||||
child: FutureBuilder<List<String>>(
|
||||
future: _pathLoader,
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.hasError) {
|
||||
return Text(snapshot.error.toString());
|
||||
}
|
||||
if (snapshot.connectionState != ConnectionState.done && _lastPaths == null) {
|
||||
return SizedBox.shrink();
|
||||
}
|
||||
_lastPaths = snapshot.data..sort();
|
||||
final count = _lastPaths.length;
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Aves has access to ${Intl.plural(count, zero: 'no directories.', one: 'one directory:', other: '$count directories:')}',
|
||||
style: textTheme.subtitle1,
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text('Storage Access'),
|
||||
),
|
||||
body: SafeArea(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(vertical: 8, horizontal: 16),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(AIcons.info),
|
||||
SizedBox(width: 16),
|
||||
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.')),
|
||||
],
|
||||
),
|
||||
..._lastPaths.map((path) => Row(
|
||||
children: [
|
||||
Expanded(child: Text(path, style: textTheme.caption)),
|
||||
SizedBox(width: 8),
|
||||
OutlinedButton(
|
||||
onPressed: () async {
|
||||
await AndroidFileService.revokeDirectoryAccess(path);
|
||||
_load();
|
||||
setState(() {});
|
||||
},
|
||||
child: Text('Revoke'.toUpperCase()),
|
||||
),
|
||||
],
|
||||
)),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
Divider(),
|
||||
Expanded(
|
||||
child: FutureBuilder<List<String>>(
|
||||
future: _pathLoader,
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.hasError) {
|
||||
return Text(snapshot.error.toString());
|
||||
}
|
||||
if (snapshot.connectionState != ConnectionState.done && _lastPaths == null) {
|
||||
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/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:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class HiddenFilters extends StatelessWidget {
|
||||
class HiddenFilterTile extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Selector<Settings, Set<CollectionFilter>>(
|
||||
selector: (context, s) => s.hiddenFilters,
|
||||
builder: (context, hiddenFilters, child) {
|
||||
return ListTile(
|
||||
title: hiddenFilters.isEmpty ? Text('There are no hidden filters') : Text('Hidden filters'),
|
||||
trailing: hiddenFilters.isEmpty
|
||||
? null
|
||||
: OutlinedButton(
|
||||
onPressed: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
settings: RouteSettings(name: HiddenFilterPage.routeName),
|
||||
builder: (context) => HiddenFilterPage(),
|
||||
),
|
||||
);
|
||||
},
|
||||
child: Text('Edit'.toUpperCase()),
|
||||
),
|
||||
);
|
||||
});
|
||||
return ListTile(
|
||||
title: Text('Hidden filters'),
|
||||
onTap: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
settings: RouteSettings(name: HiddenFilterPage.routeName),
|
||||
builder: (context) => HiddenFilterPage(),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -42,24 +34,49 @@ class HiddenFilterPage extends StatelessWidget {
|
|||
title: Text('Hidden Filters'),
|
||||
),
|
||||
body: SafeArea(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(8),
|
||||
child: Consumer<Settings>(
|
||||
builder: (context, settings, child) {
|
||||
final filterList = settings.hiddenFilters.toList()..sort();
|
||||
return Wrap(
|
||||
spacing: 8,
|
||||
runSpacing: 8,
|
||||
children: filterList
|
||||
.map((filter) => AvesFilterChip(
|
||||
filter: filter,
|
||||
removable: true,
|
||||
onTap: (filter) => context.read<CollectionSource>().changeFilterVisibility(filter, true),
|
||||
))
|
||||
.toList(),
|
||||
);
|
||||
},
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(vertical: 8, horizontal: 16),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(AIcons.info),
|
||||
SizedBox(width: 16),
|
||||
Expanded(child: Text('Photos and videos matching hidden filters will not appear in your collection.')),
|
||||
],
|
||||
),
|
||||
),
|
||||
Divider(),
|
||||
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,
|
||||
title: Text('Allow anonymous analytics and crash reporting'),
|
||||
),
|
||||
HiddenFilters(),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 8, bottom: 16),
|
||||
child: GrantedDirectories(),
|
||||
),
|
||||
HiddenFilterTile(),
|
||||
StorageAccessTile(),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue