settings: changed layout for hidden filters and access grants

This commit is contained in:
Thibault Deckers 2021-02-17 11:44:59 +09:00
parent cadd2b4d1c
commit f16d98ba2b
4 changed files with 151 additions and 94 deletions

View file

@ -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(

View file

@ -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(),
);
},
),
),
],
),
),
);
}

View file

@ -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(),
);
},
),
),
),
],
),
),
);

View file

@ -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(),
],
);
}