diff --git a/projects/lib/controller/eventHandler/deepLinkListener.dart b/projects/lib/controller/eventHandler/deepLinkListener.dart index 76dfa34..14e0b8d 100644 --- a/projects/lib/controller/eventHandler/deepLinkListener.dart +++ b/projects/lib/controller/eventHandler/deepLinkListener.dart @@ -1,4 +1,3 @@ -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_branch_sdk/flutter_branch_sdk.dart'; diff --git a/projects/lib/controller/internal/imageDataExtractor.dart b/projects/lib/controller/internal/imageDataExtractor.dart index 7f245e8..fe28938 100644 --- a/projects/lib/controller/internal/imageDataExtractor.dart +++ b/projects/lib/controller/internal/imageDataExtractor.dart @@ -9,11 +9,13 @@ class ImageDataExtractor { Future> futureCollector() async { // TODO check if metadata tags are same on other devices + // Returns an empty list when something goes wrong List maps = List.empty(growable: true); try { for (XFile image in collector.images) { Map infoMap = new Map(); - infoMap['image'] = Image.file(File(image.path), height: 100); + infoMap['image'] = Image.file(File(image.path), + scale: 0.5, filterQuality: FilterQuality.medium, fit: BoxFit.cover); final imageBytes = await image.readAsBytes(); final decodedImage = await decodeImageFromList(imageBytes); final exifData = await readExifFromBytes(imageBytes); @@ -51,8 +53,10 @@ class ImageDataExtractor { String fileName = infoMap['fileName'].toString().split(".")[1]; infoMap['fileType'] = "." + fileName.substring(0, fileName.length - 1); collector.fileType = infoMap['fileType']; + maps.add(infoMap); } + if (!_assureSameFiletype(maps)) return List.empty(); return maps; } catch (e) { // Somehow, thrown errors don't get printed to console, so I print them as well. @@ -60,4 +64,21 @@ class ImageDataExtractor { throw ("Error while processing image: $e"); } } + + bool _assureSameFiletype(List maps) { + // Makes sure all selected files have the same filetype + String? fileType1; + String? fileType2; + for (Map map in maps) { + fileType1 = map['fileType']; + if (fileType2 != null) { + if (fileType1 != fileType2) { + // If different file types have been found + return false; + } + } + fileType2 = fileType1; + } + return true; + } } diff --git a/projects/lib/controller/wiki/uploadService.dart b/projects/lib/controller/wiki/uploadService.dart index df9efa0..81464f8 100644 --- a/projects/lib/controller/wiki/uploadService.dart +++ b/projects/lib/controller/wiki/uploadService.dart @@ -16,6 +16,7 @@ class UploadService { uploadImage( List images, String fileName, + String fileType, String source, List description, String author, @@ -37,19 +38,19 @@ class UploadService { XFile image = images[i]; progressStream.progress(progressNumber); - map = await _getCsrfToken(); + map = await getCsrfToken(); token = map["tokens"]["csrftoken"]; - String batchFileName = fileName; + String batchFileName = fileName + fileType; if (images.length != 1) { - batchFileName = fileName + "_" + i.toString(); + batchFileName = fileName + "_" + (i + 1).toString() + fileType; } await _sendImage(image, batchFileName, token); progressStream.progress(progressNumber); - map = await _getCsrfToken(); + map = await getCsrfToken(); token = map["tokens"]["csrftoken"]; await _editDetails(author, description, license, source, date, - categories, fileName, token); + categories, batchFileName, token); progressStream.progress(progressNumber); // map = await _getCsrfToken(); @@ -94,7 +95,7 @@ class UploadService { Uri.parse("$WIKIMEDIA_API?format=json" + "&action=upload" + "&filename=$fileName")); - request.headers['Authorization'] = "Bearer " + await _getAccessToken(); + request.headers['Authorization'] = "Bearer " + await getAccessToken(); request.fields['token'] = csrfToken; request.files.add(await _convertToMultiPartFile(image, fileName)); @@ -142,7 +143,7 @@ ${_generateDescriptions(description)} Uri.parse('$WIKIMEDIA_API?action=edit&format=json'), headers: { 'Content-Type': 'application/x-www-form-urlencoded ', - 'Authorization': 'Bearer ${await _getAccessToken()}', + 'Authorization': 'Bearer ${await getAccessToken()}', }, body: { 'title': 'File:' + filename, @@ -166,12 +167,12 @@ ${_generateDescriptions(description)} } // For debug purposes - _checkCsrfToken(String token) async { + checkCsrfToken(String token) async { Future response = http.post( Uri.parse('$WIKIMEDIA_API?action=checktoken&type=csrf&format=json'), headers: { 'Content-Type': 'application/x-www-form-urlencoded ', - 'Authorization': 'Bearer ${await _getAccessToken()}', + 'Authorization': 'Bearer ${await getAccessToken()}', }, body: { 'token': token, @@ -184,12 +185,12 @@ ${_generateDescriptions(description)} } } - Future _getCsrfToken() async { + Future getCsrfToken() async { // Get a CSRF Token (https://www.mediawiki.org/wiki/API:Tokens) Future response = http.get( Uri.parse('$WIKIMEDIA_API?action=query&meta=tokens&format=json'), headers: { - 'Authorization': 'Bearer ${await _getAccessToken()}', + 'Authorization': 'Bearer ${await getAccessToken()}', }, ); var responseJson = await response; @@ -227,7 +228,7 @@ ${_generateDescriptions(description)} return value; } - Future _getAccessToken() async { + Future getAccessToken() async { Userdata? data = await LoginHandler().getUserInformationFromFile(); if (data != null) { return data.accessToken; diff --git a/projects/lib/main.dart b/projects/lib/main.dart index ccab07e..88f2212 100644 --- a/projects/lib/main.dart +++ b/projects/lib/main.dart @@ -13,6 +13,7 @@ import 'package:flutter/services.dart'; // TODO allow custom categories, but are you sure prompt // TODO add gps coordinates from exif header if available // TODO Guide to licences article +// TODO Handle Main activity destruction (https://pub.dev/packages/image_picker#handling-mainactivity-destruction-on-android) void main() { WidgetsFlutterBinding.ensureInitialized(); diff --git a/projects/lib/model/informationCollector.dart b/projects/lib/model/informationCollector.dart index 2c0415f..7eab635 100644 --- a/projects/lib/model/informationCollector.dart +++ b/projects/lib/model/informationCollector.dart @@ -43,7 +43,7 @@ class InformationCollector { _source = "Own Work"; } try { - await UploadService().uploadImage(images, fileName! + fileType!, _source, + await UploadService().uploadImage(images, fileName!, fileType!, _source, description, _author, license!, date, categories, depictions); } catch (e) { throw (e); diff --git a/projects/lib/style/textStyles.dart b/projects/lib/style/textStyles.dart index 7a1df7e..ffb5957 100644 --- a/projects/lib/style/textStyles.dart +++ b/projects/lib/style/textStyles.dart @@ -23,6 +23,6 @@ final TextStyle hyperlink = new TextStyle( color: Colors.blue, ); final TextStyle hintText = new TextStyle( - fontWeight: FontWeight.w300, fontSize: 15, color: Colors.grey.shade800); + fontWeight: FontWeight.w300, fontSize: 15, color: Colors.grey); final TextStyle introBigText = GoogleFonts.blackHanSans(fontSize: 48); final TextStyle introSmallText = GoogleFonts.blackHanSans(fontSize: 21); diff --git a/projects/lib/view/articles/licenseGuide.dart b/projects/lib/view/articles/licenseGuide.dart new file mode 100644 index 0000000..c2218ee --- /dev/null +++ b/projects/lib/view/articles/licenseGuide.dart @@ -0,0 +1,67 @@ +import 'package:flutter/material.dart'; +import 'package:projects/controller/internal/actionHelper.dart'; +import 'package:projects/style/textStyles.dart' as customStyles; +import 'package:projects/style/unorderedListWidget.dart'; + +class LicenseGuide extends StatelessWidget { + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text("License Guide"), + ), + body: Center( + child: ListView( + padding: EdgeInsets.all(16), + children: [ + Text("Acceptable licenses", style: customStyles.headerText), + Divider(), + Text( + "A copyright license is a formal permission stating who may use a copyrighted work and how they may use it. A license can only be granted by the copyright holder, which is usually the author (photographer, painter or similar). ", + style: customStyles.articleText, + ), + Text( + 'All copyrighted material on Commons (not in the public domain) must be licensed under a free license that specifically and irrevocably allows anyone to use the material for any purpose; simply writing that "the material may be used freely by anyone" or similar is not sufficient. In particular, the license must meet the following conditions: ', + style: customStyles.articleText, + ), + UnorderedList([ + "Republication and distribution must be allowed.", + "Publication of derivative work must be allowed.", + "Commercial use of the work must be allowed.", + "The license must be perpetual (non-expiring) and non-revocable.", + "Acknowledgment of all authors/contributors of a work may be required.", + "Publication of derivative work under the same license may be required.", + "For digital distribution, use of open file formats free of digital restrictions management (DRM) may be required." + ], customStyles.articleText), + Text("Licenses supported by Commons Uploader", + style: customStyles.headerText), + Divider(), + Text( + "In Commons Uploader, you can select one of the five most commonly used licenses for uploading Media to Wikimedia Commons. These include: ", + style: customStyles.articleText, + ), + UnorderedList([ + "CC0", + "Attribution 3.0", + "Attribution-ShareAlike 3.0", + "Attribution 4.0", + "Attribution-ShareAlike 4.0", + ], customStyles.articleText), + Text( + "The main similarity between all these licenses is that they allow others distribute, remix, adapt, and build upon your work, even commercially, as long as they credit you for the original creation. The only exception to this is the CCO license, where the author does not need to be credited.", + style: customStyles.articleText, + ), + Padding(padding: EdgeInsets.only(bottom: 8)), + TextButton( + child: Text("Read more"), + onPressed: () async { + String url = + "https://commons.wikimedia.org/wiki/Commons:Licensing"; + ActionHelper().launchUrl(url); + }, + ) + ], + )), + ); + } +} diff --git a/projects/lib/view/articles/pictureOfTheDayFragment.dart b/projects/lib/view/articles/pictureOfTheDay.dart similarity index 100% rename from projects/lib/view/articles/pictureOfTheDayFragment.dart rename to projects/lib/view/articles/pictureOfTheDay.dart diff --git a/projects/lib/view/articles/reusingContentFragment.dart b/projects/lib/view/articles/reusingContent.dart similarity index 100% rename from projects/lib/view/articles/reusingContentFragment.dart rename to projects/lib/view/articles/reusingContent.dart diff --git a/projects/lib/view/articles/uploadGuideFragment.dart b/projects/lib/view/articles/uploadGuide.dart similarity index 100% rename from projects/lib/view/articles/uploadGuideFragment.dart rename to projects/lib/view/articles/uploadGuide.dart diff --git a/projects/lib/view/commonsUploadFragment.dart b/projects/lib/view/commonsUploadFragment.dart index e713a35..aa5d20e 100644 --- a/projects/lib/view/commonsUploadFragment.dart +++ b/projects/lib/view/commonsUploadFragment.dart @@ -87,7 +87,13 @@ class _CommonsUploadFragmentState extends State { label: "Review"), ], ), - Divider(), + Padding( + padding: EdgeInsets.only(top: 8), + child: Divider( + height: 1, + thickness: 1, + ), + ), Expanded( child: _content(selectedTab), ), diff --git a/projects/lib/view/homeFragment.dart b/projects/lib/view/homeFragment.dart index ddc011b..06f9970 100644 --- a/projects/lib/view/homeFragment.dart +++ b/projects/lib/view/homeFragment.dart @@ -1,9 +1,10 @@ import 'dart:core'; import 'package:flutter/material.dart'; import 'package:projects/controller/wiki/pictureOfTheDayService.dart'; -import 'package:projects/view/articles/pictureOfTheDayFragment.dart'; -import 'package:projects/view/articles/reusingContentFragment.dart'; -import 'package:projects/view/articles/uploadGuideFragment.dart'; +import 'package:projects/view/articles/licenseGuide.dart'; +import 'package:projects/view/articles/pictureOfTheDay.dart'; +import 'package:projects/view/articles/reusingContent.dart'; +import 'package:projects/view/articles/uploadGuide.dart'; import 'package:projects/style/textStyles.dart' as customStyles; class HomeFragment extends StatelessWidget { @@ -25,17 +26,12 @@ class HomeFragment extends StatelessWidget { "If you wish to reuse content from Wikimedia Commons - on your own website, in print, or otherwise - check out this article.", onTap: ReusingContentFragment())); articleList.add(new Article( - title: "Placeholder Text", - description: - "Quo quia ab unde dolor. Et eaque sapiente quia eum ad deleniti quisquam. Cupiditate cupiditate velit aperiam animi voluptatum ipsa. Minus nemo odio ratione ab fugiat aut. Ipsam sapiente exercitationem deleniti delectus ducimus quod quo at. ", - )); - articleList.add(new Article( - title: "Text Place 2", - image: Image.network( - "https://upload.wikimedia.org/wikipedia/commons/5/59/SQM_GE_289A_Boxcab_Carmelita_-_Reverso.jpg"), - description: - "Quo quia ab unde dolor. Et eaque sapiente quia eum ad deleniti quisquam. ", - )); + title: "License Guide", + image: Image.network( + "https://upload.wikimedia.org/wikipedia/commons/thumb/d/d0/CC-BY-SA_icon.svg/640px-CC-BY-SA_icon.svg.png"), + description: + "This page gives non-lawyers an overview of complicated copyright laws.", + onTap: LicenseGuide())); // ------------------------------ diff --git a/projects/lib/view/simpleUpload/simpleHomePage.dart b/projects/lib/view/simpleUpload/simpleHomePage.dart index 2807bde..f004526 100644 --- a/projects/lib/view/simpleUpload/simpleHomePage.dart +++ b/projects/lib/view/simpleUpload/simpleHomePage.dart @@ -128,7 +128,7 @@ class _SimpleHomePageState extends State { } _openUploadPage() async { - if (collector.images != null) { + if (collector.images.isNotEmpty) { Navigator.push( context, MaterialPageRoute( diff --git a/projects/lib/view/simpleUpload/simpleUploadPage.dart b/projects/lib/view/simpleUpload/simpleUploadPage.dart index 0ab49c3..f179d26 100644 --- a/projects/lib/view/simpleUpload/simpleUploadPage.dart +++ b/projects/lib/view/simpleUpload/simpleUploadPage.dart @@ -105,7 +105,7 @@ class _SimpleUploadPageState extends State { } Widget _previewImageProvider() { - if (collector.images != null) { + if (collector.images.isNotEmpty) { return GestureDetector( onTap: () { Navigator.push( @@ -173,7 +173,7 @@ class _SimpleUploadPageState extends State { } Future _errorChecker() async { - if (collector.images == null) { + if (collector.images.isEmpty) { return "Please go back and select an image."; } else if (collector.fileName == null || collector.fileName!.isEmpty) { return "Enter a file name"; diff --git a/projects/lib/view/singlePage/introductionView.dart b/projects/lib/view/singlePage/introductionView.dart index 6678f1e..52554e6 100644 --- a/projects/lib/view/singlePage/introductionView.dart +++ b/projects/lib/view/singlePage/introductionView.dart @@ -1,11 +1,9 @@ import 'package:dots_indicator/dots_indicator.dart'; import 'package:flutter/material.dart'; import 'package:flutter_custom_clippers/flutter_custom_clippers.dart'; -import 'package:google_fonts/google_fonts.dart'; import 'package:liquid_swipe/liquid_swipe.dart'; import 'package:projects/controller/internal/settingsManager.dart'; import 'package:projects/style/textStyles.dart' as customStyles; -import 'package:projects/model/texts.dart' as texts; import 'package:projects/style/themes.dart'; import 'package:provider/provider.dart'; diff --git a/projects/lib/view/uploadFlow/descriptionFragment.dart b/projects/lib/view/uploadFlow/descriptionFragment.dart index f8ad3e3..39217df 100644 --- a/projects/lib/view/uploadFlow/descriptionFragment.dart +++ b/projects/lib/view/uploadFlow/descriptionFragment.dart @@ -3,6 +3,7 @@ import 'package:projects/controller/wiki/filenameCheckService.dart'; import 'package:projects/model/datasets.dart' as data; import 'package:projects/model/description.dart'; import 'package:projects/model/informationCollector.dart'; +import 'package:projects/style/infoPopUp.dart'; import 'package:projects/style/themes.dart'; import 'package:projects/style/textStyles.dart' as customStyles; @@ -221,31 +222,38 @@ class _MediaTitleWidget extends State { ))), if (collector.fileName != null && collector.fileName!.isNotEmpty) Card( - margin: EdgeInsets.all(16), + margin: EdgeInsets.fromLTRB(16, 16, 16, 0), child: SizedBox( width: double.infinity, child: Padding( - padding: EdgeInsets.all(16), - child: Column( - mainAxisSize: MainAxisSize.max, - children: [ - Text("Generated File Names:", - style: customStyles.objectDescription), - Column( - children: List.generate( - collector.images.length, (index) { - return Text( - collector.fileName! + - "_" + - (index + 1).toString() + - collector.fileType!, - style: customStyles.hintText, - ); - }), - ) - ], - ), - ))) + padding: EdgeInsets.all(16), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.max, + children: [ + Text("Generated File Names:", + style: customStyles.objectDescription), + Column( + children: List.generate( + collector.images.length, (index) { + return Text( + collector.fileName! + + "_" + + (index + 1).toString() + + collector.fileType!, + style: customStyles.hintText, + ); + }), + ) + ], + ), + InfoPopUp( + "As you are uploading multiple files, multiple file names need to be generated. Descriptions, Categories and Licences will be applied to all images uploaded.") + ], + )))) ], )); } else { diff --git a/projects/lib/view/uploadFlow/reviewFragment.dart b/projects/lib/view/uploadFlow/reviewFragment.dart index 5f66f55..7bb550e 100644 --- a/projects/lib/view/uploadFlow/reviewFragment.dart +++ b/projects/lib/view/uploadFlow/reviewFragment.dart @@ -68,15 +68,15 @@ class _ReviewFragmentState extends State { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - if (collector.images.length == 1) // If no batch upload + if (collector.images.length <= 1) // If no batch upload ValueLabelField( (collector.fileName ?? "") + (collector.fileType ?? ""), "file name", icon: fileNameIcon, replaceEmpty: true, - ), - if (collector.images.length > 1) // If batch upload + ) + else // If batch upload ValueLabelField( (collector.fileName ?? ""), "file name schema", @@ -140,7 +140,7 @@ class _ReviewFragmentState extends State { descriptionIcon.clear(); // The fields which need to be filled out - if (collector.images == null || + if (collector.images.isEmpty || collector.fileName == "" || collector.fileName == null || collector.source == "" || @@ -151,7 +151,7 @@ class _ReviewFragmentState extends State { isError = true; } - if (collector.images == null) { + if (collector.images.isEmpty) { infoText.add(errorText(context, "Select the image you want to upload")); } final validFileNameChars = RegExp(r'^[a-zA-Z0-9_\- ]+$'); @@ -164,12 +164,13 @@ class _ReviewFragmentState extends State { fileNameIcon = errorIcon(context); isError = true; } else if (collector.fileName!.length < 7) { - infoText.add(errorText(context, "File name is too short")); + infoText.add( + errorText(context, "File needs to be at least 7 characters long.")); fileNameIcon = errorIcon(context); isError = true; } else if (collector.fileName!.length < 15) { - infoText.add(warningText( - context, "Your file title needs to be at least 15 characters.")); + infoText.add( + warningText(context, "Make sure your file name is unique enough.")); fileNameIcon = warningIcon(context); } if (fileNameAlreadyExists) { @@ -187,9 +188,8 @@ class _ReviewFragmentState extends State { if (collector.description.length == 1 && collector.description[0].content == "") { - infoText.add(errorText(context, "Add at least one description")); - descriptionIcon.insert(0, errorIcon(context)); - isError = true; + infoText.add(warningText(context, "No description has been added")); + descriptionIcon.insert(0, warningIcon(context)); } else { bool alreadyWarned = false; for (Description description in collector.description) { @@ -268,10 +268,12 @@ class _ReviewFragmentState extends State { mainAxisAlignment: MainAxisAlignment.center, children: imgList.asMap().entries.map((entry) { return GestureDetector( - onTap: () => _controller.animateToPage(entry.key, curve: Curves.ease, duration: Duration(milliseconds: 500)), + onTap: () => _controller.animateToPage(entry.key, + curve: Curves.ease, + duration: Duration(milliseconds: 500)), child: Container( - width: 12.0, - height: 12.0, + width: 8.0, + height: 8.0, margin: EdgeInsets.symmetric(vertical: 8.0, horizontal: 4.0), decoration: BoxDecoration( @@ -279,7 +281,7 @@ class _ReviewFragmentState extends State { color: (Theme.of(context).brightness == Brightness.dark ? Colors.white : Colors.black) - .withOpacity(_current == entry.key ? 0.9 : 0.4)), + .withOpacity(_current == entry.key ? 0.8 : 0.3)), ), ); }).toList()) @@ -475,9 +477,8 @@ class _ReviewFragmentState extends State { return; } await collector.submitData(); - setState(() { - collector.clear(); - }); + collector + .clear(); // TODO for next test upload: When is the clear visible? Should be only after successful upload } } diff --git a/projects/lib/view/uploadFlow/selectImage.dart b/projects/lib/view/uploadFlow/selectImage.dart index e738ba0..4dfe602 100644 --- a/projects/lib/view/uploadFlow/selectImage.dart +++ b/projects/lib/view/uploadFlow/selectImage.dart @@ -4,7 +4,7 @@ import 'package:flutter/material.dart'; import 'package:image_picker/image_picker.dart'; import 'package:projects/controller/internal/imageDataExtractor.dart'; import 'package:projects/model/informationCollector.dart'; -import 'package:projects/view/articles/uploadGuideFragment.dart'; +import 'package:projects/view/articles/uploadGuide.dart'; import 'package:projects/style/textStyles.dart'; // TODO check if same file type, if not disallow upload and display message @@ -54,7 +54,7 @@ class _SelectImageFragmentState extends State { child: SizedBox( width: double.infinity, child: Padding( - padding: EdgeInsets.all(16), + padding: EdgeInsets.symmetric(horizontal: 16, vertical: 8), child: child, ), ), @@ -70,8 +70,14 @@ class _SelectImageFragmentState extends State { // TODO image still loads delayed even with future builder if (snapshot.hasData) { if (snapshot.data == null) { + collector.clear(); + showImagesLoadErrorPopup(context); throw ("Image infos are null"); } + if (snapshot.data!.isEmpty) { + collector.clear(); + showImagesLoadErrorPopup(context); + } List imageInfos = List.empty(growable: true); for (int i = 0; i < snapshot.data!.length; i++) { imageInfos.add(imageInfoWidget(snapshot.data![i])); @@ -82,7 +88,21 @@ class _SelectImageFragmentState extends State { } } return Column( - children: imageInfos, + children: imageInfos + + [ + TextButton( + onPressed: () async { + _openImagePicker(); + }, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + mainAxisSize: MainAxisSize.min, + children: [ + Icon(Icons.add), + Text("Add more images") + ], + )) + ], ); } else { return LinearProgressIndicator(); @@ -97,9 +117,7 @@ class _SelectImageFragmentState extends State { padding: EdgeInsets.all(2), child: TextButton( onPressed: () async { - collector.images = - await _picker.pickMultiImage() ?? List.empty(); - setState(() {}); + _openImagePicker(); }, child: SizedBox( width: 200, @@ -134,7 +152,7 @@ class _SelectImageFragmentState extends State { XFile? image = await _picker.pickImage(source: ImageSource.camera); if (image != null) { - collector.images = List.generate(1, (_) => image); + collector.images = [image]; } setState(() {}); }, @@ -164,9 +182,12 @@ class _SelectImageFragmentState extends State { Expanded( child: Row( children: [ - ClipRRect( - borderRadius: BorderRadius.circular(16), - child: imageInfo['image'], + SizedBox( + height: 100, + child: ClipRRect( + borderRadius: BorderRadius.circular(16), + child: imageInfo['image'], + ), ), Padding(padding: EdgeInsets.symmetric(horizontal: 6)), Expanded( @@ -199,7 +220,9 @@ class _SelectImageFragmentState extends State { collector.images.removeWhere((element) => File(element.name).toString().substring(6) == imageInfo['fileName']); - collector.fileType = null; + if (collector.images.isEmpty) { + collector.fileType = null; + } }); }, icon: Icon(Icons.delete)), @@ -212,29 +235,31 @@ class _SelectImageFragmentState extends State { double paragraphSpacing = 2.0; return Column( children: [ - Text( - "Before you start...", - style: articleTitle, + Padding( + padding: EdgeInsets.only(top: 16), ), + Text("Before you start...", + style: articleTitle, textAlign: TextAlign.center), Padding(padding: EdgeInsets.symmetric(vertical: paragraphSpacing)), Text( "Make sure that you are aware of what content can and should be " "uploaded to Wikimedia Commons. ", style: objectDescription, + textAlign: TextAlign.center, ), Padding(padding: EdgeInsets.symmetric(vertical: paragraphSpacing)), Text( - "Once uploaded, you cannot delete any content you submitted to " - "Commons. You may only upload works that are created entirely by you, " - "with a few exceptions. ", - style: objectDescription, - ), + "Once uploaded, you cannot delete any content you submitted to " + "Commons. You may only upload works that are created entirely by you, " + "with a few exceptions. ", + style: objectDescription, + textAlign: TextAlign.center), Padding(padding: EdgeInsets.symmetric(vertical: paragraphSpacing)), Text( - "If you are not familiar with the Upload " - "guidelines, you can learn more through the upload guide.", - style: objectDescription, - ), + "If you are not familiar with the Upload " + "guidelines, you can learn more through the upload guide.", + style: objectDescription, + textAlign: TextAlign.center), TextButton( onPressed: () { Navigator.push(context, @@ -245,4 +270,30 @@ class _SelectImageFragmentState extends State { ], ); } + + _openImagePicker() async { + setState(() async { + collector.images.addAll(await _picker.pickMultiImage() ?? List.empty()); + collector.images.removeRange(10, 99999); + }); + } + + showImagesLoadErrorPopup(BuildContext context) { + // TODO test if this even works lol + showDialog( + context: context, + builder: (context) { + return AlertDialog( + title: Text("Can't load images"), + content: Text("Make sure all your images are the same file type."), + actions: [ + TextButton( + onPressed: () { + Navigator.of(context).pop(); + }, + child: Text("Okay")) + ], + ); + }); + } } diff --git a/projects/lib/view/viewCategoryFragment.dart b/projects/lib/view/viewCategoryFragment.dart index 793b7e6..b884032 100644 --- a/projects/lib/view/viewCategoryFragment.dart +++ b/projects/lib/view/viewCategoryFragment.dart @@ -230,7 +230,7 @@ class _ViewCategoryFragment extends State { } _openSimpleUploadPage(String categoryName) async { - if (collector.images != null) { + if (collector.images.isNotEmpty) { collector.preFillContent = categoryName; Navigator.push( context, diff --git a/projects/pubspec.lock b/projects/pubspec.lock index 8d65708..f42e28f 100644 --- a/projects/pubspec.lock +++ b/projects/pubspec.lock @@ -1,6 +1,20 @@ # Generated by pub # See https://dart.dev/tools/pub/glossary#lockfile packages: + _fe_analyzer_shared: + dependency: transitive + description: + name: _fe_analyzer_shared + url: "https://pub.dartlang.org" + source: hosted + version: "31.0.0" + analyzer: + dependency: transitive + description: + name: analyzer + url: "https://pub.dartlang.org" + source: hosted + version: "2.8.0" animated_stack_widget: dependency: transitive description: @@ -85,6 +99,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.3.1" + cli_util: + dependency: transitive + description: + name: cli_util + url: "https://pub.dartlang.org" + source: hosted + version: "0.3.5" clock: dependency: transitive description: @@ -134,6 +155,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "3.0.1" + coverage: + dependency: transitive + description: + name: coverage + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.3" cross_file: dependency: transitive description: @@ -338,6 +366,13 @@ packages: description: flutter source: sdk version: "0.0.0" + frontend_server_client: + dependency: transitive + description: + name: frontend_server_client + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.2" geolocator: dependency: "direct main" description: @@ -373,6 +408,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.0.6" + glob: + dependency: transitive + description: + name: glob + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.2" google_fonts: dependency: "direct main" description: @@ -387,6 +429,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.13.4" + http_multi_server: + dependency: transitive + description: + name: http_multi_server + url: "https://pub.dartlang.org" + source: hosted + version: "3.2.0" http_parser: dependency: transitive description: @@ -429,6 +478,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.17.0" + io: + dependency: transitive + description: + name: io + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.3" js: dependency: transitive description: @@ -464,6 +520,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.0.1" + logging: + dependency: transitive + description: + name: logging + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.2" matcher: dependency: transitive description: @@ -485,6 +548,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.0.0" + mime: + dependency: transitive + description: + name: mime + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.1" nested: dependency: transitive description: @@ -492,6 +562,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.0.0" + node_preamble: + dependency: transitive + description: + name: node_preamble + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.1" octo_image: dependency: transitive description: @@ -499,6 +576,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.0.1" + package_config: + dependency: transitive + description: + name: package_config + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.2" package_info_plus: dependency: "direct main" description: @@ -632,6 +716,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.2" + pool: + dependency: transitive + description: + name: pool + url: "https://pub.dartlang.org" + source: hosted + version: "1.5.0" positioned_tap_detector_2: dependency: transitive description: @@ -667,6 +758,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "6.0.2" + pub_semver: + dependency: transitive + description: + name: pub_semver + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.1" quiver: dependency: transitive description: @@ -744,11 +842,53 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.0.4" + shelf: + dependency: transitive + description: + name: shelf + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.0" + shelf_packages_handler: + dependency: transitive + description: + name: shelf_packages_handler + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.0" + shelf_static: + dependency: transitive + description: + name: shelf_static + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0" + shelf_web_socket: + dependency: transitive + description: + name: shelf_web_socket + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.1" sky_engine: dependency: transitive description: flutter source: sdk version: "0.0.99" + source_map_stack_trace: + dependency: transitive + description: + name: source_map_stack_trace + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + source_maps: + dependency: transitive + description: + name: source_maps + url: "https://pub.dartlang.org" + source: hosted + version: "0.10.10" source_span: dependency: transitive description: @@ -812,6 +952,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.2.0" + test: + dependency: "direct dev" + description: + name: test + url: "https://pub.dartlang.org" + source: hosted + version: "1.17.12" test_api: dependency: transitive description: @@ -819,6 +966,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.4.3" + test_core: + dependency: transitive + description: + name: test_core + url: "https://pub.dartlang.org" + source: hosted + version: "0.4.2" transparent_image: dependency: transitive description: @@ -938,6 +1092,27 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.1" + vm_service: + dependency: transitive + description: + name: vm_service + url: "https://pub.dartlang.org" + source: hosted + version: "7.5.0" + watcher: + dependency: transitive + description: + name: watcher + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.1" + web_socket_channel: + dependency: transitive + description: + name: web_socket_channel + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" webfeed: dependency: "direct main" description: @@ -945,6 +1120,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.7.0" + webkit_inspection_protocol: + dependency: transitive + description: + name: webkit_inspection_protocol + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.0" win32: dependency: transitive description: diff --git a/projects/pubspec.yaml b/projects/pubspec.yaml index b4fb04f..f1e2dfe 100644 --- a/projects/pubspec.yaml +++ b/projects/pubspec.yaml @@ -6,7 +6,7 @@ repository: https://github.com/geometalab/PhotoUploadApp # pub.dev using `pub publish`. This is preferred for private packages. publish_to: 'none' # Remove this line if you wish to publish to pub.dev -version: 0.2.0 +version: 0.3.0 environment: sdk: ">=2.14.0 <3.0.0" @@ -55,6 +55,8 @@ dev_dependencies: flutter_test: sdk: flutter flutter_launcher_icons: ^0.9.2 + test: ^1.17.12 + # Defining the launcher icons for IOS and Android flutter_icons: