From 104b8d639a0ef362e3729911ed57e26934fc7ae0 Mon Sep 17 00:00:00 2001 From: abrenoch Date: Sat, 14 May 2022 21:54:44 -0400 Subject: [PATCH 01/50] added ContentGrid widget --- lib/views/components/content_grid.dart | 459 +++++++++++++++++++++++++ 1 file changed, 459 insertions(+) create mode 100644 lib/views/components/content_grid.dart diff --git a/lib/views/components/content_grid.dart b/lib/views/components/content_grid.dart new file mode 100644 index 0000000..48b8dd7 --- /dev/null +++ b/lib/views/components/content_grid.dart @@ -0,0 +1,459 @@ +import 'package:auto_size_text/auto_size_text.dart'; +import 'package:file_picker/file_picker.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart'; +import 'package:flutter/widgets.dart'; +import 'package:flutter_speed_dial/flutter_speed_dial.dart'; +import 'package:image_picker/image_picker.dart'; +import 'dart:async'; + +import 'package:piwigo_ng/api/API.dart'; +import 'package:piwigo_ng/api/CategoryAPI.dart'; +import 'package:piwigo_ng/constants/SettingsConstants.dart'; +import 'package:piwigo_ng/services/OrientationService.dart'; +import 'package:piwigo_ng/views/components/list_item.dart'; +import 'package:piwigo_ng/views/components/snackbars.dart'; + +import 'package:piwigo_ng/views/ImageViewPage.dart'; +import 'package:piwigo_ng/views/UploadGalleryViewPage.dart'; +import 'package:piwigo_ng/views/components/dialogs/dialogs.dart'; + + +class ContentGrid extends StatefulWidget { + ContentGrid({Key key, + this.title, this.category, this.isAdmin, this.nbImages, this.isEditMode, + @required this.selectedItems, @required this.setEditMode, @required this.loadMoreImages, + @required this.selectItem, @required this.deselectItem, + }) : super(key: key); + final bool isAdmin; + final String title; + final String category; + final int nbImages; + final Function(int) loadMoreImages; + final Function(bool, {dynamic image}) setEditMode; + final Function(dynamic) selectItem; + final Function(dynamic) deselectItem; + final bool isEditMode; + final Map selectedItems; + + @override + ContentGridState createState() => ContentGridState(); +} +class ContentGridState extends State with SingleTickerProviderStateMixin { + Future> _albumsFuture; + Future> _imagesFuture; + + int _page; + int _nbImages; + List imageList = []; + + @override + void initState() { + _getData(); + super.initState(); + _page = 0; + _nbImages = widget.nbImages ?? 0; + } + + void _getData() { + if (widget.category.isNotEmpty) { + _albumsFuture = fetchAlbums(widget.category); + } else { + _albumsFuture = Future>.value({ + 'stat': 'success', + 'result': {'categories': []} + }); + } + _imagesFuture = widget.loadMoreImages(0); + } + + @override + void dispose() { + super.dispose(); + } + + bool _isSelected(int id) { + return widget.selectedItems.keys.contains(id); + } + // int _selectedPhotos() { + // return widget.selectedItems.length; + // } + + showMore() async { + _page++; + var response = await widget.loadMoreImages(_page); + if(response['stat'] == 'fail') { + ScaffoldMessenger.of(context).showSnackBar( + errorSnackBar(context, response['result']) + ); + } else { + var newListPage = response['result']['images']; + imageList.addAll(newListPage); + } + setState(() { + print('Fetch images of page $_page'); + // _getData(); + }); + } + + reloadData() async { + imageList.clear(); + setState(() { + _page = 0; + _getData(); + }); + } + + openEditMode(dynamic image) { + widget.setEditMode(true, image: image); + } + + closeEditMode() { + widget.setEditMode(false); + } + + Future _onRefresh() { + setState(() { + _page = 0; + _getData(); + }); + return Future.delayed(Duration(milliseconds: 500)); + } + + handleAlbumSnapshot(AsyncSnapshot albumSnapshot, int nbImages) { + var albums = albumSnapshot.data['result']['categories']; + int nbImages = _nbImages; + if(albums.length > 0 && albums[0]["id"].toString() == widget.category) { + nbImages = albums[0]["total_nb_images"]; + _nbImages = nbImages; + } + albums.removeWhere((category) => + (category["id"].toString() == widget.category) + ); + return albums; + } + handleImagesSnapshot(AsyncSnapshot imagesSnapshot) { + imageList.clear(); + imageList.addAll(imagesSnapshot.data['result']['images']); + } + + @override + Widget build(BuildContext context) { + return createFutureBuilders(); + } + + Widget createFutureBuilders() { + return FutureBuilder>( + future: _albumsFuture, // Albums of the list + builder: (BuildContext context, AsyncSnapshot albumSnapshot) { + if (albumSnapshot.hasData) { + int nbImages = _nbImages; + if(albumSnapshot.data['stat'] == 'fail') { + return Center( + child: Text(appStrings(context).categoryImageList_noDataError), + ); + } + var albums = handleAlbumSnapshot(albumSnapshot, nbImages); + return FutureBuilder>( + future: _imagesFuture, + builder: (BuildContext context, AsyncSnapshot imagesSnapshot) { + if (imagesSnapshot.hasData) { + if (imageList.isEmpty || _page == 0) { + if(imagesSnapshot.data['stat'] == 'fail') { + return Center(child: Text(appStrings(context).categoryImageList_noDataError)); + } + handleImagesSnapshot(imagesSnapshot); + } + return createPageContent(albums, nbImages); + } else { + return Center( + child: CircularProgressIndicator(), + ); + } + }, + ); + } else { + return Center( + child: CircularProgressIndicator(), + ); + } + } + ); + } + + Widget createUploadActionButton() { + ThemeData _theme = Theme.of(context); + return SpeedDial( + spaceBetweenChildren: 10, + childMargin: EdgeInsets.only(bottom: 17, right: 10), + animatedIcon: AnimatedIcons.menu_close, + animatedIconTheme: IconThemeData(size: 22.0), + closeManually: false, + curve: Curves.bounceIn, + backgroundColor: _theme.floatingActionButtonTheme.backgroundColor, + foregroundColor: _theme.floatingActionButtonTheme.foregroundColor, + overlayColor: Colors.black, + elevation: 5.0, + overlayOpacity: 0.5, + shape: CircleBorder(), + children: [ + SpeedDialChild( + elevation: 5, + labelWidget: Text(appStrings(context).createNewAlbum_title, style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold, color: Colors.white)), + child: Icon(Icons.create_new_folder), + backgroundColor: _theme.floatingActionButtonTheme.backgroundColor, + foregroundColor: _theme.floatingActionButtonTheme.foregroundColor, + onTap: () async { + showDialog( + context: context, + builder: (BuildContext context) { + return CreateCategoryDialog(catId: widget.category); + } + ).whenComplete(() { + setState(() { + _getData(); + }); + }); + }, + ), + SpeedDialChild( + elevation: 5, + labelWidget: Text(appStrings(context).categoryUpload_images, style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold, color: Colors.white)), + child: Icon(Icons.add_to_photos), + backgroundColor: _theme.floatingActionButtonTheme.backgroundColor, + foregroundColor: _theme.floatingActionButtonTheme.foregroundColor, + onTap: () async { + try { + ScaffoldMessenger.of(context).removeCurrentSnackBar(); + ScaffoldMessenger.of(context).showSnackBar(SnackBar( + content: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + mainAxisSize: MainAxisSize.min, + children: [ + Text(appStrings(context).loadingHUD_label), + CircularProgressIndicator(), + ], + ), + duration: Duration(days: 365), + )); + final List images = ((await FilePicker.platform.pickFiles( + type: FileType.media, + allowMultiple: true, + )) ?.files ?? []).map((e) => XFile(e.path, name: e.name, bytes: e.bytes)).toList(); + ScaffoldMessenger.of(context).removeCurrentSnackBar(); + if(images.isNotEmpty) { + Navigator.push(context, MaterialPageRoute( + builder: (context) => UploadGalleryViewPage(imageData: images, category: widget.category) + )).whenComplete(() { + setState(() { + print('After upload'); // refresh + }); + }); + } + } catch (e) { + print('${e.toString()}'); + } + } + ), + SpeedDialChild( + elevation: 5, + labelWidget: Text(appStrings(context).categoryUpload_take, style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold, color: Colors.white)), + child: Icon(Icons.photo_camera_rounded), + backgroundColor: _theme.floatingActionButtonTheme.backgroundColor, + foregroundColor: _theme.floatingActionButtonTheme.foregroundColor, + onTap: () async { + try { + ScaffoldMessenger.of(context).removeCurrentSnackBar(); + ScaffoldMessenger.of(context).showSnackBar(SnackBar( + content: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + mainAxisSize: MainAxisSize.min, + children: [ + Text(appStrings(context).loadingHUD_label), + CircularProgressIndicator(), + ], + ), + duration: Duration(days: 365), + )); + final XFile image = await ImagePicker().pickImage(source: ImageSource.camera); + ScaffoldMessenger.of(context).removeCurrentSnackBar(); + if(image != null) { + Navigator.push(context, MaterialPageRoute( + builder: (context) => UploadGalleryViewPage(imageData: [image], category: widget.category) + )).whenComplete(() { + setState(() { + print('After upload'); // refresh + }); + }); + } + } catch (e) { + print('Dio error ${e.toString()}'); + } + } + ), + ], + ); + } + + Widget createPageContent(dynamic albums, int nbImages) { + ThemeData _theme = Theme.of(context); + + int albumCrossAxisCount = MediaQuery.of(context).size.width <= Constants.albumMinWidth ? 1 + : (MediaQuery.of(context).size.width/Constants.albumMinWidth).floor(); + + return RefreshIndicator( + displacement: 20, + notificationPredicate: (notification) { + return notification.metrics.atEdge; + }, + onRefresh: _onRefresh, + child: SingleChildScrollView( + child: Column( + children: [ + albums.length > 0 ? + GridView.builder( + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: albumCrossAxisCount, + mainAxisSpacing: 10, + crossAxisSpacing: 10, + childAspectRatio: albumGridAspectRatio(context), + ), + padding: EdgeInsets.all(10), + itemCount: albums.length, + shrinkWrap: true, + physics: NeverScrollableScrollPhysics(), + itemBuilder: (BuildContext context, int index) { + var album = albums[index]; + return AlbumListItem(album, + isAdmin: widget.isAdmin, + onClose: () { + setState(() { + _getData(); + }); + }, + onOpen: closeEditMode, + ); + }, + ) : Center(), + imageList.length > 0 ? + GridView.builder( + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: getImageCrossAxisCount(context), + mainAxisSpacing: 3.0, + crossAxisSpacing: 3.0, + ), + padding: EdgeInsets.symmetric(horizontal: 5), + itemCount: imageList.length, + shrinkWrap: true, + physics: NeverScrollableScrollPhysics(), + itemBuilder: (BuildContext context, int index) { + var image = imageList[index]; + return InkWell( + onLongPress: widget.isEditMode ? () { + setState(() { + _isSelected(image['id']) ? + widget.deselectItem(image) : + widget.selectItem(image); + }); + } : widget.isAdmin ? () { + openEditMode(image); + } : () {}, + onTap: () { + widget.isEditMode ? + _isSelected(image['id']) ? + widget.deselectItem(image) : + widget.selectItem(image) : + Navigator.of(context).push( + MaterialPageRoute(builder: (context) => ImageViewPage( + images: imageList, + index: index, + isAdmin: widget.isAdmin, + category: widget.category, + )), + ).whenComplete(() { + setState(() { + _getData(); + }); + }); + }, + child: AnimatedContainer( + duration: Duration(milliseconds: 200), + decoration: BoxDecoration( + color: Colors.white, + border: _isSelected(image['id']) ? + Border.all(width: 5, color: _theme.colorScheme.primary) : + Border.all(width: 0, color: Colors.white), + ), + child: Stack( + alignment: Alignment.center, + children: [ + Container( + width: double.infinity, + height: double.infinity, + child: Image.network(imageList[index]["derivatives"][API.prefs.getString('thumbnail_size')]["url"], + fit: BoxFit.cover, + ), + ), + _isSelected(image['id']) ? Container( + width: double.infinity, + height: double.infinity, + color: Color(0x80000000), + ) : Center(), + /* + widget.isEditMode? Align( + alignment: Alignment.topRight, + child: Padding( + padding: EdgeInsets.all(5), + child: _isSelected(image['id']) ? + Icon(Icons.check_circle, color: _theme.floatingActionButtonTheme.backgroundColor) : + Icon(Icons.check_circle_outline, color: _theme.disabledColor), + ), + ) : Center(), + + */ + API.prefs.getBool('show_thumbnail_title')? Align( + alignment: Alignment.bottomCenter, + child: Container( + width: double.infinity, + color: Color(0x80ffffff), + child: AutoSizeText('${image['name']}', + overflow: TextOverflow.ellipsis, + maxLines: 1, + style: TextStyle(fontSize: 12), + maxFontSize: 14, minFontSize: 7, + textAlign: TextAlign.center, + ), + ), + ) : Center(), + ], + ), + ), + ); + }, + ) : Center(), + nbImages > (_page+1)*100 ? GestureDetector( + onTap: () { + showMore(); + }, + child: Padding( + padding: EdgeInsets.all(10), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text(appStrings(context).showMore(nbImages-((_page+1)*100)), style: TextStyle(fontSize: 14, color: _theme.disabledColor)), + ], + ), + ), + ) : Center(), + Center( + child: Container( + padding: EdgeInsets.all(10), + child: Text(appStrings(context).imageCount(nbImages), style: TextStyle(fontSize: 20, color: _theme.textTheme.bodyText2.color, fontWeight: FontWeight.w300)), + ), + ) + ], + ), + ), + ); + } +} \ No newline at end of file From b059984f0dfb2d96d86424c9abe376c51b22bea3 Mon Sep 17 00:00:00 2001 From: abrenoch Date: Sat, 14 May 2022 22:00:54 -0400 Subject: [PATCH 02/50] updated CategoryViewPage to use ContentGrid --- lib/views/CategoryViewPage.dart | 328 +++++--------------------------- 1 file changed, 46 insertions(+), 282 deletions(-) diff --git a/lib/views/CategoryViewPage.dart b/lib/views/CategoryViewPage.dart index 6ec23aa..8318863 100644 --- a/lib/views/CategoryViewPage.dart +++ b/lib/views/CategoryViewPage.dart @@ -1,22 +1,16 @@ -import 'package:auto_size_text/auto_size_text.dart'; import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_speed_dial/flutter_speed_dial.dart'; import 'package:image_picker/image_picker.dart'; -import 'dart:async'; -import 'package:piwigo_ng/api/API.dart'; -import 'package:piwigo_ng/api/CategoryAPI.dart'; import 'package:piwigo_ng/api/ImageAPI.dart'; import 'package:piwigo_ng/constants/SettingsConstants.dart'; -import 'package:piwigo_ng/services/OrientationService.dart'; import 'package:piwigo_ng/services/UploadStatusProvider.dart'; -import 'package:piwigo_ng/views/components/list_item.dart'; +import 'package:piwigo_ng/views/components/content_grid.dart'; import 'package:piwigo_ng/views/components/snackbars.dart'; -import 'package:piwigo_ng/views/ImageViewPage.dart'; import 'package:piwigo_ng/views/UploadGalleryViewPage.dart'; import 'package:piwigo_ng/views/components/dialogs/dialogs.dart'; import 'package:provider/provider.dart'; @@ -33,59 +27,32 @@ class CategoryViewPage extends StatefulWidget { _CategoryViewPageState createState() => _CategoryViewPageState(); } class _CategoryViewPageState extends State with SingleTickerProviderStateMixin { - Future> _albumsFuture; - Future> _imagesFuture; + GlobalKey _key = GlobalKey(); bool _isEditMode; - int _page; - int _nbImages; Map _selectedItems = Map(); ScrollController _controller = ScrollController(); - List imageList = []; - @override void initState() { - _getData(); super.initState(); - _page = 0; - _nbImages = widget.nbImages; _isEditMode = false; } void _getData() { - _albumsFuture = fetchAlbums(widget.category); - _imagesFuture = fetchImages(widget.category, 0); + final ContentGridState contentGridState = _key.currentState; + contentGridState.reloadData(); } @override void dispose() { super.dispose(); } - - bool _isSelected(int id) { - return _selectedItems.keys.contains(id); - } + int _selectedPhotos() { return _selectedItems.length; } - showMore() async { - _page++; - var response = await fetchImages(widget.category, _page); - if(response['stat'] == 'fail') { - ScaffoldMessenger.of(context).showSnackBar( - errorSnackBar(context, response['result']) - ); - } else { - var newListPage = response['result']['images']; - imageList.addAll(newListPage); - } - setState(() { - print('Fetch images of page $_page'); - _getData(); - }); - } openEditMode() { setState(() { _isEditMode = true; @@ -98,14 +65,6 @@ class _CategoryViewPageState extends State with SingleTickerPr _selectedItems.clear(); } - Future _onRefresh() { - setState(() { - _page = 0; - _getData(); - }); - return Future.delayed(Duration(milliseconds: 500)); - } - void _onEditSelection() async { Navigator.of(context).push( MaterialPageRoute(builder: (_) => EditImagesPage( @@ -174,8 +133,8 @@ class _CategoryViewPageState extends State with SingleTickerPr setState(() { _selectedItems.clear(); _isEditMode = false; - _getData(); }); + _getData(); }); break; case 1: showDialog(context: context, @@ -204,8 +163,8 @@ class _CategoryViewPageState extends State with SingleTickerPr setState(() { _selectedItems.clear(); _isEditMode = false; - _getData(); }); + _getData(); }); break; default: break; @@ -238,42 +197,23 @@ class _CategoryViewPageState extends State with SingleTickerPr content: Text(appStrings(context).deleteImageSuccess_message(nbSuccess)), )); - setState(() { - _getData(); - }); + _getData(); } } void _onSelectAll() { + final ContentGridState contentGridState = _key.currentState; setState(() { - if(_selectedItems.length == imageList.length) { + if(_selectedItems.length == contentGridState.imageList.length) { _selectedItems.clear(); } else { - imageList.forEach((image) { + contentGridState.imageList.forEach((image) { _selectedItems.putIfAbsent(image['id'], () => image); }); } }); } - - handleAlbumSnapshot(AsyncSnapshot albumSnapshot, int nbImages) { - var albums = albumSnapshot.data['result']['categories']; - int nbImages = _nbImages; - if(albums.length > 0 && albums[0]["id"].toString() == widget.category) { - nbImages = albums[0]["total_nb_images"]; - _nbImages = nbImages; - } - albums.removeWhere((category) => - (category["id"].toString() == widget.category) - ); - return albums; - } - handleImagesSnapshot(AsyncSnapshot imagesSnapshot) { - imageList.clear(); - imageList.addAll(imagesSnapshot.data['result']['images']); - } - @override Widget build(BuildContext context) { return Scaffold( @@ -285,7 +225,38 @@ class _CategoryViewPageState extends State with SingleTickerPr headerSliverBuilder: (context, innerBoxScrolled) => [ createAppBar(), ], - body: createFutureBuilders(), + body: ContentGrid( + key: _key, + category: widget.category, + isAdmin: widget.isAdmin, + nbImages: widget.nbImages, + isEditMode: _isEditMode || false, + selectedItems: _selectedItems, + loadMoreImages: (int page) { + print('Loading page $page of category ${widget.category}'); + return fetchImages(widget.category, page); + }, + setEditMode: (bool isEditMode, {image}) => { + setState(() { + _isEditMode = isEditMode; + if (!_isEditMode) { + _selectedItems.clear(); + } else if (image != null) { + _selectedItems.putIfAbsent(image['id'], () => image); + } + }) + }, + deselectItem: (dynamic image) { + setState(() { + _selectedItems.remove(image['id']); + }); + }, + selectItem: (dynamic image) { + setState(() { + _selectedItems.putIfAbsent(image['id'], () => image); + }); + } + ), ), ), floatingActionButton: _isEditMode ? @@ -297,6 +268,7 @@ class _CategoryViewPageState extends State with SingleTickerPr Widget createAppBar() { ThemeData _theme = Theme.of(context); + final ContentGridState contentGridState = _key.currentState; return SliverAppBar( pinned: true, snap: false, @@ -317,7 +289,7 @@ class _CategoryViewPageState extends State with SingleTickerPr actions: [ _isEditMode ? IconButton( onPressed: _onSelectAll, - icon: _selectedItems.length == imageList.length ? + icon: _selectedItems.length == contentGridState.imageList.length ? Icon(Icons.check_circle) : Icon(Icons.circle_outlined), ) : SizedBox(), _isEditMode ? IconButton( @@ -344,45 +316,6 @@ class _CategoryViewPageState extends State with SingleTickerPr ); } - Widget createFutureBuilders() { - return FutureBuilder>( - future: _albumsFuture, // Albums of the list - builder: (BuildContext context, AsyncSnapshot albumSnapshot) { - if (albumSnapshot.hasData) { - int nbImages = _nbImages; - if(albumSnapshot.data['stat'] == 'fail') { - return Center( - child: Text(appStrings(context).categoryImageList_noDataError), - ); - } - var albums = handleAlbumSnapshot(albumSnapshot, nbImages); - return FutureBuilder>( - future: _imagesFuture, - builder: (BuildContext context, AsyncSnapshot imagesSnapshot) { - if (imagesSnapshot.hasData) { - if (imageList.isEmpty || _page == 0) { - if(imagesSnapshot.data['stat'] == 'fail') { - return Center(child: Text(appStrings(context).categoryImageList_noDataError)); - } - handleImagesSnapshot(imagesSnapshot); - } - return createPageContent(albums, nbImages); - } else { - return Center( - child: CircularProgressIndicator(), - ); - } - }, - ); - } else { - return Center( - child: CircularProgressIndicator(), - ); - } - } - ); - } - Widget createUploadActionButton() { ThemeData _theme = Theme.of(context); return SpeedDial( @@ -412,9 +345,7 @@ class _CategoryViewPageState extends State with SingleTickerPr return CreateCategoryDialog(catId: widget.category); } ).whenComplete(() { - setState(() { - _getData(); - }); + _getData(); }); }, ), @@ -497,173 +428,6 @@ class _CategoryViewPageState extends State with SingleTickerPr ); } - Widget createPageContent(dynamic albums, int nbImages) { - ThemeData _theme = Theme.of(context); - - int albumCrossAxisCount = MediaQuery.of(context).size.width <= Constants.albumMinWidth ? 1 - : (MediaQuery.of(context).size.width/Constants.albumMinWidth).floor(); - - return RefreshIndicator( - displacement: 20, - notificationPredicate: (notification) { - return notification.metrics.atEdge; - }, - onRefresh: _onRefresh, - child: SingleChildScrollView( - child: Column( - children: [ - albums.length > 0 ? - GridView.builder( - gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: albumCrossAxisCount, - mainAxisSpacing: 10, - crossAxisSpacing: 10, - childAspectRatio: albumGridAspectRatio(context), - ), - padding: EdgeInsets.all(10), - itemCount: albums.length, - shrinkWrap: true, - physics: NeverScrollableScrollPhysics(), - itemBuilder: (BuildContext context, int index) { - var album = albums[index]; - return AlbumListItem(album, - isAdmin: widget.isAdmin, - onClose: () { - setState(() { - _getData(); - }); - }, - onOpen: closeEditMode, - ); - }, - ) : Center(), - imageList.length > 0 ? - GridView.builder( - gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: getImageCrossAxisCount(context), - mainAxisSpacing: 3.0, - crossAxisSpacing: 3.0, - ), - padding: EdgeInsets.symmetric(horizontal: 5), - itemCount: imageList.length, - shrinkWrap: true, - physics: NeverScrollableScrollPhysics(), - itemBuilder: (BuildContext context, int index) { - var image = imageList[index]; - return InkWell( - onLongPress: _isEditMode ? () { - setState(() { - _isSelected(image['id']) ? - _selectedItems.remove(image['id']) : - _selectedItems.putIfAbsent(image['id'], () => image); - }); - } : widget.isAdmin ? () { - setState(() { - _isEditMode = true; - _selectedItems.putIfAbsent(image['id'], () => image); - }); - } : () {}, - onTap: () { - _isEditMode ? - setState(() { - _isSelected(image['id']) ? - _selectedItems.remove(image['id']) : - _selectedItems.putIfAbsent(image['id'], () => image); - }) : - Navigator.of(context).push( - MaterialPageRoute(builder: (context) => ImageViewPage( - images: imageList, - index: index, - isAdmin: widget.isAdmin, - category: widget.category, - )), - ).whenComplete(() { - setState(() { - _getData(); - }); - }); - }, - child: AnimatedContainer( - duration: Duration(milliseconds: 200), - decoration: BoxDecoration( - color: Colors.white, - border: _isSelected(image['id']) ? - Border.all(width: 5, color: _theme.colorScheme.primary) : - Border.all(width: 0, color: Colors.white), - ), - child: Stack( - alignment: Alignment.center, - children: [ - Container( - width: double.infinity, - height: double.infinity, - child: Image.network(imageList[index]["derivatives"][API.prefs.getString('thumbnail_size')]["url"], - fit: BoxFit.cover, - ), - ), - _isSelected(image['id']) ? Container( - width: double.infinity, - height: double.infinity, - color: Color(0x80000000), - ) : Center(), - /* - _isEditMode? Align( - alignment: Alignment.topRight, - child: Padding( - padding: EdgeInsets.all(5), - child: _isSelected(image['id']) ? - Icon(Icons.check_circle, color: _theme.floatingActionButtonTheme.backgroundColor) : - Icon(Icons.check_circle_outline, color: _theme.disabledColor), - ), - ) : Center(), - - */ - API.prefs.getBool('show_thumbnail_title')? Align( - alignment: Alignment.bottomCenter, - child: Container( - width: double.infinity, - color: Color(0x80ffffff), - child: AutoSizeText('${image['name']}', - overflow: TextOverflow.ellipsis, - maxLines: 1, - style: TextStyle(fontSize: 12), - maxFontSize: 14, minFontSize: 7, - textAlign: TextAlign.center, - ), - ), - ) : Center(), - ], - ), - ), - ); - }, - ) : Center(), - nbImages > (_page+1)*100 ? GestureDetector( - onTap: () { - showMore(); - }, - child: Padding( - padding: EdgeInsets.all(10), - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text(appStrings(context).showMore(nbImages-((_page+1)*100)), style: TextStyle(fontSize: 14, color: _theme.disabledColor)), - ], - ), - ), - ) : Center(), - Center( - child: Container( - padding: EdgeInsets.all(10), - child: Text(appStrings(context).imageCount(nbImages), style: TextStyle(fontSize: 20, color: _theme.textTheme.bodyText2.color, fontWeight: FontWeight.w300)), - ), - ) - ], - ), - ), - ); - } - Widget createBottomBar() { ThemeData _theme = Theme.of(context); return BottomNavigationBar( From f0164ac3811a04dd20c1e06c7605cee8ad719e90 Mon Sep 17 00:00:00 2001 From: abrenoch Date: Mon, 16 May 2022 00:46:29 -0400 Subject: [PATCH 03/50] added getTags and editTag api methods --- lib/api/TagAPI.dart | 54 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/lib/api/TagAPI.dart b/lib/api/TagAPI.dart index 1a03276..f441f02 100644 --- a/lib/api/TagAPI.dart +++ b/lib/api/TagAPI.dart @@ -29,6 +29,32 @@ Future> getAdminTags() async { } } +Future> getTags() async { + Map queries = { + "format":"json", + "method": "pwg.tags.getList", + }; + + Response response = await API().dio.get('ws.php', queryParameters: queries); + + try { + if (response.statusCode == 200) { + return json.decode(response.data); + } else { + return { + 'stat': 'fail', + 'result': response.statusMessage + }; + } + } catch(e) { + var error = e as DioError; + return { + 'stat': 'fail', + 'result': error.message + }; + } +} + Future createTag(String tagName) async { Map queries = { "format":"json", @@ -54,4 +80,32 @@ Future createTag(String tagName) async { 'result': error.message }; } +} + +Future editTag(int tagId, String tagName) async { + Map queries = { + "format": "json", + "method": "pwg.tags.rename", + }; + FormData formData = FormData.fromMap({ + "tag_id": tagId, + "new_name": tagName, + "pwg_token": API.prefs.getString("pwg_token"), + }); + try { + Response response = await API().dio.post( + 'ws.php', + data: formData, + queryParameters: queries, + ); + if (response.statusCode == 200) { + return json.decode(response.data); + } + } catch (e) { + var error = e as DioError; + return { + 'stat': 'fail', + 'result': error.message + }; + } } \ No newline at end of file From 6971b3feb9b3168ac9e669f5181f637810bbd773 Mon Sep 17 00:00:00 2001 From: abrenoch Date: Mon, 16 May 2022 00:47:13 -0400 Subject: [PATCH 04/50] added fetchTagImages and fetchFavoriteImages api methods --- lib/api/ImageAPI.dart | 65 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 61 insertions(+), 4 deletions(-) diff --git a/lib/api/ImageAPI.dart b/lib/api/ImageAPI.dart index 2f665f4..11f15fd 100644 --- a/lib/api/ImageAPI.dart +++ b/lib/api/ImageAPI.dart @@ -41,6 +41,63 @@ Future> fetchImages(String albumID, int page) async { } } +Future> fetchTagImages(String tagID, int page) async { + Map queries = { + "format": "json", + "method": "pwg.tags.getImages", + "tag_id": tagID, + "per_page": "100", + "page": page.toString(), + }; + + try { + Response response = await API().dio.get('ws.php', queryParameters: queries); + + if (response.statusCode == 200) { + return json.decode(response.data); + } else { + return { + 'stat': 'fail', + 'result': response.statusMessage + }; + } + } catch(e) { + var error = e as DioError; + return { + 'stat': 'fail', + 'result': error.message + }; + } +} + +Future> fetchFavoriteImages(int page) async { + Map queries = { + "format": "json", + "method": "pwg.users.favorites.getList", + "per_page": "100", + "page": page.toString(), + }; + + try { + Response response = await API().dio.get('ws.php', queryParameters: queries); + + if (response.statusCode == 200) { + return json.decode(response.data); + } else { + return { + 'stat': 'fail', + 'result': response.statusMessage + }; + } + } catch(e) { + var error = e as DioError; + return { + 'stat': 'fail', + 'result': error.message + }; + } +} + Future getImageInfo(int imageId) async { Map queries = { "format": "json", @@ -238,7 +295,7 @@ Future removeImage(int imageId, String catId) async { if(imageInfo['stat'] == 'fail') return imageInfo; List categories = imageInfo['result']['categories'].map( - (cat) => cat['id'].toString() + (cat) => cat['id'].toString() ).toList(); categories.removeWhere((cat) => cat == catId); @@ -309,9 +366,9 @@ Future moveImage(int imageId, List categories) async { try { Response response = await API().dio.post( - 'ws.php', - data: formData, - queryParameters: queries + 'ws.php', + data: formData, + queryParameters: queries ); if (response.statusCode == 200) { From f8a1d2cb292f573d3f241f5da090483fc166bbd0 Mon Sep 17 00:00:00 2001 From: abrenoch Date: Mon, 16 May 2022 00:48:18 -0400 Subject: [PATCH 05/50] added view_menu_button.dart --- lib/views/components/view_menu_button.dart | 74 ++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 lib/views/components/view_menu_button.dart diff --git a/lib/views/components/view_menu_button.dart b/lib/views/components/view_menu_button.dart new file mode 100644 index 0000000..89ca9c0 --- /dev/null +++ b/lib/views/components/view_menu_button.dart @@ -0,0 +1,74 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:piwigo_ng/constants/SettingsConstants.dart'; + +enum ViewPopupMenuOptions { favorites, tags, top_viewed, top_rated } + +class ViewPopupMenuButton extends StatelessWidget { + const ViewPopupMenuButton({Key key, this.isAdmin = false}) : super(key: key); + + final bool isAdmin; + + @override + Widget build(BuildContext context) { + return PopupMenuButton( + onSelected: (value) { + _onMenuItemSelected(value as int); + }, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(10.0)) + ), + // padding: EdgeInsets.zero, + // offset: Offset(0, 50), + itemBuilder: (ctx) => [ + isAdmin ? _buildPopupMenuItem( + appStrings(context).categoryDiscoverFavorites_title, ViewPopupMenuOptions.favorites.index, + iconData: Icons.favorite_border + ) : null, + _buildPopupMenuItem( + appStrings(context).tags, ViewPopupMenuOptions.tags.index, + iconData: Icons.local_offer_outlined + ), + _buildPopupMenuItem( + appStrings(context).categoryDiscoverVisits_title, ViewPopupMenuOptions.top_viewed.index, + ), + _buildPopupMenuItem( + appStrings(context).categoryDiscoverBest_title, ViewPopupMenuOptions.top_rated.index, + ), + _buildPopupMenuItem( + appStrings(context).categoryDiscoverRecent_title, ViewPopupMenuOptions.top_rated.index, + iconData: Icons.access_time_rounded + ), + ], + ); + } + + PopupMenuItem _buildPopupMenuItem( + String title, int position, {IconData iconData}) { + return PopupMenuItem( + value: position, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text(title), + Spacer(), + if (iconData != null) Icon(iconData, color: Colors.black), + ], + ), + ); + } + + _onMenuItemSelected(int value) { + // switch (value) { + // case ViewPopupMenuOptions.favorites.index: + // break; + // case ViewPopupMenuOptions.tags.index: + // break; + // case ViewPopupMenuOptions.top_viewed.index: + // break; + // case ViewPopupMenuOptions.top_rated.index: + // break; + // } + } +} + From 4b92976c8350801d26c4431fab9c4362d0b43100 Mon Sep 17 00:00:00 2001 From: abrenoch Date: Mon, 16 May 2022 00:49:10 -0400 Subject: [PATCH 06/50] ViewPopupMenuButton usage --- lib/views/RootCategoryViewPage.dart | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/views/RootCategoryViewPage.dart b/lib/views/RootCategoryViewPage.dart index d2d0620..27c4a69 100644 --- a/lib/views/RootCategoryViewPage.dart +++ b/lib/views/RootCategoryViewPage.dart @@ -12,6 +12,7 @@ import 'package:piwigo_ng/views/SettingsViewPage.dart'; import 'package:piwigo_ng/views/components/appbars.dart'; import 'package:piwigo_ng/views/components/dialogs/dialogs.dart'; +import 'package:piwigo_ng/views/components/view_menu_button.dart'; class RootCategoryViewPage extends StatefulWidget { final bool isAdmin; @@ -56,6 +57,9 @@ class _RootCategoryViewPageState extends State with Single }, icon: Icon(Icons.settings, color: _theme.iconTheme.color), ), + actions: [ + ViewPopupMenuButton(isAdmin: widget.isAdmin), + ], title: appStrings(context).tabBar_albums, ), ], From 8b34749ef5da7def2f4ee804920da4798d09bc0e Mon Sep 17 00:00:00 2001 From: abrenoch Date: Wed, 18 May 2022 20:32:38 -0400 Subject: [PATCH 07/50] content_grid.dart tweaks --- lib/views/components/content_grid.dart | 29 ++++++++++++++------------ 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/lib/views/components/content_grid.dart b/lib/views/components/content_grid.dart index 48b8dd7..4fb68df 100644 --- a/lib/views/components/content_grid.dart +++ b/lib/views/components/content_grid.dart @@ -56,7 +56,7 @@ class ContentGridState extends State with SingleTickerProviderState } void _getData() { - if (widget.category.isNotEmpty) { + if (widget.category != null && widget.category.isNotEmpty) { _albumsFuture = fetchAlbums(widget.category); } else { _albumsFuture = Future>.value({ @@ -120,21 +120,25 @@ class ContentGridState extends State with SingleTickerProviderState return Future.delayed(Duration(milliseconds: 500)); } - handleAlbumSnapshot(AsyncSnapshot albumSnapshot, int nbImages) { + handleAlbumSnapshot(AsyncSnapshot albumSnapshot) { var albums = albumSnapshot.data['result']['categories']; - int nbImages = _nbImages; + // int nbImages = _nbImages; if(albums.length > 0 && albums[0]["id"].toString() == widget.category) { - nbImages = albums[0]["total_nb_images"]; - _nbImages = nbImages; + // nbImages = albums[0]["total_nb_images"]; + // _nbImages = nbImages; + _nbImages = albums[0]["total_nb_images"]; } albums.removeWhere((category) => - (category["id"].toString() == widget.category) + (category["id"].toString() == widget.category) ); return albums; } handleImagesSnapshot(AsyncSnapshot imagesSnapshot) { imageList.clear(); imageList.addAll(imagesSnapshot.data['result']['images']); + if (_nbImages == 0 && imagesSnapshot.data['result'].containsKey('paging')) { + _nbImages = imagesSnapshot.data['result']['paging']['count']; + } } @override @@ -147,13 +151,12 @@ class ContentGridState extends State with SingleTickerProviderState future: _albumsFuture, // Albums of the list builder: (BuildContext context, AsyncSnapshot albumSnapshot) { if (albumSnapshot.hasData) { - int nbImages = _nbImages; if(albumSnapshot.data['stat'] == 'fail') { return Center( child: Text(appStrings(context).categoryImageList_noDataError), ); } - var albums = handleAlbumSnapshot(albumSnapshot, nbImages); + var albums = handleAlbumSnapshot(albumSnapshot); return FutureBuilder>( future: _imagesFuture, builder: (BuildContext context, AsyncSnapshot imagesSnapshot) { @@ -164,7 +167,7 @@ class ContentGridState extends State with SingleTickerProviderState } handleImagesSnapshot(imagesSnapshot); } - return createPageContent(albums, nbImages); + return createPageContent(albums); } else { return Center( child: CircularProgressIndicator(), @@ -295,7 +298,7 @@ class ContentGridState extends State with SingleTickerProviderState ); } - Widget createPageContent(dynamic albums, int nbImages) { + Widget createPageContent(dynamic albums) { ThemeData _theme = Theme.of(context); int albumCrossAxisCount = MediaQuery.of(context).size.width <= Constants.albumMinWidth ? 1 @@ -431,7 +434,7 @@ class ContentGridState extends State with SingleTickerProviderState ); }, ) : Center(), - nbImages > (_page+1)*100 ? GestureDetector( + _nbImages > (_page+1)*100 ? GestureDetector( onTap: () { showMore(); }, @@ -440,7 +443,7 @@ class ContentGridState extends State with SingleTickerProviderState child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ - Text(appStrings(context).showMore(nbImages-((_page+1)*100)), style: TextStyle(fontSize: 14, color: _theme.disabledColor)), + Text(appStrings(context).showMore(_nbImages-((_page+1)*100)), style: TextStyle(fontSize: 14, color: _theme.disabledColor)), ], ), ), @@ -448,7 +451,7 @@ class ContentGridState extends State with SingleTickerProviderState Center( child: Container( padding: EdgeInsets.all(10), - child: Text(appStrings(context).imageCount(nbImages), style: TextStyle(fontSize: 20, color: _theme.textTheme.bodyText2.color, fontWeight: FontWeight.w300)), + child: Text(appStrings(context).imageCount(_nbImages), style: TextStyle(fontSize: 20, color: _theme.textTheme.bodyText2.color, fontWeight: FontWeight.w300)), ), ) ], From d8d53559ce78f4c9dfa2a4cdfaa42aaaa211a2a0 Mon Sep 17 00:00:00 2001 From: abrenoch Date: Wed, 18 May 2022 20:33:20 -0400 Subject: [PATCH 08/50] view_menu_button.dart fix / route --- lib/views/components/view_menu_button.dart | 25 +++++++++++----------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/lib/views/components/view_menu_button.dart b/lib/views/components/view_menu_button.dart index 89ca9c0..351fb75 100644 --- a/lib/views/components/view_menu_button.dart +++ b/lib/views/components/view_menu_button.dart @@ -1,6 +1,7 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:piwigo_ng/constants/SettingsConstants.dart'; +import 'package:piwigo_ng/views/FavoritesViewPage.dart'; enum ViewPopupMenuOptions { favorites, tags, top_viewed, top_rated } @@ -13,7 +14,7 @@ class ViewPopupMenuButton extends StatelessWidget { Widget build(BuildContext context) { return PopupMenuButton( onSelected: (value) { - _onMenuItemSelected(value as int); + _onMenuItemSelected(value as int, context); }, shape: RoundedRectangleBorder( borderRadius: BorderRadius.all(Radius.circular(10.0)) @@ -58,17 +59,17 @@ class ViewPopupMenuButton extends StatelessWidget { ); } - _onMenuItemSelected(int value) { - // switch (value) { - // case ViewPopupMenuOptions.favorites.index: - // break; - // case ViewPopupMenuOptions.tags.index: - // break; - // case ViewPopupMenuOptions.top_viewed.index: - // break; - // case ViewPopupMenuOptions.top_rated.index: - // break; - // } + _onMenuItemSelected(int value, BuildContext context) { + MaterialPageRoute route; + + if (value == ViewPopupMenuOptions.favorites.index) { + route = MaterialPageRoute(builder: (context) => FavoritesViewPage( + isAdmin: isAdmin, + )); + } else if (value == ViewPopupMenuOptions.top_viewed.index) { + // _changeColorAccordingToMenuItem = Colors.green; + } + if (route != null) Navigator.of(context).push(route); } } From 01f33f8b9d1365aa4792dfac6513ca32c0691e55 Mon Sep 17 00:00:00 2001 From: abrenoch Date: Wed, 18 May 2022 20:33:32 -0400 Subject: [PATCH 09/50] added favorites page --- lib/views/FavoritesViewPage.dart | 518 +++++++++++++++++++++++++++++++ 1 file changed, 518 insertions(+) create mode 100644 lib/views/FavoritesViewPage.dart diff --git a/lib/views/FavoritesViewPage.dart b/lib/views/FavoritesViewPage.dart new file mode 100644 index 0000000..aaaf63f --- /dev/null +++ b/lib/views/FavoritesViewPage.dart @@ -0,0 +1,518 @@ +import 'package:file_picker/file_picker.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart'; +import 'package:flutter/widgets.dart'; +import 'package:flutter_speed_dial/flutter_speed_dial.dart'; +import 'package:image_picker/image_picker.dart'; + +import 'package:piwigo_ng/api/ImageAPI.dart'; +import 'package:piwigo_ng/constants/SettingsConstants.dart'; +import 'package:piwigo_ng/services/UploadStatusProvider.dart'; +import 'package:piwigo_ng/views/components/content_grid.dart'; +import 'package:piwigo_ng/views/components/snackbars.dart'; + +import 'package:piwigo_ng/views/UploadGalleryViewPage.dart'; +import 'package:piwigo_ng/views/components/dialogs/dialogs.dart'; +import 'package:provider/provider.dart'; + + +class FavoritesViewPage extends StatefulWidget { + FavoritesViewPage({Key key, this.isAdmin, this.nbImages}) : super(key: key); + final bool isAdmin; + final int nbImages; + + @override + _FavoritesViewPageState createState() => _FavoritesViewPageState(); +} +class _FavoritesViewPageState extends State with SingleTickerProviderStateMixin { + GlobalKey _key = GlobalKey(); + + bool _isEditMode; + Map _selectedItems = Map(); + ScrollController _controller = ScrollController(); + + @override + void initState() { + super.initState(); + _isEditMode = false; + } + + void _getData() { + final ContentGridState contentGridState = _key.currentState; + contentGridState.reloadData(); + } + + @override + void dispose() { + super.dispose(); + } + + int _selectedPhotos() { + return _selectedItems.length; + } + + openEditMode() { + setState(() { + _isEditMode = true; + }); + } + closeEditMode() { + setState(() { + _isEditMode = false; + }); + _selectedItems.clear(); + } + + void _onEditSelection() async { + // Navigator.of(context).push( + // MaterialPageRoute(builder: (_) => EditImagesPage( + // catId: int.parse(widget.category), + // images: _selectedItems.values.toList(), + // )) + // ); + } + void _onDownloadSelection() async { + if (await confirmDownloadDialog(context, + content: appStrings(context) + .downloadImage_title(_selectedItems.length), + )) { + print('Download ${_selectedItems.keys.toList()}'); + + List selection = []; + selection.addAll(_selectedItems.values.toList()); + + setState(() { + _isEditMode = false; + _selectedItems.clear(); + }); + ScaffoldMessenger.of(context).hideCurrentSnackBar(); + ScaffoldMessenger.of(context).showSnackBar(SnackBar( + content: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text(appStrings(context).downloadingImages(selection.length)), + CircularProgressIndicator(), + ], + ), + )); + + await downloadImages(selection); + } + } + void _onMoveCopySelection() async { + int choice = await chooseMoveCopyImage(context, + content: appStrings(context).moveOrCopyImage_title(_selectedItems.length) + ); + + switch(choice) { + case 0: showDialog(context: context, + builder: (context) { + return MoveOrCopyDialog( + title: appStrings(context).moveImage_title, + subtitle: appStrings(context).moveImage_selectAlbum(_selectedItems.length, ''), + catName: appStrings(context).categoryDiscoverFavorites_title, + isImage: true, + onSelected: (item) async { + if( await confirmMoveDialog(context, + content: appStrings(context).moveImage_message(_selectedItems.length, "", item.name), + )) { + int nbMoved = await moveImages(context, + _selectedItems.values.toList(), + int.parse(item.id) + ); + ScaffoldMessenger.of(context).showSnackBar(imagesMovedSnackBar(context, nbMoved)); + Navigator.of(context).pop(); + } + }, + ); + } + ).whenComplete(() { + setState(() { + _selectedItems.clear(); + _isEditMode = false; + }); + _getData(); + }); + break; + case 1: showDialog(context: context, + builder: (context) { + return MoveOrCopyDialog( + title: appStrings(context).copyImage_title, + subtitle: appStrings(context).copyImage_selectAlbum(_selectedItems.length, ''), + // catName: widget.title, + isImage: true, + onSelected: (item) async { + if( await confirmAssignDialog(context, + content: appStrings(context).copyImage_message(_selectedItems.length, "", item.name), + )) { + int nbCopied = await assignImages(context, + _selectedItems.values.toList(), + int.parse(item.id) + ); + ScaffoldMessenger.of(context).showSnackBar(imagesAssignedSnackBar(context, nbCopied)); + Navigator.of(context).pop(); + } + }, + ); + } + ).whenComplete(() { + setState(() { + _selectedItems.clear(); + _isEditMode = false; + }); + _getData(); + }); + break; + default: break; + } + } + void _onDeleteSelection() async { + int choice = await confirmRemoveImagesFromAlbumDialog(context, + content: appStrings(context).deleteImage_message(_selectedItems.length), + count: _selectedItems.length, + ); + if(choice != -1) { + List selection = []; + selection.addAll(_selectedItems.keys.toList()); + + setState(() { + _isEditMode = false; + _selectedItems.clear(); + }); + + int nbSuccess = 0; + switch(choice) { + case 0: nbSuccess = await deleteImages(context, selection); + break; + case 1: nbSuccess = await removeImages(context, selection, '0'); //TODO: update this + break; + default: break; + } + + ScaffoldMessenger.of(context).showSnackBar(SnackBar( + content: Text(appStrings(context).deleteImageSuccess_message(nbSuccess)), + )); + + _getData(); + } + } + + void _onSelectAll() { + final ContentGridState contentGridState = _key.currentState; + setState(() { + if(_selectedItems.length == contentGridState.imageList.length) { + _selectedItems.clear(); + } else { + contentGridState.imageList.forEach((image) { + _selectedItems.putIfAbsent(image['id'], () => image); + }); + } + }); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + resizeToAvoidBottomInset: true, + extendBody: true, + body: createListeners( + NestedScrollView( + controller: _controller, + headerSliverBuilder: (context, innerBoxScrolled) => [ + createAppBar(), + ], + body: ContentGrid( + key: _key, + // category: widget.category, + isAdmin: widget.isAdmin, + nbImages: widget.nbImages, + isEditMode: _isEditMode || false, + selectedItems: _selectedItems, + loadMoreImages: (int page) { + print('Loading page $page of favorite images'); + return fetchFavoriteImages(page); + }, + setEditMode: (bool isEditMode, {image}) => { + setState(() { + _isEditMode = isEditMode; + if (!_isEditMode) { + _selectedItems.clear(); + } else if (image != null) { + _selectedItems.putIfAbsent(image['id'], () => image); + } + }) + }, + deselectItem: (dynamic image) { + setState(() { + _selectedItems.remove(image['id']); + }); + }, + selectItem: (dynamic image) { + setState(() { + _selectedItems.putIfAbsent(image['id'], () => image); + }); + } + ), + ), + ), + // floatingActionButton: _isEditMode ? + // Center() : createFloatingActionButton(), + bottomNavigationBar: _isEditMode ? + createBottomBar() : Container(height: 0), + ); + } + + Widget createAppBar() { + ThemeData _theme = Theme.of(context); + final ContentGridState contentGridState = _key.currentState; + return SliverAppBar( + pinned: true, + snap: false, + floating: false, + centerTitle: true, + iconTheme: IconThemeData( + color: _theme.iconTheme.color, + ), + leading: IconButton( + onPressed: () { + Navigator.of(context).pop(); + }, + icon: Icon(Icons.chevron_left), + ), + title: _isEditMode ? + Text("${_selectedPhotos()}", overflow: TextOverflow.fade, softWrap: true) : + Text(appStrings(context).categoryDiscoverFavorites_title), + actions: [ + _isEditMode ? IconButton( + onPressed: _onSelectAll, + icon: _selectedItems.length == contentGridState.imageList.length ? + Icon(Icons.check_circle) : Icon(Icons.circle_outlined), + ) : SizedBox(), + _isEditMode ? IconButton( + onPressed: closeEditMode, + icon: Icon(Icons.cancel), + ) : widget.isAdmin? IconButton( + onPressed: openEditMode, + icon: Icon(Icons.touch_app_rounded), + ) : SizedBox(), + ], + ); + } + + Widget createListeners(Widget child) { + return WillPopScope( + onWillPop: () async { + if(_isEditMode) { + closeEditMode(); + return false; + } + return true; + }, + child: child, + ); + } + + // Widget createUploadActionButton() { + // ThemeData _theme = Theme.of(context); + // return SpeedDial( + // spaceBetweenChildren: 10, + // childMargin: EdgeInsets.only(bottom: 17, right: 10), + // animatedIcon: AnimatedIcons.menu_close, + // animatedIconTheme: IconThemeData(size: 22.0), + // closeManually: false, + // curve: Curves.bounceIn, + // backgroundColor: _theme.floatingActionButtonTheme.backgroundColor, + // foregroundColor: _theme.floatingActionButtonTheme.foregroundColor, + // overlayColor: Colors.black, + // elevation: 5.0, + // overlayOpacity: 0.5, + // shape: CircleBorder(), + // children: [ + // SpeedDialChild( + // elevation: 5, + // labelWidget: Text(appStrings(context).createNewAlbum_title, style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold, color: Colors.white)), + // child: Icon(Icons.create_new_folder), + // backgroundColor: _theme.floatingActionButtonTheme.backgroundColor, + // foregroundColor: _theme.floatingActionButtonTheme.foregroundColor, + // onTap: () async { + // showDialog( + // context: context, + // builder: (BuildContext context) { + // return CreateCategoryDialog(catId: widget.category); + // } + // ).whenComplete(() { + // _getData(); + // }); + // }, + // ), + // SpeedDialChild( + // elevation: 5, + // labelWidget: Text(appStrings(context).categoryUpload_images, style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold, color: Colors.white)), + // child: Icon(Icons.add_to_photos), + // backgroundColor: _theme.floatingActionButtonTheme.backgroundColor, + // foregroundColor: _theme.floatingActionButtonTheme.foregroundColor, + // onTap: () async { + // try { + // ScaffoldMessenger.of(context).removeCurrentSnackBar(); + // ScaffoldMessenger.of(context).showSnackBar(SnackBar( + // content: Row( + // mainAxisAlignment: MainAxisAlignment.spaceBetween, + // mainAxisSize: MainAxisSize.min, + // children: [ + // Text(appStrings(context).loadingHUD_label), + // CircularProgressIndicator(), + // ], + // ), + // duration: Duration(days: 365), + // )); + // final List images = ((await FilePicker.platform.pickFiles( + // type: FileType.media, + // allowMultiple: true, + // )) ?.files ?? []).map((e) => XFile(e.path, name: e.name, bytes: e.bytes)).toList(); + // ScaffoldMessenger.of(context).removeCurrentSnackBar(); + // if(images.isNotEmpty) { + // Navigator.push(context, MaterialPageRoute( + // builder: (context) => UploadGalleryViewPage(imageData: images, category: widget.category) + // )).whenComplete(() { + // setState(() { + // print('After upload'); // refresh + // }); + // }); + // } + // } catch (e) { + // print('${e.toString()}'); + // } + // } + // ), + // SpeedDialChild( + // elevation: 5, + // labelWidget: Text(appStrings(context).categoryUpload_take, style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold, color: Colors.white)), + // child: Icon(Icons.photo_camera_rounded), + // backgroundColor: _theme.floatingActionButtonTheme.backgroundColor, + // foregroundColor: _theme.floatingActionButtonTheme.foregroundColor, + // onTap: () async { + // try { + // ScaffoldMessenger.of(context).removeCurrentSnackBar(); + // ScaffoldMessenger.of(context).showSnackBar(SnackBar( + // content: Row( + // mainAxisAlignment: MainAxisAlignment.spaceBetween, + // mainAxisSize: MainAxisSize.min, + // children: [ + // Text(appStrings(context).loadingHUD_label), + // CircularProgressIndicator(), + // ], + // ), + // duration: Duration(days: 365), + // )); + // final XFile image = await ImagePicker().pickImage(source: ImageSource.camera); + // ScaffoldMessenger.of(context).removeCurrentSnackBar(); + // if(image != null) { + // Navigator.push(context, MaterialPageRoute( + // builder: (context) => UploadGalleryViewPage(imageData: [image], category: widget.category) + // )).whenComplete(() { + // setState(() { + // print('After upload'); // refresh + // }); + // }); + // } + // } catch (e) { + // print('Dio error ${e.toString()}'); + // } + // } + // ), + // ], + // ); + // } + + Widget createBottomBar() { + ThemeData _theme = Theme.of(context); + return BottomNavigationBar( + onTap: (index) async { + if(_selectedItems.length > 0) { + switch (index) { + case 0: + _onEditSelection(); + break; + case 1: + _onDownloadSelection(); + break; + case 2: + _onMoveCopySelection(); + break; + case 3: + _onDeleteSelection(); + break; + default: + break; + } + } + }, + items: [ + BottomNavigationBarItem( + icon: Icon(Icons.edit, color: _theme.iconTheme.color), + label: appStrings(context).imageOptions_edit, + ), + BottomNavigationBarItem( + icon: Icon(Icons.download_rounded, color: _theme.iconTheme.color), + label: appStrings(context).imageOptions_download, + ), + BottomNavigationBarItem( + icon: Icon(Icons.reply_outlined, color: _theme.iconTheme.color), + label: appStrings(context).moveImage_title, + ), + BottomNavigationBarItem( + icon: Icon(Icons.delete_outline, color: _theme.errorColor), + label: appStrings(context).deleteImage_delete, + ), + ], + backgroundColor: _theme.scaffoldBackgroundColor, + type: BottomNavigationBarType.fixed, + selectedFontSize: 14, + unselectedFontSize: 14, + showSelectedLabels: false, + showUnselectedLabels: false, + currentIndex: 0, + ); + } + + // Widget createFloatingActionButton() { + // final uploadStatusProvider = Provider.of(context); + // return Padding( + // padding: const EdgeInsets.all(8.0), + // child: Stack( + // children: [ + // Align( + // alignment: Alignment.bottomRight, + // child: Container( + // margin: EdgeInsets.only(bottom: 0, right: widget.isAdmin? 70 : 0), + // child: FloatingActionButton( + // backgroundColor: Color(0xff868686), + // onPressed: () { + // Navigator.of(context).popUntil((route) => route.isFirst); + // }, + // child: uploadStatusProvider.status ? + // Stack( + // alignment: Alignment.center, + // children: [ + // SizedBox( + // height: 55, + // width: 55, + // child: CircularProgressIndicator( + // strokeWidth: 5, + // value: uploadStatusProvider.progress, + // ), + // ), + // Text("${uploadStatusProvider.getRemaining()}", + // style: TextStyle(fontSize: 16), + // ), + // ], + // ) : + // Icon(Icons.home, color: Colors.grey.shade200, size: 30), + // ), + // ), + // ), + // ], + // ), + // ); + // } +} \ No newline at end of file From 25f7cfb61ac0e938a9e9eee16594c342515291b7 Mon Sep 17 00:00:00 2001 From: abrenoch Date: Fri, 20 May 2022 18:07:53 -0400 Subject: [PATCH 10/50] added RootTagViewPage.dart and TagViewPage.dart --- lib/views/TagViewPage.dart | 533 ++++++++++++++++++++++ lib/views/components/RootTagViewPage.dart | 149 ++++++ 2 files changed, 682 insertions(+) create mode 100644 lib/views/TagViewPage.dart create mode 100644 lib/views/components/RootTagViewPage.dart diff --git a/lib/views/TagViewPage.dart b/lib/views/TagViewPage.dart new file mode 100644 index 0000000..a3d5fc5 --- /dev/null +++ b/lib/views/TagViewPage.dart @@ -0,0 +1,533 @@ +import 'package:file_picker/file_picker.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart'; +import 'package:flutter/widgets.dart'; +import 'package:flutter_speed_dial/flutter_speed_dial.dart'; +import 'package:image_picker/image_picker.dart'; + +import 'package:piwigo_ng/api/ImageAPI.dart'; +import 'package:piwigo_ng/constants/SettingsConstants.dart'; +import 'package:piwigo_ng/services/UploadStatusProvider.dart'; +import 'package:piwigo_ng/views/ImageViewPage.dart'; +import 'package:piwigo_ng/views/components/content_grid.dart'; +import 'package:piwigo_ng/views/components/snackbars.dart'; + +import 'package:piwigo_ng/views/UploadGalleryViewPage.dart'; +import 'package:piwigo_ng/views/components/dialogs/dialogs.dart'; +import 'package:provider/provider.dart'; + + +class TagViewPage extends StatefulWidget { + TagViewPage({Key key, this.title, this.tag, this.isAdmin, this.nbImages}) : super(key: key); + final bool isAdmin; + final String title; + final String tag; + final int nbImages; + + @override + _TagViewPageState createState() => _TagViewPageState(); +} +class _TagViewPageState extends State with SingleTickerProviderStateMixin { + GlobalKey _key = GlobalKey(); + + bool _isEditMode; + Map _selectedItems = Map(); + ScrollController _controller = ScrollController(); + + @override + void initState() { + super.initState(); + _isEditMode = false; + } + + void _getData() { + final ContentGridState contentGridState = _key.currentState; + contentGridState.reloadData(); + } + + @override + void dispose() { + super.dispose(); + } + + int _selectedPhotos() { + return _selectedItems.length; + } + + openEditMode() { + setState(() { + _isEditMode = true; + }); + } + closeEditMode() { + setState(() { + _isEditMode = false; + }); + _selectedItems.clear(); + } + + void _onEditSelection() async { + // Navigator.of(context).push( + // MaterialPageRoute(builder: (_) => EditImagesPage( + // catId: int.parse(widget.category), + // images: _selectedItems.values.toList(), + // )) + // ); + } + void _onDownloadSelection() async { + if (await confirmDownloadDialog(context, + content: appStrings(context) + .downloadImage_title(_selectedItems.length), + )) { + print('Download ${_selectedItems.keys.toList()}'); + + List selection = []; + selection.addAll(_selectedItems.values.toList()); + + setState(() { + _isEditMode = false; + _selectedItems.clear(); + }); + ScaffoldMessenger.of(context).hideCurrentSnackBar(); + ScaffoldMessenger.of(context).showSnackBar(SnackBar( + content: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text(appStrings(context).downloadingImages(selection.length)), + CircularProgressIndicator(), + ], + ), + )); + + await downloadImages(selection); + } + } + void _onMoveCopySelection() async { + int choice = await chooseMoveCopyImage(context, + content: appStrings(context).moveOrCopyImage_title(_selectedItems.length) + ); + + switch(choice) { + case 0: showDialog(context: context, + builder: (context) { + return MoveOrCopyDialog( + title: appStrings(context).moveImage_title, + subtitle: appStrings(context).moveImage_selectAlbum(_selectedItems.length, ''), + catName: appStrings(context).categoryDiscoverFavorites_title, + isImage: true, + onSelected: (item) async { + if( await confirmMoveDialog(context, + content: appStrings(context).moveImage_message(_selectedItems.length, "", item.name), + )) { + int nbMoved = await moveImages(context, + _selectedItems.values.toList(), + int.parse(item.id) + ); + ScaffoldMessenger.of(context).showSnackBar(imagesMovedSnackBar(context, nbMoved)); + Navigator.of(context).pop(); + } + }, + ); + } + ).whenComplete(() { + setState(() { + _selectedItems.clear(); + _isEditMode = false; + }); + _getData(); + }); + break; + case 1: showDialog(context: context, + builder: (context) { + return MoveOrCopyDialog( + title: appStrings(context).copyImage_title, + subtitle: appStrings(context).copyImage_selectAlbum(_selectedItems.length, ''), + // catName: widget.title, + isImage: true, + onSelected: (item) async { + if( await confirmAssignDialog(context, + content: appStrings(context).copyImage_message(_selectedItems.length, "", item.name), + )) { + int nbCopied = await assignImages(context, + _selectedItems.values.toList(), + int.parse(item.id) + ); + ScaffoldMessenger.of(context).showSnackBar(imagesAssignedSnackBar(context, nbCopied)); + Navigator.of(context).pop(); + } + }, + ); + } + ).whenComplete(() { + setState(() { + _selectedItems.clear(); + _isEditMode = false; + }); + _getData(); + }); + break; + default: break; + } + } + void _onDeleteSelection() async { + int choice = await confirmRemoveImagesFromAlbumDialog(context, + content: appStrings(context).deleteImage_message(_selectedItems.length), + count: _selectedItems.length, + ); + if(choice != -1) { + List selection = []; + selection.addAll(_selectedItems.keys.toList()); + + setState(() { + _isEditMode = false; + _selectedItems.clear(); + }); + + int nbSuccess = 0; + switch(choice) { + case 0: nbSuccess = await deleteImages(context, selection); + break; + case 1: nbSuccess = await removeImages(context, selection, '0'); //TODO: update this + break; + default: break; + } + + ScaffoldMessenger.of(context).showSnackBar(SnackBar( + content: Text(appStrings(context).deleteImageSuccess_message(nbSuccess)), + )); + + _getData(); + } + } + + void _onSelectAll() { + final ContentGridState contentGridState = _key.currentState; + setState(() { + if(_selectedItems.length == contentGridState.imageList.length) { + _selectedItems.clear(); + } else { + contentGridState.imageList.forEach((image) { + _selectedItems.putIfAbsent(image['id'], () => image); + }); + } + }); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + resizeToAvoidBottomInset: true, + extendBody: true, + body: createListeners( + NestedScrollView( + controller: _controller, + headerSliverBuilder: (context, innerBoxScrolled) => [ + createAppBar(), + ], + body: ContentGrid( + key: _key, + // category: widget.category, + isAdmin: widget.isAdmin, + nbImages: widget.nbImages, + isEditMode: _isEditMode || false, + selectedItems: _selectedItems, + loadMoreImages: (int page) { + print('Loading page $page of tag ${widget.tag} images'); + return fetchTagImages(widget.tag, page); + }, + setEditMode: (bool isEditMode, {image}) => { + setState(() { + _isEditMode = isEditMode; + if (!_isEditMode) { + _selectedItems.clear(); + } else if (image != null) { + _selectedItems.putIfAbsent(image['id'], () => image); + } + }) + }, + deselectItem: (dynamic image) { + setState(() { + _selectedItems.remove(image['id']); + }); + }, + selectItem: (dynamic image) { + setState(() { + _selectedItems.putIfAbsent(image['id'], () => image); + }); + }, + onImageTap: (List images, int index) { + Navigator.of(context).push( + MaterialPageRoute(builder: (context) => ImageViewPage( + images: images, + index: index, + isAdmin: widget.isAdmin, + tag: widget.tag, + )), + ).whenComplete(() { + _getData(); + }); + }, + ), + ), + ), + // floatingActionButton: _isEditMode ? + // Center() : createFloatingActionButton(), + bottomNavigationBar: _isEditMode ? + createBottomBar() : Container(height: 0), + ); + } + + Widget createAppBar() { + ThemeData _theme = Theme.of(context); + final ContentGridState contentGridState = _key.currentState; + return SliverAppBar( + pinned: true, + snap: false, + floating: false, + centerTitle: true, + iconTheme: IconThemeData( + color: _theme.iconTheme.color, + ), + leading: IconButton( + onPressed: () { + Navigator.of(context).pop(); + }, + icon: Icon(Icons.chevron_left), + ), + title: _isEditMode ? + Text("${_selectedPhotos()}", overflow: TextOverflow.fade, softWrap: true) : + Text(widget.title), + actions: [ + _isEditMode ? IconButton( + onPressed: _onSelectAll, + icon: _selectedItems.length == contentGridState.imageList.length ? + Icon(Icons.check_circle) : Icon(Icons.circle_outlined), + ) : SizedBox(), + _isEditMode ? IconButton( + onPressed: closeEditMode, + icon: Icon(Icons.cancel), + ) : widget.isAdmin? IconButton( + onPressed: openEditMode, + icon: Icon(Icons.touch_app_rounded), + ) : SizedBox(), + ], + ); + } + + Widget createListeners(Widget child) { + return WillPopScope( + onWillPop: () async { + if(_isEditMode) { + closeEditMode(); + return false; + } + return true; + }, + child: child, + ); + } + + // Widget createUploadActionButton() { + // ThemeData _theme = Theme.of(context); + // return SpeedDial( + // spaceBetweenChildren: 10, + // childMargin: EdgeInsets.only(bottom: 17, right: 10), + // animatedIcon: AnimatedIcons.menu_close, + // animatedIconTheme: IconThemeData(size: 22.0), + // closeManually: false, + // curve: Curves.bounceIn, + // backgroundColor: _theme.floatingActionButtonTheme.backgroundColor, + // foregroundColor: _theme.floatingActionButtonTheme.foregroundColor, + // overlayColor: Colors.black, + // elevation: 5.0, + // overlayOpacity: 0.5, + // shape: CircleBorder(), + // children: [ + // SpeedDialChild( + // elevation: 5, + // labelWidget: Text(appStrings(context).createNewAlbum_title, style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold, color: Colors.white)), + // child: Icon(Icons.create_new_folder), + // backgroundColor: _theme.floatingActionButtonTheme.backgroundColor, + // foregroundColor: _theme.floatingActionButtonTheme.foregroundColor, + // onTap: () async { + // showDialog( + // context: context, + // builder: (BuildContext context) { + // return CreateCategoryDialog(catId: widget.category); + // } + // ).whenComplete(() { + // _getData(); + // }); + // }, + // ), + // SpeedDialChild( + // elevation: 5, + // labelWidget: Text(appStrings(context).categoryUpload_images, style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold, color: Colors.white)), + // child: Icon(Icons.add_to_photos), + // backgroundColor: _theme.floatingActionButtonTheme.backgroundColor, + // foregroundColor: _theme.floatingActionButtonTheme.foregroundColor, + // onTap: () async { + // try { + // ScaffoldMessenger.of(context).removeCurrentSnackBar(); + // ScaffoldMessenger.of(context).showSnackBar(SnackBar( + // content: Row( + // mainAxisAlignment: MainAxisAlignment.spaceBetween, + // mainAxisSize: MainAxisSize.min, + // children: [ + // Text(appStrings(context).loadingHUD_label), + // CircularProgressIndicator(), + // ], + // ), + // duration: Duration(days: 365), + // )); + // final List images = ((await FilePicker.platform.pickFiles( + // type: FileType.media, + // allowMultiple: true, + // )) ?.files ?? []).map((e) => XFile(e.path, name: e.name, bytes: e.bytes)).toList(); + // ScaffoldMessenger.of(context).removeCurrentSnackBar(); + // if(images.isNotEmpty) { + // Navigator.push(context, MaterialPageRoute( + // builder: (context) => UploadGalleryViewPage(imageData: images, category: widget.category) + // )).whenComplete(() { + // setState(() { + // print('After upload'); // refresh + // }); + // }); + // } + // } catch (e) { + // print('${e.toString()}'); + // } + // } + // ), + // SpeedDialChild( + // elevation: 5, + // labelWidget: Text(appStrings(context).categoryUpload_take, style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold, color: Colors.white)), + // child: Icon(Icons.photo_camera_rounded), + // backgroundColor: _theme.floatingActionButtonTheme.backgroundColor, + // foregroundColor: _theme.floatingActionButtonTheme.foregroundColor, + // onTap: () async { + // try { + // ScaffoldMessenger.of(context).removeCurrentSnackBar(); + // ScaffoldMessenger.of(context).showSnackBar(SnackBar( + // content: Row( + // mainAxisAlignment: MainAxisAlignment.spaceBetween, + // mainAxisSize: MainAxisSize.min, + // children: [ + // Text(appStrings(context).loadingHUD_label), + // CircularProgressIndicator(), + // ], + // ), + // duration: Duration(days: 365), + // )); + // final XFile image = await ImagePicker().pickImage(source: ImageSource.camera); + // ScaffoldMessenger.of(context).removeCurrentSnackBar(); + // if(image != null) { + // Navigator.push(context, MaterialPageRoute( + // builder: (context) => UploadGalleryViewPage(imageData: [image], category: widget.category) + // )).whenComplete(() { + // setState(() { + // print('After upload'); // refresh + // }); + // }); + // } + // } catch (e) { + // print('Dio error ${e.toString()}'); + // } + // } + // ), + // ], + // ); + // } + + Widget createBottomBar() { + ThemeData _theme = Theme.of(context); + return BottomNavigationBar( + onTap: (index) async { + if(_selectedItems.length > 0) { + switch (index) { + case 0: + _onEditSelection(); + break; + case 1: + _onDownloadSelection(); + break; + case 2: + _onMoveCopySelection(); + break; + case 3: + _onDeleteSelection(); + break; + default: + break; + } + } + }, + items: [ + BottomNavigationBarItem( + icon: Icon(Icons.edit, color: _theme.iconTheme.color), + label: appStrings(context).imageOptions_edit, + ), + BottomNavigationBarItem( + icon: Icon(Icons.download_rounded, color: _theme.iconTheme.color), + label: appStrings(context).imageOptions_download, + ), + BottomNavigationBarItem( + icon: Icon(Icons.reply_outlined, color: _theme.iconTheme.color), + label: appStrings(context).moveImage_title, + ), + BottomNavigationBarItem( + icon: Icon(Icons.delete_outline, color: _theme.errorColor), + label: appStrings(context).deleteImage_delete, + ), + ], + backgroundColor: _theme.scaffoldBackgroundColor, + type: BottomNavigationBarType.fixed, + selectedFontSize: 14, + unselectedFontSize: 14, + showSelectedLabels: false, + showUnselectedLabels: false, + currentIndex: 0, + ); + } + +// Widget createFloatingActionButton() { +// final uploadStatusProvider = Provider.of(context); +// return Padding( +// padding: const EdgeInsets.all(8.0), +// child: Stack( +// children: [ +// Align( +// alignment: Alignment.bottomRight, +// child: Container( +// margin: EdgeInsets.only(bottom: 0, right: widget.isAdmin? 70 : 0), +// child: FloatingActionButton( +// backgroundColor: Color(0xff868686), +// onPressed: () { +// Navigator.of(context).popUntil((route) => route.isFirst); +// }, +// child: uploadStatusProvider.status ? +// Stack( +// alignment: Alignment.center, +// children: [ +// SizedBox( +// height: 55, +// width: 55, +// child: CircularProgressIndicator( +// strokeWidth: 5, +// value: uploadStatusProvider.progress, +// ), +// ), +// Text("${uploadStatusProvider.getRemaining()}", +// style: TextStyle(fontSize: 16), +// ), +// ], +// ) : +// Icon(Icons.home, color: Colors.grey.shade200, size: 30), +// ), +// ), +// ), +// ], +// ), +// ); +// } +} \ No newline at end of file diff --git a/lib/views/components/RootTagViewPage.dart b/lib/views/components/RootTagViewPage.dart new file mode 100644 index 0000000..d67e364 --- /dev/null +++ b/lib/views/components/RootTagViewPage.dart @@ -0,0 +1,149 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; + +import 'package:piwigo_ng/api/API.dart'; +import 'package:piwigo_ng/api/TagAPI.dart'; +import 'package:piwigo_ng/constants/SettingsConstants.dart'; +import 'package:piwigo_ng/services/OrientationService.dart'; +import 'package:piwigo_ng/services/upload/Uploader.dart'; +import 'package:piwigo_ng/views/SettingsViewPage.dart'; + +import 'package:piwigo_ng/views/components/appbars.dart'; +import 'package:piwigo_ng/views/components/dialogs/dialogs.dart'; +import 'package:piwigo_ng/views/components/tag_list_item.dart'; + +class RootTagViewPage extends StatefulWidget { + final bool isAdmin; + + const RootTagViewPage({Key key, this.isAdmin = false}) : super(key: key); + @override + _RootTagViewPageState createState() => _RootTagViewPageState(); +} +class _RootTagViewPageState extends State with SingleTickerProviderStateMixin { + TextEditingController _searchController = TextEditingController(); + ScrollController _scrollController = ScrollController(); + + @override + void initState() { + super.initState(); + WidgetsBinding.instance.addPostFrameCallback((_) { + API.uploader = Uploader(context); + }); + } + @override + void dispose() { + super.dispose(); + } + + @override + Widget build(BuildContext context) { + ThemeData _theme = Theme.of(context); + return Scaffold( + resizeToAvoidBottomInset: true, + body: NestedScrollView( + controller: _scrollController, + headerSliverBuilder: (context, innerBoxScrolled) => [ + AppBarExpandable( + scrollController: _scrollController, + actions: [IconButton( + onPressed: () { + Navigator.of(context).push( + MaterialPageRoute(builder: (context) => SettingsPage()), + ); + }, + icon: Icon(Icons.settings, color: _theme.iconTheme.color), + )], + title: appStrings(context).tags, + ), + ], + body: GestureDetector( + onTap: () { + FocusScope.of(context).unfocus(); + }, + child: FutureBuilder>( + future: getTags(), // Albums of the list + builder: (BuildContext context, AsyncSnapshot albumSnapshot) { + if(albumSnapshot.hasData){ + if(albumSnapshot.data['stat'] == 'fail') { + return Container( + padding: EdgeInsets.all(10), + child: Text(albumSnapshot.data['result']), + ); //appStrings(context).categoryMainEmtpy + } + var tags = albumSnapshot.data['result']['tags']; + // int nbPhotos = 0; + // albums.forEach((cat) => nbPhotos+=cat["total_nb_images"]); + // albums.removeWhere((category) => ( + // category["id"].toString() == _rootCategory + // )); + return RefreshIndicator( + displacement: 20, + notificationPredicate: (notification) { + return notification.metrics.atEdge; + }, + onRefresh: () { + setState(() { + print("refresh"); + }); + return Future.delayed(Duration(milliseconds: 1000)); + }, + child: SingleChildScrollView( + child: Column( + children: [ + _tagGrid(tags), + ], + ), + ), + ); + } else { + return Center( + child: CircularProgressIndicator(), + ); + } + } + ), + ), + ), + floatingActionButton: widget.isAdmin? FloatingActionButton( + onPressed: () { + showDialog( + context: context, + builder: (BuildContext context) { + return CreateCategoryDialog(catId: "0"); + } + ).whenComplete(() { + setState(() { + print('refresh'); + }); + }); + }, + child: Icon(Icons.create_new_folder, color: _theme.primaryColorLight, size: 30), + ) : Container(), + ); + } + + Widget _tagGrid(dynamic tags) { + int albumCrossAxisCount = MediaQuery.of(context).size.width <= Constants.albumMinWidth ? 1 + : (MediaQuery.of(context).size.width/Constants.albumMinWidth).floor(); + + return GridView.builder( + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: albumCrossAxisCount, + mainAxisSpacing: 10, + crossAxisSpacing: 10, + childAspectRatio: 5, + ), + padding: EdgeInsets.all(10), + itemCount: tags.length, + shrinkWrap: true, + physics: NeverScrollableScrollPhysics(), + itemBuilder: (BuildContext context, int index) { + var tag = tags[index]; + return TagListItem(tag, isAdmin: widget.isAdmin, onClose: () { + setState(() {}); + }); + }, + ); + } +} \ No newline at end of file From da8bc6e33266c15ee4cd835cf691b8d7301c47c7 Mon Sep 17 00:00:00 2001 From: abrenoch Date: Fri, 20 May 2022 18:08:54 -0400 Subject: [PATCH 11/50] added onImageTap to content_grid --- lib/views/components/content_grid.dart | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/lib/views/components/content_grid.dart b/lib/views/components/content_grid.dart index 4fb68df..a1c6ab0 100644 --- a/lib/views/components/content_grid.dart +++ b/lib/views/components/content_grid.dart @@ -23,7 +23,7 @@ class ContentGrid extends StatefulWidget { ContentGrid({Key key, this.title, this.category, this.isAdmin, this.nbImages, this.isEditMode, @required this.selectedItems, @required this.setEditMode, @required this.loadMoreImages, - @required this.selectItem, @required this.deselectItem, + @required this.selectItem, @required this.deselectItem, @required this.onImageTap, }) : super(key: key); final bool isAdmin; final String title; @@ -33,6 +33,7 @@ class ContentGrid extends StatefulWidget { final Function(bool, {dynamic image}) setEditMode; final Function(dynamic) selectItem; final Function(dynamic) deselectItem; + final Function(List, int) onImageTap; final bool isEditMode; final Map selectedItems; @@ -366,18 +367,7 @@ class ContentGridState extends State with SingleTickerProviderState _isSelected(image['id']) ? widget.deselectItem(image) : widget.selectItem(image) : - Navigator.of(context).push( - MaterialPageRoute(builder: (context) => ImageViewPage( - images: imageList, - index: index, - isAdmin: widget.isAdmin, - category: widget.category, - )), - ).whenComplete(() { - setState(() { - _getData(); - }); - }); + widget.onImageTap(imageList, index); }, child: AnimatedContainer( duration: Duration(milliseconds: 200), From 19b66720b76f3f291477b5c1c64744d8166b954f Mon Sep 17 00:00:00 2001 From: abrenoch Date: Fri, 20 May 2022 18:09:32 -0400 Subject: [PATCH 12/50] added tag_list_item.dart --- lib/views/components/tag_list_item.dart | 245 ++++++++++++++++++++++++ 1 file changed, 245 insertions(+) create mode 100644 lib/views/components/tag_list_item.dart diff --git a/lib/views/components/tag_list_item.dart b/lib/views/components/tag_list_item.dart new file mode 100644 index 0000000..c8c55d9 --- /dev/null +++ b/lib/views/components/tag_list_item.dart @@ -0,0 +1,245 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_slidable/flutter_slidable.dart'; +import 'package:piwigo_ng/constants/SettingsConstants.dart'; +// import 'package:piwigo_ng/model/PageArguments.dart'; +// import 'package:piwigo_ng/routes/RoutePaths.dart'; +import 'package:piwigo_ng/services/OrientationService.dart'; +import 'package:piwigo_ng/views/CategoryViewPage.dart'; +import 'package:piwigo_ng/api/CategoryAPI.dart'; +import 'package:piwigo_ng/views/TagViewPage.dart'; +// import 'package:piwigo_ng/views/TagViewPage.dart'; + +import 'package:piwigo_ng/views/components/dialogs/dialogs.dart'; +import 'package:piwigo_ng/views/components/dialogs/tags_dialogs.dart'; +import 'package:piwigo_ng/views/components/snackbars.dart'; +import 'package:piwigo_ng/views/components/custom_shapes.dart'; + + +// String albumSubCount(dynamic album, context) { +// String displayString = appStrings(context).imageCount(album["total_nb_images"]); +// if(album["nb_categories"] > 0) { +// displayString += ', ${appStrings(context).subAlbumCount(album["nb_categories"])}'; +// } +// return displayString; +// } + +class TagListItem extends StatefulWidget { + const TagListItem(this.tag, {Key key, + this.isAdmin = false, this.onClose, this.onOpen + }) : super(key: key); + + final dynamic tag; + final bool isAdmin; + final Function() onClose; + final Function() onOpen; + + @override + _TagListItemState createState() => _TagListItemState(); +} +class _TagListItemState extends State { + + void _onRenameTag() async { + // showDialog( + // context: context, + // builder: (BuildContext context) { + // return EditTagDialog( + // tagId: widget.tag['id'], + // tagName: widget.tag['name'] + // ); + // } + // ).whenComplete(() { + // widget.onClose(); + // }); + } + void _onMoveAlbum() async { + // showDialog( + // context: context, + // builder: (BuildContext context) { + // return MoveOrCopyDialog( + // title: appStrings(context).moveCategory, + // subtitle: appStrings(context).moveCategory_select(widget.album['name']), + // catId: widget.tag['id'].toString(), + // catName: widget.tag['name'], + // isImage: false, + // onSelected: (item) async { + // if (await confirmMoveDialog(context, + // content: appStrings(context).moveCategory_message(widget.album['name'], item.name), + // )) { + // var result = await moveCategory(widget.album['id'], item.id); + // if(result['stat'] == 'fail') { + // ScaffoldMessenger.of(context).showSnackBar( + // errorSnackBar(context, result['result']) + // ); + // } else { + // ScaffoldMessenger.of(context).showSnackBar( + // albumMovedSnackBar(context) + // ); + // } + // Navigator.of(context).pop(); + // } + // }, + // ); + // } + // ).whenComplete(() { + // widget.onClose(); + // }); + // widget.onClose(); + } + void _onDeleteTag() async { + // if(widget.album["total_nb_images"] > 0) { + // int choice = await confirmDeleteAlbumWithImagesDialog(context, + // content: appStrings(context).deleteCategory_message(widget.album["total_nb_images"], widget.album["name"]), + // count: widget.album["total_nb_images"], + // ); + // var result; + // switch(choice) { + // case 0: result = await deleteCategory(widget.album['id'].toString(), + // deletionMode: 'no_delete'); + // break; + // case 1: result = await deleteCategory(widget.album['id'].toString(), + // deletionMode: 'delete_orphans'); + // break; + // case 2: result = await deleteCategory(widget.album['id'].toString(), + // deletionMode: 'force_delete'); + // break; + // default: break; + // } + // widget.onClose(); + // } + // else { + // if (await confirmDeleteDialog(context, + // content: appStrings(context).deleteCategory_message(widget.album["total_nb_images"], widget.album["name"]), + // )) { + // var result = await deleteCategory(widget.album['id'].toString()); + // if(result['stat'] == 'fail') { + // ScaffoldMessenger.of(context).showSnackBar( + // errorSnackBar(context, result['result']) + // ); + // } else { + // ScaffoldMessenger.of(context).showSnackBar( + // albumDeletedSnackBar(context) + // ); + // widget.onClose(); + // } + // } + // } + } + + @override + Widget build(BuildContext context) { + return InkWell( + onTap: () { + if(widget.onOpen != null) widget.onOpen(); + // + // Navigator.of(context).pushNamed(RoutePaths.TagContent, + // arguments: PageArguments( + // tag: widget.tag['id'].toString(), + // title: widget.tag['name'], + // isAdmin: widget.isAdmin, + // ) + // ); + + Navigator.of(context).push( + MaterialPageRoute(builder: (context) => TagViewPage( + title: widget.tag["name"], + tag: widget.tag["id"].toString(), + isAdmin: widget.isAdmin, + nbImages: widget.tag["counter"], + )), + ).whenComplete(() { + widget.onClose(); + }); + + + + }, + child: ClipRRect( + borderRadius: BorderRadius.circular(10), + child: TagListCard(widget.tag, isAdmin: widget.isAdmin), + ), + ); + } +} + +class TagListCard extends StatelessWidget { + const TagListCard(this.tag, {Key key, this.isAdmin = false}) : super(key: key); + + final dynamic tag; + final bool isAdmin; + + @override + Widget build(BuildContext context) { + return Row( + children: [ + // albumThumbnail(context, tag), + // albumItemSeparator(context), + tagInfo(context, tag), + ], + ); + } + + Widget tagInfo(BuildContext context, tag) { + + return Expanded( + child: Container( + decoration: BoxDecoration( + border: Border.all(width: 0, color: Theme.of(context).backgroundColor), + color: Theme.of(context).backgroundColor, + ), + padding: EdgeInsets.all(5), + height: 80, + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('${tag["name"]} (${appStrings(context).imageCount(tag["counter"])})', + style: Theme.of(context).textTheme.headline4, + textAlign: TextAlign.left, + overflow: TextOverflow.ellipsis, + softWrap: false, + maxLines: 1 + ), + ] + ), + ), + ); + } +// Widget albumThumbnail(BuildContext context, album) { +// return LayoutBuilder(builder: (context, layout) { +// return Container( +// decoration: BoxDecoration( +// border: Border.all(width: 0, color: Theme.of(context).backgroundColor), +// color: Theme.of(context).backgroundColor, +// borderRadius: BorderRadius.only( +// topLeft: Radius.circular(10), +// bottomLeft: Radius.circular(10), +// ), +// ), +// padding: EdgeInsets.all(5), +// height: layout.maxHeight,// albumGridItemHeight(context), +// width: layout.maxHeight,// albumGridItemHeight(context), +// child: album["tn_url"] == null ? +// Icon(Icons.image_not_supported_outlined, size: 50) +// : +// ClipRRect( +// borderRadius: BorderRadius.circular(7.0), +// child: Image.network( +// album["tn_url"], +// fit: BoxFit.cover, +// ), +// ), +// ); +// }); +// } +// Widget albumItemSeparator(BuildContext context) { +// return Container( +// decoration: ShapeDecoration( +// shape: AlbumCardSeparatorShape(radius: 7), +// color: Theme.of(context).backgroundColor, +// ), +// width: 14, +// height: 80, +// ); +// } +} From 3feee1ab7315e1273730d408c7700fc0ce32d594 Mon Sep 17 00:00:00 2001 From: abrenoch Date: Fri, 20 May 2022 18:10:09 -0400 Subject: [PATCH 13/50] updated CategoryViewPage.dart and FavoritesViewPage.dart to use new property --- lib/views/CategoryViewPage.dart | 15 ++++++++++++++- lib/views/FavoritesViewPage.dart | 15 ++++++++++++++- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/lib/views/CategoryViewPage.dart b/lib/views/CategoryViewPage.dart index 8318863..9deffe4 100644 --- a/lib/views/CategoryViewPage.dart +++ b/lib/views/CategoryViewPage.dart @@ -8,6 +8,7 @@ import 'package:image_picker/image_picker.dart'; import 'package:piwigo_ng/api/ImageAPI.dart'; import 'package:piwigo_ng/constants/SettingsConstants.dart'; import 'package:piwigo_ng/services/UploadStatusProvider.dart'; +import 'package:piwigo_ng/views/ImageViewPage.dart'; import 'package:piwigo_ng/views/components/content_grid.dart'; import 'package:piwigo_ng/views/components/snackbars.dart'; @@ -255,7 +256,19 @@ class _CategoryViewPageState extends State with SingleTickerPr setState(() { _selectedItems.putIfAbsent(image['id'], () => image); }); - } + }, + onImageTap: (List images, int index) { + Navigator.of(context).push( + MaterialPageRoute(builder: (context) => ImageViewPage( + images: images, + index: index, + isAdmin: widget.isAdmin, + category: widget.category, + )), + ).whenComplete(() { + _getData(); + }); + }, ), ), ), diff --git a/lib/views/FavoritesViewPage.dart b/lib/views/FavoritesViewPage.dart index aaaf63f..333770a 100644 --- a/lib/views/FavoritesViewPage.dart +++ b/lib/views/FavoritesViewPage.dart @@ -8,6 +8,7 @@ import 'package:image_picker/image_picker.dart'; import 'package:piwigo_ng/api/ImageAPI.dart'; import 'package:piwigo_ng/constants/SettingsConstants.dart'; import 'package:piwigo_ng/services/UploadStatusProvider.dart'; +import 'package:piwigo_ng/views/ImageViewPage.dart'; import 'package:piwigo_ng/views/components/content_grid.dart'; import 'package:piwigo_ng/views/components/snackbars.dart'; @@ -251,7 +252,19 @@ class _FavoritesViewPageState extends State with SingleTicker setState(() { _selectedItems.putIfAbsent(image['id'], () => image); }); - } + }, + onImageTap: (List images, int index) { + Navigator.of(context).push( + MaterialPageRoute(builder: (context) => ImageViewPage( + images: images, + index: index, + isAdmin: widget.isAdmin, + favorites: true, + )), + ).whenComplete(() { + _getData(); + }); + }, ), ), ), From 5fb173d7d580a57191535f1c1a020b40be8a89b5 Mon Sep 17 00:00:00 2001 From: abrenoch Date: Fri, 20 May 2022 18:10:42 -0400 Subject: [PATCH 14/50] ImageViewPage.dart updated to work with tag & favorites sets --- lib/views/ImageViewPage.dart | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/lib/views/ImageViewPage.dart b/lib/views/ImageViewPage.dart index 044bec7..d7f5b42 100644 --- a/lib/views/ImageViewPage.dart +++ b/lib/views/ImageViewPage.dart @@ -13,13 +13,19 @@ import 'package:piwigo_ng/views/components/dialogs/dialogs.dart'; class ImageViewPage extends StatefulWidget { - ImageViewPage({Key key, this.images, this.index = 0, this.isAdmin = false, this.category = "0", this.title = "Album", this.page = 0}) : super(key: key); + static const String routeName = '/images'; + ImageViewPage({ + Key key, this.images, this.index = 0, this.isAdmin = false, this.category = "0", + this.tag = "0", this.title = "Album", this.favorites = false, this.page = 0 + }) : super(key: key); final int index; final List images; final bool isAdmin; + final bool favorites; final String category; final String title; + final String tag; final int page; @override @@ -67,7 +73,14 @@ class _ImageViewPageState extends State with SingleTickerProvider Future nextPage() async { _imagePage++; - var response = await fetchImages(widget.category, _imagePage); + var response; + if (widget.favorites) { + response = await fetchFavoriteImages(_imagePage); + } else if (widget.tag != null && widget.tag != "0") { + response = await fetchTagImages(widget.tag, _imagePage); + } else { + response = await fetchImages(widget.category, _imagePage); + } if(response['stat'] == 'fail') { ScaffoldMessenger.of(context).showSnackBar( errorSnackBar(context, response['result']) @@ -79,10 +92,10 @@ class _ImageViewPageState extends State with SingleTickerProvider void _onEditImage() async { Navigator.of(context).push( - MaterialPageRoute(builder: (_) => EditImagesPage( - catId: int.parse(widget.category), - images: [_images[_page]], - )) + MaterialPageRoute(builder: (_) => EditImagesPage( + catId: int.parse(widget.category), + images: [_images[_page]], + )) ); } void _onDownloadImage() async { From dde397129a58e5232de9bf3124b1a93f1874c232 Mon Sep 17 00:00:00 2001 From: abrenoch Date: Fri, 20 May 2022 18:11:25 -0400 Subject: [PATCH 15/50] added tag route to popup --- lib/views/components/view_menu_button.dart | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/views/components/view_menu_button.dart b/lib/views/components/view_menu_button.dart index 351fb75..817e558 100644 --- a/lib/views/components/view_menu_button.dart +++ b/lib/views/components/view_menu_button.dart @@ -2,6 +2,7 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:piwigo_ng/constants/SettingsConstants.dart'; import 'package:piwigo_ng/views/FavoritesViewPage.dart'; +import 'package:piwigo_ng/views/components/RootTagViewPage.dart'; enum ViewPopupMenuOptions { favorites, tags, top_viewed, top_rated } @@ -66,8 +67,10 @@ class ViewPopupMenuButton extends StatelessWidget { route = MaterialPageRoute(builder: (context) => FavoritesViewPage( isAdmin: isAdmin, )); - } else if (value == ViewPopupMenuOptions.top_viewed.index) { - // _changeColorAccordingToMenuItem = Colors.green; + } else if (value == ViewPopupMenuOptions.tags.index) { + route = MaterialPageRoute(builder: (context) => RootTagViewPage( + isAdmin: isAdmin, + )); } if (route != null) Navigator.of(context).push(route); } From e68f133698d26db847608ffbcf0a6ee2adf63020 Mon Sep 17 00:00:00 2001 From: abrenoch Date: Wed, 15 Jun 2022 22:42:19 -0400 Subject: [PATCH 16/50] removed root category view page --- lib/views/components/RootTagViewPage.dart | 149 ---------------------- 1 file changed, 149 deletions(-) delete mode 100644 lib/views/components/RootTagViewPage.dart diff --git a/lib/views/components/RootTagViewPage.dart b/lib/views/components/RootTagViewPage.dart deleted file mode 100644 index d67e364..0000000 --- a/lib/views/components/RootTagViewPage.dart +++ /dev/null @@ -1,149 +0,0 @@ -import 'dart:async'; - -import 'package:flutter/material.dart'; - -import 'package:piwigo_ng/api/API.dart'; -import 'package:piwigo_ng/api/TagAPI.dart'; -import 'package:piwigo_ng/constants/SettingsConstants.dart'; -import 'package:piwigo_ng/services/OrientationService.dart'; -import 'package:piwigo_ng/services/upload/Uploader.dart'; -import 'package:piwigo_ng/views/SettingsViewPage.dart'; - -import 'package:piwigo_ng/views/components/appbars.dart'; -import 'package:piwigo_ng/views/components/dialogs/dialogs.dart'; -import 'package:piwigo_ng/views/components/tag_list_item.dart'; - -class RootTagViewPage extends StatefulWidget { - final bool isAdmin; - - const RootTagViewPage({Key key, this.isAdmin = false}) : super(key: key); - @override - _RootTagViewPageState createState() => _RootTagViewPageState(); -} -class _RootTagViewPageState extends State with SingleTickerProviderStateMixin { - TextEditingController _searchController = TextEditingController(); - ScrollController _scrollController = ScrollController(); - - @override - void initState() { - super.initState(); - WidgetsBinding.instance.addPostFrameCallback((_) { - API.uploader = Uploader(context); - }); - } - @override - void dispose() { - super.dispose(); - } - - @override - Widget build(BuildContext context) { - ThemeData _theme = Theme.of(context); - return Scaffold( - resizeToAvoidBottomInset: true, - body: NestedScrollView( - controller: _scrollController, - headerSliverBuilder: (context, innerBoxScrolled) => [ - AppBarExpandable( - scrollController: _scrollController, - actions: [IconButton( - onPressed: () { - Navigator.of(context).push( - MaterialPageRoute(builder: (context) => SettingsPage()), - ); - }, - icon: Icon(Icons.settings, color: _theme.iconTheme.color), - )], - title: appStrings(context).tags, - ), - ], - body: GestureDetector( - onTap: () { - FocusScope.of(context).unfocus(); - }, - child: FutureBuilder>( - future: getTags(), // Albums of the list - builder: (BuildContext context, AsyncSnapshot albumSnapshot) { - if(albumSnapshot.hasData){ - if(albumSnapshot.data['stat'] == 'fail') { - return Container( - padding: EdgeInsets.all(10), - child: Text(albumSnapshot.data['result']), - ); //appStrings(context).categoryMainEmtpy - } - var tags = albumSnapshot.data['result']['tags']; - // int nbPhotos = 0; - // albums.forEach((cat) => nbPhotos+=cat["total_nb_images"]); - // albums.removeWhere((category) => ( - // category["id"].toString() == _rootCategory - // )); - return RefreshIndicator( - displacement: 20, - notificationPredicate: (notification) { - return notification.metrics.atEdge; - }, - onRefresh: () { - setState(() { - print("refresh"); - }); - return Future.delayed(Duration(milliseconds: 1000)); - }, - child: SingleChildScrollView( - child: Column( - children: [ - _tagGrid(tags), - ], - ), - ), - ); - } else { - return Center( - child: CircularProgressIndicator(), - ); - } - } - ), - ), - ), - floatingActionButton: widget.isAdmin? FloatingActionButton( - onPressed: () { - showDialog( - context: context, - builder: (BuildContext context) { - return CreateCategoryDialog(catId: "0"); - } - ).whenComplete(() { - setState(() { - print('refresh'); - }); - }); - }, - child: Icon(Icons.create_new_folder, color: _theme.primaryColorLight, size: 30), - ) : Container(), - ); - } - - Widget _tagGrid(dynamic tags) { - int albumCrossAxisCount = MediaQuery.of(context).size.width <= Constants.albumMinWidth ? 1 - : (MediaQuery.of(context).size.width/Constants.albumMinWidth).floor(); - - return GridView.builder( - gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: albumCrossAxisCount, - mainAxisSpacing: 10, - crossAxisSpacing: 10, - childAspectRatio: 5, - ), - padding: EdgeInsets.all(10), - itemCount: tags.length, - shrinkWrap: true, - physics: NeverScrollableScrollPhysics(), - itemBuilder: (BuildContext context, int index) { - var tag = tags[index]; - return TagListItem(tag, isAdmin: widget.isAdmin, onClose: () { - setState(() {}); - }); - }, - ); - } -} \ No newline at end of file From dac20b77a7fab55d7eaed0e0708addffad8f57fa Mon Sep 17 00:00:00 2001 From: abrenoch Date: Wed, 15 Jun 2022 22:42:36 -0400 Subject: [PATCH 17/50] showChooseTagSheet usage --- .../components/dialogs/tags_dialogs.dart | 182 ++++++++++++++++-- lib/views/components/view_menu_button.dart | 6 +- 2 files changed, 169 insertions(+), 19 deletions(-) diff --git a/lib/views/components/dialogs/tags_dialogs.dart b/lib/views/components/dialogs/tags_dialogs.dart index 22a4ac2..adf6b90 100644 --- a/lib/views/components/dialogs/tags_dialogs.dart +++ b/lib/views/components/dialogs/tags_dialogs.dart @@ -4,6 +4,7 @@ import 'package:piwigo_ng/api/TagAPI.dart'; import 'package:piwigo_ng/constants/SettingsConstants.dart'; import 'package:piwigo_ng/model/TagModel.dart'; import 'package:piwigo_ng/services/OrientationService.dart'; +import 'package:piwigo_ng/views/TagViewPage.dart'; import 'package:piwigo_ng/views/components/buttons.dart'; import 'package:piwigo_ng/views/components/textfields.dart'; @@ -81,10 +82,10 @@ class _SelectTagsPageState extends State { IconButton( onPressed: () { showDialog( - context: context, - builder: (BuildContext context) { - return CreateTagDialog(); - } + context: context, + builder: (BuildContext context) { + return CreateTagDialog(); + } ).whenComplete(() { setState(() {}); }); @@ -431,18 +432,18 @@ class _TagItemState extends State with SingleTickerProviderStateMixin { child: Column( children: [ Container( - height: 40, - padding: EdgeInsets.symmetric(horizontal: 15, vertical: 10), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text('${widget.tag.name}', - style: Theme.of(context).textTheme.subtitle1 - ), - widget.icon ?? SizedBox(), - ], + height: 40, + padding: EdgeInsets.symmetric(horizontal: 15, vertical: 10), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text('${widget.tag.name}', + style: Theme.of(context).textTheme.subtitle1 + ), + widget.icon ?? SizedBox(), + ], + ), ), - ), _isExpanded && !widget.isEnd ? Divider( indent: 10.0, endIndent: 0.0, @@ -478,3 +479,154 @@ class _TagItemState extends State with SingleTickerProviderStateMixin { ); } } + +showChooseTagSheet(context, {content = ''}) async { + showModalBottomSheet( + context: context, + isScrollControlled: true, + backgroundColor: Colors.transparent, + builder: (BuildContext context) { + return DraggableScrollableSheet( + key: UniqueKey(), + initialChildSize: 0.7, + maxChildSize: 0.93, + minChildSize: .5, + expand: false, + builder: (context, controller) => Column( + children: [ + Container( + height: 55, + padding: EdgeInsets.symmetric(horizontal: 15, vertical: 10), + decoration: BoxDecoration( + color: Theme.of(context).primaryColor, + borderRadius: BorderRadius.only( + topLeft: Radius.circular(20), + topRight: Radius.circular(20), + ), + ), + child: Align( + alignment: Alignment.centerLeft, + child: Text( + appStrings(context).alertDismissButton, + style: Theme.of(context).textTheme.headline4, + ) + ) + ), + Expanded( + child: Ink( + color: Theme.of(context).primaryColor, + child: ListView( + controller: controller, + padding: EdgeInsets.symmetric(horizontal: 15), + children: [ + SizedBox(height: 18), + Align( + alignment: Alignment.topLeft, + child: Text(appStrings(context).tags, + style: Theme.of(context).textTheme.headline1 + ), + ), + Align( + alignment: Alignment.topLeft, + child: Text(appStrings(context).tagsTitle_selectOne, + style: Theme.of(context).textTheme.headline5 + ), + ), + SizedBox(height: 10), + + FutureBuilder>( + future: getTags(), + builder: (BuildContext context, AsyncSnapshot tagsSnapshot) { + if(tagsSnapshot.hasData){ + if(tagsSnapshot.data['stat'] == 'fail') { + return Container( + padding: EdgeInsets.all(10), + child: Text(tagsSnapshot.data['result']), + ); //appStrings(context).categoryMainEmtpy + } + var tags = tagsSnapshot.data['result']['tags']; + tags.removeWhere((category) => ( + category["counter"] == 0 + )); + return Wrap( + direction: Axis.horizontal, + children: [ + ListView.separated( + scrollDirection: Axis.vertical, + shrinkWrap: true, + physics: ClampingScrollPhysics(), + clipBehavior: Clip.hardEdge, + padding: EdgeInsets.fromLTRB(0, 0, 0, 10), + // itemExtent: 40.0, + separatorBuilder: (BuildContext context, int index) { + return Divider(height: 1, color: Theme.of(context).primaryColor); + }, + itemCount: tags.length, + itemBuilder: (context, index) { + var tag = tags[index]; + var borderRadius = BorderRadius.circular(0); + var radius = Radius.circular(10); + + if(index == tags.length - 1) { + borderRadius = BorderRadius.only( + bottomLeft: radius, + bottomRight: radius, + ); + } else if (index == 0) { + borderRadius = BorderRadius.only( + topLeft: radius, + topRight: radius, + ); + } + + return ListTile( + shape: RoundedRectangleBorder( + borderRadius: borderRadius + ), + title: Text( + tag['name'] + ' (${ appStrings(context).imageCount(tag['counter']) })', + style: Theme.of(context).textTheme.bodyText1 + ), + // title: Text(tag['name'], style: Theme.of(context).textTheme.bodyText1), + tileColor: Theme.of(context).backgroundColor, + contentPadding: EdgeInsets.symmetric(horizontal: 15), + dense: true, + onTap: () => { + Navigator.of(context).push( + MaterialPageRoute(builder: (context) => TagViewPage( + isAdmin: true, + tag: tag['id'].toString(), + title: tag['name'] + )) + ) + } + ); + }, + ), + Align( + alignment: Alignment.center, + child: Text( + appStrings(context).tagCount(tags.length), + style: Theme.of(context).textTheme.subtitle2 + ), + ), + ] + ); + } else { + return Center( + child: CircularProgressIndicator(), + ); + } + } + ), + SizedBox(height: 10) + ], + ), + ) + ), + ], + ), + ); + }, + ); +} diff --git a/lib/views/components/view_menu_button.dart b/lib/views/components/view_menu_button.dart index 817e558..06830e7 100644 --- a/lib/views/components/view_menu_button.dart +++ b/lib/views/components/view_menu_button.dart @@ -2,7 +2,7 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:piwigo_ng/constants/SettingsConstants.dart'; import 'package:piwigo_ng/views/FavoritesViewPage.dart'; -import 'package:piwigo_ng/views/components/RootTagViewPage.dart'; +import 'package:piwigo_ng/views/components/dialogs/dialogs.dart'; enum ViewPopupMenuOptions { favorites, tags, top_viewed, top_rated } @@ -68,9 +68,7 @@ class ViewPopupMenuButton extends StatelessWidget { isAdmin: isAdmin, )); } else if (value == ViewPopupMenuOptions.tags.index) { - route = MaterialPageRoute(builder: (context) => RootTagViewPage( - isAdmin: isAdmin, - )); + showChooseTagSheet(context); } if (route != null) Navigator.of(context).push(route); } From e6c4c8ffecf4693d175446b914a2bd23159cd4d5 Mon Sep 17 00:00:00 2001 From: abrenoch Date: Wed, 15 Jun 2022 22:55:52 -0400 Subject: [PATCH 18/50] image paging fix --- lib/views/components/content_grid.dart | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/views/components/content_grid.dart b/lib/views/components/content_grid.dart index a1c6ab0..196be51 100644 --- a/lib/views/components/content_grid.dart +++ b/lib/views/components/content_grid.dart @@ -138,7 +138,15 @@ class ContentGridState extends State with SingleTickerProviderState imageList.clear(); imageList.addAll(imagesSnapshot.data['result']['images']); if (_nbImages == 0 && imagesSnapshot.data['result'].containsKey('paging')) { - _nbImages = imagesSnapshot.data['result']['paging']['count']; + if (imagesSnapshot.data['result']['paging'].containsKey('total_count')) { + if (imagesSnapshot.data['result']['paging']['total_count'] is String) { + _nbImages = int.parse(imagesSnapshot.data['result']['paging']['total_count']); + } else { + _nbImages = imagesSnapshot.data['result']['paging']['total_count']; + } + } else { + _nbImages = imagesSnapshot.data['result']['paging']['count']; + } } } From 1d06625d539c9e7b4a1d6e16345a29fcc0eac8fd Mon Sep 17 00:00:00 2001 From: abrenoch Date: Fri, 17 Jun 2022 17:56:54 -0400 Subject: [PATCH 19/50] some cleanup --- lib/views/FavoritesViewPage.dart | 173 +------------------------------ 1 file changed, 2 insertions(+), 171 deletions(-) diff --git a/lib/views/FavoritesViewPage.dart b/lib/views/FavoritesViewPage.dart index 333770a..a58542f 100644 --- a/lib/views/FavoritesViewPage.dart +++ b/lib/views/FavoritesViewPage.dart @@ -268,8 +268,6 @@ class _FavoritesViewPageState extends State with SingleTicker ), ), ), - // floatingActionButton: _isEditMode ? - // Center() : createFloatingActionButton(), bottomNavigationBar: _isEditMode ? createBottomBar() : Container(height: 0), ); @@ -325,118 +323,6 @@ class _FavoritesViewPageState extends State with SingleTicker ); } - // Widget createUploadActionButton() { - // ThemeData _theme = Theme.of(context); - // return SpeedDial( - // spaceBetweenChildren: 10, - // childMargin: EdgeInsets.only(bottom: 17, right: 10), - // animatedIcon: AnimatedIcons.menu_close, - // animatedIconTheme: IconThemeData(size: 22.0), - // closeManually: false, - // curve: Curves.bounceIn, - // backgroundColor: _theme.floatingActionButtonTheme.backgroundColor, - // foregroundColor: _theme.floatingActionButtonTheme.foregroundColor, - // overlayColor: Colors.black, - // elevation: 5.0, - // overlayOpacity: 0.5, - // shape: CircleBorder(), - // children: [ - // SpeedDialChild( - // elevation: 5, - // labelWidget: Text(appStrings(context).createNewAlbum_title, style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold, color: Colors.white)), - // child: Icon(Icons.create_new_folder), - // backgroundColor: _theme.floatingActionButtonTheme.backgroundColor, - // foregroundColor: _theme.floatingActionButtonTheme.foregroundColor, - // onTap: () async { - // showDialog( - // context: context, - // builder: (BuildContext context) { - // return CreateCategoryDialog(catId: widget.category); - // } - // ).whenComplete(() { - // _getData(); - // }); - // }, - // ), - // SpeedDialChild( - // elevation: 5, - // labelWidget: Text(appStrings(context).categoryUpload_images, style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold, color: Colors.white)), - // child: Icon(Icons.add_to_photos), - // backgroundColor: _theme.floatingActionButtonTheme.backgroundColor, - // foregroundColor: _theme.floatingActionButtonTheme.foregroundColor, - // onTap: () async { - // try { - // ScaffoldMessenger.of(context).removeCurrentSnackBar(); - // ScaffoldMessenger.of(context).showSnackBar(SnackBar( - // content: Row( - // mainAxisAlignment: MainAxisAlignment.spaceBetween, - // mainAxisSize: MainAxisSize.min, - // children: [ - // Text(appStrings(context).loadingHUD_label), - // CircularProgressIndicator(), - // ], - // ), - // duration: Duration(days: 365), - // )); - // final List images = ((await FilePicker.platform.pickFiles( - // type: FileType.media, - // allowMultiple: true, - // )) ?.files ?? []).map((e) => XFile(e.path, name: e.name, bytes: e.bytes)).toList(); - // ScaffoldMessenger.of(context).removeCurrentSnackBar(); - // if(images.isNotEmpty) { - // Navigator.push(context, MaterialPageRoute( - // builder: (context) => UploadGalleryViewPage(imageData: images, category: widget.category) - // )).whenComplete(() { - // setState(() { - // print('After upload'); // refresh - // }); - // }); - // } - // } catch (e) { - // print('${e.toString()}'); - // } - // } - // ), - // SpeedDialChild( - // elevation: 5, - // labelWidget: Text(appStrings(context).categoryUpload_take, style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold, color: Colors.white)), - // child: Icon(Icons.photo_camera_rounded), - // backgroundColor: _theme.floatingActionButtonTheme.backgroundColor, - // foregroundColor: _theme.floatingActionButtonTheme.foregroundColor, - // onTap: () async { - // try { - // ScaffoldMessenger.of(context).removeCurrentSnackBar(); - // ScaffoldMessenger.of(context).showSnackBar(SnackBar( - // content: Row( - // mainAxisAlignment: MainAxisAlignment.spaceBetween, - // mainAxisSize: MainAxisSize.min, - // children: [ - // Text(appStrings(context).loadingHUD_label), - // CircularProgressIndicator(), - // ], - // ), - // duration: Duration(days: 365), - // )); - // final XFile image = await ImagePicker().pickImage(source: ImageSource.camera); - // ScaffoldMessenger.of(context).removeCurrentSnackBar(); - // if(image != null) { - // Navigator.push(context, MaterialPageRoute( - // builder: (context) => UploadGalleryViewPage(imageData: [image], category: widget.category) - // )).whenComplete(() { - // setState(() { - // print('After upload'); // refresh - // }); - // }); - // } - // } catch (e) { - // print('Dio error ${e.toString()}'); - // } - // } - // ), - // ], - // ); - // } - Widget createBottomBar() { ThemeData _theme = Theme.of(context); return BottomNavigationBar( @@ -444,38 +330,24 @@ class _FavoritesViewPageState extends State with SingleTicker if(_selectedItems.length > 0) { switch (index) { case 0: - _onEditSelection(); - break; - case 1: _onDownloadSelection(); break; case 2: _onMoveCopySelection(); break; - case 3: - _onDeleteSelection(); - break; default: break; } } }, items: [ - BottomNavigationBarItem( - icon: Icon(Icons.edit, color: _theme.iconTheme.color), - label: appStrings(context).imageOptions_edit, - ), BottomNavigationBarItem( icon: Icon(Icons.download_rounded, color: _theme.iconTheme.color), label: appStrings(context).imageOptions_download, ), BottomNavigationBarItem( - icon: Icon(Icons.reply_outlined, color: _theme.iconTheme.color), - label: appStrings(context).moveImage_title, - ), - BottomNavigationBarItem( - icon: Icon(Icons.delete_outline, color: _theme.errorColor), - label: appStrings(context).deleteImage_delete, + icon: Icon(Icons.favorite_border_rounded, color: _theme.errorColor), + label: "Remove from favorites", ), ], backgroundColor: _theme.scaffoldBackgroundColor, @@ -487,45 +359,4 @@ class _FavoritesViewPageState extends State with SingleTicker currentIndex: 0, ); } - - // Widget createFloatingActionButton() { - // final uploadStatusProvider = Provider.of(context); - // return Padding( - // padding: const EdgeInsets.all(8.0), - // child: Stack( - // children: [ - // Align( - // alignment: Alignment.bottomRight, - // child: Container( - // margin: EdgeInsets.only(bottom: 0, right: widget.isAdmin? 70 : 0), - // child: FloatingActionButton( - // backgroundColor: Color(0xff868686), - // onPressed: () { - // Navigator.of(context).popUntil((route) => route.isFirst); - // }, - // child: uploadStatusProvider.status ? - // Stack( - // alignment: Alignment.center, - // children: [ - // SizedBox( - // height: 55, - // width: 55, - // child: CircularProgressIndicator( - // strokeWidth: 5, - // value: uploadStatusProvider.progress, - // ), - // ), - // Text("${uploadStatusProvider.getRemaining()}", - // style: TextStyle(fontSize: 16), - // ), - // ], - // ) : - // Icon(Icons.home, color: Colors.grey.shade200, size: 30), - // ), - // ), - // ), - // ], - // ), - // ); - // } } \ No newline at end of file From e531d6a89e9d011ab998285acbb6fd734abf363a Mon Sep 17 00:00:00 2001 From: abrenoch Date: Fri, 17 Jun 2022 17:57:10 -0400 Subject: [PATCH 20/50] tag menu close button --- lib/views/components/dialogs/tags_dialogs.dart | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/views/components/dialogs/tags_dialogs.dart b/lib/views/components/dialogs/tags_dialogs.dart index adf6b90..1d03f5f 100644 --- a/lib/views/components/dialogs/tags_dialogs.dart +++ b/lib/views/components/dialogs/tags_dialogs.dart @@ -505,11 +505,14 @@ showChooseTagSheet(context, {content = ''}) async { ), ), child: Align( - alignment: Alignment.centerLeft, + alignment: Alignment.centerLeft, + child: TextButton( + onPressed: () => Navigator.of(context).pop(), child: Text( appStrings(context).alertDismissButton, style: Theme.of(context).textTheme.headline4, - ) + ), + ) ) ), Expanded( From 7bfdcbc97b79f9ccb93ba0686f9d1278c3d8952d Mon Sep 17 00:00:00 2001 From: abrenoch Date: Fri, 17 Jun 2022 18:03:55 -0400 Subject: [PATCH 21/50] disabled some list types --- lib/views/components/view_menu_button.dart | 30 ++++++++++------------ 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/lib/views/components/view_menu_button.dart b/lib/views/components/view_menu_button.dart index 06830e7..67f6d6f 100644 --- a/lib/views/components/view_menu_button.dart +++ b/lib/views/components/view_menu_button.dart @@ -20,27 +20,25 @@ class ViewPopupMenuButton extends StatelessWidget { shape: RoundedRectangleBorder( borderRadius: BorderRadius.all(Radius.circular(10.0)) ), - // padding: EdgeInsets.zero, - // offset: Offset(0, 50), itemBuilder: (ctx) => [ - isAdmin ? _buildPopupMenuItem( - appStrings(context).categoryDiscoverFavorites_title, ViewPopupMenuOptions.favorites.index, - iconData: Icons.favorite_border - ) : null, + // isAdmin ? _buildPopupMenuItem( + // appStrings(context).categoryDiscoverFavorites_title, ViewPopupMenuOptions.favorites.index, + // iconData: Icons.favorite_border + // ) : null, _buildPopupMenuItem( appStrings(context).tags, ViewPopupMenuOptions.tags.index, iconData: Icons.local_offer_outlined ), - _buildPopupMenuItem( - appStrings(context).categoryDiscoverVisits_title, ViewPopupMenuOptions.top_viewed.index, - ), - _buildPopupMenuItem( - appStrings(context).categoryDiscoverBest_title, ViewPopupMenuOptions.top_rated.index, - ), - _buildPopupMenuItem( - appStrings(context).categoryDiscoverRecent_title, ViewPopupMenuOptions.top_rated.index, - iconData: Icons.access_time_rounded - ), + // _buildPopupMenuItem( + // appStrings(context).categoryDiscoverVisits_title, ViewPopupMenuOptions.top_viewed.index, + // ), + // _buildPopupMenuItem( + // appStrings(context).categoryDiscoverBest_title, ViewPopupMenuOptions.top_rated.index, + // ), + // _buildPopupMenuItem( + // appStrings(context).categoryDiscoverRecent_title, ViewPopupMenuOptions.top_rated.index, + // iconData: Icons.access_time_rounded + // ), ], ); } From ebd39ff9535e91ee881e94ce93f9797a21a6ea4e Mon Sep 17 00:00:00 2001 From: abrenoch Date: Fri, 17 Jun 2022 18:13:18 -0400 Subject: [PATCH 22/50] TagViewPage.dart cleanup --- lib/views/TagViewPage.dart | 249 +------------------------------------ 1 file changed, 6 insertions(+), 243 deletions(-) diff --git a/lib/views/TagViewPage.dart b/lib/views/TagViewPage.dart index a3d5fc5..06dfbcf 100644 --- a/lib/views/TagViewPage.dart +++ b/lib/views/TagViewPage.dart @@ -1,20 +1,13 @@ -import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter/widgets.dart'; -import 'package:flutter_speed_dial/flutter_speed_dial.dart'; -import 'package:image_picker/image_picker.dart'; import 'package:piwigo_ng/api/ImageAPI.dart'; import 'package:piwigo_ng/constants/SettingsConstants.dart'; -import 'package:piwigo_ng/services/UploadStatusProvider.dart'; import 'package:piwigo_ng/views/ImageViewPage.dart'; import 'package:piwigo_ng/views/components/content_grid.dart'; -import 'package:piwigo_ng/views/components/snackbars.dart'; -import 'package:piwigo_ng/views/UploadGalleryViewPage.dart'; import 'package:piwigo_ng/views/components/dialogs/dialogs.dart'; -import 'package:provider/provider.dart'; class TagViewPage extends StatefulWidget { @@ -67,12 +60,11 @@ class _TagViewPageState extends State with SingleTickerProviderStat } void _onEditSelection() async { - // Navigator.of(context).push( - // MaterialPageRoute(builder: (_) => EditImagesPage( - // catId: int.parse(widget.category), - // images: _selectedItems.values.toList(), - // )) - // ); + Navigator.of(context).push( + MaterialPageRoute(builder: (_) => EditImagesPage( + images: _selectedItems.values.toList(), + )) + ); } void _onDownloadSelection() async { if (await confirmDownloadDialog(context, @@ -102,73 +94,6 @@ class _TagViewPageState extends State with SingleTickerProviderStat await downloadImages(selection); } } - void _onMoveCopySelection() async { - int choice = await chooseMoveCopyImage(context, - content: appStrings(context).moveOrCopyImage_title(_selectedItems.length) - ); - - switch(choice) { - case 0: showDialog(context: context, - builder: (context) { - return MoveOrCopyDialog( - title: appStrings(context).moveImage_title, - subtitle: appStrings(context).moveImage_selectAlbum(_selectedItems.length, ''), - catName: appStrings(context).categoryDiscoverFavorites_title, - isImage: true, - onSelected: (item) async { - if( await confirmMoveDialog(context, - content: appStrings(context).moveImage_message(_selectedItems.length, "", item.name), - )) { - int nbMoved = await moveImages(context, - _selectedItems.values.toList(), - int.parse(item.id) - ); - ScaffoldMessenger.of(context).showSnackBar(imagesMovedSnackBar(context, nbMoved)); - Navigator.of(context).pop(); - } - }, - ); - } - ).whenComplete(() { - setState(() { - _selectedItems.clear(); - _isEditMode = false; - }); - _getData(); - }); - break; - case 1: showDialog(context: context, - builder: (context) { - return MoveOrCopyDialog( - title: appStrings(context).copyImage_title, - subtitle: appStrings(context).copyImage_selectAlbum(_selectedItems.length, ''), - // catName: widget.title, - isImage: true, - onSelected: (item) async { - if( await confirmAssignDialog(context, - content: appStrings(context).copyImage_message(_selectedItems.length, "", item.name), - )) { - int nbCopied = await assignImages(context, - _selectedItems.values.toList(), - int.parse(item.id) - ); - ScaffoldMessenger.of(context).showSnackBar(imagesAssignedSnackBar(context, nbCopied)); - Navigator.of(context).pop(); - } - }, - ); - } - ).whenComplete(() { - setState(() { - _selectedItems.clear(); - _isEditMode = false; - }); - _getData(); - }); - break; - default: break; - } - } void _onDeleteSelection() async { int choice = await confirmRemoveImagesFromAlbumDialog(context, content: appStrings(context).deleteImage_message(_selectedItems.length), @@ -232,7 +157,7 @@ class _TagViewPageState extends State with SingleTickerProviderStat isEditMode: _isEditMode || false, selectedItems: _selectedItems, loadMoreImages: (int page) { - print('Loading page $page of tag ${widget.tag} images'); + print('Loading page $page of tag ${widget.tag}'); return fetchTagImages(widget.tag, page); }, setEditMode: (bool isEditMode, {image}) => { @@ -270,8 +195,6 @@ class _TagViewPageState extends State with SingleTickerProviderStat ), ), ), - // floatingActionButton: _isEditMode ? - // Center() : createFloatingActionButton(), bottomNavigationBar: _isEditMode ? createBottomBar() : Container(height: 0), ); @@ -327,118 +250,6 @@ class _TagViewPageState extends State with SingleTickerProviderStat ); } - // Widget createUploadActionButton() { - // ThemeData _theme = Theme.of(context); - // return SpeedDial( - // spaceBetweenChildren: 10, - // childMargin: EdgeInsets.only(bottom: 17, right: 10), - // animatedIcon: AnimatedIcons.menu_close, - // animatedIconTheme: IconThemeData(size: 22.0), - // closeManually: false, - // curve: Curves.bounceIn, - // backgroundColor: _theme.floatingActionButtonTheme.backgroundColor, - // foregroundColor: _theme.floatingActionButtonTheme.foregroundColor, - // overlayColor: Colors.black, - // elevation: 5.0, - // overlayOpacity: 0.5, - // shape: CircleBorder(), - // children: [ - // SpeedDialChild( - // elevation: 5, - // labelWidget: Text(appStrings(context).createNewAlbum_title, style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold, color: Colors.white)), - // child: Icon(Icons.create_new_folder), - // backgroundColor: _theme.floatingActionButtonTheme.backgroundColor, - // foregroundColor: _theme.floatingActionButtonTheme.foregroundColor, - // onTap: () async { - // showDialog( - // context: context, - // builder: (BuildContext context) { - // return CreateCategoryDialog(catId: widget.category); - // } - // ).whenComplete(() { - // _getData(); - // }); - // }, - // ), - // SpeedDialChild( - // elevation: 5, - // labelWidget: Text(appStrings(context).categoryUpload_images, style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold, color: Colors.white)), - // child: Icon(Icons.add_to_photos), - // backgroundColor: _theme.floatingActionButtonTheme.backgroundColor, - // foregroundColor: _theme.floatingActionButtonTheme.foregroundColor, - // onTap: () async { - // try { - // ScaffoldMessenger.of(context).removeCurrentSnackBar(); - // ScaffoldMessenger.of(context).showSnackBar(SnackBar( - // content: Row( - // mainAxisAlignment: MainAxisAlignment.spaceBetween, - // mainAxisSize: MainAxisSize.min, - // children: [ - // Text(appStrings(context).loadingHUD_label), - // CircularProgressIndicator(), - // ], - // ), - // duration: Duration(days: 365), - // )); - // final List images = ((await FilePicker.platform.pickFiles( - // type: FileType.media, - // allowMultiple: true, - // )) ?.files ?? []).map((e) => XFile(e.path, name: e.name, bytes: e.bytes)).toList(); - // ScaffoldMessenger.of(context).removeCurrentSnackBar(); - // if(images.isNotEmpty) { - // Navigator.push(context, MaterialPageRoute( - // builder: (context) => UploadGalleryViewPage(imageData: images, category: widget.category) - // )).whenComplete(() { - // setState(() { - // print('After upload'); // refresh - // }); - // }); - // } - // } catch (e) { - // print('${e.toString()}'); - // } - // } - // ), - // SpeedDialChild( - // elevation: 5, - // labelWidget: Text(appStrings(context).categoryUpload_take, style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold, color: Colors.white)), - // child: Icon(Icons.photo_camera_rounded), - // backgroundColor: _theme.floatingActionButtonTheme.backgroundColor, - // foregroundColor: _theme.floatingActionButtonTheme.foregroundColor, - // onTap: () async { - // try { - // ScaffoldMessenger.of(context).removeCurrentSnackBar(); - // ScaffoldMessenger.of(context).showSnackBar(SnackBar( - // content: Row( - // mainAxisAlignment: MainAxisAlignment.spaceBetween, - // mainAxisSize: MainAxisSize.min, - // children: [ - // Text(appStrings(context).loadingHUD_label), - // CircularProgressIndicator(), - // ], - // ), - // duration: Duration(days: 365), - // )); - // final XFile image = await ImagePicker().pickImage(source: ImageSource.camera); - // ScaffoldMessenger.of(context).removeCurrentSnackBar(); - // if(image != null) { - // Navigator.push(context, MaterialPageRoute( - // builder: (context) => UploadGalleryViewPage(imageData: [image], category: widget.category) - // )).whenComplete(() { - // setState(() { - // print('After upload'); // refresh - // }); - // }); - // } - // } catch (e) { - // print('Dio error ${e.toString()}'); - // } - // } - // ), - // ], - // ); - // } - Widget createBottomBar() { ThemeData _theme = Theme.of(context); return BottomNavigationBar( @@ -451,9 +262,6 @@ class _TagViewPageState extends State with SingleTickerProviderStat case 1: _onDownloadSelection(); break; - case 2: - _onMoveCopySelection(); - break; case 3: _onDeleteSelection(); break; @@ -471,10 +279,6 @@ class _TagViewPageState extends State with SingleTickerProviderStat icon: Icon(Icons.download_rounded, color: _theme.iconTheme.color), label: appStrings(context).imageOptions_download, ), - BottomNavigationBarItem( - icon: Icon(Icons.reply_outlined, color: _theme.iconTheme.color), - label: appStrings(context).moveImage_title, - ), BottomNavigationBarItem( icon: Icon(Icons.delete_outline, color: _theme.errorColor), label: appStrings(context).deleteImage_delete, @@ -489,45 +293,4 @@ class _TagViewPageState extends State with SingleTickerProviderStat currentIndex: 0, ); } - -// Widget createFloatingActionButton() { -// final uploadStatusProvider = Provider.of(context); -// return Padding( -// padding: const EdgeInsets.all(8.0), -// child: Stack( -// children: [ -// Align( -// alignment: Alignment.bottomRight, -// child: Container( -// margin: EdgeInsets.only(bottom: 0, right: widget.isAdmin? 70 : 0), -// child: FloatingActionButton( -// backgroundColor: Color(0xff868686), -// onPressed: () { -// Navigator.of(context).popUntil((route) => route.isFirst); -// }, -// child: uploadStatusProvider.status ? -// Stack( -// alignment: Alignment.center, -// children: [ -// SizedBox( -// height: 55, -// width: 55, -// child: CircularProgressIndicator( -// strokeWidth: 5, -// value: uploadStatusProvider.progress, -// ), -// ), -// Text("${uploadStatusProvider.getRemaining()}", -// style: TextStyle(fontSize: 16), -// ), -// ], -// ) : -// Icon(Icons.home, color: Colors.grey.shade200, size: 30), -// ), -// ), -// ), -// ], -// ), -// ); -// } } \ No newline at end of file From 37e0cf9821e40d173b9a8876bf79d737c249454f Mon Sep 17 00:00:00 2001 From: abrenoch Date: Fri, 17 Jun 2022 18:15:21 -0400 Subject: [PATCH 23/50] removed tag_list_item --- lib/views/components/tag_list_item.dart | 245 ------------------------ 1 file changed, 245 deletions(-) delete mode 100644 lib/views/components/tag_list_item.dart diff --git a/lib/views/components/tag_list_item.dart b/lib/views/components/tag_list_item.dart deleted file mode 100644 index c8c55d9..0000000 --- a/lib/views/components/tag_list_item.dart +++ /dev/null @@ -1,245 +0,0 @@ -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_slidable/flutter_slidable.dart'; -import 'package:piwigo_ng/constants/SettingsConstants.dart'; -// import 'package:piwigo_ng/model/PageArguments.dart'; -// import 'package:piwigo_ng/routes/RoutePaths.dart'; -import 'package:piwigo_ng/services/OrientationService.dart'; -import 'package:piwigo_ng/views/CategoryViewPage.dart'; -import 'package:piwigo_ng/api/CategoryAPI.dart'; -import 'package:piwigo_ng/views/TagViewPage.dart'; -// import 'package:piwigo_ng/views/TagViewPage.dart'; - -import 'package:piwigo_ng/views/components/dialogs/dialogs.dart'; -import 'package:piwigo_ng/views/components/dialogs/tags_dialogs.dart'; -import 'package:piwigo_ng/views/components/snackbars.dart'; -import 'package:piwigo_ng/views/components/custom_shapes.dart'; - - -// String albumSubCount(dynamic album, context) { -// String displayString = appStrings(context).imageCount(album["total_nb_images"]); -// if(album["nb_categories"] > 0) { -// displayString += ', ${appStrings(context).subAlbumCount(album["nb_categories"])}'; -// } -// return displayString; -// } - -class TagListItem extends StatefulWidget { - const TagListItem(this.tag, {Key key, - this.isAdmin = false, this.onClose, this.onOpen - }) : super(key: key); - - final dynamic tag; - final bool isAdmin; - final Function() onClose; - final Function() onOpen; - - @override - _TagListItemState createState() => _TagListItemState(); -} -class _TagListItemState extends State { - - void _onRenameTag() async { - // showDialog( - // context: context, - // builder: (BuildContext context) { - // return EditTagDialog( - // tagId: widget.tag['id'], - // tagName: widget.tag['name'] - // ); - // } - // ).whenComplete(() { - // widget.onClose(); - // }); - } - void _onMoveAlbum() async { - // showDialog( - // context: context, - // builder: (BuildContext context) { - // return MoveOrCopyDialog( - // title: appStrings(context).moveCategory, - // subtitle: appStrings(context).moveCategory_select(widget.album['name']), - // catId: widget.tag['id'].toString(), - // catName: widget.tag['name'], - // isImage: false, - // onSelected: (item) async { - // if (await confirmMoveDialog(context, - // content: appStrings(context).moveCategory_message(widget.album['name'], item.name), - // )) { - // var result = await moveCategory(widget.album['id'], item.id); - // if(result['stat'] == 'fail') { - // ScaffoldMessenger.of(context).showSnackBar( - // errorSnackBar(context, result['result']) - // ); - // } else { - // ScaffoldMessenger.of(context).showSnackBar( - // albumMovedSnackBar(context) - // ); - // } - // Navigator.of(context).pop(); - // } - // }, - // ); - // } - // ).whenComplete(() { - // widget.onClose(); - // }); - // widget.onClose(); - } - void _onDeleteTag() async { - // if(widget.album["total_nb_images"] > 0) { - // int choice = await confirmDeleteAlbumWithImagesDialog(context, - // content: appStrings(context).deleteCategory_message(widget.album["total_nb_images"], widget.album["name"]), - // count: widget.album["total_nb_images"], - // ); - // var result; - // switch(choice) { - // case 0: result = await deleteCategory(widget.album['id'].toString(), - // deletionMode: 'no_delete'); - // break; - // case 1: result = await deleteCategory(widget.album['id'].toString(), - // deletionMode: 'delete_orphans'); - // break; - // case 2: result = await deleteCategory(widget.album['id'].toString(), - // deletionMode: 'force_delete'); - // break; - // default: break; - // } - // widget.onClose(); - // } - // else { - // if (await confirmDeleteDialog(context, - // content: appStrings(context).deleteCategory_message(widget.album["total_nb_images"], widget.album["name"]), - // )) { - // var result = await deleteCategory(widget.album['id'].toString()); - // if(result['stat'] == 'fail') { - // ScaffoldMessenger.of(context).showSnackBar( - // errorSnackBar(context, result['result']) - // ); - // } else { - // ScaffoldMessenger.of(context).showSnackBar( - // albumDeletedSnackBar(context) - // ); - // widget.onClose(); - // } - // } - // } - } - - @override - Widget build(BuildContext context) { - return InkWell( - onTap: () { - if(widget.onOpen != null) widget.onOpen(); - // - // Navigator.of(context).pushNamed(RoutePaths.TagContent, - // arguments: PageArguments( - // tag: widget.tag['id'].toString(), - // title: widget.tag['name'], - // isAdmin: widget.isAdmin, - // ) - // ); - - Navigator.of(context).push( - MaterialPageRoute(builder: (context) => TagViewPage( - title: widget.tag["name"], - tag: widget.tag["id"].toString(), - isAdmin: widget.isAdmin, - nbImages: widget.tag["counter"], - )), - ).whenComplete(() { - widget.onClose(); - }); - - - - }, - child: ClipRRect( - borderRadius: BorderRadius.circular(10), - child: TagListCard(widget.tag, isAdmin: widget.isAdmin), - ), - ); - } -} - -class TagListCard extends StatelessWidget { - const TagListCard(this.tag, {Key key, this.isAdmin = false}) : super(key: key); - - final dynamic tag; - final bool isAdmin; - - @override - Widget build(BuildContext context) { - return Row( - children: [ - // albumThumbnail(context, tag), - // albumItemSeparator(context), - tagInfo(context, tag), - ], - ); - } - - Widget tagInfo(BuildContext context, tag) { - - return Expanded( - child: Container( - decoration: BoxDecoration( - border: Border.all(width: 0, color: Theme.of(context).backgroundColor), - color: Theme.of(context).backgroundColor, - ), - padding: EdgeInsets.all(5), - height: 80, - child: Column( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text('${tag["name"]} (${appStrings(context).imageCount(tag["counter"])})', - style: Theme.of(context).textTheme.headline4, - textAlign: TextAlign.left, - overflow: TextOverflow.ellipsis, - softWrap: false, - maxLines: 1 - ), - ] - ), - ), - ); - } -// Widget albumThumbnail(BuildContext context, album) { -// return LayoutBuilder(builder: (context, layout) { -// return Container( -// decoration: BoxDecoration( -// border: Border.all(width: 0, color: Theme.of(context).backgroundColor), -// color: Theme.of(context).backgroundColor, -// borderRadius: BorderRadius.only( -// topLeft: Radius.circular(10), -// bottomLeft: Radius.circular(10), -// ), -// ), -// padding: EdgeInsets.all(5), -// height: layout.maxHeight,// albumGridItemHeight(context), -// width: layout.maxHeight,// albumGridItemHeight(context), -// child: album["tn_url"] == null ? -// Icon(Icons.image_not_supported_outlined, size: 50) -// : -// ClipRRect( -// borderRadius: BorderRadius.circular(7.0), -// child: Image.network( -// album["tn_url"], -// fit: BoxFit.cover, -// ), -// ), -// ); -// }); -// } -// Widget albumItemSeparator(BuildContext context) { -// return Container( -// decoration: ShapeDecoration( -// shape: AlbumCardSeparatorShape(radius: 7), -// color: Theme.of(context).backgroundColor, -// ), -// width: 14, -// height: 80, -// ); -// } -} From 842c6c575488b0afeb93e327c3a8bbcebd991a14 Mon Sep 17 00:00:00 2001 From: abrenoch Date: Fri, 17 Jun 2022 20:13:26 -0400 Subject: [PATCH 24/50] tag sheet start size --- lib/views/components/dialogs/tags_dialogs.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/views/components/dialogs/tags_dialogs.dart b/lib/views/components/dialogs/tags_dialogs.dart index 1d03f5f..2443c42 100644 --- a/lib/views/components/dialogs/tags_dialogs.dart +++ b/lib/views/components/dialogs/tags_dialogs.dart @@ -488,9 +488,9 @@ showChooseTagSheet(context, {content = ''}) async { builder: (BuildContext context) { return DraggableScrollableSheet( key: UniqueKey(), - initialChildSize: 0.7, + initialChildSize: 0.93, maxChildSize: 0.93, - minChildSize: .5, + // minChildSize: .5, expand: false, builder: (context, controller) => Column( children: [ From ed76d5f56ec0ab17c18698e0ffecf5dd44a3275a Mon Sep 17 00:00:00 2001 From: abrenoch Date: Wed, 13 Jul 2022 22:14:51 -0400 Subject: [PATCH 25/50] updated per 7fd0fa7c0b951a05c7b6f3926703c12ae8474431 --- lib/views/components/content_grid.dart | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/views/components/content_grid.dart b/lib/views/components/content_grid.dart index 196be51..78e34aa 100644 --- a/lib/views/components/content_grid.dart +++ b/lib/views/components/content_grid.dart @@ -14,7 +14,6 @@ import 'package:piwigo_ng/services/OrientationService.dart'; import 'package:piwigo_ng/views/components/list_item.dart'; import 'package:piwigo_ng/views/components/snackbars.dart'; -import 'package:piwigo_ng/views/ImageViewPage.dart'; import 'package:piwigo_ng/views/UploadGalleryViewPage.dart'; import 'package:piwigo_ng/views/components/dialogs/dialogs.dart'; @@ -311,7 +310,7 @@ class ContentGridState extends State with SingleTickerProviderState ThemeData _theme = Theme.of(context); int albumCrossAxisCount = MediaQuery.of(context).size.width <= Constants.albumMinWidth ? 1 - : (MediaQuery.of(context).size.width/Constants.albumMinWidth).floor(); + : (MediaQuery.of(context).size.width/Constants.albumMinWidth).round(); return RefreshIndicator( displacement: 20, From 671c71abc03934f7624dc59478171866f7d7a845 Mon Sep 17 00:00:00 2001 From: Remi Martin <71256855+remi-martin@users.noreply.github.com> Date: Sat, 19 Aug 2023 13:11:44 +0200 Subject: [PATCH 26/50] New translations app_en.arb (German) --- l10n/app_de.arb | 1 - 1 file changed, 1 deletion(-) diff --git a/l10n/app_de.arb b/l10n/app_de.arb index 588d83c..99ebe7c 100644 --- a/l10n/app_de.arb +++ b/l10n/app_de.arb @@ -1,6 +1,5 @@ { "tabBar_albums": "Alben", - "settings_language": "Wählen Sie die Sprache", "tabBar_upload": "Hochladen", "tabBar_preferences": "Einstellungen", "alertOkButton": "OK", From ae6fb483b3bf47645cd7d96434c5d75a58b377c5 Mon Sep 17 00:00:00 2001 From: Remi Martin <71256855+remi-martin@users.noreply.github.com> Date: Sat, 19 Aug 2023 13:11:46 +0200 Subject: [PATCH 27/50] New translations app_en.arb (Chinese Simplified) --- l10n/app_zh.arb | 52 ++++++++++++++++++++++++------------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/l10n/app_zh.arb b/l10n/app_zh.arb index 4b9bbab..c3feb1d 100644 --- a/l10n/app_zh.arb +++ b/l10n/app_zh.arb @@ -56,8 +56,8 @@ "loginHTTP_enable": "Enable HTTP Basic", "loginHTTPuser_placeholder": "用户名", "loginHTTPpwd_placeholder": "密码", - "loginCert_title": "Self Signed Certificates", - "loginCert_enable": "Allow SSL", + "loginCert_title": "自签名证书", + "loginCert_enable": "允许 SSL", "loginCertFailed_title": "非私密连接", "loginCertFailed_message": "Piwigo警告网站证书无效。您仍然想要接受此证书吗?", "loginHTTPSfailed_title": "安全连接失败", @@ -153,7 +153,7 @@ "categoryUpload_loadSubCategories": "加载", "categoryUpload_images": "上传图片", "categoryUpload_videos": "上传视频", - "categoryUpload_camera": "Camera", + "categoryUpload_camera": "相机", "categoryUpload_takePhoto": "拍照", "categoryUpload_takeVideo": "录制", "uploadList_title": "上传状态", @@ -242,23 +242,23 @@ "moveCategoryHUD_moved": "相册已移动", "moveCategoryError_title": "移动失败", "moveCategoryError_message": "移动相册失败", - "categoryPrivacy": "Manage Permissions", - "categoryPrivacy_subtitle": "Manage access permissions of \"{album_name}\".", + "categoryPrivacy": "管理权限", + "categoryPrivacy_subtitle": "管理 \"{album_name}\" 的访问权限", "@categoryPrivacy_subtitle": { "placeholders": { "album_name": {} } }, - "categoryPrivacyMode_public": "Public", - "categoryPrivacyMode_publicMessage": "Every user can see this album.", - "categoryPrivacyMode_private": "Private", - "categoryPrivacyMode_privateMessage": "Visitors must log in and have the necessary permissions to see this album.", - "categoryPrivacyGroups": "Group permissions", - "categoryPrivacyGroups_add": "Authorize groups", - "categoryPrivacyUsers": "User permissions", - "categoryPrivacyUsers_message": "To manage user permissions, go to your web administration.", - "categoryPrivacyRecursive": "Apply to sub-albums", - "categoryPrivacyRecursive_message": "After confirmation, all modifications will be applied to sub-albums.", + "categoryPrivacyMode_public": "公开的", + "categoryPrivacyMode_publicMessage": "每个用户都可以看到此相册。", + "categoryPrivacyMode_private": "私有的", + "categoryPrivacyMode_privateMessage": "访客必须登录并拥有查看此相册的必要权限。", + "categoryPrivacyGroups": "组权限", + "categoryPrivacyGroups_add": "授权组", + "categoryPrivacyUsers": "用户权限", + "categoryPrivacyUsers_message": "要管理用户权限,请前往您的网站管理界面。", + "categoryPrivacyRecursive": "应用到子相册", + "categoryPrivacyRecursive_message": "确认后,所有修改都将应用于子相册。", "categorySelection_setThumbnail": "请选择要使用照片 {photo} 作为缩略图的相册。", "@categorySelection_setThumbnail": { "placeholders": { @@ -364,17 +364,17 @@ "tagsAdd_placeholder": "新标签", "tagsAddHUD_label": "创建标签中...", "tagsAddHUD_created": "标签已创建", - "group": "Group", - "groups": "Groups", - "groupsTitle_selectOne": "Select a Group", - "groupsHeader_selected": "Selected", - "groupsHeader_notSelected": "Not Selected", - "groupsHeader_all": "All Groups", - "groupsAdd_title": "Add Group", - "groupsAdd_message": "Enter a name for this new group", - "groupsAdd_placeholder": "New group", - "groupsAddHUD_label": "Creating Group…", - "groupsAddHUD_created": "Group Created", + "group": "组", + "groups": "组", + "groupsTitle_selectOne": "选择一个组", + "groupsHeader_selected": "已选中", + "groupsHeader_notSelected": "未选中", + "groupsHeader_all": "所有组", + "groupsAdd_title": "添加组", + "groupsAdd_message": "为新增的组设置名称", + "groupsAdd_placeholder": "新增组", + "groupsAddHUD_label": "正在创建组…", + "groupsAddHUD_created": "已创建组", "tagsAddError_message": "无法创建新标签", "tagsAddError_title": "创建失败", "selectImages": "选择图片", From 9b515463c525fc0207158dd43807a18079fa2f0e Mon Sep 17 00:00:00 2001 From: Remi Martin <71256855+remi-martin@users.noreply.github.com> Date: Sun, 20 Aug 2023 13:15:40 +0200 Subject: [PATCH 28/50] New translations app_en.arb (Chinese Simplified) --- l10n/app_zh.arb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/l10n/app_zh.arb b/l10n/app_zh.arb index c3feb1d..843cb30 100644 --- a/l10n/app_zh.arb +++ b/l10n/app_zh.arb @@ -20,7 +20,7 @@ "loadingHUD_label": "正在加载…", "completeHUD_label": "完成", "errorHUD_label": "出错", - "loadMoreHUD_label": "Release to load more", + "loadMoreHUD_label": "释放加载更多", "uploadRights_title": "需要上传权限", "uploadRights_message": "您必须拥有上传权限才能上传照片或视频。", "internetErrorGeneral_title": "连接出错", @@ -48,12 +48,12 @@ "login_newSession": "正在打开会话", "login_communityParameters": "社区参数", "login_serverParameters": "Piwigo参数", - "login_advancedParameters": "Authentication Settings", + "login_advancedParameters": "验证设置", "login_connectionChanged": "连接已变更!", - "login_rememberCredentials": "Remember credentials", + "login_rememberCredentials": "记住凭据", "loginHTTP_title": "HTTP 证书", "loginHTTP_message": "Piwigo服务器需要基本访问验证:", - "loginHTTP_enable": "Enable HTTP Basic", + "loginHTTP_enable": "使能 HTTP Basic", "loginHTTPuser_placeholder": "用户名", "loginHTTPpwd_placeholder": "密码", "loginCert_title": "自签名证书", From 1e93ffed11c118e2495dbf3afc9a2e180c490f6c Mon Sep 17 00:00:00 2001 From: Rainer Ots Date: Wed, 25 Oct 2023 17:57:38 +0300 Subject: [PATCH 29/50] Change ImageModel.name to nullable string Null-coalesce to empty string where used --- lib/components/cards/image_card.dart | 2 +- lib/components/dialogs/image_comment_dialog.dart | 4 ++-- lib/components/modals/image_info_modal.dart | 2 +- lib/models/image_model.dart | 6 +++--- lib/utils/image_actions.dart | 2 +- lib/views/image/edit_image_page.dart | 2 +- lib/views/image/image_page.dart | 2 +- 7 files changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/components/cards/image_card.dart b/lib/components/cards/image_card.dart index 1c3a469..0cad66a 100644 --- a/lib/components/cards/image_card.dart +++ b/lib/components/cards/image_card.dart @@ -80,7 +80,7 @@ class ImageCard extends StatelessWidget { end: Alignment.topCenter), ), child: AutoSizeText( - image.name, + image.name ?? "", maxLines: 1, maxFontSize: 14, minFontSize: 8, diff --git a/lib/components/dialogs/image_comment_dialog.dart b/lib/components/dialogs/image_comment_dialog.dart index 00b8c42..5544ab6 100644 --- a/lib/components/dialogs/image_comment_dialog.dart +++ b/lib/components/dialogs/image_comment_dialog.dart @@ -59,7 +59,7 @@ class _ImageCommentDialogState extends State { curve: Curves.ease, opacity: isNameHidden ? 1.0 : 0.0, child: Text( - widget.image.name, + widget.image.name ?? "", maxLines: 1, overflow: TextOverflow.ellipsis, style: Theme.of(context).textTheme.titleMedium, @@ -78,7 +78,7 @@ class _ImageCommentDialogState extends State { crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Text( - widget.image.name, + widget.image.name ?? "", textAlign: TextAlign.center, style: Theme.of(context).textTheme.titleMedium, ), diff --git a/lib/components/modals/image_info_modal.dart b/lib/components/modals/image_info_modal.dart index 9777cef..c5bef46 100644 --- a/lib/components/modals/image_info_modal.dart +++ b/lib/components/modals/image_info_modal.dart @@ -45,7 +45,7 @@ class ImageInfoModal extends StatelessWidget { Padding( padding: const EdgeInsets.all(8.0), child: Text( - image.name, + image.name ?? "", style: Theme.of(context).textTheme.titleMedium, ), ), diff --git a/lib/models/image_model.dart b/lib/models/image_model.dart index 2754887..4b719af 100644 --- a/lib/models/image_model.dart +++ b/lib/models/image_model.dart @@ -8,7 +8,7 @@ class ImageModel { int hit; bool favorite; String file; - String name; + String? name; String? comment; String? dateCreation; String? dateAvailable; @@ -25,7 +25,7 @@ class ImageModel { this.hit = 0, this.favorite = false, this.file = '', - required this.name, + this.name, this.comment, this.dateCreation, this.dateAvailable, @@ -43,7 +43,7 @@ class ImageModel { hit = int.tryParse(json['hit'].toString()) ?? 0, favorite = json['is_favorite'] ?? false, file = json['file'].toString(), - name = json['name'].toString(), + name = json['name']?.toString(), comment = json['comment'], dateCreation = json['date_creation'], dateAvailable = json['date_available'], diff --git a/lib/utils/image_actions.dart b/lib/utils/image_actions.dart index 97362c8..4e16d09 100644 --- a/lib/utils/image_actions.dart +++ b/lib/utils/image_actions.dart @@ -188,7 +188,7 @@ Future onMovePhotos(BuildContext context, List images, title: appStrings.moveImage_title, subtitle: appStrings.moveImage_selectAlbum( images.length, - images.first.name, + images.first.name ?? "", ), isImage: true, album: origin, diff --git a/lib/views/image/edit_image_page.dart b/lib/views/image/edit_image_page.dart index 4678576..8f9047c 100644 --- a/lib/views/image/edit_image_page.dart +++ b/lib/views/image/edit_image_page.dart @@ -51,7 +51,7 @@ class _EditImagePageState extends State { _authorController = TextEditingController(text: Preferences.getUploadAuthor); if (_imageList.length == 1) { - _titleController.text = _imageList.first.name; + _titleController.text = _imageList.first.name ?? ""; _descriptionController.text = _imageList.first.comment ?? ''; _tags = _imageList.first.tags; } diff --git a/lib/views/image/image_page.dart b/lib/views/image/image_page.dart index 6c9a46a..7769f95 100644 --- a/lib/views/image/image_page.dart +++ b/lib/views/image/image_page.dart @@ -345,7 +345,7 @@ class _ImagePageState extends State { ), Expanded( child: AutoSizeText( - '${_currentImage.name}', + '${_currentImage.name ?? ""}', softWrap: true, maxLines: 1, maxFontSize: 16.0, From 515fe5b3279617e33abb66624c587930745bc598 Mon Sep 17 00:00:00 2001 From: abrenoch Date: Mon, 6 Nov 2023 22:45:44 -0500 Subject: [PATCH 30/50] removed old files --- lib/api/TagAPI.dart | 81 ------- lib/views/FavoritesViewPage.dart | 362 ------------------------------- lib/views/TagViewPage.dart | 296 ------------------------- 3 files changed, 739 deletions(-) delete mode 100644 lib/api/TagAPI.dart delete mode 100644 lib/views/FavoritesViewPage.dart delete mode 100644 lib/views/TagViewPage.dart diff --git a/lib/api/TagAPI.dart b/lib/api/TagAPI.dart deleted file mode 100644 index 12bb799..0000000 --- a/lib/api/TagAPI.dart +++ /dev/null @@ -1,81 +0,0 @@ - -Future> getTags() async { - Map queries = { - "format":"json", - "method": "pwg.tags.getList", - }; - - Response response = await API().dio.get('ws.php', queryParameters: queries); - - try { - if (response.statusCode == 200) { - return json.decode(response.data); - } else { - return { - 'stat': 'fail', - 'result': response.statusMessage - }; - } - } catch(e) { - var error = e as DioError; - return { - 'stat': 'fail', - 'result': error.message - }; - } -} - -Future createTag(String tagName) async { - Map queries = { - "format":"json", - "method": "pwg.tags.add", - "name": tagName, - }; - - try { - Response response = await API().dio.get('ws.php', queryParameters: queries); - - if (response.statusCode == 200) { - return json.decode(response.data)["result"]; - } else { - return { - 'stat': 'fail', - 'result': response.statusMessage - }; - } - } catch(e) { - var error = e as DioError; - return { - 'stat': 'fail', - 'result': error.message - }; - } -} - -Future editTag(int tagId, String tagName) async { - Map queries = { - "format": "json", - "method": "pwg.tags.rename", - }; - FormData formData = FormData.fromMap({ - "tag_id": tagId, - "new_name": tagName, - "pwg_token": API.prefs.getString("pwg_token"), - }); - try { - Response response = await API().dio.post( - 'ws.php', - data: formData, - queryParameters: queries, - ); - if (response.statusCode == 200) { - return json.decode(response.data); - } - } catch (e) { - var error = e as DioError; - return { - 'stat': 'fail', - 'result': error.message - }; - } -} \ No newline at end of file diff --git a/lib/views/FavoritesViewPage.dart b/lib/views/FavoritesViewPage.dart deleted file mode 100644 index a58542f..0000000 --- a/lib/views/FavoritesViewPage.dart +++ /dev/null @@ -1,362 +0,0 @@ -import 'package:file_picker/file_picker.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/rendering.dart'; -import 'package:flutter/widgets.dart'; -import 'package:flutter_speed_dial/flutter_speed_dial.dart'; -import 'package:image_picker/image_picker.dart'; - -import 'package:piwigo_ng/api/ImageAPI.dart'; -import 'package:piwigo_ng/constants/SettingsConstants.dart'; -import 'package:piwigo_ng/services/UploadStatusProvider.dart'; -import 'package:piwigo_ng/views/ImageViewPage.dart'; -import 'package:piwigo_ng/views/components/content_grid.dart'; -import 'package:piwigo_ng/views/components/snackbars.dart'; - -import 'package:piwigo_ng/views/UploadGalleryViewPage.dart'; -import 'package:piwigo_ng/views/components/dialogs/dialogs.dart'; -import 'package:provider/provider.dart'; - - -class FavoritesViewPage extends StatefulWidget { - FavoritesViewPage({Key key, this.isAdmin, this.nbImages}) : super(key: key); - final bool isAdmin; - final int nbImages; - - @override - _FavoritesViewPageState createState() => _FavoritesViewPageState(); -} -class _FavoritesViewPageState extends State with SingleTickerProviderStateMixin { - GlobalKey _key = GlobalKey(); - - bool _isEditMode; - Map _selectedItems = Map(); - ScrollController _controller = ScrollController(); - - @override - void initState() { - super.initState(); - _isEditMode = false; - } - - void _getData() { - final ContentGridState contentGridState = _key.currentState; - contentGridState.reloadData(); - } - - @override - void dispose() { - super.dispose(); - } - - int _selectedPhotos() { - return _selectedItems.length; - } - - openEditMode() { - setState(() { - _isEditMode = true; - }); - } - closeEditMode() { - setState(() { - _isEditMode = false; - }); - _selectedItems.clear(); - } - - void _onEditSelection() async { - // Navigator.of(context).push( - // MaterialPageRoute(builder: (_) => EditImagesPage( - // catId: int.parse(widget.category), - // images: _selectedItems.values.toList(), - // )) - // ); - } - void _onDownloadSelection() async { - if (await confirmDownloadDialog(context, - content: appStrings(context) - .downloadImage_title(_selectedItems.length), - )) { - print('Download ${_selectedItems.keys.toList()}'); - - List selection = []; - selection.addAll(_selectedItems.values.toList()); - - setState(() { - _isEditMode = false; - _selectedItems.clear(); - }); - ScaffoldMessenger.of(context).hideCurrentSnackBar(); - ScaffoldMessenger.of(context).showSnackBar(SnackBar( - content: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text(appStrings(context).downloadingImages(selection.length)), - CircularProgressIndicator(), - ], - ), - )); - - await downloadImages(selection); - } - } - void _onMoveCopySelection() async { - int choice = await chooseMoveCopyImage(context, - content: appStrings(context).moveOrCopyImage_title(_selectedItems.length) - ); - - switch(choice) { - case 0: showDialog(context: context, - builder: (context) { - return MoveOrCopyDialog( - title: appStrings(context).moveImage_title, - subtitle: appStrings(context).moveImage_selectAlbum(_selectedItems.length, ''), - catName: appStrings(context).categoryDiscoverFavorites_title, - isImage: true, - onSelected: (item) async { - if( await confirmMoveDialog(context, - content: appStrings(context).moveImage_message(_selectedItems.length, "", item.name), - )) { - int nbMoved = await moveImages(context, - _selectedItems.values.toList(), - int.parse(item.id) - ); - ScaffoldMessenger.of(context).showSnackBar(imagesMovedSnackBar(context, nbMoved)); - Navigator.of(context).pop(); - } - }, - ); - } - ).whenComplete(() { - setState(() { - _selectedItems.clear(); - _isEditMode = false; - }); - _getData(); - }); - break; - case 1: showDialog(context: context, - builder: (context) { - return MoveOrCopyDialog( - title: appStrings(context).copyImage_title, - subtitle: appStrings(context).copyImage_selectAlbum(_selectedItems.length, ''), - // catName: widget.title, - isImage: true, - onSelected: (item) async { - if( await confirmAssignDialog(context, - content: appStrings(context).copyImage_message(_selectedItems.length, "", item.name), - )) { - int nbCopied = await assignImages(context, - _selectedItems.values.toList(), - int.parse(item.id) - ); - ScaffoldMessenger.of(context).showSnackBar(imagesAssignedSnackBar(context, nbCopied)); - Navigator.of(context).pop(); - } - }, - ); - } - ).whenComplete(() { - setState(() { - _selectedItems.clear(); - _isEditMode = false; - }); - _getData(); - }); - break; - default: break; - } - } - void _onDeleteSelection() async { - int choice = await confirmRemoveImagesFromAlbumDialog(context, - content: appStrings(context).deleteImage_message(_selectedItems.length), - count: _selectedItems.length, - ); - if(choice != -1) { - List selection = []; - selection.addAll(_selectedItems.keys.toList()); - - setState(() { - _isEditMode = false; - _selectedItems.clear(); - }); - - int nbSuccess = 0; - switch(choice) { - case 0: nbSuccess = await deleteImages(context, selection); - break; - case 1: nbSuccess = await removeImages(context, selection, '0'); //TODO: update this - break; - default: break; - } - - ScaffoldMessenger.of(context).showSnackBar(SnackBar( - content: Text(appStrings(context).deleteImageSuccess_message(nbSuccess)), - )); - - _getData(); - } - } - - void _onSelectAll() { - final ContentGridState contentGridState = _key.currentState; - setState(() { - if(_selectedItems.length == contentGridState.imageList.length) { - _selectedItems.clear(); - } else { - contentGridState.imageList.forEach((image) { - _selectedItems.putIfAbsent(image['id'], () => image); - }); - } - }); - } - - @override - Widget build(BuildContext context) { - return Scaffold( - resizeToAvoidBottomInset: true, - extendBody: true, - body: createListeners( - NestedScrollView( - controller: _controller, - headerSliverBuilder: (context, innerBoxScrolled) => [ - createAppBar(), - ], - body: ContentGrid( - key: _key, - // category: widget.category, - isAdmin: widget.isAdmin, - nbImages: widget.nbImages, - isEditMode: _isEditMode || false, - selectedItems: _selectedItems, - loadMoreImages: (int page) { - print('Loading page $page of favorite images'); - return fetchFavoriteImages(page); - }, - setEditMode: (bool isEditMode, {image}) => { - setState(() { - _isEditMode = isEditMode; - if (!_isEditMode) { - _selectedItems.clear(); - } else if (image != null) { - _selectedItems.putIfAbsent(image['id'], () => image); - } - }) - }, - deselectItem: (dynamic image) { - setState(() { - _selectedItems.remove(image['id']); - }); - }, - selectItem: (dynamic image) { - setState(() { - _selectedItems.putIfAbsent(image['id'], () => image); - }); - }, - onImageTap: (List images, int index) { - Navigator.of(context).push( - MaterialPageRoute(builder: (context) => ImageViewPage( - images: images, - index: index, - isAdmin: widget.isAdmin, - favorites: true, - )), - ).whenComplete(() { - _getData(); - }); - }, - ), - ), - ), - bottomNavigationBar: _isEditMode ? - createBottomBar() : Container(height: 0), - ); - } - - Widget createAppBar() { - ThemeData _theme = Theme.of(context); - final ContentGridState contentGridState = _key.currentState; - return SliverAppBar( - pinned: true, - snap: false, - floating: false, - centerTitle: true, - iconTheme: IconThemeData( - color: _theme.iconTheme.color, - ), - leading: IconButton( - onPressed: () { - Navigator.of(context).pop(); - }, - icon: Icon(Icons.chevron_left), - ), - title: _isEditMode ? - Text("${_selectedPhotos()}", overflow: TextOverflow.fade, softWrap: true) : - Text(appStrings(context).categoryDiscoverFavorites_title), - actions: [ - _isEditMode ? IconButton( - onPressed: _onSelectAll, - icon: _selectedItems.length == contentGridState.imageList.length ? - Icon(Icons.check_circle) : Icon(Icons.circle_outlined), - ) : SizedBox(), - _isEditMode ? IconButton( - onPressed: closeEditMode, - icon: Icon(Icons.cancel), - ) : widget.isAdmin? IconButton( - onPressed: openEditMode, - icon: Icon(Icons.touch_app_rounded), - ) : SizedBox(), - ], - ); - } - - Widget createListeners(Widget child) { - return WillPopScope( - onWillPop: () async { - if(_isEditMode) { - closeEditMode(); - return false; - } - return true; - }, - child: child, - ); - } - - Widget createBottomBar() { - ThemeData _theme = Theme.of(context); - return BottomNavigationBar( - onTap: (index) async { - if(_selectedItems.length > 0) { - switch (index) { - case 0: - _onDownloadSelection(); - break; - case 2: - _onMoveCopySelection(); - break; - default: - break; - } - } - }, - items: [ - BottomNavigationBarItem( - icon: Icon(Icons.download_rounded, color: _theme.iconTheme.color), - label: appStrings(context).imageOptions_download, - ), - BottomNavigationBarItem( - icon: Icon(Icons.favorite_border_rounded, color: _theme.errorColor), - label: "Remove from favorites", - ), - ], - backgroundColor: _theme.scaffoldBackgroundColor, - type: BottomNavigationBarType.fixed, - selectedFontSize: 14, - unselectedFontSize: 14, - showSelectedLabels: false, - showUnselectedLabels: false, - currentIndex: 0, - ); - } -} \ No newline at end of file diff --git a/lib/views/TagViewPage.dart b/lib/views/TagViewPage.dart deleted file mode 100644 index 06dfbcf..0000000 --- a/lib/views/TagViewPage.dart +++ /dev/null @@ -1,296 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter/rendering.dart'; -import 'package:flutter/widgets.dart'; - -import 'package:piwigo_ng/api/ImageAPI.dart'; -import 'package:piwigo_ng/constants/SettingsConstants.dart'; -import 'package:piwigo_ng/views/ImageViewPage.dart'; -import 'package:piwigo_ng/views/components/content_grid.dart'; - -import 'package:piwigo_ng/views/components/dialogs/dialogs.dart'; - - -class TagViewPage extends StatefulWidget { - TagViewPage({Key key, this.title, this.tag, this.isAdmin, this.nbImages}) : super(key: key); - final bool isAdmin; - final String title; - final String tag; - final int nbImages; - - @override - _TagViewPageState createState() => _TagViewPageState(); -} -class _TagViewPageState extends State with SingleTickerProviderStateMixin { - GlobalKey _key = GlobalKey(); - - bool _isEditMode; - Map _selectedItems = Map(); - ScrollController _controller = ScrollController(); - - @override - void initState() { - super.initState(); - _isEditMode = false; - } - - void _getData() { - final ContentGridState contentGridState = _key.currentState; - contentGridState.reloadData(); - } - - @override - void dispose() { - super.dispose(); - } - - int _selectedPhotos() { - return _selectedItems.length; - } - - openEditMode() { - setState(() { - _isEditMode = true; - }); - } - closeEditMode() { - setState(() { - _isEditMode = false; - }); - _selectedItems.clear(); - } - - void _onEditSelection() async { - Navigator.of(context).push( - MaterialPageRoute(builder: (_) => EditImagesPage( - images: _selectedItems.values.toList(), - )) - ); - } - void _onDownloadSelection() async { - if (await confirmDownloadDialog(context, - content: appStrings(context) - .downloadImage_title(_selectedItems.length), - )) { - print('Download ${_selectedItems.keys.toList()}'); - - List selection = []; - selection.addAll(_selectedItems.values.toList()); - - setState(() { - _isEditMode = false; - _selectedItems.clear(); - }); - ScaffoldMessenger.of(context).hideCurrentSnackBar(); - ScaffoldMessenger.of(context).showSnackBar(SnackBar( - content: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text(appStrings(context).downloadingImages(selection.length)), - CircularProgressIndicator(), - ], - ), - )); - - await downloadImages(selection); - } - } - void _onDeleteSelection() async { - int choice = await confirmRemoveImagesFromAlbumDialog(context, - content: appStrings(context).deleteImage_message(_selectedItems.length), - count: _selectedItems.length, - ); - if(choice != -1) { - List selection = []; - selection.addAll(_selectedItems.keys.toList()); - - setState(() { - _isEditMode = false; - _selectedItems.clear(); - }); - - int nbSuccess = 0; - switch(choice) { - case 0: nbSuccess = await deleteImages(context, selection); - break; - case 1: nbSuccess = await removeImages(context, selection, '0'); //TODO: update this - break; - default: break; - } - - ScaffoldMessenger.of(context).showSnackBar(SnackBar( - content: Text(appStrings(context).deleteImageSuccess_message(nbSuccess)), - )); - - _getData(); - } - } - - void _onSelectAll() { - final ContentGridState contentGridState = _key.currentState; - setState(() { - if(_selectedItems.length == contentGridState.imageList.length) { - _selectedItems.clear(); - } else { - contentGridState.imageList.forEach((image) { - _selectedItems.putIfAbsent(image['id'], () => image); - }); - } - }); - } - - @override - Widget build(BuildContext context) { - return Scaffold( - resizeToAvoidBottomInset: true, - extendBody: true, - body: createListeners( - NestedScrollView( - controller: _controller, - headerSliverBuilder: (context, innerBoxScrolled) => [ - createAppBar(), - ], - body: ContentGrid( - key: _key, - // category: widget.category, - isAdmin: widget.isAdmin, - nbImages: widget.nbImages, - isEditMode: _isEditMode || false, - selectedItems: _selectedItems, - loadMoreImages: (int page) { - print('Loading page $page of tag ${widget.tag}'); - return fetchTagImages(widget.tag, page); - }, - setEditMode: (bool isEditMode, {image}) => { - setState(() { - _isEditMode = isEditMode; - if (!_isEditMode) { - _selectedItems.clear(); - } else if (image != null) { - _selectedItems.putIfAbsent(image['id'], () => image); - } - }) - }, - deselectItem: (dynamic image) { - setState(() { - _selectedItems.remove(image['id']); - }); - }, - selectItem: (dynamic image) { - setState(() { - _selectedItems.putIfAbsent(image['id'], () => image); - }); - }, - onImageTap: (List images, int index) { - Navigator.of(context).push( - MaterialPageRoute(builder: (context) => ImageViewPage( - images: images, - index: index, - isAdmin: widget.isAdmin, - tag: widget.tag, - )), - ).whenComplete(() { - _getData(); - }); - }, - ), - ), - ), - bottomNavigationBar: _isEditMode ? - createBottomBar() : Container(height: 0), - ); - } - - Widget createAppBar() { - ThemeData _theme = Theme.of(context); - final ContentGridState contentGridState = _key.currentState; - return SliverAppBar( - pinned: true, - snap: false, - floating: false, - centerTitle: true, - iconTheme: IconThemeData( - color: _theme.iconTheme.color, - ), - leading: IconButton( - onPressed: () { - Navigator.of(context).pop(); - }, - icon: Icon(Icons.chevron_left), - ), - title: _isEditMode ? - Text("${_selectedPhotos()}", overflow: TextOverflow.fade, softWrap: true) : - Text(widget.title), - actions: [ - _isEditMode ? IconButton( - onPressed: _onSelectAll, - icon: _selectedItems.length == contentGridState.imageList.length ? - Icon(Icons.check_circle) : Icon(Icons.circle_outlined), - ) : SizedBox(), - _isEditMode ? IconButton( - onPressed: closeEditMode, - icon: Icon(Icons.cancel), - ) : widget.isAdmin? IconButton( - onPressed: openEditMode, - icon: Icon(Icons.touch_app_rounded), - ) : SizedBox(), - ], - ); - } - - Widget createListeners(Widget child) { - return WillPopScope( - onWillPop: () async { - if(_isEditMode) { - closeEditMode(); - return false; - } - return true; - }, - child: child, - ); - } - - Widget createBottomBar() { - ThemeData _theme = Theme.of(context); - return BottomNavigationBar( - onTap: (index) async { - if(_selectedItems.length > 0) { - switch (index) { - case 0: - _onEditSelection(); - break; - case 1: - _onDownloadSelection(); - break; - case 3: - _onDeleteSelection(); - break; - default: - break; - } - } - }, - items: [ - BottomNavigationBarItem( - icon: Icon(Icons.edit, color: _theme.iconTheme.color), - label: appStrings(context).imageOptions_edit, - ), - BottomNavigationBarItem( - icon: Icon(Icons.download_rounded, color: _theme.iconTheme.color), - label: appStrings(context).imageOptions_download, - ), - BottomNavigationBarItem( - icon: Icon(Icons.delete_outline, color: _theme.errorColor), - label: appStrings(context).deleteImage_delete, - ), - ], - backgroundColor: _theme.scaffoldBackgroundColor, - type: BottomNavigationBarType.fixed, - selectedFontSize: 14, - unselectedFontSize: 14, - showSelectedLabels: false, - showUnselectedLabels: false, - currentIndex: 0, - ); - } -} \ No newline at end of file From 81f87722ec3dabe31bf8ff333995c80c4f19e780 Mon Sep 17 00:00:00 2001 From: abrenoch Date: Mon, 6 Nov 2023 22:46:32 -0500 Subject: [PATCH 31/50] added editTag to tags.dart --- lib/network/tags.dart | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/lib/network/tags.dart b/lib/network/tags.dart index c7ba74c..d559e99 100644 --- a/lib/network/tags.dart +++ b/lib/network/tags.dart @@ -4,6 +4,7 @@ import 'package:dio/dio.dart'; import 'package:flutter/material.dart'; import 'package:piwigo_ng/models/tag_model.dart'; import 'package:piwigo_ng/network/api_error.dart'; +import 'package:piwigo_ng/services/preferences_service.dart'; import 'api_client.dart'; @@ -60,3 +61,34 @@ Future> createTag(String name) async { } return ApiResponse(error: ApiErrors.error); } + +Future editTag(int tagId, String tagName) async { + Map queries = { + "format": "json", + "method": "pwg.tags.rename", + }; + FormData formData = FormData.fromMap({ + "tag_id": tagId, + "new_name": tagName, + 'pwg_token': appPreferences.getString('PWG_TOKEN'), + }); + Response response = await ApiClient.post( + data: formData, + queryParameters: queries + ); + + try { + if (response.statusCode == 200) { + var data = json.decode(response.data); + if (data['stat'] == 'fail') { + return ApiResponse(error: ApiErrors.error); + } + return ApiResponse(data: true); + } + } on DioError catch (e) { + debugPrint('Get tags: ${e.message}'); + } on Error catch (e) { + debugPrint('Get tags: $e\n${e.stackTrace}'); + } + return ApiResponse(error: ApiErrors.error); +} \ No newline at end of file From 69447add5128d6138d6f4bdf7aa39477601f7be7 Mon Sep 17 00:00:00 2001 From: abrenoch Date: Mon, 6 Nov 2023 22:47:12 -0500 Subject: [PATCH 32/50] added fetchTagImages to images.dart --- lib/network/images.dart | 43 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/lib/network/images.dart b/lib/network/images.dart index 1cee573..7fe1c6f 100644 --- a/lib/network/images.dart +++ b/lib/network/images.dart @@ -167,6 +167,49 @@ Future> fetchFavorites([ return ApiResponse(error: ApiErrors.error); } +Future> fetchTagImages(int tagID, [int page = 0]) async { + Map query = { + "format": "json", + "method": "pwg.tags.getImages", + "tag_id": tagID.toString(), + "per_page": "100", + "page": page.toString(), + }; + + try { + Response response = await ApiClient.get(queryParameters: query); + + if (response.statusCode == 200) { + final Map result = json.decode(response.data); + if (result['stat'] == 'fail') { + return ApiResponse(data: { + 'total_count': 0, + 'images': [], + }); + } + final jsonImages = result['result']['images']; + List images = List.from( + jsonImages.map((image) { + image['tags'] = null; + return ImageModel.fromJson(image); + }), + ); + + print(result['result']['paging']); + + return ApiResponse(data: { + 'total_count': result['result']['paging']['total_count'], + 'images': images, + }); + } + } on DioError catch (e) { + debugPrint('Fetch tag images: ${e.message}'); + } on Error catch (e) { + debugPrint('Fetch tag images: ${e.stackTrace}'); + } + return ApiResponse(error: ApiErrors.error); +} + Future pickDirectoryPath() async { return await FilePicker.platform.getDirectoryPath(); } From 3109b42bfba729ae8b2dedcacc07dee913db9998 Mon Sep 17 00:00:00 2001 From: abrenoch Date: Mon, 6 Nov 2023 22:48:17 -0500 Subject: [PATCH 33/50] added image_tags_page --- lib/views/image/image_tags_page.dart | 367 +++++++++++++++++++++++++++ 1 file changed, 367 insertions(+) create mode 100644 lib/views/image/image_tags_page.dart diff --git a/lib/views/image/image_tags_page.dart b/lib/views/image/image_tags_page.dart new file mode 100644 index 0000000..c16ebac --- /dev/null +++ b/lib/views/image/image_tags_page.dart @@ -0,0 +1,367 @@ +import 'package:flutter/material.dart'; +import 'package:piwigo_ng/components/lists/image_grid_view.dart'; +import 'package:piwigo_ng/components/popup_list_item.dart'; +import 'package:piwigo_ng/models/album_model.dart'; +import 'package:piwigo_ng/models/image_model.dart'; +import 'package:piwigo_ng/models/tag_model.dart'; +import 'package:piwigo_ng/network/api_error.dart'; +import 'package:piwigo_ng/network/images.dart'; +import 'package:piwigo_ng/services/preferences_service.dart'; +import 'package:piwigo_ng/utils/image_actions.dart'; +import 'package:piwigo_ng/utils/localizations.dart'; +import 'package:piwigo_ng/utils/settings.dart'; +import 'package:piwigo_ng/views/image/image_page.dart'; +import 'package:pull_to_refresh/pull_to_refresh.dart'; + +class ImageTagsPage extends StatefulWidget { + const ImageTagsPage({ + Key? key, + required this.tag, + this.isAdmin = false + }) : super(key: key); + + static const String routeName = '/images/tags'; + + final TagModel tag; + final bool isAdmin; + + @override + State createState() => _ImageTagsPageState(); +} + +class _ImageTagsPageState extends State { + final RefreshController _refreshController = + RefreshController(initialRefresh: false); + final ScrollController _scrollController = ScrollController(); + + late final Future> _imageFuture; + List? _imageList; + List _selectedList = []; + + int _nbImages = 0; + int _page = 0; + + @override + void initState() { + super.initState(); + _imageFuture = fetchTagImages(widget.tag.id).then((response) { + if (response.hasData) { + setState(() { + final int? total = response.data!['total_count']; + if (total != null) { + _nbImages = total; + } + _imageList = response.data!['images'].cast() ?? []; + }); + } + return response; + }); + } + + @override + void dispose() { + _refreshController.dispose(); + _scrollController.dispose(); + super.dispose(); + } + + bool get _hasNonFavorites => + _selectedList.where((image) => !image.favorite).isNotEmpty; + + Future _onWillPop() async { + if (_selectedList.isNotEmpty) { + setState(() { + _selectedList.clear(); + }); + return false; + } + return true; + } + + Future _onRefresh() async { + final ApiResponse result = await fetchTagImages(widget.tag.id); + if (!result.hasData) { + _refreshController.refreshFailed(); + await Future.delayed(const Duration(milliseconds: 500)); + return _refreshController.refreshCompleted(); + } + final int? total = result.data!['total_count']; + setState(() { + _page = 0; + if (total != null) { + _nbImages = total; + } + _imageList = result.data!['images'].cast() ?? []; + _selectedList.clear(); + }); + return _refreshController.refreshCompleted(); + } + + Future _loadMoreImages() async { + if (_imageList == null || _nbImages <= _imageList!.length) return; + ApiResponse result = await fetchTagImages(widget.tag.id, _page + 1); + if (result.hasError || !result.hasData) { + _refreshController.loadFailed(); + await Future.delayed(const Duration(milliseconds: 500)); + return _refreshController.loadComplete(); + } + final int? total = result.data!['total_count']; + setState(() { + if (total != null) { + _nbImages = total; + } + _imageList!.addAll(result.data!['images'].cast() ?? []); + }); + _refreshController.loadComplete(); + } + + void _onTapPhoto(ImageModel image) => Navigator.of(context).pushNamed( + ImagePage.routeName, + arguments: { + 'images': _imageList, + 'startId': image.id, + 'album': AlbumModel( + id: -1, + name: '', + nbImages: _nbImages, + nbTotalImages: _nbImages, + ), + }, + ).then((images) { + if (images == null || images is! List) return; + setState(() { + _imageList = images; + _page = + ((images.length - 1) / Settings.defaultElementPerPage).floor(); + }); + }); + void _onEditPhotos() => onEditPhotos(context, _selectedList).then((success) { + if (success == true) { + _selectedList.clear(); + _onRefresh(); + } + }); + void _onLikePhotos() => + onLikePhotos(_selectedList, false).whenComplete(() => _onRefresh()); + _onDeletePhotos() => onDeletePhotos(context, _selectedList).then((success) { + if (success) _onRefresh(); + }); + + @override + Widget build(BuildContext context) { + return WillPopScope( + onWillPop: _onWillPop, + child: Scaffold( + body: SafeArea( + child: SmartRefresher( + controller: _refreshController, + scrollController: _scrollController, + enablePullUp: _imageList != null && _nbImages > _imageList!.length, + onLoading: _loadMoreImages, + onRefresh: _onRefresh, + header: MaterialClassicHeader( + backgroundColor: Theme.of(context).cardColor, + color: Theme.of(context).colorScheme.secondary, + ), + footer: ClassicFooter( + loadingText: appStrings.loadingHUD_label, + noDataText: appStrings.categoryImageList_noDataError, + failedText: appStrings.errorHUD_label, + idleText: '', + canLoadingText: appStrings.loadMoreHUD_label, + ), + child: CustomScrollView( + controller: _scrollController, + slivers: [ + _appBar, + SliverToBoxAdapter( + child: _taggedImageGrid, + ), + ], + ), + ), + ), + bottomNavigationBar: AnimatedSlide( + duration: const Duration(milliseconds: 300), + curve: Curves.easeInOut, + offset: _selectedList.isEmpty ? Offset(0, 1) : Offset.zero, + child: _bottomBar, + ), + ), + ); + } + + Widget get _appBar { + Orientation orientation = MediaQuery.of(context).orientation; + return SliverAppBar( + pinned: true, + centerTitle: false, + titleSpacing: 0.0, + leading: BackButton( + onPressed: () => Navigator.of(context).pop(), + ), + // title: Text(appStrings.categoryDiscoverFavorites_title), + title: Text(widget.tag.name), + actions: [ + if (_selectedList.isNotEmpty) + IconButton( + onPressed: () => setState(() { + _selectedList.clear(); + }), + tooltip: appStrings.categoryImageList_deselectButton, + icon: Icon(Icons.cancel), + ), + if (orientation == Orientation.landscape) ..._actions, + if (widget.isAdmin) + PopupMenuButton( + tooltip: appStrings.imageOptions_title, + enabled: _selectedList.isNotEmpty, + position: PopupMenuPosition.under, + itemBuilder: (context) => [ + PopupMenuItem( + onTap: () => Future.delayed( + const Duration(seconds: 0), + () => share(_selectedList), + ), + child: PopupListItem( + icon: Icons.share, + text: appStrings.imageOptions_share, + ), + ), + if (Preferences.getUserStatus != 'guest') + PopupMenuItem( + onTap: () => Future.delayed( + const Duration(seconds: 0), + _onLikePhotos, + ), + child: PopupListItem( + icon: Icons.remove_circle, + text: appStrings.imageOptions_removeFavorites, + color: Theme.of(context).colorScheme.error, + ), + ), + PopupMenuItem( + onTap: () => Future.delayed( + const Duration(seconds: 0), + () => downloadImages(_selectedList), + ), + child: PopupListItem( + icon: Icons.download, + text: appStrings.downloadImage_title(_selectedList.length), + ), + ), + ], + ), + ], + ); + } + + Widget get _taggedImageGrid { + return FutureBuilder>( + future: _imageFuture, + builder: (context, snapshot) { + if (snapshot.hasData) { + if (!snapshot.data!.hasData) { + return Center( + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Text(appStrings.categoryImageList_noDataError), + ), + ); + } + return _buildImageGrid(snapshot); + } + return Center(child: CircularProgressIndicator()); + }, + ); + } + + Widget _buildImageGrid(AsyncSnapshot snapshot) { + final ApiResponse result = snapshot.data!; + if (_imageList == null) { + _nbImages = result.data!['total_count']; + _imageList = result.data!['images'].cast() ?? []; + } + + _selectedList = + _imageList!.where((image) => _selectedList.contains(image)).toList(); + + if (_imageList!.isEmpty) { + return Center( + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Text(appStrings.noImages), + ), + ); + } + return ImageGridView( + imageList: _imageList!, + selectedList: _selectedList, + onSelectImage: (image) => setState(() { + _selectedList.add(image); + }), + onDeselectImage: (image) => setState(() { + _selectedList.remove(image); + }), + onTapImage: _onTapPhoto, + ); + } + + Widget get _bottomBar { + return OrientationBuilder(builder: (context, orientation) { + return AnimatedContainer( + duration: const Duration(milliseconds: 300), + curve: Curves.easeInOut, + height: _selectedList.isEmpty || orientation == Orientation.landscape + ? 0 + : 56.0, + child: BottomAppBar( + height: 56.0, + child: Row( + children: + _actions.map((action) => Expanded(child: action)).toList(), + ), + ), + ); + }); + } + + List get _actions { + List adminActions = [ + IconButton( + onPressed: _onEditPhotos, + tooltip: appStrings.imageOptions_edit, + icon: Icon(Icons.edit), + ), + IconButton( + onPressed: _onDeletePhotos, + tooltip: appStrings.deleteImage_delete, + icon: Icon(Icons.delete), + ), + ]; + List userActions = [ + IconButton( + onPressed: () => share(_selectedList), + tooltip: appStrings.imageOptions_share, + icon: Icon(Icons.share), + ), + if (Preferences.getUserStatus != 'guest') // Todo: enum roles + IconButton( + onPressed: _onLikePhotos, + tooltip: _hasNonFavorites + ? appStrings.imageOptions_addFavorites + : appStrings.imageOptions_removeFavorites, + isSelected: !_hasNonFavorites, + selectedIcon: Icon(Icons.favorite), + icon: Icon(Icons.favorite_border), + ), + IconButton( + onPressed: () => downloadImages(_selectedList), + tooltip: appStrings.imageOptions_download, + icon: Icon(Icons.download), + ), + ]; + + return widget.isAdmin ? adminActions : userActions; + } +} From 40f9fd715746cf0f781adb4043e8d983cdab5329 Mon Sep 17 00:00:00 2001 From: abrenoch Date: Mon, 6 Nov 2023 22:48:45 -0500 Subject: [PATCH 34/50] added image_tags_page route --- lib/app.dart | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lib/app.dart b/lib/app.dart index e7f02d0..8224c75 100644 --- a/lib/app.dart +++ b/lib/app.dart @@ -16,6 +16,7 @@ import 'package:piwigo_ng/views/image/edit_image_page.dart'; import 'package:piwigo_ng/views/image/image_favorites_page.dart'; import 'package:piwigo_ng/views/image/image_page.dart'; import 'package:piwigo_ng/views/image/image_search_page.dart'; +import 'package:piwigo_ng/views/image/image_tags_page.dart'; import 'package:piwigo_ng/views/image/video_player_page.dart'; import 'package:piwigo_ng/views/settings/auto_upload_page.dart'; import 'package:piwigo_ng/views/settings/privacy_policy_page.dart'; @@ -147,6 +148,14 @@ Route generateRoute(RouteSettings settings) { ), settings: settings, ); + case ImageTagsPage.routeName: + return MaterialPageRoute( + builder: (_) => ImageTagsPage( + isAdmin: arguments['isAdmin'] ?? isAdmin, + tag: arguments["tag"], + ), + settings: settings, + ); case ImageFavoritesPage.routeName: return MaterialPageRoute( builder: (_) => ImageFavoritesPage( From 987d866db3d939e48b2b4fa95bb62f62a4a4abf1 Mon Sep 17 00:00:00 2001 From: abrenoch Date: Mon, 6 Nov 2023 22:49:35 -0500 Subject: [PATCH 35/50] added showChooseTagSheet to root_search_app_bar.dart --- lib/components/appbars/root_search_app_bar.dart | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/lib/components/appbars/root_search_app_bar.dart b/lib/components/appbars/root_search_app_bar.dart index 1208940..f5807b4 100644 --- a/lib/components/appbars/root_search_app_bar.dart +++ b/lib/components/appbars/root_search_app_bar.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:piwigo_ng/components/dialogs/tags_dialogs.dart'; import 'package:piwigo_ng/components/notification_dot.dart'; import 'package:piwigo_ng/components/popup_list_item.dart'; import 'package:piwigo_ng/services/preferences_service.dart'; @@ -147,6 +148,15 @@ class _RootSearchAppBarState extends State { ], ), ), + PopupMenuItem( + onTap: () => ( + showChooseTagSheet(context) + ), + child: PopupListItem( + icon: Icons.local_offer_outlined, + text: appStrings.tags, + ), + ), if (Preferences.getUserStatus != 'guest') PopupMenuItem( onTap: () => Future.delayed( From d4781522d94cff9ce238858d3a6ce2f87f0101bd Mon Sep 17 00:00:00 2001 From: abrenoch Date: Mon, 6 Nov 2023 22:49:59 -0500 Subject: [PATCH 36/50] bug fix in image_favorites_page.dart --- lib/views/image/image_favorites_page.dart | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/lib/views/image/image_favorites_page.dart b/lib/views/image/image_favorites_page.dart index 63636ab..1da49d0 100644 --- a/lib/views/image/image_favorites_page.dart +++ b/lib/views/image/image_favorites_page.dart @@ -38,7 +38,18 @@ class _ImageFavoritesPageState extends State { @override void initState() { super.initState(); - _imageFuture = fetchFavorites(); + _imageFuture = fetchFavorites().then((response) { + if (response.hasData) { + setState(() { + final int? total = response.data!['total_count']; + if (total != null) { + _nbImages = total; + } + _imageList = response.data!['images'].cast() ?? []; + }); + } + return response; + }); } @override From a79ee17c4418b3a98228f9c32eaad528ccd3f520 Mon Sep 17 00:00:00 2001 From: abrenoch Date: Mon, 6 Nov 2023 22:51:22 -0500 Subject: [PATCH 37/50] unused files --- lib/views/components/content_grid.dart | 459 --------------------- lib/views/components/view_menu_button.dart | 74 ---- 2 files changed, 533 deletions(-) delete mode 100644 lib/views/components/content_grid.dart delete mode 100644 lib/views/components/view_menu_button.dart diff --git a/lib/views/components/content_grid.dart b/lib/views/components/content_grid.dart deleted file mode 100644 index 78e34aa..0000000 --- a/lib/views/components/content_grid.dart +++ /dev/null @@ -1,459 +0,0 @@ -import 'package:auto_size_text/auto_size_text.dart'; -import 'package:file_picker/file_picker.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/rendering.dart'; -import 'package:flutter/widgets.dart'; -import 'package:flutter_speed_dial/flutter_speed_dial.dart'; -import 'package:image_picker/image_picker.dart'; -import 'dart:async'; - -import 'package:piwigo_ng/api/API.dart'; -import 'package:piwigo_ng/api/CategoryAPI.dart'; -import 'package:piwigo_ng/constants/SettingsConstants.dart'; -import 'package:piwigo_ng/services/OrientationService.dart'; -import 'package:piwigo_ng/views/components/list_item.dart'; -import 'package:piwigo_ng/views/components/snackbars.dart'; - -import 'package:piwigo_ng/views/UploadGalleryViewPage.dart'; -import 'package:piwigo_ng/views/components/dialogs/dialogs.dart'; - - -class ContentGrid extends StatefulWidget { - ContentGrid({Key key, - this.title, this.category, this.isAdmin, this.nbImages, this.isEditMode, - @required this.selectedItems, @required this.setEditMode, @required this.loadMoreImages, - @required this.selectItem, @required this.deselectItem, @required this.onImageTap, - }) : super(key: key); - final bool isAdmin; - final String title; - final String category; - final int nbImages; - final Function(int) loadMoreImages; - final Function(bool, {dynamic image}) setEditMode; - final Function(dynamic) selectItem; - final Function(dynamic) deselectItem; - final Function(List, int) onImageTap; - final bool isEditMode; - final Map selectedItems; - - @override - ContentGridState createState() => ContentGridState(); -} -class ContentGridState extends State with SingleTickerProviderStateMixin { - Future> _albumsFuture; - Future> _imagesFuture; - - int _page; - int _nbImages; - List imageList = []; - - @override - void initState() { - _getData(); - super.initState(); - _page = 0; - _nbImages = widget.nbImages ?? 0; - } - - void _getData() { - if (widget.category != null && widget.category.isNotEmpty) { - _albumsFuture = fetchAlbums(widget.category); - } else { - _albumsFuture = Future>.value({ - 'stat': 'success', - 'result': {'categories': []} - }); - } - _imagesFuture = widget.loadMoreImages(0); - } - - @override - void dispose() { - super.dispose(); - } - - bool _isSelected(int id) { - return widget.selectedItems.keys.contains(id); - } - // int _selectedPhotos() { - // return widget.selectedItems.length; - // } - - showMore() async { - _page++; - var response = await widget.loadMoreImages(_page); - if(response['stat'] == 'fail') { - ScaffoldMessenger.of(context).showSnackBar( - errorSnackBar(context, response['result']) - ); - } else { - var newListPage = response['result']['images']; - imageList.addAll(newListPage); - } - setState(() { - print('Fetch images of page $_page'); - // _getData(); - }); - } - - reloadData() async { - imageList.clear(); - setState(() { - _page = 0; - _getData(); - }); - } - - openEditMode(dynamic image) { - widget.setEditMode(true, image: image); - } - - closeEditMode() { - widget.setEditMode(false); - } - - Future _onRefresh() { - setState(() { - _page = 0; - _getData(); - }); - return Future.delayed(Duration(milliseconds: 500)); - } - - handleAlbumSnapshot(AsyncSnapshot albumSnapshot) { - var albums = albumSnapshot.data['result']['categories']; - // int nbImages = _nbImages; - if(albums.length > 0 && albums[0]["id"].toString() == widget.category) { - // nbImages = albums[0]["total_nb_images"]; - // _nbImages = nbImages; - _nbImages = albums[0]["total_nb_images"]; - } - albums.removeWhere((category) => - (category["id"].toString() == widget.category) - ); - return albums; - } - handleImagesSnapshot(AsyncSnapshot imagesSnapshot) { - imageList.clear(); - imageList.addAll(imagesSnapshot.data['result']['images']); - if (_nbImages == 0 && imagesSnapshot.data['result'].containsKey('paging')) { - if (imagesSnapshot.data['result']['paging'].containsKey('total_count')) { - if (imagesSnapshot.data['result']['paging']['total_count'] is String) { - _nbImages = int.parse(imagesSnapshot.data['result']['paging']['total_count']); - } else { - _nbImages = imagesSnapshot.data['result']['paging']['total_count']; - } - } else { - _nbImages = imagesSnapshot.data['result']['paging']['count']; - } - } - } - - @override - Widget build(BuildContext context) { - return createFutureBuilders(); - } - - Widget createFutureBuilders() { - return FutureBuilder>( - future: _albumsFuture, // Albums of the list - builder: (BuildContext context, AsyncSnapshot albumSnapshot) { - if (albumSnapshot.hasData) { - if(albumSnapshot.data['stat'] == 'fail') { - return Center( - child: Text(appStrings(context).categoryImageList_noDataError), - ); - } - var albums = handleAlbumSnapshot(albumSnapshot); - return FutureBuilder>( - future: _imagesFuture, - builder: (BuildContext context, AsyncSnapshot imagesSnapshot) { - if (imagesSnapshot.hasData) { - if (imageList.isEmpty || _page == 0) { - if(imagesSnapshot.data['stat'] == 'fail') { - return Center(child: Text(appStrings(context).categoryImageList_noDataError)); - } - handleImagesSnapshot(imagesSnapshot); - } - return createPageContent(albums); - } else { - return Center( - child: CircularProgressIndicator(), - ); - } - }, - ); - } else { - return Center( - child: CircularProgressIndicator(), - ); - } - } - ); - } - - Widget createUploadActionButton() { - ThemeData _theme = Theme.of(context); - return SpeedDial( - spaceBetweenChildren: 10, - childMargin: EdgeInsets.only(bottom: 17, right: 10), - animatedIcon: AnimatedIcons.menu_close, - animatedIconTheme: IconThemeData(size: 22.0), - closeManually: false, - curve: Curves.bounceIn, - backgroundColor: _theme.floatingActionButtonTheme.backgroundColor, - foregroundColor: _theme.floatingActionButtonTheme.foregroundColor, - overlayColor: Colors.black, - elevation: 5.0, - overlayOpacity: 0.5, - shape: CircleBorder(), - children: [ - SpeedDialChild( - elevation: 5, - labelWidget: Text(appStrings(context).createNewAlbum_title, style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold, color: Colors.white)), - child: Icon(Icons.create_new_folder), - backgroundColor: _theme.floatingActionButtonTheme.backgroundColor, - foregroundColor: _theme.floatingActionButtonTheme.foregroundColor, - onTap: () async { - showDialog( - context: context, - builder: (BuildContext context) { - return CreateCategoryDialog(catId: widget.category); - } - ).whenComplete(() { - setState(() { - _getData(); - }); - }); - }, - ), - SpeedDialChild( - elevation: 5, - labelWidget: Text(appStrings(context).categoryUpload_images, style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold, color: Colors.white)), - child: Icon(Icons.add_to_photos), - backgroundColor: _theme.floatingActionButtonTheme.backgroundColor, - foregroundColor: _theme.floatingActionButtonTheme.foregroundColor, - onTap: () async { - try { - ScaffoldMessenger.of(context).removeCurrentSnackBar(); - ScaffoldMessenger.of(context).showSnackBar(SnackBar( - content: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - mainAxisSize: MainAxisSize.min, - children: [ - Text(appStrings(context).loadingHUD_label), - CircularProgressIndicator(), - ], - ), - duration: Duration(days: 365), - )); - final List images = ((await FilePicker.platform.pickFiles( - type: FileType.media, - allowMultiple: true, - )) ?.files ?? []).map((e) => XFile(e.path, name: e.name, bytes: e.bytes)).toList(); - ScaffoldMessenger.of(context).removeCurrentSnackBar(); - if(images.isNotEmpty) { - Navigator.push(context, MaterialPageRoute( - builder: (context) => UploadGalleryViewPage(imageData: images, category: widget.category) - )).whenComplete(() { - setState(() { - print('After upload'); // refresh - }); - }); - } - } catch (e) { - print('${e.toString()}'); - } - } - ), - SpeedDialChild( - elevation: 5, - labelWidget: Text(appStrings(context).categoryUpload_take, style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold, color: Colors.white)), - child: Icon(Icons.photo_camera_rounded), - backgroundColor: _theme.floatingActionButtonTheme.backgroundColor, - foregroundColor: _theme.floatingActionButtonTheme.foregroundColor, - onTap: () async { - try { - ScaffoldMessenger.of(context).removeCurrentSnackBar(); - ScaffoldMessenger.of(context).showSnackBar(SnackBar( - content: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - mainAxisSize: MainAxisSize.min, - children: [ - Text(appStrings(context).loadingHUD_label), - CircularProgressIndicator(), - ], - ), - duration: Duration(days: 365), - )); - final XFile image = await ImagePicker().pickImage(source: ImageSource.camera); - ScaffoldMessenger.of(context).removeCurrentSnackBar(); - if(image != null) { - Navigator.push(context, MaterialPageRoute( - builder: (context) => UploadGalleryViewPage(imageData: [image], category: widget.category) - )).whenComplete(() { - setState(() { - print('After upload'); // refresh - }); - }); - } - } catch (e) { - print('Dio error ${e.toString()}'); - } - } - ), - ], - ); - } - - Widget createPageContent(dynamic albums) { - ThemeData _theme = Theme.of(context); - - int albumCrossAxisCount = MediaQuery.of(context).size.width <= Constants.albumMinWidth ? 1 - : (MediaQuery.of(context).size.width/Constants.albumMinWidth).round(); - - return RefreshIndicator( - displacement: 20, - notificationPredicate: (notification) { - return notification.metrics.atEdge; - }, - onRefresh: _onRefresh, - child: SingleChildScrollView( - child: Column( - children: [ - albums.length > 0 ? - GridView.builder( - gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: albumCrossAxisCount, - mainAxisSpacing: 10, - crossAxisSpacing: 10, - childAspectRatio: albumGridAspectRatio(context), - ), - padding: EdgeInsets.all(10), - itemCount: albums.length, - shrinkWrap: true, - physics: NeverScrollableScrollPhysics(), - itemBuilder: (BuildContext context, int index) { - var album = albums[index]; - return AlbumListItem(album, - isAdmin: widget.isAdmin, - onClose: () { - setState(() { - _getData(); - }); - }, - onOpen: closeEditMode, - ); - }, - ) : Center(), - imageList.length > 0 ? - GridView.builder( - gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: getImageCrossAxisCount(context), - mainAxisSpacing: 3.0, - crossAxisSpacing: 3.0, - ), - padding: EdgeInsets.symmetric(horizontal: 5), - itemCount: imageList.length, - shrinkWrap: true, - physics: NeverScrollableScrollPhysics(), - itemBuilder: (BuildContext context, int index) { - var image = imageList[index]; - return InkWell( - onLongPress: widget.isEditMode ? () { - setState(() { - _isSelected(image['id']) ? - widget.deselectItem(image) : - widget.selectItem(image); - }); - } : widget.isAdmin ? () { - openEditMode(image); - } : () {}, - onTap: () { - widget.isEditMode ? - _isSelected(image['id']) ? - widget.deselectItem(image) : - widget.selectItem(image) : - widget.onImageTap(imageList, index); - }, - child: AnimatedContainer( - duration: Duration(milliseconds: 200), - decoration: BoxDecoration( - color: Colors.white, - border: _isSelected(image['id']) ? - Border.all(width: 5, color: _theme.colorScheme.primary) : - Border.all(width: 0, color: Colors.white), - ), - child: Stack( - alignment: Alignment.center, - children: [ - Container( - width: double.infinity, - height: double.infinity, - child: Image.network(imageList[index]["derivatives"][API.prefs.getString('thumbnail_size')]["url"], - fit: BoxFit.cover, - ), - ), - _isSelected(image['id']) ? Container( - width: double.infinity, - height: double.infinity, - color: Color(0x80000000), - ) : Center(), - /* - widget.isEditMode? Align( - alignment: Alignment.topRight, - child: Padding( - padding: EdgeInsets.all(5), - child: _isSelected(image['id']) ? - Icon(Icons.check_circle, color: _theme.floatingActionButtonTheme.backgroundColor) : - Icon(Icons.check_circle_outline, color: _theme.disabledColor), - ), - ) : Center(), - - */ - API.prefs.getBool('show_thumbnail_title')? Align( - alignment: Alignment.bottomCenter, - child: Container( - width: double.infinity, - color: Color(0x80ffffff), - child: AutoSizeText('${image['name']}', - overflow: TextOverflow.ellipsis, - maxLines: 1, - style: TextStyle(fontSize: 12), - maxFontSize: 14, minFontSize: 7, - textAlign: TextAlign.center, - ), - ), - ) : Center(), - ], - ), - ), - ); - }, - ) : Center(), - _nbImages > (_page+1)*100 ? GestureDetector( - onTap: () { - showMore(); - }, - child: Padding( - padding: EdgeInsets.all(10), - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text(appStrings(context).showMore(_nbImages-((_page+1)*100)), style: TextStyle(fontSize: 14, color: _theme.disabledColor)), - ], - ), - ), - ) : Center(), - Center( - child: Container( - padding: EdgeInsets.all(10), - child: Text(appStrings(context).imageCount(_nbImages), style: TextStyle(fontSize: 20, color: _theme.textTheme.bodyText2.color, fontWeight: FontWeight.w300)), - ), - ) - ], - ), - ), - ); - } -} \ No newline at end of file diff --git a/lib/views/components/view_menu_button.dart b/lib/views/components/view_menu_button.dart deleted file mode 100644 index 67f6d6f..0000000 --- a/lib/views/components/view_menu_button.dart +++ /dev/null @@ -1,74 +0,0 @@ -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; -import 'package:piwigo_ng/constants/SettingsConstants.dart'; -import 'package:piwigo_ng/views/FavoritesViewPage.dart'; -import 'package:piwigo_ng/views/components/dialogs/dialogs.dart'; - -enum ViewPopupMenuOptions { favorites, tags, top_viewed, top_rated } - -class ViewPopupMenuButton extends StatelessWidget { - const ViewPopupMenuButton({Key key, this.isAdmin = false}) : super(key: key); - - final bool isAdmin; - - @override - Widget build(BuildContext context) { - return PopupMenuButton( - onSelected: (value) { - _onMenuItemSelected(value as int, context); - }, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(10.0)) - ), - itemBuilder: (ctx) => [ - // isAdmin ? _buildPopupMenuItem( - // appStrings(context).categoryDiscoverFavorites_title, ViewPopupMenuOptions.favorites.index, - // iconData: Icons.favorite_border - // ) : null, - _buildPopupMenuItem( - appStrings(context).tags, ViewPopupMenuOptions.tags.index, - iconData: Icons.local_offer_outlined - ), - // _buildPopupMenuItem( - // appStrings(context).categoryDiscoverVisits_title, ViewPopupMenuOptions.top_viewed.index, - // ), - // _buildPopupMenuItem( - // appStrings(context).categoryDiscoverBest_title, ViewPopupMenuOptions.top_rated.index, - // ), - // _buildPopupMenuItem( - // appStrings(context).categoryDiscoverRecent_title, ViewPopupMenuOptions.top_rated.index, - // iconData: Icons.access_time_rounded - // ), - ], - ); - } - - PopupMenuItem _buildPopupMenuItem( - String title, int position, {IconData iconData}) { - return PopupMenuItem( - value: position, - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text(title), - Spacer(), - if (iconData != null) Icon(iconData, color: Colors.black), - ], - ), - ); - } - - _onMenuItemSelected(int value, BuildContext context) { - MaterialPageRoute route; - - if (value == ViewPopupMenuOptions.favorites.index) { - route = MaterialPageRoute(builder: (context) => FavoritesViewPage( - isAdmin: isAdmin, - )); - } else if (value == ViewPopupMenuOptions.tags.index) { - showChooseTagSheet(context); - } - if (route != null) Navigator.of(context).push(route); - } -} - From e1ecc78a63326e9b91050428cd7f264c3f61edb8 Mon Sep 17 00:00:00 2001 From: Remi Martin <71256855+remi-martin@users.noreply.github.com> Date: Sat, 30 Mar 2024 09:23:13 +0100 Subject: [PATCH 38/50] New translations app_en.arb (Chinese Simplified) --- l10n/app_zh.arb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/l10n/app_zh.arb b/l10n/app_zh.arb index 843cb30..31287e4 100644 --- a/l10n/app_zh.arb +++ b/l10n/app_zh.arb @@ -20,7 +20,7 @@ "loadingHUD_label": "正在加载…", "completeHUD_label": "完成", "errorHUD_label": "出错", - "loadMoreHUD_label": "释放加载更多", + "loadMoreHUD_label": "松开以加载更多", "uploadRights_title": "需要上传权限", "uploadRights_message": "您必须拥有上传权限才能上传照片或视频。", "internetErrorGeneral_title": "连接出错", From 3c0c14617d8128bed8b9220f031983016d53d6b1 Mon Sep 17 00:00:00 2001 From: remartin Date: Sat, 30 Mar 2024 11:21:41 +0100 Subject: [PATCH 39/50] feat: fixed select tag dialog --- .../appbars/root_search_app_bar.dart | 30 +- lib/components/modals/open_tag_modal.dart | 154 +++++ lib/components/modals/select_tags_modal.dart | 5 +- lib/network/tags.dart | 36 +- .../components/dialogs/tags_dialogs.dart | 635 ------------------ lib/views/image/image_search_page.dart | 32 +- 6 files changed, 204 insertions(+), 688 deletions(-) create mode 100644 lib/components/modals/open_tag_modal.dart delete mode 100644 lib/views/components/dialogs/tags_dialogs.dart diff --git a/lib/components/appbars/root_search_app_bar.dart b/lib/components/appbars/root_search_app_bar.dart index f5807b4..e042521 100644 --- a/lib/components/appbars/root_search_app_bar.dart +++ b/lib/components/appbars/root_search_app_bar.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:piwigo_ng/components/dialogs/tags_dialogs.dart'; +import 'package:piwigo_ng/components/modals/open_tag_modal.dart'; import 'package:piwigo_ng/components/notification_dot.dart'; import 'package:piwigo_ng/components/popup_list_item.dart'; import 'package:piwigo_ng/services/preferences_service.dart'; @@ -41,9 +41,7 @@ class _RootSearchAppBarState extends State { if (widget.scrollController.offset > _expandedHeight * _opacityScale) { return 0.0; } - return (_expandedHeight * _opacityScale - - widget.scrollController.offset) / - (_expandedHeight * _opacityScale); + return (_expandedHeight * _opacityScale - widget.scrollController.offset) / (_expandedHeight * _opacityScale); } return 1.0; } @@ -59,8 +57,7 @@ class _RootSearchAppBarState extends State { } // In case 0%-100% of the expanded height is viewed - double scrollDelta = - (_expandedHeight - widget.scrollController.offset) / _expandedHeight; + double scrollDelta = (_expandedHeight - widget.scrollController.offset) / _expandedHeight; double scrollPercent = (scrollDelta * 2 - 1); return (1 - scrollPercent) * delta * basePadding + basePadding; } @@ -72,8 +69,7 @@ class _RootSearchAppBarState extends State { Widget build(BuildContext context) { return SliverAppBar( leading: IconButton( - onPressed: () => - Navigator.of(context).pushNamed(SettingsPage.routeName), + onPressed: () => Navigator.of(context).pushNamed(SettingsPage.routeName), icon: const Icon(Icons.settings), ), pinned: true, @@ -91,7 +87,6 @@ class _RootSearchAppBarState extends State { child: AppField( padding: const EdgeInsets.symmetric(vertical: 8.0), prefix: Icon(Icons.search), - hint: "Search...", ), ), ), @@ -126,8 +121,7 @@ class _RootSearchAppBarState extends State { PopupMenuItem( onTap: () => Future.delayed( const Duration(seconds: 0), - () => - Navigator.of(context).pushNamed(UploadStatusPage.routeName), + () => Navigator.of(context).pushNamed(UploadStatusPage.routeName), ), child: Stack( children: [ @@ -138,8 +132,7 @@ class _RootSearchAppBarState extends State { Positioned( top: 14.0, left: 0.0, - child: Consumer( - builder: (context, uploadNotifier, child) { + child: Consumer(builder: (context, uploadNotifier, child) { return NotificationDot( isShown: uploadNotifier.uploadList.isNotEmpty, ); @@ -149,8 +142,9 @@ class _RootSearchAppBarState extends State { ), ), PopupMenuItem( - onTap: () => ( - showChooseTagSheet(context) + onTap: () => Future.delayed( + const Duration(seconds: 0), + () => showOpenTagModal(context), ), child: PopupListItem( icon: Icons.local_offer_outlined, @@ -161,8 +155,7 @@ class _RootSearchAppBarState extends State { PopupMenuItem( onTap: () => Future.delayed( const Duration(seconds: 0), - () => Navigator.of(context) - .pushNamed(ImageFavoritesPage.routeName), + () => Navigator.of(context).pushNamed(ImageFavoritesPage.routeName), ), child: PopupListItem( icon: Icons.favorite, @@ -174,8 +167,7 @@ class _RootSearchAppBarState extends State { Positioned( top: 12.0, left: 12.0, - child: Consumer( - builder: (context, uploadNotifier, child) { + child: Consumer(builder: (context, uploadNotifier, child) { return NotificationDot( isShown: uploadNotifier.uploadList.isNotEmpty, ); diff --git a/lib/components/modals/open_tag_modal.dart b/lib/components/modals/open_tag_modal.dart new file mode 100644 index 0000000..5e3a6b1 --- /dev/null +++ b/lib/components/modals/open_tag_modal.dart @@ -0,0 +1,154 @@ +import 'package:flutter/material.dart'; +import 'package:modal_bottom_sheet/modal_bottom_sheet.dart'; +import 'package:piwigo_ng/components/fields/app_field.dart'; +import 'package:piwigo_ng/models/tag_model.dart'; +import 'package:piwigo_ng/network/api_error.dart'; +import 'package:piwigo_ng/network/tags.dart'; +import 'package:piwigo_ng/utils/localizations.dart'; +import 'package:piwigo_ng/views/image/image_tags_page.dart'; + +class OpenTagModal extends StatefulWidget { + const OpenTagModal({super.key}); + + @override + _OpenTagModalState createState() => _OpenTagModalState(); +} + +class _OpenTagModalState extends State { + final ScrollController _scrollController = ScrollController(); + late final Future _tagsFuture; + + String _searchQuery = ''; + List? _tagList; + + @override + void initState() { + super.initState(); + _tagsFuture = _onRefresh(); + } + + @override + void dispose() { + _scrollController.dispose(); + super.dispose(); + } + + Future _onRefresh() async { + try { + final ApiResponse> result = await getTags(); + if (!result.hasData) return; + setState(() { + _tagList = result.data!.where((tag) => tag.counter > 0).toList()..sort((a, b) => a.name.compareTo(b.name)); + }); + } catch (e) { + setState(() { + _tagList = null; + }); + } + } + + void _onSelectTag(TagModel tag) { + Navigator.of(context).pop(); + Navigator.of(context).pushNamed( + ImageTagsPage.routeName, + arguments: { + 'tag': tag, + }, + ); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Colors.transparent, + appBar: AppBar( + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.vertical( + top: Radius.circular(15.0), + ), + ), + elevation: 0.0, + scrolledUnderElevation: 5.0, + leading: IconButton( + icon: Icon(Icons.close), + onPressed: () => Navigator.of(context).pop(), + ), + title: Text(appStrings.tags), + ), + body: ListView( + controller: ModalScrollController.of(context), + physics: const AlwaysScrollableScrollPhysics(), + children: [ + Padding( + padding: const EdgeInsets.symmetric( + horizontal: 16.0, + vertical: 8.0, + ), + child: AppField( + padding: const EdgeInsets.symmetric(vertical: 8.0), + prefix: Icon(Icons.search), + onChanged: (query) => setState(() { + _searchQuery = query; + }), + ), + ), + FutureBuilder( + future: _tagsFuture, + builder: (context, snapshot) { + switch (snapshot.connectionState) { + case ConnectionState.done: + return _buildTagList(); + default: + return Center( + child: CircularProgressIndicator(), + ); + } + }, + ), + ], + ), + ); + } + + Widget _buildTagList() { + if (_tagList == null) { + return Center( + child: Text(appStrings.coreDataFetch_TagError), + ); + } + + List tags = + _tagList!.where((tag) => tag.name.toLowerCase().contains(_searchQuery.toLowerCase())).toList(); + if (tags.isEmpty) { + return Center( + child: Text(appStrings.none), + ); + } + + return Column( + children: tags.map((tag) => _buildItem(tag)).toList(), + ); + } + + Widget _buildItem(TagModel tag) => ListTile( + visualDensity: VisualDensity.compact, + shape: Border( + bottom: BorderSide(color: Theme.of(context).scaffoldBackgroundColor), + ), + title: Text(tag.name), + trailing: Text(appStrings.imageCount(tag.counter)), + onTap: () => _onSelectTag(tag), + ); +} + +Future showOpenTagModal(BuildContext context) async { + return showMaterialModalBottomSheet( + context: context, + enableDrag: false, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.vertical(top: Radius.circular(30.0)), + ), + backgroundColor: Theme.of(context).scaffoldBackgroundColor, + builder: (context) => OpenTagModal(), + ); +} diff --git a/lib/components/modals/select_tags_modal.dart b/lib/components/modals/select_tags_modal.dart index 69e3d9e..56d291b 100644 --- a/lib/components/modals/select_tags_modal.dart +++ b/lib/components/modals/select_tags_modal.dart @@ -41,11 +41,10 @@ class _SelectTagsModalState extends State { super.dispose(); } - List get _unselectedTagList => - _tagList!.where((t) => !_selectedTagList.contains(t)).toList(); + List get _unselectedTagList => _tagList!.where((t) => !_selectedTagList.contains(t)).toList(); Future _onRefresh() async { - final ApiResponse> result = await getTags(); + final ApiResponse> result = await getAdminTags(); if (!result.hasData) return; setState(() { _tagList = result.data!; diff --git a/lib/network/tags.dart b/lib/network/tags.dart index d559e99..a37ef98 100644 --- a/lib/network/tags.dart +++ b/lib/network/tags.dart @@ -9,6 +9,31 @@ import 'package:piwigo_ng/services/preferences_service.dart'; import 'api_client.dart'; Future>> getTags() async { + Map queries = { + 'format': 'json', + 'method': 'pwg.tags.getList', + }; + + Response response = await ApiClient.get(queryParameters: queries); + + try { + if (response.statusCode == 200) { + var data = json.decode(response.data); + if (data['stat'] == 'fail') { + return ApiResponse(error: ApiErrors.error); + } + List tags = data['result']['tags'].map((tag) => TagModel.fromJson(tag)).toList(); + return ApiResponse(data: tags); + } + } on DioError catch (e) { + debugPrint('Get tags: ${e.message}'); + } on Error catch (e) { + debugPrint('Get tags: $e\n${e.stackTrace}'); + } + return ApiResponse(error: ApiErrors.error); +} + +Future>> getAdminTags() async { Map queries = { 'format': 'json', 'method': 'pwg.tags.getAdminList', @@ -22,9 +47,7 @@ Future>> getTags() async { if (data['stat'] == 'fail') { return ApiResponse(error: ApiErrors.error); } - List tags = data['result']['tags'] - .map((tag) => TagModel.fromJson(tag)) - .toList(); + List tags = data['result']['tags'].map((tag) => TagModel.fromJson(tag)).toList(); return ApiResponse(data: tags); } } on DioError catch (e) { @@ -72,10 +95,7 @@ Future editTag(int tagId, String tagName) async { "new_name": tagName, 'pwg_token': appPreferences.getString('PWG_TOKEN'), }); - Response response = await ApiClient.post( - data: formData, - queryParameters: queries - ); + Response response = await ApiClient.post(data: formData, queryParameters: queries); try { if (response.statusCode == 200) { @@ -91,4 +111,4 @@ Future editTag(int tagId, String tagName) async { debugPrint('Get tags: $e\n${e.stackTrace}'); } return ApiResponse(error: ApiErrors.error); -} \ No newline at end of file +} diff --git a/lib/views/components/dialogs/tags_dialogs.dart b/lib/views/components/dialogs/tags_dialogs.dart deleted file mode 100644 index 2443c42..0000000 --- a/lib/views/components/dialogs/tags_dialogs.dart +++ /dev/null @@ -1,635 +0,0 @@ -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; -import 'package:piwigo_ng/api/TagAPI.dart'; -import 'package:piwigo_ng/constants/SettingsConstants.dart'; -import 'package:piwigo_ng/model/TagModel.dart'; -import 'package:piwigo_ng/services/OrientationService.dart'; -import 'package:piwigo_ng/views/TagViewPage.dart'; -import 'package:piwigo_ng/views/components/buttons.dart'; -import 'package:piwigo_ng/views/components/textfields.dart'; - -import 'package:piwigo_ng/views/components/snackbars.dart'; -import 'piwigo_dialog.dart'; - -class SelectTagsPage extends StatefulWidget { - const SelectTagsPage({Key key, this.onConfirm, this.selectedTags}) : super(key: key); - - final Function(List) onConfirm; - final List selectedTags; - - @override - _SelectTagsPageState createState() => _SelectTagsPageState(); -} -class _SelectTagsPageState extends State { - List _selectedTags = []; - bool _isLoading = false; - - @override - void initState() { - super.initState(); - _selectedTags.addAll(widget.selectedTags); - _sortTagList(_selectedTags); - } - - void _sortTagList(List list) { - list.sort((a, b) => a.compareTo(b)); - } - void _updateLists(List allTags) { - _sortTagList(allTags); - _sortTagList(_selectedTags); - } - bool _isSelected(TagModel tag) { - for(var selectedTag in _selectedTags) { - if(tag.id == selectedTag.id) { - return true; - } - } - return false; - } - List _tagListFromJson(List jsonList) { - return jsonList.map((json) => TagModel.fromJson(json)).toList(); - } - _isSelectedEndTag(TagModel tag) { - return _isSelected(tag) && (tag.id == _selectedTags.last.id); - } - _isUnselectedEndTag(TagModel tag, List tags) { - List unselectedTags = tags.where((e) => !_selectedTags.contains(e)).toList(); - return !_isSelected(tag) && (tag.id == unselectedTags.last.id); - } - - void _onSelectTags() { - setState(() { - _isLoading = true; - }); - widget.onConfirm(_selectedTags); - Navigator.of(context).pop(); - } - - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - leading: IconButton( - onPressed: () { - Navigator.of(context).pop(); - }, - icon: Icon(Icons.chevron_left), - ), - title: Text(appStrings(context).tags), - centerTitle: true, - actions: [ - IconButton( - onPressed: () { - showDialog( - context: context, - builder: (BuildContext context) { - return CreateTagDialog(); - } - ).whenComplete(() { - setState(() {}); - }); - }, - icon: Icon(Icons.add_circle_outline), - ), - ], - ), - body: FutureBuilder>( - future: getAdminTags(), - builder: (BuildContext context, AsyncSnapshot snapshot) { - if(snapshot.hasData) { - if(snapshot.data['stat'] == 'fail') { - return Center( - child: Text(appStrings(context).coreDataFetch_TagError), - ); - } - List tags = _tagListFromJson(snapshot.data["result"]['tags']); - _updateLists(tags); - return Container( - padding: EdgeInsets.symmetric(horizontal: 20), - child: Column( - children: [ - SizedBox(height: 10), - isPortrait(context) ? _portraitTagsList(tags) - : _landscapeTagsList(tags), - SizedBox(height: 10), - Padding( - padding: EdgeInsets.all(5.0), - child: DialogButton( - child: _isLoading? - CircularProgressIndicator( - valueColor: AlwaysStoppedAnimation(Colors.white) - ) : Text(appStrings(context).alertConfirmButton, - style: TextStyle(fontSize: 16, color: Colors.white) - ), - onPressed: _onSelectTags, - ), - ), - ], - ), - ); - } else { - return Center( - child: CircularProgressIndicator(), - ); - } - }, - ), - ); - } - - Widget _selectedTagsColumn(List tags) { - return Container( - padding: EdgeInsets.symmetric(vertical: 5.0), - decoration: BoxDecoration( - color: Theme.of(context).inputDecorationTheme.fillColor, - borderRadius: BorderRadius.circular(20), - ), - child: _selectedTags.isEmpty ? TagItem( - TagModel(0, appStrings(context).none), - isEnd: true, - ) : ListView.builder( - shrinkWrap: true, - physics: NeverScrollableScrollPhysics(), - itemCount: tags.length, - itemBuilder: (BuildContext context, int index) { - TagModel tag = tags[index]; - return TagItem(tag, - isExpanded: _isSelected(tag), - isEnd: _isSelectedEndTag(tag), - icon: Icon(Icons.remove_circle_outline, color: Colors.red), - onTap: () { - setState(() { - _selectedTags.removeWhere((e) => e.id == tag.id); - _updateLists(tags); - }); - }, - ); - }, - ), - ); - } - Widget _unselectedTagsColumn(List tags) { - return Container( - padding: EdgeInsets.symmetric(vertical: 5.0), - decoration: BoxDecoration( - color: Theme.of(context).inputDecorationTheme.fillColor, - borderRadius: BorderRadius.circular(20), - ), - child: _selectedTags.length == tags.length ? TagItem( - TagModel(0, appStrings(context).none), - isEnd: true, - ) : ListView.builder( - shrinkWrap: true, - physics: NeverScrollableScrollPhysics(), - itemCount: tags.length, - itemBuilder: (BuildContext context, int index) { - TagModel tag = tags[index]; - return TagItem(tag, - isExpanded: !_isSelected(tag), - isEnd: _isUnselectedEndTag(tag, tags), - icon: Icon(Icons.add_circle_outline, color: Colors.green), - onTap: () { - setState(() { - _selectedTags.add(tag); - _updateLists(tags); - }); - }, - ); - }, - ), - ); - } - - Widget _portraitTagsList(List tags) { - return Expanded( - child: SingleChildScrollView( - child: Column( - children: [ - Container( - alignment: Alignment.topLeft, - padding: EdgeInsets.all(5), - child: Text(appStrings(context).tagsHeader_selected, - style: Theme.of(context).textTheme.headline5 - ), - ), - _selectedTagsColumn(tags), - SizedBox(height: 20), - Container( - alignment: Alignment.topLeft, - padding: EdgeInsets.all(5), - child: Text(appStrings(context).tagsHeader_notSelected, - style: Theme.of(context).textTheme.headline5 - ), - ), - _unselectedTagsColumn(tags), - ], - ), - ), - ); - } - Widget _landscapeTagsList(List tags) { - return Expanded( - child: Column( - children: [ - Row( - children: [ - Expanded( - child: Container( - alignment: Alignment.topLeft, - padding: EdgeInsets.all(5), - child: Text(appStrings(context).tagsHeader_selected, - style: Theme.of(context).textTheme.headline5 - ), - ), - ), - SizedBox(width: 20), - Expanded( - child: Container( - alignment: Alignment.topLeft, - padding: EdgeInsets.all(5), - child: Text(appStrings(context).tagsHeader_notSelected, - style: Theme.of(context).textTheme.headline5 - ), - ), - ), - ], - ), - Expanded( - child: Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Expanded( - child: SingleChildScrollView( - child: _selectedTagsColumn(tags), - ), - ), - SizedBox(width: 20), - Expanded( - child: SingleChildScrollView( - child: _unselectedTagsColumn(tags), - ), - ), - ], - ), - ), - ], - ), - ); - } -} - -class CreateTagDialog extends StatefulWidget { - const CreateTagDialog({Key key}) : super(key: key); - - @override - _CreateTagDialogState createState() => _CreateTagDialogState(); -} -class _CreateTagDialogState extends State { - final _formKey = GlobalKey(); - TextEditingController _nameController; - bool _isLoading = false; - - @override - void initState() { - _nameController = TextEditingController(); - - _nameController.addListener(() { - setState(() {}); - }); - - super.initState(); - } - - bool _isNameEmpty() { - return _nameController.text.toString() == '' - || _nameController.text == null; - } - - void onCreateTag() async { - if (_isNameEmpty()) return null; - if (_formKey.currentState.validate()) { - _formKey.currentState.save(); - setState(() { - _isLoading = true; - }); - try { - var result = await createTag( - _nameController.text, - ); - - if(result['stat'] == 'fail') { - setState(() { - _isLoading = false; - }); - ScaffoldMessenger.of(context).showSnackBar( - errorSnackBar(context, result['result']) - ); - } else { - _nameController.text = ""; - Navigator.of(context).pop(); - } - } catch (e) { - print(e); - } - } - } - - @override - Widget build(BuildContext context) { - return PiwigoDialog( - title: appStrings(context).tagsAdd_title, - content: Form( - key: _formKey, - child: Column( - children: [ - Center( - child: Text(appStrings(context).tagsAdd_message, - style: Theme.of(context).textTheme.subtitle1, - ), - ), - Padding( - padding: EdgeInsets.all(5), - child: TextFieldRequired( - controller: _nameController, - hint: appStrings(context).tagsAdd_placeholder, - ), - ), - Padding( - padding: EdgeInsets.all(5.0), - child: DialogButton( - child: _isLoading? - CircularProgressIndicator( - valueColor: AlwaysStoppedAnimation(Colors.white) - ) : Text(appStrings(context).alertAddButton, - style: TextStyle(fontSize: 16, color: Colors.white) - ), - style: _isNameEmpty() ? - dialogButtonStyleDisabled(context) : - dialogButtonStyle(context), - onPressed: onCreateTag, - ), - ), - ], - ), - ), - ); - } -} - -class TagItem extends StatefulWidget { - const TagItem(this.tag, {Key key, this.onTap, this.icon, this.isExpanded = true, this.isEnd = false}) : super(key: key); - - final TagModel tag; - final Icon icon; - final bool isExpanded; - final bool isEnd; - final Function() onTap; - - @override - _TagItemState createState() => _TagItemState(); -} -class _TagItemState extends State with SingleTickerProviderStateMixin { - bool _isExpanded; - AnimationController _controller; - Animation _heightFactor; - - @override - initState() { - _controller = - AnimationController(duration: Duration(milliseconds: 300), vsync: this); - _heightFactor = _controller.drive(CurveTween(curve: Curves.easeIn)); - _isExpanded = widget.isExpanded; - super.initState(); - } - - @override - void dispose() { - _controller.dispose(); - super.dispose(); - } - - @override - void didUpdateWidget(TagItem oldWidget) { - if (widget.isExpanded != oldWidget.isExpanded) { - setState(() { - _isExpanded = widget.isExpanded; - if (_isExpanded) { - _controller.forward(); - } else { - _controller.reverse(); - } - }); - } else if (widget.tag != oldWidget.tag) { - setState(() {}); - } - super.didUpdateWidget(oldWidget); - } - - Widget _buildTagItem() { - return InkWell( - onTap: _isExpanded ? widget.onTap : () {}, - child: Column( - children: [ - Container( - height: 40, - padding: EdgeInsets.symmetric(horizontal: 15, vertical: 10), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text('${widget.tag.name}', - style: Theme.of(context).textTheme.subtitle1 - ), - widget.icon ?? SizedBox(), - ], - ), - ), - _isExpanded && !widget.isEnd ? Divider( - indent: 10.0, - endIndent: 0.0, - height: 1.0, - ) : SizedBox(), - ], - ), - ); - } - - @override - Widget build(BuildContext context) { - if (_isExpanded && widget.isExpanded) { - _controller.forward(); - } else { - _controller.reverse(); - } - - final bool closed = - (!_isExpanded || !widget.isExpanded) && _controller.isDismissed; - - return AnimatedBuilder( - animation: _controller.view, - builder: (BuildContext context, Widget child) { - return ClipRect( - child: Align( - heightFactor: _heightFactor.value, - child: child, - ), - ); - }, - child: closed ? null : _buildTagItem(), - ); - } -} - -showChooseTagSheet(context, {content = ''}) async { - showModalBottomSheet( - context: context, - isScrollControlled: true, - backgroundColor: Colors.transparent, - builder: (BuildContext context) { - return DraggableScrollableSheet( - key: UniqueKey(), - initialChildSize: 0.93, - maxChildSize: 0.93, - // minChildSize: .5, - expand: false, - builder: (context, controller) => Column( - children: [ - Container( - height: 55, - padding: EdgeInsets.symmetric(horizontal: 15, vertical: 10), - decoration: BoxDecoration( - color: Theme.of(context).primaryColor, - borderRadius: BorderRadius.only( - topLeft: Radius.circular(20), - topRight: Radius.circular(20), - ), - ), - child: Align( - alignment: Alignment.centerLeft, - child: TextButton( - onPressed: () => Navigator.of(context).pop(), - child: Text( - appStrings(context).alertDismissButton, - style: Theme.of(context).textTheme.headline4, - ), - ) - ) - ), - Expanded( - child: Ink( - color: Theme.of(context).primaryColor, - child: ListView( - controller: controller, - padding: EdgeInsets.symmetric(horizontal: 15), - children: [ - SizedBox(height: 18), - Align( - alignment: Alignment.topLeft, - child: Text(appStrings(context).tags, - style: Theme.of(context).textTheme.headline1 - ), - ), - Align( - alignment: Alignment.topLeft, - child: Text(appStrings(context).tagsTitle_selectOne, - style: Theme.of(context).textTheme.headline5 - ), - ), - SizedBox(height: 10), - - FutureBuilder>( - future: getTags(), - builder: (BuildContext context, AsyncSnapshot tagsSnapshot) { - if(tagsSnapshot.hasData){ - if(tagsSnapshot.data['stat'] == 'fail') { - return Container( - padding: EdgeInsets.all(10), - child: Text(tagsSnapshot.data['result']), - ); //appStrings(context).categoryMainEmtpy - } - var tags = tagsSnapshot.data['result']['tags']; - tags.removeWhere((category) => ( - category["counter"] == 0 - )); - return Wrap( - direction: Axis.horizontal, - children: [ - ListView.separated( - scrollDirection: Axis.vertical, - shrinkWrap: true, - physics: ClampingScrollPhysics(), - clipBehavior: Clip.hardEdge, - padding: EdgeInsets.fromLTRB(0, 0, 0, 10), - // itemExtent: 40.0, - separatorBuilder: (BuildContext context, int index) { - return Divider(height: 1, color: Theme.of(context).primaryColor); - }, - itemCount: tags.length, - itemBuilder: (context, index) { - var tag = tags[index]; - var borderRadius = BorderRadius.circular(0); - var radius = Radius.circular(10); - - if(index == tags.length - 1) { - borderRadius = BorderRadius.only( - bottomLeft: radius, - bottomRight: radius, - ); - } else if (index == 0) { - borderRadius = BorderRadius.only( - topLeft: radius, - topRight: radius, - ); - } - - return ListTile( - shape: RoundedRectangleBorder( - borderRadius: borderRadius - ), - title: Text( - tag['name'] + ' (${ appStrings(context).imageCount(tag['counter']) })', - style: Theme.of(context).textTheme.bodyText1 - ), - // title: Text(tag['name'], style: Theme.of(context).textTheme.bodyText1), - tileColor: Theme.of(context).backgroundColor, - contentPadding: EdgeInsets.symmetric(horizontal: 15), - dense: true, - onTap: () => { - Navigator.of(context).push( - MaterialPageRoute(builder: (context) => TagViewPage( - isAdmin: true, - tag: tag['id'].toString(), - title: tag['name'] - )) - ) - } - ); - }, - ), - Align( - alignment: Alignment.center, - child: Text( - appStrings(context).tagCount(tags.length), - style: Theme.of(context).textTheme.subtitle2 - ), - ), - ] - ); - } else { - return Center( - child: CircularProgressIndicator(), - ); - } - } - ), - SizedBox(height: 10) - ], - ), - ) - ), - ], - ), - ); - }, - ); -} diff --git a/lib/views/image/image_search_page.dart b/lib/views/image/image_search_page.dart index f590a58..d814781 100644 --- a/lib/views/image/image_search_page.dart +++ b/lib/views/image/image_search_page.dart @@ -29,8 +29,7 @@ class ImageSearchPage extends StatefulWidget { class _ImageSearchPageState extends State { final TextEditingController _searchController = TextEditingController(); - final RefreshController _refreshController = - RefreshController(initialRefresh: false); + final RefreshController _refreshController = RefreshController(initialRefresh: false); final ScrollController _scrollController = ScrollController(); final FocusNode _focusNode = FocusNode(); @@ -62,8 +61,7 @@ class _ImageSearchPageState extends State { super.dispose(); } - bool get _hasNonFavorites => - _selectedList.where((image) => !image.favorite).isNotEmpty; + bool get _hasNonFavorites => _selectedList.where((image) => !image.favorite).isNotEmpty; Future _onWillPop() async { if (_selectedList.isNotEmpty) { @@ -132,8 +130,7 @@ class _ImageSearchPageState extends State { if (images == null || images is! List) return; setState(() { _searchList = images; - _page = - ((images.length - 1) / Settings.defaultElementPerPage).floor(); + _page = ((images.length - 1) / Settings.defaultElementPerPage).floor(); }); }); @@ -144,8 +141,7 @@ class _ImageSearchPageState extends State { } }); - void _onLikePhotos() => onLikePhotos(_selectedList, _hasNonFavorites) - .whenComplete(() => _onRefresh()); + void _onLikePhotos() => onLikePhotos(_selectedList, _hasNonFavorites).whenComplete(() => _onRefresh()); _onDeletePhotos() => onDeletePhotos(context, _selectedList).then((success) { if (success) _onRefresh(); @@ -160,9 +156,7 @@ class _ImageSearchPageState extends State { child: SmartRefresher( controller: _refreshController, scrollController: _scrollController, - enablePullUp: _searchList != null && - _searchList!.isNotEmpty && - _nbImages > _searchList!.length, + enablePullUp: _searchList != null && _searchList!.isNotEmpty && _nbImages > _searchList!.length, onLoading: _loadMoreImages, onRefresh: _onRefresh, header: MaterialClassicHeader( @@ -219,7 +213,6 @@ class _ImageSearchPageState extends State { controller: _searchController, focusNode: _focusNode, prefix: const Icon(Icons.search), - hint: "Search...", onChanged: (value) => setState(() { _searchText = value; _onRefresh(); @@ -263,9 +256,7 @@ class _ImageSearchPageState extends State { _onLikePhotos, ), child: PopupListItem( - icon: _hasNonFavorites - ? Icons.favorite_border - : Icons.favorite, + icon: _hasNonFavorites ? Icons.favorite_border : Icons.favorite, text: _hasNonFavorites ? appStrings.imageOptions_addFavorites : appStrings.imageOptions_removeFavorites, @@ -335,14 +326,11 @@ class _ImageSearchPageState extends State { return AnimatedContainer( duration: const Duration(milliseconds: 300), curve: Curves.easeInOut, - height: _selectedList.isEmpty || orientation == Orientation.landscape - ? 0 - : 56.0, + height: _selectedList.isEmpty || orientation == Orientation.landscape ? 0 : 56.0, child: BottomAppBar( height: 56.0, child: Row( - children: - _actions.map((action) => Expanded(child: action)).toList(), + children: _actions.map((action) => Expanded(child: action)).toList(), ), ), ); @@ -371,9 +359,7 @@ class _ImageSearchPageState extends State { if (Preferences.getUserStatus != 'guest') // Todo: enum roles IconButton( onPressed: _onLikePhotos, - tooltip: _hasNonFavorites - ? appStrings.imageOptions_addFavorites - : appStrings.imageOptions_removeFavorites, + tooltip: _hasNonFavorites ? appStrings.imageOptions_addFavorites : appStrings.imageOptions_removeFavorites, isSelected: !_hasNonFavorites, selectedIcon: Icon(Icons.favorite), icon: Icon(Icons.favorite_border), From e433182a022d633743f2e8671d3db2a049f74e3b Mon Sep 17 00:00:00 2001 From: Remi Martin <71256855+remi-martin@users.noreply.github.com> Date: Sat, 19 Aug 2023 13:11:44 +0200 Subject: [PATCH 40/50] New translations app_en.arb (German) --- l10n/app_de.arb | 1 - 1 file changed, 1 deletion(-) diff --git a/l10n/app_de.arb b/l10n/app_de.arb index 588d83c..99ebe7c 100644 --- a/l10n/app_de.arb +++ b/l10n/app_de.arb @@ -1,6 +1,5 @@ { "tabBar_albums": "Alben", - "settings_language": "Wählen Sie die Sprache", "tabBar_upload": "Hochladen", "tabBar_preferences": "Einstellungen", "alertOkButton": "OK", From a9292219046488f297ddf51e0f79f6a9a5698810 Mon Sep 17 00:00:00 2001 From: Remi Martin <71256855+remi-martin@users.noreply.github.com> Date: Sat, 19 Aug 2023 13:11:46 +0200 Subject: [PATCH 41/50] New translations app_en.arb (Chinese Simplified) --- l10n/app_zh.arb | 52 ++++++++++++++++++++++++------------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/l10n/app_zh.arb b/l10n/app_zh.arb index 4b9bbab..c3feb1d 100644 --- a/l10n/app_zh.arb +++ b/l10n/app_zh.arb @@ -56,8 +56,8 @@ "loginHTTP_enable": "Enable HTTP Basic", "loginHTTPuser_placeholder": "用户名", "loginHTTPpwd_placeholder": "密码", - "loginCert_title": "Self Signed Certificates", - "loginCert_enable": "Allow SSL", + "loginCert_title": "自签名证书", + "loginCert_enable": "允许 SSL", "loginCertFailed_title": "非私密连接", "loginCertFailed_message": "Piwigo警告网站证书无效。您仍然想要接受此证书吗?", "loginHTTPSfailed_title": "安全连接失败", @@ -153,7 +153,7 @@ "categoryUpload_loadSubCategories": "加载", "categoryUpload_images": "上传图片", "categoryUpload_videos": "上传视频", - "categoryUpload_camera": "Camera", + "categoryUpload_camera": "相机", "categoryUpload_takePhoto": "拍照", "categoryUpload_takeVideo": "录制", "uploadList_title": "上传状态", @@ -242,23 +242,23 @@ "moveCategoryHUD_moved": "相册已移动", "moveCategoryError_title": "移动失败", "moveCategoryError_message": "移动相册失败", - "categoryPrivacy": "Manage Permissions", - "categoryPrivacy_subtitle": "Manage access permissions of \"{album_name}\".", + "categoryPrivacy": "管理权限", + "categoryPrivacy_subtitle": "管理 \"{album_name}\" 的访问权限", "@categoryPrivacy_subtitle": { "placeholders": { "album_name": {} } }, - "categoryPrivacyMode_public": "Public", - "categoryPrivacyMode_publicMessage": "Every user can see this album.", - "categoryPrivacyMode_private": "Private", - "categoryPrivacyMode_privateMessage": "Visitors must log in and have the necessary permissions to see this album.", - "categoryPrivacyGroups": "Group permissions", - "categoryPrivacyGroups_add": "Authorize groups", - "categoryPrivacyUsers": "User permissions", - "categoryPrivacyUsers_message": "To manage user permissions, go to your web administration.", - "categoryPrivacyRecursive": "Apply to sub-albums", - "categoryPrivacyRecursive_message": "After confirmation, all modifications will be applied to sub-albums.", + "categoryPrivacyMode_public": "公开的", + "categoryPrivacyMode_publicMessage": "每个用户都可以看到此相册。", + "categoryPrivacyMode_private": "私有的", + "categoryPrivacyMode_privateMessage": "访客必须登录并拥有查看此相册的必要权限。", + "categoryPrivacyGroups": "组权限", + "categoryPrivacyGroups_add": "授权组", + "categoryPrivacyUsers": "用户权限", + "categoryPrivacyUsers_message": "要管理用户权限,请前往您的网站管理界面。", + "categoryPrivacyRecursive": "应用到子相册", + "categoryPrivacyRecursive_message": "确认后,所有修改都将应用于子相册。", "categorySelection_setThumbnail": "请选择要使用照片 {photo} 作为缩略图的相册。", "@categorySelection_setThumbnail": { "placeholders": { @@ -364,17 +364,17 @@ "tagsAdd_placeholder": "新标签", "tagsAddHUD_label": "创建标签中...", "tagsAddHUD_created": "标签已创建", - "group": "Group", - "groups": "Groups", - "groupsTitle_selectOne": "Select a Group", - "groupsHeader_selected": "Selected", - "groupsHeader_notSelected": "Not Selected", - "groupsHeader_all": "All Groups", - "groupsAdd_title": "Add Group", - "groupsAdd_message": "Enter a name for this new group", - "groupsAdd_placeholder": "New group", - "groupsAddHUD_label": "Creating Group…", - "groupsAddHUD_created": "Group Created", + "group": "组", + "groups": "组", + "groupsTitle_selectOne": "选择一个组", + "groupsHeader_selected": "已选中", + "groupsHeader_notSelected": "未选中", + "groupsHeader_all": "所有组", + "groupsAdd_title": "添加组", + "groupsAdd_message": "为新增的组设置名称", + "groupsAdd_placeholder": "新增组", + "groupsAddHUD_label": "正在创建组…", + "groupsAddHUD_created": "已创建组", "tagsAddError_message": "无法创建新标签", "tagsAddError_title": "创建失败", "selectImages": "选择图片", From f77ef281968c0f8312367793b09e4a7208cebdb6 Mon Sep 17 00:00:00 2001 From: Remi Martin <71256855+remi-martin@users.noreply.github.com> Date: Sun, 20 Aug 2023 13:15:40 +0200 Subject: [PATCH 42/50] New translations app_en.arb (Chinese Simplified) --- l10n/app_zh.arb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/l10n/app_zh.arb b/l10n/app_zh.arb index c3feb1d..843cb30 100644 --- a/l10n/app_zh.arb +++ b/l10n/app_zh.arb @@ -20,7 +20,7 @@ "loadingHUD_label": "正在加载…", "completeHUD_label": "完成", "errorHUD_label": "出错", - "loadMoreHUD_label": "Release to load more", + "loadMoreHUD_label": "释放加载更多", "uploadRights_title": "需要上传权限", "uploadRights_message": "您必须拥有上传权限才能上传照片或视频。", "internetErrorGeneral_title": "连接出错", @@ -48,12 +48,12 @@ "login_newSession": "正在打开会话", "login_communityParameters": "社区参数", "login_serverParameters": "Piwigo参数", - "login_advancedParameters": "Authentication Settings", + "login_advancedParameters": "验证设置", "login_connectionChanged": "连接已变更!", - "login_rememberCredentials": "Remember credentials", + "login_rememberCredentials": "记住凭据", "loginHTTP_title": "HTTP 证书", "loginHTTP_message": "Piwigo服务器需要基本访问验证:", - "loginHTTP_enable": "Enable HTTP Basic", + "loginHTTP_enable": "使能 HTTP Basic", "loginHTTPuser_placeholder": "用户名", "loginHTTPpwd_placeholder": "密码", "loginCert_title": "自签名证书", From 1fdb5977f6270910bb671a92e38d6c46aa552ca4 Mon Sep 17 00:00:00 2001 From: Remi Martin <71256855+remi-martin@users.noreply.github.com> Date: Sat, 30 Mar 2024 09:23:13 +0100 Subject: [PATCH 43/50] New translations app_en.arb (Chinese Simplified) --- l10n/app_zh.arb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/l10n/app_zh.arb b/l10n/app_zh.arb index 843cb30..31287e4 100644 --- a/l10n/app_zh.arb +++ b/l10n/app_zh.arb @@ -20,7 +20,7 @@ "loadingHUD_label": "正在加载…", "completeHUD_label": "完成", "errorHUD_label": "出错", - "loadMoreHUD_label": "释放加载更多", + "loadMoreHUD_label": "松开以加载更多", "uploadRights_title": "需要上传权限", "uploadRights_message": "您必须拥有上传权限才能上传照片或视频。", "internetErrorGeneral_title": "连接出错", From 6d2541207e7d9eeee2438f1cff36d07614c3ca29 Mon Sep 17 00:00:00 2001 From: remartin Date: Sat, 30 Mar 2024 12:34:38 +0100 Subject: [PATCH 44/50] feat: removed parallel upload --- lib/network/upload.dart | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/lib/network/upload.dart b/lib/network/upload.dart index 9471efe..3b47584 100644 --- a/lib/network/upload.dart +++ b/lib/network/upload.dart @@ -85,8 +85,7 @@ Future> uploadPhotos( if (url == null) return []; String? username = await storage.read(key: Preferences.usernameKey); String? password = await storage.read(key: Preferences.passwordKey); - UploadNotifier uploadNotifier = - App.appKey.currentContext!.read(); + UploadNotifier uploadNotifier = App.appKey.currentContext!.read(); int nbError = 0; // Creates Upload Item list for the upload notifier @@ -106,8 +105,7 @@ Future> uploadPhotos( App.navigatorKey.currentState?.popAndPushNamed(UploadStatusPage.routeName); // Iterate on each item - await Future.wait(List>.generate(items.length, (index) async { - UploadItem item = items[index]; + for (UploadItem item in items) { try { // Upload image Response? response = await uploadChunk( @@ -152,7 +150,7 @@ Future> uploadPhotos( uploadNotifier.itemUploadCompleted(item, error: true); nbError++; } - })); + } // Send notifications showUploadNotification(nbError, result.length); @@ -202,10 +200,8 @@ Future uploadChunk({ // Filter fields if (info['name'] != '' && info['name'] != null) fields['name'] = info['name']; - if (info['comment'] != '' && info['comment'] != null) - fields['comment'] = info['comment']; - if (info['tag_ids']?.isNotEmpty ?? false) - fields['tag_ids'] = info['tag_ids'].join(','); + if (info['comment'] != '' && info['comment'] != null) fields['comment'] = info['comment']; + if (info['tag_ids']?.isNotEmpty ?? false) fields['tag_ids'] = info['tag_ids'].join(','); if (info['level'] != -1) fields['level'] = info['level']; // Create dio client @@ -247,8 +243,7 @@ Future uploadCompleted(List imageId, int categoryId) async { }); try { - Response response = - await ApiClient.post(data: formData, queryParameters: queries); + Response response = await ApiClient.post(data: formData, queryParameters: queries); if (response.statusCode == 200) { return true; } @@ -270,8 +265,7 @@ Future communityUploadCompleted(List imageId, int categoryId) async { 'category_id': categoryId, }); try { - Response response = - await ApiClient.post(data: formData, queryParameters: queries); + Response response = await ApiClient.post(data: formData, queryParameters: queries); if (response.statusCode == 200) { return true; } From 9f69ff50722d4e090e4c5c8995616d98b7c2d7fb Mon Sep 17 00:00:00 2001 From: remartin Date: Mon, 24 Jun 2024 18:39:33 +0200 Subject: [PATCH 45/50] feat: upgraded flutter --- android/app/build.gradle | 16 ++++------- android/build.gradle | 13 --------- android/settings.gradle | 30 ++++++++++++++------ lib/views/image/image_page.dart | 49 ++++++++++----------------------- pubspec.yaml | 14 ++++++---- 5 files changed, 52 insertions(+), 70 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index bc9c6cf..7ce7ead 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -1,3 +1,9 @@ +plugins { + id "com.android.application" + id "kotlin-android" + id "dev.flutter.flutter-gradle-plugin" +} + def localProperties = new Properties() def localPropertiesFile = rootProject.file('local.properties') if (localPropertiesFile.exists()) { @@ -6,11 +12,6 @@ if (localPropertiesFile.exists()) { } } -def flutterRoot = localProperties.getProperty('flutter.sdk') -if (flutterRoot == null) { - throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") -} - def flutterVersionCode = localProperties.getProperty('flutter.versionCode') if (flutterVersionCode == null) { flutterVersionCode = '1' @@ -21,10 +22,6 @@ if (flutterVersionName == null) { flutterVersionName = '1.0' } -apply plugin: 'com.android.application' -apply plugin: 'kotlin-android' -apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" - def keystoreProperties = new Properties() def keystorePropertiesFile = rootProject.file('key.properties') if (keystorePropertiesFile.exists()) { @@ -81,7 +78,6 @@ flutter { } dependencies { - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" implementation "androidx.window:window:1.0.0" coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5' } diff --git a/android/build.gradle b/android/build.gradle index d1b666a..b42eb3a 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -1,16 +1,3 @@ -buildscript { - ext.kotlin_version = '1.7.10' - repositories { - google() - mavenCentral() - } - - dependencies { - classpath 'com.android.tools.build:gradle:7.1.3' - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - } -} - allprojects { repositories { google() diff --git a/android/settings.gradle b/android/settings.gradle index 44e62bc..08fcadc 100644 --- a/android/settings.gradle +++ b/android/settings.gradle @@ -1,11 +1,25 @@ -include ':app' +pluginManagement { + def flutterSdkPath = { + def properties = new Properties() + file("local.properties").withInputStream { properties.load(it) } + def flutterSdkPath = properties.getProperty("flutter.sdk") + assert flutterSdkPath != null, "flutter.sdk not set in local.properties" + return flutterSdkPath + }() -def localPropertiesFile = new File(rootProject.projectDir, "local.properties") -def properties = new Properties() + includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") -assert localPropertiesFile.exists() -localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } + repositories { + google() + mavenCentral() + gradlePluginPortal() + } +} -def flutterSdkPath = properties.getProperty("flutter.sdk") -assert flutterSdkPath != null, "flutter.sdk not set in local.properties" -apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" +plugins { + id "dev.flutter.flutter-plugin-loader" version "1.0.0" + id "com.android.application" version "7.1.3" apply false + id "org.jetbrains.kotlin.android" version "1.7.10" apply false +} + +include ":app" \ No newline at end of file diff --git a/lib/views/image/image_page.dart b/lib/views/image/image_page.dart index 7769f95..d327d18 100644 --- a/lib/views/image/image_page.dart +++ b/lib/views/image/image_page.dart @@ -82,11 +82,9 @@ class _ImagePageState extends State { void initState() { _imageList = widget.images.sublist(0); _album = widget.album; - _imagePage = - ((_imageList.length - 1) / Settings.defaultElementPerPage).floor(); + _imagePage = ((_imageList.length - 1) / Settings.defaultElementPerPage).floor(); - final ImageModel? startImage = - _imageList.firstWhere((image) => image.id == widget.startId); + final ImageModel? startImage = _imageList.firstWhere((image) => image.id == widget.startId); if (startImage != null) { _page = _imageList.indexOf(startImage); if (_imageList.last == startImage) { @@ -117,9 +115,7 @@ class _ImagePageState extends State { systemNavigationBarColor: Colors.black.withOpacity(0.001), statusBarColor: Colors.black.withOpacity(0.001), statusBarIconBrightness: - App.appKey.currentContext?.read().isDark ?? false - ? Brightness.light - : Brightness.dark, + App.appKey.currentContext?.read().isDark ?? false ? Brightness.light : Brightness.dark, )); super.dispose(); } @@ -129,8 +125,7 @@ class _ImagePageState extends State { Future _loadMoreImages() async { if (_album.id == -1) return; if (_album.nbImages <= _imageList.length) return; - ApiResponse> result = - await fetchImages(_album.id, _imagePage + 1); + ApiResponse> result = await fetchImages(_album.id, _imagePage + 1); if (result.hasError || !result.hasData) return; setState(() { _imagePage += 1; @@ -145,10 +140,8 @@ class _ImagePageState extends State { if (serverUrl == null) return {}; // Get server cookies - List cookies = - await ApiClient.cookieJar.loadForRequest(Uri.parse(serverUrl)); - String cookiesStr = - cookies.map((cookie) => '${cookie.name}=${cookie.value}').join('; '); + List cookies = await ApiClient.cookieJar.loadForRequest(Uri.parse(serverUrl)); + String cookiesStr = cookies.map((cookie) => '${cookie.name}=${cookie.value}').join('; '); // Get HTTP Basic id SharedPreferences prefs = await SharedPreferences.getInstance(); @@ -354,9 +347,7 @@ class _ImagePageState extends State { style: TextStyle(fontSize: 16.0, color: Colors.white), ), ), - if (MediaQuery.of(context).orientation == - Orientation.landscape) - ..._actions, + if (MediaQuery.of(context).orientation == Orientation.landscape) ..._actions, if (widget.isAdmin) PopupMenuButton( position: PopupMenuPosition.under, @@ -385,9 +376,7 @@ class _ImagePageState extends State { _onLike, ), child: PopupListItem( - icon: !_currentImage.favorite - ? Icons.favorite_border - : Icons.favorite, + icon: !_currentImage.favorite ? Icons.favorite_border : Icons.favorite, text: !_currentImage.favorite ? appStrings.imageOptions_addFavorites : appStrings.imageOptions_removeFavorites, @@ -495,10 +484,7 @@ class _ImagePageState extends State { imageUrl = image.elementUrl; imageUrl = HtmlUnescape().convert(imageUrl); } else { - imageUrl = image - .getDerivativeFromString(Preferences.getImageFullScreenSize) - ?.url ?? - ''; + imageUrl = image.getDerivativeFromString(Preferences.getImageFullScreenSize)?.url ?? ''; } // ApiClient.cookieJar.loadForRequest(Uri.parse(imageUrl)); @@ -520,10 +506,8 @@ class _ImagePageState extends State { child: IconButton( color: Colors.white, style: ButtonStyle( - backgroundColor: MaterialStateProperty.resolveWith( - (states) => Colors.black.withOpacity(0.5)), - shape: MaterialStateProperty.resolveWith( - (states) => CircleBorder()), + backgroundColor: MaterialStateProperty.resolveWith((states) => Colors.black.withOpacity(0.5)), + shape: MaterialStateProperty.resolveWith((states) => CircleBorder()), ), onPressed: () { Navigator.of(context).pushNamed( @@ -562,6 +546,7 @@ class _ImagePageState extends State { debugPrint("$o\n$s"); return const Icon(Icons.broken_image_outlined); }, + filterQuality: FilterQuality.medium, ); }, ); @@ -601,14 +586,11 @@ class _ImagePageState extends State { crossAxisAlignment: CrossAxisAlignment.stretch, children: [ _pagination, - if (MediaQuery.of(context).orientation == - Orientation.portrait) + if (MediaQuery.of(context).orientation == Orientation.portrait) SizedBox( height: 56.0, child: Row( - children: _actions - .map((action) => Expanded(child: action)) - .toList(), + children: _actions.map((action) => Expanded(child: action)).toList(), ), ), ], @@ -667,8 +649,7 @@ class _ImagePageState extends State { duration: _overlayAnimationDuration, curve: _overlayAnimationCurve, child: Builder(builder: (context) { - if (_currentImage.comment == null || _currentImage.comment!.isEmpty) - return const SizedBox(); + if (_currentImage.comment == null || _currentImage.comment!.isEmpty) return const SizedBox(); return GestureDetector( behavior: HitTestBehavior.opaque, onTap: _showImageDetails, diff --git a/pubspec.yaml b/pubspec.yaml index 4bc3198..7b8761f 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -6,7 +6,8 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev version: 2.2.3+223 environment: - sdk: ">=2.17.6 <3.0.0" + sdk: ">=3.0.0 <4.0.0" + flutter: 3.22.2 dependencies: flutter: @@ -24,15 +25,18 @@ dependencies: auto_size_text: ^3.0.0 # Text that auto-sizes (image thumbnail title) cupertino_icons: ^1.0.2 # iOS style icons (might be useless) font_awesome_flutter: ^10.1.0 # Font awesome icons (might be useless) - flutter_slidable: ^2.0.0 # Album card's sliding cations + flutter_slidable: ^3.0.1 # Album card's sliding cations drag_select_grid_view: ^0.6.1 # Drag to select image grid - rounded_loading_button: ^2.1.0 # Loading button animation + rounded_loading_button: # Loading button animation + git: + url: https://github.com/scopendo/flutter_rounded_loading_button # A custom fork that can work with the latest flutter SDK + ref: dd4b76a modal_bottom_sheet: ^3.0.0-pre # Custom modals (might be useless) cached_network_image: ^3.2.2 # Better cache for images (used for album's thumbnail) flutter_speed_dial: ^6.1.0+1 # Speed dial pull_to_refresh: ^2.0.0 # Top and bottom refresh gestures photo_view: ^0.14.0 # Zoom on fullscreen photos - extended_text: ^11.0.0 # Text overflow on left side + extended_text: ^13.0.0 # Text overflow on left side flutter_easyloading: ^3.0.5 # Show loading dialog # Storage @@ -64,7 +68,7 @@ dependencies: # Translations flutter_localizations: sdk: flutter - intl: ^0.18.0 # Used for translations + intl: ^0.19.0 # Used for translations html_unescape: ^2.0.0 dev_dependencies: From fca897dadfbb703e08b7ad082f91ea3dd681091c Mon Sep 17 00:00:00 2001 From: remartin Date: Mon, 24 Jun 2024 19:13:10 +0200 Subject: [PATCH 46/50] feat: rebase --- android/app/build.gradle | 16 ++++------- android/build.gradle | 13 --------- android/settings.gradle | 30 ++++++++++++++------ lib/views/image/image_page.dart | 49 ++++++++++----------------------- pubspec.yaml | 14 ++++++---- 5 files changed, 52 insertions(+), 70 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index bc9c6cf..7ce7ead 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -1,3 +1,9 @@ +plugins { + id "com.android.application" + id "kotlin-android" + id "dev.flutter.flutter-gradle-plugin" +} + def localProperties = new Properties() def localPropertiesFile = rootProject.file('local.properties') if (localPropertiesFile.exists()) { @@ -6,11 +12,6 @@ if (localPropertiesFile.exists()) { } } -def flutterRoot = localProperties.getProperty('flutter.sdk') -if (flutterRoot == null) { - throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") -} - def flutterVersionCode = localProperties.getProperty('flutter.versionCode') if (flutterVersionCode == null) { flutterVersionCode = '1' @@ -21,10 +22,6 @@ if (flutterVersionName == null) { flutterVersionName = '1.0' } -apply plugin: 'com.android.application' -apply plugin: 'kotlin-android' -apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" - def keystoreProperties = new Properties() def keystorePropertiesFile = rootProject.file('key.properties') if (keystorePropertiesFile.exists()) { @@ -81,7 +78,6 @@ flutter { } dependencies { - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" implementation "androidx.window:window:1.0.0" coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5' } diff --git a/android/build.gradle b/android/build.gradle index d1b666a..b42eb3a 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -1,16 +1,3 @@ -buildscript { - ext.kotlin_version = '1.7.10' - repositories { - google() - mavenCentral() - } - - dependencies { - classpath 'com.android.tools.build:gradle:7.1.3' - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - } -} - allprojects { repositories { google() diff --git a/android/settings.gradle b/android/settings.gradle index 44e62bc..08fcadc 100644 --- a/android/settings.gradle +++ b/android/settings.gradle @@ -1,11 +1,25 @@ -include ':app' +pluginManagement { + def flutterSdkPath = { + def properties = new Properties() + file("local.properties").withInputStream { properties.load(it) } + def flutterSdkPath = properties.getProperty("flutter.sdk") + assert flutterSdkPath != null, "flutter.sdk not set in local.properties" + return flutterSdkPath + }() -def localPropertiesFile = new File(rootProject.projectDir, "local.properties") -def properties = new Properties() + includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") -assert localPropertiesFile.exists() -localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } + repositories { + google() + mavenCentral() + gradlePluginPortal() + } +} -def flutterSdkPath = properties.getProperty("flutter.sdk") -assert flutterSdkPath != null, "flutter.sdk not set in local.properties" -apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" +plugins { + id "dev.flutter.flutter-plugin-loader" version "1.0.0" + id "com.android.application" version "7.1.3" apply false + id "org.jetbrains.kotlin.android" version "1.7.10" apply false +} + +include ":app" \ No newline at end of file diff --git a/lib/views/image/image_page.dart b/lib/views/image/image_page.dart index 7769f95..d327d18 100644 --- a/lib/views/image/image_page.dart +++ b/lib/views/image/image_page.dart @@ -82,11 +82,9 @@ class _ImagePageState extends State { void initState() { _imageList = widget.images.sublist(0); _album = widget.album; - _imagePage = - ((_imageList.length - 1) / Settings.defaultElementPerPage).floor(); + _imagePage = ((_imageList.length - 1) / Settings.defaultElementPerPage).floor(); - final ImageModel? startImage = - _imageList.firstWhere((image) => image.id == widget.startId); + final ImageModel? startImage = _imageList.firstWhere((image) => image.id == widget.startId); if (startImage != null) { _page = _imageList.indexOf(startImage); if (_imageList.last == startImage) { @@ -117,9 +115,7 @@ class _ImagePageState extends State { systemNavigationBarColor: Colors.black.withOpacity(0.001), statusBarColor: Colors.black.withOpacity(0.001), statusBarIconBrightness: - App.appKey.currentContext?.read().isDark ?? false - ? Brightness.light - : Brightness.dark, + App.appKey.currentContext?.read().isDark ?? false ? Brightness.light : Brightness.dark, )); super.dispose(); } @@ -129,8 +125,7 @@ class _ImagePageState extends State { Future _loadMoreImages() async { if (_album.id == -1) return; if (_album.nbImages <= _imageList.length) return; - ApiResponse> result = - await fetchImages(_album.id, _imagePage + 1); + ApiResponse> result = await fetchImages(_album.id, _imagePage + 1); if (result.hasError || !result.hasData) return; setState(() { _imagePage += 1; @@ -145,10 +140,8 @@ class _ImagePageState extends State { if (serverUrl == null) return {}; // Get server cookies - List cookies = - await ApiClient.cookieJar.loadForRequest(Uri.parse(serverUrl)); - String cookiesStr = - cookies.map((cookie) => '${cookie.name}=${cookie.value}').join('; '); + List cookies = await ApiClient.cookieJar.loadForRequest(Uri.parse(serverUrl)); + String cookiesStr = cookies.map((cookie) => '${cookie.name}=${cookie.value}').join('; '); // Get HTTP Basic id SharedPreferences prefs = await SharedPreferences.getInstance(); @@ -354,9 +347,7 @@ class _ImagePageState extends State { style: TextStyle(fontSize: 16.0, color: Colors.white), ), ), - if (MediaQuery.of(context).orientation == - Orientation.landscape) - ..._actions, + if (MediaQuery.of(context).orientation == Orientation.landscape) ..._actions, if (widget.isAdmin) PopupMenuButton( position: PopupMenuPosition.under, @@ -385,9 +376,7 @@ class _ImagePageState extends State { _onLike, ), child: PopupListItem( - icon: !_currentImage.favorite - ? Icons.favorite_border - : Icons.favorite, + icon: !_currentImage.favorite ? Icons.favorite_border : Icons.favorite, text: !_currentImage.favorite ? appStrings.imageOptions_addFavorites : appStrings.imageOptions_removeFavorites, @@ -495,10 +484,7 @@ class _ImagePageState extends State { imageUrl = image.elementUrl; imageUrl = HtmlUnescape().convert(imageUrl); } else { - imageUrl = image - .getDerivativeFromString(Preferences.getImageFullScreenSize) - ?.url ?? - ''; + imageUrl = image.getDerivativeFromString(Preferences.getImageFullScreenSize)?.url ?? ''; } // ApiClient.cookieJar.loadForRequest(Uri.parse(imageUrl)); @@ -520,10 +506,8 @@ class _ImagePageState extends State { child: IconButton( color: Colors.white, style: ButtonStyle( - backgroundColor: MaterialStateProperty.resolveWith( - (states) => Colors.black.withOpacity(0.5)), - shape: MaterialStateProperty.resolveWith( - (states) => CircleBorder()), + backgroundColor: MaterialStateProperty.resolveWith((states) => Colors.black.withOpacity(0.5)), + shape: MaterialStateProperty.resolveWith((states) => CircleBorder()), ), onPressed: () { Navigator.of(context).pushNamed( @@ -562,6 +546,7 @@ class _ImagePageState extends State { debugPrint("$o\n$s"); return const Icon(Icons.broken_image_outlined); }, + filterQuality: FilterQuality.medium, ); }, ); @@ -601,14 +586,11 @@ class _ImagePageState extends State { crossAxisAlignment: CrossAxisAlignment.stretch, children: [ _pagination, - if (MediaQuery.of(context).orientation == - Orientation.portrait) + if (MediaQuery.of(context).orientation == Orientation.portrait) SizedBox( height: 56.0, child: Row( - children: _actions - .map((action) => Expanded(child: action)) - .toList(), + children: _actions.map((action) => Expanded(child: action)).toList(), ), ), ], @@ -667,8 +649,7 @@ class _ImagePageState extends State { duration: _overlayAnimationDuration, curve: _overlayAnimationCurve, child: Builder(builder: (context) { - if (_currentImage.comment == null || _currentImage.comment!.isEmpty) - return const SizedBox(); + if (_currentImage.comment == null || _currentImage.comment!.isEmpty) return const SizedBox(); return GestureDetector( behavior: HitTestBehavior.opaque, onTap: _showImageDetails, diff --git a/pubspec.yaml b/pubspec.yaml index 4bc3198..7b8761f 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -6,7 +6,8 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev version: 2.2.3+223 environment: - sdk: ">=2.17.6 <3.0.0" + sdk: ">=3.0.0 <4.0.0" + flutter: 3.22.2 dependencies: flutter: @@ -24,15 +25,18 @@ dependencies: auto_size_text: ^3.0.0 # Text that auto-sizes (image thumbnail title) cupertino_icons: ^1.0.2 # iOS style icons (might be useless) font_awesome_flutter: ^10.1.0 # Font awesome icons (might be useless) - flutter_slidable: ^2.0.0 # Album card's sliding cations + flutter_slidable: ^3.0.1 # Album card's sliding cations drag_select_grid_view: ^0.6.1 # Drag to select image grid - rounded_loading_button: ^2.1.0 # Loading button animation + rounded_loading_button: # Loading button animation + git: + url: https://github.com/scopendo/flutter_rounded_loading_button # A custom fork that can work with the latest flutter SDK + ref: dd4b76a modal_bottom_sheet: ^3.0.0-pre # Custom modals (might be useless) cached_network_image: ^3.2.2 # Better cache for images (used for album's thumbnail) flutter_speed_dial: ^6.1.0+1 # Speed dial pull_to_refresh: ^2.0.0 # Top and bottom refresh gestures photo_view: ^0.14.0 # Zoom on fullscreen photos - extended_text: ^11.0.0 # Text overflow on left side + extended_text: ^13.0.0 # Text overflow on left side flutter_easyloading: ^3.0.5 # Show loading dialog # Storage @@ -64,7 +68,7 @@ dependencies: # Translations flutter_localizations: sdk: flutter - intl: ^0.18.0 # Used for translations + intl: ^0.19.0 # Used for translations html_unescape: ^2.0.0 dev_dependencies: From c04268c9f854827639732a435317caa978f4c501 Mon Sep 17 00:00:00 2001 From: remartin Date: Fri, 28 Jun 2024 15:46:02 +0200 Subject: [PATCH 47/50] fix: local notification dependency - Cleaned dart analysis --- .../appbars/root_search_app_bar.dart | 2 +- lib/components/appbars/settings_app_bar.dart | 5 +- lib/components/cards/image_details_card.dart | 56 ++++------- lib/components/dialogs/confirm_dialog.dart | 12 +-- lib/components/modals/move_or_copy_modal.dart | 12 +-- lib/services/notification_service.dart | 29 ++---- lib/utils/themes.dart | 22 ++--- lib/views/album/album_page.dart | 99 +++++++------------ lib/views/album/album_privacy_page.dart | 16 ++- lib/views/authentication/login_form_view.dart | 19 ++-- .../authentication/login_settings_page.dart | 8 +- lib/views/image/image_favorites_page.dart | 38 +++---- lib/views/image/image_page.dart | 16 +-- lib/views/image/image_search_page.dart | 9 +- lib/views/image/image_tags_page.dart | 44 +++------ lib/views/image/video_player_page.dart | 5 +- lib/views/image/video_view.dart | 33 +++---- lib/views/settings/settings_page.dart | 79 +++++---------- lib/views/upload/upload_page.dart | 42 +++----- pubspec.yaml | 2 +- 20 files changed, 193 insertions(+), 355 deletions(-) diff --git a/lib/components/appbars/root_search_app_bar.dart b/lib/components/appbars/root_search_app_bar.dart index e042521..d12413f 100644 --- a/lib/components/appbars/root_search_app_bar.dart +++ b/lib/components/appbars/root_search_app_bar.dart @@ -104,7 +104,7 @@ class _RootSearchAppBarState extends State { ), title: Text( appStrings.tabBar_albums, - textScaleFactor: 1, + textScaler: TextScaler.linear(1), style: Theme.of(context).appBarTheme.titleTextStyle, ), ), diff --git a/lib/components/appbars/settings_app_bar.dart b/lib/components/appbars/settings_app_bar.dart index 7ea496a..b260357 100644 --- a/lib/components/appbars/settings_app_bar.dart +++ b/lib/components/appbars/settings_app_bar.dart @@ -33,8 +33,7 @@ class _SettingsAppBarState extends State { } // In case 0%-100% of the expanded height is viewed - double scrollDelta = - (_expandedHeight - widget.scrollController.offset) / _expandedHeight; + double scrollDelta = (_expandedHeight - widget.scrollController.offset) / _expandedHeight; double scrollPercent = (scrollDelta * 2 - 1); return (1 - scrollPercent) * delta * basePadding + basePadding; } @@ -59,7 +58,7 @@ class _SettingsAppBarState extends State { ), title: Text( appStrings.tabBar_preferences, - textScaleFactor: 1, + textScaler: TextScaler.linear(1), style: Theme.of(context).appBarTheme.titleTextStyle, ), ), diff --git a/lib/components/cards/image_details_card.dart b/lib/components/cards/image_details_card.dart index 197a75b..9197448 100644 --- a/lib/components/cards/image_details_card.dart +++ b/lib/components/cards/image_details_card.dart @@ -1,5 +1,5 @@ import 'dart:io'; -import 'dart:ui' as ui show Codec, FrameInfo, Image; +import 'dart:ui' as ui show Image; import 'package:auto_size_text/auto_size_text.dart'; import 'package:extended_text/extended_text.dart'; @@ -17,8 +17,7 @@ import 'package:provider/provider.dart'; import 'package:video_player/video_player.dart'; class ImageDetailsCard extends StatelessWidget { - const ImageDetailsCard({Key? key, required this.image, this.onRemove}) - : super(key: key); + const ImageDetailsCard({Key? key, required this.image, this.onRemove}) : super(key: key); final ImageModel image; final Function()? onRemove; @@ -57,9 +56,7 @@ class ImageDetailsCard extends StatelessWidget { child: ClipRRect( borderRadius: BorderRadius.circular(5.0), child: Builder(builder: (context) { - final String? imageUrl = image - .getDerivativeFromString(Preferences.getImageThumbnailSize) - ?.url; + final String? imageUrl = image.getDerivativeFromString(Preferences.getImageThumbnailSize)?.url; return ImageNetworkDisplay( imageUrl: imageUrl, ); @@ -91,10 +88,7 @@ class ImageDetailsCard extends StatelessWidget { children: [ Flexible( child: Text( - image.file - .replaceAll('', '\u200B') - .split(path.extension(image.file)) - .first, + image.file.replaceAll('', '\u200B').split(path.extension(image.file)).first, maxLines: 1, overflow: TextOverflow.ellipsis, style: Theme.of(context).textTheme.bodySmall, @@ -112,13 +106,11 @@ class ImageDetailsCard extends StatelessWidget { const Spacer(), if (image.dateAvailable != null) Builder(builder: (context) { - LocaleNotifier localeNotifier = - Provider.of(context, listen: false); + LocaleNotifier localeNotifier = Provider.of(context, listen: false); String date = - DateFormat.yMMMMd(localeNotifier.locale.languageCode) - .format(DateTime.parse(image.dateAvailable!)); - String time = DateFormat.Hms(localeNotifier.locale.languageCode) - .format(DateTime.parse(image.dateAvailable!)); + DateFormat.yMMMMd(localeNotifier.locale.languageCode).format(DateTime.parse(image.dateAvailable!)); + String time = + DateFormat.Hms(localeNotifier.locale.languageCode).format(DateTime.parse(image.dateAvailable!)); return AutoSizeText( "$date $time", maxLines: 1, @@ -150,8 +142,7 @@ class ImageDetailsCard extends StatelessWidget { } class LocalImageDetailsCard extends StatefulWidget { - const LocalImageDetailsCard( - {Key? key, required this.image, this.onRemove, this.isDuplicate = false}) + const LocalImageDetailsCard({Key? key, required this.image, this.onRemove, this.isDuplicate = false}) : super(key: key); final File image; @@ -214,17 +205,12 @@ class _LocalImageDetailsCardState extends State { fit: StackFit.expand, children: [ LayoutBuilder(builder: (context, constraints) { - List? mimeType = - mime(widget.image.path.split('/').last)?.split('/'); + List? mimeType = mime(widget.image.path.split('/').last)?.split('/'); if (mimeType?.first == 'image') { _checkMemory(); - double? cacheWidth = constraints.maxWidth.isInfinite - ? constraints.maxWidth - : null; - double? cacheHeight = constraints.maxHeight.isInfinite - ? constraints.maxHeight - : null; + double? cacheWidth = constraints.maxWidth.isInfinite ? constraints.maxWidth : null; + double? cacheHeight = constraints.maxHeight.isInfinite ? constraints.maxHeight : null; return Image.file( widget.image, fit: BoxFit.cover, @@ -327,8 +313,7 @@ class _LocalImageDetailsCardState extends State { } class LocalVideoDetailsCard extends StatefulWidget { - const LocalVideoDetailsCard( - {Key? key, required this.video, this.onRemove, this.isDuplicate = false}) + const LocalVideoDetailsCard({Key? key, required this.video, this.onRemove, this.isDuplicate = false}) : super(key: key); final File video; @@ -361,9 +346,7 @@ class _LocalVideoDetailsCardState extends State { final Duration duration = _controller.value.duration; int hours = duration.inHours; int minutes = (duration - Duration(hours: hours)).inMinutes; - int seconds = - (duration - Duration(hours: hours) - Duration(minutes: minutes)) - .inSeconds; + int seconds = (duration - Duration(hours: hours) - Duration(minutes: minutes)).inSeconds; return '${hours > 0 ? '$hours:' : ''}${minutes < 10 ? '0$minutes' : '$minutes'}:${seconds < 10 ? '0$seconds' : '$seconds'}'; } @@ -433,17 +416,12 @@ class _LocalVideoDetailsCardState extends State { bottom: 2.0, left: 2.0, child: Container( - padding: const EdgeInsets.symmetric( - horizontal: 4, vertical: 2), + padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 2), decoration: BoxDecoration( - borderRadius: BorderRadius.circular(5), - color: AppColors.black.withOpacity(0.7)), + borderRadius: BorderRadius.circular(5), color: AppColors.black.withOpacity(0.7)), child: Text( _duration, - style: TextStyle( - color: AppColors.white, - fontSize: 10, - fontWeight: FontWeight.bold), + style: TextStyle(color: AppColors.white, fontSize: 10, fontWeight: FontWeight.bold), ), ), ), diff --git a/lib/components/dialogs/confirm_dialog.dart b/lib/components/dialogs/confirm_dialog.dart index d335380..a2fdef4 100644 --- a/lib/components/dialogs/confirm_dialog.dart +++ b/lib/components/dialogs/confirm_dialog.dart @@ -27,12 +27,10 @@ class ConfirmDialog extends StatelessWidget { actions: [ TextButton( style: ButtonStyle( - foregroundColor: MaterialStateColor.resolveWith( - (states) => - Theme.of(context).textTheme.bodySmall?.color ?? - AppColors.disabled, + foregroundColor: WidgetStateColor.resolveWith( + (states) => Theme.of(context).textTheme.bodySmall?.color ?? AppColors.disabled, ), - overlayColor: MaterialStateColor.resolveWith( + overlayColor: WidgetStateColor.resolveWith( (states) => AppColors.accent.withOpacity(0.3), ), ), @@ -43,10 +41,10 @@ class ConfirmDialog extends StatelessWidget { ), TextButton( style: ButtonStyle( - foregroundColor: MaterialStateColor.resolveWith( + foregroundColor: WidgetStateColor.resolveWith( (states) => confirmColor ?? AppColors.accent, ), - overlayColor: MaterialStateColor.resolveWith( + overlayColor: WidgetStateColor.resolveWith( (states) => AppColors.accent.withOpacity(0.3), ), ), diff --git a/lib/components/modals/move_or_copy_modal.dart b/lib/components/modals/move_or_copy_modal.dart index 01f1e5c..f52a667 100644 --- a/lib/components/modals/move_or_copy_modal.dart +++ b/lib/components/modals/move_or_copy_modal.dart @@ -26,7 +26,6 @@ class MoveOrCopyModal extends StatefulWidget { } class _MoveOrCopyModalState extends State { - final ScrollController _scrollController = ScrollController(); late final Future>> _albumFuture; late final List _disabledAlbums; @@ -39,8 +38,7 @@ class _MoveOrCopyModalState extends State { _disabledAlbums = [ if (widget.album != null) widget.album!.id, if (parentAlbums.length == 1 || widget.isImage) 0, - if (!widget.isImage && parentAlbums.length > 1) - int.parse(parentAlbums[parentAlbums.length - 2]), + if (!widget.isImage && parentAlbums.length > 1) int.parse(parentAlbums[parentAlbums.length - 2]), ]; super.initState(); @@ -213,9 +211,7 @@ class _ExpansionAlbumTileState extends State { child: Text( "${List.generate(widget.index, (index) => '.').join()}${widget.index > 0 ? ' ' : ''}${widget.album.name}", overflow: TextOverflow.ellipsis, - style: _disabled - ? Theme.of(context).textTheme.bodySmall - : Theme.of(context).textTheme.bodyMedium, + style: _disabled ? Theme.of(context).textTheme.bodySmall : Theme.of(context).textTheme.bodyMedium, ), ), ), @@ -235,9 +231,7 @@ class _ExpansionAlbumTileState extends State { child: Text( appStrings.albumCount(widget.album.nbCategories), style: TextStyle( - color: Theme.of(context) - .primaryColor - .withOpacity(0.7), + color: Theme.of(context).primaryColor.withOpacity(0.7), fontSize: 14, ), ), diff --git a/lib/services/notification_service.dart b/lib/services/notification_service.dart index 705fd22..0538629 100644 --- a/lib/services/notification_service.dart +++ b/lib/services/notification_service.dart @@ -10,16 +10,13 @@ import 'package:piwigo_ng/utils/localizations.dart'; import 'package:piwigo_ng/utils/settings.dart'; import 'package:shared_preferences/shared_preferences.dart'; -final FlutterLocalNotificationsPlugin localNotification = - FlutterLocalNotificationsPlugin(); +final FlutterLocalNotificationsPlugin localNotification = FlutterLocalNotificationsPlugin(); Future initLocalNotifications() async { - const AndroidInitializationSettings initializationSettingsAndroid = - AndroidInitializationSettings( + const AndroidInitializationSettings initializationSettingsAndroid = AndroidInitializationSettings( '@mipmap/ic_launcher', ); - final initSettings = - InitializationSettings(android: initializationSettingsAndroid); + final initSettings = InitializationSettings(android: initializationSettingsAndroid); localNotification.initialize( initSettings, onDidReceiveNotificationResponse: onSelectNotification, @@ -36,9 +33,8 @@ Future onSelectNotification(NotificationResponse response) async { Future askNotificationPermissions() async { return localNotification - .resolvePlatformSpecificImplementation< - AndroidFlutterLocalNotificationsPlugin>() - ?.requestPermission(); + .resolvePlatformSpecificImplementation() + ?.requestNotificationsPermission(); } Future showLocalNotification({ @@ -88,9 +84,7 @@ Future showUploadNotification([int nbError = 0, int nbImage = 0]) async { } else if (nbError == 0 && nbImage > 0) { // Upload completed title = appStrings.imageUploadCompleted_title; - message = nbImage == 1 - ? appStrings.imageUploadCompleted_message - : appStrings.imageUploadCompleted_message1; + message = nbImage == 1 ? appStrings.imageUploadCompleted_message : appStrings.imageUploadCompleted_message1; } else if (nbError > 0 && nbImage != nbError) { // Upload partially completed title = appStrings.coreDataStore_WarningTitle; @@ -118,10 +112,8 @@ Future showAutoUploadNotification([ if (!(prefs.getBool(AutoUploadPreferences.notificationKey) ?? false)) return; // Find localizations - Locale locale = Locale(prefs.getString(LocaleNotifier.key) ?? - Platform.localeName.split('_').first); - AppLocalizations backgroundStrings = - await AppLocalizations.delegate.load(locale); + Locale locale = Locale(prefs.getString(LocaleNotifier.key) ?? Platform.localeName.split('_').first); + AppLocalizations backgroundStrings = await AppLocalizations.delegate.load(locale); // Init notifications final android = AndroidNotificationDetails( @@ -139,9 +131,8 @@ Future showAutoUploadNotification([ if (nbError == 0 && nbImage > 0) { // Upload completed title = backgroundStrings.imageUploadCompleted_title; - message = nbImage == 1 - ? backgroundStrings.imageUploadCompleted_message - : backgroundStrings.imageUploadCompleted_message1; + message = + nbImage == 1 ? backgroundStrings.imageUploadCompleted_message : backgroundStrings.imageUploadCompleted_message1; } else if (nbError > 0 && nbImage != nbError) { // Upload partially completed title = backgroundStrings.coreDataStore_WarningTitle; diff --git a/lib/utils/themes.dart b/lib/utils/themes.dart index 80761d3..00e809e 100644 --- a/lib/utils/themes.dart +++ b/lib/utils/themes.dart @@ -19,7 +19,7 @@ final ThemeData lightTheme = ThemeData.light(useMaterial3: true).copyWith( primary: AppColors.white, secondary: AppColors.accent, error: AppColors.error, - background: AppColors.backgroundLight, + surface: AppColors.backgroundLight, outline: AppColors.fieldLight, ), progressIndicatorTheme: const ProgressIndicatorThemeData( @@ -101,12 +101,12 @@ final ThemeData lightTheme = ThemeData.light(useMaterial3: true).copyWith( backgroundColor: Colors.black.withOpacity(0), ), switchTheme: SwitchThemeData( - thumbColor: MaterialStateProperty.all(AppColors.backgroundLight), - overlayColor: MaterialStateProperty.all(AppColors.backgroundLight), - trackColor: MaterialStateProperty.resolveWith((states) { - if (states.contains(MaterialState.selected)) { + thumbColor: WidgetStateProperty.all(AppColors.backgroundLight), + overlayColor: WidgetStateProperty.all(AppColors.backgroundLight), + trackColor: WidgetStateProperty.resolveWith((states) { + if (states.contains(WidgetState.selected)) { return AppColors.accent; - } else if (states.contains(MaterialState.disabled)) { + } else if (states.contains(WidgetState.disabled)) { return AppColors.disabled; } return AppColors.fieldLight; @@ -190,7 +190,7 @@ final ThemeData darkTheme = ThemeData.dark(useMaterial3: true).copyWith( primary: AppColors.white, secondary: AppColors.accent, error: AppColors.error, - background: AppColors.backgroundDark, + surface: AppColors.backgroundDark, outline: AppColors.fieldDark, ), progressIndicatorTheme: const ProgressIndicatorThemeData( @@ -272,11 +272,11 @@ final ThemeData darkTheme = ThemeData.dark(useMaterial3: true).copyWith( backgroundColor: Colors.black.withOpacity(0), ), switchTheme: SwitchThemeData( - thumbColor: MaterialStateProperty.all(const Color(0x80FFFFFF)), - trackColor: MaterialStateProperty.resolveWith((states) { - if (states.contains(MaterialState.selected)) { + thumbColor: WidgetStateProperty.all(const Color(0x80FFFFFF)), + trackColor: WidgetStateProperty.resolveWith((states) { + if (states.contains(WidgetState.selected)) { return AppColors.accent; - } else if (states.contains(MaterialState.disabled)) { + } else if (states.contains(WidgetState.disabled)) { return AppColors.disabled; } return AppColors.backgroundDark; diff --git a/lib/views/album/album_page.dart b/lib/views/album/album_page.dart index 80c7ad8..24b64ed 100644 --- a/lib/views/album/album_page.dart +++ b/lib/views/album/album_page.dart @@ -40,8 +40,7 @@ class AlbumPage extends StatefulWidget { } class _AlbumPageState extends State { - final RefreshController _refreshController = - RefreshController(initialRefresh: false); + final RefreshController _refreshController = RefreshController(initialRefresh: false); final ScrollController _scrollController = ScrollController(); late AlbumModel _currentAlbum; @@ -63,8 +62,7 @@ class _AlbumPageState extends State { super.initState(); } - bool get _hasNonFavorites => - _selectedList.where((image) => !image.favorite).isNotEmpty; + bool get _hasNonFavorites => _selectedList.where((image) => !image.favorite).isNotEmpty; bool get _enableLoad { if (_imageList == null || _imageList!.isEmpty) return false; @@ -85,8 +83,7 @@ class _AlbumPageState extends State { Future _loadMoreImages() async { if (_imageList == null) return; if (_currentAlbum.nbImages <= _imageList!.length) return; - ApiResponse> result = - await fetchImages(widget.album.id, _page + 1); + ApiResponse> result = await fetchImages(widget.album.id, _page + 1); if (result.hasError || !result.hasData) { _refreshController.loadFailed(); await Future.delayed(const Duration(milliseconds: 500)); @@ -100,12 +97,9 @@ class _AlbumPageState extends State { } Future _onRefresh() async { - final result = await Future.wait( - [fetchAlbums(widget.album.id), fetchImages(widget.album.id, 0)]); - final ApiResponse> albumsResult = - result.first as ApiResponse>; - final ApiResponse> imagesResult = - result.last as ApiResponse>; + final result = await Future.wait([fetchAlbums(widget.album.id), fetchImages(widget.album.id, 0)]); + final ApiResponse> albumsResult = result.first as ApiResponse>; + final ApiResponse> imagesResult = result.last as ApiResponse>; if (!albumsResult.hasData || !imagesResult.hasData) { _refreshController.refreshFailed(); await Future.delayed(const Duration(milliseconds: 500)); @@ -120,14 +114,14 @@ class _AlbumPageState extends State { _refreshController.refreshCompleted(); } - void _onAddAlbum() => - onAddAlbum(context, widget.album.id).whenComplete(() => _onRefresh()); - void _onTapAlbum(AlbumModel album) => - onOpenAlbum(context, album).whenComplete(() => _onRefresh()); - void _onEditAlbum(AlbumModel album) => - onEditAlbum(context, album).whenComplete(() => _onRefresh()); - void _onMoveAlbum(AlbumModel album) => - onMoveAlbum(context, album).whenComplete(() => _onRefresh()); + void _onAddAlbum() => onAddAlbum(context, widget.album.id).whenComplete(() => _onRefresh()); + + void _onTapAlbum(AlbumModel album) => onOpenAlbum(context, album).whenComplete(() => _onRefresh()); + + void _onEditAlbum(AlbumModel album) => onEditAlbum(context, album).whenComplete(() => _onRefresh()); + + void _onMoveAlbum(AlbumModel album) => onMoveAlbum(context, album).whenComplete(() => _onRefresh()); + Future _onDeleteAlbum(AlbumModel album) async { return onDeleteAlbum(context, album).then((success) { if (success) _onRefresh(); @@ -135,8 +129,7 @@ class _AlbumPageState extends State { }); } - void _onAlbumPrivacy(AlbumModel album) => - onEditAlbumPrivacy(context, album).whenComplete(() => _onRefresh()); + void _onAlbumPrivacy(AlbumModel album) => onEditAlbumPrivacy(context, album).whenComplete(() => _onRefresh()); void _onTapPhoto(ImageModel image) { Navigator.of(context).pushNamed( @@ -165,13 +158,11 @@ class _AlbumPageState extends State { } Future _onMovePhotos() async { - onMovePhotos(context, _selectedList, _currentAlbum) - .whenComplete(() => _onRefresh()); + onMovePhotos(context, _selectedList, _currentAlbum).whenComplete(() => _onRefresh()); } void _onLikePhotos() { - onLikePhotos(_selectedList, _hasNonFavorites) - .whenComplete(() => _onRefresh()); + onLikePhotos(_selectedList, _hasNonFavorites).whenComplete(() => _onRefresh()); } void _onDeletePhotos() { @@ -236,11 +227,9 @@ class _AlbumPageState extends State { future: _data, builder: (context, snapshot) { if (snapshot.hasData) { - if (snapshot.data!.first.hasError && - snapshot.data!.last.hasError) { + if (snapshot.data!.first.hasError && snapshot.data!.last.hasError) { return Padding( - padding: const EdgeInsets.symmetric( - horizontal: 16.0, vertical: 8.0), + padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0), child: Text( appStrings.categoryImageList_noDataError, textAlign: TextAlign.center, @@ -257,8 +246,7 @@ class _AlbumPageState extends State { child: Padding( padding: const EdgeInsets.all(8.0), child: Text( - appStrings - .imageCount(_currentAlbum.nbTotalImages), + appStrings.imageCount(_currentAlbum.nbTotalImages), style: Theme.of(context).textTheme.titleSmall, ), ), @@ -292,14 +280,12 @@ class _AlbumPageState extends State { Widget get _appBar { Orientation orientation = MediaQuery.of(context).orientation; return SliverAppBar( - backgroundColor: Theme.of(context).colorScheme.background, + backgroundColor: Theme.of(context).colorScheme.surface, pinned: true, titleSpacing: 0, centerTitle: _selectedList.isNotEmpty, title: Text( - _selectedList.isEmpty - ? _currentAlbum.name - : _selectedList.length.toString(), + _selectedList.isEmpty ? _currentAlbum.name : _selectedList.length.toString(), style: Theme.of(context).appBarTheme.titleTextStyle, ), actions: [ @@ -311,8 +297,7 @@ class _AlbumPageState extends State { tooltip: appStrings.categoryImageList_deselectButton, icon: Icon(Icons.cancel), ), - if (orientation == Orientation.landscape && _selectedList.isNotEmpty) - ..._imageActions, + if (orientation == Orientation.landscape && _selectedList.isNotEmpty) ..._imageActions, if (widget.isAdmin || _currentAlbum.canUpload) PopupMenuButton( tooltip: appStrings.imageOptions_title, @@ -329,17 +314,14 @@ class _AlbumPageState extends State { text: appStrings.imageOptions_share, ), ), - if (_selectedList.isNotEmpty && - Preferences.getUserStatus != 'guest') + if (_selectedList.isNotEmpty && Preferences.getUserStatus != 'guest') PopupMenuItem( onTap: () => Future.delayed( const Duration(seconds: 0), _onLikePhotos, ), child: PopupListItem( - icon: _hasNonFavorites - ? Icons.favorite_border - : Icons.favorite, + icon: _hasNonFavorites ? Icons.favorite_border : Icons.favorite, text: _hasNonFavorites ? appStrings.imageOptions_addFavorites : appStrings.imageOptions_removeFavorites, @@ -400,8 +382,7 @@ class _AlbumPageState extends State { Widget _albumGrid(AsyncSnapshot snapshot) { // initialize album list if (_albumList == null) { - final ApiResponse> result = - snapshot.data!.first as ApiResponse>; + final ApiResponse> result = snapshot.data!.first as ApiResponse>; // if only albums has error if (!result.hasData) { return Center( @@ -424,8 +405,7 @@ class _AlbumPageState extends State { Widget _imageGrid(AsyncSnapshot snapshot) { // Initialize image list if (_imageList == null) { - final ApiResponse> result = - snapshot.data!.last as ApiResponse>; + final ApiResponse> result = snapshot.data!.last as ApiResponse>; // if only images has error if (!result.hasData) { return Center( @@ -434,13 +414,11 @@ class _AlbumPageState extends State { } _imageList = result.data!; // Refresh after build (for _enableLoad) - WidgetsBinding.instance - .addPostFrameCallback((timeStamp) => setState(() {})); + WidgetsBinding.instance.addPostFrameCallback((timeStamp) => setState(() {})); } if (_imageList!.isEmpty) return const SizedBox(); // rebuild current selection with new images - _selectedList = - _imageList!.where((image) => _selectedList.contains(image)).toList(); + _selectedList = _imageList!.where((image) => _selectedList.contains(image)).toList(); return ImageGridView( album: _currentAlbum, imageList: _imageList!, @@ -465,9 +443,7 @@ class _AlbumPageState extends State { builder: (context, uploadNotifier, child) { bool uploading = uploadNotifier.uploadList.isNotEmpty; return FloatingActionButton( - tooltip: uploading - ? appStrings.uploadList_title - : appStrings.categorySelection_root, + tooltip: uploading ? appStrings.uploadList_title : appStrings.categorySelection_root, shape: uploading ? CircleBorder() : null, backgroundColor: Theme.of(context).disabledColor.withOpacity(0.7), onPressed: () { @@ -562,14 +538,11 @@ class _AlbumPageState extends State { return AnimatedContainer( duration: const Duration(milliseconds: 300), curve: Curves.easeInOut, - height: _selectedList.isEmpty || orientation == Orientation.landscape - ? 0 - : 56.0, + height: _selectedList.isEmpty || orientation == Orientation.landscape ? 0 : 56.0, child: BottomAppBar( height: 56.0, child: Row( - children: - _imageActions.map((action) => Expanded(child: action)).toList(), + children: _imageActions.map((action) => Expanded(child: action)).toList(), ), ), ); @@ -603,9 +576,7 @@ class _AlbumPageState extends State { if (Preferences.getUserStatus != 'guest') // Todo: enum roles IconButton( onPressed: _onLikePhotos, - tooltip: _hasNonFavorites - ? appStrings.imageOptions_addFavorites - : appStrings.imageOptions_removeFavorites, + tooltip: _hasNonFavorites ? appStrings.imageOptions_addFavorites : appStrings.imageOptions_removeFavorites, isSelected: !_hasNonFavorites, selectedIcon: Icon(Icons.favorite), icon: Icon(Icons.favorite_border), @@ -617,8 +588,6 @@ class _AlbumPageState extends State { ), ]; - return widget.isAdmin || _currentAlbum.canUpload - ? adminActions - : userActions; + return widget.isAdmin || _currentAlbum.canUpload ? adminActions : userActions; } } diff --git a/lib/views/album/album_privacy_page.dart b/lib/views/album/album_privacy_page.dart index 0402e37..68d3d37 100644 --- a/lib/views/album/album_privacy_page.dart +++ b/lib/views/album/album_privacy_page.dart @@ -81,18 +81,16 @@ class _AlbumPrivacyPageState extends State { if (!editResult.hasData || !editResult.data) return; if (_selectedMode == AlbumStatus.private) { - List newGroups = - _groups.where((e) => !_allowedGroups.contains(e)).toList(); - List removedGroups = - _allowedGroups.where((e) => !_groups.contains(e)).toList(); + List newGroups = _groups.where((e) => !_allowedGroups.contains(e)).toList(); + List removedGroups = _allowedGroups.where((e) => !_groups.contains(e)).toList(); - bool addSuccess = await addPermission( + await addPermission( albumId: widget.album.id, groups: newGroups.map((group) => group.id).toList(), recursive: _recursive, ); - bool removeSuccess = await removePermission( + await removePermission( albumId: widget.album.id, groups: removedGroups.map((group) => group.id).toList(), ); @@ -153,8 +151,7 @@ class _AlbumPrivacyPageState extends State { groupValue: _selectedMode, activeColor: Theme.of(context).colorScheme.secondary, title: Text(appStrings.categoryPrivacyMode_public), - subtitle: - Text(appStrings.categoryPrivacyMode_publicMessage), + subtitle: Text(appStrings.categoryPrivacyMode_publicMessage), onChanged: _onChangeMode, ), RadioListTile( @@ -162,8 +159,7 @@ class _AlbumPrivacyPageState extends State { groupValue: _selectedMode, activeColor: Theme.of(context).colorScheme.secondary, title: Text(appStrings.categoryPrivacyMode_private), - subtitle: - Text(appStrings.categoryPrivacyMode_privateMessage), + subtitle: Text(appStrings.categoryPrivacyMode_privateMessage), onChanged: _onChangeMode, ), ], diff --git a/lib/views/authentication/login_form_view.dart b/lib/views/authentication/login_form_view.dart index f65cf1c..a2dcf35 100644 --- a/lib/views/authentication/login_form_view.dart +++ b/lib/views/authentication/login_form_view.dart @@ -32,8 +32,7 @@ class _LoginFormViewState extends State { final TextEditingController _usernameController = TextEditingController(); final TextEditingController _passwordController = TextEditingController(); final GlobalKey _urlKey = GlobalKey(); - final RoundedLoadingButtonController _btnController = - RoundedLoadingButtonController(); + final RoundedLoadingButtonController _btnController = RoundedLoadingButtonController(); String _url = ''; String _username = ''; @@ -169,8 +168,7 @@ class _LoginFormViewState extends State { AppField( key: _urlKey, margin: const EdgeInsets.symmetric(vertical: 4.0), - padding: - const EdgeInsets.symmetric(vertical: 16.0, horizontal: 8.0), + padding: const EdgeInsets.symmetric(vertical: 16.0, horizontal: 8.0), controller: _urlController, onChanged: (value) { bool isError = !_urlValidator(value); @@ -189,8 +187,7 @@ class _LoginFormViewState extends State { ), AppField( margin: const EdgeInsets.symmetric(vertical: 4.0), - padding: - const EdgeInsets.symmetric(vertical: 16.0, horizontal: 8.0), + padding: const EdgeInsets.symmetric(vertical: 16.0, horizontal: 8.0), controller: _usernameController, onChanged: (value) { if (_idError) { @@ -224,8 +221,7 @@ class _LoginFormViewState extends State { ), AppField( margin: const EdgeInsets.symmetric(vertical: 4.0), - padding: - const EdgeInsets.symmetric(vertical: 16.0, horizontal: 8.0), + padding: const EdgeInsets.symmetric(vertical: 16.0, horizontal: 8.0), controller: _passwordController, onFieldSubmitted: (String value) { FocusScope.of(context).unfocus(); @@ -282,7 +278,7 @@ class _LoginFormViewState extends State { ), TextButton( style: ButtonStyle( - foregroundColor: MaterialStateProperty.resolveWith( + foregroundColor: WidgetStateProperty.resolveWith( (states) => Theme.of(context).colorScheme.secondary, ), ), @@ -319,10 +315,7 @@ class _LoginFormViewState extends State { top: Theme.of(context).textTheme.bodyMedium?.fontSize, child: Text( !_isSecured ? 'https' : 'http', - style: Theme.of(context) - .textTheme - .bodyMedium - ?.copyWith(fontSize: 11), + style: Theme.of(context).textTheme.bodyMedium?.copyWith(fontSize: 11), ), ), ], diff --git a/lib/views/authentication/login_settings_page.dart b/lib/views/authentication/login_settings_page.dart index bab9cb8..6e4af7a 100644 --- a/lib/views/authentication/login_settings_page.dart +++ b/lib/views/authentication/login_settings_page.dart @@ -21,10 +21,8 @@ class _LoginSettingsPageState extends State { @override void initState() { - _basicAuthUsername = - appPreferences.getString(Preferences.basicUsernameKey) ?? ''; - _basicAuthPassword = - appPreferences.getString(Preferences.basicPasswordKey) ?? ''; + _basicAuthUsername = appPreferences.getString(Preferences.basicUsernameKey) ?? ''; + _basicAuthPassword = appPreferences.getString(Preferences.basicPasswordKey) ?? ''; _sslEnabled = Preferences.getEnableSSL; _basiAuth = Preferences.getEnableBasicAuth; _rememberCredentials = Preferences.getRememberCredentials; @@ -37,7 +35,7 @@ class _LoginSettingsPageState extends State { appBar: AppBar( title: Text( appStrings.tabBar_preferences, - textScaleFactor: 1, + textScaler: TextScaler.linear(1), style: Theme.of(context).appBarTheme.titleTextStyle, ), ), diff --git a/lib/views/image/image_favorites_page.dart b/lib/views/image/image_favorites_page.dart index 1da49d0..5ea6f64 100644 --- a/lib/views/image/image_favorites_page.dart +++ b/lib/views/image/image_favorites_page.dart @@ -24,8 +24,7 @@ class ImageFavoritesPage extends StatefulWidget { } class _ImageFavoritesPageState extends State { - final RefreshController _refreshController = - RefreshController(initialRefresh: false); + final RefreshController _refreshController = RefreshController(initialRefresh: false); final ScrollController _scrollController = ScrollController(); late final Future> _imageFuture; @@ -59,17 +58,14 @@ class _ImageFavoritesPageState extends State { super.dispose(); } - bool get _hasNonFavorites => - _selectedList.where((image) => !image.favorite).isNotEmpty; + bool get _hasNonFavorites => _selectedList.where((image) => !image.favorite).isNotEmpty; - Future _onWillPop() async { + void _onWillPop(bool pop) async { if (_selectedList.isNotEmpty) { setState(() { _selectedList.clear(); }); - return false; } - return true; } Future _onRefresh() async { @@ -125,26 +121,28 @@ class _ImageFavoritesPageState extends State { if (images == null || images is! List) return; setState(() { _imageList = images; - _page = - ((images.length - 1) / Settings.defaultElementPerPage).floor(); + _page = ((images.length - 1) / Settings.defaultElementPerPage).floor(); }); }); + void _onEditPhotos() => onEditPhotos(context, _selectedList).then((success) { if (success == true) { _selectedList.clear(); _onRefresh(); } }); - void _onLikePhotos() => - onLikePhotos(_selectedList, false).whenComplete(() => _onRefresh()); + + void _onLikePhotos() => onLikePhotos(_selectedList, false).whenComplete(() => _onRefresh()); + _onDeletePhotos() => onDeletePhotos(context, _selectedList).then((success) { if (success) _onRefresh(); }); @override Widget build(BuildContext context) { - return WillPopScope( - onWillPop: _onWillPop, + return PopScope( + canPop: _selectedList.isEmpty, + onPopInvoked: _onWillPop, child: Scaffold( body: SafeArea( child: SmartRefresher( @@ -276,8 +274,7 @@ class _ImageFavoritesPageState extends State { _imageList = result.data!['images'].cast() ?? []; } - _selectedList = - _imageList!.where((image) => _selectedList.contains(image)).toList(); + _selectedList = _imageList!.where((image) => _selectedList.contains(image)).toList(); if (_imageList!.isEmpty) { return Center( @@ -305,14 +302,11 @@ class _ImageFavoritesPageState extends State { return AnimatedContainer( duration: const Duration(milliseconds: 300), curve: Curves.easeInOut, - height: _selectedList.isEmpty || orientation == Orientation.landscape - ? 0 - : 56.0, + height: _selectedList.isEmpty || orientation == Orientation.landscape ? 0 : 56.0, child: BottomAppBar( height: 56.0, child: Row( - children: - _actions.map((action) => Expanded(child: action)).toList(), + children: _actions.map((action) => Expanded(child: action)).toList(), ), ), ); @@ -341,9 +335,7 @@ class _ImageFavoritesPageState extends State { if (Preferences.getUserStatus != 'guest') // Todo: enum roles IconButton( onPressed: _onLikePhotos, - tooltip: _hasNonFavorites - ? appStrings.imageOptions_addFavorites - : appStrings.imageOptions_removeFavorites, + tooltip: _hasNonFavorites ? appStrings.imageOptions_addFavorites : appStrings.imageOptions_removeFavorites, isSelected: !_hasNonFavorites, selectedIcon: Icon(Icons.favorite), icon: Icon(Icons.favorite_border), diff --git a/lib/views/image/image_page.dart b/lib/views/image/image_page.dart index d327d18..d883f00 100644 --- a/lib/views/image/image_page.dart +++ b/lib/views/image/image_page.dart @@ -177,15 +177,14 @@ class _ImagePageState extends State { /// Handler before closing the page. /// * If overlay is hidden, show it. /// * Otherwise, close the page. - Future _onWillPop() async { + void _onWillPop(bool pop) { if (!_showOverlay) { setState(() { _showOverlay = true; }); - return false; + } else { + Navigator.of(context).pop(_imageList); } - Navigator.of(context).pop(_imageList); - return false; } /// Toggle overlay action (orientation was necessary, *see comments*). @@ -282,8 +281,9 @@ class _ImagePageState extends State { @override Widget build(BuildContext context) { - return WillPopScope( - onWillPop: _onWillPop, + return PopScope( + canPop: !_showOverlay, + onPopInvoked: _onWillPop, child: Scaffold( backgroundColor: Colors.black, resizeToAvoidBottomInset: true, @@ -506,8 +506,8 @@ class _ImagePageState extends State { child: IconButton( color: Colors.white, style: ButtonStyle( - backgroundColor: MaterialStateProperty.resolveWith((states) => Colors.black.withOpacity(0.5)), - shape: MaterialStateProperty.resolveWith((states) => CircleBorder()), + backgroundColor: WidgetStateProperty.resolveWith((states) => Colors.black.withOpacity(0.5)), + shape: WidgetStateProperty.resolveWith((states) => CircleBorder()), ), onPressed: () { Navigator.of(context).pushNamed( diff --git a/lib/views/image/image_search_page.dart b/lib/views/image/image_search_page.dart index d814781..41aa507 100644 --- a/lib/views/image/image_search_page.dart +++ b/lib/views/image/image_search_page.dart @@ -63,14 +63,12 @@ class _ImageSearchPageState extends State { bool get _hasNonFavorites => _selectedList.where((image) => !image.favorite).isNotEmpty; - Future _onWillPop() async { + void _onWillPop(bool pop) { if (_selectedList.isNotEmpty) { setState(() { _selectedList.clear(); }); - return false; } - return true; } Future _onRefresh() async { @@ -149,8 +147,9 @@ class _ImageSearchPageState extends State { @override Widget build(BuildContext context) { - return WillPopScope( - onWillPop: _onWillPop, + return PopScope( + canPop: _selectedList.isEmpty, + onPopInvoked: _onWillPop, child: Scaffold( body: SafeArea( child: SmartRefresher( diff --git a/lib/views/image/image_tags_page.dart b/lib/views/image/image_tags_page.dart index c16ebac..f909f17 100644 --- a/lib/views/image/image_tags_page.dart +++ b/lib/views/image/image_tags_page.dart @@ -14,11 +14,7 @@ import 'package:piwigo_ng/views/image/image_page.dart'; import 'package:pull_to_refresh/pull_to_refresh.dart'; class ImageTagsPage extends StatefulWidget { - const ImageTagsPage({ - Key? key, - required this.tag, - this.isAdmin = false - }) : super(key: key); + const ImageTagsPage({Key? key, required this.tag, this.isAdmin = false}) : super(key: key); static const String routeName = '/images/tags'; @@ -30,8 +26,7 @@ class ImageTagsPage extends StatefulWidget { } class _ImageTagsPageState extends State { - final RefreshController _refreshController = - RefreshController(initialRefresh: false); + final RefreshController _refreshController = RefreshController(initialRefresh: false); final ScrollController _scrollController = ScrollController(); late final Future> _imageFuture; @@ -65,17 +60,14 @@ class _ImageTagsPageState extends State { super.dispose(); } - bool get _hasNonFavorites => - _selectedList.where((image) => !image.favorite).isNotEmpty; + bool get _hasNonFavorites => _selectedList.where((image) => !image.favorite).isNotEmpty; - Future _onWillPop() async { + void _onWillPop(bool pop) { if (_selectedList.isNotEmpty) { setState(() { _selectedList.clear(); }); - return false; } - return true; } Future _onRefresh() async { @@ -131,26 +123,28 @@ class _ImageTagsPageState extends State { if (images == null || images is! List) return; setState(() { _imageList = images; - _page = - ((images.length - 1) / Settings.defaultElementPerPage).floor(); + _page = ((images.length - 1) / Settings.defaultElementPerPage).floor(); }); }); + void _onEditPhotos() => onEditPhotos(context, _selectedList).then((success) { if (success == true) { _selectedList.clear(); _onRefresh(); } }); - void _onLikePhotos() => - onLikePhotos(_selectedList, false).whenComplete(() => _onRefresh()); + + void _onLikePhotos() => onLikePhotos(_selectedList, false).whenComplete(() => _onRefresh()); + _onDeletePhotos() => onDeletePhotos(context, _selectedList).then((success) { if (success) _onRefresh(); }); @override Widget build(BuildContext context) { - return WillPopScope( - onWillPop: _onWillPop, + return PopScope( + canPop: _selectedList.isEmpty, + onPopInvoked: _onWillPop, child: Scaffold( body: SafeArea( child: SmartRefresher( @@ -283,8 +277,7 @@ class _ImageTagsPageState extends State { _imageList = result.data!['images'].cast() ?? []; } - _selectedList = - _imageList!.where((image) => _selectedList.contains(image)).toList(); + _selectedList = _imageList!.where((image) => _selectedList.contains(image)).toList(); if (_imageList!.isEmpty) { return Center( @@ -312,14 +305,11 @@ class _ImageTagsPageState extends State { return AnimatedContainer( duration: const Duration(milliseconds: 300), curve: Curves.easeInOut, - height: _selectedList.isEmpty || orientation == Orientation.landscape - ? 0 - : 56.0, + height: _selectedList.isEmpty || orientation == Orientation.landscape ? 0 : 56.0, child: BottomAppBar( height: 56.0, child: Row( - children: - _actions.map((action) => Expanded(child: action)).toList(), + children: _actions.map((action) => Expanded(child: action)).toList(), ), ), ); @@ -348,9 +338,7 @@ class _ImageTagsPageState extends State { if (Preferences.getUserStatus != 'guest') // Todo: enum roles IconButton( onPressed: _onLikePhotos, - tooltip: _hasNonFavorites - ? appStrings.imageOptions_addFavorites - : appStrings.imageOptions_removeFavorites, + tooltip: _hasNonFavorites ? appStrings.imageOptions_addFavorites : appStrings.imageOptions_removeFavorites, isSelected: !_hasNonFavorites, selectedIcon: Icon(Icons.favorite), icon: Icon(Icons.favorite_border), diff --git a/lib/views/image/video_player_page.dart b/lib/views/image/video_player_page.dart index 02c19eb..0c903d0 100644 --- a/lib/views/image/video_player_page.dart +++ b/lib/views/image/video_player_page.dart @@ -60,7 +60,7 @@ class _VideoPlayerPageState extends State { Future initializePlayer() async { if (widget.videoUrl == null) return; - _videoPlayerController = VideoPlayerController.network(widget.videoUrl!); + _videoPlayerController = VideoPlayerController.networkUrl(Uri.parse(widget.videoUrl!)); await _videoPlayerController.initialize(); _createChewieController(); setState(() {}); @@ -85,8 +85,7 @@ class _VideoPlayerPageState extends State { child: Text(appStrings.errorHUD_label), ); } - if (_chewieController == null || - !_chewieController!.videoPlayerController.value.isInitialized) { + if (_chewieController == null || !_chewieController!.videoPlayerController.value.isInitialized) { return Center( child: CircularProgressIndicator(), ); diff --git a/lib/views/image/video_view.dart b/lib/views/image/video_view.dart index 746fd09..4e5b0ee 100644 --- a/lib/views/image/video_view.dart +++ b/lib/views/image/video_view.dart @@ -43,8 +43,8 @@ class _VideoViewState extends State { @override void initState() { super.initState(); - _controller = VideoPlayerController.network( - widget.videoUrl ?? '', + _controller = VideoPlayerController.networkUrl( + Uri.parse(widget.videoUrl!), videoPlayerOptions: VideoPlayerOptions(), )..initialize().then((_) { debugPrint("---- controller initialized"); @@ -79,8 +79,7 @@ class _VideoViewState extends State { final Duration position = controller.value.position; if (!mounted) return; setState(() { - _progress = position.inMilliseconds.ceilToDouble() / - controller.value.duration.inMilliseconds.ceilToDouble(); + _progress = position.inMilliseconds.ceilToDouble() / controller.value.duration.inMilliseconds.ceilToDouble(); }); } } @@ -90,8 +89,7 @@ class _VideoViewState extends State { void _checkControllerEnd() async { if (!mounted) return; if (_controller.value.position.inMilliseconds > 0 && - _controller.value.position.inSeconds >= - _controller.value.duration.inSeconds) { + _controller.value.position.inSeconds >= _controller.value.duration.inSeconds) { setState(() { _isEnd = true; if (!widget.showOverlay) { @@ -190,8 +188,7 @@ class _VideoViewState extends State { Future _onVideoTimeChangeEnd(double value) async { // Parse slider time final double newValue = max(0, min(value, 99)) * 0.01; - final int millis = - (_controller.value.duration.inMilliseconds * newValue).toInt(); + final int millis = (_controller.value.duration.inMilliseconds * newValue).toInt(); // Change time await _controller.seekTo(Duration(milliseconds: millis)); // Resume player @@ -212,9 +209,7 @@ class _VideoViewState extends State { } int hours = duration.inHours; int minutes = (duration - Duration(hours: hours)).inMinutes; - int seconds = - (duration - Duration(hours: hours) - Duration(minutes: minutes)) - .inSeconds; + int seconds = (duration - Duration(hours: hours) - Duration(minutes: minutes)).inSeconds; return '${hours > 0 ? '$hours:' : ''}${minutes < 10 ? '0$minutes' : '$minutes'}:${seconds < 10 ? '0$seconds' : '$seconds'}'; } @@ -271,8 +266,7 @@ class _VideoViewState extends State { /// * Fast forward Widget get _overlayCenter { // Check if the player is processing / loading. - if ((_controller.value.isBuffering && !_isEnd) || - (_isEnd && _controller.value.isPlaying)) { + if ((_controller.value.isBuffering && !_isEnd) || (_isEnd && _controller.value.isPlaying)) { return Center(child: CircularProgressIndicator()); } // If player has ended, show the replay button @@ -362,10 +356,7 @@ class _VideoViewState extends State { padding: const EdgeInsets.all(8.0), child: Text( _durationText, - style: TextStyle( - fontSize: 14, - color: Colors.white, - fontWeight: FontWeight.w500), + style: TextStyle(fontSize: 14, color: Colors.white, fontWeight: FontWeight.w500), ), ), Expanded( @@ -431,8 +422,7 @@ class _VideoViewState extends State { ), ), // Loading... - if (!_controller.value.isInitialized) - Center(child: CircularProgressIndicator()), + if (!_controller.value.isInitialized) Center(child: CircularProgressIndicator()), // Error while loading the video if (_controller.value.hasError) Center( @@ -494,7 +484,7 @@ class _VideoPlayerViewState extends State { Future initializePlayer() async { if (widget.videoUrl == null) return; - _videoPlayerController = VideoPlayerController.network(widget.videoUrl!); + _videoPlayerController = VideoPlayerController.networkUrl(Uri.parse(widget.videoUrl!)); await _videoPlayerController.initialize(); _createChewieController(); setState(() {}); @@ -518,8 +508,7 @@ class _VideoPlayerViewState extends State { child: Text(appStrings.errorHUD_label), ); } - if (_chewieController == null || - !_chewieController!.videoPlayerController.value.isInitialized) { + if (_chewieController == null || !_chewieController!.videoPlayerController.value.isInitialized) { return Center( child: CircularProgressIndicator(), ); diff --git a/lib/views/settings/settings_page.dart b/lib/views/settings/settings_page.dart index 7d9fb6e..8f54a89 100644 --- a/lib/views/settings/settings_page.dart +++ b/lib/views/settings/settings_page.dart @@ -47,9 +47,7 @@ class _SettingsPageState extends State { late SortMethods _imageSort; late bool _stripMetadata; late String _author; - late bool _compressBeforeUpload; late bool _wifiOnly; - late bool _deleteAfterUpload; late bool _downloadNotification; late bool _uploadNotification; late bool _autoUploadEnabled; @@ -67,8 +65,6 @@ class _SettingsPageState extends State { _availablePreviewSizes = Preferences.getAvailableSizes; _author = Preferences.getUploadAuthor ?? ''; _stripMetadata = Preferences.getRemoveMetadata; - _compressBeforeUpload = Preferences.getCompressUpload; - _deleteAfterUpload = Preferences.getDeleteAfterUpload; _wifiOnly = Preferences.getWifiUpload; _quality = Preferences.getUploadQuality; _downloadNotification = Preferences.getDownloadNotification; @@ -108,9 +104,7 @@ class _SettingsPageState extends State { try { if (cacheDir.existsSync()) { - cacheDir - .listSync(recursive: true, followLinks: false) - .forEach((FileSystemEntity entity) { + cacheDir.listSync(recursive: true, followLinks: false).forEach((FileSystemEntity entity) { if (entity is File) { totalSize = totalSize! + entity.lengthSync(); } @@ -171,14 +165,11 @@ class _SettingsPageState extends State { delegate: SliverChildListDelegate([ _serverSection, _logoutSection, - if (appPreferences.getString(Preferences.fileTypesKey) != - null) - _supportedFilesSection, + if (appPreferences.getString(Preferences.fileTypesKey) != null) _supportedFilesSection, _albumsSection, _photosSection, // Hide upload section for non admin / community users - if (appPreferences.getBool(Preferences.isAdminKey) ?? false) - _uploadSection, + if (appPreferences.getBool(Preferences.isAdminKey) ?? false) _uploadSection, // _privacySection, _appearanceSection, _cacheSection, @@ -194,8 +185,7 @@ class _SettingsPageState extends State { } Widget get _serverSection => SettingsSection( - title: appStrings - .settingsHeader_server(appPreferences.getString('VERSION') ?? ''), + title: appStrings.settingsHeader_server(appPreferences.getString('VERSION') ?? ''), children: [ SettingsSectionItemInfo( title: appStrings.settings_server, @@ -210,6 +200,7 @@ class _SettingsPageState extends State { ), ], ); + Widget get _logoutSection => SettingsSection( children: [ SettingsSectionButton( @@ -229,8 +220,9 @@ class _SettingsPageState extends State { ), ], ); + Widget get _supportedFilesSection { - String fileTypes = Preferences.getAvailableFileTypes.join(', ') ?? ''; + String fileTypes = Preferences.getAvailableFileTypes.join(', '); return SettingsSection( color: Colors.transparent, children: [ @@ -258,9 +250,7 @@ class _SettingsPageState extends State { if (size != null) { setState(() { _albumThumbnailSize = size; - appPreferences.setString( - Preferences.albumThumbnailSizeKey, - _albumThumbnailSize); + appPreferences.setString(Preferences.albumThumbnailSizeKey, _albumThumbnailSize); }); } }, @@ -289,6 +279,7 @@ class _SettingsPageState extends State { // ), ], ); + Widget get _photosSection => Column( children: [ SettingsSection( @@ -301,8 +292,7 @@ class _SettingsPageState extends State { if (size == null) return; setState(() { _imageThumbnailSize = size; - appPreferences.setString( - Preferences.imageThumbnailSizeKey, _imageThumbnailSize); + appPreferences.setString(Preferences.imageThumbnailSizeKey, _imageThumbnailSize); }); }, selectedItemBuilder: (context) { @@ -329,8 +319,7 @@ class _SettingsPageState extends State { if (size == null) return; setState(() { _imageFullScreenSize = size; - appPreferences.setString(Preferences.imageFullScreenSizeKey, - _imageFullScreenSize); + appPreferences.setString(Preferences.imageFullScreenSizeKey, _imageFullScreenSize); }); }, selectedItemBuilder: (context) { @@ -357,8 +346,7 @@ class _SettingsPageState extends State { if (sort == null) return; setState(() { _imageSort = sort; - appPreferences.setString( - Preferences.imageSortKey, _imageSort.value); + appPreferences.setString(Preferences.imageSortKey, _imageSort.value); }); }, selectedItemBuilder: (context) { @@ -380,10 +368,8 @@ class _SettingsPageState extends State { ), Builder(builder: (context) { final orientation = MediaQuery.of(context).orientation; - final int nbImages = - Settings.getImageCrossAxisCount(context, _imageRowNumber); - final int maxNbImages = - Settings.getImageCrossAxisCount(context, 6); + final int nbImages = Settings.getImageCrossAxisCount(context, _imageRowNumber); + final int maxNbImages = Settings.getImageCrossAxisCount(context, 6); return SettingsSectionItemSlider( enableField: false, title: appStrings.defaultNberOfThumbnailsShort, @@ -391,13 +377,11 @@ class _SettingsPageState extends State { textWidth: orientation == Orientation.portrait ? 24.0 : 40.0, min: Settings.minImageRowCount.toDouble(), max: Settings.maxImageRowCount.toDouble(), - divisions: - Settings.maxImageRowCount - Settings.minImageRowCount, + divisions: Settings.maxImageRowCount - Settings.minImageRowCount, value: _imageRowNumber.toDouble(), onChanged: (value) => setState(() { _imageRowNumber = value.round(); - appPreferences.setInt( - Preferences.imageRowCountKey, _imageRowNumber); + appPreferences.setInt(Preferences.imageRowCountKey, _imageRowNumber); }), ); }), @@ -406,8 +390,7 @@ class _SettingsPageState extends State { value: _thumbnailTitle, onChanged: (value) => setState(() { _thumbnailTitle = value; - appPreferences.setBool( - Preferences.showThumbnailTitleKey, _thumbnailTitle); + appPreferences.setBool(Preferences.showThumbnailTitleKey, _thumbnailTitle); }), ), ], @@ -419,6 +402,7 @@ class _SettingsPageState extends State { // ), ], ); + Widget get _uploadSection => SettingsSection( title: appStrings.settingsHeader_upload, children: [ @@ -488,11 +472,9 @@ class _SettingsPageState extends State { SettingsSectionItemSwitch( title: appStrings.settings_autoUpload, value: _autoUploadEnabled, - onChanged: (_) => Navigator.of(context) - .pushNamed(AutoUploadPage.routeName) - .then((_) => setState(() { - _autoUploadEnabled = AutoUploadPreferences.getEnabled; - })), + onChanged: (_) => Navigator.of(context).pushNamed(AutoUploadPage.routeName).then((_) => setState(() { + _autoUploadEnabled = AutoUploadPreferences.getEnabled; + })), ), // SettingsSectionItemSwitch( // title: appStrings.settings_deleteImage, @@ -529,15 +511,7 @@ class _SettingsPageState extends State { ), ], ); - Widget get _privacySection => SettingsSection( - title: appStrings.settings_defaultPrivacy, - children: [ - SettingsSectionItemButton( - title: "App Lock", - text: "Off", - ), - ], - ); // todo: use biometry unlock + Widget get _appearanceSection => SettingsSection( title: appStrings.settingsHeader_appearance, children: [ @@ -552,6 +526,7 @@ class _SettingsPageState extends State { ), ], ); + Widget get _cacheSection => SettingsSection( title: appStrings.settingsHeader_cache, children: [ @@ -560,8 +535,7 @@ class _SettingsPageState extends State { builder: (context, snapshot) { String cacheSize = appStrings.none; if (snapshot.hasData && snapshot.data != null) { - cacheSize = - '${snapshot.data!.toStringAsFixed(1)} ${appStrings.settings_cacheMegabytes}'; + cacheSize = '${snapshot.data!.toStringAsFixed(1)} ${appStrings.settings_cacheMegabytes}'; } return SettingsSectionItemInfo( title: appStrings.settings_cacheSize, @@ -587,6 +561,7 @@ class _SettingsPageState extends State { ), ], ); + Widget get _infoSection => SettingsSection( title: appStrings.settingsHeader_about, children: [ @@ -621,8 +596,7 @@ class _SettingsPageState extends State { SettingsSectionItemButton( title: appStrings.settings_language, icon: const Icon(Icons.language), - onPressed: () => - Navigator.of(context).pushNamed(SelectLanguagePage.routeName), + onPressed: () => Navigator.of(context).pushNamed(SelectLanguagePage.routeName), ), SettingsSectionItemButton( title: appStrings.settings_translateWithCrowdin, @@ -657,6 +631,7 @@ class _SettingsPageState extends State { ), ], ); + Widget get _content => SettingsSection( color: Colors.transparent, children: [ diff --git a/lib/views/upload/upload_page.dart b/lib/views/upload/upload_page.dart index a106db4..76ef769 100644 --- a/lib/views/upload/upload_page.dart +++ b/lib/views/upload/upload_page.dart @@ -22,8 +22,7 @@ import 'package:rounded_loading_button/rounded_loading_button.dart'; import 'package:video_player/video_player.dart'; class UploadPage extends StatefulWidget { - const UploadPage({Key? key, required this.imageList, required this.albumId}) - : super(key: key); + const UploadPage({Key? key, required this.imageList, required this.albumId}) : super(key: key); static const String routeName = '/upload'; final List imageList; @@ -33,15 +32,13 @@ class UploadPage extends StatefulWidget { State createState() => _UploadGalleryViewPage(); } -class _UploadGalleryViewPage extends State - with SingleTickerProviderStateMixin { +class _UploadGalleryViewPage extends State with SingleTickerProviderStateMixin { static const double maxCarouselElementWidth = 300.0; static const double carouselHeight = 128.0; final TextEditingController _titleController = TextEditingController(); final TextEditingController _descriptionController = TextEditingController(); late final TextEditingController _authorController; - final RoundedLoadingButtonController _btnController = - RoundedLoadingButtonController(); + final RoundedLoadingButtonController _btnController = RoundedLoadingButtonController(); final List> _levelItems = []; List _tags = []; @@ -52,8 +49,7 @@ class _UploadGalleryViewPage extends State @override void initState() { _imageList = List.from(widget.imageList); - _authorController = - TextEditingController(text: Preferences.getUploadAuthor); + _authorController = TextEditingController(text: Preferences.getUploadAuthor); super.initState(); WidgetsBinding.instance.addPostFrameCallback((_) async { setState(() { @@ -126,17 +122,10 @@ class _UploadGalleryViewPage extends State }); } - void _onDeselectTag(TagModel tag) { - setState(() { - _tags.remove(tag); - }); - } - Future _onUpload() async { _btnController.start(); List tagIds = _tags.map((tag) => tag.id).toList(); - List filesToUpload = - _imageList.where((e) => !_imageExistList.contains(e.path)).toList(); + List filesToUpload = _imageList.where((e) => !_imageExistList.contains(e.path)).toList(); var result = await uploadPhotos(filesToUpload, widget.albumId, info: { 'name': _titleController.text, 'comment': _descriptionController.text, @@ -219,9 +208,7 @@ class _UploadGalleryViewPage extends State disabled: _imageList.isEmpty, onPressed: _onUpload, child: Text( - _imageList.isEmpty - ? appStrings.noImages - : appStrings.imageUploadDetailsButton_title, + _imageList.isEmpty ? appStrings.noImages : appStrings.imageUploadDetailsButton_title, style: Theme.of(context).textTheme.displaySmall, ), ), @@ -343,8 +330,7 @@ class _UploadGalleryViewPage extends State right: index == _imageList.length - 1 ? 8.0 : 0.0, ), child: Builder(builder: (context) { - List? mimeType = - mime(file.path.split('/').last)?.split('/'); + List? mimeType = mime(file.path.split('/').last)?.split('/'); if (mimeType?.first == 'video') { return LocalVideoDetailsCard( video: File(file.path), @@ -397,9 +383,7 @@ class _VideoUploadItemState extends State { final Duration duration = _controller.value.duration; int hours = duration.inHours; int minutes = (duration - Duration(hours: hours)).inMinutes; - int seconds = - (duration - Duration(hours: hours) - Duration(minutes: minutes)) - .inSeconds; + int seconds = (duration - Duration(hours: hours) - Duration(minutes: minutes)).inSeconds; return '${hours > 0 ? '$hours:' : ''}${minutes < 10 ? '0$minutes' : '$minutes'}:${seconds < 10 ? '0$seconds' : '$seconds'}'; } @@ -438,15 +422,11 @@ class _VideoUploadItemState extends State { left: 2, child: Container( padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 2), - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(5), - color: AppColors.black.withOpacity(0.7)), + decoration: + BoxDecoration(borderRadius: BorderRadius.circular(5), color: AppColors.black.withOpacity(0.7)), child: Text( _duration, - style: TextStyle( - color: AppColors.white, - fontSize: 10, - fontWeight: FontWeight.bold), + style: TextStyle(color: AppColors.white, fontSize: 10, fontWeight: FontWeight.bold), ), ), ), diff --git a/pubspec.yaml b/pubspec.yaml index 7b8761f..a1255af 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -49,7 +49,7 @@ dependencies: # Device device_info_plus: ^8.2.0 # Get device info (version) - flutter_local_notifications: ^12.0.2 # Throws notifications on download or upload + flutter_local_notifications: ^17.2.0 # Throws notifications on download or upload open_filex: ^4.3.2 # Open files with devices apps workmanager: ^0.5.0 # Background processes (auto upload) image_gallery_saver: ^2.0.3 # Download images From bcaee2bc3524a622c75957b4ff9bba140569158f Mon Sep 17 00:00:00 2001 From: remartin Date: Fri, 28 Jun 2024 16:03:58 +0200 Subject: [PATCH 48/50] feat: missing album pop scope --- lib/views/album/album_page.dart | 148 +++++++++++++++++--------------- 1 file changed, 80 insertions(+), 68 deletions(-) diff --git a/lib/views/album/album_page.dart b/lib/views/album/album_page.dart index 24b64ed..6e3a59b 100644 --- a/lib/views/album/album_page.dart +++ b/lib/views/album/album_page.dart @@ -197,83 +197,95 @@ class _AlbumPageState extends State { }).then((value) => _refreshController.requestRefresh()); } + void _onWillPop(bool pop) { + if (_selectedList.isNotEmpty) { + setState(() { + _selectedList.clear(); + }); + } + } + @override Widget build(BuildContext context) { - return Scaffold( - body: SafeArea( - child: SmartRefresher( - controller: _refreshController, - scrollController: _scrollController, - enablePullUp: _enableLoad, - onLoading: _loadMoreImages, - onRefresh: _onRefresh, - header: MaterialClassicHeader( - backgroundColor: Theme.of(context).cardColor, - color: Theme.of(context).colorScheme.secondary, - ), - footer: ClassicFooter( - loadingText: appStrings.loadingHUD_label, - noDataText: appStrings.categoryImageList_noDataError, - failedText: appStrings.errorHUD_label, - idleText: '', - canLoadingText: appStrings.loadMoreHUD_label, - ), - child: CustomScrollView( - controller: _scrollController, - slivers: [ - _appBar, - SliverToBoxAdapter( - child: FutureBuilder>( - future: _data, - builder: (context, snapshot) { - if (snapshot.hasData) { - if (snapshot.data!.first.hasError && snapshot.data!.last.hasError) { - return Padding( - padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0), - child: Text( - appStrings.categoryImageList_noDataError, - textAlign: TextAlign.center, - ), - ); - } - return Column( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - _albumGrid(snapshot), - _imageGrid(snapshot), - SizedBox( - height: 72.0, - child: Padding( - padding: const EdgeInsets.all(8.0), - child: Text( - appStrings.imageCount(_currentAlbum.nbTotalImages), - style: Theme.of(context).textTheme.titleSmall, + return PopScope( + canPop: _selectedList.isEmpty, + onPopInvoked: _onWillPop, + child: Scaffold( + body: SafeArea( + child: SmartRefresher( + controller: _refreshController, + scrollController: _scrollController, + enablePullUp: _enableLoad, + onLoading: _loadMoreImages, + onRefresh: _onRefresh, + header: MaterialClassicHeader( + backgroundColor: Theme.of(context).cardColor, + color: Theme.of(context).colorScheme.secondary, + ), + footer: ClassicFooter( + loadingText: appStrings.loadingHUD_label, + noDataText: appStrings.categoryImageList_noDataError, + failedText: appStrings.errorHUD_label, + idleText: '', + canLoadingText: appStrings.loadMoreHUD_label, + ), + child: CustomScrollView( + controller: _scrollController, + slivers: [ + _appBar, + SliverToBoxAdapter( + child: FutureBuilder>( + future: _data, + builder: (context, snapshot) { + if (snapshot.hasData) { + if (snapshot.data!.first.hasError && snapshot.data!.last.hasError) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0), + child: Text( + appStrings.categoryImageList_noDataError, + textAlign: TextAlign.center, + ), + ); + } + return Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + _albumGrid(snapshot), + _imageGrid(snapshot), + SizedBox( + height: 72.0, + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Text( + appStrings.imageCount(_currentAlbum.nbTotalImages), + style: Theme.of(context).textTheme.titleSmall, + ), ), ), - ), - ], + ], + ); + } + return const Center( + child: Padding( + padding: EdgeInsets.all(8.0), + child: CircularProgressIndicator(), + ), ); - } - return const Center( - child: Padding( - padding: EdgeInsets.all(8.0), - child: CircularProgressIndicator(), - ), - ); - }, + }, + ), ), - ), - ], + ], + ), ), ), + bottomNavigationBar: AnimatedSlide( + duration: const Duration(milliseconds: 300), + curve: Curves.easeInOut, + offset: _selectedList.isEmpty ? Offset(0, 1) : Offset.zero, + child: _bottomBar, + ), + floatingActionButton: _adminActionsSpeedDial, ), - bottomNavigationBar: AnimatedSlide( - duration: const Duration(milliseconds: 300), - curve: Curves.easeInOut, - offset: _selectedList.isEmpty ? Offset(0, 1) : Offset.zero, - child: _bottomBar, - ), - floatingActionButton: _adminActionsSpeedDial, ); } From 3c2c1913d8da650288a9429528b16585f5784ab8 Mon Sep 17 00:00:00 2001 From: remartin Date: Fri, 28 Jun 2024 17:55:35 +0200 Subject: [PATCH 49/50] fix: pre-release - Upgraded kotlin version - Fixed JVM - Upgraded DIO version for security issue --- android/.gitignore | 1 + android/app/build.gradle | 8 ++-- android/settings.gradle | 2 +- lib/network/albums.dart | 26 +++++------ lib/network/api_client.dart | 15 +++---- lib/network/api_interceptor.dart | 14 +++--- lib/network/authentication.dart | 33 +++++--------- lib/network/groups.dart | 4 +- lib/network/images.dart | 54 +++++++++------------- lib/network/permissions.dart | 12 +++-- lib/network/tags.dart | 8 ++-- lib/network/upload.dart | 10 ++--- lib/network/users.dart | 12 ++--- lib/services/auto_upload_manager.dart | 65 +++++++++------------------ lib/services/chunked_uploader.dart | 18 ++++---- pubspec.yaml | 2 +- 16 files changed, 113 insertions(+), 171 deletions(-) diff --git a/android/.gitignore b/android/.gitignore index 0a741cb..7ae15b8 100644 --- a/android/.gitignore +++ b/android/.gitignore @@ -9,3 +9,4 @@ GeneratedPluginRegistrant.java # Remember to never publicly share your keystore. # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app key.properties +/build/.last_build_id diff --git a/android/app/build.gradle b/android/app/build.gradle index 7ce7ead..60f3056 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -34,13 +34,13 @@ android { compileOptions { // Flag to enable support for the new language APIs coreLibraryDesugaringEnabled true - // Sets Java compatibility to Java 8 - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 + // Sets Java compatibility to Java 11 + sourceCompatibility JavaVersion.VERSION_11 + targetCompatibility JavaVersion.VERSION_11 } kotlinOptions { - jvmTarget = '1.8' + jvmTarget = '11' } sourceSets { diff --git a/android/settings.gradle b/android/settings.gradle index 08fcadc..13915fe 100644 --- a/android/settings.gradle +++ b/android/settings.gradle @@ -19,7 +19,7 @@ pluginManagement { plugins { id "dev.flutter.flutter-plugin-loader" version "1.0.0" id "com.android.application" version "7.1.3" apply false - id "org.jetbrains.kotlin.android" version "1.7.10" apply false + id "org.jetbrains.kotlin.android" version "1.9.0" apply false } include ":app" \ No newline at end of file diff --git a/lib/network/albums.dart b/lib/network/albums.dart index c601a9b..fa80a34 100644 --- a/lib/network/albums.dart +++ b/lib/network/albums.dart @@ -27,12 +27,10 @@ Future>> fetchAlbums(int albumID) async { ); if (response.statusCode == 200) { - List jsonAlbums = - tryParseJson(response.data)['result']['categories']; + List jsonAlbums = tryParseJson(response.data)['result']['categories']; List albums = List.from(jsonAlbums.map( (album) { - bool canUpload = - appPreferences.getBool(Preferences.isAdminKey) ?? false; + bool canUpload = appPreferences.getBool(Preferences.isAdminKey) ?? false; album['can_upload'] = canUpload; return AlbumModel.fromJson(album); }, @@ -47,8 +45,7 @@ Future>> fetchAlbums(int albumID) async { } if (communityResult.hasData) { for (AlbumModel communityAlbum in communityResult.data) { - int index = - albums.indexWhere((album) => album.id == communityAlbum.id); + int index = albums.indexWhere((album) => album.id == communityAlbum.id); if (index >= 0) { AlbumModel newAlbum = albums.elementAt(index); newAlbum.canUpload = true; @@ -65,7 +62,7 @@ Future>> fetchAlbums(int albumID) async { data: albums, ); } - } on DioError catch (e) { + } on DioException catch (e) { debugPrint(e.message); } catch (e) { debugPrint("$e"); @@ -86,8 +83,7 @@ Future>> fetchCommunityAlbums(int albumID) async { ); if (response.statusCode == 200) { - List jsonAlbums = - json.decode(response.data)['result']['categories']; + List jsonAlbums = json.decode(response.data)['result']['categories']; List albums = List.from(jsonAlbums.map( (album) => AlbumModel.fromJson(album), )); @@ -96,7 +92,7 @@ Future>> fetchCommunityAlbums(int albumID) async { data: albums, ); } - } on DioError catch (e) { + } on DioException catch (e) { debugPrint(e.message); } catch (e) { debugPrint("$e"); @@ -129,7 +125,7 @@ Future>> getAlbumTree([int? startId]) async { data: albums, ); } - } on DioError catch (e) { + } on DioException catch (e) { debugPrint(e.message); } catch (e) { debugPrint("$e"); @@ -164,7 +160,7 @@ Future> addAlbum({ } return ApiResponse(data: true); } - } on DioError catch (e) { + } on DioException catch (e) { debugPrint(e.message); } catch (e) { debugPrint("$e"); @@ -197,7 +193,7 @@ Future> moveAlbum(int catId, int parentCatId) async { } return ApiResponse(data: true); } - } on DioError catch (e) { + } on DioException catch (e) { debugPrint("${e.message}"); } catch (e) { debugPrint("$e"); @@ -239,7 +235,7 @@ Future> editAlbum({ print(data); return ApiResponse(data: true); } - } on DioError catch (e) { + } on DioException catch (e) { debugPrint("${e.message}"); } catch (e) { debugPrint("$e"); @@ -276,7 +272,7 @@ Future> deleteAlbum( } return ApiResponse(data: true); } - } on DioError catch (e) { + } on DioException catch (e) { debugPrint("${e.message}"); } catch (e) { debugPrint("$e"); diff --git a/lib/network/api_client.dart b/lib/network/api_client.dart index 812d306..b472b9f 100644 --- a/lib/network/api_client.dart +++ b/lib/network/api_client.dart @@ -2,8 +2,8 @@ import 'dart:convert'; import 'dart:io'; import 'package:cookie_jar/cookie_jar.dart'; -import 'package:dio/adapter.dart'; import 'package:dio/dio.dart'; +import 'package:dio/io.dart'; import 'package:dio_cookie_manager/dio_cookie_manager.dart'; import 'package:flutter/foundation.dart'; import 'package:piwigo_ng/services/preferences_service.dart'; @@ -17,13 +17,9 @@ class ApiClient { ..interceptors.add(CookieManager(cookieJar)) ..httpClientAdapter = sslHttpClientAdapter; - static HttpClientAdapter get sslHttpClientAdapter { - return DefaultHttpClientAdapter() - ..onHttpClientCreate = (HttpClient client) { - client.badCertificateCallback = piwigoSSLBypass; - return client; - }; - } + static HttpClientAdapter get sslHttpClientAdapter => IOHttpClientAdapter( + createHttpClient: () => HttpClient()..badCertificateCallback = piwigoSSLBypass, + ); static bool piwigoSSLBypass(X509Certificate cert, String host, int port) { if (Preferences.getEnableSSL) { @@ -138,8 +134,7 @@ class ApiClient { class SSLHttpOverrides extends HttpOverrides { @override HttpClient createHttpClient(SecurityContext? context) { - return super.createHttpClient(context) - ..badCertificateCallback = ApiClient.piwigoSSLBypass; + return super.createHttpClient(context)..badCertificateCallback = ApiClient.piwigoSSLBypass; } } diff --git a/lib/network/api_interceptor.dart b/lib/network/api_interceptor.dart index 7abe58b..a84a1e1 100644 --- a/lib/network/api_interceptor.dart +++ b/lib/network/api_interceptor.dart @@ -21,8 +21,7 @@ class ApiInterceptor extends Interceptor { if (prefs.getBool(Preferences.enableBasicAuthKey) ?? false) { String? username = prefs.getString(Preferences.basicUsernameKey) ?? ''; String? password = prefs.getString(Preferences.basicPasswordKey) ?? ''; - String basicAuth = - "Basic ${base64.encode(utf8.encode('$username:$password'))}"; + String basicAuth = "Basic ${base64.encode(utf8.encode('$username:$password'))}"; options.headers['authorization'] = basicAuth; } return super.onRequest(options, handler); @@ -33,18 +32,16 @@ class ApiInterceptor extends Interceptor { Response response, ResponseInterceptorHandler handler, ) async { - debugPrint( - "[${response.statusCode}] ${response.requestOptions.queryParameters['method']}"); + debugPrint("[${response.statusCode}] ${response.requestOptions.queryParameters['method']}"); return super.onResponse(response, handler); } @override void onError( - DioError err, + DioException err, ErrorInterceptorHandler handler, ) async { - debugPrint( - "[${err.response?.statusCode}] ${err.requestOptions.queryParameters['method']}"); + debugPrint("[${err.response?.statusCode}] ${err.requestOptions.queryParameters['method']}"); debugPrint('${err.error}\n${err.response?.data}\n${err.stackTrace}'); switch (err.response?.statusCode) { @@ -61,8 +58,7 @@ class ApiInterceptor extends Interceptor { if (err.error is HandshakeException) { HandshakeException handshakeError = err.error as HandshakeException; String? message = handshakeError.osError?.message; - if (message != null && - message.contains('CERTIFICATE_VERIFY_FAILED')) { + if (message != null && message.contains('CERTIFICATE_VERIFY_FAILED')) { App.scaffoldMessengerKey.currentState?.showSnackBar( errorSnackBar( message: appStrings.loginCertFailed_title, diff --git a/lib/network/authentication.dart b/lib/network/authentication.dart index bf82f48..f20468c 100644 --- a/lib/network/authentication.dart +++ b/lib/network/authentication.dart @@ -23,7 +23,7 @@ Future> pingAPI() async { if (data['stat'] == 'ok') { return ApiResponse(data: data['result']); } - } on DioError catch (e) { + } on DioException catch (e) { debugPrint(e.message); } catch (e) { debugPrint('Error $e'); @@ -88,15 +88,14 @@ Future> loginUser( } ApiResponse status = await sessionStatus(); if (status.hasData) { - Preferences.saveId(status.data!, - username: username, password: password); + Preferences.saveId(status.data!, username: username, password: password); } askMediaPermission(); return ApiResponse( data: true, ); } - } on DioError catch (e) { + } on DioException catch (e) { debugPrint(e.message); } catch (e) { debugPrint('Error $e'); @@ -108,10 +107,7 @@ Future> loginUser( } Future> sessionStatus() async { - Map queries = { - 'format': 'json', - 'method': 'pwg.session.getStatus' - }; + Map queries = {'format': 'json', 'method': 'pwg.session.getStatus'}; try { Response response = await ApiClient.get(queryParameters: queries); @@ -125,7 +121,7 @@ Future> sessionStatus() async { data: StatusModel.fromJson(data['result']), ); } - } on DioError catch (e) { + } on DioException catch (e) { debugPrint(e.message); } catch (e) { debugPrint('Session Status Error: $e'); @@ -139,10 +135,7 @@ Future> sessionStatus() async { } Future communityStatus() async { - Map queries = { - 'format': 'json', - 'method': 'community.session.getStatus' - }; + Map queries = {'format': 'json', 'method': 'community.session.getStatus'}; try { Response response = await ApiClient.get(queryParameters: queries); @@ -150,7 +143,7 @@ Future communityStatus() async { if (data['stat'] == 'ok') { return data['result']['real_user_status']; } - } on DioError catch (e) { + } on DioException catch (e) { debugPrint(e.message); } catch (e) { debugPrint('Error $e'); @@ -169,7 +162,7 @@ Future> getInfo() async { data: InfoModel.fromJson(data['result']), ); } - } on DioError catch (e) { + } on DioException catch (e) { debugPrint(e.message); } catch (e) { debugPrint('Error $e'); @@ -180,18 +173,14 @@ Future> getInfo() async { } Future>> getMethods() async { - Map queries = { - 'format': 'json', - 'method': 'reflection.getMethodList' - }; + Map queries = {'format': 'json', 'method': 'reflection.getMethodList'}; try { Response response = await ApiClient.get(queryParameters: queries); Map data = json.decode(response.data); - final List methods = - data['result']['methods'].map((e) => e.toString()).toList(); + final List methods = data['result']['methods'].map((e) => e.toString()).toList(); return ApiResponse>(data: methods); - } on DioError catch (e) { + } on DioException catch (e) { debugPrint(e.message); } catch (e) { debugPrint('Error $e'); diff --git a/lib/network/groups.dart b/lib/network/groups.dart index 6f45009..b3fd988 100644 --- a/lib/network/groups.dart +++ b/lib/network/groups.dart @@ -57,7 +57,7 @@ Future?> getAllGroups({ } return groupsResponse; - } on DioError catch (e) { + } on DioException catch (e) { debugPrint('Fetch all groups: ${e.message}'); } on Error catch (e) { debugPrint('Fetch all groups: ${e.stackTrace}'); @@ -89,7 +89,7 @@ Future?>> getGroups([int page = 0]) async { data: groups, ); } - } on DioError catch (e) { + } on DioException catch (e) { debugPrint('Fetch groups: ${e.message}'); } on Error catch (e) { debugPrint('Fetch groups: ${e.stackTrace}'); diff --git a/lib/network/images.dart b/lib/network/images.dart index 7fe1c6f..033a7a6 100644 --- a/lib/network/images.dart +++ b/lib/network/images.dart @@ -44,7 +44,7 @@ Future> getImage( ImageModel image = ImageModel.fromJson(jsonImage); return ApiResponse(data: image); } - } on DioError catch (e) { + } on DioException catch (e) { debugPrint('Fetch images: ${e.message}'); } on Error catch (e) { debugPrint('Fetch images: $e\n${e.stackTrace}'); @@ -76,7 +76,7 @@ Future>> fetchImages( return ApiResponse>(data: images); } - } on DioError catch (e) { + } on DioException catch (e) { debugPrint('Fetch images: ${e.message}'); } catch (e) { debugPrint('Fetch images: $e'); @@ -117,7 +117,7 @@ Future> searchImages( 'images': images, }); } - } on DioError catch (e) { + } on DioException catch (e) { debugPrint('Search images: ${e.message}'); } on Error catch (e) { debugPrint('Search images: ${e.stackTrace}'); @@ -159,7 +159,7 @@ Future> fetchFavorites([ 'images': images, }); } - } on DioError catch (e) { + } on DioException catch (e) { debugPrint('Fetch favorites: ${e.message}'); } on Error catch (e) { debugPrint('Fetch favorites: ${e.stackTrace}'); @@ -202,7 +202,7 @@ Future> fetchTagImages(int tagID, [int page = 0]) async { 'images': images, }); } - } on DioError catch (e) { + } on DioException catch (e) { debugPrint('Fetch tag images: ${e.message}'); } on Error catch (e) { debugPrint('Fetch tag images: ${e.stackTrace}'); @@ -228,12 +228,8 @@ Future _showDownloadNotification({ ); await showLocalNotification( id: 0, - title: success - ? appStrings.downloadImageSuccess_title - : appStrings.downloadImageFail_title, - body: success - ? appStrings.downloadImageSuccess_message - : appStrings.deleteImageFail_message, + title: success ? appStrings.downloadImageSuccess_title : appStrings.downloadImageFail_title, + body: success ? appStrings.downloadImageSuccess_message : appStrings.deleteImageFail_message, details: android, payload: payload, ); @@ -306,7 +302,7 @@ Future downloadImage( name: image.name, ); return XFile(localPath); - } on DioError catch (e) { + } on DioException catch (e) { debugPrint("Download images: ${e.message}"); } on Error catch (e) { debugPrint("Download images: ${e.stackTrace}"); @@ -361,7 +357,7 @@ Future deleteImage( if (response.statusCode == 200) { return true; } - } on DioError catch (e) { + } on DioException catch (e) { debugPrint('Delete images: ${e.message}'); } on Error catch (e) { debugPrint('Delete images: ${e.stackTrace}'); @@ -387,8 +383,7 @@ Future removeImage( ImageModel image, int albumId, ) async { - final List albums = - image.categories.map((album) => album['id']).toList(); + final List albums = image.categories.map((album) => album['id']).toList(); albums.removeWhere((album) => album == albumId); if (albums.isEmpty) { @@ -406,13 +401,12 @@ Future removeImage( }); try { - Response response = - await ApiClient.post(data: formData, queryParameters: queries); + Response response = await ApiClient.post(data: formData, queryParameters: queries); if (response.statusCode == 200) { return true; } - } on DioError catch (e) { + } on DioException catch (e) { debugPrint('Remove images: ${e.message}'); } on Error catch (e) { debugPrint('Remove images: ${e.stackTrace}'); @@ -440,8 +434,7 @@ Future moveImage( int oldAlbumId, int newAlbumId, ) async { - final List albums = - image.categories.map((album) => album['id']).toList(); + final List albums = image.categories.map((album) => album['id']).toList(); albums.removeWhere((id) => id == oldAlbumId); albums.add(newAlbumId); Map queries = { @@ -456,13 +449,12 @@ Future moveImage( }); try { - Response response = - await ApiClient.post(data: formData, queryParameters: queries); + Response response = await ApiClient.post(data: formData, queryParameters: queries); if (response.statusCode == 200) { return true; } - } on DioError catch (e) { + } on DioException catch (e) { debugPrint('Move images: ${e.message}'); } on Error catch (e) { debugPrint('Move images: ${e.stackTrace}'); @@ -476,8 +468,7 @@ Future assignImages( ) async { int nbAssigned = 0; for (ImageModel image in images) { - final List categories = - image.categories.map((album) => album['id']).toList(); + final List categories = image.categories.map((album) => album['id']).toList(); categories.add(albumId); bool response = await assignImage(image.id, categories); if (response == true) { @@ -503,13 +494,12 @@ Future assignImage( }); try { - Response response = - await ApiClient.post(data: formData, queryParameters: queries); + Response response = await ApiClient.post(data: formData, queryParameters: queries); if (response.statusCode == 200) { return true; } - } on DioError catch (e) { + } on DioException catch (e) { debugPrint('Assign images: ${e.message}'); } on Error catch (e) { debugPrint('Assign images: ${e.stackTrace}'); @@ -568,7 +558,7 @@ Future editImage( if (response.statusCode == 200) { return true; } - } on DioError catch (e) { + } on DioException catch (e) { debugPrint('Edit images: ${e.message}'); } on Error catch (e) { debugPrint('Edit images: ${e.stackTrace}'); @@ -609,7 +599,7 @@ Future> checkImagesNotExist( existResult.removeWhere((key, value) => value != null); } return existResult.keys.map((md5sum) => md5sumList[md5sum]!).toList(); - } on DioError catch (e) { + } on DioException catch (e) { debugPrint('Edit images: ${e.message}'); } on Error catch (e) { debugPrint('Edit images: ${e.stackTrace}'); @@ -653,9 +643,7 @@ String? cleanImageUrl(String? originalUrl) { // So we remove the path to avoid a duplicate if necessary String? loginUrl = appPreferences.getString(Preferences.serverUrlKey); loginUrl = removeUrlProtocol(loginUrl); - if (loginUrl != null && - loginUrl.isNotEmpty && - leftUrl.startsWith(loginUrl)) { + if (loginUrl != null && loginUrl.isNotEmpty && leftUrl.startsWith(loginUrl)) { leftUrl = leftUrl.substring(loginUrl.length); } diff --git a/lib/network/permissions.dart b/lib/network/permissions.dart index 10faeeb..b474b3c 100644 --- a/lib/network/permissions.dart +++ b/lib/network/permissions.dart @@ -10,8 +10,7 @@ import 'package:shared_preferences/shared_preferences.dart'; Future getAlbumPermissions( int albumId, ) async { - List? permissions = - await getPermissions(byAlbum: albumId); + List? permissions = await getPermissions(byAlbum: albumId); if (permissions == null || permissions.isEmpty) { return null; } @@ -52,13 +51,12 @@ Future?> getPermissions({ Map data = json.decode(response.data); var jsonPermissions = data['result']['categories']; List permissions = List.from( - jsonPermissions - .map((permission) => AlbumPermissionModel.fromJson(permission)), + jsonPermissions.map((permission) => AlbumPermissionModel.fromJson(permission)), ); return permissions; } - } on DioError catch (e) { + } on DioException catch (e) { debugPrint('Fetch permissions: ${e.message}'); } on Error catch (e) { debugPrint('Fetch permissions: ${e.stackTrace}'); @@ -103,7 +101,7 @@ Future addPermission({ } return false; } - } on DioError catch (e) { + } on DioException catch (e) { debugPrint('Add permission: ${e.message}'); } on Error catch (e) { debugPrint('Add permission: ${e.stackTrace}'); @@ -146,7 +144,7 @@ Future removePermission({ } return false; } - } on DioError catch (e) { + } on DioException catch (e) { debugPrint('Add permission: ${e.message}'); } on Error catch (e) { debugPrint('Add permission: ${e.stackTrace}'); diff --git a/lib/network/tags.dart b/lib/network/tags.dart index a37ef98..c038000 100644 --- a/lib/network/tags.dart +++ b/lib/network/tags.dart @@ -25,7 +25,7 @@ Future>> getTags() async { List tags = data['result']['tags'].map((tag) => TagModel.fromJson(tag)).toList(); return ApiResponse(data: tags); } - } on DioError catch (e) { + } on DioException catch (e) { debugPrint('Get tags: ${e.message}'); } on Error catch (e) { debugPrint('Get tags: $e\n${e.stackTrace}'); @@ -50,7 +50,7 @@ Future>> getAdminTags() async { List tags = data['result']['tags'].map((tag) => TagModel.fromJson(tag)).toList(); return ApiResponse(data: tags); } - } on DioError catch (e) { + } on DioException catch (e) { debugPrint('Get tags: ${e.message}'); } on Error catch (e) { debugPrint('Get tags: $e\n${e.stackTrace}'); @@ -77,7 +77,7 @@ Future> createTag(String name) async { data: TagModel.fromJson(data['result']), ); } - } on DioError catch (e) { + } on DioException catch (e) { debugPrint('Fetch tags: ${e.message}'); } on Error catch (e) { debugPrint('Fetch tags: $e\n${e.stackTrace}'); @@ -105,7 +105,7 @@ Future editTag(int tagId, String tagName) async { } return ApiResponse(data: true); } - } on DioError catch (e) { + } on DioException catch (e) { debugPrint('Get tags: ${e.message}'); } on Error catch (e) { debugPrint('Get tags: $e\n${e.stackTrace}'); diff --git a/lib/network/upload.dart b/lib/network/upload.dart index 3b47584..c1aa2ca 100644 --- a/lib/network/upload.dart +++ b/lib/network/upload.dart @@ -137,7 +137,7 @@ Future> uploadPhotos( // todo: delete real file path, not the cached one. } } - } on DioError catch (e) { + } on DioException catch (e) { debugPrint("${e.message}"); debugPrint("${e.stackTrace}"); uploadNotifier.itemUploadCompleted(item, error: true); @@ -164,7 +164,7 @@ Future> uploadPhotos( if (await methodExist('community.images.uploadCompleted')) { await communityUploadCompleted(result, albumId); } - } on DioError catch (e) { + } on DioException catch (e) { debugPrint(e.message); } @@ -202,7 +202,7 @@ Future uploadChunk({ if (info['name'] != '' && info['name'] != null) fields['name'] = info['name']; if (info['comment'] != '' && info['comment'] != null) fields['comment'] = info['comment']; if (info['tag_ids']?.isNotEmpty ?? false) fields['tag_ids'] = info['tag_ids'].join(','); - if (info['level'] != -1) fields['level'] = info['level']; + if (info['level'] != -1 && info['level'] != null) fields['level'] = info['level']; // Create dio client Dio dio = Dio( @@ -247,7 +247,7 @@ Future uploadCompleted(List imageId, int categoryId) async { if (response.statusCode == 200) { return true; } - } on DioError catch (e) { + } on DioException catch (e) { debugPrint("$e"); } return false; @@ -269,7 +269,7 @@ Future communityUploadCompleted(List imageId, int categoryId) async { if (response.statusCode == 200) { return true; } - } on DioError catch (e) { + } on DioException catch (e) { debugPrint("$e"); } return false; diff --git a/lib/network/users.dart b/lib/network/users.dart index e507ddb..784c3bc 100644 --- a/lib/network/users.dart +++ b/lib/network/users.dart @@ -58,7 +58,7 @@ Future?> getAllUsers({ } return usersResponse; - } on DioError catch (e) { + } on DioException catch (e) { debugPrint('Fetch all users: ${e.message}'); } on Error catch (e) { debugPrint('Fetch all users: ${e.stackTrace}'); @@ -90,7 +90,7 @@ Future>> getUsers([int page = 0]) async { data: users, ); } - } on DioError catch (e) { + } on DioException catch (e) { debugPrint('Fetch users: ${e.message}'); } on Error catch (e) { debugPrint('Fetch users: ${e.stackTrace}'); @@ -140,7 +140,7 @@ Future>> getAllAdmins() async { return ApiResponse>( data: users, ); - } on DioError catch (e) { + } on DioException catch (e) { debugPrint('Fetch admins: ${e.message}'); } on Error catch (e) { debugPrint('Fetch admins: ${e.stackTrace}'); @@ -167,7 +167,7 @@ Future>> fetchFavorites(int page) async { return ApiResponse>(data: images); } - } on DioError catch (e) { + } on DioException catch (e) { debugPrint('Fetch favorites: ${e.message}'); } on Error catch (e) { debugPrint('Fetch favorites: ${e.stackTrace}'); @@ -200,7 +200,7 @@ Future addFavorite(int imageId) async { if (response.statusCode == 200) { return true; } - } on DioError catch (e) { + } on DioException catch (e) { debugPrint('Add favorite: ${e.message}'); } on Error catch (e) { debugPrint('Add favorite: ${e.stackTrace}'); @@ -234,7 +234,7 @@ Future removeFavorite(int imageId) async { if (response.statusCode == 200) { return true; } - } on DioError catch (e) { + } on DioException catch (e) { debugPrint('Remove favorite: ${e.message}'); } on Error catch (e) { debugPrint('Remove favorite: ${e.stackTrace}'); diff --git a/lib/services/auto_upload_manager.dart b/lib/services/auto_upload_manager.dart index ea0eb3e..381ba66 100644 --- a/lib/services/auto_upload_manager.dart +++ b/lib/services/auto_upload_manager.dart @@ -56,8 +56,7 @@ class AutoUploadManager { // Save a copy of the current account credentials await AutoUploadPreferences.saveCredentials(); // Get task frequency - int hours = prefs.getInt(AutoUploadPreferences.frequencyKey) ?? - Settings.defaultAutoUploadFrequency; + int hours = prefs.getInt(AutoUploadPreferences.frequencyKey) ?? Settings.defaultAutoUploadFrequency; // Enable auto upload prefs.setBool(AutoUploadPreferences.enabledKey, true); // Register task @@ -90,10 +89,7 @@ class AutoUploadManager { List dirFiles = appDocDir.listSync(recursive: true); // Remove folders and links - List files = dirFiles - .where((file) => file is File) - .map((e) => e as File) - .toList(); + List files = dirFiles.where((file) => file is File).map((e) => e as File).toList(); // Convert .heic files to .jpg for (File file in files) { @@ -138,10 +134,8 @@ class AutoUploadManager { cookieJar.delete(Uri.parse(url)); // Get server credentials - String? username = - await storage.read(key: AutoUploadPreferences.usernameKey); - String? password = - await storage.read(key: AutoUploadPreferences.passwordKey); + String? username = await storage.read(key: AutoUploadPreferences.usernameKey); + String? password = await storage.read(key: AutoUploadPreferences.passwordKey); // Get destination album String? albumJson = prefs.getString(AutoUploadPreferences.destinationKey); @@ -189,7 +183,7 @@ class AutoUploadManager { // todo: delete file } } - } on DioError catch (e) { + } on DioException catch (e) { debugPrint("Dio Error ${e.type}"); nbError++; } on Error catch (e) { @@ -221,13 +215,10 @@ class AutoUploadManager { ); } - String? username = - await secureStorage.read(key: AutoUploadPreferences.usernameKey); - String? password = - await secureStorage.read(key: AutoUploadPreferences.passwordKey); + String? username = await secureStorage.read(key: AutoUploadPreferences.usernameKey); + String? password = await secureStorage.read(key: AutoUploadPreferences.passwordKey); - if ((username == null || username.isEmpty) && - (password == null || password.isEmpty)) { + if ((username == null || username.isEmpty) && (password == null || password.isEmpty)) { return ApiResponse( data: false, error: ApiErrors.wrongServerUrl, @@ -278,7 +269,7 @@ class AutoUploadManager { data: false, error: ApiErrors.wrongLoginId, ); - } on DioError catch (e) { + } on DioException catch (e) { debugPrint(e.message); } catch (e) { debugPrint('Error $e'); @@ -290,10 +281,7 @@ class AutoUploadManager { } Future> _sessionStatus() async { - Map queries = { - 'format': 'json', - 'method': 'pwg.session.getStatus' - }; + Map queries = {'format': 'json', 'method': 'pwg.session.getStatus'}; try { Response response = await dio.get('ws.php', queryParameters: queries); @@ -308,7 +296,7 @@ class AutoUploadManager { data: StatusModel.fromJson(data['result']), ); } - } on DioError catch (e) { + } on DioException catch (e) { debugPrint(e.message); } catch (e) { debugPrint('Error $e'); @@ -319,10 +307,7 @@ class AutoUploadManager { } Future _communityStatus() async { - Map queries = { - 'format': 'json', - 'method': 'community.session.getStatus' - }; + Map queries = {'format': 'json', 'method': 'community.session.getStatus'}; try { Response response = await dio.get('ws.php', queryParameters: queries); @@ -330,7 +315,7 @@ class AutoUploadManager { if (data['stat'] == 'ok') { return data['result']['real_user_status']; } - } on DioError catch (e) { + } on DioException catch (e) { debugPrint(e.message); } catch (e) { debugPrint('Error $e'); @@ -371,7 +356,7 @@ class AutoUploadManager { existResult.removeWhere((key, value) => value != null); } return existResult.keys.map((md5sum) => md5sumList[md5sum]!).toList(); - } on DioError catch (e) { + } on DioException catch (e) { debugPrint('Edit images: ${e.message}'); } on Error catch (e) { debugPrint('Edit images: ${e.stackTrace}'); @@ -386,16 +371,13 @@ class AutoUploadManager { if (result.data?.contains('community.images.uploadCompleted') ?? false) { await communityAutoUploadCompleted(idList, destinationId); } - } on DioError catch (e) { + } on DioException catch (e) { debugPrint(e.message); } } Future>> _getMethods() async { - Map queries = { - 'format': 'json', - 'method': 'reflection.getMethodList' - }; + Map queries = {'format': 'json', 'method': 'reflection.getMethodList'}; try { Response response = await dio.get( @@ -403,10 +385,9 @@ class AutoUploadManager { queryParameters: queries, ); Map data = json.decode(response.data); - final List methods = - data['result']['methods'].map((e) => e.toString()).toList(); + final List methods = data['result']['methods'].map((e) => e.toString()).toList(); return ApiResponse>(data: methods); - } on DioError catch (e) { + } on DioException catch (e) { debugPrint(e.message); } catch (e) { debugPrint('Error $e'); @@ -425,8 +406,7 @@ class AutoUploadManager { }; FormData formData = FormData.fromMap({ 'image_id': imageId, - 'pwg_token': - await secureStorage.read(key: AutoUploadPreferences.tokenKey), + 'pwg_token': await secureStorage.read(key: AutoUploadPreferences.tokenKey), 'category_id': categoryId, }); @@ -439,7 +419,7 @@ class AutoUploadManager { if (response.statusCode == 200) { return true; } - } on DioError catch (e) { + } on DioException catch (e) { debugPrint("$e"); } return false; @@ -456,8 +436,7 @@ class AutoUploadManager { }; FormData formData = FormData.fromMap({ 'image_id': imageId, - 'pwg_token': - await secureStorage.read(key: AutoUploadPreferences.tokenKey), + 'pwg_token': await secureStorage.read(key: AutoUploadPreferences.tokenKey), 'category_id': categoryId, }); try { @@ -469,7 +448,7 @@ class AutoUploadManager { if (response.statusCode == 200) { return true; } - } on DioError catch (e) { + } on DioException catch (e) { debugPrint("$e"); } return false; diff --git a/lib/services/chunked_uploader.dart b/lib/services/chunked_uploader.dart index 23e78ce..7207bd4 100644 --- a/lib/services/chunked_uploader.dart +++ b/lib/services/chunked_uploader.dart @@ -91,7 +91,11 @@ class UploadRequest { 'chunk': i, 'chunk_sum': chunkSums[i], 'original_sum': originalSum, - 'file': MultipartFile(chunkStream, end - start, filename: fileName), + 'file': MultipartFile.fromStream( + () => chunkStream, + end - start, + filename: fileName, + ), ...data }); @@ -108,16 +112,14 @@ class UploadRequest { onSendProgress: (current, total) => _updateProgress(i, current, total), ); - if (response.data != null && - json.decode(response.data)?['result']?['id'] != null) { + if (response.data != null && json.decode(response.data)?['result']?['id'] != null) { finalResponse = response; } } return finalResponse; } - Stream> _getChunkStream(int start, int end) => - _file.openRead(start, end); + Stream> _getChunkStream(int start, int end) => _file.openRead(start, end); _updateProgress(int chunkIndex, int chunkCurrent, int chunkTotal) { int totalUploadedSize = (chunkIndex * _maxChunkSize) + chunkCurrent; @@ -127,11 +129,9 @@ class UploadRequest { int _getChunkStart(int chunkIndex) => chunkIndex * _maxChunkSize; - int _getChunkEnd(int chunkIndex) => - min((chunkIndex + 1) * _maxChunkSize, _fileSize); + int _getChunkEnd(int chunkIndex) => min((chunkIndex + 1) * _maxChunkSize, _fileSize); - Map _getHeaders(int start, int end) => - {'Content-Range': 'bytes $start-${end - 1}/$_fileSize'}; + Map _getHeaders(int start, int end) => {'Content-Range': 'bytes $start-${end - 1}/$_fileSize'}; int get _chunksCount => (_fileSize / _maxChunkSize).ceil(); } diff --git a/pubspec.yaml b/pubspec.yaml index a1255af..b1c3dda 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -16,7 +16,7 @@ dependencies: # Network url_launcher: ^6.1.5 # Open links with device's applications webview_flutter: ^3.0.4 # Show web page (CGU) - dio: ^4.0.6 # Requests to API + dio: ^5.4.3+1 # Requests to API dio_cookie_manager: ^2.0.0 # Compatibility between dio and cookie_far cookie_jar: ^3.0.1 # Handles cookies connectivity_plus: ^3.0.2 # Check if Wifi is enabled From e1a3807d840034500f819beba9789567089d0f7c Mon Sep 17 00:00:00 2001 From: remartin Date: Fri, 28 Jun 2024 18:09:40 +0200 Subject: [PATCH 50/50] fix: updated app version --- android/settings.gradle | 2 +- pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/android/settings.gradle b/android/settings.gradle index 13915fe..138ec95 100644 --- a/android/settings.gradle +++ b/android/settings.gradle @@ -19,7 +19,7 @@ pluginManagement { plugins { id "dev.flutter.flutter-plugin-loader" version "1.0.0" id "com.android.application" version "7.1.3" apply false - id "org.jetbrains.kotlin.android" version "1.9.0" apply false + id "org.jetbrains.kotlin.android" version "1.9.22" apply false } include ":app" \ No newline at end of file diff --git a/pubspec.yaml b/pubspec.yaml index b1c3dda..b7a4120 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -3,7 +3,7 @@ description: A Piwigo Android application publish_to: 'none' # Remove this line if you wish to publish to pub.dev -version: 2.2.3+223 +version: 2.3.0+230 environment: sdk: ">=3.0.0 <4.0.0"