diff --git a/lib/Components/audio_input_container.dart b/lib/Components/audio_input_container.dart new file mode 100644 index 0000000..3682f36 --- /dev/null +++ b/lib/Components/audio_input_container.dart @@ -0,0 +1,165 @@ +import 'package:auto_size_text/auto_size_text.dart'; +import 'package:flutter/material.dart'; +import 'package:manager_app/Components/loading_common.dart'; +import 'package:manager_app/app_context.dart'; +import 'package:manager_app/constants.dart'; +import 'package:manager_app/Screens/Resources/select_resource_modal.dart'; +import 'package:managerapi/api.dart'; +import 'package:provider/provider.dart'; + +class AudioInputContainer extends StatefulWidget { + final Color color; + final String label; + final String initialValue; + final ValueChanged onChanged; + final BoxFit imageFit; + final bool isSmall; + final double fontSize; + const AudioInputContainer({ + Key key, + this.color = kSecond, + this.label, + this.initialValue, + this.onChanged, + this.imageFit = BoxFit.cover, + this.isSmall = false, + this.fontSize = 25 + }) : super(key: key); + + @override + _AudioInputContainerState createState() => _AudioInputContainerState(); +} + +class _AudioInputContainerState extends State { + String resourceIdToShow; + + @override + void initState() { + resourceIdToShow = widget.initialValue; + super.initState(); + } + + @override + Widget build(BuildContext context) { + Size size = MediaQuery.of(context).size; + return Container( + child: Row( + children: [ + Align( + alignment: AlignmentDirectional.centerStart, + child: AutoSizeText( + widget.label, + style: TextStyle(fontSize: widget.fontSize, fontWeight: FontWeight.w300), + maxLines: 2, + maxFontSize: widget.fontSize, + textAlign: TextAlign.center, + ) + ), + Container( + //color: widget.isSmall ? Colors.cyanAccent: Colors.red, + child: Padding( + padding: EdgeInsets.only(left: widget.isSmall ? 5 : 10, top: 10, bottom: 10), + child: Container( + width: size.width *0.08, + height: size.width *0.08, + child: InkWell( + onTap: () async { + var result = await showSelectResourceModal( + "Sélectionner une ressource", + 1, + [ResourceType.audio], + context + ); + + if (result != null) { + setState(() { + resourceIdToShow = result.id; + }); + widget.onChanged(result); + } + }, + child: getElement(widget.initialValue, context, widget.isSmall), + ), + ), + ), + ), + ], + ), + ); + } + + getElement(String initialValue, BuildContext context, bool isSmall) { + if (resourceIdToShow != null) { + Size size = MediaQuery.of(context).size; + final appContext = Provider.of(context); + return FutureBuilder( + future: getResource(resourceIdToShow, appContext), + builder: (context, AsyncSnapshot snapshot) { + if (snapshot.connectionState == ConnectionState.done) { + if (snapshot.data != null) { + return Container( + decoration: boxDecoration(snapshot.data, appContext), + child: Center(child: AutoSizeText("Audio")), + ); + } else { + return Text("No data"); + } + + } else if (snapshot.connectionState == ConnectionState.none) { + return Text("No data"); + } else { + return Center( + child: Container( + height: size.height * 0.1, + child: LoadingCommon() + ) + ); + } + } + ); + } else { + return Container( + decoration: BoxDecoration( + color: widget.color, + borderRadius: BorderRadius.circular(20), + ), + child: Padding( + padding: const EdgeInsets.only(left: 5, right: 5, top: 15, bottom: 15), + child: Center( + child: AutoSizeText( + "Choisir un fichier audio", + style: TextStyle(color: kWhite), + maxLines: 1, + ) + ), + ) + ); + } + } + + Future getResource(String resourceIdToShow, dynamic appContext) async { + ResourceDTO resource = await appContext.getContext().clientAPI.resourceApi.resourceGetDetail(resourceIdToShow); + return resource; + } + + boxDecoration(ResourceDTO resourceDTO, appContext) { + return BoxDecoration( + shape: BoxShape.rectangle, + color: kWhite, + borderRadius: BorderRadius.circular(30.0), + /*image: new DecorationImage( + fit: widget.imageFit, + image: resourceDTO.type != null ? new NetworkImage( + resourceDTO.type == ResourceType.image ? appContext.getContext().clientAPI.resourceApi.apiClient.basePath+"/api/Resource/"+ resourceDTO.id : resourceDTO.data, + ) : null, + ),*/ + boxShadow: [ + BoxShadow( + spreadRadius: 0.5, + blurRadius: 1, + offset: Offset(0, 1.5), // changes position of shadow + ), + ], + ); + } +} \ No newline at end of file diff --git a/lib/Components/audio_player.dart b/lib/Components/audio_player.dart new file mode 100644 index 0000000..67d4640 --- /dev/null +++ b/lib/Components/audio_player.dart @@ -0,0 +1,105 @@ +/*import 'dart:async'; + +import 'package:audioplayers/audioplayers.dart'; +import 'package:flutter/material.dart'; + +class AudioPlayerContainer extends StatefulWidget { + const AudioPlayerContainer({Key? key}) : super(key: key); + + @override + _AudioPlayerContainerState createState() => _AudioPlayerContainerState(); +} + +class _AudioPlayerContainerState extends State { + List players = + List.generate(4, (_) => AudioPlayer()..setReleaseMode(ReleaseMode.stop)); + int selectedPlayerIdx = 0; + + AudioPlayer get selectedPlayer => players[selectedPlayerIdx]; + List streams = []; + + @override + void initState() { + super.initState(); + players.asMap().forEach((index, player) { + streams.add( + player.onPlayerComplete.listen( + (it) => toast( + 'Player complete!', + textKey: Key('toast-player-complete-$index'), + ), + ), + ); + streams.add( + player.onSeekComplete.listen( + (it) => toast( + 'Seek complete!', + textKey: Key('toast-seek-complete-$index'), + ), + ), + ); + }); + } + + @override + void dispose() { + streams.forEach((it) => it.cancel()); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('audioplayers example'), + ), + body: Column( + children: [ + Padding( + padding: const EdgeInsets.all(8.0), + child: Center( + child: Tgl( + options: ['P1', 'P2', 'P3', 'P4'] + .asMap() + .map((key, value) => MapEntry('player-$key', value)), + selected: selectedPlayerIdx, + onChange: (v) => setState(() => selectedPlayerIdx = v), + ), + ), + ), + Expanded( + child: Tabs( + tabs: [ + TabData( + key: 'sourcesTab', + label: 'Src', + content: SourcesTab(player: selectedPlayer), + ), + TabData( + key: 'controlsTab', + label: 'Ctrl', + content: ControlsTab(player: selectedPlayer), + ), + TabData( + key: 'streamsTab', + label: 'Stream', + content: StreamsTab(player: selectedPlayer), + ), + TabData( + key: 'audioContextTab', + label: 'Ctx', + content: AudioContextTab(player: selectedPlayer), + ), + TabData( + key: 'loggerTab', + label: 'Log', + content: const LoggerTab(), + ), + ], + ), + ), + ], + ), + ); + } +}*/ \ No newline at end of file diff --git a/lib/Components/fetch_resource_icon.dart b/lib/Components/fetch_resource_icon.dart index 46a2e8e..68c0bc6 100644 --- a/lib/Components/fetch_resource_icon.dart +++ b/lib/Components/fetch_resource_icon.dart @@ -9,6 +9,9 @@ IconData getResourceIcon(elementType) { case ResourceType.imageUrl: return Icons.image_search; // art_track break; + case ResourceType.audio: + return Icons.audiotrack; // art_track + break; case ResourceType.video: return Icons.slow_motion_video; break; diff --git a/lib/Components/image_input_container.dart b/lib/Components/image_input_container.dart index 0f85d66..c794a69 100644 --- a/lib/Components/image_input_container.dart +++ b/lib/Components/image_input_container.dart @@ -67,7 +67,7 @@ class _ImageInputContainerState extends State { var result = await showSelectResourceModal( "Sélectionner une ressource", 1, - true, + [ResourceType.image, ResourceType.imageUrl], context ); diff --git a/lib/Components/resource_tab.dart b/lib/Components/resource_tab.dart index 79bbfea..9179d61 100644 --- a/lib/Components/resource_tab.dart +++ b/lib/Components/resource_tab.dart @@ -2,6 +2,7 @@ import 'dart:io'; import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart'; +import 'package:manager_app/Components/upload_audio_container.dart'; import 'package:manager_app/Components/upload_image_container.dart'; import 'package:manager_app/Components/upload_online_resources_container.dart'; import 'package:manager_app/constants.dart'; @@ -29,7 +30,8 @@ class _ResourceTabState extends State with SingleTickerProviderStat void initState() { tabsToShow.add(new Tab(text: "Image local")); tabsToShow.add(new Tab(text: "Image en ligne")); - tabsToShow.add(new Tab(text: "Vidéo en ligne")); + tabsToShow.add(new Tab(text: "Audio local")); + //tabsToShow.add(new Tab(text: "Vidéo en ligne")); _tabController = new TabController(length: 3, vsync: this); _tabController.addListener(_handleTabSelection); @@ -81,7 +83,7 @@ class _ResourceTabState extends State with SingleTickerProviderStat case 2: setState(() { widget.resourceDTO.data = null; - widget.resourceDTO.type = ResourceType.videoUrl; + widget.resourceDTO.type = ResourceType.audio; }); break; } @@ -121,8 +123,25 @@ getContent(ResourceDTO resourceDTO, Function onFileUpload, Function onFileUpload ) ); - // Online Video + // Audio local tabsToShow.add( + new Padding( + padding: EdgeInsets.symmetric(horizontal: 8, vertical: 16), + child: UploadAudioContainer( + onChanged: (List files) { + onFileUpload(files); + resourceDTO.type = ResourceType.audio; + }, + onChangedWeb: (List files) { + onFileUploadWeb(files); + resourceDTO.type = ResourceType.audio; + }, + ), + ) + ); + + // Online Video + /*tabsToShow.add( new Padding( padding: EdgeInsets.symmetric(horizontal: 8, vertical: 16), child: UploadOnlineResourceContainer( @@ -132,6 +151,6 @@ getContent(ResourceDTO resourceDTO, Function onFileUpload, Function onFileUpload }, ), ) - ); + );*/ return tabsToShow; } \ No newline at end of file diff --git a/lib/Components/upload_audio_container.dart b/lib/Components/upload_audio_container.dart new file mode 100644 index 0000000..d40c636 --- /dev/null +++ b/lib/Components/upload_audio_container.dart @@ -0,0 +1,204 @@ +import 'dart:io'; +import 'package:file_picker/file_picker.dart'; +import 'package:manager_app/constants.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/foundation.dart' show kIsWeb; + +class UploadAudioContainer extends StatefulWidget { + final ValueChanged> onChanged; + final ValueChanged> onChangedWeb; + const UploadAudioContainer({ + Key key, + this.onChanged, + this.onChangedWeb, + }) : super(key: key); + + @override + _UploadAudioContainerState createState() => _UploadAudioContainerState(); +} + +class _UploadAudioContainerState extends State with SingleTickerProviderStateMixin { + var filePath; + File fileToShow; + String fileToShowWeb; + + @override + void initState() { + super.initState(); + } + + @override + Widget build(BuildContext context) { + Size size = MediaQuery.of(context).size; + return Container( + width: size.width *0.5, + height: size.height *0.5, + child: displayElement(), + ); + } + + String getFileName(filePath) { + return (filePath.toString().split('\\').last); + } + + Future filePicker() async { + FilePickerResult result; + if (kIsWeb) { + result = await FilePicker.platform.pickFiles( + type: FileType.custom, + dialogTitle: 'Sélectionner un fichier audio mp3', + allowMultiple: true, + allowedExtensions: ['mp3'], + ); + + if (result != null) { + List files = result.files; + + setState(() { + filePath = "Fichiers"; // Only show one picture + fileToShowWeb = "Aucun aperçu possible"; // Only show one picture + widget.onChangedWeb(files); + }); + } + } else { + result = await FilePicker.platform.pickFiles( + type: FileType.custom, + dialogTitle: 'Sélectionner un fichier audio mp3', + allowMultiple: true, + allowedExtensions: ['mp3'], + ); + + List files = result.paths.map((path) => File(path)).toList(); + var file = files[0]; + setState(() { + filePath = file.path; // Only show one picture + fileToShow = file; // Only show one picture + widget.onChanged(files); + }); + } + /*final file = OpenFilePicker() + ..filterSpecification = { + 'Images (*.jpg; *.jpeg;*.png)': '*.jpg;*.jpeg;*.png', + //'Video (*.mp4)': '*.mp4', + //'All Files': '*.*' + } + ..defaultFilterIndex = 0 + ..title = 'Sélectionner un fichier'; + + final result = file.getFile(); + + if (result != null) { + + setState(() { + filePath = result.path; + fileToShow = result; + widget.onChanged(result); + }); + }*/ + } + + showFile() { + if (getFileName(filePath).contains(".mp4")) { + /*return FutureBuilder( + future: loadFile(fileToShow), + builder: (context, AsyncSnapshot snapshot) { + if (snapshot.connectionState == ConnectionState.done) { + return Container( + height: 300, + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Padding( + padding: const EdgeInsets.all(8.0), + child: Text("La prévisualisation de la vidéo n'est pas disponible"), + ), + Icon( + Icons.ondemand_video_sharp, + size: 35, + ), + ], + ), + //DartVLC(file: snapshot.data) + ); + } else if (snapshot.connectionState == ConnectionState.none) { + return Text("No data"); + } else { + return Center( + child: Container( + height: 200, + child: LoadingCommon() + ) + ); + } + } + );*/ + } else { + if (kIsWeb) { + return null; + } else { + return Text("TODO player affiché ou pas ?");/*Image.file( + fileToShow, + height: 200, + fit:BoxFit.scaleDown + );*/ + } + } + } + + loadFile(File fileToShow) async { + //return await Media.file(fileToShow); + return null; // Useless no mp4 for now + } + + displayElement() { + if (fileToShow == null && fileToShowWeb == null) return Center( + child: InkWell( + onTap: () async { + filePicker(); + }, + child: Container( + decoration: BoxDecoration( + color: kPrimaryColor, + borderRadius: BorderRadius.circular(15) + ), + child: Padding( + padding: const EdgeInsets.only(left: 25.0, right: 25.0, top: 15.0, bottom: 15.0), + child: Text( + "Ajouter un ou plusieurs fichiers", + style: new TextStyle(color: kWhite), + ), + ) + ), + ), + ); + if (fileToShow != null || fileToShowWeb != null) + return Container( + margin: EdgeInsets.all(8.0), + child: Card( + color: kBackgroundColor, + shape: RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(8.0))), + child: InkWell( + onTap: () async { + filePicker(); + }, + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + ClipRRect( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(8.0), + topRight: Radius.circular(8.0), + ), + child: showFile() + ), + ListTile( + title: Text(getFileName(filePath)), + subtitle: Text(filePath), + ), + ], + ), + ), + ), + ); + } +} \ No newline at end of file diff --git a/lib/Models/resourceTypeModel.dart b/lib/Models/resourceTypeModel.dart new file mode 100644 index 0000000..dc31615 --- /dev/null +++ b/lib/Models/resourceTypeModel.dart @@ -0,0 +1,27 @@ +import 'package:managerapi/api.dart'; + +class ResourceTypeModel { + String label; + ResourceType type; + + ResourceTypeModel({this.label, this.type}); + + factory ResourceTypeModel.fromJson(Map json) { + return new ResourceTypeModel( + label: json['label'] as String, + type: json['type'] as ResourceType, + ); + } + + Map toMap() { + return { + 'label': label, + 'type': type + }; + } + + @override + String toString() { + return '{label: $label, type: $type}'; + } +} \ No newline at end of file diff --git a/lib/Screens/Configurations/Section/SubSection/Article/article_config.dart b/lib/Screens/Configurations/Section/SubSection/Article/article_config.dart index f20d069..77463fb 100644 --- a/lib/Screens/Configurations/Section/SubSection/Article/article_config.dart +++ b/lib/Screens/Configurations/Section/SubSection/Article/article_config.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:manager_app/Components/audio_input_container.dart'; import 'package:manager_app/Components/check_input_container.dart'; import 'package:manager_app/Components/multi_string_input_container.dart'; import 'package:manager_app/Screens/Configurations/Section/SubSection/Slider/listView_card_image.dart'; @@ -77,7 +78,7 @@ class _ArticleConfigState extends State { mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ MultiStringContainer( - label: "Contenu affiché:", + label: "Contenu affiché :", modalLabel: "Contenu", color: kPrimaryColor, initialValue: articleDTO != null ? articleDTO.content : [], @@ -94,7 +95,7 @@ class _ArticleConfigState extends State { maxLines: 1, ), CheckInputContainer( - label: "Contenu au-dessus :", + label: "Contenu au-dessus :", isChecked: articleDTO.isContentTop, onChanged: (value) { setState(() { @@ -104,6 +105,26 @@ class _ArticleConfigState extends State { }); }, ), + AudioInputContainer( + label: "Audio :", + initialValue: articleDTO.audioId, + color: kPrimaryColor, + onChanged: (ResourceDTO resource) { + articleDTO.audioId = resource.id; + widget.onChanged(jsonEncode(articleDTO).toString()); + }, + ), + CheckInputContainer( + label: "Lecture auto audio :", + isChecked: articleDTO.isReadAudioAuto, + onChanged: (value) { + setState(() { + //print(value); + articleDTO.isReadAudioAuto = value; + widget.onChanged(jsonEncode(articleDTO).toString()); + }); + }, + ), ], ), ), @@ -170,7 +191,7 @@ class _ArticleConfigState extends State { right: 15, child: InkWell( onTap: () async { - var result = await showNewOrUpdateImageSlider(null, appContext, context, false, true); + var result = await showNewOrUpdateImageSlider(null, appContext, context, true, false); if (result != null) { setState(() { diff --git a/lib/Screens/Configurations/Section/SubSection/Map/geopoint_image_list.dart b/lib/Screens/Configurations/Section/SubSection/Map/geopoint_image_list.dart index aed2479..c7257b2 100644 --- a/lib/Screens/Configurations/Section/SubSection/Map/geopoint_image_list.dart +++ b/lib/Screens/Configurations/Section/SubSection/Map/geopoint_image_list.dart @@ -91,7 +91,7 @@ class _GeoPointImageListState extends State { var result = await showSelectResourceModal( "Sélectionner une ressource", 1, - true, + [ResourceType.image, ResourceType.imageUrl], context ); if (result != null) { diff --git a/lib/Screens/Configurations/Section/section_detail_screen.dart b/lib/Screens/Configurations/Section/section_detail_screen.dart index 6f5d42f..541f788 100644 --- a/lib/Screens/Configurations/Section/section_detail_screen.dart +++ b/lib/Screens/Configurations/Section/section_detail_screen.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:manager_app/Components/audio_input_container.dart'; import 'package:manager_app/Components/confirmation_dialog.dart'; import 'package:manager_app/Components/fetch_section_icon.dart'; import 'package:manager_app/Components/image_input_container.dart'; diff --git a/lib/Screens/Configurations/configurations_screen.dart b/lib/Screens/Configurations/configurations_screen.dart index e18b3e9..0ae5b7a 100644 --- a/lib/Screens/Configurations/configurations_screen.dart +++ b/lib/Screens/Configurations/configurations_screen.dart @@ -147,6 +147,13 @@ boxDecoration(ConfigurationDTO configurationDTO) { color: configurationDTO.id == null ? kSuccess : kTextLightColor, shape: BoxShape.rectangle, borderRadius: BorderRadius.circular(25.0), + image: configurationDTO.imageSource != null ? new DecorationImage( + fit: BoxFit.cover, + colorFilter: new ColorFilter.mode(Colors.black.withOpacity(0.18), BlendMode.dstATop), + image: new NetworkImage( + configurationDTO.imageSource, + ), + ) : null, boxShadow: [ BoxShadow( color: kSecond, diff --git a/lib/Screens/Main/components/body.dart b/lib/Screens/Main/components/body.dart index 823db12..13aa83f 100644 --- a/lib/Screens/Main/components/body.dart +++ b/lib/Screens/Main/components/body.dart @@ -11,6 +11,7 @@ import 'package:manager_app/Screens/login_screen.dart'; import 'package:manager_app/app_context.dart'; import 'package:manager_app/constants.dart'; import 'package:manager_app/main.dart'; +import 'package:managerapi/api.dart'; import 'package:provider/provider.dart'; @@ -190,7 +191,13 @@ class _BodyState extends State { case 'resources' : return Padding( padding: const EdgeInsets.all(8.0), - child: ResourcesScreen() + child: ResourcesScreen( + resourceTypes: [ + ResourceType.audio, + ResourceType.image, + ResourceType.imageUrl + ] + ) ); break; default: diff --git a/lib/Screens/Resources/fetch_image_for_resource.dart b/lib/Screens/Resources/fetch_image_for_resource.dart index b869ba5..a0a33e0 100644 --- a/lib/Screens/Resources/fetch_image_for_resource.dart +++ b/lib/Screens/Resources/fetch_image_for_resource.dart @@ -16,8 +16,11 @@ getImageForResource(dynamic resourceDTO, AppContext appContext) { fit:BoxFit.fill ); break; + case ResourceType.audio: + return Text("Fichier audio - aucune visualisation possible"); + break; case ResourceType.video: - return Text("THIS IS VIDEO LOCAL"); + return Text("Vidéo locale - aucune visualisation possible"); break; case ResourceType.videoUrl: return Text(resourceDTO.data); diff --git a/lib/Screens/Resources/resource_body_grid.dart b/lib/Screens/Resources/resource_body_grid.dart index 383128f..2a3bccb 100644 --- a/lib/Screens/Resources/resource_body_grid.dart +++ b/lib/Screens/Resources/resource_body_grid.dart @@ -11,14 +11,14 @@ import 'package:provider/provider.dart'; class ResourceBodyGrid extends StatefulWidget { final List resources; //return ResourceDTO final Function onSelect; - final bool isImage; final bool isAddButton; + final List resourceTypesIn; const ResourceBodyGrid({ Key key, this.resources, this.onSelect, - this.isImage, this.isAddButton, + this.resourceTypesIn, }) : super(key: key); @override @@ -26,7 +26,8 @@ class ResourceBodyGrid extends StatefulWidget { } class _ResourceBodyGridState extends State { - List filterType; + List filterTypes; + List currentFilterTypes; String filterSearch = ''; List selectedTypes; List resourcesToShow; @@ -34,8 +35,9 @@ class _ResourceBodyGridState extends State { @override void initState() { resourcesToShow = widget.resources; - filterType = [resource_types[0], resource_types[1]];//, resource_types[2]]; // resource_types[3] - selectedTypes = resource_types; + currentFilterTypes = resource_types.where((rt) => widget.resourceTypesIn.contains(rt.type)).map((rt) => rt.label).toList();//, resource_types[2]]; // resource_types[3] + filterTypes = resource_types.where((rt) => widget.resourceTypesIn.contains(rt.type)).map((rt) => rt.label).toList();//, resource_types[2]]; // resource_types[3] + selectedTypes = resource_types.where((rt) => widget.resourceTypesIn.contains(rt.type)).map((rt) => rt.label).toList(); super.initState(); } @@ -69,8 +71,8 @@ class _ResourceBodyGridState extends State { padding: const EdgeInsets.all(8.0), child: MultiSelectContainer( label: "Type :", - initialValue: filterType, - values: widget.isImage ? resource_types.where((type) => type != "video" && type != "video url").toList(): resource_types, + initialValue: filterTypes, + values: currentFilterTypes, isMultiple: true, onChanged: (result) { setState(() { @@ -181,31 +183,8 @@ class _ResourceBodyGridState extends State { void filterResource() { resourcesToShow = filterSearch.isEmpty ? widget.resources: widget.resources.where((ResourceDTO resource) => resource.label.toUpperCase().contains(filterSearch.toUpperCase())).toList(); - - resource_types.forEach((type) { - switch(type) { - case "image": - if (!selectedTypes.contains(type)) { - resourcesToShow = resourcesToShow.where((resource) => resource.type != ResourceType.image).toList(); - } - break; - case "image url": - if (!selectedTypes.contains(type)) { - resourcesToShow = resourcesToShow.where((resource) => resource.type != ResourceType.imageUrl).toList(); - } - break; - case "video": - if (!selectedTypes.contains(type)) { - resourcesToShow = resourcesToShow.where((resource) => resource.type != ResourceType.video).toList(); - } - break; - case "video url": - if (!selectedTypes.contains(type)) { - resourcesToShow = resourcesToShow.where((resource) => resource.type != ResourceType.videoUrl).toList(); - } - break; - } - }); + var getTypesInSelected = resource_types.where((ft) => selectedTypes.contains(ft.label)).map((rt) => rt.type).toList(); + resourcesToShow = resourcesToShow.where((resource) => getTypesInSelected.contains(resource.type)).toList(); } } @@ -214,7 +193,7 @@ boxDecoration(dynamic resourceDetailDTO, appContext) { color: resourceDetailDTO.id == null ? kSuccess : kBackgroundColor, shape: BoxShape.rectangle, borderRadius: BorderRadius.circular(30.0), - image: resourceDetailDTO.id != null && resourceDetailDTO.type != ResourceType.videoUrl ? new DecorationImage( + image: resourceDetailDTO.id != null && (resourceDetailDTO.type == ResourceType.image || resourceDetailDTO.type == ResourceType.imageUrl) ? new DecorationImage( fit: BoxFit.cover, colorFilter: new ColorFilter.mode(Colors.black.withOpacity(0.3), BlendMode.dstATop), image: new NetworkImage( diff --git a/lib/Screens/Resources/resources_screen.dart b/lib/Screens/Resources/resources_screen.dart index 0826645..61f9a45 100644 --- a/lib/Screens/Resources/resources_screen.dart +++ b/lib/Screens/Resources/resources_screen.dart @@ -18,11 +18,15 @@ class ResourcesScreen extends StatefulWidget { final Function onGetResult; //return ResourceDTO final bool isImage; final bool isAddButton; + final bool isFilter; + final List resourceTypes; const ResourcesScreen({ Key key, this.isImage = false, this.onGetResult, this.isAddButton = true, + this.resourceTypes, + this.isFilter = true }) : super(key: key); @override @@ -39,13 +43,13 @@ class _ResourcesScreenState extends State { Size size = MediaQuery.of(context).size; return FutureBuilder( - future: getResources(widget.onGetResult, widget.isImage, appContext), + future: getResources(widget.onGetResult, widget.isImage, appContext, widget.resourceTypes), builder: (context, AsyncSnapshot snapshot) { if (snapshot.connectionState == ConnectionState.done) { if(snapshot.data != null) { var tempOutput = new List.from(snapshot.data); // tempOutput.add(ResourceDTO(id: null)); - return ResourceBodyGrid(resources: tempOutput, isImage: widget.isImage, isAddButton: widget.isAddButton, onSelect: (value) async { + return ResourceBodyGrid(resources: tempOutput, resourceTypesIn: widget.isImage ? resource_types.where((rt) => rt.type == ResourceType.image || rt.type == ResourceType.imageUrl).map((rt) => rt.type).toList() : widget.resourceTypes, isAddButton: widget.isAddButton, onSelect: (value) async { if (widget.onGetResult == null) { // Main screen if (value.id == null) { @@ -81,8 +85,9 @@ class _ResourcesScreenState extends State { } } -Future getResources(Function onGetResult, bool isImage, AppContext appContext) async { - List resources = await (appContext.getContext() as ManagerAppContext).clientAPI.resourceApi.resourceGet(instanceId: (appContext.getContext() as ManagerAppContext).instanceId); +Future getResources(Function onGetResult, bool isImage, AppContext appContext, List types) async { + types = types != null ? types : []; + List resources = await (appContext.getContext() as ManagerAppContext).clientAPI.resourceApi.resourceGet(instanceId: (appContext.getContext() as ManagerAppContext).instanceId, types: types); if (onGetResult != null && isImage) { resources = resources.where((element) => element.type == ResourceType.image || element.type == ResourceType.imageUrl || element.type == ResourceType.audio).toList(); } @@ -91,6 +96,7 @@ Future getResources(Function onGetResult, bool isImage, AppContext appCont Future create(ResourceDTO resourceDTO, List files, List filesWeb, AppContext appContext, context) async { switch(resourceDTO.type) { + case ResourceType.audio: case ResourceType.image: case ResourceType.video: @@ -122,7 +128,7 @@ Future create(ResourceDTO resourceDTO, List files, List resourceTypes, BuildContext mainContext) async { /*Function onSelect,*/ Size size = MediaQuery.of(mainContext).size; var result = await showDialog( @@ -28,7 +28,7 @@ dynamic showSelectResourceModal (String text, int maxLines, bool onlyImage, Buil Navigator.pop(context, result); } }, - isImage: onlyImage, + resourceTypes: resourceTypes, ), ), ], diff --git a/lib/Screens/Resources/show_resource_popup.dart b/lib/Screens/Resources/show_resource_popup.dart index 4954d42..5ef041b 100644 --- a/lib/Screens/Resources/show_resource_popup.dart +++ b/lib/Screens/Resources/show_resource_popup.dart @@ -25,7 +25,7 @@ void showResource(ResourceDTO resourceDTO, AppContext appContext, BuildContext c style: new TextStyle(fontSize: 25, fontWeight: FontWeight.w400)), ), Container( - height: size.height *0.6, + height: size.height *0.5, child: Center( child: Container( decoration: BoxDecoration( @@ -46,36 +46,42 @@ void showResource(ResourceDTO resourceDTO, AppContext appContext, BuildContext c Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ - Align( - alignment: AlignmentDirectional.bottomEnd, - child: Container( - width: 175, - height: 70, - child: RoundedButton( - text: "Retour", - icon: Icons.undo, - color: kSecond, - press: () { - Navigator.of(context).pop(); - }, - fontSize: 20, + Padding( + padding: const EdgeInsets.all(8.0), + child: Align( + alignment: AlignmentDirectional.bottomEnd, + child: Container( + width: 175, + height: 70, + child: RoundedButton( + text: "Retour", + icon: Icons.undo, + color: kSecond, + press: () { + Navigator.of(context).pop(); + }, + fontSize: 20, + ), ), ), ), - Align( - alignment: AlignmentDirectional.bottomEnd, - child: Container( - width: 200, - height: 70, - child: RoundedButton( - text: "Supprimer", - icon: Icons.delete, - color: kError, - textColor: kWhite, - press: () { - delete(resourceDTO, appContext, context); - }, - fontSize: 20, + Padding( + padding: const EdgeInsets.all(8.0), + child: Align( + alignment: AlignmentDirectional.bottomEnd, + child: Container( + width: 200, + height: 70, + child: RoundedButton( + text: "Supprimer", + icon: Icons.delete, + color: kError, + textColor: kWhite, + press: () { + delete(resourceDTO, appContext, context); + }, + fontSize: 20, + ), ), ), ), diff --git a/lib/constants.dart b/lib/constants.dart index b15ddb8..e1fb78f 100644 --- a/lib/constants.dart +++ b/lib/constants.dart @@ -1,4 +1,6 @@ import 'package:flutter/material.dart'; +import 'package:manager_app/Models/resourceTypeModel.dart'; +import 'package:managerapi/api.dart'; // Colors - TO FILL WIT CORRECT COLOR //const kBackgroundColor = Color(0xFFFFFFFF); @@ -17,7 +19,11 @@ const kSuccess = Color(0xFF8bc34a); const List section_types = ["Map", "Slider", "Video", "Web", "Menu", "Quizz", "Article"]; const List map_types = ["none", "normal", "satellite", "terrain", "hybrid"]; const List languages = ["FR", "NL", "EN", "DE", "IT", "ES", "CN", "PL"]; -const List resource_types = ["image", "image url"]; // "video url" , "video", +List resource_types = [ + ResourceTypeModel(label: "image", type: ResourceType.image), + ResourceTypeModel(label: "image url", type: ResourceType.imageUrl), + ResourceTypeModel(label: "audio", type: ResourceType.audio) +]; // "video url" , "video", {"label": "image"}, "image url", "audio" /* const kTextStyle = TextStyle( diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index cccf817..2119932 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -5,6 +5,10 @@ import FlutterMacOS import Foundation +import audioplayers +import path_provider_macos func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { + AudioplayersPlugin.register(with: registry.registrar(forPlugin: "AudioplayersPlugin")) + PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) } diff --git a/manager_api/doc/ResourceApi.md b/manager_api/doc/ResourceApi.md index 6748700..204d1e5 100644 --- a/manager_api/doc/ResourceApi.md +++ b/manager_api/doc/ResourceApi.md @@ -105,7 +105,7 @@ Name | Type | Description | Notes [[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) # **resourceGet** -> List resourceGet(instanceId) +> List resourceGet(instanceId, types) @@ -117,9 +117,10 @@ import 'package:managerapi/api.dart'; final api_instance = ResourceApi(); final instanceId = instanceId_example; // String | +final types = []; // List | try { - final result = api_instance.resourceGet(instanceId); + final result = api_instance.resourceGet(instanceId, types); print(result); } catch (e) { print('Exception when calling ResourceApi->resourceGet: $e\n'); @@ -131,6 +132,7 @@ try { Name | Type | Description | Notes ------------- | ------------- | ------------- | ------------- **instanceId** | **String**| | [optional] + **types** | [**List**](ResourceType.md)| | [optional] [default to const []] ### Return type diff --git a/manager_api/doc/ResourceDTO.md b/manager_api/doc/ResourceDTO.md index cc4d87b..97271f8 100644 --- a/manager_api/doc/ResourceDTO.md +++ b/manager_api/doc/ResourceDTO.md @@ -11,8 +11,8 @@ Name | Type | Description | Notes **id** | **String** | | [optional] **type** | [**ResourceType**](ResourceType.md) | | [optional] **label** | **String** | | [optional] -**dateCreation** | [**DateTime**](DateTime.md) | | [optional] **data** | **String** | | [optional] +**dateCreation** | [**DateTime**](DateTime.md) | | [optional] **instanceId** | **String** | | [optional] [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) diff --git a/manager_api/lib/api/resource_api.dart b/manager_api/lib/api/resource_api.dart index 0e975bb..899f961 100644 --- a/manager_api/lib/api/resource_api.dart +++ b/manager_api/lib/api/resource_api.dart @@ -146,7 +146,9 @@ class ResourceApi { /// Parameters: /// /// * [String] instanceId: - Future resourceGetWithHttpInfo({ String instanceId }) async { + /// + /// * [List] types: + Future resourceGetWithHttpInfo({ String instanceId, List types }) async { // Verify required params are set. final path = r'/api/Resource'; @@ -160,6 +162,9 @@ class ResourceApi { if (instanceId != null) { queryParams.addAll(_convertParametersForCollectionFormat('', 'instanceId', instanceId)); } + if (types != null) { + queryParams.addAll(_convertParametersForCollectionFormat('multi', 'types', types)); + } final contentTypes = []; final nullableContentType = contentTypes.isNotEmpty ? contentTypes[0] : null; @@ -192,8 +197,10 @@ class ResourceApi { /// Parameters: /// /// * [String] instanceId: - Future> resourceGet({ String instanceId }) async { - final response = await resourceGetWithHttpInfo( instanceId: instanceId ); + /// + /// * [List] types: + Future> resourceGet({ String instanceId, List types }) async { + final response = await resourceGetWithHttpInfo( instanceId: instanceId, types: types ); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, _decodeBodyBytes(response)); } diff --git a/manager_api/swagger.yaml b/manager_api/swagger.yaml index d126a0d..2b5be51 100644 --- a/manager_api/swagger.yaml +++ b/manager_api/swagger.yaml @@ -676,6 +676,16 @@ paths: type: string nullable: true x-position: 1 + - name: types + in: query + style: form + explode: true + schema: + type: array + nullable: true + items: + $ref: '#/components/schemas/ResourceType' + x-position: 2 responses: '200': description: '' @@ -1756,12 +1766,12 @@ components: label: type: string nullable: true - dateCreation: - type: string - format: date-time data: type: string nullable: true + dateCreation: + type: string + format: date-time instanceId: type: string nullable: true @@ -1772,16 +1782,19 @@ components: 1 = Video 2 = ImageUrl 3 = VideoUrl + 4 = Audio x-enumNames: - Image - Video - ImageUrl - VideoUrl + - Audio enum: - 0 - 1 - 2 - 3 + - 4 DeviceDTO: type: object additionalProperties: false diff --git a/pubspec.lock b/pubspec.lock index 3de08b6..c62f14f 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -29,6 +29,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.8.2" + audioplayers: + dependency: "direct main" + description: + name: audioplayers + url: "https://pub.dartlang.org" + source: hosted + version: "0.18.3" auto_size_text: dependency: "direct main" description: @@ -134,6 +141,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.2.1" + file: + dependency: transitive + description: + name: file + url: "https://pub.dartlang.org" + source: hosted + version: "6.1.4" file_picker: dependency: "direct main" description: @@ -296,6 +310,55 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.2.1" + path_provider: + dependency: transitive + description: + name: path_provider + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.11" + path_provider_android: + dependency: transitive + description: + name: path_provider_android + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.20" + path_provider_ios: + dependency: transitive + description: + name: path_provider_ios + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.11" + path_provider_linux: + dependency: transitive + description: + name: path_provider_linux + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.7" + path_provider_macos: + dependency: transitive + description: + name: path_provider_macos + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.6" + path_provider_platform_interface: + dependency: transitive + description: + name: path_provider_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.5" + path_provider_windows: + dependency: transitive + description: + name: path_provider_windows + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.7" pdf: dependency: "direct main" description: @@ -317,6 +380,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "4.4.0" + platform: + dependency: transitive + description: + name: platform + url: "https://pub.dartlang.org" + source: hosted + version: "3.1.0" plugin_platform_interface: dependency: transitive description: @@ -331,6 +401,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "3.6.0" + process: + dependency: transitive + description: + name: process + url: "https://pub.dartlang.org" + source: hosted + version: "4.2.4" provider: dependency: "direct main" description: @@ -413,6 +490,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.3.0" + uuid: + dependency: transitive + description: + name: uuid + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.6" vector_math: dependency: transitive description: @@ -462,6 +546,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.5.2" + xdg_directories: + dependency: transitive + description: + name: xdg_directories + url: "https://pub.dartlang.org" + source: hosted + version: "0.2.0+2" xml: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 3c35b94..9704578 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -41,6 +41,7 @@ dependencies: #path_provider: ^2.0.2 encrypt: ^5.0.0 qr_flutter: ^4.0.0 + audioplayers: 0.18.3 pdf: ^3.6.0 multi_select_flutter: ^4.1.2 #msix: ^2.1.3