nc-photos/app/lib/widget/people_browser.dart

227 lines
6.3 KiB
Dart
Raw Normal View History

import 'package:collection/collection.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';
import 'package:kiwi/kiwi.dart';
import 'package:logging/logging.dart';
import 'package:nc_photos/account.dart';
import 'package:nc_photos/api/api_util.dart' as api_util;
import 'package:nc_photos/app_localizations.dart';
import 'package:nc_photos/bloc/list_person.dart';
import 'package:nc_photos/di_container.dart';
import 'package:nc_photos/entity/person.dart';
import 'package:nc_photos/exception.dart';
import 'package:nc_photos/exception_util.dart' as exception_util;
import 'package:nc_photos/k.dart' as k;
import 'package:nc_photos/snack_bar_manager.dart';
import 'package:nc_photos/theme.dart';
2022-08-28 16:10:28 +02:00
import 'package:nc_photos/widget/collection_list_item.dart';
import 'package:nc_photos/widget/person_browser.dart';
class PeopleBrowserArguments {
const PeopleBrowserArguments(this.account);
final Account account;
}
/// Show a list of all people associated with this account
class PeopleBrowser extends StatefulWidget {
static const routeName = "/people-browser";
static Route buildRoute(PeopleBrowserArguments args) => MaterialPageRoute(
builder: (context) => PeopleBrowser.fromArgs(args),
);
const PeopleBrowser({
Key? key,
required this.account,
}) : super(key: key);
PeopleBrowser.fromArgs(PeopleBrowserArguments args, {Key? key})
: this(
key: key,
account: args.account,
);
@override
createState() => _PeopleBrowserState();
final Account account;
}
class _PeopleBrowserState extends State<PeopleBrowser> {
@override
initState() {
super.initState();
_initBloc();
}
@override
build(BuildContext context) {
return AppTheme(
child: Scaffold(
body: BlocListener<ListPersonBloc, ListPersonBlocState>(
bloc: _bloc,
listener: (context, state) => _onStateChange(context, state),
child: BlocBuilder<ListPersonBloc, ListPersonBlocState>(
bloc: _bloc,
builder: (context, state) => _buildContent(context, state),
),
),
),
);
}
void _initBloc() {
if (_bloc.state is ListPersonBlocInit) {
_log.info("[_initBloc] Initialize bloc");
} else {
// process the current state
WidgetsBinding.instance.addPostFrameCallback((_) {
setState(() {
_onStateChange(context, _bloc.state);
});
});
}
_reqQuery();
}
Widget _buildContent(BuildContext context, ListPersonBlocState state) {
return Stack(
children: [
Theme(
data: Theme.of(context).copyWith(
colorScheme: Theme.of(context).colorScheme.copyWith(
secondary: AppTheme.getOverscrollIndicatorColor(context),
),
),
child: CustomScrollView(
slivers: [
_buildAppBar(context),
if (state is ListPersonBlocLoading)
const SliverToBoxAdapter(
child: Align(
alignment: Alignment.center,
child: LinearProgressIndicator(),
),
),
2022-08-17 18:27:35 +02:00
SliverStaggeredGrid.extentBuilder(
maxCrossAxisExtent: 160,
2022-08-28 16:10:28 +02:00
mainAxisSpacing: 2,
crossAxisSpacing: 2,
2022-08-17 18:27:35 +02:00
itemCount: _items.length,
itemBuilder: _buildItem,
staggeredTileBuilder: (_) => const StaggeredTile.count(1, 1),
),
],
),
),
],
);
}
Widget _buildAppBar(BuildContext context) {
return SliverAppBar(
title: Text(L10n.global().collectionPeopleLabel),
floating: true,
);
}
Widget _buildItem(BuildContext context, int index) {
final item = _items[index];
return item.buildWidget(context);
}
void _onStateChange(BuildContext context, ListPersonBlocState state) {
if (state is ListPersonBlocInit) {
_items = [];
} else if (state is ListPersonBlocSuccess ||
state is ListPersonBlocLoading) {
_transformItems(state.items);
} else if (state is ListPersonBlocFailure) {
_transformItems(state.items);
try {
final e = state.exception as ApiException;
if (e.response.statusCode == 404) {
// face recognition app probably not installed, ignore
return;
}
} catch (_) {}
SnackBarManager().showSnackBar(SnackBar(
content: Text(exception_util.toUserString(state.exception)),
duration: k.snackBarDurationNormal,
));
}
}
void _onItemTap(Person person) {
Navigator.pushNamed(context, PersonBrowser.routeName,
arguments: PersonBrowserArguments(widget.account, person));
}
void _transformItems(List<Person> items) {
_items = items
.sorted((a, b) {
final countCompare = b.count.compareTo(a.count);
if (countCompare == 0) {
return a.name.compareTo(b.name);
} else {
return countCompare;
}
})
.map((e) => _PersonListItem(
account: widget.account,
name: e.name,
faceUrl: api_util.getFacePreviewUrl(widget.account, e.thumbFaceId,
size: k.faceThumbSize),
onTap: () => _onItemTap(e),
))
.toList();
}
void _reqQuery() {
_bloc.add(ListPersonBlocQuery(widget.account));
}
late final _bloc = ListPersonBloc(KiwiContainer().resolve<DiContainer>());
var _items = <_ListItem>[];
static final _log = Logger("widget.people_browser._PeopleBrowserState");
}
abstract class _ListItem {
_ListItem({
this.onTap,
});
Widget buildWidget(BuildContext context);
final VoidCallback? onTap;
}
class _PersonListItem extends _ListItem {
_PersonListItem({
required this.account,
required this.name,
required this.faceUrl,
VoidCallback? onTap,
}) : super(onTap: onTap);
@override
2022-08-28 16:10:28 +02:00
buildWidget(BuildContext context) => CollectionListSmall(
account: account,
label: name,
coverUrl: faceUrl,
fallbackBuilder: (_) => Icon(
2022-08-17 18:27:35 +02:00
Icons.person,
color: Colors.white.withOpacity(.8),
),
2022-08-28 16:10:28 +02:00
onTap: onTap,
);
final Account account;
final String name;
2022-08-28 16:10:28 +02:00
final String faceUrl;
}