diff --git a/Sources/justMUSIC/lib/components/editable_post_component.dart b/Sources/justMUSIC/lib/components/editable_post_component.dart index eb38b8c..cec93db 100644 --- a/Sources/justMUSIC/lib/components/editable_post_component.dart +++ b/Sources/justMUSIC/lib/components/editable_post_component.dart @@ -1,6 +1,8 @@ import 'dart:io'; import 'package:animated_appear/animated_appear.dart'; import 'package:auto_size_text/auto_size_text.dart'; +import 'package:firebase_storage/firebase_storage.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/services.dart'; @@ -60,18 +62,19 @@ class _EditablePostComponentState extends State with Tick } Future pickImage(ImageSource source) async { - try { - final image = await ImagePicker().pickImage(source: source, imageQuality: 20); - if (image == null) return; - final imageTemp = File(image.path); - setState(() { - this.image = imageTemp; - widget.callBackImage(imageTemp); - }); - } on PlatformException catch (e) { - print('Failed to pick image: $e'); - } + try { + final image = await ImagePicker().pickImage(source: source, imageQuality: 20); + if (image == null) return; + final imageTemp = File(image.path); + setState(() { + this.image = imageTemp; + widget.callBackImage(imageTemp); + }); + } on PlatformException catch (e) { + print('Failed to pick image: $e'); + } + } void _updateDescription(String text) { @@ -182,7 +185,10 @@ class _EditablePostComponentState extends State with Tick borderRadius: BorderRadius.circular(20), child: InstaImageViewer( backgroundIsTransparent: true, - child: Image( + child: kIsWeb?InstaImageViewer( + backgroundIsTransparent: true, + child: Image.network(image!.path) + ):Image( image: FileImage(image!), fit: BoxFit.cover, ), @@ -250,8 +256,8 @@ class _EditablePostComponentState extends State with Tick width: double.infinity, child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Expanded( + children: [ kIsWeb?Container() + :Expanded( flex: 5, child: GestureDetector( onTap: () { @@ -262,7 +268,7 @@ class _EditablePostComponentState extends State with Tick ), ), ), - SizedBox( + kIsWeb?Container():SizedBox( width: 15, ), Expanded( diff --git a/Sources/justMUSIC/lib/components/historic_component.dart b/Sources/justMUSIC/lib/components/historic_component.dart index 52a8fd6..a5d7146 100644 --- a/Sources/justMUSIC/lib/components/historic_component.dart +++ b/Sources/justMUSIC/lib/components/historic_component.dart @@ -34,59 +34,68 @@ class _HistoricComponentState extends State { .getHistoryCapsulesMonthWhitIdUser(MyApp.userViewModel.userCurrent.id, widget.month, widget.year), builder: (context, snapshot) { if (snapshot.hasData) { - return Wrap( - spacing: 14, - runSpacing: 14, - children: List.generate(getNumberOfDaysInMonth(widget.year, widget.month), (index) { - Tuple2? checkCapsule; - if (snapshot.data != null) { - for (var element in snapshot.data!) { - if (element.item1 == index + 1) { - checkCapsule = element; + return Container( + constraints: const BoxConstraints( maxWidth: 600), + child: Wrap( + spacing: 14, + runSpacing: 14, + children: List.generate(getNumberOfDaysInMonth(widget.year, widget.month), (index) { + Tuple2? checkCapsule; + if (snapshot.data != null) { + for (var element in snapshot.data!) { + if (element.item1 == index + 1) { + checkCapsule = element; + } } } - } - if ((widget.year > DateTime.now().year || widget.month > DateTime.now().month) || - (widget.year == DateTime.now().year && - widget.month == DateTime.now().month && - index > DateTime.now().day)) { - return Container( - decoration: BoxDecoration( - gradient: LinearGradient(colors: [ - Color(0xFF1E1E1E).withOpacity(0.7), - Color(0xFF1E1E1E).withOpacity(0), - ], begin: Alignment.topCenter, end: Alignment.bottomCenter), - borderRadius: BorderRadius.circular(5)), - height: 60, - width: 60, - ); - } - if (checkCapsule != null) { - return Container( - decoration: BoxDecoration( - image: DecorationImage(image: NetworkImage((checkCapsule.item2.cover)!)), - borderRadius: BorderRadius.circular(5)), - height: 60, - width: 60, - ); - } else { - return Container( - color: bgColor, - height: 60, - width: 60, - child: Center( - child: Text( - (index + 1).toString(), - style: - GoogleFonts.plusJakartaSans(color: Colors.white, fontSize: 22, fontWeight: FontWeight.w800), + if ((widget.year > DateTime.now().year || widget.month > DateTime.now().month) || + (widget.year == DateTime.now().year && + widget.month == DateTime.now().month && + index > DateTime.now().day)) { + return Container( + decoration: BoxDecoration( + gradient: LinearGradient(colors: [ + Color(0xFF1E1E1E).withOpacity(0.7), + Color(0xFF1E1E1E).withOpacity(0), + ], begin: Alignment.topCenter, end: Alignment.bottomCenter), + borderRadius: BorderRadius.circular(5)), + height: 60, + width: 60, + ); + } + if (checkCapsule != null) { + return Tooltip( + message: "${checkCapsule.item2.artists.first.name} - ${checkCapsule.item2.title}".length < 30? "${checkCapsule.item2.artists.first.name} - ${checkCapsule.item2.title}": "${checkCapsule.item2.artists.first.name} - ${checkCapsule.item2.title}".substring(0,30)+"...", + decoration: const BoxDecoration( + border: Border.fromBorderSide(BorderSide(color: Color(0xFF3A3A3A), width: 1)), + color: tooltipBackground, borderRadius: BorderRadius.all(Radius.circular(20)) + ), + child: Container( + decoration: BoxDecoration( + image: DecorationImage(image: NetworkImage((checkCapsule.item2.cover)!)), + borderRadius: BorderRadius.circular(5)), + height: 60, + width: 60, + )); + } else { + return Container( + color: bgColor, + height: 60, + width: 60, + child: Center( + child: Text( + (index + 1).toString(), + style: + GoogleFonts.plusJakartaSans(color: Colors.white, fontSize: 22, fontWeight: FontWeight.w800), + ), ), - ), - ); - } + ); + } - // Generate widgets - }), + // Generate widgets + }), + ), ); } else { return CupertinoActivityIndicator(); diff --git a/Sources/justMUSIC/lib/components/post_component.dart b/Sources/justMUSIC/lib/components/post_component.dart index f9a5ade..e8ead7f 100644 --- a/Sources/justMUSIC/lib/components/post_component.dart +++ b/Sources/justMUSIC/lib/components/post_component.dart @@ -152,10 +152,8 @@ class _PostComponentState extends State with TickerProviderStateM children: [ SizedBox( width: double.infinity, - child: FadeInImage.assetNetwork( - image: widget.post.music.cover!, - fadeInDuration: const Duration(milliseconds: 100), - placeholder: "assets/images/loadingPlaceholder.gif", + child: Image.network( + widget.post.music.cover!, ), ), Image( @@ -196,11 +194,9 @@ class _PostComponentState extends State with TickerProviderStateM child: ClipRRect( borderRadius: BorderRadius.circular(13), // implement image - child: FadeInImage.assetNetwork( - image: widget.post.selfie!, + child: Image.network( + widget.post.selfie!, fit: BoxFit.cover, - fadeInDuration: const Duration(milliseconds: 100), - placeholder: "assets/images/loadingPlaceholder.gif", ), ), ), diff --git a/Sources/justMUSIC/lib/main.dart b/Sources/justMUSIC/lib/main.dart index 7c1d49e..bd974fd 100644 --- a/Sources/justMUSIC/lib/main.dart +++ b/Sources/justMUSIC/lib/main.dart @@ -80,9 +80,7 @@ class _MyAppState extends State { @override Widget build(BuildContext context) { SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersiveSticky); - Paint.enableDithering = true; - - FullScreenWindow.setFullScreen(true); // enter fullscreen + Paint.enableDithering = true; // enter fullscreen return ScreenUtilInit( useInheritedMediaQuery: true, diff --git a/Sources/justMUSIC/lib/screens/capsule_historic_screen.dart b/Sources/justMUSIC/lib/screens/capsule_historic_screen.dart index 6187694..b50f908 100644 --- a/Sources/justMUSIC/lib/screens/capsule_historic_screen.dart +++ b/Sources/justMUSIC/lib/screens/capsule_historic_screen.dart @@ -77,8 +77,9 @@ class _CapsuleHistoricScreenState extends State { mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ - Padding( - padding: const EdgeInsets.only(top: 80, left: 60, right: 60), + Container( + padding: const EdgeInsets.only(top: 80, left: 30, right: 30), + constraints: const BoxConstraints( maxWidth: 700), child: Align( alignment: Alignment.center, child: Row( @@ -130,8 +131,6 @@ class _CapsuleHistoricScreenState extends State { child: SizedBox( width: double.infinity, child: Container( - padding: EdgeInsets.symmetric(horizontal: 15), - constraints: BoxConstraints(maxWidth: 600), child: Column( children: [ HistoricComponent( diff --git a/Sources/justMUSIC/lib/screens/detail_post_screen.dart b/Sources/justMUSIC/lib/screens/detail_post_screen.dart index cc95607..f2bd56e 100644 --- a/Sources/justMUSIC/lib/screens/detail_post_screen.dart +++ b/Sources/justMUSIC/lib/screens/detail_post_screen.dart @@ -612,6 +612,7 @@ class _DetailPostScreenState extends State { Expanded( child: TextField( keyboardAppearance: Brightness.dark, + keyboardType: TextInputType.text, controller: _textController, focusNode: myFocusNode, onSubmitted: (value) async { @@ -626,7 +627,6 @@ class _DetailPostScreenState extends State { setState(() {}); }, cursorColor: primaryColor, - keyboardType: TextInputType.emailAddress, style: GoogleFonts.plusJakartaSans(color: Colors.white), decoration: InputDecoration( suffixIcon: _textController.text.isEmpty diff --git a/Sources/justMUSIC/lib/screens/feed_screen.dart b/Sources/justMUSIC/lib/screens/feed_screen.dart index 4ad8120..1a38fbd 100644 --- a/Sources/justMUSIC/lib/screens/feed_screen.dart +++ b/Sources/justMUSIC/lib/screens/feed_screen.dart @@ -25,13 +25,15 @@ class _FeedScreenState extends State with SingleTickerProviderStateM late Animation animation; late List friendFeed; Timer? timer; - + var pageFriend = 0; late List discoveryFeed; late Tuple2, List> displayFeed; bool isDismissed = true; bool choiceFeed = true; PageController controller = PageController(); + + @override void initState() { super.initState(); @@ -124,9 +126,26 @@ class _FeedScreenState extends State with SingleTickerProviderStateM @override Widget build(BuildContext context) { displayFeed = - Tuple2(MyApp.postViewModel.postsFriends.reversed.toList(), MyApp.postViewModel.bestPosts.reversed.toList()); + Tuple2(MyApp.postViewModel.postsFriends.toList(), MyApp.postViewModel.bestPosts.toList()); bool empty = (choiceFeed == true && displayFeed.item1.isEmpty) || (choiceFeed == false && displayFeed.item2.isEmpty); + ScrollController _scrollController = ScrollController(); + _scrollController.addListener(() { + if (_scrollController.position.maxScrollExtent == + _scrollController.position.pixels) { + print("fin"); + if (choiceFeed) { + setState(() { + MyApp.postViewModel.getMorePostsFriends(); + }); + } else { + setState(() { + MyApp.postViewModel.getMoreBestPosts(); + }); + } + } + }); + return Scaffold( resizeToAvoidBottomInset: false, backgroundColor: bgColor, @@ -185,6 +204,7 @@ class _FeedScreenState extends State with SingleTickerProviderStateM physics: const BouncingScrollPhysics(parent: AlwaysScrollableScrollPhysics()), clipBehavior: Clip.none, shrinkWrap: false, + controller: _scrollController, itemCount: displayFeed.item1.length, itemBuilder: (BuildContext context, int index) { return Padding( @@ -193,6 +213,7 @@ class _FeedScreenState extends State with SingleTickerProviderStateM PostComponent(callback: openDetailPost, post: displayFeed.item1[index], index: index), ); }, + ), ), ), @@ -212,6 +233,7 @@ class _FeedScreenState extends State with SingleTickerProviderStateM triggerMode: RefreshIndicatorTriggerMode.onEdge, onRefresh: _refresh, child: ListView.builder( + controller: _scrollController, physics: const BouncingScrollPhysics(parent: AlwaysScrollableScrollPhysics()), clipBehavior: Clip.none, shrinkWrap: false, diff --git a/Sources/justMUSIC/lib/screens/profile_screen.dart b/Sources/justMUSIC/lib/screens/profile_screen.dart index df3cf81..7dcd244 100644 --- a/Sources/justMUSIC/lib/screens/profile_screen.dart +++ b/Sources/justMUSIC/lib/screens/profile_screen.dart @@ -2,6 +2,7 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:google_fonts/google_fonts.dart'; +import 'package:justmusic/services/AuthService.dart'; import 'package:justmusic/values/icons.dart'; import '../components/profile_component.dart'; import '../components/setting_part_component.dart'; @@ -32,7 +33,40 @@ class _ProfileScreenState extends State { Navigator.of(context).push(routeUser(MyApp.userViewModel.userCurrent)); } - void _openPassword() { + void openConfirmationDelete() { + showCupertinoModalPopup( + context: context, + barrierColor: Colors.black.withOpacity(0.7), + builder: (BuildContext context) => Container( + child: CupertinoActionSheet( + title: Text( + 'Supprimer mon compte', + style: GoogleFonts.plusJakartaSans(fontWeight: FontWeight.bold), + ), + actions: [ + CupertinoActionSheetAction( + onPressed: () { + MyApp.userViewModel.delete(); + Navigator.pop(context); + Navigator.of(context).push(routeWelcome()); + + }, + child: Text('Confirmer', style: TextStyle(color: Colors.blue),), + ), + ], + cancelButton: CupertinoActionSheetAction( + isDestructiveAction: true, + onPressed: () { + Navigator.pop(context); + }, + child: const Text('Annuler'), + ), + ), + ), + ); + } + + void openPassword() { Navigator.of(context).push(routePassword()); } @@ -122,12 +156,12 @@ class _ProfileScreenState extends State { SettingPartComponent( icon: JustMusicIcon.password, label: 'Modifier mon mot de passe', - action: _openPassword, + action: openPassword, ), - const SettingPartComponent( + SettingPartComponent( icon: JustMusicIcon.trash, label: 'Supprimer mon compte', - action: null, + action: openConfirmationDelete, ), SettingPartComponent( icon: JustMusicIcon.cross, diff --git a/Sources/justMUSIC/lib/screens/user_screen.dart b/Sources/justMUSIC/lib/screens/user_screen.dart index 187584a..9d5efb2 100644 --- a/Sources/justMUSIC/lib/screens/user_screen.dart +++ b/Sources/justMUSIC/lib/screens/user_screen.dart @@ -143,10 +143,11 @@ class _UserScreenState extends State { padding: const EdgeInsets.symmetric(horizontal: settingPadding), child: Column( mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.center, children: [ - Padding( + Container( padding: EdgeInsets.only(top: 68.h, bottom: 40), + constraints: const BoxConstraints( maxWidth: 500), child: Stack( alignment: Alignment.center, children: [ @@ -249,7 +250,11 @@ class _UserScreenState extends State { SizedBox( height: 40, ), - RecapComponent(user: widget.user) + Container( + constraints: const BoxConstraints( maxWidth: 500), + child: RecapComponent(user: widget.user), + ) + ], ), ), diff --git a/Sources/justMUSIC/lib/services/PostService.dart b/Sources/justMUSIC/lib/services/PostService.dart index 28557a8..a180f40 100644 --- a/Sources/justMUSIC/lib/services/PostService.dart +++ b/Sources/justMUSIC/lib/services/PostService.dart @@ -42,16 +42,41 @@ class PostService { deletePost() {} - Future>>> getPopularPosts( - {int limit = 10, - QueryDocumentSnapshot>? offset}) async { + Future>>> getPopularPosts(int limit) async { + DateTime twentyFourHoursAgo = DateTime.now().subtract(Duration(hours: 24)); + var response = await FirebaseFirestore.instance + .collection("posts") + .where("date", isGreaterThan: twentyFourHoursAgo) + .orderBy("date", descending: true) + .limit(limit) + .get(); + + MyApp.postViewModel.lastPostDiscovery = response.docs.isNotEmpty + ? response.docs.last + : MyApp.postViewModel.lastPostDiscovery; + + var filteredPosts = response.docs.where((doc) { + String user = doc["user_id"]; + return user != MyApp.userViewModel.userCurrent.id; + }).toList(); + return filteredPosts; + } + + Future>>> getMorePopularPosts(int limit) async { + DateTime twentyFourHoursAgo = DateTime.now().subtract(Duration(hours: 24)); QuerySnapshot> response; response = await FirebaseFirestore.instance .collection("posts") - .orderBy("date") + .where("date", isGreaterThan: twentyFourHoursAgo) + .orderBy("date", descending: true) .limit(limit) + .startAfterDocument(MyApp.postViewModel.lastPostDiscovery) .get(); + MyApp.postViewModel.lastPostDiscovery = response.docs.isNotEmpty + ? response.docs.last + : MyApp.postViewModel.lastPostDiscovery; + var filteredPosts = response.docs.where((doc) { String user = doc["user_id"]; return user != MyApp.userViewModel.userCurrent.id; @@ -59,18 +84,41 @@ class PostService { return filteredPosts; } - Future>>> getPostsFriends( - {int limit = 10, int offset = 0}) async { + Future>>> getPostsFriends(int limit) async { var response = await FirebaseFirestore.instance .collection("posts") .where("user_id", whereIn: [ MyApp.userViewModel.userCurrent.id, ...MyApp.userViewModel.userCurrent.followed ]) - .orderBy("date") + .where("") + .orderBy("date", descending: true) .limit(limit) .get(); + MyApp.postViewModel.lastPostFriend = response.docs.isNotEmpty + ? response.docs.last + : MyApp.postViewModel.lastPostFriend; + + return response.docs; + } + + Future>>> getMorePostsFriends(int limit) async { + var response = await FirebaseFirestore.instance + .collection("posts") + .where("user_id", whereIn: [ + MyApp.userViewModel.userCurrent.id, + ...MyApp.userViewModel.userCurrent.followed + ]) + .orderBy("date", descending: true) + .limit(limit) + .startAfterDocument(MyApp.postViewModel.lastPostFriend) + .get(); + + MyApp.postViewModel.lastPostFriend = response.docs.isNotEmpty + ? response.docs.last + : MyApp.postViewModel.lastPostFriend; + return response.docs; } diff --git a/Sources/justMUSIC/lib/values/constants.dart b/Sources/justMUSIC/lib/values/constants.dart index af24e9d..68a68b1 100644 --- a/Sources/justMUSIC/lib/values/constants.dart +++ b/Sources/justMUSIC/lib/values/constants.dart @@ -24,6 +24,7 @@ const searchBarColor = Color(0xFF161616); const postbutton = Color(0xFF1B1B1B); const fillButton = Color(0xFF633AF4); const selectedButton = Color(0xFF1F1B2E); +const tooltipBackground = Color(0xFF2D2D2D); // All constants important too us const defaultPadding = 30.0; diff --git a/Sources/justMUSIC/lib/view_model/MusicViewModel.dart b/Sources/justMUSIC/lib/view_model/MusicViewModel.dart index c3a1618..2d9db58 100644 --- a/Sources/justMUSIC/lib/view_model/MusicViewModel.dart +++ b/Sources/justMUSIC/lib/view_model/MusicViewModel.dart @@ -242,7 +242,6 @@ class MusicViewModel { for (var capsule in capsulesData) { var music = musics.firstWhere((music) => music.id == capsule.item2); - print(capsule.item1); capsules.add(Tuple2(capsule.item1, music)); } return capsules; diff --git a/Sources/justMUSIC/lib/view_model/PostViewModel.dart b/Sources/justMUSIC/lib/view_model/PostViewModel.dart index 1c2a361..061f7d3 100644 --- a/Sources/justMUSIC/lib/view_model/PostViewModel.dart +++ b/Sources/justMUSIC/lib/view_model/PostViewModel.dart @@ -11,6 +11,8 @@ import '../model/mapper/PostMapper.dart'; class PostViewModel { List _postsFriends = []; List _bestPosts = []; + var lastPostFriend; + var lastPostDiscovery; final PostService _postService = PostService(); // Constructor @@ -26,10 +28,10 @@ class PostViewModel { await _postService.createPost(description, idMusic, image, location); } - Future> getPostsFriends() async { + Future> getPostsFriends({int limit = 10}) async { try { _postsFriends = []; - var responseData = await _postService.getPostsFriends(); + var responseData = await _postService.getPostsFriends(limit); List ids = []; var postsFutures = responseData.map((value) { ids.add(value.data()["song_id"]); @@ -40,7 +42,7 @@ class PostViewModel { for (int i = 0; i < posts.length; i++) { posts[i].music = musics[i]; } - _postsFriends = posts; + _postsFriends.addAll(posts); return _postsFriends; } catch (e) { print(e); @@ -49,13 +51,29 @@ class PostViewModel { } } - List getMorePostsFriends() { - throw new Error(); + void getMorePostsFriends({int limit = 10}) async { + try { + var responseData = await _postService.getMorePostsFriends(limit); + List ids = []; + var postsFutures = responseData.map((value) { + ids.add(value.data()["song_id"]); + return PostMapper.toModel(value); + }).toList(); + var posts = await Future.wait(postsFutures); + List musics = await MyApp.musicViewModel.getMusicsWithIds(ids); + for (int i = 0; i < posts.length; i++) { + posts[i].music = musics[i]; + } + _postsFriends.addAll(posts); + } catch (e) { + print(e); + } } - Future> getBestPosts() async { + Future> getBestPosts({int limit = 10}) async { try { - var responseData = await _postService.getPopularPosts(); + _bestPosts = []; + var responseData = await _postService.getPopularPosts(limit); List ids = []; var postsFutures = responseData.map((value) async { ids.add(value.data()["song_id"]); @@ -66,7 +84,7 @@ class PostViewModel { for (int i = 0; i < posts.length; i++) { posts[i].music = musics[i]; } - _bestPosts = posts; + _bestPosts.addAll(posts); return _bestPosts; } catch (e) { print(e); @@ -75,8 +93,23 @@ class PostViewModel { } } - List getMoreBestPosts() { - throw new Error(); + void getMoreBestPosts({int limit = 10}) async { + try { + var responseData = await _postService.getMorePopularPosts(limit); + List ids = []; + var postsFutures = responseData.map((value) async { + ids.add(value.data()["song_id"]); + return await PostMapper.toModel(value); + }).toList(); + var posts = await Future.wait(postsFutures); + List musics = await MyApp.musicViewModel.getMusicsWithIds(ids); + for (int i = 0; i < posts.length; i++) { + posts[i].music = musics[i]; + } + _bestPosts.addAll(posts); + } catch (e) { + print(e); + } } Future> recapSevenDays(String id) async { diff --git a/Sources/justMUSIC/pubspec.lock b/Sources/justMUSIC/pubspec.lock index cfef692..079d274 100644 --- a/Sources/justMUSIC/pubspec.lock +++ b/Sources/justMUSIC/pubspec.lock @@ -745,7 +745,7 @@ packages: source: hosted version: "0.8.7+4" image_picker_for_web: - dependency: "direct main" + dependency: transitive description: name: image_picker_for_web sha256: "50bc9ae6a77eea3a8b11af5eb6c661eeb858fdd2f734c2a4fd17086922347ef7" diff --git a/Sources/justMUSIC/pubspec.yaml b/Sources/justMUSIC/pubspec.yaml index c0bd0f0..df8b275 100644 --- a/Sources/justMUSIC/pubspec.yaml +++ b/Sources/justMUSIC/pubspec.yaml @@ -78,7 +78,6 @@ dependencies: google_sign_in: ^6.1.4 flutter_launcher_icons: ^0.13.1 fullscreen_window: ^1.0.3 - image_picker_for_web: ^3.0.1 dev_dependencies: flutter_test: