From 583066ad47a481a81b324b31ae09f00dff5df6d2 Mon Sep 17 00:00:00 2001 From: Amr Ahmed Date: Fri, 23 Dec 2022 14:28:51 +0200 Subject: [PATCH 01/16] modified posts --- lib/post/screens/post_screen.dart | 40 ++-- lib/post/widgets/post.dart | 1 + lib/post/widgets/post_body.dart | 129 ++++++++---- lib/post/widgets/post_body_in_screen.dart | 6 +- lib/post/widgets/post_footer.dart | 33 +++- lib/post/widgets/post_header.dart | 21 +- lib/post/widgets/post_images_in_screen.dart | 153 ++++++++++++++ lib/post/widgets/post_images_web.dart | 152 ++++++++++++++ lib/post/widgets/post_pop_up_web.dart | 53 +++++ .../widgets/post_video_in_widget_web.dart | 186 ++++++++++++++++++ lib/show_post/screens/show_post_web.dart | 150 ++++++++++++++ 11 files changed, 854 insertions(+), 70 deletions(-) create mode 100644 lib/post/widgets/post_images_in_screen.dart create mode 100644 lib/post/widgets/post_images_web.dart create mode 100644 lib/post/widgets/post_pop_up_web.dart create mode 100644 lib/post/widgets/post_video_in_widget_web.dart create mode 100644 lib/show_post/screens/show_post_web.dart diff --git a/lib/post/screens/post_screen.dart b/lib/post/screens/post_screen.dart index 9656a2db..fcffab8a 100644 --- a/lib/post/screens/post_screen.dart +++ b/lib/post/screens/post_screen.dart @@ -1,10 +1,13 @@ //import 'package:animated_tree_view/animated_tree_view.dart'; import 'package:flutter/material.dart'; -import 'package:post/comments/models/comment_model.dart'; -import 'package:post/comments/providers/comments_provider.dart'; +import 'package:post/post/widgets/post_list.dart'; +import 'package:post/widgets/loading_reddit.dart'; import 'package:provider/provider.dart'; +import 'package:responsive_sizer/responsive_sizer.dart'; import 'package:video_player/video_player.dart'; import '../../comments/widgets/comment.dart'; +import '../../providers/Profile_provider.dart'; +import '../models/post_model.dart'; class PostScreen extends StatefulWidget { static const routeName = '/post'; @@ -23,20 +26,19 @@ class _PostScreenState extends State { late VideoPlayerController controller1; bool _isInit = true; bool _isLoading = false; - List? commentsData = []; + List? posts = []; @override void didChangeDependencies() { - Provider test; if (_isInit) { setState(() { _isLoading = true; }); - Provider.of(context, listen: false) - .fetchPostComments('s', 1, 2) + Provider.of(context, listen: false) + .fetchProfilePosts('Amr', 'Hot', 1, 25, context) .then((value) { - commentsData = Provider.of(context, listen: false) - .gettingPostComments; + posts = Provider.of(context, listen: false) + .gettingProfilePostData; setState(() { _isLoading = false; }); @@ -97,8 +99,10 @@ class _PostScreenState extends State { @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar(), - body: SingleChildScrollView( + appBar: AppBar(), + body: Center( + child: Container( + width: 40.w, child: Column( children: [ // Post.home( @@ -106,10 +110,14 @@ class _PostScreenState extends State { // inView: false, // userName: 'Amr', // ) - Comment( - data: commentsData![0], - userName: 'Amr', - ), + _isLoading + ? LoadingReddit() + : Container( + child: PostList( + userName: 'Amr', + updateData: updateData, + data: posts as List), + ) // Comment( // data: commentsData![2], // userName: 'Amr', @@ -180,6 +188,8 @@ class _PostScreenState extends State { // ), ], ), - )); + ), + ), + ); } } diff --git a/lib/post/widgets/post.dart b/lib/post/widgets/post.dart index 898e60d4..ca3bc888 100644 --- a/lib/post/widgets/post.dart +++ b/lib/post/widgets/post.dart @@ -137,6 +137,7 @@ class _PostState extends State { data: data, ), PostFooter( + userName: widget.userName, inScreen: widget.inScreen, data: widget.data, isMyPost: (widget.data.author?.name == widget.userName), diff --git a/lib/post/widgets/post_body.dart b/lib/post/widgets/post_body.dart index 6084c7d5..160a797f 100644 --- a/lib/post/widgets/post_body.dart +++ b/lib/post/widgets/post_body.dart @@ -2,11 +2,18 @@ import 'package:flutter/material.dart'; import 'package:post/post/models/post_model.dart'; import 'package:post/post/widgets/post_card.dart'; import 'package:post/post/widgets/post_images.dart'; +import 'package:post/post/widgets/post_images_web.dart'; +import 'package:post/post/widgets/post_link_in_screen.dart'; +import 'package:post/post/widgets/post_pop_up_web.dart'; import 'package:post/post/widgets/post_tags_and_title.dart'; +import 'package:post/post/widgets/post_video_in_widget_web.dart'; import 'package:video_player/video_player.dart'; import '../../show_post/screens/show_post.dart'; +import '../../show_post/screens/show_post_web.dart'; +import '../../widgets/loading_reddit.dart'; import 'post_video_in_widget.dart'; +import 'package:flutter/foundation.dart' show kIsWeb; /// This Widget is responsible for the body of the post. @@ -29,10 +36,23 @@ class _PostBodyState extends State { @override Widget build(BuildContext context) { return Flexible( - child: GestureDetector( + child: InkWell( onTap: () { - Navigator.of(context).pushNamed(ShowPostDetails.routeName, - arguments: {'data': widget.data, 'userName': widget.userName}); + kIsWeb + ? Navigator.of(context).pushNamed(ShowPostDetailsWeb.routeName, + arguments: {'data': widget.data, 'userName': widget.userName}) + // ? showDialog( + // context: context, + // builder: (context) => PostPopUpWeb( + // data: widget.data, + // userName: widget.userName, + // ), + // ) + : Navigator.of(context).pushNamed(ShowPostDetails.routeName, + arguments: { + 'data': widget.data, + 'userName': widget.userName + }); }, child: Container( color: (widget.data.isSpam ?? false) @@ -54,55 +74,86 @@ class _PostBodyState extends State { title: widget.data.title as String, ), ), - - // Container( - // padding: const EdgeInsetsDirectional.only( - // start: 10, end: 10, top: 10), - // child: Text( - // widget.data.text as String, - // maxLines: 3, - // style: TextStyle( - // color: Theme.of(context).colorScheme.secondary, - // fontSize: 12, - // ), - // textAlign: TextAlign.start, - // overflow: TextOverflow.ellipsis, - // ), - // ), + Container( + padding: const EdgeInsetsDirectional.only( + start: 10, end: 10, top: 10), + child: Text( + widget.data.text as String, + maxLines: 3, + style: TextStyle( + color: Theme.of(context).colorScheme.secondary, + fontSize: 12, + ), + textAlign: TextAlign.start, + overflow: TextOverflow.ellipsis, + ), + ), ], ) : (widget.data.kind == 'image') - ? PostImages( - data: widget.data, - flair: widget.data.flairId, - nsfw: widget.data.nsfw as bool, - spoiler: widget.data.spoiler as bool, - title: widget.data.title as String, - imageNumber: widget.data.imageNumber, - links: widget.data.images!.cast(), - maxHeightImageSize: - widget.data.maxHeightImageSize as Size, - ) - : (widget.data.kind == 'link') - ? PostCard( + ? kIsWeb + ? PostImagesWeb( + data: widget.data, flair: widget.data.flairId, nsfw: widget.data.nsfw as bool, spoiler: widget.data.spoiler as bool, - link: widget.data.url as String, title: widget.data.title as String, - type: widget.data.kind as String, + imageNumber: widget.data.imageNumber, + links: widget.data.images!.cast(), + maxHeightImageSize: + widget.data.maxHeightImageSize as Size, ) - : (widget.data.kind == 'video') - ? PostVideoInWidget( + : PostImages( + data: widget.data, + flair: widget.data.flairId, + nsfw: widget.data.nsfw as bool, + spoiler: widget.data.spoiler as bool, + title: widget.data.title as String, + imageNumber: widget.data.imageNumber, + links: widget.data.images!.cast(), + maxHeightImageSize: + widget.data.maxHeightImageSize as Size, + ) + : (widget.data.kind == 'link') + ? kIsWeb + ? PostLinkInScreen( + flair: widget.data.flairId, + nsfw: widget.data.nsfw as bool, + spoiler: widget.data.spoiler as bool, + link: widget.data.url as String, title: widget.data.title as String, + type: widget.data.kind as String, + ) + : PostCard( flair: widget.data.flairId, nsfw: widget.data.nsfw as bool, spoiler: widget.data.spoiler as bool, - inView: widget.inView, - url: widget.data.video as String, - videoController: widget.data.videoController - as VideoPlayerController, + link: widget.data.url as String, + title: widget.data.title as String, + type: widget.data.kind as String, ) + : (widget.data.kind == 'video') + ? kIsWeb + ? PostVideoInWidgetWeb( + title: widget.data.title as String, + flair: widget.data.flairId, + nsfw: widget.data.nsfw as bool, + spoiler: widget.data.spoiler as bool, + inView: widget.inView, + url: widget.data.video as String, + videoController: widget.data.videoController + as VideoPlayerController, + ) + : PostVideoInWidget( + title: widget.data.title as String, + flair: widget.data.flairId, + nsfw: widget.data.nsfw as bool, + spoiler: widget.data.spoiler as bool, + inView: widget.inView, + url: widget.data.video as String, + videoController: widget.data.videoController + as VideoPlayerController, + ) : const SizedBox(), ), ), diff --git a/lib/post/widgets/post_body_in_screen.dart b/lib/post/widgets/post_body_in_screen.dart index a919fa4a..18645f83 100644 --- a/lib/post/widgets/post_body_in_screen.dart +++ b/lib/post/widgets/post_body_in_screen.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:post/post/models/post_model.dart'; import 'package:post/post/widgets/post_images.dart'; +import 'package:post/post/widgets/post_images_in_screen.dart'; import 'package:post/post/widgets/post_link_in_screen.dart'; import 'package:post/post/widgets/post_tags_and_title.dart'; import 'package:video_player/video_player.dart'; @@ -62,7 +63,7 @@ class _PostBodyInScreenState extends State { ], ) : (widget.data.kind == 'image') - ? PostImages( + ? PostImagesInScreen( data: widget.data, flair: widget.data.flairId, nsfw: widget.data.nsfw as bool, @@ -70,8 +71,7 @@ class _PostBodyInScreenState extends State { title: widget.data.title as String, imageNumber: widget.data.imageNumber, links: widget.data.images!.cast(), - maxHeightImageSize: - widget.data.maxHeightImageSize as Size, + maxHeightImageSize: widget.data.maxHeightImageSize as Size, ) : (widget.data.kind == 'link') ? PostLinkInScreen( diff --git a/lib/post/widgets/post_footer.dart b/lib/post/widgets/post_footer.dart index 9cf0e87b..0556c639 100644 --- a/lib/post/widgets/post_footer.dart +++ b/lib/post/widgets/post_footer.dart @@ -1,3 +1,4 @@ +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:fluttericon/font_awesome_icons.dart'; import 'package:fluttericon/typicons_icons.dart'; @@ -7,6 +8,9 @@ import 'package:post/post/provider/post_provider.dart'; import 'package:provider/provider.dart'; import 'package:hexcolor/hexcolor.dart'; +import '../../show_post/screens/show_post.dart'; +import '../../show_post/screens/show_post_web.dart'; + /// This Widget is responsible for the footer of the post. class PostFooter extends StatefulWidget { @@ -23,6 +27,7 @@ class PostFooter extends StatefulWidget { final String id; final PostModel data; final bool inScreen; + final String userName; const PostFooter( {super.key, required this.votes, @@ -31,7 +36,8 @@ class PostFooter extends StatefulWidget { required this.id, required this.isMyPost, required this.data, - required this.inScreen}); + required this.inScreen, + required this.userName}); @override State createState() => _PostFooterState(postVoteStatus: postVoteStatus, votes: votes); @@ -170,7 +176,30 @@ class _PostFooterState extends State { ], ), InkWell( - onTap: null, + onTap: widget.inScreen + ? null + : () { + kIsWeb + ? Navigator.of(context).pushNamed( + ShowPostDetailsWeb.routeName, + arguments: { + 'data': widget.data, + 'userName': widget.userName + }) + // ? showDialog( + // context: context, + // builder: (context) => PostPopUpWeb( + // data: widget.data, + // userName: widget.userName, + // ), + // ) + : Navigator.of(context).pushNamed( + ShowPostDetails.routeName, + arguments: { + 'data': widget.data, + 'userName': widget.userName + }); + }, child: Row( children: [ Icon( diff --git a/lib/post/widgets/post_header.dart b/lib/post/widgets/post_header.dart index 371f147b..fad14bd7 100644 --- a/lib/post/widgets/post_header.dart +++ b/lib/post/widgets/post_header.dart @@ -5,6 +5,7 @@ import 'package:post/post/widgets/user_info_popup.dart'; import 'package:post/subreddit/screens/subreddit_screen.dart'; import '../../moderated_subreddit/screens/moderated_subreddit_screen.dart'; import '../models/post_model.dart'; +import 'package:flutter/foundation.dart' show kIsWeb; /// This Widget is responsible for the header of the post. @@ -109,7 +110,7 @@ class _PostHeaderState extends State { size: 18, ) : const SizedBox(), - !widget.inScreen + !widget.inScreen || kIsWeb ? PostPopupMenu( isMyPost: widget.isMyPost, data: widget.data, @@ -183,7 +184,6 @@ class _PostHeaderBasicState extends State { widget.ownerType == 'User' && !widget.inProfile ? const SizedBox() : InkWell( - onTap: (!widget.inProfile) ? () { print( @@ -197,17 +197,16 @@ class _PostHeaderBasicState extends State { ), ); } - : (){ - - print( + : () { + print( '===============================Is mod:${widget.isModerator}============================='); - - Navigator.of(context).pushNamed( - widget.isModerator - ? ModeratedSubredditScreen.routeName - : SubredditScreen.routeName, - arguments: widget.ownerName);}, + Navigator.of(context).pushNamed( + widget.isModerator + ? ModeratedSubredditScreen.routeName + : SubredditScreen.routeName, + arguments: widget.ownerName); + }, child: Row( children: [ if (widget.inProfile) diff --git a/lib/post/widgets/post_images_in_screen.dart b/lib/post/widgets/post_images_in_screen.dart new file mode 100644 index 00000000..ddc9b328 --- /dev/null +++ b/lib/post/widgets/post_images_in_screen.dart @@ -0,0 +1,153 @@ +import 'dart:async'; +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:dots_indicator/dots_indicator.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_image_slideshow/flutter_image_slideshow.dart'; +import 'package:post/post/models/post_model.dart'; +import 'package:post/post/widgets/post_tags_and_title.dart'; +import 'package:post/widgets/loading_reddit.dart'; + +class PostImagesInScreen extends StatefulWidget { + final PostModel data; + final List links; + final Size maxHeightImageSize; + final int imageNumber; + final String title; + final bool spoiler; + final bool nsfw; + final FlairId? flair; + const PostImagesInScreen({ + super.key, + required this.links, + required this.maxHeightImageSize, + required this.imageNumber, + required this.title, + required this.spoiler, + required this.nsfw, + required this.flair, + required this.data, + }); + + @override + State createState() => + _PostImagesInScreenState(imageNumber); +} + +class _PostImagesInScreenState extends State { + int imageNumber; + bool imageCounterVisible = false; + late List images; + _PostImagesInScreenState(this.imageNumber); + + void updateImageNumber(value) => setState(() { + imageNumber = value; + widget.data.imageNumber = value; + imageCounterVisible = true; + var timer; + timer = Timer(const Duration(milliseconds: 3000), () { + if (!mounted) { + timer.cancel(); + } else { + setState(() { + imageCounterVisible = false; + }); + } + }); + }); + @override + Widget build(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + PostTagsAndTitle( + flair: widget.flair, + isSpoiler: widget.spoiler, + isNSFW: widget.nsfw, + title: widget.title, + ), + Container( + margin: const EdgeInsetsDirectional.only(top: 10, bottom: 10), + child: Stack( + alignment: Alignment.topRight, + children: [ + Stack( + alignment: Alignment.bottomCenter, + children: [ + ImageSlideshow( + initialPage: imageNumber, + height: !(widget.maxHeightImageSize.height * + (MediaQuery.of(context).size.width / + widget.maxHeightImageSize.width)) + .isNaN + ? (widget.maxHeightImageSize.height * + (MediaQuery.of(context).size.width / + widget.maxHeightImageSize.width) < + .5 * MediaQuery.of(context).size.height) + ? (widget.maxHeightImageSize.height * + (MediaQuery.of(context).size.width / + widget.maxHeightImageSize.width)) + : .5 * MediaQuery.of(context).size.height + : .5 * MediaQuery.of(context).size.height, + indicatorColor: Colors.white, + indicatorRadius: 0, + onPageChanged: (value) => updateImageNumber(value), + children: widget.links.map((String link) { + CachedNetworkImage image = CachedNetworkImage( + fit: BoxFit.contain, + imageUrl: link, + placeholder: (context, url) => const LoadingReddit(), + ); + return image; + }).toList(), + ), + AnimatedOpacity( + opacity: imageCounterVisible ? 1 : 0, + duration: const Duration(milliseconds: 500), + child: Container( + margin: const EdgeInsetsDirectional.only(bottom: 8), + child: DotsIndicator( + decorator: const DotsDecorator( + size: Size.square(8), + activeSize: Size.square(8), + activeColor: Colors.white, + color: Colors.transparent, + shape: CircleBorder( + side: BorderSide(width: 1, color: Colors.white), + ), + spacing: EdgeInsets.all(4.0)), + dotsCount: + widget.links.isEmpty ? 1 : widget.links.length, + position: imageNumber.toDouble(), + ), + ), + ), + ], + ), + AnimatedOpacity( + opacity: imageCounterVisible ? 1 : 0, + duration: const Duration(milliseconds: 500), + child: Container( + margin: const EdgeInsetsDirectional.only(top: 8, end: 8), + child: ClipRRect( + borderRadius: BorderRadius.circular(20.0), + child: Container( + padding: const EdgeInsetsDirectional.only( + start: 10, end: 10, top: 5, bottom: 5), + color: Colors.black.withOpacity(0.6), + child: Text( + '${(imageNumber + 1).toString()}/${widget.links.length}', + style: const TextStyle( + color: Colors.white, + overflow: TextOverflow.ellipsis), + ), + ), + ), + ), + ) + ], + ), + ), + ], + ); + } +} diff --git a/lib/post/widgets/post_images_web.dart b/lib/post/widgets/post_images_web.dart new file mode 100644 index 00000000..976b54c7 --- /dev/null +++ b/lib/post/widgets/post_images_web.dart @@ -0,0 +1,152 @@ +import 'dart:async'; +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:dots_indicator/dots_indicator.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_image_slideshow/flutter_image_slideshow.dart'; +import 'package:post/post/models/post_model.dart'; +import 'package:post/post/widgets/post_tags_and_title.dart'; +import 'package:post/widgets/loading_reddit.dart'; + +class PostImagesWeb extends StatefulWidget { + final PostModel data; + final List links; + final Size maxHeightImageSize; + final int imageNumber; + final String title; + final bool spoiler; + final bool nsfw; + final FlairId? flair; + const PostImagesWeb({ + super.key, + required this.links, + required this.maxHeightImageSize, + required this.imageNumber, + required this.title, + required this.spoiler, + required this.nsfw, + required this.flair, + required this.data, + }); + + @override + State createState() => _PostImagesWebState(imageNumber); +} + +class _PostImagesWebState extends State { + int imageNumber; + bool imageCounterVisible = false; + late List images; + _PostImagesWebState(this.imageNumber); + + void updateImageNumber(value) => setState(() { + imageNumber = value; + widget.data.imageNumber = value; + imageCounterVisible = true; + var timer; + timer = Timer(const Duration(milliseconds: 3000), () { + if (!mounted) { + timer.cancel(); + } else { + setState(() { + imageCounterVisible = false; + }); + } + }); + }); + @override + Widget build(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + PostTagsAndTitle( + flair: widget.flair, + isSpoiler: widget.spoiler, + isNSFW: widget.nsfw, + title: widget.title, + ), + Container( + margin: const EdgeInsetsDirectional.only(top: 10, bottom: 10), + child: Stack( + alignment: Alignment.topRight, + children: [ + Stack( + alignment: Alignment.bottomCenter, + children: [ + ImageSlideshow( + initialPage: imageNumber, + height: !(widget.maxHeightImageSize.height * + (MediaQuery.of(context).size.width / + widget.maxHeightImageSize.width)) + .isNaN + ? (widget.maxHeightImageSize.height * + (MediaQuery.of(context).size.width / + widget.maxHeightImageSize.width) < + .5 * MediaQuery.of(context).size.height) + ? (widget.maxHeightImageSize.height * + (MediaQuery.of(context).size.width / + widget.maxHeightImageSize.width)) + : .5 * MediaQuery.of(context).size.height + : .5 * MediaQuery.of(context).size.height, + indicatorColor: Colors.white, + indicatorRadius: 0, + onPageChanged: (value) => updateImageNumber(value), + children: widget.links.map((String link) { + CachedNetworkImage image = CachedNetworkImage( + fit: BoxFit.cover, + imageUrl: link, + placeholder: (context, url) => const LoadingReddit(), + ); + return image; + }).toList(), + ), + AnimatedOpacity( + opacity: imageCounterVisible ? 1 : 0, + duration: const Duration(milliseconds: 500), + child: Container( + margin: const EdgeInsetsDirectional.only(bottom: 8), + child: DotsIndicator( + decorator: const DotsDecorator( + size: Size.square(8), + activeSize: Size.square(8), + activeColor: Colors.white, + color: Colors.transparent, + shape: CircleBorder( + side: BorderSide(width: 1, color: Colors.white), + ), + spacing: EdgeInsets.all(4.0)), + dotsCount: + widget.links.isEmpty ? 1 : widget.links.length, + position: imageNumber.toDouble(), + ), + ), + ), + ], + ), + AnimatedOpacity( + opacity: imageCounterVisible ? 1 : 0, + duration: const Duration(milliseconds: 500), + child: Container( + margin: const EdgeInsetsDirectional.only(top: 8, end: 8), + child: ClipRRect( + borderRadius: BorderRadius.circular(20.0), + child: Container( + padding: const EdgeInsetsDirectional.only( + start: 10, end: 10, top: 5, bottom: 5), + color: Colors.black.withOpacity(0.6), + child: Text( + '${(imageNumber + 1).toString()}/${widget.links.length}', + style: const TextStyle( + color: Colors.white, + overflow: TextOverflow.ellipsis), + ), + ), + ), + ), + ) + ], + ), + ), + ], + ); + } +} diff --git a/lib/post/widgets/post_pop_up_web.dart b/lib/post/widgets/post_pop_up_web.dart new file mode 100644 index 00000000..7715c246 --- /dev/null +++ b/lib/post/widgets/post_pop_up_web.dart @@ -0,0 +1,53 @@ +import 'package:flutter/material.dart'; +import 'package:responsive_sizer/responsive_sizer.dart'; + +import '../../comments/widgets/comments_list.dart'; +import '../../post/models/post_model.dart'; +import 'post.dart'; + +//import '../widgets/edit_post.dart'; + +class PostPopUpWeb extends StatefulWidget { + final String userName; + final PostModel data; + static const routeName = '/showpost-screen'; + + const PostPopUpWeb({ + super.key, + required this.userName, + required this.data, + }); + + @override + State createState() => _PostPopUpWebState(); +} + +class _PostPopUpWebState extends State { + @override + Widget build(BuildContext context) { + return AlertDialog( + content: Container( + height: MediaQuery.of(context).size.width * .8, + width: MediaQuery.of(context).size.width * .8, + child: SingleChildScrollView( + child: Container( + width: 40.w, + child: Column( + children: [ + Post.home( + data: widget.data, + userName: widget.userName, + inView: true, + inScreen: true), + CommentsList( + postId: widget.data.sId ?? '', + userName: widget.userName, + ) + ], + ), + ), + ), + ), + ); + } +} diff --git a/lib/post/widgets/post_video_in_widget_web.dart b/lib/post/widgets/post_video_in_widget_web.dart new file mode 100644 index 00000000..f5423670 --- /dev/null +++ b/lib/post/widgets/post_video_in_widget_web.dart @@ -0,0 +1,186 @@ +import 'package:flutter/material.dart'; +import 'package:post/post/widgets/post_tags_and_title.dart'; +import 'package:post/providers/global_settings.dart'; +import 'package:provider/provider.dart'; +import 'package:video_player/video_player.dart'; +import 'package:chewie/chewie.dart'; + +import '../models/post_model.dart'; + +class PostVideoInWidgetWeb extends StatefulWidget { + /// The url of the video + final String url; + final VideoPlayerController videoController; + final bool inView; + final String title; + final bool spoiler; + final bool nsfw; + final FlairId? flair; + const PostVideoInWidgetWeb({ + super.key, + required this.url, + required this.videoController, + required this.inView, + required this.title, + required this.spoiler, + required this.nsfw, + required this.flair, + }); + + @override + State createState() => _PostVideoInWidgetWebState(); +} + +class _PostVideoInWidgetWebState extends State { + late ChewieController chewieController; + bool isMuted = true; + bool autoPlay = true; + @override + void initState() { + super.initState(); + chewieController = ChewieController( + videoPlayerController: widget.videoController, + autoInitialize: true, + autoPlay: false, + looping: true, + showControls: false, + ); + } + + updateMuted() { + if (Provider.of(context, listen: false).getIsMuted) { + widget.videoController.setVolume(0); + } else { + widget.videoController.setVolume(1); + } + setState(() { + isMuted = Provider.of(context, listen: false).getIsMuted; + }); + } + + play() { + if (Provider.of(context, listen: false).getIsMuted) { + widget.videoController.setVolume(0); + } else { + widget.videoController.setVolume(1); + } + widget.videoController.play(); + setState(() { + isMuted = Provider.of(context, listen: false).getIsMuted; + }); + } + + toggleSound() { + if (Provider.of(context, listen: false).getIsMuted) { + Provider.of(context, listen: false).unMute(); + updateMuted(); + } else { + Provider.of(context, listen: false).mute(); + updateMuted(); + } + } + + @override + Widget build(BuildContext context) { + if (widget.inView && !widget.videoController.value.isPlaying && autoPlay) { + { + play(); + } + } else if (!widget.inView && widget.videoController.value.isPlaying) { + { + widget.videoController.pause(); + widget.videoController.seekTo(const Duration(seconds: 0)); + updateMuted(); + } + } + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + PostTagsAndTitle( + flair: widget.flair, + isSpoiler: widget.spoiler, + isNSFW: widget.nsfw, + title: widget.title, + ), + Center( + child: widget.videoController.value.isInitialized + ? Container( + margin: const EdgeInsetsDirectional.only( + start: 20, end: 20, top: 10), + height: (widget.videoController.value.size.height * + (MediaQuery.of(context).size.width / + widget.videoController.value.size.width) < + MediaQuery.of(context).size.height * .5) + ? widget.videoController.value.size.height * + (MediaQuery.of(context).size.width / + widget.videoController.value.size.width) + : MediaQuery.of(context).size.height * 0.5, + child: Stack( + alignment: Alignment.center, + children: [ + Stack( + alignment: Alignment.bottomRight, + children: [ + Align( + alignment: Alignment.center, + child: AspectRatio( + aspectRatio: + widget.videoController.value.aspectRatio, + child: Chewie( + controller: chewieController, + ), + ), + ), + GestureDetector( + onTap: () => toggleSound(), + child: Container( + margin: const EdgeInsetsDirectional.all(10), + child: ClipRRect( + borderRadius: BorderRadius.circular(20.0), + child: Container( + padding: const EdgeInsetsDirectional.all(2), + color: Colors.black.withOpacity(0.5), + child: Icon( + isMuted + ? Icons.volume_off + : Icons.volume_up, + color: Colors.white, + size: 15, + ), + ), + ), + ), + ), + ], + ), + !autoPlay + ? GestureDetector( + onTap: () { + autoPlay = true; + play(); + }, + child: Container( + margin: const EdgeInsetsDirectional.all(10), + child: const Icon( + Icons.play_circle_outline_rounded, + color: Colors.white, + size: 50, + ), + ), + ) + : const SizedBox(), + ], + ), + ) + : Container(), + ), + ], + ); + } + + @override + void dispose() { + chewieController.dispose(); + super.dispose(); + } +} diff --git a/lib/show_post/screens/show_post_web.dart b/lib/show_post/screens/show_post_web.dart new file mode 100644 index 00000000..099e0348 --- /dev/null +++ b/lib/show_post/screens/show_post_web.dart @@ -0,0 +1,150 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:post/comments/screens/add_comment_screen.dart'; +import 'package:responsive_sizer/responsive_sizer.dart'; + +import '../../comments/widgets/comments_list.dart'; +import '../../notification/widgets/list_tile_widget.dart'; +import '../../post/models/post_model.dart'; +import '../../post/widgets/post.dart'; +import '../widgets/edit_post.dart'; + +class ShowPostDetailsWeb extends StatefulWidget { + static const routeName = '/showpost-screen'; + + const ShowPostDetailsWeb({ + super.key, + }); + + @override + State createState() => _ShowPostDetailsWebState(); +} + +class _ShowPostDetailsWebState extends State { + late String userName; + late PostModel data; + @override + void didChangeDependencies() { + // TODO: implement didChangeDependencies + //===============================Fetch subreddit data =======================================// + + var temp = + ModalRoute.of(context)?.settings.arguments as Map; + userName = temp['userName']; + data = temp['data']; + super.didChangeDependencies(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + backgroundColor: Colors.blue, + actions: [ + // IconButton( + // onPressed: () { + PopupMenuButton( + // elevation: -1, + // position: PopupMenuPosition.under, + constraints: BoxConstraints.expand(width: 60.w, height: 55.h), + icon: const Icon(Icons.more_vert, color: Colors.white), + itemBuilder: (context) { + //return list.map((e) { + return [ + PopupMenuItem( + child: ListTileWidget(icon: Icons.share, title: 'Share')), + PopupMenuItem( + child: ListTileWidget(icon: Icons.save, title: 'Save')), + PopupMenuItem( + child: + ListTileWidget(icon: Icons.copy, title: 'Copy text')), + PopupMenuItem( + child: ListTileWidget( + icon: Icons.edit, + title: 'Edit', + onpressed: () => Navigator.of(context) + .popAndPushNamed(EditPost.routeName))), + PopupMenuItem( + child: ListTileWidget( + icon: Icons.tag, title: 'Add post flair')), + PopupMenuItem( + child: ListTileWidget( + icon: Icons.warning, title: 'Mark spoiler')), + PopupMenuItem( + child: ListTileWidget( + icon: Icons.plus_one, title: 'Mark NSFW')), + PopupMenuItem( + child: ListTileWidget(icon: Icons.delete, title: 'Delete')), + ]; + }, + ), + ], + ), + body: Center( + child: Container( + width: 50.w, + child: SingleChildScrollView( + child: Column( + children: [ + Post.home( + data: data, + userName: userName, + inView: true, + inScreen: true), + CommentsList( + postId: data.sId ?? '', + userName: userName, + ) + ], + ), + ), + ), + ), + bottomNavigationBar: Padding( + padding: const EdgeInsetsDirectional.all(5), + child: GestureDetector( + onTap: () { + Get.to(AddCommentScreen(), + arguments: {'parentId': data.sId, 'title': data.title}); + }, + child: Container( + decoration: BoxDecoration( + color: Colors.grey[100], + border: Border.all( + color: Colors.grey, + ), + ), + child: const Padding( + padding: EdgeInsetsDirectional.all(5), + child: Text( + 'Add a comment', + style: TextStyle(color: Colors.grey), + ), + ), + ), + ) + + // TextFormField( + // onChanged: (value) {}, + // onTap: () { + // Get.to(AddCommentScreen(), + // arguments: {'parentId': data.sId, 'title': data.title}); + // }, + // enabled: false, + // style: const TextStyle(fontSize: 14.0, fontWeight: FontWeight.w700), + // showCursor: true, + + // textAlign: TextAlign.start, + // decoration: InputDecoration( + // hintText: 'Add a comment', + // enabledBorder: OutlineInputBorder( + // borderSide: + // BorderSide(width: 2, color: Colors.grey), //<-- SEE HERE + // borderRadius: BorderRadius.circular(10.0), + // ), + // ), + // ), + ), + ); + } +} From 2ea2cd7459c9b0d170e73e9916bf132983f97a8d Mon Sep 17 00:00:00 2001 From: Amr Ahmed Date: Fri, 23 Dec 2022 14:37:12 +0200 Subject: [PATCH 02/16] modified main --- lib/main.dart | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/lib/main.dart b/lib/main.dart index cf4400ac..6d68c297 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -9,6 +9,7 @@ import 'package:post/create_community/widgets/community_type.dart'; import 'package:post/messages/screens/reply_message_screen.dart'; import 'package:post/messages/screens/show_message_body.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart'; +import 'package:post/show_post/screens/show_post_web.dart'; import './messages/screens/web_message_all_screen.dart'; import './messages/screens/web_message_screen.dart'; import 'package:post/providers/global_settings.dart'; @@ -497,15 +498,15 @@ class _MyAppState extends State { onSurface: Colors.white), ), // home: NotificationScreen(), - // home: HomeLayoutScreen(), + // home: HomeLayoutScreen(), // home: Description(), // home: HomeScreen(), // home: NotificationScreen(), // home:ShowPostDetails(), //home: CreateCommunity(), - //home: NewMessageScreen(), + //home: NewMessageScreen(), // home: EditPost(), - // home: WebMessageScreen(), + // home: WebMessageScreen(), // home: SearchInside(quiry: 'mohab'), //home: const DiscoverScreen(), // home: homeLayoutScreen(), @@ -519,7 +520,7 @@ class _MyAppState extends State { // home: HomeScreen(), home: Login(), // home: CreateCommunity(), - //home: Login(), + //home: Login(), // home: ForgotUserName(), // home: ForgotPassword(), // home: TraficState(), @@ -541,10 +542,10 @@ class _MyAppState extends State { // home: Search(), routes: { TraficState.routeName: (context) => TraficState(), - AllMessageScreen.routeName : (context) => AllMessageScreen(), - WebNewMessageScreen.routeName : (context) => WebNewMessageScreen(), - SentMessage.routeName : (context) => SentMessage(), - UnreadMessageScreen.routeName : (context) => UnreadMessageScreen(), + AllMessageScreen.routeName: (context) => AllMessageScreen(), + WebNewMessageScreen.routeName: (context) => WebNewMessageScreen(), + SentMessage.routeName: (context) => SentMessage(), + UnreadMessageScreen.routeName: (context) => UnreadMessageScreen(), ReplyMessageScreen.routeName: (context) => ReplyMessageScreen(), ShowMessageBody.routeName: (context) => ShowMessageBody(), MessageMainScreen.routeName: (context) => MessageMainScreen(), @@ -621,6 +622,8 @@ class _MyAppState extends State { ModNotificationScreen(), ModeratedSubredditScreen.routeName: (context) => ModeratedSubredditScreen(), + ShowPostDetailsWeb.routeName: (context) => ShowPostDetailsWeb(), + // LoginPage.routeName: (context) => LoginPage(), }, ), From 1260253ef05b87ec3a06155b154c7770d2e92cbb Mon Sep 17 00:00:00 2001 From: Amr Ahmed Date: Fri, 23 Dec 2022 15:26:31 +0200 Subject: [PATCH 03/16] added overview list --- lib/post/widgets/post_comment_list.dart | 142 ++++++++++++++++++++++++ 1 file changed, 142 insertions(+) create mode 100644 lib/post/widgets/post_comment_list.dart diff --git a/lib/post/widgets/post_comment_list.dart b/lib/post/widgets/post_comment_list.dart new file mode 100644 index 00000000..56663bca --- /dev/null +++ b/lib/post/widgets/post_comment_list.dart @@ -0,0 +1,142 @@ +import 'package:flutter/material.dart'; +import 'package:fluttericon/typicons_icons.dart'; +import 'package:inview_notifier_list/inview_notifier_list.dart'; +import '../models/post_model.dart'; +import './post.dart'; + +class PostList extends StatefulWidget { + final String userName; + final Widget topOfTheList; + final Function updateData; + final List> data; + + final String type; + const PostList( + {super.key, + required this.userName, + this.topOfTheList = const SizedBox(), + required this.updateData, + required this.data, + this.type = 'home'}); + + @override + State createState() => _PostListState(); +} + +class _PostListState extends State { + String dateOfcomment(String date) { + final data1 = DateTime.parse(date); + final date2 = DateTime.now(); + final difference = date2.difference(data1); + final differenceMonth = date2.month - data1.month; + final differenceYear = date2.year - data1.year; + + if (difference.inHours < 24) { + return '${difference.inHours}h'; + } else if (difference.inDays < 30) { + final numOfWeeks = difference.inDays ~/ 7; + if (numOfWeeks > 0) { + return '${numOfWeeks}w'; + } else { + return '${difference.inDays}d'; + } + } else if (differenceYear > 0) { + return '${differenceYear}y'; + } else { + return '${differenceMonth}mon'; + } + } + + @override + Widget build(BuildContext context) { + return Flexible( + child: InViewNotifierCustomScrollView( + onListEndReached: () => widget.updateData(), + scrollDirection: Axis.vertical, + initialInViewIds: const ['0'], + isInViewPortCondition: + (double deltaTop, double deltaBottom, double viewPortDimension) { + return deltaTop < (0.5 * viewPortDimension) && + deltaBottom > (0.5 * viewPortDimension); + }, + slivers: [ + SliverList( + delegate: SliverChildBuilderDelegate( + childCount: 1, + (context, index) => widget.topOfTheList, + ), + ), + SliverList( + delegate: SliverChildBuilderDelegate(childCount: widget.data.length, + (context, index) { + return InViewNotifierWidget( + id: '$index', + builder: (context, isInView, child) { + if (widget.data[index]['type'] == 'comment') { + return ListTile( + title: + Text(widget.data[index]['data'].title.toString()), + subtitle: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Text.rich( + TextSpan( + children: [ + TextSpan( + text: + '${(widget.data[index]['data'].ownerType == 'User') ? 'u/' : 'r/'}${widget.data[index]['data'].owner}.${dateOfcomment(widget.data[index]['data'].createdAt.toString())} .${widget.data[index]['data'].votes}'), + const WidgetSpan( + child: Icon( + Typicons.up, + size: 15, + )), + ], + ), + ), + Text(widget.data[index]['data'].text.toString()), + ], + ), + ); + } else { + if (widget.type == 'profile') { + return Post.profile( + userName: widget.userName, + inView: isInView, + data: widget.data[index]['data'], + ); + } else if (widget.type == 'community') { + return Post.community( + userName: widget.userName, + inView: isInView, + data: widget.data[index]['data'], + ); + } else { + return Post.home( + userName: widget.userName, + inView: isInView, + data: widget.data[index]['data'], + ); + } + } + }); + }), + ) + ], + ), + ); + } + + @override + void dispose() { + if (widget.data != null) { + for (var d in widget.data) { + d['type'] == 'comment'; + if (d['data'].kind == 'video') { + d['data'].videoController?.dispose(); + } + } + } + super.dispose(); + } +} From 00f2999649e95c09c508a670caba8bd29e795aa8 Mon Sep 17 00:00:00 2001 From: Amr Ahmed Date: Fri, 23 Dec 2022 15:37:19 +0200 Subject: [PATCH 04/16] added overview list --- lib/post/widgets/post_comment_list.dart | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/post/widgets/post_comment_list.dart b/lib/post/widgets/post_comment_list.dart index 56663bca..43ae3a58 100644 --- a/lib/post/widgets/post_comment_list.dart +++ b/lib/post/widgets/post_comment_list.dart @@ -4,14 +4,14 @@ import 'package:inview_notifier_list/inview_notifier_list.dart'; import '../models/post_model.dart'; import './post.dart'; -class PostList extends StatefulWidget { +class PostCommentList extends StatefulWidget { final String userName; final Widget topOfTheList; final Function updateData; final List> data; final String type; - const PostList( + const PostCommentList( {super.key, required this.userName, this.topOfTheList = const SizedBox(), @@ -20,10 +20,10 @@ class PostList extends StatefulWidget { this.type = 'home'}); @override - State createState() => _PostListState(); + State createState() => _PostCommentListState(); } -class _PostListState extends State { +class _PostCommentListState extends State { String dateOfcomment(String date) { final data1 = DateTime.parse(date); final date2 = DateTime.now(); From 7782cc1f2dade4888cbe9c4ce7b69da5c17946cf Mon Sep 17 00:00:00 2001 From: Ahmed Fawzy <99263226+belfooz@users.noreply.github.com> Date: Fri, 23 Dec 2022 21:01:21 +0200 Subject: [PATCH 05/16] Add files via upload --- pubspec.yaml | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/pubspec.yaml b/pubspec.yaml index bd6b0d2f..d785e900 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -54,6 +54,9 @@ dependencies: responsive_sizer: ^3.1.1 intl: ^0.17.0 provider: ^6.0.3 + animated_tree_view: ^1.0.0 + buttons_tabbar: ^1.3.6 + flutter_staggered_grid_view: ^0.6.2 mockito: ^5.3.2 build_runner: ^2.3.2 dartdoc: ^6.1.2 @@ -87,9 +90,8 @@ dependencies: url: https://github.com/Amr146/link_preview_generator.git expandable_page_view: ^1.0.17 flutter_html: ^2.1.1 - animated_tree_view: ^1.0.0 - buttons_tabbar: ^1.3.6 - flutter_staggered_grid_view: ^0.6.2 + responsive_framework: ^0.2.0 + device_preview: ^1.1.0 dev_dependencies: test_cov_console: ^0.2.2 @@ -226,6 +228,7 @@ flutter: - assets/images/up-arrow.png - assets/images/default-img.png - assets/images/reddit.png + - assets/images/ # - images/a_dot_burr.jpeg # - images/a_dot_ham.jpeg @@ -253,4 +256,7 @@ flutter: # weight: 700 # # For details regarding fonts from package dependencies, - # see https://flutter.dev/custom-fonts/#from-packages \ No newline at end of file + # see https://flutter.dev/custom-fonts/#from-packages + + + From fb4dc9c46eb5586683e9830da9a2808b4194553e Mon Sep 17 00:00:00 2001 From: Ahmed Fawzy <99263226+belfooz@users.noreply.github.com> Date: Fri, 23 Dec 2022 21:03:28 +0200 Subject: [PATCH 06/16] Add files via upload --- lib/home/controller/home_controller.dart | 202 ++--- lib/home/models/flairs.dart | 62 +- lib/home/models/test_post_model.dart | 448 ++++----- lib/home/screens/all.dart | 850 +++++++++++------- lib/home/screens/history.dart | 471 +++++----- lib/home/screens/home_layout.dart | 383 ++++++-- lib/home/screens/saved.dart | 138 +-- lib/home/screens/saved_comments.dart | 177 ++-- lib/home/screens/saved_posts.dart | 171 ++-- lib/home/widgets/buttom_nav_bar.dart | 5 +- lib/home/widgets/comment_layout.dart | 20 +- .../container_in_recently_visited.dart | 96 +- lib/home/widgets/custom_upper_bar.dart | 371 ++++++++ lib/home/widgets/end_drawer.dart | 309 +++---- lib/home/widgets/new_drawer.dart | 2 +- lib/home/widgets/recently_visited_list.dart | 148 +-- 16 files changed, 2336 insertions(+), 1517 deletions(-) create mode 100644 lib/home/widgets/custom_upper_bar.dart diff --git a/lib/home/controller/home_controller.dart b/lib/home/controller/home_controller.dart index f6d30f0f..b1d59cc5 100644 --- a/lib/home/controller/home_controller.dart +++ b/lib/home/controller/home_controller.dart @@ -1,41 +1,47 @@ +import 'dart:collection'; +import 'dart:io'; +import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:fluttertoast/fluttertoast.dart'; import 'package:get/get.dart'; +import 'package:post/create_community/screens/post.dart'; import 'package:post/createpost/model/subreddits_of_user.dart'; import 'package:post/myprofile/models/myprofile_data.dart'; import 'package:post/networks/const_endpoint_data.dart'; import 'package:shared_preferences/shared_preferences.dart'; import '../../comments/models/comment_model.dart'; +import '../../createpost/controllers/posts_controllers.dart'; import '../../networks/dio_client.dart'; import '../../post/models/post_model.dart'; class HomeController extends GetxController with StateMixin> { - RxString sortHistoryBy = "upvoted".obs; - RxString sortHomePostsBy = "best".obs; - RxString sortAllBy = "hot".obs; - - Rx allIcon = Icons.local_fire_department_rounded.obs; - RxInt pageNumber = 1.obs; - List homePosts = [].obs; - List allPosts = [].obs; - List userSavedPosts = [].obs; - List userSavedComments = [].obs; + RxString sortHistoryBy="upvoted".obs; + RxString sortHomePostsBy="best".obs; + RxString sortAllBy="hot".obs; + + RxallIcon=Icons.local_fire_department_rounded.obs; + RxInt pageNumber=1.obs; + ListhomePosts=[].obs; + ListallPosts=[].obs; + ListuserSavedPosts=[].obs; + ListuserSavedComments=[].obs; // ListupvotedPosts=[].obs; // ListdownvotedPosts=[].obs; // ListhiddenPosts=[].obs; - List historyPosts = [].obs; - RxBool isRecentlyVisitedDrawer = false.obs; - List recentlyVisited = [].obs; + ListhistoryPosts=[].obs; + RxBool isRecentlyVisitedDrawer=false.obs; + List recentlyVisited = [ + ].obs; //ListmyCommunities=[].obs; List following = [].obs; RxBool isRecentlyVisitedPannelExpanded = true.obs; RxBool isModeratingPannelExpanded = true.obs; RxBool isFollowingPannelExpanded = true.obs; RxBool isYourCommunitiesPannelExpanded = true.obs; - MyProfileData? myProfile; + MyProfileData? myProfile ; - ///////// for scrol in home////////////////////////// - ScrollController myScroll = ScrollController(); + ///////// for scrol in home////////////////////////// + ScrollController myScroll =ScrollController(); /// true when posts are loading. RxBool isLoading = false.obs; @@ -43,25 +49,22 @@ class HomeController extends GetxController with StateMixin> { /// true when error occurred RxBool error = false.obs; ///////////////////////for in all////////////// - ScrollController myScrollAll = ScrollController(); - RxInt pageNumberAll = 1.obs; - + ScrollController myScrollAll =ScrollController(); +RxInt pageNumberAll=1.obs; /// true when posts are loading. RxBool isLoadingAll = false.obs; /// true when error occurred RxBool errorAll = false.obs; //////History////////////// - RxInt pageNumberHistory = 1.obs; - + RxInt pageNumberHistory=1.obs; /// true when posts are loading. RxBool isLoadingHistory = false.obs; /// true when error occurred RxBool errorHistory = false.obs; //////////////SAVED///////////// - RxInt pageNumberSaved = 1.obs; - + RxInt pageNumberSaved=1.obs; /// true when posts are loading. RxBool isLoadingSaved = false.obs; @@ -69,20 +72,19 @@ class HomeController extends GetxController with StateMixin> { RxBool errorSaved = false.obs; //////////////SAVEDCOMMENTS///////////// - RxInt pageNumberComment = 1.obs; - + RxInt pageNumberComment=1.obs; /// true when posts are loading. RxBool isLoadingCommetns = false.obs; /// true when error occurred RxBool errorComment = false.obs; + @override void onInit() { getInfoOfMe(); super.onInit(); } - Future getInfoOfMe() async { try { final prefs = await SharedPreferences.getInstance(); @@ -92,106 +94,111 @@ class HomeController extends GetxController with StateMixin> { myProfile = MyProfileData.fromJson(response.data['user']); }); } catch (error) { - print( - "there is an error in fetching the data of my profile the error is -> $error"); + print("there is an error in fetching the data of my profile the error is -> $error"); } } - - Future getSavedPosts() async { + Future getSavedPosts({required int p,}) async { final prefs = await SharedPreferences.getInstance(); DioClient.init(prefs); try { - final response = await DioClient.get( - path: '/users/saved?page=${pageNumberSaved}&limit=20', + final response =await DioClient.get( + path:'/users/saved?page=${p}&limit=20', ); - if (response.statusCode == 200) { - for (var post in response.data['savedPosts']) { - PostModel temp = PostModel(); + if(response.statusCode==200) + { + for (var post in response.data['savedPosts']) + { + PostModel temp =PostModel(); await temp.fromJson(post); userSavedPosts.add(temp); } - } else { + } + else { await showToast(response.statusMessage.toString()); - errorSaved.value = true; + errorSaved.value=true; } - isLoadingSaved.value = false; + isLoadingSaved.value=false; } catch (error) { print("error in fetching history posts $error"); change([], status: RxStatus.error(error.toString())); } } - - Future getSavedComments() async { + Future getSavedComments({required int p}) async { final prefs = await SharedPreferences.getInstance(); DioClient.init(prefs); try { - final response = await DioClient.get( - path: '/users/saved?page=${pageNumberComment}&limit=20', + final response =await DioClient.get( + path:'/users/saved?page=${p}&limit=20', ); - if (response.statusCode == 200) { - for (var comment in response.data['savedComments']) { + if(response.statusCode==200) + { + for (var comment in response.data['savedComments']) + { userSavedComments.add(CommentModel.fromJson(comment)); } - } else { + } + else { await showToast(response.statusMessage.toString()); - errorComment.value = true; + errorComment.value=true; } - isLoadingCommetns.value = false; + isLoadingCommetns.value=false; } catch (error) { print("error in fetching saved comments $error"); change([], status: RxStatus.error(error.toString())); } } - - Future getHistory() async { + Future getHistory({required String sort,required int p}) async { final prefs = await SharedPreferences.getInstance(); DioClient.init(prefs); try { - final response = await DioClient.get( - path: '/users/${sortHistoryBy}?page=${pageNumberHistory}&limit=20', + final response =await DioClient.get( + path:'/users/${sort}?page=${p}&limit=20', ); - if (response.statusCode == 200) { - for (var post in response.data['posts']) { - PostModel temp = PostModel(); + if(response.statusCode==200) + { + for (var post in response.data['posts']) + { + PostModel temp =PostModel(); await temp.fromJson(post); historyPosts.add(temp); } - } else { + } + else { await showToast(response.statusMessage.toString()); - errorHistory.value = true; + errorHistory.value=true; } - isLoadingHistory.value = false; + isLoadingHistory.value=false; } catch (error) { print("error in fetching history posts $error"); change([], status: RxStatus.error(error.toString())); } } - - Future getPosts() async { - isLoading.value = true; - error.value = false; + Future getPosts({required String sort,required int p}) async { final prefs = await SharedPreferences.getInstance(); DioClient.init(prefs); try { - final response = await DioClient.get( - path: '/users/${sortHomePostsBy}?page=${pageNumberAll}&limit=20', + final response =await DioClient.get( + path:'/users/${sort}?page=${p}&limit=20', ); - if (response.statusCode == 200) { - for (var post in response.data['data']) { - PostModel temp = PostModel(); - await temp.fromJson(post); - homePosts.add(temp); + if(response.statusCode==200) + { + for (var post in response.data['data']) + { + PostModel temp =PostModel(); + await temp.fromJson(post); + homePosts.add(temp); + } + // response.data["data"].forEach((value1) async{ + // PostModel temp =PostModel(); + // await temp.fromJson(value1); + // homePosts.add(temp); + // }); } - // response.data["data"].forEach((value1) async{ - // PostModel temp =PostModel(); - // await temp.fromJson(value1); - // homePosts.add(temp); - // }); - } else { + else { await showToast(response.statusMessage.toString()); - error.value = true; + error.value=true; } - isLoading.value = false; + isLoading.value=false; print("the length of returned list in home is ${homePosts.length}"); print("${homePosts[0]}"); } catch (error) { @@ -199,41 +206,29 @@ class HomeController extends GetxController with StateMixin> { change([], status: RxStatus.error(error.toString())); } } - - Future getPostsInAll() async { + Future getPostsInAll({required String sort,required int p}) async { // isLoading.value=true; // error.value=false; final prefs = await SharedPreferences.getInstance(); DioClient.init(prefs); try { - final response = await DioClient.get( - path: '/users/${sortAllBy}?page=${pageNumber}&limit=20', + final response =await DioClient.get( + path:'/users/${sort}?page=${p}&limit=20', ); - // .then((value) { - // print(value); - // // List data = value.data.map((e) => PostModel.fromJson(value)); - // // change(data, status: RxStatus.success()); - // value.data["data"].forEach((value1){ - // homePosts.add(PostModel.fromJson(value1)); - // }); - // }); - - if (response.statusCode == 200) { - for (var post in response.data['data']) { - PostModel temp = PostModel(); - await temp.fromJson(post); - allPosts.add(temp); - } - // response.data["data"].forEach((value1) async{ - // PostModel temp =PostModel(); - // await temp.fromJson(value1); - // allPosts.add(temp); - // }); - } else { + if(response.statusCode==200) + { + for (var post in response.data['data']) + { + PostModel temp =PostModel(); + await temp.fromJson(post); + allPosts.add(temp); + } + } + else { await showToast(response.statusMessage.toString()); - error.value = true; + error.value=true; } - isLoading.value = false; + isLoading.value=false; print("the length of returned list in all is ${homePosts.length}"); print("${homePosts[0]}"); } catch (error) { @@ -241,7 +236,6 @@ class HomeController extends GetxController with StateMixin> { change([], status: RxStatus.error(error.toString())); } } - Future showToast(final String msg) async { await Fluttertoast.showToast( msg: msg, diff --git a/lib/home/models/flairs.dart b/lib/home/models/flairs.dart index 099dc044..e87a3b1d 100644 --- a/lib/home/models/flairs.dart +++ b/lib/home/models/flairs.dart @@ -1,32 +1,32 @@ -class FlairModel { - String? sId; - String? text; - String? backgroundColor; - String? textColor; - String? permissions; - - FlairModel( - {this.sId, - this.text, - this.backgroundColor, - this.textColor, - this.permissions}); - - FlairModel.fromJson(Map json) { - sId = json['_id']; - text = json['text']; - backgroundColor = json['backgroundColor']; - textColor = json['textColor']; - permissions = json['permissions']; - } - - Map toJson() { - final Map data = new Map(); - data['_id'] = this.sId; - data['text'] = this.text; - data['backgroundColor'] = this.backgroundColor; - data['textColor'] = this.textColor; - data['permissions'] = this.permissions; - return data; - } +class FlairModel { + String? sId; + String? text; + String? backgroundColor; + String? textColor; + String? permissions; + + FlairModel( + {this.sId, + this.text, + this.backgroundColor, + this.textColor, + this.permissions}); + + FlairModel.fromJson(Map json) { + sId = json['_id']; + text = json['text']; + backgroundColor = json['backgroundColor']; + textColor = json['textColor']; + permissions = json['permissions']; + } + + Map toJson() { + final Map data = new Map(); + data['_id'] = this.sId; + data['text'] = this.text; + data['backgroundColor'] = this.backgroundColor; + data['textColor'] = this.textColor; + data['permissions'] = this.permissions; + return data; + } } \ No newline at end of file diff --git a/lib/home/models/test_post_model.dart b/lib/home/models/test_post_model.dart index 55c0f9b0..535885bf 100644 --- a/lib/home/models/test_post_model.dart +++ b/lib/home/models/test_post_model.dart @@ -1,225 +1,225 @@ -class Data { - String? sId; - Author? author; - String? ownerType; - Owner? owner; - List? replies; - FlairId? flairId; - String? flairText; - String? sharedFrom; - String? title; - String? kind; - String? text; - List? images; - String? video; - String? createdAt; - bool? locked; - bool? isDeleted; - bool? sendReplies; - bool? nsfw; - bool? spoiler; - int? votes; - int? views; - int? commentCount; - int? shareCount; - String? suggestedSort; - bool? scheduled; - int? sortOnHot; - int? sortOnBest; - bool? isSpam; - String? url; - bool? isSaved; - int? postVoteStatus; - - Data( - {this.sId, - this.author, - this.ownerType, - this.owner, - this.replies, - this.flairId, - this.flairText, - this.sharedFrom, - this.title, - this.kind, - this.text, - this.images, - this.video, - this.createdAt, - this.locked, - this.isDeleted, - this.sendReplies, - this.nsfw, - this.spoiler, - this.votes, - this.views, - this.commentCount, - this.shareCount, - this.suggestedSort, - this.scheduled, - this.sortOnHot, - this.sortOnBest, - this.isSpam, - this.url, - this.isSaved, - this.postVoteStatus}); - - Data.fromJson(Map json) { - sId = json['_id']; - author = - json['author'] != null ? new Author.fromJson(json['author']) : null; - ownerType = json['ownerType']; - owner = json['owner'] != null ? new Owner.fromJson(json['owner']) : null; - replies = json['replies'].cast(); - flairId = - json['flairId'] != null ? new FlairId.fromJson(json['flairId']) : null; - flairText = json['flairText']; - sharedFrom = json['sharedFrom']; - title = json['title']; - kind = json['kind']; - text = json['text']; - // if (json['images'] != null) { - // images = []; - // json['images'].forEach((v) { - // images!.add(new Null.fromJson(v)); - // }); - // } - video = json['video']; - createdAt = json['createdAt']; - locked = json['locked']; - isDeleted = json['isDeleted']; - sendReplies = json['sendReplies']; - nsfw = json['nsfw']; - spoiler = json['spoiler']; - votes = json['votes']; - views = json['views']; - commentCount = json['commentCount']; - shareCount = json['shareCount']; - suggestedSort = json['suggestedSort']; - scheduled = json['scheduled']; - sortOnHot = json['sortOnHot']; - sortOnBest = json['sortOnBest']; - isSpam = json['isSpam']; - url = json['url']; - isSaved = json['isSaved']; - postVoteStatus = json['postVoteStatus']; - } - - Map toJson() { - final Map data = new Map(); - data['_id'] = this.sId; - if (this.author != null) { - data['author'] = this.author!.toJson(); - } - data['ownerType'] = this.ownerType; - if (this.owner != null) { - data['owner'] = this.owner!.toJson(); - } - data['replies'] = this.replies; - if (this.flairId != null) { - data['flairId'] = this.flairId!.toJson(); - } - data['flairText'] = this.flairText; - data['sharedFrom'] = this.sharedFrom; - data['title'] = this.title; - data['kind'] = this.kind; - data['text'] = this.text; - // if (this.images != null) { - // data['images'] = this.images!.map((v) => v.toJson()).toList(); - // } - data['video'] = this.video; - data['createdAt'] = this.createdAt; - data['locked'] = this.locked; - data['isDeleted'] = this.isDeleted; - data['sendReplies'] = this.sendReplies; - data['nsfw'] = this.nsfw; - data['spoiler'] = this.spoiler; - data['votes'] = this.votes; - data['views'] = this.views; - data['commentCount'] = this.commentCount; - data['shareCount'] = this.shareCount; - data['suggestedSort'] = this.suggestedSort; - data['scheduled'] = this.scheduled; - data['sortOnHot'] = this.sortOnHot; - data['sortOnBest'] = this.sortOnBest; - data['isSpam'] = this.isSpam; - data['url'] = this.url; - data['isSaved'] = this.isSaved; - data['postVoteStatus'] = this.postVoteStatus; - return data; - } -} - -class Author { - String? sId; - String? name; - - Author({this.sId, this.name}); - - Author.fromJson(Map json) { - sId = json['_id']; - name = json['name']; - } - - Map toJson() { - final Map data = new Map(); - data['_id'] = this.sId; - data['name'] = this.name; - return data; - } -} - -class Owner { - String? sId; - String? name; - String? icon; - - Owner({this.sId, this.name, this.icon}); - - Owner.fromJson(Map json) { - sId = json['_id']; - name = json['name']; - icon = json['icon']; - } - - Map toJson() { - final Map data = new Map(); - data['_id'] = this.sId; - data['name'] = this.name; - data['icon'] = this.icon; - return data; - } -} - -class FlairId { - String? sId; - String? text; - String? backgroundColor; - String? textColor; - String? permissions; - - FlairId( - {this.sId, - this.text, - this.backgroundColor, - this.textColor, - this.permissions}); - - FlairId.fromJson(Map json) { - sId = json['_id']; - text = json['text']; - backgroundColor = json['backgroundColor']; - textColor = json['textColor']; - permissions = json['permissions']; - } - - Map toJson() { - final Map data = new Map(); - data['_id'] = this.sId; - data['text'] = this.text; - data['backgroundColor'] = this.backgroundColor; - data['textColor'] = this.textColor; - data['permissions'] = this.permissions; - return data; - } +class Data { + String? sId; + Author? author; + String? ownerType; + Owner? owner; + List? replies; + FlairId? flairId; + String? flairText; + String? sharedFrom; + String? title; + String? kind; + String? text; + List? images; + String? video; + String? createdAt; + bool? locked; + bool? isDeleted; + bool? sendReplies; + bool? nsfw; + bool? spoiler; + int? votes; + int? views; + int? commentCount; + int? shareCount; + String? suggestedSort; + bool? scheduled; + int? sortOnHot; + int? sortOnBest; + bool? isSpam; + String? url; + bool? isSaved; + int? postVoteStatus; + + Data( + {this.sId, + this.author, + this.ownerType, + this.owner, + this.replies, + this.flairId, + this.flairText, + this.sharedFrom, + this.title, + this.kind, + this.text, + this.images, + this.video, + this.createdAt, + this.locked, + this.isDeleted, + this.sendReplies, + this.nsfw, + this.spoiler, + this.votes, + this.views, + this.commentCount, + this.shareCount, + this.suggestedSort, + this.scheduled, + this.sortOnHot, + this.sortOnBest, + this.isSpam, + this.url, + this.isSaved, + this.postVoteStatus}); + + Data.fromJson(Map json) { + sId = json['_id']; + author = + json['author'] != null ? new Author.fromJson(json['author']) : null; + ownerType = json['ownerType']; + owner = json['owner'] != null ? new Owner.fromJson(json['owner']) : null; + replies = json['replies'].cast(); + flairId = + json['flairId'] != null ? new FlairId.fromJson(json['flairId']) : null; + flairText = json['flairText']; + sharedFrom = json['sharedFrom']; + title = json['title']; + kind = json['kind']; + text = json['text']; + // if (json['images'] != null) { + // images = []; + // json['images'].forEach((v) { + // images!.add(new Null.fromJson(v)); + // }); + // } + video = json['video']; + createdAt = json['createdAt']; + locked = json['locked']; + isDeleted = json['isDeleted']; + sendReplies = json['sendReplies']; + nsfw = json['nsfw']; + spoiler = json['spoiler']; + votes = json['votes']; + views = json['views']; + commentCount = json['commentCount']; + shareCount = json['shareCount']; + suggestedSort = json['suggestedSort']; + scheduled = json['scheduled']; + sortOnHot = json['sortOnHot']; + sortOnBest = json['sortOnBest']; + isSpam = json['isSpam']; + url = json['url']; + isSaved = json['isSaved']; + postVoteStatus = json['postVoteStatus']; + } + + Map toJson() { + final Map data = new Map(); + data['_id'] = this.sId; + if (this.author != null) { + data['author'] = this.author!.toJson(); + } + data['ownerType'] = this.ownerType; + if (this.owner != null) { + data['owner'] = this.owner!.toJson(); + } + data['replies'] = this.replies; + if (this.flairId != null) { + data['flairId'] = this.flairId!.toJson(); + } + data['flairText'] = this.flairText; + data['sharedFrom'] = this.sharedFrom; + data['title'] = this.title; + data['kind'] = this.kind; + data['text'] = this.text; + // if (this.images != null) { + // data['images'] = this.images!.map((v) => v.toJson()).toList(); + // } + data['video'] = this.video; + data['createdAt'] = this.createdAt; + data['locked'] = this.locked; + data['isDeleted'] = this.isDeleted; + data['sendReplies'] = this.sendReplies; + data['nsfw'] = this.nsfw; + data['spoiler'] = this.spoiler; + data['votes'] = this.votes; + data['views'] = this.views; + data['commentCount'] = this.commentCount; + data['shareCount'] = this.shareCount; + data['suggestedSort'] = this.suggestedSort; + data['scheduled'] = this.scheduled; + data['sortOnHot'] = this.sortOnHot; + data['sortOnBest'] = this.sortOnBest; + data['isSpam'] = this.isSpam; + data['url'] = this.url; + data['isSaved'] = this.isSaved; + data['postVoteStatus'] = this.postVoteStatus; + return data; + } +} + +class Author { + String? sId; + String? name; + + Author({this.sId, this.name}); + + Author.fromJson(Map json) { + sId = json['_id']; + name = json['name']; + } + + Map toJson() { + final Map data = new Map(); + data['_id'] = this.sId; + data['name'] = this.name; + return data; + } +} + +class Owner { + String? sId; + String? name; + String? icon; + + Owner({this.sId, this.name, this.icon}); + + Owner.fromJson(Map json) { + sId = json['_id']; + name = json['name']; + icon = json['icon']; + } + + Map toJson() { + final Map data = new Map(); + data['_id'] = this.sId; + data['name'] = this.name; + data['icon'] = this.icon; + return data; + } +} + +class FlairId { + String? sId; + String? text; + String? backgroundColor; + String? textColor; + String? permissions; + + FlairId( + {this.sId, + this.text, + this.backgroundColor, + this.textColor, + this.permissions}); + + FlairId.fromJson(Map json) { + sId = json['_id']; + text = json['text']; + backgroundColor = json['backgroundColor']; + textColor = json['textColor']; + permissions = json['permissions']; + } + + Map toJson() { + final Map data = new Map(); + data['_id'] = this.sId; + data['text'] = this.text; + data['backgroundColor'] = this.backgroundColor; + data['textColor'] = this.textColor; + data['permissions'] = this.permissions; + return data; + } } \ No newline at end of file diff --git a/lib/home/screens/all.dart b/lib/home/screens/all.dart index 990a6eb4..f731bf42 100644 --- a/lib/home/screens/all.dart +++ b/lib/home/screens/all.dart @@ -1,305 +1,545 @@ -import 'package:flutter/material.dart'; -import 'package:get/get.dart'; -import 'package:post/home/widgets/buttom_nav_bar.dart'; -import 'package:responsive_sizer/responsive_sizer.dart'; -import '../../icons/icon_broken.dart'; -import '../../post/widgets/post_list.dart'; -import '../../widgets/loading_reddit.dart'; -import '../controller/home_controller.dart'; - -class All extends StatefulWidget { - const All({Key? key}) : super(key: key); - @override - State createState() => _AllState(); -} - -class _AllState extends State with TickerProviderStateMixin { - final HomeController controller = Get.put(HomeController()); - late AnimationController loadingSpinnerAnimationController; - @override - void initState() { - super.initState(); - loadingSpinnerAnimationController = - AnimationController(duration: const Duration(seconds: 2), vsync: this); - loadingSpinnerAnimationController.repeat(); - if (controller.allPosts.isEmpty) { - controller.getPostsInAll(); - } - } - - void dispose() { - loadingSpinnerAnimationController.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return Obx( - () => Scaffold( - bottomNavigationBar: const buttomNavBar( - fromProfile: 0, - icon: '', - nameOfSubreddit: '', - x: 1, - ), - backgroundColor: Colors.grey[300], - appBar: AppBar( - elevation: 1, - leading: IconButton( - icon: const Icon(IconBroken.Arrow___Left_2), - onPressed: () { - Get.back(); - }, - ), - title: const Text( - "All", - style: TextStyle(fontSize: 18, fontWeight: FontWeight.w700), - ), - ), - body: RefreshIndicator( - backgroundColor: Colors.white, - color: Colors.blue[900], - onRefresh: () async { - controller.allPosts.clear(); - controller.pageNumberAll.value = 1; - controller.pageNumber.update((val) {}); - controller.getPostsInAll(); - controller.update(); - }, - child: controller.isLoadingAll.value - ? Center( - child: CircularProgressIndicator( - valueColor: loadingSpinnerAnimationController.drive( - ColorTween( - //begin: Colors.blueAccent, - end: Colors.blueAccent, - ), - ), - ), - ) - : controller.error.value - ? ListView( - children: const [ - Padding( - padding: EdgeInsets.all(150), - child: Text("Unexpected Error Try Again.."), - ), - ], - ) - : controller.allPosts.isEmpty - ? LoadingReddit() - : PostList( - userName: '${controller.myProfile!.userName}', - updateData: () { - controller.pageNumberAll.value++; - controller.pageNumberAll.update((val) {}); - controller.getPostsInAll(); - }, - data: controller.allPosts, - topOfTheList: Padding( - padding: const EdgeInsetsDirectional.only( - start: 15, top: 5), - child: Row( - children: [ - Icon( - controller.allIcon.value, - color: Colors.grey, - size: 20, - ), - ElevatedButton.icon( - onPressed: () { - showModalBottomSheet( - backgroundColor: Colors.transparent, - context: context, - builder: (context) => GestureDetector( - onTap: () {}, - child: Container( - padding: - const EdgeInsets.all(30), - height: 30.h, - width: 30.w, - margin: const EdgeInsets.all(5), - decoration: BoxDecoration( - borderRadius: - BorderRadius.circular( - 5), - color: Colors.white), - child: Column( - mainAxisAlignment: - MainAxisAlignment.start, - crossAxisAlignment: - CrossAxisAlignment.start, - mainAxisSize: - MainAxisSize.min, - children: [ - const Text('SORT POSTS BY', - style: TextStyle( - color: Colors.grey, - )), - const Divider( - height: 5, - ), - ListTile( - onTap: () { - controller.sortAllBy - .value = "hot"; - controller - .allIcon.value = - Icons - .local_fire_department_rounded; - controller.sortHistoryBy - .update((val) {}); - Get.back(); - // Get.forceAppUpdate(); - }, - leading: Icon( - Icons - .local_fire_department_rounded, - color: (controller - .sortAllBy - .value == - "hot") - ? Colors.black - : Colors - .grey[500]), - title: Text( - "Hot", - style: TextStyle( - color: (controller - .sortAllBy - .value == - "hot") - ? Colors.black - : Colors - .grey[500]), - ), - trailing: (controller - .sortAllBy - .value == - "hot") - ? Icon( - Icons.done, - color: - Colors.green, - ) - : null, - ), - ListTile( - onTap: () { - controller.sortAllBy - .value = "new"; - controller - .allIcon.value = - Icons - .brightness_low; - controller.sortHistoryBy - .update((val) {}); - Get.back(); - // Get.forceAppUpdate(); - }, - leading: Icon( - Icons.brightness_low, - color: (controller - .sortAllBy - .value == - "new") - ? Colors.black - : Colors - .grey[500]), - title: Text( - "New", - style: TextStyle( - color: (controller - .sortAllBy - .value == - "new") - ? Colors.black - : Colors - .grey[500]), - ), - trailing: (controller - .sortAllBy - .value == - "new") - ? Icon( - Icons.done, - color: - Colors.green, - ) - : null, - ), - ListTile( - onTap: () { - controller.sortAllBy - .value = "top"; - controller - .allIcon.value = - Icons - .turn_sharp_right_sharp; - controller.sortHistoryBy - .update((val) {}); - Get.back(); - // Get.forceAppUpdate(); - }, - leading: Icon( - Icons - .turn_sharp_right_sharp, - color: (controller - .sortAllBy - .value == - "top") - ? Colors.black - : Colors - .grey[500]), - title: Text( - "Top", - style: TextStyle( - color: (controller - .sortAllBy - .value == - "top") - ? Colors.black - : Colors - .grey[500]), - ), - trailing: (controller - .sortAllBy - .value == - "top") - ? Icon( - Icons.done, - color: - Colors.green, - ) - : null, - ), - ], - ), - ), - )); - }, - icon: Text( - "${controller.sortAllBy.value.toUpperCase()}", - style: const TextStyle(color: Colors.grey), - ), - label: const Icon( - IconBroken.Arrow___Down_2, - color: Colors.grey, - size: 15, - ), - style: ButtonStyle( - elevation: - MaterialStateProperty.all(0), - backgroundColor: MaterialStateProperty.all( - Colors.grey[300]), - ), - ), - ], - ), - ), - ), - ), - ), - ); - } -} +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:fluttericon/typicons_icons.dart'; +import 'package:get/get.dart'; +import 'package:post/home/widgets/buttom_nav_bar.dart'; +import 'package:responsive_sizer/responsive_sizer.dart'; +import '../../create_community/screens/create_community.dart'; +import '../../createpost/controllers/posts_controllers.dart'; +import '../../createpost/screens/createpost.dart'; +import '../../icons/icon_broken.dart'; +import '../../post/widgets/post.dart'; +import '../../post/widgets/post_list.dart'; +import '../../widgets/loading_reddit.dart'; +import '../controller/home_controller.dart'; +import '../widgets/custom_upper_bar.dart'; +import 'home_layout.dart'; + +class All extends StatefulWidget { + const All({Key? key}) : super(key: key); + @override + State createState() => _AllState(); +} + +class _AllState extends State with TickerProviderStateMixin { + final HomeController controller = Get.put( + HomeController(), + ); + final PostController controllerForPost = Get.put( + PostController(), + ); + late AnimationController loadingSpinnerAnimationController; + @override + void initState() { + super.initState(); + loadingSpinnerAnimationController = + AnimationController(duration: const Duration(seconds: 2), vsync: this); + loadingSpinnerAnimationController.repeat(); + if (controller.allPosts.isEmpty) { + controller.getPostsInAll(sort: controller.sortAllBy.value,p: controller.pageNumberAll.value); + } + } + void dispose() { + loadingSpinnerAnimationController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + + double ScreenSizeWidth = MediaQuery.of(context).size.width; + + final isDesktop = ScreenSizeWidth >= 700; + final isMobile = ScreenSizeWidth < 700; + + if (isDesktop) { + return + Obx(()=> + Scaffold( + appBar: PreferredSize( + preferredSize: Size(700, 60), + child: UpBar(controller: controller, controllerForCreatePost: controllerForPost,)), + backgroundColor: const Color(0xA2D4E4FA), + body: + SingleChildScrollView( + scrollDirection: Axis.vertical, + child: Column( + children: [ + Row( + children: [ + Padding( + padding: EdgeInsetsDirectional.only(start: 200), + child: Column( + children: [ + SizedBox(height: 2,), + Container( + color: Colors.white, + height: 50, + width: 650, + child: Row( + children: [ + Padding( + padding: const EdgeInsets.all(8.0), + child: Container( + child: Image.asset('assets/images/1.png'), + height: 40, + width: 40, + ), + ), + Container( + width: 450.0, + height: 35.0, + color: const Color(0xA2F2F4F6), + child: TextFormField( + onTap: ()=> Get.to(CreatePostSCreen()), + enabled: false, + decoration: const InputDecoration( + border: InputBorder.none, + labelText: 'Create Post', + labelStyle: TextStyle( + color: Colors.grey, + fontSize: 18.0, + ), + ), + ), + ), + SizedBox(width: 10,), + IconButton( + icon:Icon(Icons.photo), + onPressed: () { + Get.to(CreatePostSCreen(), arguments: [ + 0, 0, + 0 + ]); + }, + + ), + const SizedBox( + width: 20, + ), + IconButton( + onPressed: () + { + Get.to(CreatePostSCreen(), arguments: [ + 0, 0, + 0 + ]); + }, + icon:Icon(Icons.insert_link)), + ], + ), + ), + SizedBox(height: 10,), + Container( + color: Colors.white, + height: 50, + width: 650, + child: Row( + children: [ + Padding( + padding: const EdgeInsets.all(8.0), + child: Container( + child: ElevatedButton.icon( + style: ElevatedButton.styleFrom(shape: StadiumBorder()), + onPressed: () { + controller.sortHomePostsBy.value="best"; + controller.sortHomePostsBy.update((val) { }); + }, + icon: Icon( // <-- Icon + Icons.add_alert_rounded, + size: 15.0, + ), + label: Text('Best'), // <-- Text + ), + height: 80, + width: 80, + ), + ), + + ElevatedButton.icon( + style: ElevatedButton.styleFrom(shape: StadiumBorder()), + onPressed: () { + controller.sortHomePostsBy.value="hot"; + controller.sortHomePostsBy.update((val) { }); + }, + icon: Icon( // <-- Icon + Icons.fireplace, + size: 15.0, + ), + label: Text('Hot'), // <-- Text + ), + SizedBox( + width: 10, + ), + ElevatedButton.icon( + style: ElevatedButton.styleFrom(shape: StadiumBorder(),shadowColor: Colors.blue), + onPressed: () { + controller.sortHomePostsBy.value="new"; + controller.sortHomePostsBy.update((val) { }); + }, + icon: Icon( // <-- Icon + Icons.new_releases_outlined, + size: 15.0, + ), + label: Text('New'), // <-- Text + ), + SizedBox( + width: 10, + ), + ElevatedButton.icon( + style: ElevatedButton.styleFrom(shape: StadiumBorder()), + onPressed: () { + controller.sortHomePostsBy.value="top"; + controller.sortHomePostsBy.update((val) { }); + }, + icon: Icon( // <-- Icon + Icons.topic, + size: 15.0, + ), + label: Text('Top'), // <-- Text + ), + SizedBox( + width: 50, + ), + + ], + ), + ), + ], + ), + ), + SizedBox(width:100 ,), + Container( + margin:EdgeInsetsDirectional.only(top: 100) , + height: 300, + width: 350, + color: Colors.white, + child: Column(mainAxisAlignment: MainAxisAlignment.center, + children: [ + Padding( + padding: const EdgeInsets.all(30), + child: Text("Your personal Reddit frontpage. Come here to check in with your favorite communities."), + ), + SizedBox(height: 50,), + Container( + width: 300, + child: ElevatedButton( + onPressed: () { + Get.to(CreatePostSCreen(),arguments: [0,0,0]); + }, + child: Text(' Create post ',style: TextStyle(color: Colors.white),), + style: ElevatedButton.styleFrom(shape: StadiumBorder(),backgroundColor: Colors.blue), + ), + ), + SizedBox(height: 10,), + Container( + width: 300, + child: OutlinedButton( + onPressed: () { + Get.to(CreateCommunity()); + }, + child: Text('Create Community',style: TextStyle(color: Colors.blue),), + style: OutlinedButton.styleFrom( + shape: StadiumBorder(), + side: BorderSide(width: 2.0, color: Colors.blue), + + ), + ), + ) + ],), + ) + ], + ), + RefreshIndicator( + backgroundColor: Colors.white, + color: Colors.blue[900], + onRefresh: () async { + controller.allPosts.clear(); + controller.pageNumberAll.value = 1; + controller.pageNumberAll.update((val) {}); + controller.getPostsInAll(sort: controller.sortAllBy.value,p: controller.pageNumberAll.value); + controller.update(); + }, + child: controller.isLoading.value + ? Center( + child: CircularProgressIndicator( + valueColor: loadingSpinnerAnimationController.drive( + ColorTween( + //begin: Colors.blueAccent, + end: Colors.blueAccent, + ), + ), + ), + ) + : controller.error.value + ? Text("Unexpected Error Try Again..") + : controller.homePosts.isEmpty + ? LoadingReddit() + //const Text( "No Posts to show") + : PostList( + leftMargin: 200.0, + rightMargin: 650.0, + userName: '${controller.myProfile!.userName}', + updateData: () + { + controller.pageNumberAll.value++; + controller.pageNumberAll.update((val) { }); + }, + data: controller.homePosts, + ), + ), + ], + ), + ), + floatingActionButtonLocation:FloatingActionButtonLocation.miniEndFloat , + floatingActionButton: Padding( + padding: const EdgeInsetsDirectional.only(end: 320.0), + child: ElevatedButton( + onPressed: () { + Get.to(HomeLayoutScreen()); + }, + child: Text(' Back to top ',style: TextStyle(color: Colors.white),), + style: ElevatedButton.styleFrom(shape: StadiumBorder(),backgroundColor: Colors.blue), + ), + ), + + + ), + ); + } + return + Obx(()=> Scaffold( + bottomNavigationBar: const buttomNavBar( + fromProfile: 0, + icon: '', + nameOfSubreddit: '', x: 1, + ), + backgroundColor: Colors.grey[300], + appBar: AppBar( + elevation: 1, + leading: IconButton( + icon: const Icon(IconBroken.Arrow___Left_2), + onPressed: () { + Get.back(); + }, + ), + title: const Text( + "All", + style: TextStyle(fontSize: 18, fontWeight: FontWeight.w700), + ), + ), + body: + RefreshIndicator( + backgroundColor: Colors.white, + color: Colors.blue[900], + onRefresh: () async { + controller.allPosts.clear(); + controller.pageNumberAll.value = 1; + controller.pageNumberAll.update((val) {}); + controller.getPostsInAll(sort: controller.sortAllBy.value, p: controller.pageNumberAll.value); + controller.update(); + }, + child: controller.isLoadingAll.value + ? Center( + child: CircularProgressIndicator( + valueColor: loadingSpinnerAnimationController.drive( + ColorTween( + //begin: Colors.blueAccent, + end: Colors.blueAccent, + ), + ), + ), + ) + : controller.error.value + ? ListView( + children: const [ + Padding( + padding: EdgeInsets.all(150), + child: Text("Unexpected Error Try Again.."), + ), + ], + ) + : controller.allPosts.isEmpty + ? LoadingReddit() + : + PostList( + userName: '${controller.myProfile!.userName}', updateData: (){ + controller.pageNumberAll.value++; + controller.pageNumberAll.update((val) { }); + controller.getPostsInAll(sort: controller.sortAllBy.value,p: controller.pageNumberAll.value); + }, data: controller.allPosts, + topOfTheList: + Padding( + padding: const EdgeInsetsDirectional.only(start: 15, top: 5), + child: Row( + children: [ + Icon( + controller.allIcon.value, + color: Colors.grey, + size: 20, + ), + ElevatedButton.icon( + onPressed: () { + showModalBottomSheet( + backgroundColor: Colors.transparent, + context: context, + builder: (context) => GestureDetector( + onTap: () {}, + child: Container( + padding: const EdgeInsets.all(30), + height: 30.h, + width: 30.w, + margin: const EdgeInsets.all(5), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(5), + color: Colors.white), + child: Column( + mainAxisAlignment: + MainAxisAlignment.start, + crossAxisAlignment: + CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + const Text('SORT POSTS BY', + style: TextStyle( + color: Colors.grey, + )), + const Divider( + height: 5, + ), + ListTile( + onTap: () { + controller.sortAllBy.value = "hot"; + controller.allIcon.value = Icons + .local_fire_department_rounded; + controller.sortHistoryBy.update((val) { }); + Get.back(); + // Get.forceAppUpdate(); + + }, + leading: Icon( + Icons + .local_fire_department_rounded, + color: + (controller.sortAllBy.value == + "hot") + ? Colors.black + : Colors.grey[500]), + title: Text( + "Hot", + style: TextStyle( + color: (controller + .sortAllBy.value == + "hot") + ? Colors.black + : Colors.grey[500]), + ), + trailing: + (controller.sortAllBy.value == + "hot") + ? Icon( + Icons.done, + color: Colors.green, + ) + : null, + ), + ListTile( + onTap: () { + controller.sortAllBy.value = "new"; + controller.allIcon.value = + Icons.brightness_low; + controller.sortHistoryBy.update((val) { }); + Get.back(); + // Get.forceAppUpdate(); + + }, + leading: Icon(Icons.brightness_low, + color: + (controller.sortAllBy.value == + "new") + ? Colors.black + : Colors.grey[500]), + title: Text( + "New", + style: TextStyle( + color: (controller + .sortAllBy.value == + "new") + ? Colors.black + : Colors.grey[500]), + ), + trailing: + (controller.sortAllBy.value == + "new") + ? Icon( + Icons.done, + color: Colors.green, + ) + : null, + ), + ListTile( + onTap: () { + controller.sortAllBy.value = "top"; + controller.allIcon.value = + Icons.turn_sharp_right_sharp; + controller.sortHistoryBy.update((val) { }); + Get.back(); + // Get.forceAppUpdate(); + + }, + leading: Icon( + Icons.turn_sharp_right_sharp, + color: + (controller.sortAllBy.value == + "top") + ? Colors.black + : Colors.grey[500]), + title: Text( + "Top", + style: TextStyle( + color: (controller + .sortAllBy.value == + "top") + ? Colors.black + : Colors.grey[500]), + ), + trailing: + (controller.sortAllBy.value == + "top") + ? Icon( + Icons.done, + color: Colors.green, + ) + : null, + ), + ], + ), + ), + )); + }, + icon: Text( + "${controller.sortAllBy.value.toUpperCase()}", + style: const TextStyle(color: Colors.grey), + ), + label: const Icon( + IconBroken.Arrow___Down_2, + color: Colors.grey, + size: 15, + ), + style: ButtonStyle( + elevation: MaterialStateProperty.all(0), + backgroundColor: + MaterialStateProperty.all(Colors.grey[300]), + ), + ), + ], + ), + ), + ), + ), + + + ), + ); + } +} diff --git a/lib/home/screens/history.dart b/lib/home/screens/history.dart index e031c7d6..0da51c6f 100644 --- a/lib/home/screens/history.dart +++ b/lib/home/screens/history.dart @@ -1,231 +1,240 @@ -import 'package:flutter/material.dart'; -import 'package:fluttericon/typicons_icons.dart'; -import 'package:get/get.dart'; -import 'package:post/home/controller/home_controller.dart'; -import 'package:post/icons/icon_broken.dart'; -import 'package:responsive_sizer/responsive_sizer.dart'; -import '../../post/widgets/post_list.dart'; -import '../../widgets/loading_reddit.dart'; -class History extends StatefulWidget { - History({Key? key}) : super(key: key); - - @override - State createState() => _HistoryState(); -} - - -class _HistoryState extends Statewith TickerProviderStateMixin { - final HomeController controller = Get.put( - HomeController(), - ); - late AnimationController loadingSpinnerAnimationController; - @override - void initState() { - super.initState(); - loadingSpinnerAnimationController = - AnimationController(duration: const Duration(seconds: 2), vsync: this); - loadingSpinnerAnimationController.repeat(); - if (controller.allPosts.isEmpty) { - controller.getHistory(); - } - } - void dispose() { - loadingSpinnerAnimationController.dispose(); - super.dispose(); - } - @override - Widget build(BuildContext context) { - return Obx(()=> - Scaffold( - backgroundColor: Colors.grey[300], - appBar: AppBar( - elevation: 1, - leading: IconButton( - icon:const Icon( IconBroken.Arrow___Left_2), - onPressed: () - { - Get.back(); - }, - ), - title: const Text("History", - style: TextStyle( - fontSize: 18, - fontWeight: FontWeight.w700 - ), - ), - actions: [ - PopupMenuButton( - onSelected: (value){}, - itemBuilder: (BuildContext context) { - return {'Clear History'}.map((String choice) { - return PopupMenuItem( - value: choice, - child: ListTile( - leading: Icon(Icons.close_outlined), - title: Text(choice), - ), - ); - }).toList(); - }, - ), - ], - ), - body: RefreshIndicator( - backgroundColor: Colors.white, - color: Colors.blue[900], - onRefresh: () async { - controller.historyPosts.clear(); - controller.pageNumberHistory.value = 1; - controller.pageNumberHistory.update((val) {}); - controller.getHistory(); - controller.update(); - //Get.forceAppUpdate(); - }, - child: controller.isLoadingHistory.value - ? Center( - child: CircularProgressIndicator( - valueColor: loadingSpinnerAnimationController.drive( - ColorTween( - //begin: Colors.blueAccent, - end: Colors.blueAccent, - ), - ), - ), - ) - : controller.errorHistory.value - ? ListView( - children: const [ - Padding( - padding: EdgeInsets.all(150), - child: Text("Unexpected Error Try Again.."), - ), - ], - ) - : controller.historyPosts.isEmpty - ? LoadingReddit() - : - PostList( - userName: '${controller.myProfile!.userName}', updateData: (){ - controller.pageNumberHistory.value++; - controller.pageNumberHistory.update((val) { }); - controller.getHistory(); - }, data: controller.historyPosts, - topOfTheList: - Padding( - padding: const EdgeInsetsDirectional.only(start: 15,top: 5), - child: Row( - children: [ - const Icon(Icons.access_time_outlined,color: Colors.grey,size: 20,), - ElevatedButton.icon( - onPressed: () { - showModalBottomSheet( - backgroundColor: Colors.transparent, - context: context, - builder: (context) => GestureDetector( - onTap: () {}, - child: Container( - padding: const EdgeInsets.all(30), - height: 30.h, - width: 30.w, - margin: const EdgeInsets.all(5), - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(5), color: Colors.white), - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - const Text('SORT HISTORY BY', - style: TextStyle( - color: Colors.grey, - )), - const Divider(height: 5,), - ListTile( - onTap: () - { - controller.sortHistoryBy.value="upvoted"; - controller.sortHistoryBy.update((val) { }); - Get.back(); - - }, - leading: Icon( - Typicons.up_outline, - color: (controller.sortHistoryBy.value=="upvoted")?Colors.black:Colors.grey[500] - ) , - title: Text( - "Upvoted", - style: TextStyle( - color: (controller.sortHistoryBy.value=="upvoted")?Colors.black:Colors.grey[500] - ), - ) , - trailing:(controller.sortHistoryBy.value=="upvoted")? Icon(Icons.done,color: Colors.green,):null , - ), - ListTile( - onTap: () - { - controller.sortHistoryBy.value="downvoted"; - controller.sortHistoryBy.update((val) { }); - Get.back(); - }, - leading: Icon( - Typicons.down_outline, - color: (controller.sortHistoryBy.value=="downvoted")?Colors.black:Colors.grey[500] - ) , - title: Text( - "Downvoted", - style: TextStyle( - color: (controller.sortHistoryBy.value=="downvoted")?Colors.black:Colors.grey[500] - ), - ) , - trailing:(controller.sortHistoryBy.value=="downvoted")? Icon(Icons.done,color: Colors.green,):null , - ), - ListTile( - onTap: () - { - controller.sortHistoryBy.value="hidden"; - controller.sortHistoryBy.update((val) { }); - Get.back(); - }, - leading: Icon( - Icons.visibility_off, - color: (controller.sortHistoryBy.value=="hidden")?Colors.black:Colors.grey[500] - ) , - title: Text( - "Hidden", - style: TextStyle( - color: (controller.sortHistoryBy.value=="hidden")?Colors.black:Colors.grey[500] - ), - ) , - trailing:(controller.sortHistoryBy.value=="hidden")? Icon(Icons.done,color: Colors.green,):null , - ), - ], - ), - ), - ) - ); - }, - icon: Text( - "${controller.sortHistoryBy.value.toUpperCase()}", - style: const TextStyle(color: Colors.grey), - ), - label: const Icon( - IconBroken.Arrow___Down_2, - color: Colors.grey, - size: 15, - ), - style: ButtonStyle( - elevation: MaterialStateProperty.all(0), - backgroundColor: - MaterialStateProperty.all(Colors.grey[300]), - ), - ), - ], - ), - ), - ), - ) , - ), - - ); - } -} +import 'package:flutter/cupertino.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/material.dart'; +import 'package:fluttericon/typicons_icons.dart'; +import 'package:get/get.dart'; +import 'package:get/get_core/src/get_main.dart'; +import 'package:post/home/controller/home_controller.dart'; +import 'package:post/icons/icon_broken.dart'; +import 'package:responsive_sizer/responsive_sizer.dart'; +import '../../post/widgets/post_list.dart'; +import '../../widgets/loading_reddit.dart'; +import '../controller/home_controller.dart'; +class History extends StatefulWidget { + History({Key? key}) : super(key: key); + + @override + State createState() => _HistoryState(); +} + + +class _HistoryState extends Statewith TickerProviderStateMixin { + final HomeController controller = Get.put( + HomeController(), + ); + late AnimationController loadingSpinnerAnimationController; + @override + void initState() { + super.initState(); + loadingSpinnerAnimationController = + AnimationController(duration: const Duration(seconds: 2), vsync: this); + loadingSpinnerAnimationController.repeat(); + if (controller.allPosts.isEmpty) { + controller.getHistory(sort: controller.sortHistoryBy.value, p: controller.pageNumberHistory.value,); + } + } + void dispose() { + loadingSpinnerAnimationController.dispose(); + super.dispose(); + } + @override + Widget build(BuildContext context) { + return Obx(()=> + Scaffold( + backgroundColor: Colors.grey[300], + appBar: AppBar( + elevation: 1, + leading: IconButton( + icon:const Icon( IconBroken.Arrow___Left_2), + onPressed: () + { + Get.back(); + }, + ), + title: const Text("History", + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.w700 + ), + ), + actions: [ + PopupMenuButton( + onSelected: (value){}, + itemBuilder: (BuildContext context) { + return {'Clear History'}.map((String choice) { + return PopupMenuItem( + value: choice, + child: ListTile( + onTap: () + { + controller.historyPosts.clear(); + }, + leading: Icon(Icons.close_outlined), + title: Text(choice), + ), + ); + }).toList(); + }, + ), + ], + ), + body: RefreshIndicator( + backgroundColor: Colors.white, + color: Colors.blue[900], + onRefresh: () async { + controller.historyPosts.clear(); + controller.pageNumberHistory.value = 1; + controller.pageNumberHistory.update((val) {}); + controller.getHistory(sort: controller.sortHistoryBy.value, p: controller.pageNumberHistory.value,); + controller.update(); + //Get.forceAppUpdate(); + }, + child: controller.isLoadingHistory.value + ? Center( + child: CircularProgressIndicator( + valueColor: loadingSpinnerAnimationController.drive( + ColorTween( + //begin: Colors.blueAccent, + end: Colors.blueAccent, + ), + ), + ), + ) + : controller.errorHistory.value + ? ListView( + children: const [ + Padding( + padding: EdgeInsets.all(150), + child: Text("Unexpected Error Try Again.."), + ), + ], + ) + : controller.historyPosts.isEmpty + ? LoadingReddit() + : + PostList( + userName: '${controller.myProfile!.userName}', updateData: (){ + controller.pageNumberHistory.value++; + controller.pageNumberHistory.update((val) { }); + controller.getHistory(sort: controller.sortHistoryBy.value, p: controller.pageNumberHistory.value,); + }, data: controller.historyPosts, + topOfTheList: + Padding( + padding: const EdgeInsetsDirectional.only(start: 15,top: 5), + child: Row( + children: [ + const Icon(Icons.access_time_outlined,color: Colors.grey,size: 20,), + ElevatedButton.icon( + onPressed: () { + showModalBottomSheet( + backgroundColor: Colors.transparent, + context: context, + builder: (context) => GestureDetector( + onTap: () {}, + child: Container( + padding: const EdgeInsets.all(30), + height: 30.h, + width: 30.w, + margin: const EdgeInsets.all(5), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(5), color: Colors.white), + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + const Text('SORT HISTORY BY', + style: TextStyle( + color: Colors.grey, + )), + const Divider(height: 5,), + ListTile( + onTap: () + { + controller.sortHistoryBy.value="upvoted"; + controller.sortHistoryBy.update((val) { }); + Get.back(); + + }, + leading: Icon( + Typicons.up_outline, + color: (controller.sortHistoryBy.value=="upvoted")?Colors.black:Colors.grey[500] + ) , + title: Text( + "Upvoted", + style: TextStyle( + color: (controller.sortHistoryBy.value=="upvoted")?Colors.black:Colors.grey[500] + ), + ) , + trailing:(controller.sortHistoryBy.value=="upvoted")? Icon(Icons.done,color: Colors.green,):null , + ), + ListTile( + onTap: () + { + controller.sortHistoryBy.value="downvoted"; + controller.sortHistoryBy.update((val) { }); + Get.back(); + }, + leading: Icon( + Typicons.down_outline, + color: (controller.sortHistoryBy.value=="downvoted")?Colors.black:Colors.grey[500] + ) , + title: Text( + "Downvoted", + style: TextStyle( + color: (controller.sortHistoryBy.value=="downvoted")?Colors.black:Colors.grey[500] + ), + ) , + trailing:(controller.sortHistoryBy.value=="downvoted")? Icon(Icons.done,color: Colors.green,):null , + ), + ListTile( + onTap: () + { + controller.sortHistoryBy.value="hidden"; + controller.sortHistoryBy.update((val) { }); + Get.back(); + }, + leading: Icon( + Icons.visibility_off, + color: (controller.sortHistoryBy.value=="hidden")?Colors.black:Colors.grey[500] + ) , + title: Text( + "Hidden", + style: TextStyle( + color: (controller.sortHistoryBy.value=="hidden")?Colors.black:Colors.grey[500] + ), + ) , + trailing:(controller.sortHistoryBy.value=="hidden")? Icon(Icons.done,color: Colors.green,):null , + ), + ], + ), + ), + ) + ); + }, + icon: Text( + "${controller.sortHistoryBy.value.toUpperCase()}", + style: const TextStyle(color: Colors.grey), + ), + label: const Icon( + IconBroken.Arrow___Down_2, + color: Colors.grey, + size: 15, + ), + style: ButtonStyle( + elevation: MaterialStateProperty.all(0), + backgroundColor: + MaterialStateProperty.all(Colors.grey[300]), + ), + ), + ], + ), + ), + ), + ) , + ), + + ); + } +} diff --git a/lib/home/screens/home_layout.dart b/lib/home/screens/home_layout.dart index 06271396..3d3e6142 100644 --- a/lib/home/screens/home_layout.dart +++ b/lib/home/screens/home_layout.dart @@ -1,16 +1,24 @@ +import 'package:custom_refresh_indicator/custom_refresh_indicator.dart'; import 'package:flutter/services.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; +import 'package:post/create_community/screens/create_community.dart'; import 'package:post/createpost/controllers/posts_controllers.dart'; +import 'package:post/createpost/screens/createpost.dart'; import 'package:post/home/controller/home_controller.dart'; +import 'package:post/home/widgets/community_container.dart'; import 'package:post/home/widgets/new_drawer.dart'; +import 'package:post/post/models/post_model.dart'; import 'package:post/widgets/loading_reddit.dart'; import '../../icons/icon_broken.dart'; +import '../../post/widgets/post.dart'; import '../../post/widgets/post_list.dart'; import '../widgets/buttom_nav_bar.dart'; +import '../widgets/custom_upper_bar.dart'; import '../widgets/end_drawer.dart'; +import '../controller/home_controller.dart'; +import '../../createpost/controllers/posts_controllers.dart'; import '../widgets/recently_visited_list.dart'; -import '../../search/screens/search.dart'; class HomeLayoutScreen extends StatefulWidget { static const routeName = '/homepage'; @@ -69,7 +77,7 @@ class _HomeLayoutScreenState extends State AnimationController(duration: const Duration(seconds: 2), vsync: this); loadingSpinnerAnimationController.repeat(); if (controller.homePosts.isEmpty) { - controller.getPosts(); + controller.getPosts(sort: controller.sortHomePostsBy.value,p: controller.pageNumber.value); } } @@ -78,32 +86,286 @@ class _HomeLayoutScreenState extends State super.dispose(); } - void updateData() { - controller.pageNumber.value++; - controller.getPosts(); - } + // for dropdown list - String dropValue = "Home"; - // Lists for DropDown Menu at appBar List dropdownItems = [ DropdownMenuItem( child: ButtonBar( - children: [Text("Home")], - )), + children: [Text("Home")], + )), DropdownMenuItem( child: ButtonBar( - children: [Text("Popular")], - )) + children: [Text("Popular")], + )) ]; @override Widget build(BuildContext context) { + double ScreenSizeWidth = MediaQuery.of(context).size.width; + + final isDesktop = ScreenSizeWidth >= 700; + final isMobile = ScreenSizeWidth < 700; + + if (isDesktop) { + return + Obx(()=> + Scaffold( + appBar: PreferredSize( + preferredSize: Size(700, 60), + child: UpBar(controller: controller, controllerForCreatePost: controllerForPost,)), + backgroundColor: const Color(0xA2D4E4FA), + body:SingleChildScrollView( + scrollDirection: Axis.vertical, + child: Column( + children: [ + Row( + children: [ + Padding( + padding: EdgeInsetsDirectional.only(start: 200), + child: Column( + children: [ + SizedBox(height: 2,), + Container( + color: Colors.white, + height: 50, + width: 650, + child: Row( + children: [ + Padding( + padding: const EdgeInsets.all(8.0), + child: Container( + child: Image.asset('assets/images/1.png'), + height: 40, + width: 40, + ), + ), + Container( + width: 450.0, + height: 35.0, + color: const Color(0xA2F2F4F6), + child: TextFormField( + onTap: ()=> Get.to(CreatePostSCreen()), + enabled: false, + decoration: const InputDecoration( + border: InputBorder.none, + labelText: 'Create Post', + labelStyle: TextStyle( + color: Colors.grey, + fontSize: 18.0, + ), + ), + ), + ), + SizedBox(width: 10,), + IconButton( + icon:Icon(Icons.photo), + onPressed: () { + Get.to(CreatePostSCreen(), arguments: [ + 0, 0, + 0 + ]); + }, + + ), + const SizedBox( + width: 20, + ), + IconButton( + onPressed: () + { + Get.to(CreatePostSCreen(), arguments: [ + 0, 0, + 0 + ]); + }, + icon:Icon(Icons.insert_link)), + ], + ), + ), + SizedBox(height: 10,), + Container( + color: Colors.white, + height: 50, + width: 650, + child: Row( + children: [ + Padding( + padding: const EdgeInsets.all(8.0), + child: Container( + child: ElevatedButton.icon( + style: ElevatedButton.styleFrom(shape: StadiumBorder()), + onPressed: () { + controller.sortHomePostsBy.value="best"; + controller.sortHomePostsBy.update((val) { }); + }, + icon: Icon( // <-- Icon + Icons.add_alert_rounded, + size: 15.0, + ), + label: Text('Best'), // <-- Text + ), + height: 80, + width: 80, + ), + ), + + ElevatedButton.icon( + style: ElevatedButton.styleFrom(shape: StadiumBorder()), + onPressed: () { + controller.sortHomePostsBy.value="hot"; + controller.sortHomePostsBy.update((val) { }); + }, + icon: Icon( // <-- Icon + Icons.fireplace, + size: 15.0, + ), + label: Text('Hot'), // <-- Text + ), + SizedBox( + width: 10, + ), + ElevatedButton.icon( + style: ElevatedButton.styleFrom(shape: StadiumBorder(),shadowColor: Colors.blue), + onPressed: () { + controller.sortHomePostsBy.value="new"; + controller.sortHomePostsBy.update((val) { }); + }, + icon: Icon( // <-- Icon + Icons.new_releases_outlined, + size: 15.0, + ), + label: Text('New'), // <-- Text + ), + SizedBox( + width: 10, + ), + ElevatedButton.icon( + style: ElevatedButton.styleFrom(shape: StadiumBorder()), + onPressed: () { + controller.sortHomePostsBy.value="top"; + controller.sortHomePostsBy.update((val) { }); + }, + icon: Icon( // <-- Icon + Icons.topic, + size: 15.0, + ), + label: Text('Top'), // <-- Text + ), + SizedBox( + width: 50, + ), + + ], + ), + ), + ], + ), + ), + SizedBox(width:100 ,), + Container( + margin:EdgeInsetsDirectional.only(top: 100) , + height: 300, + width: 350, + color: Colors.white, + child: Column(mainAxisAlignment: MainAxisAlignment.center, + children: [ + Padding( + padding: const EdgeInsets.all(30), + child: Text("Your personal Reddit frontpage. Come here to check in with your favorite communities."), + ), + SizedBox(height: 50,), + Container( + width: 300, + child: ElevatedButton( + onPressed: () { + Get.to(CreatePostSCreen(),arguments: [0,0,0]); + }, + child: Text(' Create post ',style: TextStyle(color: Colors.white),), + style: ElevatedButton.styleFrom(shape: StadiumBorder(),backgroundColor: Colors.blue), + ), + ), + SizedBox(height: 10,), + Container( + width: 300, + child: OutlinedButton( + onPressed: () { + Get.to(CreateCommunity()); + }, + child: Text('Create Community',style: TextStyle(color: Colors.blue),), + style: OutlinedButton.styleFrom( + shape: StadiumBorder(), + side: BorderSide(width: 2.0, color: Colors.blue), + + ), + ), + ) + ],), + ) + ], + ), + RefreshIndicator( + backgroundColor: Colors.white, + color: Colors.blue[900], + onRefresh: () async { + controller.homePosts.clear(); + controller.pageNumber.value = 1; + controller.pageNumber.update((val) {}); + controller.getPosts(sort: controller.sortHomePostsBy.value,p: controller.pageNumber.value); + controller.update(); + }, + child: controller.isLoading.value + ? Center( + child: CircularProgressIndicator( + valueColor: loadingSpinnerAnimationController.drive( + ColorTween( + //begin: Colors.blueAccent, + end: Colors.blueAccent, + ), + ), + ), + ) + : controller.error.value + ? Text("Unexpected Error Try Again..") + : controller.homePosts.isEmpty + ? LoadingReddit() + //const Text( "No Posts to show") + : PostList( + leftMargin: 200.0, + rightMargin: 650.0, + userName: '${controller.myProfile!.userName}', + updateData: () + { + controller.pageNumber.value++; + controller.pageNumber.update((val) { }); + }, + data: controller.homePosts, + ), + ), + ], + ), + ), + floatingActionButtonLocation:FloatingActionButtonLocation.miniEndFloat , + floatingActionButton: Padding( + padding: const EdgeInsetsDirectional.only(end: 320.0), + child: ElevatedButton( + onPressed: () { + Get.to(HomeLayoutScreen()); + }, + child: Text(' Back to top ',style: TextStyle(color: Colors.white),), + style: ElevatedButton.styleFrom(shape: StadiumBorder(),backgroundColor: Colors.blue), + ), + ), + + + ), + ); + } return Obx( - () => Scaffold( + () => Scaffold( appBar: AppBar( - // To make style for status bar - systemOverlayStyle: SystemUiOverlayStyle( + // To make style for status bar + systemOverlayStyle: const SystemUiOverlayStyle( statusBarColor: Colors.white, statusBarIconBrightness: Brightness.dark, ), @@ -131,14 +393,14 @@ class _HomeLayoutScreenState extends State "modsub length ${controllerForPost.moderatedSubreddits.length}"); print("name of ${controllerForPost.moderatedSubreddits[0]}"); }, - icon: Text( + icon: const Text( "Home", style: TextStyle( color: Colors.black, fontSize: 17.0, fontWeight: FontWeight.w600), ), - label: Icon( + label: const Icon( IconBroken.Arrow___Down_2, color: Colors.black, ), @@ -150,9 +412,7 @@ class _HomeLayoutScreenState extends State ), actions: [ IconButton( - onPressed: () { - Navigator.pushNamed(context, Search.routeName); - }, + onPressed: () {}, icon: Icon( IconBroken.Search, color: Colors.black87, @@ -198,13 +458,13 @@ class _HomeLayoutScreenState extends State controller: controller, ), drawer: //RecentlyVisitedDrawer(controller: this.controller,), - (controller.isRecentlyVisitedDrawer.value == true) - ? RecentlyVisitedDrawer( - controller: this.controller, - ) - : MyDrawer( - controller: this.controller, - controllerForCreatePost: this.controllerForPost), + (controller.isRecentlyVisitedDrawer.value == true) + ? RecentlyVisitedDrawer( + controller: this.controller, + ) + : MyDrawer( + controller: this.controller, + controllerForCreatePost: this.controllerForPost), body: RefreshIndicator( backgroundColor: Colors.white, color: Colors.blue[900], @@ -212,50 +472,41 @@ class _HomeLayoutScreenState extends State controller.homePosts.clear(); controller.pageNumber.value = 1; controller.pageNumber.update((val) {}); - controller.getPosts(); + controller.getPosts(sort: controller.sortHomePostsBy.value,p: controller.pageNumber.value); controller.update(); }, child: controller.isLoading.value ? Center( - child: CircularProgressIndicator( - valueColor: loadingSpinnerAnimationController.drive( - ColorTween( - //begin: Colors.blueAccent, - end: Colors.blueAccent, - ), - ), - ), - ) + child: CircularProgressIndicator( + valueColor: loadingSpinnerAnimationController.drive( + ColorTween( + //begin: Colors.blueAccent, + end: Colors.blueAccent, + ), + ), + ), + ) : controller.error.value - ? ListView( - children: const [ - Padding( - padding: EdgeInsets.all(150), - child: Text("Unexpected Error Try Again.."), - ), - ], - ) - : controller.homePosts.isEmpty - ? LoadingReddit() - //const Text( "No Posts to show") - : PostList( - userName: '${controller.myProfile!.userName}', - updateData: updateData, - data: controller.homePosts, - ), - - // : ListView.builder( - // controller: controller.myScroll, - // itemCount: controller.homePosts.length, - // itemBuilder: ( - // final BuildContext ctx, - // final int index, - // ) { - // return Post.home( - // data: controller.homePosts[index], - // ); - // }, - // ), + ? ListView( + children: const [ + Padding( + padding: EdgeInsets.all(150), + child: Text("Unexpected Error Try Again.."), + ), + ], + ) + : controller.homePosts.isEmpty + ? LoadingReddit() + //const Text( "No Posts to show") + : PostList( + userName: '${controller.myProfile!.userName}', + updateData: () + { + controller.pageNumber.value++; + controller.getPosts(sort: controller.sortHomePostsBy.value,p: controller.pageNumber.value); + }, + data: controller.homePosts, + ), ), ), ); diff --git a/lib/home/screens/saved.dart b/lib/home/screens/saved.dart index 79a7b4ae..00ee1f90 100644 --- a/lib/home/screens/saved.dart +++ b/lib/home/screens/saved.dart @@ -1,67 +1,71 @@ -import 'package:flutter/material.dart'; -import 'package:post/home/screens/saved_comments.dart'; -import 'package:post/home/screens/saved_posts.dart'; - -class Saved extends StatelessWidget { - const Saved({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - late TabController controller; - return DefaultTabController( - length: 2, - child: Scaffold( - appBar: PreferredSize( - preferredSize: const Size.fromHeight(100), - child: NestedScrollView( - headerSliverBuilder: - (BuildContext context, bool innerBoxIsScrolled) => [ - SliverAppBar( - backgroundColor: Colors.white, - titleSpacing: 0, - elevation: 2, - titleTextStyle: const TextStyle( - color: Colors.black, - fontWeight: FontWeight.w500, - fontSize: 18), - shadowColor: Colors.white, - title: const Text('Saved'), - bottom: PreferredSize( - preferredSize: const Size.fromHeight(40), - child: TabBar( - indicatorWeight: 3, - indicatorColor: Colors.blue, - indicatorSize: TabBarIndicatorSize.tab, - labelPadding: const EdgeInsets.symmetric(horizontal: 60), - isScrollable: true, - labelStyle: const TextStyle( - fontWeight: FontWeight.w700, fontSize: 14), - labelColor: Colors.black, - unselectedLabelColor: Colors.grey, - tabs: [ - Text( - 'Posts', - style: TextStyle(fontSize: 15), - ), - const Padding( - padding: EdgeInsets.only(bottom: 6), - child: Text( - 'Comments', - ), - ), - ], - ), - ), - ), - ], - body: SizedBox(), - ), - ), - body: TabBarView(children: [ - SavedPosts(), - SavedCommentView(), - ]), - ), - ); - } -} +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:post/home/screens/saved_comments.dart'; +import 'package:post/home/screens/saved_posts.dart'; + + +class Saved extends StatelessWidget { + const Saved({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + late TabController controller; + return + DefaultTabController( + length: 2, + child: Scaffold( + appBar: PreferredSize( + preferredSize: const Size.fromHeight(100), + child: NestedScrollView( + headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) =>[ + SliverAppBar( + backgroundColor: Colors.white, + titleSpacing: 0, + elevation: 2, + titleTextStyle: const TextStyle( + color: Colors.black, + fontWeight: FontWeight.w500, + fontSize: 18), + shadowColor: Colors.white, + title: const Text('Saved'), + bottom: PreferredSize( + preferredSize: const Size.fromHeight(40), + child: TabBar( + indicatorWeight: 3, + indicatorColor: Colors.blue, + indicatorSize: TabBarIndicatorSize.tab, + labelPadding: const EdgeInsets.symmetric(horizontal: 60), + isScrollable: true, + labelStyle: const TextStyle( + fontWeight: FontWeight.w700, fontSize: 14), + labelColor: Colors.black, + unselectedLabelColor: Colors.grey, + tabs: [ + Text( + 'Posts', + style: TextStyle(fontSize: 15), + ), + const Padding( + padding: EdgeInsets.only(bottom: 6), + child: Text( + 'Comments', + ), + ), + ], + ), + ), + ), + ], + body: SizedBox(), + ), + ), + + body: TabBarView( + children: [ + SavedPosts(), + SavedCommentView(), + ]), + ), + ); + } +} diff --git a/lib/home/screens/saved_comments.dart b/lib/home/screens/saved_comments.dart index adcd89bf..5e3e0c5f 100644 --- a/lib/home/screens/saved_comments.dart +++ b/lib/home/screens/saved_comments.dart @@ -1,85 +1,92 @@ -import 'package:flutter/material.dart'; -import 'package:get/get.dart'; -import 'package:post/comments/widgets/comment.dart'; -import '../../widgets/loading_reddit.dart'; -import '../controller/home_controller.dart'; - -class SavedCommentView extends StatefulWidget { - SavedCommentView({Key? key}) : super(key: key); - - @override - State createState() => _SavedCommentViewState(); -} - -class _SavedCommentViewState extends State - with TickerProviderStateMixin { - final HomeController controller = Get.put(HomeController()); - late AnimationController loadingSpinnerAnimationController; - @override - void initState() { - super.initState(); - loadingSpinnerAnimationController = - AnimationController(duration: const Duration(seconds: 2), vsync: this); - loadingSpinnerAnimationController.repeat(); - if (controller.userSavedComments.isEmpty) { - controller.getSavedComments(); - } - } - - void dispose() { - loadingSpinnerAnimationController.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return Obx( - () => Scaffold( - body: RefreshIndicator( - backgroundColor: Colors.white, - color: Colors.blue[900], - onRefresh: () async { - controller.userSavedComments.clear(); - controller.pageNumberComment.value = 1; - controller.pageNumberComment.update((val) {}); - controller.getSavedComments(); - controller.update(); - //Get.forceAppUpdate(); - }, - child: controller.isLoadingCommetns.value - ? Center( - child: CircularProgressIndicator( - valueColor: loadingSpinnerAnimationController.drive( - ColorTween( - //begin: Colors.blueAccent, - end: Colors.blueAccent, - ), - ), - ), - ) - : controller.errorComment.value - ? ListView( - children: const [ - Padding( - padding: EdgeInsets.all(150), - child: Text("Unexpected Error Try Again.."), - ), - ], - ) - : controller.userSavedComments.isEmpty - ? LoadingReddit() - : ListView.builder( - itemCount: controller.userSavedComments.length, - itemBuilder: ( - final BuildContext ctx, - final int index, - ) { - return Comment( - data: controller.userSavedComments[index], - userName: '${controller.myProfile!.userName}'); - }, - ), - )), - ); - } -} +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:post/comments/widgets/comment.dart'; +import 'package:responsive_sizer/responsive_sizer.dart'; + + +import '../../networks/const_endpoint_data.dart'; +import '../../post/widgets/post.dart'; +import '../../post/widgets/post_list.dart'; +import '../../widgets/loading_reddit.dart'; +import '../controller/home_controller.dart'; + +class SavedCommentView extends StatefulWidget { + SavedCommentView({Key? key}) : super(key: key); + + @override + State createState() => _SavedCommentViewState(); +} + +class _SavedCommentViewState extends State with TickerProviderStateMixin{ + final HomeController controller = Get.put(HomeController()); + late AnimationController loadingSpinnerAnimationController; + @override + void initState() + { + super.initState(); + loadingSpinnerAnimationController = + AnimationController(duration: const Duration(seconds: 2), vsync: this); + loadingSpinnerAnimationController.repeat(); + if (controller.userSavedComments.isEmpty) { + controller.getSavedComments(p: controller.pageNumberComment.value, ); + } + } + void dispose() { + loadingSpinnerAnimationController.dispose(); + super.dispose(); + } + @override + Widget build(BuildContext context) { + return Obx(()=> + Scaffold( + body: + RefreshIndicator( + backgroundColor: Colors.white, + color: Colors.blue[900], + onRefresh: () async { + controller.userSavedComments.clear(); + controller.pageNumberComment.value = 1; + controller.pageNumberComment.update((val) {}); + controller.getSavedComments(p: controller.pageNumberComment.value, ); + controller.update(); + //Get.forceAppUpdate(); + }, + child: controller.isLoadingCommetns.value + ? Center( + child: CircularProgressIndicator( + valueColor: loadingSpinnerAnimationController.drive( + ColorTween( + //begin: Colors.blueAccent, + end: Colors.blueAccent, + ), + ), + ), + ) + : controller.errorComment.value + ? ListView( + children: const [ + Padding( + padding: EdgeInsets.all(150), + child: Text("Unexpected Error Try Again.."), + ), + ], + ) + : controller.userSavedComments.isEmpty + ? LoadingReddit() + + : ListView.builder( + itemCount: controller.userSavedComments.length, + itemBuilder: ( + final BuildContext ctx, + final int index, + ) { + return Comment(data:controller.userSavedComments[index], userName: '${controller.myProfile!.userName}' + ); + }, + ), + ) + ), + ); + } +} diff --git a/lib/home/screens/saved_posts.dart b/lib/home/screens/saved_posts.dart index eca4e9d1..cec6e372 100644 --- a/lib/home/screens/saved_posts.dart +++ b/lib/home/screens/saved_posts.dart @@ -1,83 +1,88 @@ -import 'package:flutter/material.dart'; -import 'package:get/get.dart'; -import '../../post/widgets/post_list.dart'; -import '../../widgets/loading_reddit.dart'; -import '../controller/home_controller.dart'; - -class SavedPosts extends StatefulWidget { - SavedPosts({Key? key}) : super(key: key); - - @override - State createState() => _SavedPostsState(); -} - -class _SavedPostsState extends State with TickerProviderStateMixin { - final HomeController controller = Get.put(HomeController()); - late AnimationController loadingSpinnerAnimationController; - @override - void initState() { - super.initState(); - loadingSpinnerAnimationController = - AnimationController(duration: const Duration(seconds: 2), vsync: this); - loadingSpinnerAnimationController.repeat(); - if (controller.userSavedPosts.isEmpty) { - controller.getSavedPosts(); - } - } - - void dispose() { - loadingSpinnerAnimationController.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return Obx( - () => Scaffold( - body: RefreshIndicator( - backgroundColor: Colors.white, - color: Colors.blue[900], - onRefresh: () async { - controller.userSavedPosts.clear(); - controller.pageNumberSaved.value = 1; - controller.pageNumberSaved.update((val) {}); - controller.getSavedPosts(); - controller.update(); - //Get.forceAppUpdate(); - }, - child: controller.isLoadingSaved.value - ? Center( - child: CircularProgressIndicator( - valueColor: loadingSpinnerAnimationController.drive( - ColorTween( - //begin: Colors.blueAccent, - end: Colors.blueAccent, - ), - ), - ), - ) - : controller.errorSaved.value - ? ListView( - children: const [ - Padding( - padding: EdgeInsets.all(150), - child: Text("Unexpected Error Try Again.."), - ), - ], - ) - : controller.userSavedPosts.isEmpty - ? LoadingReddit() - : PostList( - userName: '${controller.myProfile!.userName}', - updateData: () { - controller.pageNumberSaved.value++; - controller.pageNumberSaved.update((val) {}); - controller.getSavedPosts(); - }, - data: controller.userSavedPosts, - ), - ), - ), - ); - } -} +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:responsive_sizer/responsive_sizer.dart'; + + +import '../../post/widgets/post.dart'; +import '../../post/widgets/post_list.dart'; +import '../../widgets/loading_reddit.dart'; +import '../controller/home_controller.dart'; + +class SavedPosts extends StatefulWidget { + SavedPosts({Key? key}) : super(key: key); + + @override + State createState() => _SavedPostsState(); +} + +class _SavedPostsState extends State with TickerProviderStateMixin{ + final HomeController controller = Get.put(HomeController()); + late AnimationController loadingSpinnerAnimationController; + @override + void initState() + { + super.initState(); + loadingSpinnerAnimationController = + AnimationController(duration: const Duration(seconds: 2), vsync: this); + loadingSpinnerAnimationController.repeat(); + if (controller.userSavedPosts.isEmpty) { + controller.getSavedPosts(p: controller.pageNumberSaved.value, ); + } + } + void dispose() { + loadingSpinnerAnimationController.dispose(); + super.dispose(); + } + @override + Widget build(BuildContext context) { + return Obx(()=> + Scaffold( + body: + RefreshIndicator( + backgroundColor: Colors.white, + color: Colors.blue[900], + onRefresh: () async { + controller.userSavedPosts.clear(); + controller.pageNumberSaved.value = 1; + controller.pageNumberSaved.update((val) {}); + controller.getSavedPosts(p: controller.pageNumberSaved.value,); + controller.update(); + //Get.forceAppUpdate(); + }, + child: controller.isLoadingSaved.value + ? Center( + child: CircularProgressIndicator( + valueColor: loadingSpinnerAnimationController.drive( + ColorTween( + //begin: Colors.blueAccent, + end: Colors.blueAccent, + ), + ), + ), + ) + : controller.errorSaved.value + ? ListView( + children: const [ + Padding( + padding: EdgeInsets.all(150), + child: Text("Unexpected Error Try Again.."), + ), + ], + ) + : controller.userSavedPosts.isEmpty + ? LoadingReddit() + : + PostList( + userName: '${controller.myProfile!.userName}', updateData: (){ + controller.pageNumberSaved.value++; + controller.pageNumberSaved.update((val) { }); + controller.getSavedPosts(p: controller.pageNumberSaved.value,); + }, data: controller.userSavedPosts, + ), + ) , + + ), + ); + } +} diff --git a/lib/home/widgets/buttom_nav_bar.dart b/lib/home/widgets/buttom_nav_bar.dart index 8ccf952b..3bf4908d 100644 --- a/lib/home/widgets/buttom_nav_bar.dart +++ b/lib/home/widgets/buttom_nav_bar.dart @@ -8,7 +8,6 @@ import '../../notification/screens/notifications_screen.dart'; import '../../icons/icon_broken.dart'; import '../controller/home_controller.dart'; import '../screens/home_layout.dart'; -import '../../discover/screens/discover_screen.dart'; class buttomNavBar extends StatefulWidget { const buttomNavBar( @@ -50,13 +49,13 @@ class _buttomNavBarState extends State { // duration: Duration(seconds: 2), // curve: Curves.easeOut); // } else { - Get.to(HomeLayoutScreen()); + Get.to(HomeLayoutScreen()); //} } break; case 1: { - Get.to(DiscoverScreen()); + Get.to(HomeLayoutScreen()); } break; case 2: diff --git a/lib/home/widgets/comment_layout.dart b/lib/home/widgets/comment_layout.dart index a2ca2531..38f6a7dc 100644 --- a/lib/home/widgets/comment_layout.dart +++ b/lib/home/widgets/comment_layout.dart @@ -1,10 +1,10 @@ -import 'package:flutter/cupertino.dart'; - -class SavedCommentView extends StatelessWidget { - const SavedCommentView({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Container(); - } -} +import 'package:flutter/cupertino.dart'; + +class SavedCommentView extends StatelessWidget { + const SavedCommentView({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Container(); + } +} diff --git a/lib/home/widgets/container_in_recently_visited.dart b/lib/home/widgets/container_in_recently_visited.dart index cb7fe372..fb3b4033 100644 --- a/lib/home/widgets/container_in_recently_visited.dart +++ b/lib/home/widgets/container_in_recently_visited.dart @@ -1,48 +1,48 @@ -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; -import 'package:get/get.dart'; -import 'package:get/get_core/src/get_main.dart'; -import 'package:post/home/controller/home_controller.dart'; -import 'package:post/subreddit/screens/subreddit_screen.dart'; - -import '../../create_community/screens/create_community.dart'; -import '../../moderated_subreddit/screens/moderated_subreddit_screen.dart'; -class RecentlyVisitedContainer extends StatelessWidget { - - String nameOfSubreddit=""; - String iconOfSubreddit=''; - RecentlyVisitedContainer( - { - required this.nameOfSubreddit, - required this.iconOfSubreddit, - } - ); - @override - Widget build(BuildContext context) { - return ListTile( - horizontalTitleGap: 0.0, - leading: CircleAvatar(radius: 13,backgroundColor: Colors.blue, - backgroundImage: NetworkImage( - "$iconOfSubreddit" - ), - ), - title: Text("$nameOfSubreddit"), - trailing: IconButton( - onPressed: (){ - - }, - icon: Icon( - Icons.close, - color: Colors.grey, - size: 20 - ), - ), - onTap: () => - Navigator.of(context).pushNamed( - SubredditScreen.routeName, - arguments: nameOfSubreddit) - - - ); - } -} +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:get/get_core/src/get_main.dart'; +import 'package:post/home/controller/home_controller.dart'; +import 'package:post/subreddit/screens/subreddit_screen.dart'; + +import '../../create_community/screens/create_community.dart'; +import '../../moderated_subreddit/screens/moderated_subreddit_screen.dart'; +class RecentlyVisitedContainer extends StatelessWidget { + + String nameOfSubreddit=""; + String iconOfSubreddit=''; + RecentlyVisitedContainer( + { + required this.nameOfSubreddit, + required this.iconOfSubreddit, + } + ); + @override + Widget build(BuildContext context) { + return ListTile( + horizontalTitleGap: 0.0, + leading: CircleAvatar(radius: 13,backgroundColor: Colors.blue, + backgroundImage: NetworkImage( + "$iconOfSubreddit" + ), + ), + title: Text("$nameOfSubreddit"), + trailing: IconButton( + onPressed: (){ + + }, + icon: Icon( + Icons.close, + color: Colors.grey, + size: 20 + ), + ), + onTap: () => + Navigator.of(context).pushNamed( + SubredditScreen.routeName, + arguments: nameOfSubreddit) + + + ); + } +} diff --git a/lib/home/widgets/custom_upper_bar.dart b/lib/home/widgets/custom_upper_bar.dart new file mode 100644 index 00000000..5d306232 --- /dev/null +++ b/lib/home/widgets/custom_upper_bar.dart @@ -0,0 +1,371 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:post/create_community/screens/create_community.dart'; +import 'package:post/createpost/screens/createpost.dart'; +import 'package:post/logins/screens/login.dart'; +import 'package:post/notification/screens/notifications_screen.dart'; +import 'package:post/settings/screens/settings.dart'; +import '../../logins/providers/authentication.dart'; +import '../../createpost/controllers/posts_controllers.dart'; +import '../../icons/icon_broken.dart'; +import '../../logins/providers/authentication.dart'; +import '../../myprofile/screens/myprofile_screen.dart'; +import '../../search/screens/search.dart'; +import '../controller/home_controller.dart'; +import '../screens/all.dart'; +import '../screens/home_layout.dart'; +import 'community_container.dart'; + +class UpBar extends StatelessWidget { + final HomeController controller ; + final PostController controllerForCreatePost ; + UpBar({ + Key? key, + required this.controller, + required this.controllerForCreatePost, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return AppBar( + backgroundColor: Colors.white, + elevation: 0, + + leading: GestureDetector(child: Padding( + padding: const EdgeInsetsDirectional.only(start: 20.0), + child: Image.asset('assets/images/redditlogo.png'), + )) , + title: Row( + children: [ + const Text( + 'Reddit', + style: TextStyle( + fontSize: 20.0, fontWeight: FontWeight.bold), + ), + const SizedBox( + width: 30, + ), + Container( + width: 220, + child: PopupMenuButton( + child: Row( + children: [ + Icon(Icons.home_filled), + SizedBox(width: 20,), + Text("Home"), + SizedBox(width: 80,), + Icon(Icons.arrow_drop_down_sharp) + ], + ), + elevation: 0, + color: Colors.white, + offset:Offset(0,43) , + itemBuilder: (context)=>[ + PopupMenuItem( + value:1, + child: Container(width:220,child: Text("MODERATING",style: TextStyle(color: Colors.grey,fontSize: 10),)), + + ), + PopupMenuItem( + value:2, + child: Container(width:220,child:Column( + children: List.generate(controllerForCreatePost.moderatedSubreddits.length, (index) => + CommunityContainer( + nameOfSubreddit: controllerForCreatePost + .moderatedSubreddits[index] + .subredditName!, + iconOfSubreddit: (controllerForCreatePost + .moderatedSubreddits[index] + .icon != + null) + ? controllerForCreatePost + .moderatedSubreddits[index].icon! + : '') + ), + )), + + ), + PopupMenuItem( + value:3, + child: Container(width:220,child: Text("YOUR COMMUNITIES",style: TextStyle(color: Colors.grey,fontSize: 10),)), + ), + PopupMenuItem( + value:4, + child: ListTile( + onTap: () + { + Get.to(CreateCommunity()); + }, + horizontalTitleGap: 0, + leading: Icon(Icons.add,color: Colors.grey,), + title: Text("Create Community",style: TextStyle(color: Colors.grey),), + ), + ), + PopupMenuItem( + value:5, + child: Container(width:220,child:Column( + children: List.generate(controllerForCreatePost.subscribedSubreddits.length, (index) => + CommunityContainer( + nameOfSubreddit: controllerForCreatePost + .subscribedSubreddits[index] + .subredditName!, + iconOfSubreddit: (controllerForCreatePost + .subscribedSubreddits[index] + .icon != + null) + ? controllerForCreatePost + .subscribedSubreddits[index].icon! + : '') + ), + )), + + ), + PopupMenuItem( + value:6, + child: Container(width:220,child: Text("FEEDS",style: TextStyle(color: Colors.grey,fontSize: 10),)), + + ), + PopupMenuItem( + value:7, + child: Column( + children: [ + ListTile( + onTap: () + { + Get.to(HomeLayoutScreen()); + }, + horizontalTitleGap:0, + leading: Icon(Icons.home_filled), + title: Text("Home"), + ), + ListTile( + onTap: () + { + Get.to(All()); + }, + horizontalTitleGap:0, + leading: Icon(Icons.stacked_bar_chart), + title: Text("All"), + ) + ], + ) + ), + ], + ) + ), + Expanded( + child: Container( + width: 350.0, + height: 35.0, + child: TextFormField( + onTap: () + { + Get.to(Search()); + }, + enabled: false, + decoration: const InputDecoration( + border: OutlineInputBorder(borderRadius: BorderRadius.all(Radius.circular(20))), + labelText: 'Search Reddit', + prefixIcon: Icon(Icons.search), + labelStyle: TextStyle( + color: Colors.grey, + fontSize: 18.0, + ), + ), + ), + ), + ), + const SizedBox( + width: 20, + ), + IconButton( + onPressed: () { + Get.to(All()); + }, + icon: const Icon(Icons.arrow_circle_up,size: 25,)), + const SizedBox( + width: 20, + ), + IconButton( + onPressed: () { + Get.to(NotificationScreen()); + }, + icon: const Icon(IconBroken.Notification,size: 25,)), + const SizedBox( + width: 20, + ), + IconButton( + onPressed: () { + Get.to(CreatePostSCreen(),arguments: [0,0,0]); + }, + icon: const Icon(Icons.add_sharp,size: 25,)), + const SizedBox( + width: 20, + ), + ], + ), + actions: [ + + Container( + padding: EdgeInsetsDirectional.only(end: 20,start: 10), + child: PopupMenuButton( + + child: Container( + decoration: BoxDecoration( + border: Border.all(color: Colors.black54,width: 0.25) + ), + width: 270, + height: 20, + child: Padding( + padding: const EdgeInsetsDirectional.only(start: 10.0), + child: Row( + children: [ + Stack( + alignment: AlignmentDirectional.bottomEnd, + children: const [ + CircleAvatar( + backgroundImage: AssetImage( + "assets/images/reddit.gif"), + radius: 18.0, + ) + , + CircleAvatar( + backgroundColor: Colors.white, + radius: 6, + ), + Padding( + padding: EdgeInsetsDirectional.only(end: 2, bottom: 2), + child: CircleAvatar( + backgroundColor: Colors.green, + radius: 4, + ), + ) + ], + ),const SizedBox(width: 10,), + Padding( + padding: const EdgeInsetsDirectional.only(top: 10.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: const [ + Text("Ahmed"), + Text("karma"), + ], + ), + ), + const SizedBox(width: 100,), + const Icon(IconBroken.Arrow___Down_2) + ], + ), + ), + ), + itemBuilder: (context) => [ + PopupMenuItem( + value: 1, + child: Container( + padding: EdgeInsetsDirectional.only(end: 80), + height: 40, + width: 220, + child: ListTile( + horizontalTitleGap: 0, + leading: Icon(IconBroken.Profile,color: Colors.grey,), + title: Text("My Stuff",style: TextStyle(color: Colors.grey),), + ), + ), + ), + PopupMenuItem( + value: 2, + child: Container( + padding: EdgeInsetsDirectional.only(start: 30,bottom: 10), + height: 30, + width: 220, + child: ListTile( + onTap: (){ + Get.to(MyProfileScreen()); + }, + title: Text("Profile",style: TextStyle(color: Colors.black,fontSize: 16,fontWeight: FontWeight.bold),), + ), + ), + ), + PopupMenuItem( + value: 3, + child: Container( + padding: EdgeInsetsDirectional.only(start: 30,bottom: 10), + height: 30, + width: 220, + child: ListTile( + onTap: () + { + Get.to(Settings()); + }, + title: Text("User settings",style: TextStyle(color: Colors.black,fontSize: 16,fontWeight: FontWeight.bold),), + ), + ), + ), + PopupMenuItem(child: Divider()), + PopupMenuItem( + value: 4, + child: Container( + padding: EdgeInsetsDirectional.only(end: 0), + height: 40, + width: 220, + child: ListTile( + onTap: () + { + Get.to(CreateCommunity()); + }, + horizontalTitleGap: 0, + leading: Padding( + padding: const EdgeInsetsDirectional.only(top: 12.0), + child: Icon(Icons.r_mobiledata_outlined,color: Colors.black,size: 30,), + ), + title: Text("Create a Community",style: TextStyle(color: Colors.black,fontSize: 16,fontWeight: FontWeight.bold),), + ), + ), + ), + PopupMenuItem( + value: 5, + child: Container( + padding: EdgeInsetsDirectional.only(start: 40), + height: 40, + width: 220, + child: ListTile( + horizontalTitleGap: 0, + title: Text("Privacy Policy",style: TextStyle(color: Colors.black,fontWeight: FontWeight.bold,fontSize: 16),), + ), + ), + ), + PopupMenuItem(child: Divider()), + PopupMenuItem( + value: 6, + child: Container( + padding: EdgeInsetsDirectional.only(start: 10,bottom: 1), + height: 30, + width: 220, + child: ListTile( + onTap: () + { + Auth().logOut(context); + Navigator.of(context).pushNamed(Login.routeName); + + }, + horizontalTitleGap: 0, + leading: Icon(Icons.logout,size: 25,color: Colors.black,), + title: Text("Log out",style: TextStyle(color: Colors.black,fontSize: 16,fontWeight: FontWeight.bold),), + ), + ), + ), + PopupMenuItem(child: Divider()), + ] + , + offset: Offset(0, 59), + color: Colors.white, + elevation: 0, + + + ), + ), + ], + ); + } +} diff --git a/lib/home/widgets/end_drawer.dart b/lib/home/widgets/end_drawer.dart index b44eea49..b7228032 100644 --- a/lib/home/widgets/end_drawer.dart +++ b/lib/home/widgets/end_drawer.dart @@ -4,7 +4,6 @@ import 'package:get/get.dart'; import 'package:get/get_core/src/get_main.dart'; import 'package:post/home/controller/home_controller.dart'; import 'package:post/home/screens/saved.dart'; -import 'package:post/logins/screens/login.dart'; import 'package:responsive_sizer/responsive_sizer.dart'; import '../../home/screens/history.dart'; import '../../create_community/screens/create_community.dart'; @@ -12,10 +11,11 @@ import '../../icons/icon_broken.dart'; import '../../myprofile/screens/myprofile_screen.dart'; import '../../settings/screens/settings.dart'; import 'package:shared_preferences/shared_preferences.dart'; -import '../../logins/providers/authentication.dart'; - class endDrawer extends StatelessWidget { - endDrawer({required this.controller, Key? key}) : super(key: key); + + endDrawer({ + required this.controller + ,Key? key}) : super(key: key); final HomeController controller; bool isOnline = true; @@ -84,17 +84,17 @@ class endDrawer extends StatelessWidget { icon: CircleAvatar( radius: 4, backgroundColor: - isOnline ? Colors.green : Colors.grey[200], + isOnline ? Colors.green : Colors.grey[200], ), style: ButtonStyle( elevation: MaterialStateProperty.all(0), backgroundColor: - MaterialStateProperty.all(Colors.grey[200]), + MaterialStateProperty.all(Colors.grey[200]), shape: MaterialStateProperty.all< - RoundedRectangleBorder>( + RoundedRectangleBorder>( RoundedRectangleBorder( borderRadius: - BorderRadius.circular(20.0), + BorderRadius.circular(20.0), side: BorderSide( color: isOnline ? Colors.green @@ -103,7 +103,7 @@ class endDrawer extends StatelessWidget { "Online Status: " + "${isOnline ? "On" : "Off"}", style: TextStyle( color: - isOnline ? Colors.green : Colors.black54), + isOnline ? Colors.green : Colors.black54), ), ), ), @@ -122,172 +122,121 @@ class endDrawer extends StatelessWidget { child: Row( mainAxisAlignment: MainAxisAlignment.start, children: [ - GestureDetector( - onTap: () { - showDialog( - context: context, - builder: (ctx) => AlertDialog( - content: Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(20), - ), - width: - MediaQuery.of(context).size.width / 1, - height: - MediaQuery.of(context).size.height / 2, - child: (Column( - mainAxisAlignment: - MainAxisAlignment.center, - crossAxisAlignment: - CrossAxisAlignment.center, - children: [ - SizedBox( - height: 30, - ), - CircleAvatar( - radius: 70, - backgroundImage: NetworkImage( - "${controller.myProfile!.profilePicture}"), - ), - SizedBox( - height: 130, - ), - Padding( - padding: - const EdgeInsetsDirectional.only( - end: 30), - child: Text( - 'u/${controller.myProfile!.displayName}', - style: TextStyle( - fontSize: 17, - fontWeight: FontWeight.w500), - ), - ), - SizedBox( - height: 10, - ), - Expanded( - child: Row( - children: [ - Expanded( - child: Column( - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - Text( - "${controller.myProfile!.postKarma}"), - Text( - "Post Karma", - style: TextStyle( - color: - Colors.grey[400], - fontSize: 14), - ), - ], - ), - ), - SizedBox( - width: 20, - ), - Column( - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - Text( - "${controller.myProfile!.commentkarma}"), - Text( - "Comment Karma", - style: TextStyle( - color: Colors.grey[400], - fontSize: 14), - ), - ], - ), - ], - ), - ), - SizedBox( - height: 10, - ), - Padding( - padding: - const EdgeInsetsDirectional.only( - end: 10), - child: ListTile( - onTap: () { - Navigator.of(context).pushNamed( - MyProfileScreen.routeName, - arguments: userName); - }, - leading: Icon( - Icons.account_circle, - color: Colors.black, - ), - title: Text( - "View profile", - style: TextStyle( - color: Colors.black, - fontSize: 16, - fontWeight: FontWeight.w600), - ), - horizontalTitleGap: 0, - ), - ) - ], - )))), - ); - }, - child: Row( - children: [ - Icon( - Icons.ac_unit_outlined, - color: Colors.blue[700], - ), - SizedBox( - width: 5, - ), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - "${controller.myProfile!.postKarma}", - style: TextStyle( - color: Colors.black, - fontWeight: FontWeight.bold), - ), - Text( - "karma", - style: TextStyle(color: Colors.grey), - ) - ], - ) - ], - )), - SizedBox( - width: 10, - ), - SizedBox( - width: 10, - ), - GestureDetector( - child: Row( + GestureDetector( + onTap: (){ + showDialog( + context: context, + builder: (ctx) => AlertDialog( + content: Container( + decoration: BoxDecoration( + borderRadius:BorderRadius.circular(20), + ), + width: MediaQuery.of(context).size.width/1, + height: MediaQuery.of(context).size.height/2, + child: ( + Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + SizedBox(height: 30,), + CircleAvatar( + radius:70, + backgroundImage: NetworkImage("${controller.myProfile!.profilePicture}"), + ), + SizedBox(height: 130,), + Padding( + padding: const EdgeInsetsDirectional.only(end: 30), + child: Text( + 'u/${controller.myProfile!.displayName}', + style: TextStyle(fontSize: 17, fontWeight: FontWeight.w500), + ), + ), + SizedBox(height: 10,), + Expanded( + child: Row( + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("${controller.myProfile!.postKarma}"), + Text("Post Karma", + style: TextStyle( + color: Colors.grey[400], + fontSize: 14 + ), + ), + ], + ), + ), + SizedBox(width:20,), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("${controller.myProfile!.commentkarma}"), + Text("Comment Karma", + style: TextStyle( + color: Colors.grey[400], + fontSize: 14 + ), + ), + ], + ), + ], + ), + ), + SizedBox(height: 10,), + Padding( + padding: const EdgeInsetsDirectional.only(end: 10), + child: ListTile( + onTap: () + { + Navigator.of(context).pushNamed(MyProfileScreen.routeName, + arguments: userName); + }, + leading: Icon(Icons.account_circle,color: Colors.black,), + title: Text("View profile",style: TextStyle(color: Colors.black,fontSize: 16,fontWeight: FontWeight.w600),), + horizontalTitleGap: 0, + ), + ) + ], + ) + ) + ) + ), + ); + }, + child: Row( + children: [ + Icon(Icons.ac_unit_outlined,color: Colors.blue[700],), + SizedBox(width: 5,), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("${controller.myProfile!.postKarma}",style: TextStyle(color: Colors.black,fontWeight: FontWeight.bold),), + Text("karma", + style: TextStyle( + color: Colors.grey + ), + ) + ], + ) + ], + )), + SizedBox(width: 10,), + + SizedBox(width: 10,), + GestureDetector(child: Row( children: [ - Icon( - Icons.text_snippet_rounded, - color: Colors.blue[700], - ), + Icon(Icons.text_snippet_rounded,color: Colors.blue[700],), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text( - "1m 2d", + Text("1m 2d",style: TextStyle(color: Colors.black,fontWeight: FontWeight.bold),), + Text("Reddit ago", style: TextStyle( - color: Colors.black, - fontWeight: FontWeight.bold), - ), - Text( - "Reddit ago", - style: TextStyle(color: Colors.grey), + color: Colors.grey + ), ) ], ) @@ -321,6 +270,7 @@ class endDrawer extends StatelessWidget { onTap: () { Navigator.of(context).pushNamed(CreateCommunity.routeName); // Navigator.pop(context); + }, ), ListTile( @@ -331,7 +281,7 @@ class endDrawer extends StatelessWidget { style: TextStyle(fontSize: 14, fontWeight: FontWeight.w600), ), onTap: () { - Get.to(Saved()); + Get.to(Saved()); }, ), ListTile( @@ -342,24 +292,13 @@ class endDrawer extends StatelessWidget { style: TextStyle(fontSize: 14, fontWeight: FontWeight.w600), ), onTap: () { - Get.to(History()); + Get.to(History()); }, ), SizedBox( height: 20.h, ), - ListTile( - horizontalTitleGap: 3, - leading: Icon(IconBroken.Setting), - title: Text( - 'Log out', - style: TextStyle(fontSize: 14, fontWeight: FontWeight.w600), - ), - onTap: () { - Auth().logOut(context); - Navigator.of(context).pushNamed(Login.routeName); - }, - ), + ListTile( horizontalTitleGap: 3, leading: Icon(IconBroken.Setting), @@ -377,4 +316,4 @@ class endDrawer extends StatelessWidget { ), ); } -} +} \ No newline at end of file diff --git a/lib/home/widgets/new_drawer.dart b/lib/home/widgets/new_drawer.dart index f1667eab..3eff29a3 100644 --- a/lib/home/widgets/new_drawer.dart +++ b/lib/home/widgets/new_drawer.dart @@ -245,4 +245,4 @@ class MyDrawer extends StatelessWidget { ), ); } -} +} \ No newline at end of file diff --git a/lib/home/widgets/recently_visited_list.dart b/lib/home/widgets/recently_visited_list.dart index 1d7f0caa..44a3c739 100644 --- a/lib/home/widgets/recently_visited_list.dart +++ b/lib/home/widgets/recently_visited_list.dart @@ -1,74 +1,74 @@ -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; -import 'package:get/get.dart'; -import 'package:get/get_core/src/get_main.dart'; -import 'package:post/home/controller/home_controller.dart'; -import 'package:post/home/widgets/community_container.dart'; -import 'package:post/home/widgets/subscribed_community_container.dart'; -import 'package:post/networks/const_endpoint_data.dart'; - -import '../../create_community/screens/create_community.dart'; -import '../../createpost/controllers/posts_controllers.dart'; -import '../../icons/icon_broken.dart'; -import 'container_in_recently_visited.dart'; - -class RecentlyVisitedDrawer extends StatelessWidget { - - final HomeController controller; - const RecentlyVisitedDrawer({ - Key? key, - required this.controller, - }) : super(key: key); - @override - Widget build(BuildContext context) { - return Drawer( - elevation: 20.0, - child: SingleChildScrollView( - child: Column( - children: [ - const SizedBox( - height: 40, - ), - const Divider( - height: 3, - ), - Row( - children: [ - IconButton(onPressed: (){ - controller.isRecentlyVisitedDrawer.value=false; - }, icon: Icon(IconBroken.Arrow___Left_2)), - SizedBox(width: 5,), - Text( - "Recently Visited", - style: TextStyle( - fontWeight: FontWeight.w500, - fontSize: 16, - color: Colors.black), - ), - Spacer(), - TextButton( - onPressed: (){ - controller.recentlyVisited.clear(); - }, child: - Text("Clear all", - style: TextStyle( - color: Colors.grey, - fontSize: 14 - ), - ) - ), - ], - ), - SizedBox(height: 8,), - Column( - children: - List.generate(4, (index) => - RecentlyVisitedContainer(nameOfSubreddit: "coross", iconOfSubreddit: "https://png.pngtree.com/element_our/20190530/ourmid/pngtree-correct-icon-image_1267804.jpg") - ) - ) - ], - ), - ), - ); - } -} +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:get/get_core/src/get_main.dart'; +import 'package:post/home/controller/home_controller.dart'; +import 'package:post/home/widgets/community_container.dart'; +import 'package:post/home/widgets/subscribed_community_container.dart'; +import 'package:post/networks/const_endpoint_data.dart'; + +import '../../create_community/screens/create_community.dart'; +import '../../createpost/controllers/posts_controllers.dart'; +import '../../icons/icon_broken.dart'; +import 'container_in_recently_visited.dart'; + +class RecentlyVisitedDrawer extends StatelessWidget { + + final HomeController controller; + const RecentlyVisitedDrawer({ + Key? key, + required this.controller, + }) : super(key: key); + @override + Widget build(BuildContext context) { + return Drawer( + elevation: 20.0, + child: SingleChildScrollView( + child: Column( + children: [ + const SizedBox( + height: 40, + ), + const Divider( + height: 3, + ), + Row( + children: [ + IconButton(onPressed: (){ + controller.isRecentlyVisitedDrawer.value=false; + }, icon: Icon(IconBroken.Arrow___Left_2)), + SizedBox(width: 5,), + Text( + "Recently Visited", + style: TextStyle( + fontWeight: FontWeight.w500, + fontSize: 16, + color: Colors.black), + ), + Spacer(), + TextButton( + onPressed: (){ + controller.recentlyVisited.clear(); + }, child: + Text("Clear all", + style: TextStyle( + color: Colors.grey, + fontSize: 14 + ), + ) + ), + ], + ), + SizedBox(height: 8,), + Column( + children: + List.generate(4, (index) => + RecentlyVisitedContainer(nameOfSubreddit: "coross", iconOfSubreddit: "https://png.pngtree.com/element_our/20190530/ourmid/pngtree-correct-icon-image_1267804.jpg") + ) + ) + ], + ), + ), + ); + } +} From fbe35425982ed332ffb3d03eaee43ae2d7b6dad1 Mon Sep 17 00:00:00 2001 From: Ahmed Fawzy <99263226+belfooz@users.noreply.github.com> Date: Fri, 23 Dec 2022 21:04:38 +0200 Subject: [PATCH 07/16] Add files via upload --- .../controllers/posts_controllers.dart | 153 +-- lib/createpost/screens/createpost.dart | 1085 +++++++++++------ lib/createpost/screens/finalpost.dart | 614 +++++----- lib/createpost/services/post_services.dart | 22 +- lib/createpost/widgets/type_of_post_web.dart | 265 ++++ 5 files changed, 1361 insertions(+), 778 deletions(-) create mode 100644 lib/createpost/widgets/type_of_post_web.dart diff --git a/lib/createpost/controllers/posts_controllers.dart b/lib/createpost/controllers/posts_controllers.dart index 3f7c3857..ee03ba31 100644 --- a/lib/createpost/controllers/posts_controllers.dart +++ b/lib/createpost/controllers/posts_controllers.dart @@ -1,4 +1,5 @@ import 'dart:io'; +import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:image_picker/image_picker.dart'; @@ -18,21 +19,21 @@ class PostController extends GetxController { List moderatedSubreddits = [].obs; RxString subredditToSubmitPost = "".obs; - RxString iconOfSubredditToSubmittPost = "".obs; - RxString idOfSubredditToSubmittPost = "".obs; - RxBool showMore = false.obs; + RxString iconOfSubredditToSubmittPost="".obs; + RxString idOfSubredditToSubmittPost="".obs; + RxBool showMore=false.obs; RxBool isPostSpoiler = false.obs; RxBool isPostNSFW = false.obs; RxBool isLoading = true.obs; final services = PostServices(); Rx postTitle = TextEditingController().obs; - Rx textPost = quill.QuillController.basic().obs; + Rx textPost=quill.QuillController.basic().obs; Rx urlPost = TextEditingController().obs; RxString typeOfPost = ''.obs; GlobalKey formKey = GlobalKey(); GlobalKey formKeyUrl = GlobalKey(); - List? imageFileList = [].obs; + List? imageFileList = [].obs; Future? initializeVideoPlayerFuture; @@ -40,23 +41,22 @@ class PostController extends GetxController { final videoFile = Rx(null); final videoController = Rx(null); //Flairs belonging to subreddit - List flairsOfSubreddit = [].obs; - RxString idOfFlair = "".obs; - RxString textOfFlair = "".obs; - RxString textColorOfFlair = "None".obs; - RxString backgroundColorOfFlair = "None".obs; - RxBool isSubredditHasFlair = false.obs; - RxBool isFromHomeDirect = true.obs; - List checking = [].obs; - RxInt checkFromWhich = 0.obs; + ListflairsOfSubreddit=[].obs; + RxString idOfFlair="".obs; + RxString textOfFlair="".obs; + RxString textColorOfFlair="None".obs; + RxString backgroundColorOfFlair="None".obs; + RxBool isSubredditHasFlair=false.obs; + RxBool isFromHomeDirect=true.obs; + Listchecking=[].obs; + RxInt checkFromWhich=0.obs; dynamic argumentData = Get.arguments; @override - void onInit() { - _fetchHouses(); + void onInit() + { getSubreddits(); super.onInit(); } - // @override // onClose() // { @@ -64,33 +64,32 @@ class PostController extends GetxController { // typeOfPost.value=''; // super.onClose(); // } - getFlairsOfSubreddit() async { + getFlairsOfSubreddit() async{ final prefs = await SharedPreferences.getInstance(); DioClient.init(prefs); - try { + try{ print('/subreddit/${subredditToSubmitPost}/flairs'); - await DioClient.get(path: '/subreddits/${subredditToSubmitPost}/flair') - .then((value) { + await DioClient.get(path:'/subreddits/${subredditToSubmitPost}/flair').then((value){ print("Flairs returned are $value"); - value.data['data'].forEach((val) { + value.data['data'].forEach((val) + { flairsOfSubreddit.add(FlairModel.fromJson(val)); }); }); - checking = List.filled(flairsOfSubreddit.length + 1, false); - print( - "the size of returned list of flairs of subreddit is ${flairsOfSubreddit.length}"); + checking = List.filled(flairsOfSubreddit.length+1, false); + print("the size of returned list of flairs of subreddit is ${flairsOfSubreddit.length}"); print("the size of checking list is ${checking.length}"); - } catch (e) { - print( - "error in fetching the flairs of ${subredditToSubmitPost} subreddit -> $e"); } - } + catch(e){ + print("error in fetching the flairs of ${subredditToSubmitPost} subreddit -> $e"); + } + } getSubreddits() async { final prefs = await SharedPreferences.getInstance(); DioClient.init(prefs); try { - await DioClient.get(path: '$mySubreddits/subscriber').then((value) { + await DioClient.get(path:'$mySubreddits/subscriber').then((value) { print("subs list $value"); value.data['data'].forEach((value1) { subscribedSubreddits.add(userSubredditsResponse.fromJson(value1)); @@ -98,7 +97,7 @@ class PostController extends GetxController { }); }); - await DioClient.get(path: '$mySubreddits/moderator').then((value) { + await DioClient.get(path:'$mySubreddits/moderator').then((value) { print("moderator list $value"); value.data['data'].forEach((value1) { moderatedSubreddits.add(userSubredditsResponse.fromJson(value1)); @@ -111,66 +110,73 @@ class PostController extends GetxController { print("error in fething subreddits of user $error"); } } - - sendPost(BuildContext context) async { + Future sendPost(BuildContext context, {required String title,required String text,required String kind ,required String url,required String owner,required String ownerType,required bool nsfw,required bool spoiler}) async { final prefs = await SharedPreferences.getInstance(); DioClient.init(prefs); - var response; + var response ; try { - response = await DioClient.post(path: "/posts", data: { - "title": "AHMED", - //postTitle.value.text , - "kind": typeOfPost.value as String, - "text": "KKKK", - //(DeltaToHTML.encodeJson(textPost.value.document.toDelta().toJson())) as String, - //"url":urlPost.value.text as String , - "owner": idOfSubredditToSubmittPost.value as String, - "ownerType": - (subredditToSubmitPost.value == "Myprofile") ? "User" : "Subreddit", - // "nsfw": isPostNSFW.value , - // "spoiler": isPostSpoiler.value , + response= await DioClient.post(path: "/posts", data:{ + "title": title , + "kind": kind, + "text":text, + "url":url, + "owner":owner, + "ownerType": ownerType, + "nsfw": nsfw , + "spoiler": spoiler , "sendReplies": true, - - // "flairId":idOfFlair.value as String , - // "flairText":textOfFlair.value as String, - - // "title": "post", - // "kind": "self", - // "text": "Test comment count", - // "owner": "63a193ffe3d2b7ad4a939978", - // "ownerType": "User", - // "nsfw": false, - // "spoiler": true, - // "sendReplies": true + "suggestedSort":"hot" }); - print("YA RAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB"); if (response.statusCode == 200 || response.statusCode == 201) { - print("response of sending post ${response.data}"); ScaffoldMessenger.of(context).showSnackBar( - SnackBar( + const SnackBar( content: Text("sent successfuly", style: TextStyle(color: Colors.white)), backgroundColor: Colors.green), ); - print(response.statusCode); - print("the id of post created ${response.data["data"]["_id"]}"); - //print(json.decode(response.data)['message']); } - // return response.data["data"]["_id"]; + return response.data["data"]["_id"]; } catch (e) { print("error in sending the post -> $e"); - // return 0; + return ""; } } - - Future _fetchHouses() async { + Future sendPostWithFlair(BuildContext context, {required String title,required String text,required String kind ,required String url,required String owner,required String ownerType,required bool nsfw,required bool spoiler,required String textFlair,required String idFlair}) async { + final prefs = await SharedPreferences.getInstance(); + DioClient.init(prefs); + var response ; try { - ///here fitch Your Posts - } finally { - isLoading(false); + response = await DioClient.post(path: "/posts", data: { + "title": title, + "kind": kind, + "text": text, + "url": url, + "owner": owner, + "ownerType": ownerType, + "nsfw": nsfw, + "spoiler": spoiler, + "sendReplies": true, + "flairId": idOfFlair, + "flairText": textOfFlair, + "suggestedSort":"hot" + }); + if (response.statusCode == 200 || response.statusCode == 201) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text("sent successfuly", + style: TextStyle(color: Colors.white)), + backgroundColor: Colors.green), + ); + } + return response.data["data"]["_id"]; + } + catch (e) { + print("error in sending the post -> $e"); + return ""; } } + Future createPost() async { try { isLoading.value = true; @@ -183,10 +189,9 @@ class PostController extends GetxController { isLoading(false); } } - Future getVideo() async { Future videoFiles = - ImagePicker().pickVideo(source: ImageSource.gallery); + ImagePicker().pickVideo(source: ImageSource.gallery); videoFiles.then((file) async { videoFile.value = File(file!.path); videoController.value = VideoPlayerController.file(videoFile.value!); @@ -219,4 +224,6 @@ class PostController extends GetxController { typeOfPost.close(); super.dispose(); } + + } diff --git a/lib/createpost/screens/createpost.dart b/lib/createpost/screens/createpost.dart index 38e8b99c..8e968364 100644 --- a/lib/createpost/screens/createpost.dart +++ b/lib/createpost/screens/createpost.dart @@ -1,22 +1,34 @@ ///////////////////////BY ME/////////////////////////////////////////// import 'dart:convert'; +import 'dart:io'; import 'package:post/delta_to_html.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart'; +import 'package:flutter/services.dart'; import 'package:get/get.dart'; import 'package:image_picker/image_picker.dart'; -// import 'package:post/discover/discover.dart'; +import 'package:multi_image_picker/multi_image_picker.dart'; +import 'package:post/discover/discover.dart'; import 'package:post/home/screens/home_layout.dart'; import 'dart:async'; +import '../../home/controller/home_controller.dart'; +import '../../home/widgets/community_container.dart'; +import '../../home/widgets/custom_upper_bar.dart'; import '../../icons/icon_broken.dart'; import '../controllers/posts_controllers.dart'; import '../screens/posttocommunity.dart'; import '../widgets/type_of_post.dart'; +import '../widgets/type_of_post_web.dart'; import 'finalpost.dart'; + + import 'package:flutter_quill/flutter_quill.dart' as quill; + class CreatePostSCreen extends StatefulWidget { + // const CreatePostSCreen({ // Key? key, // this.name="", @@ -26,443 +38,756 @@ class CreatePostSCreen extends StatefulWidget { // final String name; @override State createState() => _CreatePostSCreenState(); + } class _CreatePostSCreenState extends State { // const CreatePostSCreen({super.key}); final ImagePicker imagePicker = ImagePicker(); final ImagePicker videoPicker = ImagePicker(); - final PostController controller = Get.put(PostController(), permanent: false); - + final PostController controller = Get.put( + PostController(),permanent: false + ); + final HomeController controllerForHome = Get.put( + HomeController(),permanent: false + ); Future? initializeVideoPlayerFuture; @override - void initState() { + void initState() + { + // TODO: implement initState - if (Get.arguments[0] == 0) { - controller.isFromHomeDirect.value = true; - } else { - controller.isFromHomeDirect.value = false; - controller.iconOfSubredditToSubmittPost.value = Get.arguments[1]; - controller.subredditToSubmitPost.value = Get.arguments[2]; + if(Get.arguments[0]==0) + { + controller.isFromHomeDirect.value=true; + } + else + { + controller.isFromHomeDirect.value=false; + controller.iconOfSubredditToSubmittPost.value=Get.arguments[1] ; + controller.subredditToSubmitPost.value=Get.arguments[2] ; } super.initState(); } @override Widget build(BuildContext context) { - return Obx( - () => WillPopScope( - onWillPop: () async { - final value = (controller.imageFileList!.length > 0 || - controller.videoFile.value != null || - controller.urlPost.value.text != "") - ? await showDialog( - context: context, - builder: (context) { - return AlertDialog( - content: Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(20), - ), - width: MediaQuery.of(context).size.width / 0.001, - height: MediaQuery.of(context).size.height / 10, - child: (Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Text( - 'Discard Post Submission?', - style: TextStyle( - fontSize: 17, fontWeight: FontWeight.w500), - ), - SizedBox( - height: 10, - ), - Row( - children: [ - Expanded( - child: ElevatedButton( - onPressed: () { - Get.back(); - }, - style: ElevatedButton.styleFrom( - backgroundColor: Colors.grey[200], - shape: RoundedRectangleBorder( - borderRadius: - BorderRadius.circular(30), - ), - ), - child: const Text( - 'Cancel', - style: TextStyle( - color: Colors.grey, - fontWeight: FontWeight.w600, - ), - ), + double ScreenSizeWidth = MediaQuery.of(context).size.width; + + final isDesktop = ScreenSizeWidth >= 700; + final isMobile = ScreenSizeWidth < 700; + if (isDesktop){ + return Obx(()=> + Scaffold( + backgroundColor: const Color(0xA2D4E4FA), + appBar: PreferredSize( preferredSize: Size(700, 60),child: UpBar(controller: controllerForHome, controllerForCreatePost: controller,)), + body:SingleChildScrollView( + scrollDirection: Axis.vertical, + child: Column( + children: [ + SizedBox(height: 70,), + Padding( + padding: const EdgeInsetsDirectional.only(end: 600.0), + child: Text("Create a Post",style: TextStyle(fontSize: 18,fontWeight: FontWeight.bold),), + ), + SizedBox(height: 10,), + Padding( + padding: const EdgeInsetsDirectional.only(end: 600.0), + child: Container( + width: 300, + height: 50, + color: Colors.white, + child:PopupMenuButton( + + child: Row( + children: [ + // Icon(Icons.home_filled), + SizedBox(width: 20,), + Text("choose a community"), + SizedBox(width: 80,), + Icon(Icons.arrow_drop_down_sharp) + ], + ), + elevation: 0, + color: Colors.white, + offset:Offset(0,35) , + itemBuilder: (context)=>[ + PopupMenuItem( + value:1, + child: Container( + width: 300, + child: Column( + children:List.generate(controller.moderatedSubreddits.length, (index) => CommunityContainer(nameOfSubreddit: controller.moderatedSubreddits[index].subredditName!, iconOfSubreddit: controller.moderatedSubreddits[index].icon!)) ), - ), - SizedBox( - width: 5, - ), - Expanded( - child: ElevatedButton( - onPressed: () { - controller.textPost.value.clear(); - controller.postTitle.value.clear(); - controller.imageFileList!.clear(); - controller.videoFile.value = null; - controller.videoController.value = null; - controller.urlPost.value.clear(); - Get.to(HomeLayoutScreen()); - }, - style: ElevatedButton.styleFrom( - backgroundColor: Colors.red[700], - shape: RoundedRectangleBorder( - borderRadius: - BorderRadius.circular(30), - ), - ), - child: const Text( - 'Discard', - style: TextStyle( - color: Colors.white, - fontWeight: FontWeight.w600, - ), - ), + ) + + ), + PopupMenuItem( + value:2, + child: Container( + width: 300, + child: Column( + children:List.generate(controller.subscribedSubreddits.length, (index) => CommunityContainer(nameOfSubreddit: controller.subscribedSubreddits[index].subredditName!, iconOfSubreddit: controller.subscribedSubreddits[index].icon!)) ), - ), - ], - ) + ) + + ), ], - ))), - ); - }) - : null; - - if (value != null) { - return Future.value(value); - } else { - return Future.value(true); - } - }, - child: Scaffold( - backgroundColor: Colors.white, - body: Form( - key: controller.formKey, - child: Column( - children: [ - const SizedBox(height: 40.0), - Row( - children: [ - IconButton( - onPressed: () { - // Get.delete(); - Get.back(); - }, - color: Colors.black45, - icon: Icon( - Icons.close, - size: 32.0, + ) ), ), - const Spacer(), + SizedBox(height: 20,), Padding( - padding: const EdgeInsetsDirectional.only(end: 20.0), - child: controller.isLoading.value - ? const CircularProgressIndicator( - color: Colors.blue, - ) - : MaterialButton( - onPressed: () { - if (controller.formKeyUrl.currentState! - .validate() && - controller.postTitle.value.text != "") { - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar( - content: Text('Processing Data')), - ); - if (controller.isFromHomeDirect.value == - true) { - Get.to(BuildSubreddit()); - } else { - Get.to(FinalPost()); - } - } - }, - elevation: 0.0, - height: 35.0, - minWidth: 80.0, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(20.0)), - color: controller.postTitle.value.text == "" - ? Colors.grey[100] - : Colors.blue[900], - child: Text( - "Next", - style: TextStyle( - color: Colors.grey[400], + padding: const EdgeInsetsDirectional.only(start: 150), + child: Row( + children: [ + + InkWell( + onTap: (){ + controller.typeOfPost.value = "self"; + controller.typeOfPost.update((val) { }); + + }, + child: Container( + padding: EdgeInsetsDirectional.only(start: 10), + height: 50, + width: 100, + decoration: BoxDecoration( + color: Colors.white, + border: Border.all(color: Colors.black,width: 0.5) + ), + child: Center( + + child: Row( + children: [ + Icon( + Icons.post_add, + color: Colors.black, + ), + const SizedBox(width: 3,), + Text( + "Post", + style: TextStyle( + color: Colors.black + ) + ) + ], ), ), ), - ), - ], - ), - SizedBox( - height: 10, - ), - Visibility( - visible: !controller.isFromHomeDirect.value, - child: Padding( - padding: const EdgeInsetsDirectional.only(start: 14), - child: Row( - children: [ - GestureDetector( - onTap: () { - Get.to(BuildSubreddit()); - }, - child: Row( - children: [ - CircleAvatar( - backgroundImage: NetworkImage( - '${controller.iconOfSubredditToSubmittPost}'), - radius: 16.0, - ), - SizedBox( - width: 4.0, + ), + InkWell( + onTap: (){ + controller.imageFileList!.clear(); + selectImages(); + print( + "lenght of list is ${controller.imageFileList!.length}"); + controller.typeOfPost.value = "image"; + }, + child: Container( + padding: EdgeInsetsDirectional.only(start: 10), + height: 50, + width: 180, + decoration: BoxDecoration( + color: Colors.white, + border: Border.all(color: Colors.black,width: 0.5) ), - ElevatedButton.icon( - onPressed: () { - Get.to(BuildSubreddit()); - }, - icon: Text( - controller.subredditToSubmitPost.value, - style: const TextStyle(color: Colors.black), + child: Center( + + child: Row( + children: [ + Icon( + Icons.camera_alt, + color: Colors.black, + ), + const SizedBox(width: 3,), + Text( + "Images ", + style: TextStyle( + color: Colors.black + ) + ) + ], ), - label: const Icon( - IconBroken.Arrow___Down_2, - color: Colors.black, - size: 15, + ), + ), + ), + InkWell( + onTap: (){ + controller.videoFile.value=null; + controller.videoController.value=null; + controller.getVideo(); + controller.typeOfPost.value = "video"; + }, + child: Container( + padding: EdgeInsetsDirectional.only(start: 10), + height: 50, + width: 180, + decoration: BoxDecoration( + color: Colors.white, + border: Border.all(color: Colors.black,width: 0.5) + ), + child: Center( + + child: Row( + children: [ + Icon( + Icons.video_camera_back_outlined, + color: Colors.black, + ), + const SizedBox(width: 3,), + Text( + "Video", + style: TextStyle( + color: Colors.black + ) + ) + ], ), - style: ButtonStyle( - elevation: - MaterialStateProperty.all(0), - backgroundColor: - MaterialStateProperty.all(Colors.white), + ), + ), + ), + InkWell( + onTap: (){ + controller.typeOfPost.value = "link"; + controller.typeOfPost.update((val) { }); + }, + child: Container( + padding: EdgeInsetsDirectional.only(start: 10), + height: 50, + width: 100, + decoration: BoxDecoration( + color: Colors.white, + border: Border.all(color: Colors.black,width: 0.5) + ), + child: Center( + + child: Row( + children: [ + Icon( + Icons.link, + color: Colors.black, + ), + const SizedBox(width: 3,), + Text( + "Link", + style: TextStyle( + color: Colors.black + ) + ) + ], ), ), - ], + ), ), - ), - const Spacer(), - TextButton( - onPressed: () => showModalBottomSheet( - context: context, - builder: (context) => Center( - child: Column( - mainAxisAlignment: - MainAxisAlignment.center, - children: [ - const Expanded( - child: Center( - child: Text( - "Community Standards"))), - ElevatedButton( - style: ButtonStyle( - backgroundColor: - MaterialStateProperty.all( - Colors.blue[900], - ), - ), - onPressed: () { - Navigator.pop(context); - }, - child: Text( - "understand", - style: TextStyle( - color: Colors.white, - ), - ), - ) - ], - ), - )), - child: const Text( - "Rules", - style: TextStyle(color: Colors.blue), - )) - ], + + ], + ), ), - ), - ), - const SizedBox( - height: 10, - ), - Padding( - padding: const EdgeInsetsDirectional.only(start: 10.0), - child: TextFormField( - onChanged: (value) { - controller.postTitle.refresh(); - }, - validator: ((value) { - if (value == null || value.isEmpty) { - return 'Please enter some text'; - } - return null; - }), - controller: controller.postTitle.value, - enabled: true, - style: const TextStyle( - fontSize: 20.0, fontWeight: FontWeight.w700), - showCursor: true, - cursorColor: Colors.blue, - cursorHeight: 20.0, - textAlign: TextAlign.start, - decoration: const InputDecoration( - hintText: "An interesting title", - border: InputBorder.none), - ), - ), - const SizedBox( - height: 10.0, - ), - const SizedBox( - height: 10.0, - ), - Expanded( - child: BuildFormType( - controller: controller, - )), - MaterialButton( - child: Text("press"), - onPressed: () { - print(controller.textPost.value.document - .toDelta() - .toJson()); - print( - "==============================================================="); - print((DeltaToHTML.encodeJson(controller - .textPost.value.document - .toDelta() - .toJson()))); - print((jsonEncode(controller.textPost.value.document - .toDelta() - .toJson())) - .toString() - .runtimeType); - print(controller.textPost.value.getPlainText); - print(controller.textPost.value.getPlainText()); - print(controller.textPost.value.document.toPlainText()); - print(controller.textPost.value.document); - print(controller.urlPost.value.text); - print(controller.textPost.value.document - .toPlainText() - .runtimeType); - print(controller.textPost.value.document.runtimeType); - print(controller.textPost.value.runtimeType); - }), - Padding( - padding: - const EdgeInsetsDirectional.only(start: 20, bottom: 10), - child: Row( - children: [ - IconButton( - onPressed: () { - controller.typeOfPost.value = "self"; - }, - icon: const Icon(IconBroken.Document), + SizedBox(height: 10,), + Container( + width: 1000, + height: 50, + decoration: BoxDecoration( + border: Border.all(color: Colors.black), + color: Colors.white, ), - IconButton( - onPressed: () async { - controller.imageFileList!.clear(); - selectImages(); - print( - "lenght of list is ${controller.imageFileList!.length}"); - controller.typeOfPost.value = "image"; - }, - icon: const Icon(IconBroken.Image_2)), - IconButton( - onPressed: () { - controller.videoFile.value = null; - controller.videoController.value = null; - controller.getVideo(); - controller.typeOfPost.value = "video"; + + child:TextFormField( + onChanged: (value) { + controller.postTitle.refresh(); }, - icon: const Icon(IconBroken.Video), + validator: ((value) { + if (value == null || value.isEmpty) { + return 'Please enter some text'; + } + return null; + }), + controller: controller.postTitle.value, + enabled: true, + style: const TextStyle( + fontSize: 20.0, fontWeight: FontWeight.w700), + showCursor: true, + cursorColor: Colors.blue, + cursorHeight: 20.0, + textAlign: TextAlign.start, + decoration: const InputDecoration( + hintText: "Title", + border:OutlineInputBorder( + borderSide: BorderSide(width: 20,color: Colors.black) + )), ), - IconButton( - onPressed: () { - controller.typeOfPost.value = "link"; - }, - icon: const Icon(IconBroken.Bookmark), + ), + SizedBox(height: 20,), + BuildFormTypeWeb(controller:controller), + SizedBox(height: 10,), + Padding( + padding: const EdgeInsetsDirectional.only(start: 20.0), + child: Row( + children: [ + /// Spoiler + InkWell( + onTap: (){ + if (controller.isPostSpoiler.value == false) { + controller.isPostSpoiler.value = true; + controller.isPostSpoiler.refresh(); + } else { + controller.isPostSpoiler.value = false; + controller.isPostSpoiler.refresh(); + } + + }, + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 20,vertical: 5), + decoration: BoxDecoration( + color:(controller.isPostSpoiler.value==true)? Colors.red:Colors.white, + borderRadius: BorderRadius.circular(30), + border: Border.all( + color: Colors.grey + ) + ), + child: Row( + children: [ + Icon(Icons.add, + color: Colors.black, + size: 20,), + const SizedBox(width: 5,), + Text( + "Spoiler", + style:TextStyle(color: Colors.black) + ) + ], + ), + ), + ), + const SizedBox(width: 5,), + /// NSFW + InkWell( + onTap: (){ + if (controller.isPostNSFW.value == false) { + controller.isPostNSFW.value = true; + controller.isPostNSFW.refresh(); + } else { + controller.isPostNSFW.value = false; + controller.isPostNSFW.refresh(); + } + }, + + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 20,vertical: 5), + decoration: BoxDecoration( + color:(controller.isPostNSFW.value==true)? Colors.red:Colors.white, + borderRadius: BorderRadius.circular(30), + border: Border.all( + color: Colors.black + ) + ), + child: Row( + children: const [ + Icon(Icons.add, + color: Colors.black,size: 20,), + SizedBox(width: 5,), + Text( + "NSFW", + style: TextStyle(color: Colors.black), + ) + ], + ), + ), + ), + const SizedBox(width: 700,), + InkWell( + onTap: (){ + if(controller.postTitle.value.text!=""&&controller.formKeyUrl.currentState!.validate()) + { + controller.sendPost(context, + title: controller.postTitle.value.text, + text: (DeltaToHTML.encodeJson(controller.textPost.value.document.toDelta().toJson())).toString(), + kind: controller.typeOfPost.value, + url: controller.urlPost.value.text, + owner: (controller.idOfSubredditToSubmittPost.value), + ownerType:"Subreddit" , + nsfw: controller.isPostNSFW.value, + spoiler: controller.isPostSpoiler.value, + ); + controller.postTitle.value.clear(); + controller.urlPost.value.clear(); + controller.textPost.value.clear(); + controller.isPostSpoiler.value=false; + controller.isPostNSFW.value=false; + // Navigator.pop(context); + Get.to(HomeLayoutScreen()); + } + else return; + }, + + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 20,vertical: 5), + decoration: BoxDecoration( + color:(controller.postTitle.value.text=="")? Colors.white:Colors.blue, + borderRadius: BorderRadius.circular(30), + border: Border.all( + color: Colors.black + ) + ), + child: Row( + children: const [ + SizedBox(width: 5,), + Text( + "Post", + style: TextStyle(color: Colors.black), + ) + ], + ), + ), + ), + ], ), - ], - ), - ), - const SizedBox( - height: 10.0, - ), - const SizedBox( - height: 10.0, + ), + SizedBox(height: 100,) + ], ), - Expanded( - child: BuildFormType( - controller: controller, - )), - Padding( - padding: - const EdgeInsetsDirectional.only(start: 20, bottom: 10), - child: Row( + ) + ), + ); + } + return Obx(()=> + WillPopScope( + onWillPop: () async { + final value = ( controller.imageFileList!.length>0 || controller.videoFile.value != null || controller.urlPost.value.text != "")?await + showDialog(context: context, + builder: (context) + { + return AlertDialog( + content: Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(20), + ), + width: MediaQuery + .of(context) + .size + .width / 0.001, + height: MediaQuery + .of(context) + .size + .height / 10, + child: ( + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text( + 'Discard Post Submission?', + style: TextStyle( + fontSize: 17, fontWeight: FontWeight.w500), + ), + SizedBox(height: 10,), + Row( + children: [ + Expanded( + child: ElevatedButton( + onPressed: () { + Get.back(); + }, + style: ElevatedButton.styleFrom( + backgroundColor: Colors.grey[200], + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular( + 30), + ), + ), + child: + const Text( + 'Cancel', + style: TextStyle( + color: Colors.grey, + fontWeight: FontWeight.w600, + ), + ), + ), + ), + SizedBox(width: 5,), + Expanded( + child: ElevatedButton( + onPressed: () { + controller.textPost.value.clear(); + controller.postTitle.value.clear(); + controller.imageFileList!.clear(); + controller.videoFile.value=null; + controller.videoController.value=null; + controller.urlPost.value.clear(); + Get.to(HomeLayoutScreen()); + }, + style: ElevatedButton.styleFrom( + backgroundColor: Colors.red[700], + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular( + 30), + ), + ), + child: + const Text( + 'Discard', + style: TextStyle( + color: Colors.white, + fontWeight: FontWeight.w600, + ), + ), + ), + ), + ], + ) + ], + ) + ) + ), + ); + } + ) : null ; + + if (value!=null) + { + return Future.value(value); + }else + { + return Future.value(true); + } + }, + child: Scaffold( + backgroundColor: Colors.white, + body: Form( + key: controller.formKey, + child: Column( + children: [ + const SizedBox(height: 40.0), + Row( children: [ IconButton( onPressed: () { - controller.typeOfPost.value = "text"; + // Get.delete(); + Get.back(); }, - icon: const Icon(IconBroken.Document), + color: Colors.black45, icon: Icon( + Icons.close, + size: 32.0, ), - IconButton( - onPressed: () async { - controller.imageFileList!.clear(); - selectImages(); - print( - "lenght of list is ${controller.imageFileList!.length}"); - controller.typeOfPost.value = "image"; - }, - icon: const Icon(IconBroken.Image_2)), - IconButton( - onPressed: () { - controller.videoFile.value = null; - controller.videoController.value = null; - controller.getVideo(); - controller.typeOfPost.value = "video"; - }, - icon: const Icon(IconBroken.Video), ), - IconButton( - onPressed: () { - controller.typeOfPost.value = "url"; - }, - icon: const Icon(IconBroken.Bookmark), + const Spacer(), + Padding( + padding: const EdgeInsetsDirectional.only(end: 20.0), + child: controller.isLoading.value + ? const CircularProgressIndicator( + color: Colors.blue, + ) + : MaterialButton( + onPressed: () { + if ( controller.formKeyUrl.currentState!.validate() && controller.postTitle.value.text !="") { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('Processing Data')), + ); + if(controller.isFromHomeDirect.value==true) + { + Get.to(BuildSubreddit()); + } + else + { + Get.to(FinalPost()); + } + } + }, + elevation: 0.0, + height: 35.0, + minWidth: 80.0, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(20.0)), + color: controller.postTitle.value.text == "" + ? Colors.grey[100] + : Colors.blue[900], + child: Text( + "Next", + style: TextStyle( + color: Colors.grey[400], + ), + ), + ), ), + ], ), - ), - ], + SizedBox(height: 10,), + Visibility( + visible: !controller.isFromHomeDirect.value, + child: Padding( + padding: const EdgeInsetsDirectional.only(start: 14), + child: Row( + children: [ + GestureDetector( + onTap: () + { + Get.to(BuildSubreddit()); + }, + child: Row( + children: [ + CircleAvatar( + backgroundImage: NetworkImage('${controller.iconOfSubredditToSubmittPost}'), + radius: 16.0, + ), + SizedBox( + width: 4.0, + ), + ElevatedButton.icon( + onPressed: () { + Get.to(BuildSubreddit()); + }, + icon: Text( + controller.subredditToSubmitPost.value, + style: const TextStyle(color: Colors.black), + ), + label: const Icon( + IconBroken.Arrow___Down_2, + color: Colors.black, + size: 15, + ), + style: ButtonStyle( + elevation: MaterialStateProperty.all(0), + backgroundColor: + MaterialStateProperty.all(Colors.white), + ), + ), + ], + ), + ), + const Spacer(), + TextButton( + onPressed: () => showModalBottomSheet( + context: context, + builder: (context) => Center( + child: Column( + mainAxisAlignment: + MainAxisAlignment.center, + children: [ + const Expanded( + child: Center( + child: Text( + "Community Standards"))), + ElevatedButton( + style: ButtonStyle( + backgroundColor: + MaterialStateProperty.all( + Colors.blue[900], + ), + ), + onPressed: () { + Navigator.pop(context); + }, + child: Text( + "understand", + style: TextStyle( + color: Colors.white, + ), + ), + ) + ], + ), + )), + child: const Text( + "Rules", + style: TextStyle(color: Colors.blue), + )) + ], + ), + ), + ), + const SizedBox(height: 10,), + Padding( + padding: const EdgeInsetsDirectional.only(start: 10.0), + child: TextFormField( + onChanged: (value) { + controller.postTitle.refresh(); + }, + validator: ((value) { + if (value == null || value.isEmpty) { + return 'Please enter some text'; + } + return null; + }), + controller: controller.postTitle.value, + enabled: true, + style: const TextStyle( + fontSize: 20.0, fontWeight: FontWeight.w700), + showCursor: true, + cursorColor: Colors.blue, + cursorHeight: 20.0, + textAlign: TextAlign.start, + decoration: const InputDecoration( + hintText: "An interesting title", + border: InputBorder.none), + ), + ), + + const SizedBox( + height: 10.0, + ), + + const SizedBox( + height: 10.0, + ), + Expanded(child: BuildFormType(controller: controller,)), + Padding( + padding: const EdgeInsetsDirectional.only(start: 20, bottom: 10), + child: Row( + children: [ + IconButton( + onPressed: () { + controller.typeOfPost.value = "self"; + }, + icon: const Icon(IconBroken.Document), + ), + IconButton( + onPressed: () async { + controller.imageFileList!.clear(); + selectImages(); + print( + "lenght of list is ${controller.imageFileList!.length}"); + controller.typeOfPost.value = "image"; + }, + icon: const Icon(IconBroken.Image_2)), + IconButton( + onPressed: () { + controller.videoFile.value=null; + controller.videoController.value=null; + controller.getVideo(); + controller.typeOfPost.value = "video"; + }, + icon: const Icon(IconBroken.Video), + ), + IconButton( + onPressed: () { + controller.typeOfPost.value = "link"; + }, + icon: const Icon(IconBroken.Bookmark), + ), + ], + ), + ), + + + + ], + ), ), ), ), - ), ); } - @override void dispose() { // TODO: implement dispose Get.delete(); super.dispose(); } - void selectImages() async { - final List? selectedImages = await imagePicker.pickMultiImage(); + final List? selectedImages = (await imagePicker.pickMultiImage()).cast(); if (selectedImages!.isNotEmpty) { controller.imageFileList!.addAll(selectedImages); } diff --git a/lib/createpost/screens/finalpost.dart b/lib/createpost/screens/finalpost.dart index e1f60995..f2c1673b 100644 --- a/lib/createpost/screens/finalpost.dart +++ b/lib/createpost/screens/finalpost.dart @@ -1,5 +1,8 @@ +import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:get/get.dart'; +import 'package:get/get_core/src/get_main.dart'; import 'package:hexcolor/hexcolor.dart'; import 'package:post/createpost/model/send_post_model.dart'; import '../../delta_to_html.dart'; @@ -8,6 +11,7 @@ import '../../icons/icon_broken.dart'; import '../controllers/posts_controllers.dart'; import 'package:responsive_sizer/responsive_sizer.dart'; +import '../services/post_services.dart'; import 'flair_list.dart'; class FinalPost extends StatefulWidget { @@ -17,347 +21,331 @@ class FinalPost extends StatefulWidget { class _FinalPostState extends State { // const FinalPost({Key? key}) : super(key: key); - final PostController controller = Get.put(PostController(), permanent: false); - + final PostController controller = Get.put( + PostController(),permanent: false + ); + final services = PostServices(); @override Widget build(BuildContext context) { return Obx(() => Scaffold( - backgroundColor: Colors.white, - body: Padding( - padding: EdgeInsets.only(top: MediaQuery.of(context).padding.top), - child: Column( + backgroundColor: Colors.white, + body: Padding( + padding: EdgeInsets.only(top: MediaQuery.of(context).padding.top), + child: Column( + children: [ + // SizedBox(height: 40.0), + Row( children: [ - // SizedBox(height: 40.0), - Row( - children: [ - Padding( - padding: const EdgeInsetsDirectional.only(start: 10.0), - child: IconButton( - color: Colors.black, - onPressed: () { - Get.back(); - }, - icon: Icon(IconBroken.Arrow___Left_2, size: 32.0), - ), - ), - SizedBox( - width: 250.0, - ), - Padding( - padding: EdgeInsetsDirectional.only(end: 1.w), - child: MaterialButton( - onPressed: () { - print(controller.postTitle.value.text); - print((controller.postTitle.value.text as String) - .runtimeType); - print(controller.typeOfPost.value); - print((DeltaToHTML.encodeJson(controller - .textPost.value.document - .toDelta() - .toJson())) - .toString()); - print(controller.urlPost.value.text); - print(controller.idOfSubredditToSubmittPost.value); - print((controller.subredditToSubmitPost.value == - "Myprofile") - ? "User" - : "Subreddit"); - print(controller.isPostNSFW.value); - print(controller.isPostSpoiler.value); - print("send replies ->true"); - print("flair id ${controller.idOfFlair.value}"); - print( - "text of flair ${controller.textOfFlair.value}"); - print("suggested sort hot"); - print("scheduled false"); - print("print data on submit"); - controller.sendPost(context); - controller.postTitle.value.clear(); - controller.urlPost.value.clear(); - controller.textPost.value.clear(); - controller.isPostSpoiler.value = false; - controller.isPostNSFW.value = false; - // Navigator.pop(context); - Get.to(HomeLayoutScreen()); - }, - elevation: 0.0, - height: 40.0, - minWidth: 80.0, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(20.0)), - color: Colors.blue[900], - child: Text( - "post", - style: TextStyle( - color: Colors.white, - ), - ), - ), - ) - ], - ), - SizedBox( - height: 25.0, - ), Padding( - padding: const EdgeInsetsDirectional.only(start: 20.0), - child: Row( - children: [ - Row( - children: [ - CircleAvatar( - backgroundImage: NetworkImage( - '${controller.iconOfSubredditToSubmittPost}'), - radius: 16.0, - ), - SizedBox( - width: 4.0, - ), - ElevatedButton.icon( - onPressed: () { - Get.back(); - controller.isPostNSFW.value = false; - controller.isPostSpoiler.value = false; - }, - icon: Text( - controller.subredditToSubmitPost.value, - style: TextStyle(color: Colors.black), - ), - label: Icon( - IconBroken.Arrow___Down_2, - color: Colors.black, - size: 15, - ), - style: ButtonStyle( - elevation: MaterialStateProperty.all(0), - backgroundColor: - MaterialStateProperty.all(Colors.white), - ), - ), - ], - ), - Spacer(), - TextButton( - onPressed: () => showModalBottomSheet( - context: context, - builder: (context) => Center( - child: Column( - mainAxisAlignment: - MainAxisAlignment.center, - children: [ - Expanded( - child: Center( - child: Text( - "Community Standards"))), - ElevatedButton( - style: ButtonStyle( - backgroundColor: - MaterialStateProperty.all( - Colors.blue[900], - ), - ), - onPressed: () { - Navigator.pop(context); - }, - child: Text( - "understand", - style: TextStyle( - color: Colors.white, - ), - ), - ) - ], - ), - )), - child: Text( - "Rules", - style: TextStyle(color: Colors.blue), - )) - ], + padding: const EdgeInsetsDirectional.only(start: 10.0), + child: IconButton( + + color: Colors.black, onPressed: () { + Get.back(); + }, icon:Icon( IconBroken.Arrow___Left_2,size: 32.0), ), ), + SizedBox( + width: 250.0, + ), Padding( - padding: const EdgeInsets.all(16.0), - child: TextFormField( - onChanged: (value) { - controller.postTitle.value; + padding: EdgeInsetsDirectional.only(end: 1.w), + child: MaterialButton( + onPressed: () { + print( controller.postTitle.value.text); + print(( controller.postTitle.value.text as String).runtimeType); + print(controller.typeOfPost.value); + print((DeltaToHTML.encodeJson(controller.textPost.value.document.toDelta().toJson())).toString()); + print(controller.urlPost.value.text); + print(controller.idOfSubredditToSubmittPost.value); + print((controller.subredditToSubmitPost.value == "Myprofile")?"User":"Subreddit"); + print(controller.isPostNSFW.value); + print(controller.isPostSpoiler.value); + print("send replies ->true"); + print("flair id ${controller.idOfFlair.value}"); + print("text of flair ${controller.textOfFlair.value}"); + print("suggested sort hot"); + print("scheduled false"); + print("print data on submit"); + Future str; + if(controller.idOfFlair.value=="") + { + str=controller.sendPost(context, + title: controller.postTitle.value.text, + text: (DeltaToHTML.encodeJson( + controller.textPost.value.document.toDelta() + .toJson())).toString(), + kind: controller.typeOfPost.value, + url: controller.urlPost.value.text, + owner: (controller.idOfSubredditToSubmittPost + .value), + ownerType: controller.subredditToSubmitPost.value=="Myprofile"?"User":"Subreddit", + nsfw: controller.isPostNSFW.value, + spoiler: controller.isPostSpoiler.value, + ); + } + else + { + str= controller.sendPostWithFlair(context, + title: controller.postTitle.value.text, + text: (DeltaToHTML.encodeJson( + controller.textPost.value.document.toDelta() + .toJson())).toString(), + kind: controller.typeOfPost.value, + url: controller.urlPost.value.text, + owner: (controller.idOfSubredditToSubmittPost + .value), + ownerType:controller.subredditToSubmitPost.value=="Myprofile"?"User":"Subreddit", + nsfw: controller.isPostNSFW.value, + spoiler: controller.isPostSpoiler.value, textFlair: controller.textOfFlair.value, idFlair: controller.idOfFlair.value, + ); + } + if(controller.typeOfPost.value=="image") + { + for(int i=0;i(0), - fixedSize: - MaterialStateProperty.all(Size(130.0, 20.0)), - backgroundColor: (controller.isPostNSFW.value) - ? MaterialStateProperty.all( - Colors.black87, - ) - : MaterialStateProperty.all( - Colors.grey[100], - ), - ), - label: Text( - "NSFW", - style: TextStyle( - color: Colors.grey[400], - ), + backgroundColor: + MaterialStateProperty.all(Colors.white), ), - icon: Text( - "18", - style: TextStyle( - color: Colors.grey[400], - ), - )), - SizedBox( - width: 10.0, - ), - ElevatedButton.icon( - onPressed: () { - if (controller.isPostSpoiler.value == false) { - controller.isPostSpoiler.value = true; - controller.isPostSpoiler.refresh(); - } else { - controller.isPostSpoiler.value = false; - controller.isPostSpoiler.refresh(); - } - }, - style: ButtonStyle( - elevation: MaterialStateProperty.all(0), - fixedSize: MaterialStateProperty.all(Size(122.0, 20.0)), - backgroundColor: (controller.isPostSpoiler.value) - ? MaterialStateProperty.all( - Colors.black87, - ) - : MaterialStateProperty.all( - Colors.grey[100], - ), ), - label: Text( - "Spoiler", - style: TextStyle( - color: Colors.grey[400], - ), + ], + ), + Spacer(), + TextButton( + onPressed: () => showModalBottomSheet( + context: context, + builder: (context) => Center( + child: Column( + mainAxisAlignment: + MainAxisAlignment.center, + children: [ + Expanded( + child: Center( + child: Text( + "Community Standards"))), + ElevatedButton( + style: ButtonStyle( + backgroundColor: + MaterialStateProperty.all( + Colors.blue[900], + ), + ), + onPressed: () { + Navigator.pop(context); + }, + child: Text( + "understand", + style: TextStyle( + color: Colors.white, + ), + ), + ) + ], + ), + )), + child: Text( + "Rules", + style: TextStyle(color: Colors.blue), + )) + ], + ), + ), + Padding( + padding: const EdgeInsets.all(16.0), + child: TextFormField( + onChanged: (value) { + controller.postTitle.value; + }, + controller: controller.postTitle.value, + readOnly: true, + style: TextStyle(fontSize: 20.0), + maxLines: 2, + decoration: InputDecoration( + contentPadding: const EdgeInsets.all(10.0), + border: OutlineInputBorder( + borderSide: BorderSide(color: Colors.black), + borderRadius: BorderRadius.circular(50.0), + )), + ), + ), + Row( + children: [ + SizedBox( + width: 20.0, + ), + ElevatedButton.icon( + onPressed: () { + if (controller.isPostNSFW.value == false) { + controller.isPostNSFW.value = true; + controller.isPostNSFW.refresh(); + } else { + controller.isPostNSFW.value = false; + controller.isPostNSFW.refresh(); + } + }, + style: ButtonStyle( + elevation: MaterialStateProperty.all(0), + fixedSize: + MaterialStateProperty.all(Size(130.0, 20.0)), + backgroundColor: (controller.isPostNSFW.value) + ? MaterialStateProperty.all( + Colors.black87, + ) + : MaterialStateProperty.all( + Colors.grey[100], ), - icon: Icon( - IconBroken.Danger, + ), + label: Text( + "NSFW", + style: TextStyle( color: Colors.grey[400], ), ), - ], - ), + icon: Text( + "18", + style: TextStyle( + color: Colors.grey[400], + ), + )), SizedBox( - height: 8.0, + width: 10.0, ), - Obx( - () => Visibility( - visible: (controller.flairsOfSubreddit.length > 0) - ? true - : false, - child: ListTile( - onTap: () { - Get.to(FlairList()); - }, - horizontalTitleGap: 0.0, - title: Text( - controller.textOfFlair.isEmpty - ? "Add Flar" - : controller.textOfFlair.value, - style: TextStyle( - color: controller.textColorOfFlair.value == "None" - ? Colors.black87 - : HexColor(controller.textColorOfFlair.value), - backgroundColor: - controller.backgroundColorOfFlair.value == "None" - ? Colors.white - : HexColor( - controller.backgroundColorOfFlair.value), - ), - ), - // Text("Add flair"), - leading: Icon(IconBroken.Edit), - trailing: Icon(IconBroken.Arrow___Right_2), + ElevatedButton.icon( + onPressed: () { + if (controller.isPostSpoiler.value == false) { + controller.isPostSpoiler.value = true; + controller.isPostSpoiler.refresh(); + } else { + controller.isPostSpoiler.value = false; + controller.isPostSpoiler.refresh(); + } + }, + style: ButtonStyle( + elevation: MaterialStateProperty.all(0), + fixedSize: MaterialStateProperty.all(Size(122.0, 20.0)), + backgroundColor: (controller.isPostSpoiler.value) + ? MaterialStateProperty.all( + Colors.black87, + ) + : MaterialStateProperty.all( + Colors.grey[100], ), ), - ), - const Divider( - height: 10.0, - color: Colors.grey, + label: Text( + "Spoiler", + style: TextStyle( + color: Colors.grey[400], + ), + ), + icon: Icon( + IconBroken.Danger, + color: Colors.grey[400], + ), ), ], ), - ), - )); - } - - savePost() async { - print(controller.postTitle.value.text); - print(controller.typeOfPost.value); - print((DeltaToHTML.encodeJson( - controller.textPost.value.document.toDelta().toJson())) - .toString()); - print(controller.urlPost.value.text); - print(controller.idOfSubredditToSubmittPost.value); - print((controller.subredditToSubmitPost.value == "Myprofile") - ? "User" - : "Subreddit"); - print(controller.isPostNSFW.value); - print(controller.isPostSpoiler.value); - print("send replies ->true"); - print("flair id ${controller.idOfFlair.value}"); - print("text of flair ${controller.textOfFlair.value}"); - print("suggested sort hot"); - print("scheduled false"); - - final model = SendPostModel( - title: controller.postTitle.value.text, - kind: controller.typeOfPost.value, - text: (DeltaToHTML.encodeJson( - controller.textPost.value.document.toDelta().toJson())) - .toString(), - url: (controller.typeOfPost.value == "link") - ? controller.urlPost.value.text - : "", - owner: controller.idOfSubredditToSubmittPost.value, - ownerType: (controller.subredditToSubmitPost.value == "Myprofile") - ? "User" - : "Subreddit", - nsfw: controller.isPostNSFW.value, - spoiler: controller.isPostSpoiler.value, - sendReplies: true, - flairId: controller.idOfFlair.value, - flairText: controller.textOfFlair.value, - suggestedSort: "hot", - scheduled: false, - ); - print("AFTER TO JASON ${model.toJson()}"); - Get.to(HomeLayoutScreen()); + SizedBox( + height: 8.0, + ), + Obx(()=> + Visibility( + visible: (controller.flairsOfSubreddit.length>0)?true:false, + child: ListTile( + onTap: () + { + Get.to(FlairList()); + }, + horizontalTitleGap: 0.0, + title:Text(controller.textOfFlair.isEmpty?"Add Flar": + controller.textOfFlair.value, + style: TextStyle( + color: controller.textColorOfFlair.value=="None"? + Colors.black87: + HexColor(controller.textColorOfFlair.value), + backgroundColor: controller.backgroundColorOfFlair.value=="None"? + Colors.white: + HexColor(controller.backgroundColorOfFlair.value), + ), + ), + // Text("Add flair"), + leading: Icon(IconBroken.Edit), + trailing: Icon(IconBroken.Arrow___Right_2), + ), + ), + ), + const Divider( + height: 10.0, + color: Colors.grey, + ), + ], + ), + ), + )); } - @override void dispose() { // TODO: implement dispose diff --git a/lib/createpost/services/post_services.dart b/lib/createpost/services/post_services.dart index bfaa06b3..0f821bfb 100644 --- a/lib/createpost/services/post_services.dart +++ b/lib/createpost/services/post_services.dart @@ -1,3 +1,5 @@ +import 'dart:io'; + import 'package:dio/dio.dart'; import 'package:flutter/material.dart'; import 'package:image_picker/image_picker.dart'; @@ -5,10 +7,8 @@ import 'package:shared_preferences/shared_preferences.dart'; import '../../networks/const_endpoint_data.dart'; import '../../networks/dio_client.dart'; import '../model/send_post_model.dart'; - -class PostServices { - // were final instead of static var - +class PostServices +{ static var dio = Dio(); //static var client =http.Client(); sendPost(SendPostModel model, BuildContext context) async { @@ -16,29 +16,27 @@ class PostServices { DioClient.init(prefs); try { final response = - await DioClient.post(path: createPost, data: model.toJson()); + await DioClient.post(path: createPost, data: model.toJson()); if (response.statusCode == 200 || response.statusCode == 201) { print("response of sending post ${response.data}"); ScaffoldMessenger.of(context).showSnackBar( - SnackBar( + const SnackBar( content: Text("sent successfuly", style: TextStyle(color: Colors.white)), backgroundColor: Colors.green), ); - - // print(response.statusCode); - // print(json.decode(response.data)['message']); } } catch (e) { print("error in sending the post -> $e"); } } - - Future uploadImage(XFile file, String id) async { + Future uploadImage(File file,Future id) async { String fileName = file.path.split('/').last; FormData formData = FormData.fromMap({ - "fileName": await MultipartFile.fromFile(file.path, filename: fileName), + "file": + await MultipartFile.fromFile(file.path, filename:fileName), }); final response = await dio.post("/posts/$id/images", data: formData); } + } diff --git a/lib/createpost/widgets/type_of_post_web.dart b/lib/createpost/widgets/type_of_post_web.dart new file mode 100644 index 00000000..a94f4a1e --- /dev/null +++ b/lib/createpost/widgets/type_of_post_web.dart @@ -0,0 +1,265 @@ +import 'dart:io'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_quill/flutter_quill.dart' as quill; +import 'package:get/get.dart'; +import 'package:get/get_state_manager/src/rx_flutter/rx_obx_widget.dart'; +// import 'package:html_editor_enhanced/html_editor.dart'; +import 'package:video_player/video_player.dart'; +import '../../createpost/controllers/posts_controllers.dart'; + +class BuildFormTypeWeb extends StatelessWidget { + BuildFormTypeWeb({ + Key? key, + required this.controller, + }) : super(key: key); + + final PostController controller; + @override + Widget build(BuildContext context) { + + return Obx( + () => Form( + key: controller.formKeyUrl, + child: Padding( + padding: const EdgeInsetsDirectional.only(start: 10.0), + child: controller.typeOfPost.value == "link" + ? Padding( + padding: const EdgeInsetsDirectional.only(start: 10.0), + child: Container( + width: 1000, + height: 50, + decoration: BoxDecoration( + border: Border.all(color: Colors.black), + color: Colors.white, + ), + + child: TextFormField( + keyboardType: TextInputType.url, + // inputFormatters: [ + // FilteringTextInputFormatter.allow( + // RegExp(r'^[a-zA-Z0-9_\-=@,\.;]+$')) + // ], + onChanged: (value) { + controller.urlPost.refresh(); + }, + validator: (value) { + // String pattern = + // r"((https?:www\.)|(https?:\/\/)|(www\.))[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9]{1,6}(\/[-a-zA-Z0-9()@:%_\+.~#?&\/=]*)?"; + RegExp regExp = RegExp( + "((http|https)://)(www.)?[a-zA-Z0-9@:%._\\+~#?&//=]{2,256}\\.[a-z]{2,6}\\b([-a-zA-Z0-9@:%._\\+~#?&//=]*)"); + if (value!.isEmpty) { + return "Please enter your website"; + } else if (!(regExp.hasMatch(value))) { + return "Website Url must be started from www"; + } else { + return null; + } + }, + controller: controller.urlPost.value, + enabled: true, + style: const TextStyle(fontSize: 14.0), + showCursor: true, + cursorColor: Colors.blue, + cursorHeight: 20.0, + toolbarOptions: const ToolbarOptions( + copy: true, cut: true, paste: true), + textAlign: TextAlign.start, + decoration: const InputDecoration( + hintText: "URL", border: InputBorder.none + //onfieldsubmitted : (string value ) {}دي بتاخد انونيمس فانكشن عشان لو اما اضغط علي سابمت يعمل اكشن معين + ), + ), + ), + ) + : controller.typeOfPost.value == "image" + ? (controller.imageFileList!.length == 0) + ? SizedBox() + : Container( + width: 1000, + height: 500, + decoration: BoxDecoration( + border: Border.all(color: Colors.black), + color: Colors.white, + ), + + child: ListView.separated( + physics: BouncingScrollPhysics(), + scrollDirection: Axis.horizontal, + separatorBuilder: (BuildContext context, index) => + const SizedBox( + width: 20, + ), + itemBuilder: (BuildContext context, index) => + SizedBox( + height: 250, + width: 120, + child: Stack( + //alignment: AlignmentDirectional.topEnd, + children: [ + SizedBox( + height: 200, + width: 100, + ), + Container( + width: 100, + height: 150, + child: Image.file( + File(controller + .imageFileList![index].path), + fit: BoxFit.cover, + )), + Positioned( + top: -20, + right: -15, + child: IconButton( + onPressed: () { + controller.imageFileList! + .removeAt(index); + }, + icon: Icon( + Icons.close, + color: Colors.black, + size: 30, + )), + ), + ], + ), + ), + itemCount: controller.imageFileList!.length, + ), + ) + : (controller.typeOfPost == "video") + ? (controller.videoController.value != null) + ? Container( + width: 1000, + height: 500, + decoration: BoxDecoration( + border: Border.all(color: Colors.black), + color: Colors.white, + ), + + child: FutureBuilder( + future: controller.initializeVideoPlayerFuture, + builder: (context, snapshot) { + if (snapshot.connectionState == + ConnectionState.done) { + // If the VideoPlayerController has finished initialization, use + // the data it provides to limit the aspect ratio of the video. + return AspectRatio( + aspectRatio: controller.videoController + .value!.value.aspectRatio, + // Use the VideoPlayer widget to display the video. + child: Stack( + alignment: + AlignmentDirectional.topEnd, + children: [ + Stack( + children: [ + VideoPlayer(controller + .videoController.value!), + Center( + child: IconButton( + onPressed: () { + controller.playVideo(); + }, + icon: controller + .videoController + .value! + .value + .isPlaying + ? const Icon( + Icons.pause, + color: Colors.white, + size: 30, + ) + : const Icon( + Icons.play_arrow, + color: Colors.white, + size: 30, + ), + ), + ) + ], + ), + IconButton(onPressed: (){ + controller.videoFile.value=null; + controller.videoController.value=null; + }, icon: Icon(Icons.close)) + ]), + ); + } else { + // If the VideoPlayerController is still initializing, show a + // loading spinner. + return const Center( + child: CircularProgressIndicator()); + } + }, + ), + ) + : SizedBox() + : (controller.typeOfPost == "self") + ? Padding( + padding: const EdgeInsetsDirectional.only( + start: 10.0), + child: Container( + width: 1000, + height: 500, + decoration: BoxDecoration( + border: Border.all(color: Colors.black), + color: Colors.white, + ), + child: Column( + children: [ + quill.QuillToolbar.basic( + multiRowsDisplay: true, + controller: controller.textPost.value, + ), + Expanded( + child: Padding( + padding: const EdgeInsets.all(30.0), + child: quill.QuillEditor( + customStyles: quill.DefaultStyles( + color: Colors.red), + showCursor: true, + paintCursorAboveText: true, + focusNode: FocusNode(), + scrollController: ScrollController(), + scrollable: true, + padding: EdgeInsetsDirectional.only( + start: 10.0), + autoFocus: true, + expands: true, + controller: controller.textPost.value, + readOnly: false, + ), + ), + ) + ], + ), + ) + + // child: TextFormField( + // controller: controller.textPost.value, + // enabled: true, + // style: const TextStyle(fontSize: 16.0), + // showCursor: true, + // cursorColor: Colors.blue, + // cursorHeight: 20.0, + // toolbarOptions: + // const ToolbarOptions(copy: true, cut: true, paste: true), + // keyboardType: TextInputType.multiline, + // textInputAction: TextInputAction.newline, + // autofocus: true, + // maxLines: null, + // textAlign: TextAlign.start, + // decoration: const InputDecoration( + // hintText: "Add optional body text", + // border: InputBorder.none), + // ), + ) + : SizedBox()), + ), + ); + } +} From 39c6dfbd6bdc7d11ecd62d15fe4d85a19f63e39d Mon Sep 17 00:00:00 2001 From: Amr Ahmed Date: Fri, 23 Dec 2022 21:29:50 +0200 Subject: [PATCH 08/16] small changes --- lib/comments/screens/add_comment_screen.dart | 17 +- lib/comments/screens/add_reply_screen.dart | 19 +- lib/comments/widgets/comment.dart | 15 +- lib/comments/widgets/comment_footer.dart | 72 +-- lib/comments/widgets/comment_popup_menu.dart | 2 +- lib/comments/widgets/comments_list.dart | 7 + lib/comments/widgets/view_comment.dart | 4 +- lib/discover/providers/discover_provider.dart | 4 +- lib/main.dart | 3 +- .../widgets/moderated_subreddit_web.dart | 6 +- .../moderated_subreddite_post_web.dart | 9 +- .../other_profile_card_information_web.dart | 11 +- .../widgets/overview_other_profile_web.dart | 7 +- .../widgets/position_other_profile_web.dart | 64 +- lib/post/models/post_model.dart | 24 +- lib/post/widgets/post.dart | 14 +- lib/post/widgets/post_body.dart | 8 +- lib/post/widgets/post_body_in_screen.dart | 39 +- lib/post/widgets/post_card.dart | 6 +- lib/post/widgets/post_comment_list.dart | 144 +++-- lib/post/widgets/post_footer.dart | 6 +- lib/post/widgets/post_header.dart | 10 +- lib/post/widgets/post_link_in_screen.dart | 2 +- lib/post/widgets/post_list.dart | 86 ++- lib/post/widgets/post_mod_popup.dart | 28 +- lib/post/widgets/post_mod_tools.dart | 31 +- ..._popup_menu.dart => post_pop_up_menu.dart} | 9 + lib/post/widgets/post_pop_up_web.dart | 4 +- lib/post/widgets/post_tags_and_title.dart | 12 + lib/post/widgets/post_video_in_widget.dart | 15 + .../widgets/post_video_in_widget_web.dart | 26 +- lib/post/widgets/user_info_popup.dart | 11 +- lib/subreddit/widgets/subreddit_post_web.dart | 8 +- lib/widgets/myprofile_post_web.dart | 179 +++--- lib/widgets/overview_myprofile_web.dart | 221 +++---- lib/widgets/profile_comments.dart | 2 +- linux/.gitignore | 1 - linux/CMakeLists.txt | 138 ----- linux/flutter/CMakeLists.txt | 88 --- linux/flutter/generated_plugin_registrant.cc | 19 - linux/flutter/generated_plugin_registrant.h | 15 - linux/flutter/generated_plugins.cmake | 25 - linux/main.cc | 6 - linux/my_application.cc | 104 ---- linux/my_application.h | 18 - macos/.gitignore | 7 - macos/Flutter/Flutter-Debug.xcconfig | 1 - macos/Flutter/Flutter-Release.xcconfig | 1 - macos/Flutter/GeneratedPluginRegistrant.swift | 30 - macos/Runner.xcodeproj/project.pbxproj | 572 ------------------ .../xcshareddata/IDEWorkspaceChecks.plist | 8 - .../xcshareddata/xcschemes/Runner.xcscheme | 87 --- .../contents.xcworkspacedata | 7 - .../xcshareddata/IDEWorkspaceChecks.plist | 8 - macos/Runner/AppDelegate.swift | 9 - .../AppIcon.appiconset/Contents.json | 68 --- .../AppIcon.appiconset/app_icon_1024.png | Bin 102994 -> 0 bytes .../AppIcon.appiconset/app_icon_128.png | Bin 5680 -> 0 bytes .../AppIcon.appiconset/app_icon_16.png | Bin 520 -> 0 bytes .../AppIcon.appiconset/app_icon_256.png | Bin 14142 -> 0 bytes .../AppIcon.appiconset/app_icon_32.png | Bin 1066 -> 0 bytes .../AppIcon.appiconset/app_icon_512.png | Bin 36406 -> 0 bytes .../AppIcon.appiconset/app_icon_64.png | Bin 2218 -> 0 bytes macos/Runner/Base.lproj/MainMenu.xib | 343 ----------- macos/Runner/Configs/AppInfo.xcconfig | 14 - macos/Runner/Configs/Debug.xcconfig | 2 - macos/Runner/Configs/Release.xcconfig | 2 - macos/Runner/Configs/Warnings.xcconfig | 13 - macos/Runner/DebugProfile.entitlements | 12 - macos/Runner/Info.plist | 32 - macos/Runner/MainFlutterWindow.swift | 15 - macos/Runner/Release.entitlements | 8 - pubspec.lock | 28 + windows/.gitignore | 17 - windows/CMakeLists.txt | 101 ---- windows/flutter/CMakeLists.txt | 104 ---- .../flutter/generated_plugin_registrant.cc | 17 - windows/flutter/generated_plugin_registrant.h | 15 - windows/flutter/generated_plugins.cmake | 25 - windows/runner/CMakeLists.txt | 39 -- windows/runner/Runner.rc | 121 ---- windows/runner/flutter_window.cpp | 61 -- windows/runner/flutter_window.h | 33 - windows/runner/main.cpp | 43 -- windows/runner/resource.h | 16 - windows/runner/resources/app_icon.ico | Bin 33772 -> 0 bytes windows/runner/runner.exe.manifest | 20 - windows/runner/utils.cpp | 64 -- windows/runner/utils.h | 19 - windows/runner/win32_window.cpp | 245 -------- windows/runner/win32_window.h | 98 --- 91 files changed, 575 insertions(+), 3269 deletions(-) rename lib/post/widgets/{post_popup_menu.dart => post_pop_up_menu.dart} (96%) delete mode 100644 linux/.gitignore delete mode 100644 linux/CMakeLists.txt delete mode 100644 linux/flutter/CMakeLists.txt delete mode 100644 linux/flutter/generated_plugin_registrant.cc delete mode 100644 linux/flutter/generated_plugin_registrant.h delete mode 100644 linux/flutter/generated_plugins.cmake delete mode 100644 linux/main.cc delete mode 100644 linux/my_application.cc delete mode 100644 linux/my_application.h delete mode 100644 macos/.gitignore delete mode 100644 macos/Flutter/Flutter-Debug.xcconfig delete mode 100644 macos/Flutter/Flutter-Release.xcconfig delete mode 100644 macos/Flutter/GeneratedPluginRegistrant.swift delete mode 100644 macos/Runner.xcodeproj/project.pbxproj delete mode 100644 macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist delete mode 100644 macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme delete mode 100644 macos/Runner.xcworkspace/contents.xcworkspacedata delete mode 100644 macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist delete mode 100644 macos/Runner/AppDelegate.swift delete mode 100644 macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json delete mode 100644 macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png delete mode 100644 macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png delete mode 100644 macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png delete mode 100644 macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png delete mode 100644 macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png delete mode 100644 macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png delete mode 100644 macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png delete mode 100644 macos/Runner/Base.lproj/MainMenu.xib delete mode 100644 macos/Runner/Configs/AppInfo.xcconfig delete mode 100644 macos/Runner/Configs/Debug.xcconfig delete mode 100644 macos/Runner/Configs/Release.xcconfig delete mode 100644 macos/Runner/Configs/Warnings.xcconfig delete mode 100644 macos/Runner/DebugProfile.entitlements delete mode 100644 macos/Runner/Info.plist delete mode 100644 macos/Runner/MainFlutterWindow.swift delete mode 100644 macos/Runner/Release.entitlements delete mode 100644 windows/.gitignore delete mode 100644 windows/CMakeLists.txt delete mode 100644 windows/flutter/CMakeLists.txt delete mode 100644 windows/flutter/generated_plugin_registrant.cc delete mode 100644 windows/flutter/generated_plugin_registrant.h delete mode 100644 windows/flutter/generated_plugins.cmake delete mode 100644 windows/runner/CMakeLists.txt delete mode 100644 windows/runner/Runner.rc delete mode 100644 windows/runner/flutter_window.cpp delete mode 100644 windows/runner/flutter_window.h delete mode 100644 windows/runner/main.cpp delete mode 100644 windows/runner/resource.h delete mode 100644 windows/runner/resources/app_icon.ico delete mode 100644 windows/runner/runner.exe.manifest delete mode 100644 windows/runner/utils.cpp delete mode 100644 windows/runner/utils.h delete mode 100644 windows/runner/win32_window.cpp delete mode 100644 windows/runner/win32_window.h diff --git a/lib/comments/screens/add_comment_screen.dart b/lib/comments/screens/add_comment_screen.dart index 16ae09ff..f1020e2c 100644 --- a/lib/comments/screens/add_comment_screen.dart +++ b/lib/comments/screens/add_comment_screen.dart @@ -15,9 +15,6 @@ class _AddCommentScreenState extends State { TextEditingController controller = TextEditingController(); @override void didChangeDependencies() { - // TODO: implement didChangeDependencies - //===============================Fetch subreddit data =======================================// - var temp = ModalRoute.of(context)?.settings.arguments as Map; parentId = temp['parentId']; @@ -27,9 +24,7 @@ class _AddCommentScreenState extends State { createComment(String text) async { if (await Provider.of(context, listen: false) - .postComment(parentId, 'Post', text)) { - print('created comment'); - } + .postComment(parentId, 'Post', text)) {} } @override @@ -37,7 +32,7 @@ class _AddCommentScreenState extends State { return Scaffold( appBar: AppBar( leading: IconButton( - icon: Icon( + icon: const Icon( Icons.close, size: 20, ), @@ -45,7 +40,7 @@ class _AddCommentScreenState extends State { Navigator.pop(context); }, ), - title: Text( + title: const Text( "Add a comment", style: TextStyle(fontSize: 20, color: Colors.black), ), @@ -57,7 +52,7 @@ class _AddCommentScreenState extends State { Navigator.pop(context); } : null, - child: Text( + child: const Text( "Post", style: TextStyle( color: Colors.blue, @@ -67,11 +62,11 @@ class _AddCommentScreenState extends State { ], ), body: Padding( - padding: EdgeInsetsDirectional.all(10), + padding: const EdgeInsetsDirectional.all(10), child: Column( children: [ Text(title), - Divider( + const Divider( height: 10, ), Expanded( diff --git a/lib/comments/screens/add_reply_screen.dart b/lib/comments/screens/add_reply_screen.dart index 2d69cd5c..b737df6a 100644 --- a/lib/comments/screens/add_reply_screen.dart +++ b/lib/comments/screens/add_reply_screen.dart @@ -37,9 +37,6 @@ class _AddReplyScreenState extends State { @override void didChangeDependencies() { - // TODO: implement didChangeDependencies - //===============================Fetch subreddit data =======================================// - var temp = ModalRoute.of(context)?.settings.arguments as Map; parentId = temp['parentId']; @@ -52,9 +49,7 @@ class _AddReplyScreenState extends State { createComment(String text) async { if (await Provider.of(context, listen: false) - .postComment(parentId, 'Comment', text)) { - print('created comment'); - } + .postComment(parentId, 'Comment', text)) {} } @override @@ -62,7 +57,7 @@ class _AddReplyScreenState extends State { return Scaffold( appBar: AppBar( leading: IconButton( - icon: Icon( + icon: const Icon( Icons.close, size: 20, ), @@ -70,7 +65,7 @@ class _AddReplyScreenState extends State { Navigator.pop(context); }, ), - title: Text( + title: const Text( "Reply", style: TextStyle(fontSize: 20, color: Colors.black), ), @@ -82,7 +77,7 @@ class _AddReplyScreenState extends State { Navigator.pop(context); } : null, - child: Text( + child: const Text( "Post", style: TextStyle( color: Colors.blue, @@ -92,7 +87,7 @@ class _AddReplyScreenState extends State { ], ), body: Padding( - padding: EdgeInsetsDirectional.all(10), + padding: const EdgeInsetsDirectional.all(10), child: Column( children: [ Row( @@ -125,11 +120,11 @@ class _AddReplyScreenState extends State { ), ], ), - SizedBox( + const SizedBox( height: 10, ), Text(comment), - Divider( + const Divider( height: 10, ), Expanded( diff --git a/lib/comments/widgets/comment.dart b/lib/comments/widgets/comment.dart index f42776fc..ee627a82 100644 --- a/lib/comments/widgets/comment.dart +++ b/lib/comments/widgets/comment.dart @@ -4,9 +4,16 @@ import '../models/comment_model.dart'; import 'comment_body.dart'; import 'comment_header.dart'; +/// A widget to display a comment in tree + class Comment extends StatefulWidget { + /// The data of the commment final CommentModel data; + + /// The userName final String userName; + + /// The level of comment final int level; const Comment( {super.key, required this.data, required this.userName, this.level = 0}); @@ -20,7 +27,7 @@ class _CommentState extends State { @override Widget build(BuildContext context) { return widget.data.type != null - ? Text('more comments') + ? const Text('more comments') : Container( margin: EdgeInsets.only(bottom: widget.level != 0 ? 0 : 10), child: Material( @@ -61,8 +68,8 @@ class _CommentState extends State { text: widget.data.text ?? '', ), CommentFooter( - commentVoteStatus: 1, - votes: 20, + commentVoteStatus: 0, + votes: widget.data.votes ?? 0, data: widget.data, ) ], @@ -87,7 +94,7 @@ class _CommentState extends State { level: widget.level + 1, )) .toList()) - : SizedBox() + : const SizedBox() ]), ), ); diff --git a/lib/comments/widgets/comment_footer.dart b/lib/comments/widgets/comment_footer.dart index 34df3f45..57dbbc42 100644 --- a/lib/comments/widgets/comment_footer.dart +++ b/lib/comments/widgets/comment_footer.dart @@ -27,64 +27,6 @@ class _CommentFooterState extends State { int commentVoteStatus; int votes; _CommentFooterState(this.commentVoteStatus, this.votes); -// upVote() async { -// if (postVoteStatus != 1) { -// if (await Provider.of(context, listen: false) -// .updateVotes(widget.id, 1)) { -// setState(() { -// if (postVoteStatus == -1) { -// votes = votes + 2; -// widget.data.votes = (widget.data.votes! + 2); -// } else { -// ++votes; -// widget.data.votes = (widget.data.votes! + 1); -// } -// postVoteStatus = 1; -// widget.data.postVoteStatus = 1.toString(); -// }); -// } -// } else { -// if (await Provider.of(context, listen: false) -// .updateVotes(widget.id, 0)) { -// setState(() { -// postVoteStatus = 0; -// widget.data.postVoteStatus = 0.toString(); -// widget.data.votes = (widget.data.votes! - 1); -// --votes; -// }); -// } -// } -// } - -// downVote() async { -// if (postVoteStatus != -1) { -// if (await Provider.of(context, listen: false) -// .updateVotes(widget.id, -1)) { -// setState(() { -// if (postVoteStatus == 1) { -// votes = votes - 2; -// widget.data.votes = (widget.data.votes! - 2); -// } else { -// --votes; -// widget.data.votes = (widget.data.votes! - 1); -// } -// postVoteStatus = -1; -// widget.data.postVoteStatus = (-1).toString(); -// }); -// } -// } else { -// if (await Provider.of(context, listen: false) -// .updateVotes(widget.id, 0)) { -// setState(() { -// postVoteStatus = 0; -// widget.data.postVoteStatus = 0.toString(); - -// ++votes; -// widget.data.votes = (widget.data.votes! + 1); -// }); -// } -// } -// } upVote() {} downVote() {} @@ -92,21 +34,17 @@ class _CommentFooterState extends State { @override Widget build(BuildContext context) { return Container( - margin: EdgeInsetsDirectional.only(start: 10, end: 10), + margin: const EdgeInsetsDirectional.only(start: 10, end: 10), child: Row( mainAxisAlignment: MainAxisAlignment.end, children: [ - CommentPopUpMenu(isSaved: true), + const CommentPopUpMenu(isSaved: true), const SizedBox( width: 20, ), InkWell( onTap: () { - print(widget.data.sId); - print(widget.data.text); - print(widget.data.author?.userName); - print(widget.data.createdAt); - Get.to(AddReplyScreen(), arguments: { + Get.to(const AddReplyScreen(), arguments: { 'parentId': widget.data.sId, 'comment': widget.data.text, 'authorName': widget.data.author?.userName, @@ -138,7 +76,7 @@ class _CommentFooterState extends State { child: Tooltip( message: 'Upvote', child: Container( - padding: EdgeInsetsDirectional.all(8), + padding: const EdgeInsetsDirectional.all(8), child: (commentVoteStatus != 1) ? Icon( Typicons.up_outline, @@ -164,7 +102,7 @@ class _CommentFooterState extends State { child: Tooltip( message: 'Downvote', child: Container( - padding: EdgeInsetsDirectional.all(8), + padding: const EdgeInsetsDirectional.all(8), child: (commentVoteStatus != -1) ? Icon( Typicons.down_outline, diff --git a/lib/comments/widgets/comment_popup_menu.dart b/lib/comments/widgets/comment_popup_menu.dart index bc96b074..50be1330 100644 --- a/lib/comments/widgets/comment_popup_menu.dart +++ b/lib/comments/widgets/comment_popup_menu.dart @@ -55,7 +55,7 @@ class CommentPopUpMenu extends StatelessWidget { children: [ Container( margin: const EdgeInsetsDirectional.only(end: 5), - child: Icon(Icons.share_outlined), + child: const Icon(Icons.share_outlined), ), Text( 'Share', diff --git a/lib/comments/widgets/comments_list.dart b/lib/comments/widgets/comments_list.dart index 6fe194c7..6c1d63d2 100644 --- a/lib/comments/widgets/comments_list.dart +++ b/lib/comments/widgets/comments_list.dart @@ -6,8 +6,15 @@ import '../models/comment_model.dart'; import '../providers/comments_provider.dart'; import 'comment.dart'; +/// This widget displays the comments tree of a post + class CommentsList extends StatefulWidget { + /// The ID of the post to get its comments + final String postId; + + /// The user name + final String userName; const CommentsList({super.key, required this.postId, required this.userName}); diff --git a/lib/comments/widgets/view_comment.dart b/lib/comments/widgets/view_comment.dart index 21fcd198..e22c0c92 100644 --- a/lib/comments/widgets/view_comment.dart +++ b/lib/comments/widgets/view_comment.dart @@ -13,12 +13,12 @@ class _ViewCommentState extends State { Widget build(BuildContext context) { return Container( height: 50, - margin: EdgeInsetsDirectional.only(start: 10), + margin: const EdgeInsetsDirectional.only(start: 10), child: Row( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ for (int i = 0; i < widget.level; i++) - VerticalDivider( + const VerticalDivider( color: Colors.grey, thickness: 2, ), diff --git a/lib/discover/providers/discover_provider.dart b/lib/discover/providers/discover_provider.dart index 712dff5f..68f3f472 100644 --- a/lib/discover/providers/discover_provider.dart +++ b/lib/discover/providers/discover_provider.dart @@ -17,10 +17,8 @@ class DiscoverProvider with ChangeNotifier { try { final prefs = await SharedPreferences.getInstance(); DioClient.init(prefs); - print('====================in discover================================'); - print('/subreddits/${topic}/posts/like_reels$page p $limit L'); await DioClient.get( - path: '/subreddits/study/posts/like_reels', + path: '/subreddits/${topic}/posts/like_reels', query: {'page': page, 'limit': limit}).then((response) { print(response.data); imgAndVideoList = DiscoverData.fromJson(response.data); diff --git a/lib/main.dart b/lib/main.dart index 6d68c297..cae1158b 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -9,7 +9,6 @@ import 'package:post/create_community/widgets/community_type.dart'; import 'package:post/messages/screens/reply_message_screen.dart'; import 'package:post/messages/screens/show_message_body.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart'; -import 'package:post/show_post/screens/show_post_web.dart'; import './messages/screens/web_message_all_screen.dart'; import './messages/screens/web_message_screen.dart'; import 'package:post/providers/global_settings.dart'; @@ -94,6 +93,7 @@ import './settings/provider/user_settings_provider.dart'; import './search/provider/search_provider.dart'; import './discover/providers/discover_provider.dart'; import './moderation_settings/provider/moderation_general_data.dart'; +import './show_post/screens/show_post_web.dart'; import 'widgets/custom_snack_bar.dart'; //import './models/push_notification_model.dart'; import './shared/constants.dart'; @@ -623,7 +623,6 @@ class _MyAppState extends State { ModeratedSubredditScreen.routeName: (context) => ModeratedSubredditScreen(), ShowPostDetailsWeb.routeName: (context) => ShowPostDetailsWeb(), - // LoginPage.routeName: (context) => LoginPage(), }, ), diff --git a/lib/moderated_subreddit/widgets/moderated_subreddit_web.dart b/lib/moderated_subreddit/widgets/moderated_subreddit_web.dart index 1d6b5996..10a768a9 100644 --- a/lib/moderated_subreddit/widgets/moderated_subreddit_web.dart +++ b/lib/moderated_subreddit/widgets/moderated_subreddit_web.dart @@ -6,6 +6,7 @@ import '../../widgets/subreddit_join_button_web.dart'; import '../../models/subreddit_data.dart'; import '../widgets/moderated_subreddite_post_web.dart'; import '../../widgets/back_to_button.dart'; + class ModeratedSubredditWeb extends StatelessWidget { String userName; ModeratedSubredditWeb( @@ -24,13 +25,14 @@ class ModeratedSubredditWeb extends StatelessWidget { bool isLoading; TabController? controller; ScrollController scrollController = ScrollController(); - + @override Widget build(BuildContext context) { final GlobalKey _scaffoldKey = GlobalKey(); return Scaffold( key: _scaffoldKey, - floatingActionButton:BackToTopButton(scrollController:scrollController ) , + floatingActionButton: + BackToTopButton(scrollController: scrollController), body: isLoading ? const Center( child: Icon( diff --git a/lib/moderated_subreddit/widgets/moderated_subreddite_post_web.dart b/lib/moderated_subreddit/widgets/moderated_subreddite_post_web.dart index 92e0da93..a70a1b18 100644 --- a/lib/moderated_subreddit/widgets/moderated_subreddite_post_web.dart +++ b/lib/moderated_subreddit/widgets/moderated_subreddite_post_web.dart @@ -22,10 +22,8 @@ class _ModeratedSubredditePostWebState extends State scrollDirection: Axis.vertical, children: [ Row( - mainAxisSize: MainAxisSize.min, - - mainAxisAlignment: MainAxisAlignment.spaceBetween, - //crossAxisAlignment: CrossAxisAlignment., + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, children: [ Expanded( flex: 2, @@ -40,10 +38,9 @@ class _ModeratedSubredditePostWebState extends State margin: EdgeInsets.only(left: 100, top: 40), child: SortBottomWeb(page: 1,userName: widget.loadedSubreddit!.name.toString(),), color: Colors.white, - // width: 100.w, + ), Container( - // padding: const EdgeInsets.only(bottom: 100,), margin: EdgeInsets.only(left: 100, bottom: 90, top: 30), height: 30.h, width: 50.w, diff --git a/lib/other_profile/widgets/other_profile_card_information_web.dart b/lib/other_profile/widgets/other_profile_card_information_web.dart index 36d44c56..8b66b499 100644 --- a/lib/other_profile/widgets/other_profile_card_information_web.dart +++ b/lib/other_profile/widgets/other_profile_card_information_web.dart @@ -59,10 +59,7 @@ class _OtherProfileCardInformationWebState ), //tomake widget position PositionOtherProfileWeb(loadProfile: widget.loadProfile), - // OptionsButton( - // // moreOptions: OtherProfileCardInformationWeb.moreOptions, - // ), - Positioned( + Positioned( top: 340, child: Column( children: [ @@ -186,7 +183,7 @@ class _OtherProfileCardInformationWebState ), ), Container( - width: 35.w, + width: 15.w, height: 6.h, child: ElevatedButton( style: ButtonStyle( @@ -206,13 +203,13 @@ class _OtherProfileCardInformationWebState if (!block) { ScaffoldMessenger.of(context).showSnackBar(CustomSnackBar( isError: false, - text: 'Invitation Successfully', + text: 'Block Successfully', disableStatus: true)); } else { ScaffoldMessenger.of(context).showSnackBar( CustomSnackBar( isError: false, - text: 'Invitation Successfully', + text: 'Block Failed', disableStatus: true), ); } diff --git a/lib/other_profile/widgets/overview_other_profile_web.dart b/lib/other_profile/widgets/overview_other_profile_web.dart index 8dcb6d34..270a3b37 100644 --- a/lib/other_profile/widgets/overview_other_profile_web.dart +++ b/lib/other_profile/widgets/overview_other_profile_web.dart @@ -26,24 +26,21 @@ class _OverviewOtherProfileWebState extends State { controller: widget.scrollController, children: [ Row( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.spaceBetween, + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, children: [ Expanded( flex: 2, child: Column( children: [ - // SizedBox(height: 4.h,), Container( height: 6.h, width: 50.w, margin: EdgeInsets.only(left: 100, bottom: 10, top: 30), child: SortBottomWeb(page: 1 ,userName: widget.loadProfile.userName.toString()), color: Colors.white, - // width: 100.w, ), Container( - // padding: const EdgeInsets.only(bottom: 100,), margin: EdgeInsets.only(left: 100, bottom: 90, top: 30), height: 30.h, width: 50.w, diff --git a/lib/other_profile/widgets/position_other_profile_web.dart b/lib/other_profile/widgets/position_other_profile_web.dart index 912d7445..81692eb2 100644 --- a/lib/other_profile/widgets/position_other_profile_web.dart +++ b/lib/other_profile/widgets/position_other_profile_web.dart @@ -3,6 +3,7 @@ import 'package:responsive_sizer/responsive_sizer.dart'; import '../models/others_profile_data.dart'; import 'package:provider/provider.dart'; import '../providers/other_profile_provider.dart'; +import 'package:intl/intl.dart'; import '../../widgets/custom_snack_bar.dart'; class PositionOtherProfileWeb extends StatefulWidget { PositionOtherProfileWeb({ @@ -85,8 +86,8 @@ class _PositionOtherProfileWebState extends State { // width: 100.w, height: 100.h, - padding: EdgeInsets.only(left: 20, top: 15), - margin: EdgeInsets.only(top: 10), + padding: const EdgeInsets.only(left: 20, top: 15), + margin: const EdgeInsets.only(top: 10), color: Colors.white, child: Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -109,7 +110,6 @@ class _PositionOtherProfileWebState extends State { ), //name and discibtions Text( - // ${loadProfile.numOfDaysInReddit} .${int.parse(loadProfile.postKarma.toString()) + int.parse(loadProfile.commentkarma.toString())}.${loadProfile.createdAt.toString()} 'u/${widget.loadProfile.displayName}', style: const TextStyle( color: Colors.black, @@ -142,14 +142,14 @@ class _PositionOtherProfileWebState extends State { children: [ Column( children: [ - Text('Karma', - style: const TextStyle( + const Text('Karma', + style: TextStyle( color: Colors.black, fontWeight: FontWeight.bold, fontSize: 13)), Row( children: [ - Icon( + const Icon( Icons.settings, color: Colors.blue, ), @@ -168,18 +168,18 @@ class _PositionOtherProfileWebState extends State { ), Column( children: [ - Text('CakeDay', - style: const TextStyle( + const Text('CakeDay', + style: TextStyle( color: Colors.black, fontWeight: FontWeight.bold, fontSize: 13)), Row( children: [ - Icon( + const Icon( Icons.cake, color: Colors.blue, ), - Text('${widget.loadProfile.createdAt.toString()}', + Text('${DateFormat.yMMMMd('en_US').format(DateTime.parse(widget.loadProfile.createdAt.toString()))}', style: const TextStyle( color: Colors.black, fontWeight: FontWeight.normal, @@ -211,54 +211,12 @@ class _PositionOtherProfileWebState extends State { foregroundColor: MaterialStateProperty.all(Colors.white)), child: Text( isFollowedstate ? 'Following' : 'Follow', - style: TextStyle( + style: const TextStyle( color: Colors.white, fontWeight: FontWeight.bold, fontSize: 15), ), )), - // Visibility( - // visible: moreOptions, - // child: Container( - // child: Column( - // children: [ - // TextButton(onPressed: null, child: Text('Send Message')), - // TextButton(onPressed: null, child: Text('Block User')), - // TextButton( - // onPressed: null, - // child: Text('Get Them Help and Support')), - // TextButton(onPressed: null, child: Text('Report User')) - // ], - // )), - // ), - // Container( - // width: 15.w, - // height: 6.h, - // child: TextButton( - // onPressed: () { - // setState(() { - // moreOptions = true; - // }); - // }, - // style: ButtonStyle( - // // side: MaterialStateProperty.all( - // // const BorderSide( - // // color: Colors.white)), - // shape: MaterialStateProperty.all( - // const RoundedRectangleBorder( - // borderRadius: - // BorderRadius.all(Radius.circular(30)))), - // backgroundColor: MaterialStateProperty.all(Colors.blue), - // foregroundColor: MaterialStateProperty.all(Colors.white)), - // child: Text( - // moreOptions ? 'More Options' : 'Less Options', - // style: TextStyle( - // color: Colors.white, - // fontWeight: FontWeight.bold, - // fontSize: 15), - // ), - // )), - const SizedBox( height: 7, ) diff --git a/lib/post/models/post_model.dart b/lib/post/models/post_model.dart index ff172d48..f84d6f0a 100644 --- a/lib/post/models/post_model.dart +++ b/lib/post/models/post_model.dart @@ -154,24 +154,24 @@ class PostModel { print(json['url'].runtimeType); sId = json['_id']; - ownerType = json['ownerType']; + ownerType = json['ownerType'] ?? 'Subreddit'; replies = json['replies']; - title = json['title']; - kind = json['kind']; - text = json['text']; - images = json['images']; - createdAt = json['createdAt']; + title = json['title'] ?? ''; + kind = json['kind'] ?? 'self'; + text = json['text'] ?? ''; + images = json['images'] ?? []; + createdAt = json['createdAt'] ?? '2017-07-21T17:32:28Z'; locked = json['locked'] ?? false; isDeleted = json['isDeleted'] ?? false; sendReplies = json['sendReplies'] ?? false; nsfw = json['nsfw'] ?? false; spoiler = json['spoiler'] ?? false; - votes = json['votes']; - views = json['views']; - commentCount = json['commentCount']; - shareCount = json['shareCount']; - suggestedSort = json['suggestedSort']; - scheduled = json['scheduled']; + votes = json['votes'] ?? 0; + views = json['views'] ?? 0; + commentCount = json['commentCount'] ?? 0; + shareCount = json['shareCount'] ?? 0; + suggestedSort = json['suggestedSort'] ?? ''; + scheduled = json['scheduled'] ?? false; flairId = json['flairId'] != null ? new FlairId.fromJson(json['flairId']) : null; isHidden = json['isHidden'] ?? false; diff --git a/lib/post/widgets/post.dart b/lib/post/widgets/post.dart index ca3bc888..eb6a0339 100644 --- a/lib/post/widgets/post.dart +++ b/lib/post/widgets/post.dart @@ -12,13 +12,18 @@ import './post_body.dart'; /// This is the main post Widget. /// -/// It takes a map of the post data. class Post extends StatefulWidget { + /// The data of the post final PostModel data; bool _inHome = false, _inProfile = false; //final Function updateDate; + /// A boolean to check if in view final bool inView; + + /// The user name final String userName; + + /// a boolean to determine if in the post screen final bool inScreen; /// This is the constructor for home page. @@ -70,11 +75,10 @@ class _PostState extends State { @override void didChangeDependencies() async { - // TODO: implement didChangeDependencies if (widget.data.isModerator == null && widget.data.ownerType == 'Subreddit') { final provider = - await Provider.of(context, listen: false); + Provider.of(context, listen: false); await provider .getUser( widget.data.owner?.name as String, UserCase.moderator, context) @@ -83,8 +87,6 @@ class _PostState extends State { for (var mod in moderators!) { widget.data.isModerator = false; - print('=============Is mod:${mod.userName}======================='); - if (mod.userName == widget.userName) { widget.data.isModerator = true; break; @@ -109,7 +111,7 @@ class _PostState extends State { // imageNumber: data.imageNumber, // ); return widget.data.isDeleted ?? false - ? SizedBox() + ? const SizedBox() : Container( margin: const EdgeInsetsDirectional.only(bottom: 10), child: Column( diff --git a/lib/post/widgets/post_body.dart b/lib/post/widgets/post_body.dart index 160a797f..1110854b 100644 --- a/lib/post/widgets/post_body.dart +++ b/lib/post/widgets/post_body.dart @@ -1,17 +1,16 @@ import 'package:flutter/material.dart'; +import 'package:html/parser.dart'; import 'package:post/post/models/post_model.dart'; import 'package:post/post/widgets/post_card.dart'; import 'package:post/post/widgets/post_images.dart'; import 'package:post/post/widgets/post_images_web.dart'; import 'package:post/post/widgets/post_link_in_screen.dart'; -import 'package:post/post/widgets/post_pop_up_web.dart'; import 'package:post/post/widgets/post_tags_and_title.dart'; import 'package:post/post/widgets/post_video_in_widget_web.dart'; import 'package:video_player/video_player.dart'; import '../../show_post/screens/show_post.dart'; import '../../show_post/screens/show_post_web.dart'; -import '../../widgets/loading_reddit.dart'; import 'post_video_in_widget.dart'; import 'package:flutter/foundation.dart' show kIsWeb; @@ -78,7 +77,10 @@ class _PostBodyState extends State { padding: const EdgeInsetsDirectional.only( start: 10, end: 10, top: 10), child: Text( - widget.data.text as String, + parse(widget.data.text as String) + .documentElement + ?.text ?? + '', maxLines: 3, style: TextStyle( color: Theme.of(context).colorScheme.secondary, diff --git a/lib/post/widgets/post_body_in_screen.dart b/lib/post/widgets/post_body_in_screen.dart index 18645f83..28351b38 100644 --- a/lib/post/widgets/post_body_in_screen.dart +++ b/lib/post/widgets/post_body_in_screen.dart @@ -1,18 +1,24 @@ import 'package:flutter/material.dart'; import 'package:post/post/models/post_model.dart'; -import 'package:post/post/widgets/post_images.dart'; import 'package:post/post/widgets/post_images_in_screen.dart'; import 'package:post/post/widgets/post_link_in_screen.dart'; import 'package:post/post/widgets/post_tags_and_title.dart'; +import 'package:post/post/widgets/post_video_in_widget_web.dart'; import 'package:video_player/video_player.dart'; import 'post_video_in_widget.dart'; import 'package:flutter_html/flutter_html.dart'; +import 'package:flutter/foundation.dart' show kIsWeb; /// This Widget is responsible for the body of the post. class PostBodyInScreen extends StatefulWidget { + /// The data of the post final PostModel data; + + /// The user name final String userName; + + /// a boolean to check if in screen final bool inView; const PostBodyInScreen( @@ -83,16 +89,27 @@ class _PostBodyInScreenState extends State { type: widget.data.kind as String, ) : (widget.data.kind == 'video') - ? PostVideoInWidget( - title: widget.data.title as String, - flair: widget.data.flairId, - nsfw: widget.data.nsfw as bool, - spoiler: widget.data.spoiler as bool, - inView: widget.inView, - url: widget.data.video as String, - videoController: widget.data.videoController - as VideoPlayerController, - ) + ? kIsWeb + ? PostVideoInWidgetWeb( + title: widget.data.title as String, + flair: widget.data.flairId, + nsfw: widget.data.nsfw as bool, + spoiler: widget.data.spoiler as bool, + inView: widget.inView, + url: widget.data.video as String, + videoController: widget.data.videoController + as VideoPlayerController, + ) + : PostVideoInWidget( + title: widget.data.title as String, + flair: widget.data.flairId, + nsfw: widget.data.nsfw as bool, + spoiler: widget.data.spoiler as bool, + inView: widget.inView, + url: widget.data.video as String, + videoController: widget.data.videoController + as VideoPlayerController, + ) : const SizedBox(), ), ); diff --git a/lib/post/widgets/post_card.dart b/lib/post/widgets/post_card.dart index 10e39d56..a7683227 100644 --- a/lib/post/widgets/post_card.dart +++ b/lib/post/widgets/post_card.dart @@ -43,11 +43,11 @@ class PostCard extends StatelessWidget { height: 80, child: LinkPreviewGenerator( showDescription: false, - link: 'https://github.com/ghpranav/link_preview_generator', - placeholderWidget: LoadingReddit(), + link: link, + placeholderWidget: const LoadingReddit(), backgroundColor: Colors.black.withOpacity(0.6), showTitle: false, - domainStyle: TextStyle(color: Colors.white, fontSize: 12), + domainStyle: const TextStyle(color: Colors.white, fontSize: 12), ), ), ], diff --git a/lib/post/widgets/post_comment_list.dart b/lib/post/widgets/post_comment_list.dart index 43ae3a58..331ae588 100644 --- a/lib/post/widgets/post_comment_list.dart +++ b/lib/post/widgets/post_comment_list.dart @@ -1,23 +1,46 @@ import 'package:flutter/material.dart'; import 'package:fluttericon/typicons_icons.dart'; import 'package:inview_notifier_list/inview_notifier_list.dart'; -import '../models/post_model.dart'; import './post.dart'; +/// A margin to the left of the posts and comments + class PostCommentList extends StatefulWidget { + /// The user name + final String userName; + + /// A widget to be displayed above the posts + final Widget topOfTheList; + + /// A function which is invoked when the CustomScrollView reaches the end + final Function updateData; + + /// The list of posts and comments' data to be displayed + final List> data; + /// A margin to the left of the posts + + final double leftMargin; + + /// A margin to the right of the posts + + final double rightMargin; + final String type; - const PostCommentList( - {super.key, - required this.userName, - this.topOfTheList = const SizedBox(), - required this.updateData, - required this.data, - this.type = 'home'}); + const PostCommentList({ + super.key, + required this.userName, + this.topOfTheList = const SizedBox(), + required this.updateData, + required this.data, + this.type = 'home', + this.leftMargin = 0, + this.rightMargin = 0, + }); @override State createState() => _PostCommentListState(); @@ -51,6 +74,8 @@ class _PostCommentListState extends State { Widget build(BuildContext context) { return Flexible( child: InViewNotifierCustomScrollView( + physics: const AlwaysScrollableScrollPhysics(), + shrinkWrap: true, onListEndReached: () => widget.updateData(), scrollDirection: Axis.vertical, initialInViewIds: const ['0'], @@ -60,6 +85,7 @@ class _PostCommentListState extends State { deltaBottom > (0.5 * viewPortDimension); }, slivers: [ + // Container(), SliverList( delegate: SliverChildBuilderDelegate( childCount: 1, @@ -69,57 +95,67 @@ class _PostCommentListState extends State { SliverList( delegate: SliverChildBuilderDelegate(childCount: widget.data.length, (context, index) { - return InViewNotifierWidget( - id: '$index', - builder: (context, isInView, child) { - if (widget.data[index]['type'] == 'comment') { - return ListTile( - title: - Text(widget.data[index]['data'].title.toString()), - subtitle: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.start, - children: [ - Text.rich( - TextSpan( - children: [ + return Container( + margin: EdgeInsetsDirectional.only( + start: widget.leftMargin, end: widget.rightMargin), + child: InViewNotifierWidget( + id: '$index', + builder: (context, isInView, child) { + if (widget.data[index]['type'] == 'comment') { + return Container( + color: Colors.white, + margin: const EdgeInsetsDirectional.only( + top: 10, bottom: 10), + child: ListTile( + title: Text( + widget.data[index]['data'].title.toString()), + subtitle: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Text.rich( TextSpan( - text: - '${(widget.data[index]['data'].ownerType == 'User') ? 'u/' : 'r/'}${widget.data[index]['data'].owner}.${dateOfcomment(widget.data[index]['data'].createdAt.toString())} .${widget.data[index]['data'].votes}'), - const WidgetSpan( - child: Icon( - Typicons.up, - size: 15, - )), - ], - ), + children: [ + TextSpan( + text: + '${(widget.data[index]['data'].ownerType == 'User') ? 'u/' : 'r/'}${widget.data[index]['data'].owner}.${dateOfcomment(widget.data[index]['data'].createdAt.toString())} .${widget.data[index]['data'].votes}'), + const WidgetSpan( + child: Icon( + Typicons.up, + size: 15, + )), + ], + ), + ), + Text( + widget.data[index]['data'].text.toString()), + ], ), - Text(widget.data[index]['data'].text.toString()), - ], - ), - ); - } else { - if (widget.type == 'profile') { - return Post.profile( - userName: widget.userName, - inView: isInView, - data: widget.data[index]['data'], - ); - } else if (widget.type == 'community') { - return Post.community( - userName: widget.userName, - inView: isInView, - data: widget.data[index]['data'], + ), ); } else { - return Post.home( - userName: widget.userName, - inView: isInView, - data: widget.data[index]['data'], - ); + if (widget.type == 'profile') { + return Post.profile( + userName: widget.userName, + inView: isInView, + data: widget.data[index]['data'], + ); + } else if (widget.type == 'community') { + return Post.community( + userName: widget.userName, + inView: isInView, + data: widget.data[index]['data'], + ); + } else { + return Post.home( + userName: widget.userName, + inView: isInView, + data: widget.data[index]['data'], + ); + } } - } - }); + }), + ); }), ) ], diff --git a/lib/post/widgets/post_footer.dart b/lib/post/widgets/post_footer.dart index 0556c639..e887b626 100644 --- a/lib/post/widgets/post_footer.dart +++ b/lib/post/widgets/post_footer.dart @@ -117,7 +117,7 @@ class _PostFooterState extends State { ? Theme.of(context).colorScheme.primary : Theme.of(context).colorScheme.surface, child: Container( - margin: EdgeInsetsDirectional.only(start: 10, end: 10), + margin: const EdgeInsetsDirectional.only(start: 10, end: 10), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ @@ -134,7 +134,7 @@ class _PostFooterState extends State { child: Tooltip( message: 'Upvote', child: Container( - padding: EdgeInsetsDirectional.all(8), + padding: const EdgeInsetsDirectional.all(8), child: (postVoteStatus != 1) ? Icon( Typicons.up_outline, @@ -160,7 +160,7 @@ class _PostFooterState extends State { child: Tooltip( message: 'Downvote', child: Container( - padding: EdgeInsetsDirectional.all(8), + padding: const EdgeInsetsDirectional.all(8), child: (postVoteStatus != -1) ? Icon( Typicons.down_outline, diff --git a/lib/post/widgets/post_header.dart b/lib/post/widgets/post_header.dart index fad14bd7..7e75dba4 100644 --- a/lib/post/widgets/post_header.dart +++ b/lib/post/widgets/post_header.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; import 'package:fluttericon/font_awesome5_icons.dart'; -import 'package:post/post/widgets/post_popup_menu.dart'; +import 'package:post/post/widgets/post_pop_up_menu.dart'; import 'package:post/post/widgets/user_info_popup.dart'; import 'package:post/subreddit/screens/subreddit_screen.dart'; import '../../moderated_subreddit/screens/moderated_subreddit_screen.dart'; @@ -186,9 +186,6 @@ class _PostHeaderBasicState extends State { : InkWell( onTap: (!widget.inProfile) ? () { - print( - '===============================Is mod:${widget.isModerator}============================='); - showDialog( context: context, builder: (context) => UserInfoPopUp( @@ -198,9 +195,6 @@ class _PostHeaderBasicState extends State { ); } : () { - print( - '===============================Is mod:${widget.isModerator}============================='); - Navigator.of(context).pushNamed( widget.isModerator ? ModeratedSubredditScreen.routeName @@ -287,8 +281,6 @@ class _PostHeaderHome extends StatelessWidget { children: [ InkWell( onTap: () { - print( - '==========================On Tab==========================='); if (ownerType == 'User') { showDialog( context: context, diff --git a/lib/post/widgets/post_link_in_screen.dart b/lib/post/widgets/post_link_in_screen.dart index 65817dc3..dbf4a26e 100644 --- a/lib/post/widgets/post_link_in_screen.dart +++ b/lib/post/widgets/post_link_in_screen.dart @@ -39,7 +39,7 @@ class PostLinkInScreen extends StatelessWidget { margin: const EdgeInsetsDirectional.only(top: 10, start: 10, end: 10), child: LinkPreviewGenerator( showDescription: false, - link: 'https://github.com/ghpranav/link_preview_generator', + link: link, placeholderWidget: const LoadingReddit(), backgroundColor: Colors.black.withOpacity(0.6), showTitle: false, diff --git a/lib/post/widgets/post_list.dart b/lib/post/widgets/post_list.dart index b39b87e5..c5bbf343 100644 --- a/lib/post/widgets/post_list.dart +++ b/lib/post/widgets/post_list.dart @@ -3,19 +3,40 @@ import 'package:inview_notifier_list/inview_notifier_list.dart'; import '../models/post_model.dart'; import './post.dart'; +/// This widget returns a CustomScrollView to display posts + class PostList extends StatefulWidget { + /// The user name final String userName; + + /// A widget to be displayed above the posts final Widget topOfTheList; + + /// A function which is invoked when the CustomScrollView reaches the end final Function updateData; + + /// The list of posts' data to be displayed final List data; + + /// The type of posts ["home", "profile", "community"] final String type; - const PostList( - {super.key, - required this.userName, - this.topOfTheList = const SizedBox(), - required this.updateData, - required this.data, - this.type = 'home'}); + + /// A margin to the left of the posts + final double leftMargin; + + /// A margin to the right of the posts + + final double rightMargin; + const PostList({ + super.key, + required this.userName, + this.topOfTheList = const SizedBox(), + required this.updateData, + required this.data, + this.type = 'home', + this.leftMargin = 0, + this.rightMargin = 0, + }); @override State createState() => _PostListState(); @@ -26,6 +47,7 @@ class _PostListState extends State { Widget build(BuildContext context) { return Flexible( child: InViewNotifierCustomScrollView( + shrinkWrap: true, onListEndReached: () => widget.updateData(), scrollDirection: Axis.vertical, initialInViewIds: const ['0'], @@ -44,29 +66,33 @@ class _PostListState extends State { SliverList( delegate: SliverChildBuilderDelegate(childCount: widget.data.length, (context, index) { - return InViewNotifierWidget( - id: '$index', - builder: (context, isInView, child) { - if (widget.type == 'profile') { - return Post.profile( - userName: widget.userName, - inView: isInView, - data: widget.data[index], - ); - } else if (widget.type == 'community') { - return Post.community( - userName: widget.userName, - inView: isInView, - data: widget.data[index], - ); - } else { - return Post.home( - userName: widget.userName, - inView: isInView, - data: widget.data[index], - ); - } - }); + return Container( + margin: EdgeInsetsDirectional.only( + start: widget.leftMargin, end: widget.rightMargin), + child: InViewNotifierWidget( + id: '$index', + builder: (context, isInView, child) { + if (widget.type == 'profile') { + return Post.profile( + userName: widget.userName, + inView: isInView, + data: widget.data[index], + ); + } else if (widget.type == 'community') { + return Post.community( + userName: widget.userName, + inView: isInView, + data: widget.data[index], + ); + } else { + return Post.home( + userName: widget.userName, + inView: isInView, + data: widget.data[index], + ); + } + }), + ); }), ) ], diff --git a/lib/post/widgets/post_mod_popup.dart b/lib/post/widgets/post_mod_popup.dart index fca8cbef..52c3c92d 100644 --- a/lib/post/widgets/post_mod_popup.dart +++ b/lib/post/widgets/post_mod_popup.dart @@ -106,11 +106,11 @@ class _PostModPopUpState extends State { Navigator.pop(context); }, child: Container( - margin: EdgeInsetsDirectional.only(bottom: 10, top: 10), + margin: const EdgeInsetsDirectional.only(bottom: 10, top: 10), child: Row( children: [ Container( - margin: EdgeInsetsDirectional.only(start: 2, end: 5), + margin: const EdgeInsetsDirectional.only(start: 2, end: 5), child: Icon( MfgLabs.attention, size: 18, @@ -136,11 +136,11 @@ class _PostModPopUpState extends State { Navigator.pop(context); }, child: Container( - margin: EdgeInsetsDirectional.only(bottom: 10, top: 10), + margin: const EdgeInsetsDirectional.only(bottom: 10, top: 10), child: Row( children: [ Container( - margin: EdgeInsetsDirectional.only(start: 2, end: 5), + margin: const EdgeInsetsDirectional.only(start: 2, end: 5), child: Stack( alignment: Alignment.center, children: [ @@ -185,11 +185,11 @@ class _PostModPopUpState extends State { Navigator.pop(context); }, child: Container( - margin: EdgeInsetsDirectional.only(bottom: 10, top: 10), + margin: const EdgeInsetsDirectional.only(bottom: 10, top: 10), child: Row( children: [ Container( - margin: EdgeInsetsDirectional.only(end: 5), + margin: const EdgeInsetsDirectional.only(end: 5), child: Icon( Icons.lock_outline, color: Theme.of(context).colorScheme.brightness == @@ -216,11 +216,11 @@ class _PostModPopUpState extends State { InkWell( onTap: null, child: Container( - margin: EdgeInsetsDirectional.only(bottom: 10, top: 10), + margin: const EdgeInsetsDirectional.only(bottom: 10, top: 10), child: Row( children: [ Container( - margin: EdgeInsetsDirectional.only(end: 5), + margin: const EdgeInsetsDirectional.only(end: 5), child: Icon( FontAwesome.mail, color: Theme.of(context).colorScheme.brightness == @@ -250,11 +250,11 @@ class _PostModPopUpState extends State { Navigator.pop(context); }, child: Container( - margin: EdgeInsetsDirectional.only(bottom: 10, top: 10), + margin: const EdgeInsetsDirectional.only(bottom: 10, top: 10), child: Row( children: [ Container( - margin: EdgeInsetsDirectional.only(end: 5), + margin: const EdgeInsetsDirectional.only(end: 5), child: Icon( Icons.block_flipped, color: widget.isRemoved @@ -288,11 +288,11 @@ class _PostModPopUpState extends State { Navigator.pop(context); }, child: Container( - margin: EdgeInsetsDirectional.only(bottom: 10, top: 10), + margin: const EdgeInsetsDirectional.only(bottom: 10, top: 10), child: Row( children: [ Container( - margin: EdgeInsetsDirectional.only(end: 5), + margin: const EdgeInsetsDirectional.only(end: 5), child: Icon( Icons.block_flipped, color: widget.data.isSpam ?? false @@ -326,11 +326,11 @@ class _PostModPopUpState extends State { Navigator.pop(context); }, child: Container( - margin: EdgeInsetsDirectional.only(bottom: 10, top: 10), + margin: const EdgeInsetsDirectional.only(bottom: 10, top: 10), child: Row( children: [ Container( - margin: EdgeInsetsDirectional.only(end: 5), + margin: const EdgeInsetsDirectional.only(end: 5), child: Icon( Icons.done, color: widget.isApproved diff --git a/lib/post/widgets/post_mod_tools.dart b/lib/post/widgets/post_mod_tools.dart index 6846bf35..f54c18d8 100644 --- a/lib/post/widgets/post_mod_tools.dart +++ b/lib/post/widgets/post_mod_tools.dart @@ -5,17 +5,40 @@ import 'package:provider/provider.dart'; import '../models/post_model.dart'; import '../provider/post_provider.dart'; +/// This widget displays the moderator tools of the post + class PostModTools extends StatefulWidget { /// Check if it's a post created by the user final bool isMyPost; + /// A boolean to determine if the post is approved + final bool isApproved; + + /// A boolean to determine if the post is NSFW + final bool isNSFW; + + /// A boolean to determine if the post is spoiler + final bool isSpoiler; + + /// A boolean to determine if the comments of the post is locked + final bool isCommentsLocked; + + /// A boolean to determine if the post is removed + final bool isRemoved; + + /// A function which is invoked when the state changes + final Function update; + + /// The post data final PostModel data; + + /// A boolean to check if in screen final bool inScreen; const PostModTools({ @@ -55,7 +78,7 @@ class _PostModTools extends State { @override Widget build(BuildContext context) { return Container( - margin: EdgeInsetsDirectional.only(top: 2), + margin: const EdgeInsetsDirectional.only(top: 2), child: Material( color: (!widget.inScreen && (widget.data.isSpam ?? false)) ? Colors.blueGrey[50] @@ -63,7 +86,7 @@ class _PostModTools extends State { ? Theme.of(context).colorScheme.primary : Theme.of(context).colorScheme.surface, child: Container( - margin: EdgeInsetsDirectional.only(start: 10, end: 10), + margin: const EdgeInsetsDirectional.only(start: 10, end: 10), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ @@ -75,7 +98,7 @@ class _PostModTools extends State { child: Tooltip( message: 'Approve', child: Container( - padding: EdgeInsetsDirectional.all(10), + padding: const EdgeInsetsDirectional.all(10), child: (!widget.isApproved) ? Icon( Icons.done, @@ -108,7 +131,7 @@ class _PostModTools extends State { child: Tooltip( message: 'More options', child: Container( - padding: EdgeInsetsDirectional.all(10), + padding: const EdgeInsetsDirectional.all(10), child: Icon( Icons.format_list_bulleted, color: Theme.of(context).colorScheme.secondary, diff --git a/lib/post/widgets/post_popup_menu.dart b/lib/post/widgets/post_pop_up_menu.dart similarity index 96% rename from lib/post/widgets/post_popup_menu.dart rename to lib/post/widgets/post_pop_up_menu.dart index 2f34599c..b8434476 100644 --- a/lib/post/widgets/post_popup_menu.dart +++ b/lib/post/widgets/post_pop_up_menu.dart @@ -5,10 +5,19 @@ import 'package:provider/provider.dart'; import '../provider/post_provider.dart'; +/// This widget shows the popup menu of the widget + class PostPopupMenu extends StatefulWidget { + /// A boolean to determine if the post is saved final bool isSaved; + + /// The post data final PostModel data; + + /// A function which is invoked when the state changes final Function update; + + /// A boolean to determine if it's the user's post final bool isMyPost; const PostPopupMenu( {super.key, diff --git a/lib/post/widgets/post_pop_up_web.dart b/lib/post/widgets/post_pop_up_web.dart index 7715c246..e7494ec4 100644 --- a/lib/post/widgets/post_pop_up_web.dart +++ b/lib/post/widgets/post_pop_up_web.dart @@ -26,11 +26,11 @@ class _PostPopUpWebState extends State { @override Widget build(BuildContext context) { return AlertDialog( - content: Container( + content: SizedBox( height: MediaQuery.of(context).size.width * .8, width: MediaQuery.of(context).size.width * .8, child: SingleChildScrollView( - child: Container( + child: SizedBox( width: 40.w, child: Column( children: [ diff --git a/lib/post/widgets/post_tags_and_title.dart b/lib/post/widgets/post_tags_and_title.dart index 7df42298..d07b6207 100644 --- a/lib/post/widgets/post_tags_and_title.dart +++ b/lib/post/widgets/post_tags_and_title.dart @@ -4,10 +4,22 @@ import 'package:fluttericon/mfg_labs_icons.dart'; import 'package:hexcolor/hexcolor.dart'; import 'package:post/post/models/post_model.dart'; +/// This widget displays the title and the spoiler and NSFW tags + class PostTagsAndTitle extends StatefulWidget { + /// The title of the post final String title; + + /// A boolean to determine if NSFW + final bool isNSFW; + + /// A boolean to determine if spoiler + final bool isSpoiler; + + /// The flair data + final FlairId? flair; const PostTagsAndTitle( diff --git a/lib/post/widgets/post_video_in_widget.dart b/lib/post/widgets/post_video_in_widget.dart index 46915031..a7f50103 100644 --- a/lib/post/widgets/post_video_in_widget.dart +++ b/lib/post/widgets/post_video_in_widget.dart @@ -7,14 +7,29 @@ import 'package:chewie/chewie.dart'; import '../models/post_model.dart'; +/// A widget to display a video in widget class PostVideoInWidget extends StatefulWidget { /// The url of the video final String url; + + /// The video controller final VideoPlayerController videoController; + + /// A boolean to check if in view final bool inView; + + /// The title of the post final String title; + + /// A boolean to determine if spoiler final bool spoiler; + + /// A boolean to determine if NSFW + final bool nsfw; + + /// The flair data + final FlairId? flair; const PostVideoInWidget({ super.key, diff --git a/lib/post/widgets/post_video_in_widget_web.dart b/lib/post/widgets/post_video_in_widget_web.dart index f5423670..e186e5e1 100644 --- a/lib/post/widgets/post_video_in_widget_web.dart +++ b/lib/post/widgets/post_video_in_widget_web.dart @@ -1,20 +1,41 @@ import 'package:flutter/material.dart'; import 'package:post/post/widgets/post_tags_and_title.dart'; import 'package:post/providers/global_settings.dart'; +import 'package:post/widgets/loading_reddit.dart'; import 'package:provider/provider.dart'; import 'package:video_player/video_player.dart'; import 'package:chewie/chewie.dart'; import '../models/post_model.dart'; +/// A widget to display a video in widget (web) + class PostVideoInWidgetWeb extends StatefulWidget { /// The url of the video final String url; + + /// The video controller + final VideoPlayerController videoController; + + /// A boolean to check if in view + final bool inView; + + /// The title of the post + final String title; + + /// A boolean to determine if spoiler + final bool spoiler; + + /// A boolean to determine if NSFW + final bool nsfw; + + /// The flair data + final FlairId? flair; const PostVideoInWidgetWeb({ super.key, @@ -43,7 +64,10 @@ class _PostVideoInWidgetWebState extends State { autoInitialize: true, autoPlay: false, looping: true, - showControls: false, + showControls: true, + allowMuting: false, + allowPlaybackSpeedChanging: true, + placeholder: const LoadingReddit(), ); } diff --git a/lib/post/widgets/user_info_popup.dart b/lib/post/widgets/user_info_popup.dart index 05603e72..f008d2c4 100644 --- a/lib/post/widgets/user_info_popup.dart +++ b/lib/post/widgets/user_info_popup.dart @@ -10,8 +10,12 @@ import '../../other_profile/models/others_profile_data.dart'; import '../../other_profile/providers/other_profile_provider.dart'; import '../../widgets/loading_reddit.dart'; +/// A pop up (AlertDialog) to show the user info class UserInfoPopUp extends StatefulWidget { + /// the name of the user to get his data final String authorName; + + /// A boolean to determine the look of the pop up final bool isMine; const UserInfoPopUp({ super.key, @@ -181,7 +185,6 @@ class _UserInfoPopUpState extends State { !widget.isMine ? InkWell( onTap: () { - _showLeaveDialog(); }, child: Container( @@ -282,7 +285,7 @@ class _UserInfoPopUpState extends State { context: context, builder: (ctx) => AlertDialog( //title:Text('Are you sure you want to leave the r/${widget.communityName.toString()} community?'), - content: Container( + content: SizedBox( //color: Colors.amber, height: 12.h, width: 100.w, @@ -304,7 +307,7 @@ class _UserInfoPopUpState extends State { ), ), actions: [ - Container( + SizedBox( width: 35.w, height: 6.h, child: ElevatedButton( @@ -321,7 +324,7 @@ class _UserInfoPopUpState extends State { }, ), ), - Container( + SizedBox( width: 35.w, height: 6.h, child: ElevatedButton( diff --git a/lib/subreddit/widgets/subreddit_post_web.dart b/lib/subreddit/widgets/subreddit_post_web.dart index afd7ae6a..957fd075 100644 --- a/lib/subreddit/widgets/subreddit_post_web.dart +++ b/lib/subreddit/widgets/subreddit_post_web.dart @@ -24,8 +24,8 @@ class _SubredditePostWebState extends State { scrollDirection: Axis.vertical, children: [ Row( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.spaceBetween, + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, children: [ Expanded( flex: 2, @@ -38,7 +38,9 @@ class _SubredditePostWebState extends State { height: 6.h, width: 50.w, margin: EdgeInsets.only(left: 100, top: 40), - child: SortBottomWeb(page: 1,userName:widget.loadedSubreddit!.name.toString()), + child: SortBottomWeb( + page: 1, + userName: widget.loadedSubreddit!.name.toString()), color: Colors.white, // width: 100.w, ), diff --git a/lib/widgets/myprofile_post_web.dart b/lib/widgets/myprofile_post_web.dart index fa0b0a8b..c295f79c 100644 --- a/lib/widgets/myprofile_post_web.dart +++ b/lib/widgets/myprofile_post_web.dart @@ -6,36 +6,59 @@ import '../post/widgets/post.dart'; import 'loading_reddit.dart'; import 'sort_bottom_web.dart'; import '../myprofile/models/myprofile_data.dart'; +import 'package:provider/provider.dart'; +import '../providers/Profile_provider.dart'; +import '../post/widgets/post_list.dart'; class MyProfilePostWeb extends StatefulWidget { final String userName; - MyProfilePostWeb({ - Key? key, - required this.userName - }) : super(key: key); + MyProfilePostWeb({Key? key, required this.userName}) : super(key: key); @override State createState() => _MyProfilePostWebState(); } class _MyProfilePostWebState extends State { + int _page = 1; + + bool _isLoadMoreRunning = false; + bool _isInit = true; bool _isLoading = false; List? posts = []; + late ScrollController _scrollController; + void _loadMore() { + if (_isLoading == false && _isLoadMoreRunning == false) { + setState(() { + toggleLoadingMore(); // Display a progress indicator at the bottom + }); + setState(() { + _page += 1; + }); + // Increase _page by + + setState(() { + toggleLoadingMore(); + }); + } + } + + bool toggleLoadingMore() => _isLoadMoreRunning = !_isLoadMoreRunning; + @override void didChangeDependencies() { if (_isInit) { setState(() { _isLoading = true; }); - // Provider.of(context, listen: false) - // .fetchProfilePosts('Amr') - // .then((value) { - // posts = Provider.of(context, listen: false) - // .gettingProfilePostData; - // setState(() { - // _isLoading = false; - // }); - // }); + Provider.of(context, listen: false) + .fetchProfilePosts(widget.userName, 'Hot', _page, 25, context) + .then((value) { + posts = Provider.of(context, listen: false) + .gettingProfilePostData; + setState(() { + _isLoading = false; + }); + }); } _isInit = false; super.didChangeDependencies(); @@ -43,91 +66,51 @@ class _MyProfilePostWebState extends State { @override Widget build(BuildContext context) { - return ListView( - scrollDirection: Axis.vertical, - children: [ - Expanded( - flex: 2, - child: Column( - children: [ - Container( - height: 6.h, - width: 50.w, - margin: EdgeInsets.only(left: 100, bottom: 10, top: 30), - child: SortBottomWeb(page: 1,userName: widget.userName), - color: Colors.white, - ), - Container( - margin: EdgeInsets.only(left: 100, bottom: 90, top: 30), - height: 30.h, - width: 50.w, - // color: Colors.white, - decoration: BoxDecoration( - color: Colors.white, - border: Border.all( - color: Colors.blue, - width: 3, - )), - child: Column(children: [ - Expanded( - child: Row( - children: [ - const Expanded( - child: ListTile( - title: Text('Post'), - ), - ), - if (!_isLoading) - ListView( - scrollDirection: Axis.vertical, - children: [ - SingleChildScrollView( - child: ListView.builder( - physics: const ClampingScrollPhysics(), - shrinkWrap: true, - itemBuilder: ((context, index) => Container( - margin: const EdgeInsets.only( - left: 100, bottom: 90, top: 30), - height: 30.h, - width: 50.w, - // color: Colors.white, - decoration: BoxDecoration( - color: Colors.white, - border: Border.all( - color: Colors.blue, - width: 3, - )), - child: Column(children: [ - Expanded( - child: Row( - children: const [ - Expanded( - child: ListTile( - title: Text('Post'), - ), - ), - ], - ), - ) - ]), - )), - itemCount: posts?.length, - ), - ), - ], - ) - ], + posts = Provider.of(context, listen: true) + .gettingProfilePostData; + return (_isLoading || _isLoadMoreRunning) + ? LoadingReddit() + : (posts != null) + ? + // Container( + // width: 50, + // padding:EdgeInsets.only(left: 30,right: 30) , + // child: Column( + // children: [ + PostList( + leftMargin: 15.w, + rightMargin: 35.w, + topOfTheList: Container( + margin: EdgeInsets.only(top: 10), + child: SortBottomWeb( + page: _page, + userName: widget.userName, + ), + ), + userName: widget.userName, + updateData: _loadMore, + data: posts as List, + type: 'profile', + ) + // ], + // ), + // ) + : Center( + child: Column( + children: [ + SizedBox( + height: 30.h, ), - ) - ]), - ), - ], - ), - ), - - // ], - // ), - ], - ); + const Icon( + Icons.reddit, + size: 100, + ), + const Text( + 'Wow,such empty', + style: TextStyle(color: Colors.grey), + ) + ], + ), + ); } } diff --git a/lib/widgets/overview_myprofile_web.dart b/lib/widgets/overview_myprofile_web.dart index d1e415f1..c4268c5d 100644 --- a/lib/widgets/overview_myprofile_web.dart +++ b/lib/widgets/overview_myprofile_web.dart @@ -7,13 +7,17 @@ import '../myprofile/models/myprofile_data.dart'; import '../myprofile/widgets/myprofile_card_information_web.dart'; import '../widgets/loading_reddit.dart'; import '../widgets/sort_bottom_web.dart'; +import '../post/widgets/post_comment_list.dart'; class OverviewMyProfileWeb extends StatefulWidget { final MyProfileData loadProfile; final ScrollController scrollController; final String type; - OverviewMyProfileWeb( - {Key? key, required this.loadProfile, required this.scrollController,required this.type}) + OverviewMyProfileWeb( + {Key? key, + required this.loadProfile, + required this.scrollController, + required this.type}) : super(key: key); @override State createState() => _OverviewMyProfileWebState(); @@ -113,147 +117,76 @@ class _OverviewMyProfileWebState extends State { Widget build(BuildContext context) { commentsAndPosts = Provider.of(context, listen: true) .gettingPostCommentData; - return ListView( - scrollDirection: Axis.vertical, - controller: widget.scrollController, - children: [ - Row( - // mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Expanded( - flex: 2, - child: Column( - children: [ - SizedBox( - height: 4.h, - ), - Container( - height: 6.h, - width: 50.w, - margin: EdgeInsets.only(left: 100, bottom: 10, top: 30), - child: SortBottomWeb( - page: 1, - userName: widget.loadProfile.userName.toString()), - color: Colors.white, - ), - (_isLoading || _isLoadMoreRunning) - ? const LoadingReddit() - : (commentsAndPosts == null || commentsAndPosts!.isEmpty) - ? Center( - child: Column( - children: [ - SizedBox( - height: 30.h, - ), - const Icon( - Icons.reddit, - size: 100, - color: Colors.black, - ), - const Text( - 'Wow,such empty', - style: TextStyle(color: Colors.grey), - ) - ], - ), - ) - : SingleChildScrollView( - child: ListView.builder( - physics: const ClampingScrollPhysics(), - shrinkWrap: true, - scrollDirection: Axis.vertical, - itemBuilder: ((context, index) => InkWell( - child: Container( - padding: const EdgeInsets.all(10), - width: double.infinity, - margin: EdgeInsets.all(20), - color: const Color.fromARGB( - 255, 255, 255, 255), - child: Column( - children: [ - (commentsAndPosts![index]['type'] == - 'comment') - ? ListTile( - title: Text( - commentsAndPosts![index] - ['data'] - .title - .toString()), - subtitle: Column( - crossAxisAlignment: - CrossAxisAlignment - .start, - mainAxisAlignment: - MainAxisAlignment - .start, - children: [ - Text.rich( - TextSpan( - children: [ - TextSpan( - text: - '${(commentsAndPosts![index]['data'].ownerType == 'User') ? 'u/' : 'r/'}${commentsAndPosts![index]['data'].owner}.${dateOfcomment(commentsAndPosts![index]['data'].createdAt.toString())} .${commentsAndPosts![index]['data'].votes}'), - const WidgetSpan( - child: Icon( - Typicons.up, - size: 15, - )), - ], - ), - ), - Text(commentsAndPosts![ - index]['data'] - .text - .toString()), - ], - ), - ) - : Container( - margin: EdgeInsets.only( - left: 100, - bottom: 90, - top: 30), - height: 30.h, - width: 50.w, - decoration: BoxDecoration( - color: Colors.white, - border: Border.all( - color: Colors.blue, - width: 3, - )), - child: Column(children: [ - Expanded( - child: Row( - children: const [ - Expanded( - child: ListTile( - title: Text( - 'Post'), - ), - ), - ], - ), - ) - ]), - ), - const Divider() - ], - ), - ), - onTap: () {}, - )), - itemCount: commentsAndPosts?.length, - ), - ) - ], - ), - ), - MyProfileCardInformationWeb(loadProfile: widget.loadProfile), - ], - ), - ], - ); + return + // ListView( + // scrollDirection: Axis.vertical, + // controller: widget.scrollController, + // children: [ + // Row( + // mainAxisAlignment: MainAxisAlignment.start, + // crossAxisAlignment: CrossAxisAlignment.start, + // children: [ + // Expanded( + // flex: 2, + // child: Column( + // children: [ + // SizedBox( + // height: 4.h, + // ), + // Container( + // height: 6.h, + // width: 50.w, + // margin: EdgeInsets.only(left: 100, bottom: 10, top: 30), + // child: SortBottomWeb( + // page: 1, userName: widget.loadProfile.userName.toString()), + // color: Colors.white, + // ), + (_isLoading || _isLoadMoreRunning) + ? const LoadingReddit() + : (commentsAndPosts == null || commentsAndPosts!.isEmpty) + ? Center( + child: Column( + children: [ + SizedBox( + height: 30.h, + ), + const Icon( + Icons.reddit, + size: 100, + color: Colors.black, + ), + const Text( + 'Wow,such empty', + style: TextStyle(color: Colors.grey), + ) + ], + ), + ) + : PostCommentList( + leftMargin: 15.w, + rightMargin: 35.w, + topOfTheList: Row( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + SortBottomWeb( + page: 1, + userName: widget.loadProfile.userName.toString()), + MyProfileCardInformationWeb( + loadProfile: widget.loadProfile), + ], + ), + userName: widget.loadProfile.userName.toString(), + updateData: _loadMore, + data: commentsAndPosts as List>, + type: 'Profile', + ); + // ], + // ), + // ), + // MyProfileCardInformationWeb(loadProfile: widget.loadProfile), + // ], + // ); + // ], + // ); } } diff --git a/lib/widgets/profile_comments.dart b/lib/widgets/profile_comments.dart index 3d929596..6a51c281 100644 --- a/lib/widgets/profile_comments.dart +++ b/lib/widgets/profile_comments.dart @@ -154,7 +154,7 @@ class ProfileCommentsState extends State { padding: const EdgeInsets.all(10), width: double.infinity, // height: 15.h, - color: const Color.fromARGB(255, 255, 255, 255), + color: Color.fromARGB(255, 107, 38, 38), child: Column( children: [ ListTile( diff --git a/linux/.gitignore b/linux/.gitignore deleted file mode 100644 index d3896c98..00000000 --- a/linux/.gitignore +++ /dev/null @@ -1 +0,0 @@ -flutter/ephemeral diff --git a/linux/CMakeLists.txt b/linux/CMakeLists.txt deleted file mode 100644 index ea1493e8..00000000 --- a/linux/CMakeLists.txt +++ /dev/null @@ -1,138 +0,0 @@ -# Project-level configuration. -cmake_minimum_required(VERSION 3.10) -project(runner LANGUAGES CXX) - -# The name of the executable created for the application. Change this to change -# the on-disk name of your application. -set(BINARY_NAME "web") -# The unique GTK application identifier for this application. See: -# https://wiki.gnome.org/HowDoI/ChooseApplicationID -set(APPLICATION_ID "com.example.web") - -# Explicitly opt in to modern CMake behaviors to avoid warnings with recent -# versions of CMake. -cmake_policy(SET CMP0063 NEW) - -# Load bundled libraries from the lib/ directory relative to the binary. -set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") - -# Root filesystem for cross-building. -if(FLUTTER_TARGET_PLATFORM_SYSROOT) - set(CMAKE_SYSROOT ${FLUTTER_TARGET_PLATFORM_SYSROOT}) - set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT}) - set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) - set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) - set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) - set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) -endif() - -# Define build configuration options. -if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) - set(CMAKE_BUILD_TYPE "Debug" CACHE - STRING "Flutter build mode" FORCE) - set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS - "Debug" "Profile" "Release") -endif() - -# Compilation settings that should be applied to most targets. -# -# Be cautious about adding new options here, as plugins use this function by -# default. In most cases, you should add new options to specific targets instead -# of modifying this function. -function(APPLY_STANDARD_SETTINGS TARGET) - target_compile_features(${TARGET} PUBLIC cxx_std_14) - target_compile_options(${TARGET} PRIVATE -Wall -Werror) - target_compile_options(${TARGET} PRIVATE "$<$>:-O3>") - target_compile_definitions(${TARGET} PRIVATE "$<$>:NDEBUG>") -endfunction() - -# Flutter library and tool build rules. -set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") -add_subdirectory(${FLUTTER_MANAGED_DIR}) - -# System-level dependencies. -find_package(PkgConfig REQUIRED) -pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) - -add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}") - -# Define the application target. To change its name, change BINARY_NAME above, -# not the value here, or `flutter run` will no longer work. -# -# Any new source files that you add to the application should be added here. -add_executable(${BINARY_NAME} - "main.cc" - "my_application.cc" - "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" -) - -# Apply the standard set of build settings. This can be removed for applications -# that need different build settings. -apply_standard_settings(${BINARY_NAME}) - -# Add dependency libraries. Add any application-specific dependencies here. -target_link_libraries(${BINARY_NAME} PRIVATE flutter) -target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK) - -# Run the Flutter tool portions of the build. This must not be removed. -add_dependencies(${BINARY_NAME} flutter_assemble) - -# Only the install-generated bundle's copy of the executable will launch -# correctly, since the resources must in the right relative locations. To avoid -# people trying to run the unbundled copy, put it in a subdirectory instead of -# the default top-level location. -set_target_properties(${BINARY_NAME} - PROPERTIES - RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run" -) - -# Generated plugin build rules, which manage building the plugins and adding -# them to the application. -include(flutter/generated_plugins.cmake) - - -# === Installation === -# By default, "installing" just makes a relocatable bundle in the build -# directory. -set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle") -if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) - set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) -endif() - -# Start with a clean build bundle directory every time. -install(CODE " - file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\") - " COMPONENT Runtime) - -set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") -set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib") - -install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" - COMPONENT Runtime) - -install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" - COMPONENT Runtime) - -install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" - COMPONENT Runtime) - -foreach(bundled_library ${PLUGIN_BUNDLED_LIBRARIES}) - install(FILES "${bundled_library}" - DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" - COMPONENT Runtime) -endforeach(bundled_library) - -# Fully re-copy the assets directory on each build to avoid having stale files -# from a previous install. -set(FLUTTER_ASSET_DIR_NAME "flutter_assets") -install(CODE " - file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") - " COMPONENT Runtime) -install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" - DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) - -# Install the AOT library on non-Debug builds only. -if(NOT CMAKE_BUILD_TYPE MATCHES "Debug") - install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" - COMPONENT Runtime) -endif() diff --git a/linux/flutter/CMakeLists.txt b/linux/flutter/CMakeLists.txt deleted file mode 100644 index d5bd0164..00000000 --- a/linux/flutter/CMakeLists.txt +++ /dev/null @@ -1,88 +0,0 @@ -# This file controls Flutter-level build steps. It should not be edited. -cmake_minimum_required(VERSION 3.10) - -set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") - -# Configuration provided via flutter tool. -include(${EPHEMERAL_DIR}/generated_config.cmake) - -# TODO: Move the rest of this into files in ephemeral. See -# https://github.com/flutter/flutter/issues/57146. - -# Serves the same purpose as list(TRANSFORM ... PREPEND ...), -# which isn't available in 3.10. -function(list_prepend LIST_NAME PREFIX) - set(NEW_LIST "") - foreach(element ${${LIST_NAME}}) - list(APPEND NEW_LIST "${PREFIX}${element}") - endforeach(element) - set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE) -endfunction() - -# === Flutter Library === -# System-level dependencies. -find_package(PkgConfig REQUIRED) -pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) -pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0) -pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0) - -set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so") - -# Published to parent scope for install step. -set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) -set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) -set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) -set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE) - -list(APPEND FLUTTER_LIBRARY_HEADERS - "fl_basic_message_channel.h" - "fl_binary_codec.h" - "fl_binary_messenger.h" - "fl_dart_project.h" - "fl_engine.h" - "fl_json_message_codec.h" - "fl_json_method_codec.h" - "fl_message_codec.h" - "fl_method_call.h" - "fl_method_channel.h" - "fl_method_codec.h" - "fl_method_response.h" - "fl_plugin_registrar.h" - "fl_plugin_registry.h" - "fl_standard_message_codec.h" - "fl_standard_method_codec.h" - "fl_string_codec.h" - "fl_value.h" - "fl_view.h" - "flutter_linux.h" -) -list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/") -add_library(flutter INTERFACE) -target_include_directories(flutter INTERFACE - "${EPHEMERAL_DIR}" -) -target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}") -target_link_libraries(flutter INTERFACE - PkgConfig::GTK - PkgConfig::GLIB - PkgConfig::GIO -) -add_dependencies(flutter flutter_assemble) - -# === Flutter tool backend === -# _phony_ is a non-existent file to force this command to run every time, -# since currently there's no way to get a full input/output list from the -# flutter tool. -add_custom_command( - OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} - ${CMAKE_CURRENT_BINARY_DIR}/_phony_ - COMMAND ${CMAKE_COMMAND} -E env - ${FLUTTER_TOOL_ENVIRONMENT} - "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh" - ${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE} - VERBATIM -) -add_custom_target(flutter_assemble DEPENDS - "${FLUTTER_LIBRARY}" - ${FLUTTER_LIBRARY_HEADERS} -) diff --git a/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc deleted file mode 100644 index 1560fc80..00000000 --- a/linux/flutter/generated_plugin_registrant.cc +++ /dev/null @@ -1,19 +0,0 @@ -// -// Generated file. Do not edit. -// - -// clang-format off - -#include "generated_plugin_registrant.h" - -#include -#include - -void fl_register_plugins(FlPluginRegistry* registry) { - g_autoptr(FlPluginRegistrar) pasteboard_registrar = - fl_plugin_registry_get_registrar_for_plugin(registry, "PasteboardPlugin"); - pasteboard_plugin_register_with_registrar(pasteboard_registrar); - g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar = - fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin"); - url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar); -} diff --git a/linux/flutter/generated_plugin_registrant.h b/linux/flutter/generated_plugin_registrant.h deleted file mode 100644 index e0f0a47b..00000000 --- a/linux/flutter/generated_plugin_registrant.h +++ /dev/null @@ -1,15 +0,0 @@ -// -// Generated file. Do not edit. -// - -// clang-format off - -#ifndef GENERATED_PLUGIN_REGISTRANT_ -#define GENERATED_PLUGIN_REGISTRANT_ - -#include - -// Registers Flutter plugins. -void fl_register_plugins(FlPluginRegistry* registry); - -#endif // GENERATED_PLUGIN_REGISTRANT_ diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake deleted file mode 100644 index 5a3c3882..00000000 --- a/linux/flutter/generated_plugins.cmake +++ /dev/null @@ -1,25 +0,0 @@ -# -# Generated file, do not edit. -# - -list(APPEND FLUTTER_PLUGIN_LIST - pasteboard - url_launcher_linux -) - -list(APPEND FLUTTER_FFI_PLUGIN_LIST -) - -set(PLUGIN_BUNDLED_LIBRARIES) - -foreach(plugin ${FLUTTER_PLUGIN_LIST}) - add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin}) - target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) - list(APPEND PLUGIN_BUNDLED_LIBRARIES $) - list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) -endforeach(plugin) - -foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) - add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin}) - list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) -endforeach(ffi_plugin) diff --git a/linux/main.cc b/linux/main.cc deleted file mode 100644 index e7c5c543..00000000 --- a/linux/main.cc +++ /dev/null @@ -1,6 +0,0 @@ -#include "my_application.h" - -int main(int argc, char** argv) { - g_autoptr(MyApplication) app = my_application_new(); - return g_application_run(G_APPLICATION(app), argc, argv); -} diff --git a/linux/my_application.cc b/linux/my_application.cc deleted file mode 100644 index 15be13ed..00000000 --- a/linux/my_application.cc +++ /dev/null @@ -1,104 +0,0 @@ -#include "my_application.h" - -#include -#ifdef GDK_WINDOWING_X11 -#include -#endif - -#include "flutter/generated_plugin_registrant.h" - -struct _MyApplication { - GtkApplication parent_instance; - char** dart_entrypoint_arguments; -}; - -G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION) - -// Implements GApplication::activate. -static void my_application_activate(GApplication* application) { - MyApplication* self = MY_APPLICATION(application); - GtkWindow* window = - GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application))); - - // Use a header bar when running in GNOME as this is the common style used - // by applications and is the setup most users will be using (e.g. Ubuntu - // desktop). - // If running on X and not using GNOME then just use a traditional title bar - // in case the window manager does more exotic layout, e.g. tiling. - // If running on Wayland assume the header bar will work (may need changing - // if future cases occur). - gboolean use_header_bar = TRUE; -#ifdef GDK_WINDOWING_X11 - GdkScreen* screen = gtk_window_get_screen(window); - if (GDK_IS_X11_SCREEN(screen)) { - const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen); - if (g_strcmp0(wm_name, "GNOME Shell") != 0) { - use_header_bar = FALSE; - } - } -#endif - if (use_header_bar) { - GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new()); - gtk_widget_show(GTK_WIDGET(header_bar)); - gtk_header_bar_set_title(header_bar, "web"); - gtk_header_bar_set_show_close_button(header_bar, TRUE); - gtk_window_set_titlebar(window, GTK_WIDGET(header_bar)); - } else { - gtk_window_set_title(window, "web"); - } - - gtk_window_set_default_size(window, 1280, 720); - gtk_widget_show(GTK_WIDGET(window)); - - g_autoptr(FlDartProject) project = fl_dart_project_new(); - fl_dart_project_set_dart_entrypoint_arguments(project, self->dart_entrypoint_arguments); - - FlView* view = fl_view_new(project); - gtk_widget_show(GTK_WIDGET(view)); - gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view)); - - fl_register_plugins(FL_PLUGIN_REGISTRY(view)); - - gtk_widget_grab_focus(GTK_WIDGET(view)); -} - -// Implements GApplication::local_command_line. -static gboolean my_application_local_command_line(GApplication* application, gchar*** arguments, int* exit_status) { - MyApplication* self = MY_APPLICATION(application); - // Strip out the first argument as it is the binary name. - self->dart_entrypoint_arguments = g_strdupv(*arguments + 1); - - g_autoptr(GError) error = nullptr; - if (!g_application_register(application, nullptr, &error)) { - g_warning("Failed to register: %s", error->message); - *exit_status = 1; - return TRUE; - } - - g_application_activate(application); - *exit_status = 0; - - return TRUE; -} - -// Implements GObject::dispose. -static void my_application_dispose(GObject* object) { - MyApplication* self = MY_APPLICATION(object); - g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev); - G_OBJECT_CLASS(my_application_parent_class)->dispose(object); -} - -static void my_application_class_init(MyApplicationClass* klass) { - G_APPLICATION_CLASS(klass)->activate = my_application_activate; - G_APPLICATION_CLASS(klass)->local_command_line = my_application_local_command_line; - G_OBJECT_CLASS(klass)->dispose = my_application_dispose; -} - -static void my_application_init(MyApplication* self) {} - -MyApplication* my_application_new() { - return MY_APPLICATION(g_object_new(my_application_get_type(), - "application-id", APPLICATION_ID, - "flags", G_APPLICATION_NON_UNIQUE, - nullptr)); -} diff --git a/linux/my_application.h b/linux/my_application.h deleted file mode 100644 index 72271d5e..00000000 --- a/linux/my_application.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef FLUTTER_MY_APPLICATION_H_ -#define FLUTTER_MY_APPLICATION_H_ - -#include - -G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION, - GtkApplication) - -/** - * my_application_new: - * - * Creates a new Flutter-based application. - * - * Returns: a new #MyApplication. - */ -MyApplication* my_application_new(); - -#endif // FLUTTER_MY_APPLICATION_H_ diff --git a/macos/.gitignore b/macos/.gitignore deleted file mode 100644 index 746adbb6..00000000 --- a/macos/.gitignore +++ /dev/null @@ -1,7 +0,0 @@ -# Flutter-related -**/Flutter/ephemeral/ -**/Pods/ - -# Xcode-related -**/dgph -**/xcuserdata/ diff --git a/macos/Flutter/Flutter-Debug.xcconfig b/macos/Flutter/Flutter-Debug.xcconfig deleted file mode 100644 index c2efd0b6..00000000 --- a/macos/Flutter/Flutter-Debug.xcconfig +++ /dev/null @@ -1 +0,0 @@ -#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/macos/Flutter/Flutter-Release.xcconfig b/macos/Flutter/Flutter-Release.xcconfig deleted file mode 100644 index c2efd0b6..00000000 --- a/macos/Flutter/Flutter-Release.xcconfig +++ /dev/null @@ -1 +0,0 @@ -#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift deleted file mode 100644 index faaa29a7..00000000 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ /dev/null @@ -1,30 +0,0 @@ -// -// Generated file. Do not edit. -// - -import FlutterMacOS -import Foundation - -import device_info_plus -import firebase_core -import firebase_messaging -import flutter_local_notifications -import pasteboard -import path_provider_macos -import shared_preferences_macos -import sqflite -import url_launcher_macos -import wakelock_macos - -func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { - DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin")) - FLTFirebaseCorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCorePlugin")) - FLTFirebaseMessagingPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseMessagingPlugin")) - FlutterLocalNotificationsPlugin.register(with: registry.registrar(forPlugin: "FlutterLocalNotificationsPlugin")) - PasteboardPlugin.register(with: registry.registrar(forPlugin: "PasteboardPlugin")) - PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) - SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) - SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin")) - UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) - WakelockMacosPlugin.register(with: registry.registrar(forPlugin: "WakelockMacosPlugin")) -} diff --git a/macos/Runner.xcodeproj/project.pbxproj b/macos/Runner.xcodeproj/project.pbxproj deleted file mode 100644 index ac7fe6d7..00000000 --- a/macos/Runner.xcodeproj/project.pbxproj +++ /dev/null @@ -1,572 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 51; - objects = { - -/* Begin PBXAggregateTarget section */ - 33CC111A2044C6BA0003C045 /* Flutter Assemble */ = { - isa = PBXAggregateTarget; - buildConfigurationList = 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */; - buildPhases = ( - 33CC111E2044C6BF0003C045 /* ShellScript */, - ); - dependencies = ( - ); - name = "Flutter Assemble"; - productName = FLX; - }; -/* End PBXAggregateTarget section */ - -/* Begin PBXBuildFile section */ - 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; }; - 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; }; - 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; }; - 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; }; - 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; }; -/* End PBXBuildFile section */ - -/* Begin PBXContainerItemProxy section */ - 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 33CC10E52044A3C60003C045 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 33CC111A2044C6BA0003C045; - remoteInfo = FLX; - }; -/* End PBXContainerItemProxy section */ - -/* Begin PBXCopyFilesBuildPhase section */ - 33CC110E2044A8840003C045 /* Bundle Framework */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 10; - files = ( - ); - name = "Bundle Framework"; - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXCopyFilesBuildPhase section */ - -/* Begin PBXFileReference section */ - 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = ""; }; - 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = ""; }; - 33CC10ED2044A3C60003C045 /* web.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "web.app"; sourceTree = BUILT_PRODUCTS_DIR; }; - 33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; - 33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = ""; }; - 33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; - 33CC10F72044A3C60003C045 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = Runner/Info.plist; sourceTree = ""; }; - 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainFlutterWindow.swift; sourceTree = ""; }; - 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Debug.xcconfig"; sourceTree = ""; }; - 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Release.xcconfig"; sourceTree = ""; }; - 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "Flutter-Generated.xcconfig"; path = "ephemeral/Flutter-Generated.xcconfig"; sourceTree = ""; }; - 33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = ""; }; - 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = ""; }; - 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = ""; }; - 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; - 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 33CC10EA2044A3C60003C045 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 33BA886A226E78AF003329D5 /* Configs */ = { - isa = PBXGroup; - children = ( - 33E5194F232828860026EE4D /* AppInfo.xcconfig */, - 9740EEB21CF90195004384FC /* Debug.xcconfig */, - 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, - 333000ED22D3DE5D00554162 /* Warnings.xcconfig */, - ); - path = Configs; - sourceTree = ""; - }; - 33CC10E42044A3C60003C045 = { - isa = PBXGroup; - children = ( - 33FAB671232836740065AC1E /* Runner */, - 33CEB47122A05771004F2AC0 /* Flutter */, - 33CC10EE2044A3C60003C045 /* Products */, - D73912EC22F37F3D000D13A0 /* Frameworks */, - ); - sourceTree = ""; - }; - 33CC10EE2044A3C60003C045 /* Products */ = { - isa = PBXGroup; - children = ( - 33CC10ED2044A3C60003C045 /* web.app */, - ); - name = Products; - sourceTree = ""; - }; - 33CC11242044D66E0003C045 /* Resources */ = { - isa = PBXGroup; - children = ( - 33CC10F22044A3C60003C045 /* Assets.xcassets */, - 33CC10F42044A3C60003C045 /* MainMenu.xib */, - 33CC10F72044A3C60003C045 /* Info.plist */, - ); - name = Resources; - path = ..; - sourceTree = ""; - }; - 33CEB47122A05771004F2AC0 /* Flutter */ = { - isa = PBXGroup; - children = ( - 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */, - 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */, - 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */, - 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */, - ); - path = Flutter; - sourceTree = ""; - }; - 33FAB671232836740065AC1E /* Runner */ = { - isa = PBXGroup; - children = ( - 33CC10F02044A3C60003C045 /* AppDelegate.swift */, - 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */, - 33E51913231747F40026EE4D /* DebugProfile.entitlements */, - 33E51914231749380026EE4D /* Release.entitlements */, - 33CC11242044D66E0003C045 /* Resources */, - 33BA886A226E78AF003329D5 /* Configs */, - ); - path = Runner; - sourceTree = ""; - }; - D73912EC22F37F3D000D13A0 /* Frameworks */ = { - isa = PBXGroup; - children = ( - ); - name = Frameworks; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - 33CC10EC2044A3C60003C045 /* Runner */ = { - isa = PBXNativeTarget; - buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */; - buildPhases = ( - 33CC10E92044A3C60003C045 /* Sources */, - 33CC10EA2044A3C60003C045 /* Frameworks */, - 33CC10EB2044A3C60003C045 /* Resources */, - 33CC110E2044A8840003C045 /* Bundle Framework */, - 3399D490228B24CF009A79C7 /* ShellScript */, - ); - buildRules = ( - ); - dependencies = ( - 33CC11202044C79F0003C045 /* PBXTargetDependency */, - ); - name = Runner; - productName = Runner; - productReference = 33CC10ED2044A3C60003C045 /* web.app */; - productType = "com.apple.product-type.application"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 33CC10E52044A3C60003C045 /* Project object */ = { - isa = PBXProject; - attributes = { - LastSwiftUpdateCheck = 0920; - LastUpgradeCheck = 1300; - ORGANIZATIONNAME = ""; - TargetAttributes = { - 33CC10EC2044A3C60003C045 = { - CreatedOnToolsVersion = 9.2; - LastSwiftMigration = 1100; - ProvisioningStyle = Automatic; - SystemCapabilities = { - com.apple.Sandbox = { - enabled = 1; - }; - }; - }; - 33CC111A2044C6BA0003C045 = { - CreatedOnToolsVersion = 9.2; - ProvisioningStyle = Manual; - }; - }; - }; - buildConfigurationList = 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */; - compatibilityVersion = "Xcode 9.3"; - developmentRegion = en; - hasScannedForEncodings = 0; - knownRegions = ( - en, - Base, - ); - mainGroup = 33CC10E42044A3C60003C045; - productRefGroup = 33CC10EE2044A3C60003C045 /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - 33CC10EC2044A3C60003C045 /* Runner */, - 33CC111A2044C6BA0003C045 /* Flutter Assemble */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - 33CC10EB2044A3C60003C045 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */, - 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXShellScriptBuildPhase section */ - 3399D490228B24CF009A79C7 /* ShellScript */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - ); - outputFileListPaths = ( - ); - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "echo \"$PRODUCT_NAME.app\" > \"$PROJECT_DIR\"/Flutter/ephemeral/.app_filename && \"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh embed\n"; - }; - 33CC111E2044C6BF0003C045 /* ShellScript */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - Flutter/ephemeral/FlutterInputs.xcfilelist, - ); - inputPaths = ( - Flutter/ephemeral/tripwire, - ); - outputFileListPaths = ( - Flutter/ephemeral/FlutterOutputs.xcfilelist, - ); - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire"; - }; -/* End PBXShellScriptBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 33CC10E92044A3C60003C045 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */, - 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */, - 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXTargetDependency section */ - 33CC11202044C79F0003C045 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 33CC111A2044C6BA0003C045 /* Flutter Assemble */; - targetProxy = 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */; - }; -/* End PBXTargetDependency section */ - -/* Begin PBXVariantGroup section */ - 33CC10F42044A3C60003C045 /* MainMenu.xib */ = { - isa = PBXVariantGroup; - children = ( - 33CC10F52044A3C60003C045 /* Base */, - ); - name = MainMenu.xib; - path = Runner; - sourceTree = ""; - }; -/* End PBXVariantGroup section */ - -/* Begin XCBuildConfiguration section */ - 338D0CE9231458BD00FA5F75 /* Profile */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CODE_SIGN_IDENTITY = "-"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.11; - MTL_ENABLE_DEBUG_INFO = NO; - SDKROOT = macosx; - SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_OPTIMIZATION_LEVEL = "-O"; - }; - name = Profile; - }; - 338D0CEA231458BD00FA5F75 /* Profile */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_MODULES = YES; - CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; - CODE_SIGN_STYLE = Automatic; - COMBINE_HIDPI_IMAGES = YES; - INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/../Frameworks", - ); - PROVISIONING_PROFILE_SPECIFIER = ""; - SWIFT_VERSION = 5.0; - }; - name = Profile; - }; - 338D0CEB231458BD00FA5F75 /* Profile */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_STYLE = Manual; - PRODUCT_NAME = "$(TARGET_NAME)"; - }; - name = Profile; - }; - 33CC10F92044A3C60003C045 /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CODE_SIGN_IDENTITY = "-"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = dwarf; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.11; - MTL_ENABLE_DEBUG_INFO = YES; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = macosx; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - }; - name = Debug; - }; - 33CC10FA2044A3C60003C045 /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CODE_SIGN_IDENTITY = "-"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.11; - MTL_ENABLE_DEBUG_INFO = NO; - SDKROOT = macosx; - SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_OPTIMIZATION_LEVEL = "-O"; - }; - name = Release; - }; - 33CC10FC2044A3C60003C045 /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_MODULES = YES; - CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; - CODE_SIGN_STYLE = Automatic; - COMBINE_HIDPI_IMAGES = YES; - INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/../Frameworks", - ); - PROVISIONING_PROFILE_SPECIFIER = ""; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 5.0; - }; - name = Debug; - }; - 33CC10FD2044A3C60003C045 /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_MODULES = YES; - CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements; - CODE_SIGN_STYLE = Automatic; - COMBINE_HIDPI_IMAGES = YES; - INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/../Frameworks", - ); - PROVISIONING_PROFILE_SPECIFIER = ""; - SWIFT_VERSION = 5.0; - }; - name = Release; - }; - 33CC111C2044C6BA0003C045 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_STYLE = Manual; - PRODUCT_NAME = "$(TARGET_NAME)"; - }; - name = Debug; - }; - 33CC111D2044C6BA0003C045 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_STYLE = Automatic; - PRODUCT_NAME = "$(TARGET_NAME)"; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 33CC10F92044A3C60003C045 /* Debug */, - 33CC10FA2044A3C60003C045 /* Release */, - 338D0CE9231458BD00FA5F75 /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 33CC10FC2044A3C60003C045 /* Debug */, - 33CC10FD2044A3C60003C045 /* Release */, - 338D0CEA231458BD00FA5F75 /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 33CC111C2044C6BA0003C045 /* Debug */, - 33CC111D2044C6BA0003C045 /* Release */, - 338D0CEB231458BD00FA5F75 /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = 33CC10E52044A3C60003C045 /* Project object */; -} diff --git a/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist deleted file mode 100644 index 18d98100..00000000 --- a/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEDidComputeMac32BitWarning - - - diff --git a/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme deleted file mode 100644 index df0c1df5..00000000 --- a/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ /dev/null @@ -1,87 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/macos/Runner.xcworkspace/contents.xcworkspacedata b/macos/Runner.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 1d526a16..00000000 --- a/macos/Runner.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,7 +0,0 @@ - - - - - diff --git a/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist deleted file mode 100644 index 18d98100..00000000 --- a/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEDidComputeMac32BitWarning - - - diff --git a/macos/Runner/AppDelegate.swift b/macos/Runner/AppDelegate.swift deleted file mode 100644 index d53ef643..00000000 --- a/macos/Runner/AppDelegate.swift +++ /dev/null @@ -1,9 +0,0 @@ -import Cocoa -import FlutterMacOS - -@NSApplicationMain -class AppDelegate: FlutterAppDelegate { - override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { - return true - } -} diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json deleted file mode 100644 index a2ec33f1..00000000 --- a/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json +++ /dev/null @@ -1,68 +0,0 @@ -{ - "images" : [ - { - "size" : "16x16", - "idiom" : "mac", - "filename" : "app_icon_16.png", - "scale" : "1x" - }, - { - "size" : "16x16", - "idiom" : "mac", - "filename" : "app_icon_32.png", - "scale" : "2x" - }, - { - "size" : "32x32", - "idiom" : "mac", - "filename" : "app_icon_32.png", - "scale" : "1x" - }, - { - "size" : "32x32", - "idiom" : "mac", - "filename" : "app_icon_64.png", - "scale" : "2x" - }, - { - "size" : "128x128", - "idiom" : "mac", - "filename" : "app_icon_128.png", - "scale" : "1x" - }, - { - "size" : "128x128", - "idiom" : "mac", - "filename" : "app_icon_256.png", - "scale" : "2x" - }, - { - "size" : "256x256", - "idiom" : "mac", - "filename" : "app_icon_256.png", - "scale" : "1x" - }, - { - "size" : "256x256", - "idiom" : "mac", - "filename" : "app_icon_512.png", - "scale" : "2x" - }, - { - "size" : "512x512", - "idiom" : "mac", - "filename" : "app_icon_512.png", - "scale" : "1x" - }, - { - "size" : "512x512", - "idiom" : "mac", - "filename" : "app_icon_1024.png", - "scale" : "2x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png deleted file mode 100644 index 82b6f9d9a33e198f5747104729e1fcef999772a5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 102994 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ⅅ8MglG&ER{i5lN315cO?EdHNLrg? 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 z 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 Pe7HEdAEg|=c=LY&YVNkY diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png deleted file mode 100644 index 13b35eba55c6dabc3aac36f33d859266c18fa0d0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5680 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 diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png deleted file mode 100644 index 0a3f5fa40fb3d1e0710331a48de5d256da3f275d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 520 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 diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png deleted file mode 100644 index 2f1632cfddf3d9dade342351e627a0a75609fb46..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2218 zcmV;b2vzrqP)Px#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91K%fHv1ONa40RR91KmY&$07g+lumAuE6iGxuRCodHTWf3-RTMruyW6Fu zQYeUM04eX6D5c0FCjKKPrco1(K`<0SL=crI{PC3-^hZU0kQie$gh-5!7z6SH6Q0J% zqot*`H1q{R5fHFYS}dje@;kG=v$L0(yY0?wY2%*c?A&{2?!D*x?m71{of2gv!$5|C z3>qG_BW}7K_yUcT3A5C6QD<+{aq?x;MAUyAiJn#Jv8_zZtQ{P zTRzbL3U9!qVuZzS$xKU10KiW~Bgdcv1-!uAhQxf3a7q+dU6lj?yoO4Lq4TUN4}h{N z*fIM=SS8|C2$(T>w$`t@3Tka!(r!7W`x z-isCVgQD^mG-MJ;XtJuK3V{Vy72GQ83KRWsHU?e*wrhKk=ApIYeDqLi;JI1e zuvv}5^Dc=k7F7?nm3nIw$NVmU-+R>> zyqOR$-2SDpJ}Pt;^RkJytDVXNTsu|mI1`~G7yw`EJR?VkGfNdqK9^^8P`JdtTV&tX4CNcV4 z&N06nZa??Fw1AgQOUSE2AmPE@WO(Fvo`%m`cDgiv(fAeRA%3AGXUbsGw{7Q`cY;1BI#ac3iN$$Hw z0LT0;xc%=q)me?Y*$xI@GRAw?+}>=9D+KTk??-HJ4=A>`V&vKFS75@MKdSF1JTq{S zc1!^8?YA|t+uKigaq!sT;Z!&0F2=k7F0PIU;F$leJLaw2UI6FL^w}OG&!;+b%ya1c z1n+6-inU<0VM-Y_s5iTElq)ThyF?StVcebpGI znw#+zLx2@ah{$_2jn+@}(zJZ{+}_N9BM;z)0yr|gF-4=Iyu@hI*Lk=-A8f#bAzc9f z`Kd6K--x@t04swJVC3JK1cHY-Hq+=|PN-VO;?^_C#;coU6TDP7Bt`;{JTG;!+jj(` zw5cLQ-(Cz-Tlb`A^w7|R56Ce;Wmr0)$KWOUZ6ai0PhzPeHwdl0H(etP zUV`va_i0s-4#DkNM8lUlqI7>YQLf)(lz9Q3Uw`)nc(z3{m5ZE77Ul$V%m)E}3&8L0 z-XaU|eB~Is08eORPk;=<>!1w)Kf}FOVS2l&9~A+@R#koFJ$Czd%Y(ENTV&A~U(IPI z;UY+gf+&6ioZ=roly<0Yst8ck>(M=S?B-ys3mLdM&)ex!hbt+ol|T6CTS+Sc0jv(& z7ijdvFwBq;0a{%3GGwkDKTeG`b+lyj0jjS1OMkYnepCdoosNY`*zmBIo*981BU%%U z@~$z0V`OVtIbEx5pa|Tct|Lg#ZQf5OYMUMRD>Wdxm5SAqV2}3!ceE-M2 z@O~lQ0OiKQp}o9I;?uxCgYVV?FH|?Riri*U$Zi_`V2eiA>l zdSm6;SEm6#T+SpcE8Ro_f2AwxzI z44hfe^WE3!h@W3RDyA_H440cpmYkv*)6m1XazTqw%=E5Xv7^@^^T7Q2wxr+Z2kVYr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/macos/Runner/Configs/AppInfo.xcconfig b/macos/Runner/Configs/AppInfo.xcconfig deleted file mode 100644 index 99dd48ff..00000000 --- a/macos/Runner/Configs/AppInfo.xcconfig +++ /dev/null @@ -1,14 +0,0 @@ -// Application-level settings for the Runner target. -// -// This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the -// future. If not, the values below would default to using the project name when this becomes a -// 'flutter create' template. - -// The application's name. By default this is also the title of the Flutter window. -PRODUCT_NAME = web - -// The application's bundle identifier -PRODUCT_BUNDLE_IDENTIFIER = com.example.web - -// The copyright displayed in application information -PRODUCT_COPYRIGHT = Copyright © 2022 com.example. All rights reserved. diff --git a/macos/Runner/Configs/Debug.xcconfig b/macos/Runner/Configs/Debug.xcconfig deleted file mode 100644 index 36b0fd94..00000000 --- a/macos/Runner/Configs/Debug.xcconfig +++ /dev/null @@ -1,2 +0,0 @@ -#include "../../Flutter/Flutter-Debug.xcconfig" -#include "Warnings.xcconfig" diff --git a/macos/Runner/Configs/Release.xcconfig b/macos/Runner/Configs/Release.xcconfig deleted file mode 100644 index dff4f495..00000000 --- a/macos/Runner/Configs/Release.xcconfig +++ /dev/null @@ -1,2 +0,0 @@ -#include "../../Flutter/Flutter-Release.xcconfig" -#include "Warnings.xcconfig" diff --git a/macos/Runner/Configs/Warnings.xcconfig b/macos/Runner/Configs/Warnings.xcconfig deleted file mode 100644 index 42bcbf47..00000000 --- a/macos/Runner/Configs/Warnings.xcconfig +++ /dev/null @@ -1,13 +0,0 @@ -WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings -GCC_WARN_UNDECLARED_SELECTOR = YES -CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES -CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE -CLANG_WARN__DUPLICATE_METHOD_MATCH = YES -CLANG_WARN_PRAGMA_PACK = YES -CLANG_WARN_STRICT_PROTOTYPES = YES -CLANG_WARN_COMMA = YES -GCC_WARN_STRICT_SELECTOR_MATCH = YES -CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES -CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES -GCC_WARN_SHADOW = YES -CLANG_WARN_UNREACHABLE_CODE = YES diff --git a/macos/Runner/DebugProfile.entitlements b/macos/Runner/DebugProfile.entitlements deleted file mode 100644 index dddb8a30..00000000 --- a/macos/Runner/DebugProfile.entitlements +++ /dev/null @@ -1,12 +0,0 @@ - - - - - com.apple.security.app-sandbox - - com.apple.security.cs.allow-jit - - com.apple.security.network.server - - - diff --git a/macos/Runner/Info.plist b/macos/Runner/Info.plist deleted file mode 100644 index 4789daa6..00000000 --- a/macos/Runner/Info.plist +++ /dev/null @@ -1,32 +0,0 @@ - - - - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIconFile - - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - APPL - CFBundleShortVersionString - $(FLUTTER_BUILD_NAME) - CFBundleVersion - $(FLUTTER_BUILD_NUMBER) - LSMinimumSystemVersion - $(MACOSX_DEPLOYMENT_TARGET) - NSHumanReadableCopyright - $(PRODUCT_COPYRIGHT) - NSMainNibFile - MainMenu - NSPrincipalClass - NSApplication - - diff --git a/macos/Runner/MainFlutterWindow.swift b/macos/Runner/MainFlutterWindow.swift deleted file mode 100644 index 2722837e..00000000 --- a/macos/Runner/MainFlutterWindow.swift +++ /dev/null @@ -1,15 +0,0 @@ -import Cocoa -import FlutterMacOS - -class MainFlutterWindow: NSWindow { - override func awakeFromNib() { - let flutterViewController = FlutterViewController.init() - let windowFrame = self.frame - self.contentViewController = flutterViewController - self.setFrame(windowFrame, display: true) - - RegisterGeneratedPlugins(registry: flutterViewController) - - super.awakeFromNib() - } -} diff --git a/macos/Runner/Release.entitlements b/macos/Runner/Release.entitlements deleted file mode 100644 index 852fa1a4..00000000 --- a/macos/Runner/Release.entitlements +++ /dev/null @@ -1,8 +0,0 @@ - - - - - com.apple.security.app-sandbox - - - diff --git a/pubspec.lock b/pubspec.lock index 3ac2aede..fd1e980b 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -351,6 +351,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.0.0+1" + draw_graph: + dependency: "direct main" + description: + name: draw_graph + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.0" dropdown_search: dependency: "direct main" description: @@ -358,6 +365,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "5.0.5" + editable: + dependency: "direct main" + description: + name: editable + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.0" email_validator: dependency: "direct main" description: @@ -608,6 +622,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "6.1.7" + flutter_recaptcha_v3: + dependency: "direct main" + description: + name: flutter_recaptcha_v3 + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.3" flutter_staggered_grid_view: dependency: "direct main" description: @@ -1507,6 +1528,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.9.5" + webview_flutter_plus: + dependency: transitive + description: + name: webview_flutter_plus + url: "https://pub.dartlang.org" + source: hosted + version: "0.2.4" webview_flutter_wkwebview: dependency: transitive description: diff --git a/windows/.gitignore b/windows/.gitignore deleted file mode 100644 index d492d0d9..00000000 --- a/windows/.gitignore +++ /dev/null @@ -1,17 +0,0 @@ -flutter/ephemeral/ - -# Visual Studio user-specific files. -*.suo -*.user -*.userosscache -*.sln.docstates - -# Visual Studio build-related files. -x64/ -x86/ - -# Visual Studio cache files -# files ending in .cache can be ignored -*.[Cc]ache -# but keep track of directories ending in .cache -!*.[Cc]ache/ diff --git a/windows/CMakeLists.txt b/windows/CMakeLists.txt deleted file mode 100644 index 4d39fc2b..00000000 --- a/windows/CMakeLists.txt +++ /dev/null @@ -1,101 +0,0 @@ -# Project-level configuration. -cmake_minimum_required(VERSION 3.14) -project(web LANGUAGES CXX) - -# The name of the executable created for the application. Change this to change -# the on-disk name of your application. -set(BINARY_NAME "web") - -# Explicitly opt in to modern CMake behaviors to avoid warnings with recent -# versions of CMake. -cmake_policy(SET CMP0063 NEW) - -# Define build configuration option. -get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) -if(IS_MULTICONFIG) - set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release" - CACHE STRING "" FORCE) -else() - if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) - set(CMAKE_BUILD_TYPE "Debug" CACHE - STRING "Flutter build mode" FORCE) - set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS - "Debug" "Profile" "Release") - endif() -endif() -# Define settings for the Profile build mode. -set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}") -set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}") -set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}") -set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}") - -# Use Unicode for all projects. -add_definitions(-DUNICODE -D_UNICODE) - -# Compilation settings that should be applied to most targets. -# -# Be cautious about adding new options here, as plugins use this function by -# default. In most cases, you should add new options to specific targets instead -# of modifying this function. -function(APPLY_STANDARD_SETTINGS TARGET) - target_compile_features(${TARGET} PUBLIC cxx_std_17) - target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100") - target_compile_options(${TARGET} PRIVATE /EHsc) - target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0") - target_compile_definitions(${TARGET} PRIVATE "$<$:_DEBUG>") -endfunction() - -# Flutter library and tool build rules. -set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") -add_subdirectory(${FLUTTER_MANAGED_DIR}) - -# Application build; see runner/CMakeLists.txt. -add_subdirectory("runner") - -# Generated plugin build rules, which manage building the plugins and adding -# them to the application. -include(flutter/generated_plugins.cmake) - - -# === Installation === -# Support files are copied into place next to the executable, so that it can -# run in place. This is done instead of making a separate bundle (as on Linux) -# so that building and running from within Visual Studio will work. -set(BUILD_BUNDLE_DIR "$") -# Make the "install" step default, as it's required to run. -set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1) -if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) - set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) -endif() - -set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") -set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}") - -install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" - COMPONENT Runtime) - -install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" - COMPONENT Runtime) - -install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" - COMPONENT Runtime) - -if(PLUGIN_BUNDLED_LIBRARIES) - install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" - DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" - COMPONENT Runtime) -endif() - -# Fully re-copy the assets directory on each build to avoid having stale files -# from a previous install. -set(FLUTTER_ASSET_DIR_NAME "flutter_assets") -install(CODE " - file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") - " COMPONENT Runtime) -install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" - DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) - -# Install the AOT library on non-Debug builds only. -install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" - CONFIGURATIONS Profile;Release - COMPONENT Runtime) diff --git a/windows/flutter/CMakeLists.txt b/windows/flutter/CMakeLists.txt deleted file mode 100644 index 930d2071..00000000 --- a/windows/flutter/CMakeLists.txt +++ /dev/null @@ -1,104 +0,0 @@ -# This file controls Flutter-level build steps. It should not be edited. -cmake_minimum_required(VERSION 3.14) - -set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") - -# Configuration provided via flutter tool. -include(${EPHEMERAL_DIR}/generated_config.cmake) - -# TODO: Move the rest of this into files in ephemeral. See -# https://github.com/flutter/flutter/issues/57146. -set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") - -# === Flutter Library === -set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") - -# Published to parent scope for install step. -set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) -set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) -set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) -set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE) - -list(APPEND FLUTTER_LIBRARY_HEADERS - "flutter_export.h" - "flutter_windows.h" - "flutter_messenger.h" - "flutter_plugin_registrar.h" - "flutter_texture_registrar.h" -) -list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/") -add_library(flutter INTERFACE) -target_include_directories(flutter INTERFACE - "${EPHEMERAL_DIR}" -) -target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib") -add_dependencies(flutter flutter_assemble) - -# === Wrapper === -list(APPEND CPP_WRAPPER_SOURCES_CORE - "core_implementations.cc" - "standard_codec.cc" -) -list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/") -list(APPEND CPP_WRAPPER_SOURCES_PLUGIN - "plugin_registrar.cc" -) -list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/") -list(APPEND CPP_WRAPPER_SOURCES_APP - "flutter_engine.cc" - "flutter_view_controller.cc" -) -list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/") - -# Wrapper sources needed for a plugin. -add_library(flutter_wrapper_plugin STATIC - ${CPP_WRAPPER_SOURCES_CORE} - ${CPP_WRAPPER_SOURCES_PLUGIN} -) -apply_standard_settings(flutter_wrapper_plugin) -set_target_properties(flutter_wrapper_plugin PROPERTIES - POSITION_INDEPENDENT_CODE ON) -set_target_properties(flutter_wrapper_plugin PROPERTIES - CXX_VISIBILITY_PRESET hidden) -target_link_libraries(flutter_wrapper_plugin PUBLIC flutter) -target_include_directories(flutter_wrapper_plugin PUBLIC - "${WRAPPER_ROOT}/include" -) -add_dependencies(flutter_wrapper_plugin flutter_assemble) - -# Wrapper sources needed for the runner. -add_library(flutter_wrapper_app STATIC - ${CPP_WRAPPER_SOURCES_CORE} - ${CPP_WRAPPER_SOURCES_APP} -) -apply_standard_settings(flutter_wrapper_app) -target_link_libraries(flutter_wrapper_app PUBLIC flutter) -target_include_directories(flutter_wrapper_app PUBLIC - "${WRAPPER_ROOT}/include" -) -add_dependencies(flutter_wrapper_app flutter_assemble) - -# === Flutter tool backend === -# _phony_ is a non-existent file to force this command to run every time, -# since currently there's no way to get a full input/output list from the -# flutter tool. -set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_") -set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE) -add_custom_command( - OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} - ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN} - ${CPP_WRAPPER_SOURCES_APP} - ${PHONY_OUTPUT} - COMMAND ${CMAKE_COMMAND} -E env - ${FLUTTER_TOOL_ENVIRONMENT} - "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" - windows-x64 $ - VERBATIM -) -add_custom_target(flutter_assemble DEPENDS - "${FLUTTER_LIBRARY}" - ${FLUTTER_LIBRARY_HEADERS} - ${CPP_WRAPPER_SOURCES_CORE} - ${CPP_WRAPPER_SOURCES_PLUGIN} - ${CPP_WRAPPER_SOURCES_APP} -) diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc deleted file mode 100644 index 02129f58..00000000 --- a/windows/flutter/generated_plugin_registrant.cc +++ /dev/null @@ -1,17 +0,0 @@ -// -// Generated file. Do not edit. -// - -// clang-format off - -#include "generated_plugin_registrant.h" - -#include -#include - -void RegisterPlugins(flutter::PluginRegistry* registry) { - PasteboardPluginRegisterWithRegistrar( - registry->GetRegistrarForPlugin("PasteboardPlugin")); - UrlLauncherWindowsRegisterWithRegistrar( - registry->GetRegistrarForPlugin("UrlLauncherWindows")); -} diff --git a/windows/flutter/generated_plugin_registrant.h b/windows/flutter/generated_plugin_registrant.h deleted file mode 100644 index dc139d85..00000000 --- a/windows/flutter/generated_plugin_registrant.h +++ /dev/null @@ -1,15 +0,0 @@ -// -// Generated file. Do not edit. -// - -// clang-format off - -#ifndef GENERATED_PLUGIN_REGISTRANT_ -#define GENERATED_PLUGIN_REGISTRANT_ - -#include - -// Registers Flutter plugins. -void RegisterPlugins(flutter::PluginRegistry* registry); - -#endif // GENERATED_PLUGIN_REGISTRANT_ diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake deleted file mode 100644 index 82731c9d..00000000 --- a/windows/flutter/generated_plugins.cmake +++ /dev/null @@ -1,25 +0,0 @@ -# -# Generated file, do not edit. -# - -list(APPEND FLUTTER_PLUGIN_LIST - pasteboard - url_launcher_windows -) - -list(APPEND FLUTTER_FFI_PLUGIN_LIST -) - -set(PLUGIN_BUNDLED_LIBRARIES) - -foreach(plugin ${FLUTTER_PLUGIN_LIST}) - add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin}) - target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) - list(APPEND PLUGIN_BUNDLED_LIBRARIES $) - list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) -endforeach(plugin) - -foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) - add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin}) - list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) -endforeach(ffi_plugin) diff --git a/windows/runner/CMakeLists.txt b/windows/runner/CMakeLists.txt deleted file mode 100644 index 17411a8a..00000000 --- a/windows/runner/CMakeLists.txt +++ /dev/null @@ -1,39 +0,0 @@ -cmake_minimum_required(VERSION 3.14) -project(runner LANGUAGES CXX) - -# Define the application target. To change its name, change BINARY_NAME in the -# top-level CMakeLists.txt, not the value here, or `flutter run` will no longer -# work. -# -# Any new source files that you add to the application should be added here. -add_executable(${BINARY_NAME} WIN32 - "flutter_window.cpp" - "main.cpp" - "utils.cpp" - "win32_window.cpp" - "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" - "Runner.rc" - "runner.exe.manifest" -) - -# Apply the standard set of build settings. This can be removed for applications -# that need different build settings. -apply_standard_settings(${BINARY_NAME}) - -# Add preprocessor definitions for the build version. -target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION=\"${FLUTTER_VERSION}\"") -target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MAJOR=${FLUTTER_VERSION_MAJOR}") -target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MINOR=${FLUTTER_VERSION_MINOR}") -target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_PATCH=${FLUTTER_VERSION_PATCH}") -target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_BUILD=${FLUTTER_VERSION_BUILD}") - -# Disable Windows macros that collide with C++ standard library functions. -target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") - -# Add dependency libraries and include directories. Add any application-specific -# dependencies here. -target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app) -target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") - -# Run the Flutter tool portions of the build. This must not be removed. -add_dependencies(${BINARY_NAME} flutter_assemble) diff --git a/windows/runner/Runner.rc b/windows/runner/Runner.rc deleted file mode 100644 index 7d23baf9..00000000 --- a/windows/runner/Runner.rc +++ /dev/null @@ -1,121 +0,0 @@ -// Microsoft Visual C++ generated resource script. -// -#pragma code_page(65001) -#include "resource.h" - -#define APSTUDIO_READONLY_SYMBOLS -///////////////////////////////////////////////////////////////////////////// -// -// Generated from the TEXTINCLUDE 2 resource. -// -#include "winres.h" - -///////////////////////////////////////////////////////////////////////////// -#undef APSTUDIO_READONLY_SYMBOLS - -///////////////////////////////////////////////////////////////////////////// -// English (United States) resources - -#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) -LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US - -#ifdef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// TEXTINCLUDE -// - -1 TEXTINCLUDE -BEGIN - "resource.h\0" -END - -2 TEXTINCLUDE -BEGIN - "#include ""winres.h""\r\n" - "\0" -END - -3 TEXTINCLUDE -BEGIN - "\r\n" - "\0" -END - -#endif // APSTUDIO_INVOKED - - -///////////////////////////////////////////////////////////////////////////// -// -// Icon -// - -// Icon with lowest ID value placed first to ensure application icon -// remains consistent on all systems. -IDI_APP_ICON ICON "resources\\app_icon.ico" - - -///////////////////////////////////////////////////////////////////////////// -// -// Version -// - -#if defined(FLUTTER_VERSION_MAJOR) && defined(FLUTTER_VERSION_MINOR) && defined(FLUTTER_VERSION_PATCH) && defined(FLUTTER_VERSION_BUILD) -#define VERSION_AS_NUMBER FLUTTER_VERSION_MAJOR,FLUTTER_VERSION_MINOR,FLUTTER_VERSION_PATCH,FLUTTER_VERSION_BUILD -#else -#define VERSION_AS_NUMBER 1,0,0,0 -#endif - -#if defined(FLUTTER_VERSION) -#define VERSION_AS_STRING FLUTTER_VERSION -#else -#define VERSION_AS_STRING "1.0.0" -#endif - -VS_VERSION_INFO VERSIONINFO - FILEVERSION VERSION_AS_NUMBER - PRODUCTVERSION VERSION_AS_NUMBER - FILEFLAGSMASK VS_FFI_FILEFLAGSMASK -#ifdef _DEBUG - FILEFLAGS VS_FF_DEBUG -#else - FILEFLAGS 0x0L -#endif - FILEOS VOS__WINDOWS32 - FILETYPE VFT_APP - FILESUBTYPE 0x0L -BEGIN - BLOCK "StringFileInfo" - BEGIN - BLOCK "040904e4" - BEGIN - VALUE "CompanyName", "com.example" "\0" - VALUE "FileDescription", "web" "\0" - VALUE "FileVersion", VERSION_AS_STRING "\0" - VALUE "InternalName", "web" "\0" - VALUE "LegalCopyright", "Copyright (C) 2022 com.example. All rights reserved." "\0" - VALUE "OriginalFilename", "web.exe" "\0" - VALUE "ProductName", "web" "\0" - VALUE "ProductVersion", VERSION_AS_STRING "\0" - END - END - BLOCK "VarFileInfo" - BEGIN - VALUE "Translation", 0x409, 1252 - END -END - -#endif // English (United States) resources -///////////////////////////////////////////////////////////////////////////// - - - -#ifndef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// Generated from the TEXTINCLUDE 3 resource. -// - - -///////////////////////////////////////////////////////////////////////////// -#endif // not APSTUDIO_INVOKED diff --git a/windows/runner/flutter_window.cpp b/windows/runner/flutter_window.cpp deleted file mode 100644 index b43b9095..00000000 --- a/windows/runner/flutter_window.cpp +++ /dev/null @@ -1,61 +0,0 @@ -#include "flutter_window.h" - -#include - -#include "flutter/generated_plugin_registrant.h" - -FlutterWindow::FlutterWindow(const flutter::DartProject& project) - : project_(project) {} - -FlutterWindow::~FlutterWindow() {} - -bool FlutterWindow::OnCreate() { - if (!Win32Window::OnCreate()) { - return false; - } - - RECT frame = GetClientArea(); - - // The size here must match the window dimensions to avoid unnecessary surface - // creation / destruction in the startup path. - flutter_controller_ = std::make_unique( - frame.right - frame.left, frame.bottom - frame.top, project_); - // Ensure that basic setup of the controller was successful. - if (!flutter_controller_->engine() || !flutter_controller_->view()) { - return false; - } - RegisterPlugins(flutter_controller_->engine()); - SetChildContent(flutter_controller_->view()->GetNativeWindow()); - return true; -} - -void FlutterWindow::OnDestroy() { - if (flutter_controller_) { - flutter_controller_ = nullptr; - } - - Win32Window::OnDestroy(); -} - -LRESULT -FlutterWindow::MessageHandler(HWND hwnd, UINT const message, - WPARAM const wparam, - LPARAM const lparam) noexcept { - // Give Flutter, including plugins, an opportunity to handle window messages. - if (flutter_controller_) { - std::optional result = - flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam, - lparam); - if (result) { - return *result; - } - } - - switch (message) { - case WM_FONTCHANGE: - flutter_controller_->engine()->ReloadSystemFonts(); - break; - } - - return Win32Window::MessageHandler(hwnd, message, wparam, lparam); -} diff --git a/windows/runner/flutter_window.h b/windows/runner/flutter_window.h deleted file mode 100644 index 6da0652f..00000000 --- a/windows/runner/flutter_window.h +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef RUNNER_FLUTTER_WINDOW_H_ -#define RUNNER_FLUTTER_WINDOW_H_ - -#include -#include - -#include - -#include "win32_window.h" - -// A window that does nothing but host a Flutter view. -class FlutterWindow : public Win32Window { - public: - // Creates a new FlutterWindow hosting a Flutter view running |project|. - explicit FlutterWindow(const flutter::DartProject& project); - virtual ~FlutterWindow(); - - protected: - // Win32Window: - bool OnCreate() override; - void OnDestroy() override; - LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam, - LPARAM const lparam) noexcept override; - - private: - // The project to run. - flutter::DartProject project_; - - // The Flutter instance hosted by this window. - std::unique_ptr flutter_controller_; -}; - -#endif // RUNNER_FLUTTER_WINDOW_H_ diff --git a/windows/runner/main.cpp b/windows/runner/main.cpp deleted file mode 100644 index e6bd2d5c..00000000 --- a/windows/runner/main.cpp +++ /dev/null @@ -1,43 +0,0 @@ -#include -#include -#include - -#include "flutter_window.h" -#include "utils.h" - -int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, - _In_ wchar_t *command_line, _In_ int show_command) { - // Attach to console when present (e.g., 'flutter run') or create a - // new console when running with a debugger. - if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) { - CreateAndAttachConsole(); - } - - // Initialize COM, so that it is available for use in the library and/or - // plugins. - ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); - - flutter::DartProject project(L"data"); - - std::vector command_line_arguments = - GetCommandLineArguments(); - - project.set_dart_entrypoint_arguments(std::move(command_line_arguments)); - - FlutterWindow window(project); - Win32Window::Point origin(10, 10); - Win32Window::Size size(1280, 720); - if (!window.CreateAndShow(L"web", origin, size)) { - return EXIT_FAILURE; - } - window.SetQuitOnClose(true); - - ::MSG msg; - while (::GetMessage(&msg, nullptr, 0, 0)) { - ::TranslateMessage(&msg); - ::DispatchMessage(&msg); - } - - ::CoUninitialize(); - return EXIT_SUCCESS; -} diff --git a/windows/runner/resource.h b/windows/runner/resource.h deleted file mode 100644 index 66a65d1e..00000000 --- a/windows/runner/resource.h +++ /dev/null @@ -1,16 +0,0 @@ -//{{NO_DEPENDENCIES}} -// Microsoft Visual C++ generated include file. -// Used by Runner.rc -// -#define IDI_APP_ICON 101 - -// Next default values for new objects -// -#ifdef APSTUDIO_INVOKED -#ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 102 -#define _APS_NEXT_COMMAND_VALUE 40001 -#define _APS_NEXT_CONTROL_VALUE 1001 -#define _APS_NEXT_SYMED_VALUE 101 -#endif -#endif diff --git a/windows/runner/resources/app_icon.ico b/windows/runner/resources/app_icon.ico deleted file mode 100644 index c04e20caf6370ebb9253ad831cc31de4a9c965f6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 33772 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 diff --git a/windows/runner/runner.exe.manifest b/windows/runner/runner.exe.manifest deleted file mode 100644 index a42ea768..00000000 --- a/windows/runner/runner.exe.manifest +++ /dev/null @@ -1,20 +0,0 @@ - - - - - PerMonitorV2 - - - - - - - - - - - - - - - diff --git a/windows/runner/utils.cpp b/windows/runner/utils.cpp deleted file mode 100644 index f5bf9fa0..00000000 --- a/windows/runner/utils.cpp +++ /dev/null @@ -1,64 +0,0 @@ -#include "utils.h" - -#include -#include -#include -#include - -#include - -void CreateAndAttachConsole() { - if (::AllocConsole()) { - FILE *unused; - if (freopen_s(&unused, "CONOUT$", "w", stdout)) { - _dup2(_fileno(stdout), 1); - } - if (freopen_s(&unused, "CONOUT$", "w", stderr)) { - _dup2(_fileno(stdout), 2); - } - std::ios::sync_with_stdio(); - FlutterDesktopResyncOutputStreams(); - } -} - -std::vector GetCommandLineArguments() { - // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use. - int argc; - wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc); - if (argv == nullptr) { - return std::vector(); - } - - std::vector command_line_arguments; - - // Skip the first argument as it's the binary name. - for (int i = 1; i < argc; i++) { - command_line_arguments.push_back(Utf8FromUtf16(argv[i])); - } - - ::LocalFree(argv); - - return command_line_arguments; -} - -std::string Utf8FromUtf16(const wchar_t* utf16_string) { - if (utf16_string == nullptr) { - return std::string(); - } - int target_length = ::WideCharToMultiByte( - CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, - -1, nullptr, 0, nullptr, nullptr); - std::string utf8_string; - if (target_length == 0 || target_length > utf8_string.max_size()) { - return utf8_string; - } - utf8_string.resize(target_length); - int converted_length = ::WideCharToMultiByte( - CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, - -1, utf8_string.data(), - target_length, nullptr, nullptr); - if (converted_length == 0) { - return std::string(); - } - return utf8_string; -} diff --git a/windows/runner/utils.h b/windows/runner/utils.h deleted file mode 100644 index 3879d547..00000000 --- a/windows/runner/utils.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef RUNNER_UTILS_H_ -#define RUNNER_UTILS_H_ - -#include -#include - -// Creates a console for the process, and redirects stdout and stderr to -// it for both the runner and the Flutter library. -void CreateAndAttachConsole(); - -// Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string -// encoded in UTF-8. Returns an empty std::string on failure. -std::string Utf8FromUtf16(const wchar_t* utf16_string); - -// Gets the command line arguments passed in as a std::vector, -// encoded in UTF-8. Returns an empty std::vector on failure. -std::vector GetCommandLineArguments(); - -#endif // RUNNER_UTILS_H_ diff --git a/windows/runner/win32_window.cpp b/windows/runner/win32_window.cpp deleted file mode 100644 index c10f08dc..00000000 --- a/windows/runner/win32_window.cpp +++ /dev/null @@ -1,245 +0,0 @@ -#include "win32_window.h" - -#include - -#include "resource.h" - -namespace { - -constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW"; - -// The number of Win32Window objects that currently exist. -static int g_active_window_count = 0; - -using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd); - -// Scale helper to convert logical scaler values to physical using passed in -// scale factor -int Scale(int source, double scale_factor) { - return static_cast(source * scale_factor); -} - -// Dynamically loads the |EnableNonClientDpiScaling| from the User32 module. -// This API is only needed for PerMonitor V1 awareness mode. -void EnableFullDpiSupportIfAvailable(HWND hwnd) { - HMODULE user32_module = LoadLibraryA("User32.dll"); - if (!user32_module) { - return; - } - auto enable_non_client_dpi_scaling = - reinterpret_cast( - GetProcAddress(user32_module, "EnableNonClientDpiScaling")); - if (enable_non_client_dpi_scaling != nullptr) { - enable_non_client_dpi_scaling(hwnd); - FreeLibrary(user32_module); - } -} - -} // namespace - -// Manages the Win32Window's window class registration. -class WindowClassRegistrar { - public: - ~WindowClassRegistrar() = default; - - // Returns the singleton registar instance. - static WindowClassRegistrar* GetInstance() { - if (!instance_) { - instance_ = new WindowClassRegistrar(); - } - return instance_; - } - - // Returns the name of the window class, registering the class if it hasn't - // previously been registered. - const wchar_t* GetWindowClass(); - - // Unregisters the window class. Should only be called if there are no - // instances of the window. - void UnregisterWindowClass(); - - private: - WindowClassRegistrar() = default; - - static WindowClassRegistrar* instance_; - - bool class_registered_ = false; -}; - -WindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr; - -const wchar_t* WindowClassRegistrar::GetWindowClass() { - if (!class_registered_) { - WNDCLASS window_class{}; - window_class.hCursor = LoadCursor(nullptr, IDC_ARROW); - window_class.lpszClassName = kWindowClassName; - window_class.style = CS_HREDRAW | CS_VREDRAW; - window_class.cbClsExtra = 0; - window_class.cbWndExtra = 0; - window_class.hInstance = GetModuleHandle(nullptr); - window_class.hIcon = - LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON)); - window_class.hbrBackground = 0; - window_class.lpszMenuName = nullptr; - window_class.lpfnWndProc = Win32Window::WndProc; - RegisterClass(&window_class); - class_registered_ = true; - } - return kWindowClassName; -} - -void WindowClassRegistrar::UnregisterWindowClass() { - UnregisterClass(kWindowClassName, nullptr); - class_registered_ = false; -} - -Win32Window::Win32Window() { - ++g_active_window_count; -} - -Win32Window::~Win32Window() { - --g_active_window_count; - Destroy(); -} - -bool Win32Window::CreateAndShow(const std::wstring& title, - const Point& origin, - const Size& size) { - Destroy(); - - const wchar_t* window_class = - WindowClassRegistrar::GetInstance()->GetWindowClass(); - - const POINT target_point = {static_cast(origin.x), - static_cast(origin.y)}; - HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST); - UINT dpi = FlutterDesktopGetDpiForMonitor(monitor); - double scale_factor = dpi / 96.0; - - HWND window = CreateWindow( - window_class, title.c_str(), WS_OVERLAPPEDWINDOW | WS_VISIBLE, - Scale(origin.x, scale_factor), Scale(origin.y, scale_factor), - Scale(size.width, scale_factor), Scale(size.height, scale_factor), - nullptr, nullptr, GetModuleHandle(nullptr), this); - - if (!window) { - return false; - } - - return OnCreate(); -} - -// static -LRESULT CALLBACK Win32Window::WndProc(HWND const window, - UINT const message, - WPARAM const wparam, - LPARAM const lparam) noexcept { - if (message == WM_NCCREATE) { - auto window_struct = reinterpret_cast(lparam); - SetWindowLongPtr(window, GWLP_USERDATA, - reinterpret_cast(window_struct->lpCreateParams)); - - auto that = static_cast(window_struct->lpCreateParams); - EnableFullDpiSupportIfAvailable(window); - that->window_handle_ = window; - } else if (Win32Window* that = GetThisFromHandle(window)) { - return that->MessageHandler(window, message, wparam, lparam); - } - - return DefWindowProc(window, message, wparam, lparam); -} - -LRESULT -Win32Window::MessageHandler(HWND hwnd, - UINT const message, - WPARAM const wparam, - LPARAM const lparam) noexcept { - switch (message) { - case WM_DESTROY: - window_handle_ = nullptr; - Destroy(); - if (quit_on_close_) { - PostQuitMessage(0); - } - return 0; - - case WM_DPICHANGED: { - auto newRectSize = reinterpret_cast(lparam); - LONG newWidth = newRectSize->right - newRectSize->left; - LONG newHeight = newRectSize->bottom - newRectSize->top; - - SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth, - newHeight, SWP_NOZORDER | SWP_NOACTIVATE); - - return 0; - } - case WM_SIZE: { - RECT rect = GetClientArea(); - if (child_content_ != nullptr) { - // Size and position the child window. - MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left, - rect.bottom - rect.top, TRUE); - } - return 0; - } - - case WM_ACTIVATE: - if (child_content_ != nullptr) { - SetFocus(child_content_); - } - return 0; - } - - return DefWindowProc(window_handle_, message, wparam, lparam); -} - -void Win32Window::Destroy() { - OnDestroy(); - - if (window_handle_) { - DestroyWindow(window_handle_); - window_handle_ = nullptr; - } - if (g_active_window_count == 0) { - WindowClassRegistrar::GetInstance()->UnregisterWindowClass(); - } -} - -Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept { - return reinterpret_cast( - GetWindowLongPtr(window, GWLP_USERDATA)); -} - -void Win32Window::SetChildContent(HWND content) { - child_content_ = content; - SetParent(content, window_handle_); - RECT frame = GetClientArea(); - - MoveWindow(content, frame.left, frame.top, frame.right - frame.left, - frame.bottom - frame.top, true); - - SetFocus(child_content_); -} - -RECT Win32Window::GetClientArea() { - RECT frame; - GetClientRect(window_handle_, &frame); - return frame; -} - -HWND Win32Window::GetHandle() { - return window_handle_; -} - -void Win32Window::SetQuitOnClose(bool quit_on_close) { - quit_on_close_ = quit_on_close; -} - -bool Win32Window::OnCreate() { - // No-op; provided for subclasses. - return true; -} - -void Win32Window::OnDestroy() { - // No-op; provided for subclasses. -} diff --git a/windows/runner/win32_window.h b/windows/runner/win32_window.h deleted file mode 100644 index 17ba4311..00000000 --- a/windows/runner/win32_window.h +++ /dev/null @@ -1,98 +0,0 @@ -#ifndef RUNNER_WIN32_WINDOW_H_ -#define RUNNER_WIN32_WINDOW_H_ - -#include - -#include -#include -#include - -// A class abstraction for a high DPI-aware Win32 Window. Intended to be -// inherited from by classes that wish to specialize with custom -// rendering and input handling -class Win32Window { - public: - struct Point { - unsigned int x; - unsigned int y; - Point(unsigned int x, unsigned int y) : x(x), y(y) {} - }; - - struct Size { - unsigned int width; - unsigned int height; - Size(unsigned int width, unsigned int height) - : width(width), height(height) {} - }; - - Win32Window(); - virtual ~Win32Window(); - - // Creates and shows a win32 window with |title| and position and size using - // |origin| and |size|. New windows are created on the default monitor. Window - // sizes are specified to the OS in physical pixels, hence to ensure a - // consistent size to will treat the width height passed in to this function - // as logical pixels and scale to appropriate for the default monitor. Returns - // true if the window was created successfully. - bool CreateAndShow(const std::wstring& title, - const Point& origin, - const Size& size); - - // Release OS resources associated with window. - void Destroy(); - - // Inserts |content| into the window tree. - void SetChildContent(HWND content); - - // Returns the backing Window handle to enable clients to set icon and other - // window properties. Returns nullptr if the window has been destroyed. - HWND GetHandle(); - - // If true, closing this window will quit the application. - void SetQuitOnClose(bool quit_on_close); - - // Return a RECT representing the bounds of the current client area. - RECT GetClientArea(); - - protected: - // Processes and route salient window messages for mouse handling, - // size change and DPI. Delegates handling of these to member overloads that - // inheriting classes can handle. - virtual LRESULT MessageHandler(HWND window, - UINT const message, - WPARAM const wparam, - LPARAM const lparam) noexcept; - - // Called when CreateAndShow is called, allowing subclass window-related - // setup. Subclasses should return false if setup fails. - virtual bool OnCreate(); - - // Called when Destroy is called. - virtual void OnDestroy(); - - private: - friend class WindowClassRegistrar; - - // OS callback called by message pump. Handles the WM_NCCREATE message which - // is passed when the non-client area is being created and enables automatic - // non-client DPI scaling so that the non-client area automatically - // responsponds to changes in DPI. All other messages are handled by - // MessageHandler. - static LRESULT CALLBACK WndProc(HWND const window, - UINT const message, - WPARAM const wparam, - LPARAM const lparam) noexcept; - - // Retrieves a class instance pointer for |window| - static Win32Window* GetThisFromHandle(HWND const window) noexcept; - - bool quit_on_close_ = false; - - // window handle for top level window. - HWND window_handle_ = nullptr; - - // window handle for hosted content. - HWND child_content_ = nullptr; -}; - -#endif // RUNNER_WIN32_WINDOW_H_ From 08dabb6f0ff25aca91e5ea5d4493fa17c3a93534 Mon Sep 17 00:00:00 2001 From: Amr146 <76057767+Amr146@users.noreply.github.com> Date: Fri, 23 Dec 2022 21:38:47 +0200 Subject: [PATCH 09/16] Update pubspec.yaml --- pubspec.yaml | 3 --- 1 file changed, 3 deletions(-) diff --git a/pubspec.yaml b/pubspec.yaml index e2637456..4d022bc1 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -92,9 +92,6 @@ dependencies: flutter_html: ^2.1.1 responsive_framework: ^0.2.0 device_preview: ^1.1.0 - animated_tree_view: ^1.0.0 - buttons_tabbar: ^1.3.6 - flutter_staggered_grid_view: ^0.6.2 flutter_recaptcha_v3: ^0.1.2 draw_graph: ^1.0.0 editable: ^2.0.0 From 8fe4725440bb549c66443a73f64c9269242c52c8 Mon Sep 17 00:00:00 2001 From: Amr146 <76057767+Amr146@users.noreply.github.com> Date: Fri, 23 Dec 2022 21:41:47 +0200 Subject: [PATCH 10/16] Update home_controller.dart --- lib/home/controller/home_controller.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/home/controller/home_controller.dart b/lib/home/controller/home_controller.dart index b1d59cc5..05290a4a 100644 --- a/lib/home/controller/home_controller.dart +++ b/lib/home/controller/home_controller.dart @@ -4,7 +4,6 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:fluttertoast/fluttertoast.dart'; import 'package:get/get.dart'; -import 'package:post/create_community/screens/post.dart'; import 'package:post/createpost/model/subreddits_of_user.dart'; import 'package:post/myprofile/models/myprofile_data.dart'; import 'package:post/networks/const_endpoint_data.dart'; From 1e1a6bfb436ff93c21c41cdb371ebbdc8f43188f Mon Sep 17 00:00:00 2001 From: zeinabmoawad Date: Fri, 23 Dec 2022 22:19:11 +0200 Subject: [PATCH 11/16] Last change Without Theme --- .../widgets/create_community_web.dart | 24 +- lib/createpost.zip | Bin 0 -> 21579 bytes lib/discover/discover.dart | 10 - lib/discover/models/discover_data.dart | 6 +- lib/discover/providers/discover_provider.dart | 10 +- lib/discover/screens/discover_screen.dart | 5 +- lib/discover/widgets/grid_view_discover.dart | 52 +- .../widgets/post_video_in_discover.dart | 3 +- lib/logins/providers/authentication.dart | 21 +- lib/logins/screens/forgot_password.dart | 383 +++++++++------ lib/logins/screens/forgot_username.dart | 281 +++++++---- lib/logins/screens/login.dart | 453 ++++++++++-------- lib/logins/screens/signup.dart | 400 +++++++++------- lib/logins/widgets/text_input.dart | 1 + lib/logins/widgets/web_image.dart | 18 + lib/main.dart | 363 ++++++++------ lib/messages/Provider/message_provider.dart | 131 ++++- lib/messages/screens/message_main_screen.dart | 2 +- lib/messages/screens/new_message_screen.dart | 2 +- lib/messages/screens/show_message_body.dart | 2 +- .../screens/unread_message_screen.dart | 204 ++++++++ .../screens/web_message_all_screen.dart | 287 +++++++++++ lib/messages/screens/web_message_screen.dart | 322 +++++++++++++ .../screens/web_new_message_screen.dart | 241 ++++++++++ lib/messages/screens/web_sent_message.dart | 168 +++++++ .../models/moderated_subreddit_data.dart | 60 --- .../moderated_subreddit_provider.dart | 17 +- .../screens/moderated_subreddit_screen.dart | 6 +- .../mod_subreddit_post_sort_bottom.dart | 23 +- .../widgets/moderated_subreddit_app.dart | 5 - ...erated_subreddit_card_information_web.dart | 101 ++-- ...oderated_subreddit_pop_up_menu_button.dart | 27 +- .../widgets/moderated_subreddit_web.dart | 44 +- .../moderated_subreddite_post_web.dart | 169 ++++--- .../widgets/moderated_subriddet_posts.dart | 81 ---- .../position_for_moderated_subredditInfo.dart | 6 - lib/moderation_settings/models/traffic.dart | 41 ++ .../provider/moderation_general_data.dart | 76 +++ .../moderation_settings_provider.dart | 75 ++- .../screens/traffic_state.dart | 383 +++++++++++++++ .../screens/traffic_table.dart | 72 +++ lib/myprofile/models/myprofile_data.dart | 41 +- .../models/myprofile_followers_data.dart | 2 + .../providers/myprofile_provider.dart | 6 +- lib/myprofile/screens/myprofile_screen.dart | 4 +- .../screens/user_followers_screen.dart | 35 +- lib/myprofile/widgets/myprofile_about.dart | 2 +- lib/myprofile/widgets/myprofile_app.dart | 7 - .../myprofile_card_information_web.dart | 3 +- .../position_in_flex_app_bar_myprofile.dart | 4 - .../widgets/position_myprofile_web.dart | 7 - .../models/notification_class_model.dart | 30 +- .../screens/notifications_main_screen.dart | 127 ++--- .../screens/notifications_screen.dart | 286 ++++------- .../widgets/notification_image.dart | 5 +- .../widgets/notification_text.dart | 25 +- lib/notification/widgets/reply_back.dart | 2 +- .../models/moderated_subreddit_user_data.dart | 3 +- .../models/others_profile_data.dart | 68 +-- .../providers/other_profile_provider.dart | 21 +- .../screens/others_profile_screen.dart | 5 +- lib/other_profile/widgets/invite_button.dart | 19 +- lib/other_profile/widgets/options_button.dart | 96 ---- .../widgets/other_profile_app.dart | 6 - .../other_profile_card_information_web.dart | 23 +- .../widgets/others_profile_about.dart | 66 +-- .../widgets/overview_other_profile_web.dart | 204 +++++--- lib/other_profile/widgets/pop_down_menu.dart | 8 +- .../widgets/position_other_profile_web.dart | 81 +--- lib/post/screens/post_screen.dart | 40 +- lib/post/widgets/post.dart | 1 + lib/post/widgets/post_body.dart | 129 +++-- lib/post/widgets/post_body_in_screen.dart | 6 +- lib/post/widgets/post_comment_list.dart | 160 +++++++ lib/post/widgets/post_footer.dart | 33 +- lib/post/widgets/post_header.dart | 21 +- lib/post/widgets/post_images_in_screen.dart | 153 ++++++ lib/post/widgets/post_images_web.dart | 152 ++++++ lib/post/widgets/post_list.dart | 72 +-- lib/post/widgets/post_pop_up_web.dart | 53 ++ .../widgets/post_video_in_widget_web.dart | 186 +++++++ lib/shared/constants.dart | 6 + lib/show_post/screens/show_post_web.dart | 150 ++++++ lib/subreddit/models/subreddit_data.dart | 74 +-- .../providers/subreddit_provider.dart | 11 +- lib/subreddit/screens/subreddit_screen.dart | 6 +- lib/subreddit/widgets/notify_button_web.dart | 9 +- .../subreddit_card_information_web.dart | 11 +- .../widgets/subreddit_join_buttons.dart | 49 +- .../widgets/subreddit_pop_up_menu_button.dart | 3 +- lib/subreddit/widgets/subreddit_post_web.dart | 164 ++++--- lib/subreddit/widgets/subreddit_posts.dart | 87 ---- lib/subreddit/widgets/subreddit_web.dart | 21 +- lib/widgets/back_to_button.dart | 2 +- lib/widgets/myprofile_comment_web.dart | 10 +- lib/widgets/myprofile_post_web.dart | 179 +++---- lib/widgets/overview_myprofile_web.dart | 201 +++----- lib/widgets/post_sort_bottom.dart | 8 +- lib/widgets/profile_comments.dart | 15 +- lib/widgets/profile_posts.dart | 17 +- lib/widgets/sort_bottom_web.dart | 6 +- lib/widgets/subreddit_about.dart | 20 +- lib/widgets/subreddit_copy_share.dart | 1 - lib/widgets/subreddit_join_button_web.dart | 24 +- lib/widgets/subreddit_posts.dart | 36 +- ...ted_subreddit_pop_up_menu_button_test.dart | 10 + .../widgets/follow_button_test.dart | 21 + .../widgets/invite_button_test.dart | 19 - .../widgets/pop_down_menu_test.dart} | 2 +- .../position_other_profile_web_test.dart | 22 + .../widgets/subreddit_join_buttons_test.dart | 8 +- test/widgets/myprofile_comment_web_test.dart | 7 + test/widgets/post_sort_bottom_test.dart | 15 +- test/widgets/profile_comments_test.dart | 32 ++ test/widgets/profile_posts_test.dart | 16 + .../subreddit_join_button_web_test.dart | 8 +- 116 files changed, 5353 insertions(+), 2684 deletions(-) create mode 100644 lib/createpost.zip delete mode 100644 lib/discover/discover.dart create mode 100644 lib/logins/widgets/web_image.dart create mode 100644 lib/messages/screens/unread_message_screen.dart create mode 100644 lib/messages/screens/web_message_all_screen.dart create mode 100644 lib/messages/screens/web_message_screen.dart create mode 100644 lib/messages/screens/web_new_message_screen.dart create mode 100644 lib/messages/screens/web_sent_message.dart delete mode 100644 lib/moderated_subreddit/models/moderated_subreddit_data.dart delete mode 100644 lib/moderated_subreddit/widgets/moderated_subriddet_posts.dart create mode 100644 lib/moderation_settings/models/traffic.dart create mode 100644 lib/moderation_settings/provider/moderation_general_data.dart create mode 100644 lib/moderation_settings/screens/traffic_state.dart create mode 100644 lib/moderation_settings/screens/traffic_table.dart delete mode 100644 lib/other_profile/widgets/options_button.dart create mode 100644 lib/post/widgets/post_comment_list.dart create mode 100644 lib/post/widgets/post_images_in_screen.dart create mode 100644 lib/post/widgets/post_images_web.dart create mode 100644 lib/post/widgets/post_pop_up_web.dart create mode 100644 lib/post/widgets/post_video_in_widget_web.dart create mode 100644 lib/shared/constants.dart create mode 100644 lib/show_post/screens/show_post_web.dart delete mode 100644 lib/subreddit/widgets/subreddit_posts.dart create mode 100644 test/other_profile/widgets/follow_button_test.dart delete mode 100644 test/other_profile/widgets/invite_button_test.dart rename test/{widgets/barwidget_test.dart => other_profile/widgets/pop_down_menu_test.dart} (65%) create mode 100644 test/other_profile/widgets/position_other_profile_web_test.dart create mode 100644 test/widgets/myprofile_comment_web_test.dart create mode 100644 test/widgets/profile_comments_test.dart create mode 100644 test/widgets/profile_posts_test.dart diff --git a/lib/create_community/widgets/create_community_web.dart b/lib/create_community/widgets/create_community_web.dart index 2a9d807b..18246568 100644 --- a/lib/create_community/widgets/create_community_web.dart +++ b/lib/create_community/widgets/create_community_web.dart @@ -99,12 +99,6 @@ class _CreateCommunityWebState extends State { void initState() { // TODO: implement initState choosenCommunityType = communityType.keys.elementAt(0); - // textFieldFocus.addListener(() { - // print(textFieldFocus.hasFocus); - // if (!textFieldFocus.hasFocus) { - // _validateTextField(); - // } - // }); widget.count = 21 - widget._communityNameController.text.length; selectedIndex = 0; super.initState(); @@ -123,7 +117,7 @@ class _CreateCommunityWebState extends State { try { bool found = await Provider.of(context, listen: false) - .getCommunity(widget._communityNameController.text,context); + .getCommunity(widget._communityNameController.text, context); if (found) { setState(() { uniqueCommunityName = false; @@ -181,23 +175,13 @@ class _CreateCommunityWebState extends State { nSFW: toggleSwitch, name: widget._communityNameController.text, type: choosenCommunityType); - print(toggleSwitch); - print(widget._communityNameController.text); - print(choosenCommunityType); await Provider.of(context, listen: false) - .postCommunity(createCommunityModel.toJson(),context) + .postCommunity(createCommunityModel.toJson(), context) .then((value) { - //if(value) - // print('Community $value'); Navigator.of(context).pushNamed(ModeratedSubredditScreen.routeName, // arguments: 'Cooking' arguments: widget._communityNameController.text); }); - // if (postCommunity) { - // setState(() { - // done = true; - // }); - //} } @override @@ -269,8 +253,6 @@ class _CreateCommunityWebState extends State { 'r/', style: TextStyle(color: Colors.black45), ), - // errorMaxLines: 1, - // errorText: widget.errorMessage, focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(5), borderSide: const BorderSide(color: Colors.black)), @@ -385,7 +367,7 @@ class _CreateCommunityWebState extends State { onPressed: () async { _validateTextField(); if (validated) { - await _checkIfUnique().then((value) { + _checkIfUnique().then((value) { if (uniqueCommunityName) { _saveCommunity(); } diff --git a/lib/createpost.zip b/lib/createpost.zip new file mode 100644 index 0000000000000000000000000000000000000000..f1251ea84ba3d6145ca6c07a0db01cf6d328b9d2 GIT binary patch literal 21579 zcmaHSW0Ym6Yi`cC@## zv^27J_&W*Z&tnLHzmk+q_!smc0RTR50RU+J_oTmm&OztDyoT0L&)%_7-NRwM1@T)a z8)(;6WMP0LUb1+3B(^#^n9e$0)10Go%at251%!kdB^M|M?|9y&TMGaLkMMAVWsw-E zwOx(cyFFa1$FmbrA>K}+Fv}PN4=?f;F)d~StTgXDNxHFHDNDSaf_2J52Lj^Q6{4o% z%MmkPoCYIK|FbS;LvXZv*}+|~ zA@FsmEln8Y9pca^kQT-mins^%i4#|$2=TfNCwlwUvOUkb4lRJy7!HFFDv`wL)HWQ& zt%jsb&y<9Z)6GTpNS1x)oYk+lCe;w{#h+;Q__8dA;8Sx!I16zIjYO|@aP>r_JdPIY z5PvF!WRxVfxX}@(8!@X^^dCThPZH41x-l_Q-|Knb&FA!R@hdi1fZ@{eaP5OmivP&z zVRmR;wzY3yQ=)H~Zk<}~_XCo+2)dX3upY!}Z!CGZ%cm-HottCQm7;J^x{sldMmueX zeP?jUAKEvM!n3y%zW1qVMmL;a<{S0ARztod+u7T{4p{}d z!3d=vn=8Ko-HlgBj_YheeLmv;V*y0W1TnLvCl*}jm3pp@AVzc{SNpUvAKF9_}7h(Hr zK{PI~jinRV!?X!$tlQdIVc#T0@{b0wjH_kMj=D|AB4yJdEU*RP@Ch&h!QlNiz;O60 zkMofw79j)h#Kzn#fFrot5Lv9Y1>muvc(H0V#2~QZ(N1~IRg{*5co_R`U5WreB5)B# zTULa7R6P#|hN0_QBM`0yth9=VsZd3+GIrbS%!>%qv>+#c5e$(1?L_j3ICI7i%T38g z4Pn+)mOdW;AWjpr7B1Lt)*C6(Crp8X$uIPt(L(oAbn z&h~Rb-42_06TUngx^+Ef+i26^=k04{E6;wLCy!~|BIM=-R{@v(u^M&7@hTHfjoGB; z@9X_4Ao+De3tPM^TB*;AQBUDy$+0d=;0T+nm)mb9o^P4xZQiQP1Q)?GlHN4fnSIre z`{!3C=*3IyQt0WHCm=THZEHo&*_D+S*h(tz^-C)AL5o&ab@4L=E|HqDbJf4d;NtH? z2sr2kv~$-5Z(>4U@wx({*auOe(@^79wgv6!FqkKD!jyPmS@(julu)ja^yVc-;eKGA z;H0%sq+Aqd2~JjVw1meC7_s5R)eR?Uw@W>)?A|QQG=G10UZhD7YOTa&URLf+INPH{ z^RSAG>%zTIvJD`vC$-HOmRY7+!!Ma=+X_?Q#=BT7Y=WrP~OV3;ld~@@yj^V+rw); zwZZh9O;uOnY4+*8$#k~Q`|=hb+{6wtv59I=ubF!++&ICi@C1VkK2GGCN5(q6_g7Yi zSNHdQ-PV|^NFSvf5rn5!nQ9Ga+-ij9{Q>0bqblQw*P0?1rCvRO|6oe=3C;?1Coar` zidH%q^SDq}mkHDmb*=ieK(nkgVaHku8uRN%+*pk~a zLCe_KN7d<%Z}~S`6XyYJync{rOGW)CYsxltyGhdctC12Nv{17SgT`vt&q@D&^k zMS~m1M0NB_5^M7v%N{qx9T!_SL~2(|&8C^aliIc~U=|h$b)frz%GvnvTTI(rN!G3k z$zu2z+hbc6=m}6kx#zb@d-%?u5ci0@9`*w|4ON7=t$Qv5czjt6wjI|wXExm9(zK@T zVZZvCs-V5y#GZP0mglr*1udA8e*jyYvyAKbsr`uVTA?%%#a_=J-qPDN)@zY3xq}lN z28pzJ6D^p-Oz5?~M}@P zx4mF2y(x45>?fz^TTJhOv!upFfSc>HuF?2bRoPa?diy-P@emvjQT^&HRqGH8XuDab z=q>XNS^{}d+$&xz7g5<(r4}aGILHL;BOI@3I99R#I4*wT4ef@!3_LJiBqdhXd>DyF zjCNosCbG#Ck2RWb@PR94xTgo-TFXA=p{JWunct4DEbP&j)q`{&DjAfL%nI*n%*M2o zdq|@XpAk!4MyB2!$2Ear?=-hI)b_JyPL9$N#C^SZU4HvhZ%pr6Fg^oAcOYFL*MWK% z8>>ROlRxmqM9yB1azj*lqTIA$Sb{H~W#OBKP~r}3^;0=VdSI#`_}6kA>ZbP3rXpY% zk@Z=de;D3>r-F$8PS&h!42>-R3%5H@N>EOhy*wEJ1OP||0{|fTpTZrCtPOSkqINoe zr~l3AtP&=z`{)q@t`pzjx&V>(KuyP66JyHaG!_NJU|U$64Y~3lZm%ZaOfseWLO+f7 z0#*q;l(~kT1@2w;KbCBBS1xb;YBQS^@Yp6Ks|VU-y$g>(TyKvdO)ClOTTS(b3))z3 z4{Qt;g3Mf69z>SWO9L+BuPFG0@(XMaYJQg$dL0_5z~W6b4(Hoy-(=Qy`P_XGyOx7w z045wTfCFWx^NBY@`B5_&Q2!h;9KlZ_IFG}~Ll`6u@C*V9U^b*EMWnTZElgM;t|G-v zKVU_2a20N$EArRGso?15$Uk0ha|`2AXY^itRKL<$ku9dye^sl%@qoK=KrLEb<}^oO zm7vc7ip>WCrqsG2cifH*&XI>iUetwD0{6#V0Ux8!iEHxLPYc&xQsfTIrMraUF)MOk z{N5Ft5qt{f6$odmu76<^&+^!~JV5G%gdXC-EvQ;-NL=o^y2>3iQpl#81NMWbn$eQ` z3XEXaN3H`Tb^i$gQaxQAL>;VP$kN?YhyJ8}KpzELi#`Anf^HyO2Tn?8AYMlVXnxiV z5($dLv_yi1jGj)3gh1$S@hjC5w_Ekre-4M)Bq2iOW$A2Z`myaB_%GD_2gO$jj#oZ% z8RIMm0swIPqbbP$|1hvOadWJ@*5txYj^lRg9SQ$Fr~s?LLBCw;dy@0JVq+AuILopc?Dpv^ z6G4N|%wYxMd1ZU~SJvmO%tRepe#?F%9*To@e8S+vQJ8|F`+#VH@LqkfSSdE5kp z9h*%m=qOfRvGYW#Ih?}+-u3$1A9;&~0-Rh?V=q9vsqNz8)o-#JPRD{TP`%r06eN6= zIIMjwpP)uDyHIER(v8SfSM{8*zgov|ET79>8quw~A9v$ZTk#6AaNk>-B~{@*H10Kq zh`Y8vqn)v5#P2w#y#-#5AZT>z(up5>qGGSC**vt&q{} zEtc}LjMy((b%AaA*&mn2^RritzN``?X4+PMFHI}Am<_XPs(zpMyYsEyhSwXv63e>P zUkQCLkI1{g@2QHZm>mmmINu^xT!-Ve>2z$6@v#Ku5mN*n$w7oMk|P-9kcac+k%!qU zy(lLL@r$3JfbR0Vma3tUw7ROh)O}=9t6~IoAwq}AfHYF6D@4KgCiQRn4^g81fD z&=M0gs*6>a^v)0ju_Uq|Az_6}ilHPNyeXtOjY>vCD{}8CltPNfDNwT=nj*wn$oyR5 z-d>!3d4g(gD_~j)rZjtpx6pz7~tvqE9~)482B6 zGi0f~iKy>2l6~X&ZJpvkT`>SidZB>pm#r^&!4E-NN^f3GM&h$$aQ`zJqM8(#AumB% zv!H}TK?t0n**${9NR9@BMMA3I@ps3g0MX}epWy!hzK|$E8O9d8q2y2BBLn~60Pm!4 zZ)9j__D9m%80$DW7}@^~_`>)}nf||8pIcbxtc3DA0I2&83NiZ(k-$hKj;&e7nDdK# zq6=BAfyE`U6+)}qkwYEVY=ufpGLkd#JHX%pS-iM2z6AebM$@7UqeQEUtQ3x9E_w!% zZ)K8Zhn25Mq1wKK#I1bmdUSzqWpCit&)zVc__L~tfFWih%J`2GxA!R!kuE;T9&WPF zsl_@R-B}6%6$!Hf5Cb7xT++H3?#GV_p>fTm!A{D-3qS1YOIGL{K7SPM_CT-w32AfP zJAW)99-YSE^5jMqg|kOhnM`b{E58gMrZq~qeo0rTa5y0E_ zez<;Ok~C%=gbRzA?8$aF%n8uhov&p5<$>MF1bujt-4J}ni6A$2?D7;i1v`2q8miQ) z0(wi#$3_D(rKOlg_+14j*oW;WXF3czTYvzZ zqFAg!=DCBNu>?kNG}`CZ{@)VshPhv$|NJSUcK!R<1NqvHT5;OKb~=b=EsBAJDwhg@cG7g^hwvMZ$M%E;ENnWSt9( zW77E98DiqxAP5|Qwb|CU>t+D*(1I5LqgTdGiehCkTbEijYSedtO{#9~Pw33;USnK~3C_5jJEnn)4zzge?$c?i3~h55yuybaB?SF^tE2{NN6M?0F%H z36FXJZ}Az3VLNOK9J;9+xkQ9=3im#DH+2mV5l(r)88K^(Q{s@) z)+X`tGd%+-7QN`#O*U^hjoB&-w9j3JBustRdy3TXvxPP=k%kBhoNs)~izEdei`Ioc z3U$O`QFQycd=AuJk3Uq4on!Yi5UCysS-=J<)%N2qDfZ+99dX8qUjKma1#J?ZT6_W) z;-?9jIDzSn(ovKGOF^LX9-98IYu{mAlO%Y5p?kuQTZVwRA9|a zgoSY?VQD7crzT1QA}EkbdwAX=>3-;yV-U)nzc>RAV`(ofY9M0YB|ghi!2(5cUza68 zdl@D)B6u}@?6hoL6Y3ye60~bxjYf`Ev{(pHi!Xse!X?+&6DH0WDNDcs&yJ1;iWVMW zb6;=}??PqmxMSz@dEY*tsS{M}K#hp~vS8#hM0*%EJ|DfKk5yrt6$Wojq;0ohWZu|Ngemrc z3%(J*0`>=s`@O#%nn4*DV2^s1MTOx+@p`u*_8~j1p9qd(9AnQ*c-68{5ShRa**qH&tGgi!R>1zb&RyNpeV5)(ZNDsx_mIwW{C0 zjXoK-D(?6guPnvvde+egu^AG@S_tFzI_0R?FX4$)ZZ3nzYJDI|=O^w3A&-c{p7xc} zQw^)22)9pB77M2kTet9>9e(dT%)|R!S@|q<(l~W=uN^tFksy7ywkK+$QjIy_3iUax zby66$57^Q;&`_3_J225@SaM+yscOuUt>L1%cesoplaczEPE_;LJ#}jq-;{+E7Th%u z(-9mdq--o8x4`_qO_|LAmNz17cKk>ahAwRH(Did# znYPnh+oO>3!K!EX(+hPzFx()evfo~q;>3tn?<)$l47`5G+-ml=j5p4D4J|`DA*~~l z2nj4s{bFx$Rg&_!Z^|2P#_d-~rRqs=%j+0aUL47{tjs6e;eCSOQI597Tmr7^~W}%sY~Iv2|Ei3l zKD!~}lIJCBQqgQnH{gcF=6DrPPvz?j!Y?-@NydJOa7@msD@*@F&_IN{vMDrc!2&yp zO0X_j`zMg5?uE5`wI@a+i@A4v74dvKhw8@%=es93J-V(XHyk5Ps1gJQqSOy@FrR7A!eg~P2KPuw=E zyY-#9e7lPco-r6}T2Ez%tx(GJP+3Hy+xJ+?zsnE1OgzNIJ?~rQ+y+-Bsdw1m5#@x< z3sGf3F{2bE$%50})V7P*PRSlj&t=hsgnp_;E+fm|xj-u*fU9Ghvt(aYhZc3FRoywW znjB8DJ&pl%DOo(9u!9a2JT7Z5)=F)iWyb!g_Uy*GL7z!Nf^! zX+E|^6`F{+_5__3BTSLS4>-7qr_;$?84IO+T@+Jzdk`gp3J`#5WqwH^BC{9Ufu*{- z%rL@c{Cq_g+`}A3^cbMnE3zXZ=Dh39_lQvl;>;*~GXk=+213t2PCy#t90#B8p*YNo zXCz+E#Uo=D6pBkX0esudZWar3TPhTZH|x94cW7tntp-$u9}J?;il0&~ zW>pAc4@YYYHyW9No?}Ab68gI<#)FM|L^Wen5pY`r;Nv$h;gD_6ubhi7D^O>MvB{06{{%_O@Q%*hwDqvb(loBudZ~Yn|MSi21@9YR5OE_9dAqj5pIL9poh7sTRPioX zl*_feCq)u6?(1+&QgW4aDOdA1FlY<#iFLcP+8VuD5=La82ue1f`^7k%FK@0wxffY; zULzBvqln{dM^PH3?F#h+tJw#rptj@bUpe~(aCb&`L7I#2D(JeID|^M4-kiBtB^Z>tu!7|014jn($O2Sqv;7Z z4oG-y>+#EdK#2S4(-?xwpg?0AV*^lrk`G7arTl2^L3U(ICxl=4F(4qI37^*Omz|M4gsUv4U{K~vEm5!{CdrQ#>AA^6%b06VbHYgg?%T3Io% z?p2^fJr9mQ(jg}AVk4-Cc7#X>bOK-%b{@kIZ{1ULBT#L<{hQ$&-xY=$`?PoPyC@mVTVkvlpqeX!Jj2)j9 z`RiS9u9e0f*1Rf0GekJ zGUVfi874-19y~{=#w|OL?4=!p6vk%!2eB^73kZ#!zx{x`-ru*jd}sg)v)#$yhefn< z)c9&f5zv_YC4{^lO?52HquJTgjRqrsN2XP?yek4*g^MOhEqo$eyAjk6;71CMR#7Bq zVpkFDISqI8z?^|st(6{V`9~E}3nT+nAR#3qCt3sP#JJ{N@OKAZFKu$YTGl1syTECT zqB1spTXZy&tgo)_7Si}HJab7oR{g*j8$(y9-rz0vp+8LoxT%520ncCsWRHx)OKSbJ zbgJqro$ldeNLneo?75S;UOX_+#=HLI;E3!REtU#nPg6?DogkiE{+}N(G4^;N6N8! zkNA+UELGO%=J>9#18JTHUuk~*X<*kxX?gwi9);3{ z=^?1(O8WQZMxO(7^G<>csc_Vwlve)%h9L%g9=W-PfktzLbctz|i?a$=mqY;prH^WN zmGI>f7OeF_fMvr`?Y?n38$lxmnd+|`f9|0l_h8bbmY6h`*nFQIzoWTD-S0}5C!7;^ zz1n)M_z_&N3+6W+h|ls$4m)vX(}m-H&0c()!(%OC(vhcY>I%UW1s)X}q(nEwSlFym zpB^!<@t6C7bm!C-W}oX}_^&$27(*gVFdr?x!W|NOU7k5lvNa*Ud|(`l1toyVatTrx zK{r1#W~1&|KK4!|{JK5d8}be_F4K;afNY@!iA6bU-5f1JDvM@nkP<3A9?FfB6*>Q% z`9;2_&Dhj&`rPSa?S8pI9`($Y?Fu1aVcHc^cB!Vh+ZmVgn`Q?CbId8?ay76u;*5PQ zls9^SU!s|;rZF&oc4GL$&+Za|FJJ1sVyQ`{#2@b2N1hr^ljHc$V*FgI?srN4rssZ{ z{o4y$^ZO1V*(Yf@OhiXGJ7*===scqHBo*yhXi7mOB}c2CS7dvgCdL#YA|b-CC^m z@^F+|rI?nq{Q2aB7!#2`8SyJ%fNMJZ{u3rw2aIUVifg#NRo*Wws->fGg|jIq6NOe9 z4n_G264CV-ePDI@cWJt8KKCu2WTV~AeooBq-yB`TAmOQc+qjDW{!4|5%j36WUY1gF zyh!`~ohM?YcS7gm%Lz*Ju-UaxyEGAk_gw7CsAJ4M@<-IBCFgz%LQQ!{`Dr+cLEj1C zK|;lt-l_}em3MVwGSEr6b&&V>qe>G|{`=IxcgWCExbMz|@){!tLqci0%f{!A{&dWu zz4GIV9&7$FvM1{BaGCS0;e3=QtZ!*mMl^$KjucI;&33;daWZ8}dsv9f^k62S@Ol|D z3la^4!LuY=Sp*=#7)n7kw3)XlNtjQ%hIENH=)x6^JEv}lScxl>&ZZ9tx|cR_@2yP_ z8^6XROGOg;hjxkPKpPN!J(G$HSN3$8ZDtlRRPZa0OD(;*8JhH zJ|L3@S^9=cJgy!{0C~2tUpLq8XW5{Uw04C@Ms=y;#nB>cxkh6E*`gJ420+rkndF~< zZf(%OtZ5vo9wqg243!jCt0)osu@IfaFD}1LUec`0y`iw9lKARn@9G0}L+dwR)cdkd z0lNi(hTXn|uhx(0`^fGu&jN)?Lp5s-(Q@XI$&pc2yKe12_B>6KW zls+ErJa3A4x;tG>q+tC=&^aK$3}WqV0gc+yF_Z4Q zP$(izZ1D2EEm5#{|BDbzCx{u{EPoT_Pm~iYs78FOz8^36qn%iy+&}jDOuJt zdK1m2vZ28P{Gf*oB!RE}%XSI#Zk~UzEq4JW`pU0Jh;q6^3YZprJ4+OrWayZqlNzyL zMdW)~&0G~gy$O%!-s);y>H#{~)3_a=l?!P15&5+XFR65V3HmlBRuC9BS+a7JDQ;XA z0=%`dk?zJ{(%+^$>27N(s-iHJ3_9e3)ktVUpzF#43d}N!7!2Hqj9EpGZ^bt+Ef!-T z=JAp6tPRJAq%Z@xqhR{6=i$K)863Sf4HGql;0&qkJx!PWw9o;kzj2aw zAEzgeq=hkn_0juMQIS^Kc(2>6f3RDYE5B2-FGW?D=_S(#KAQJ|+K>f2&k&a$3%%}} zN61;rSx@&e8P2+D#8oKM9<3v-Fc8&}2@mZjhqKg^Bqmr?7a>nfqYI(6AOb1%rr*Y` zbvrwGGL^pmSd^kP_A5%$gV2%c>DYkRsQ$AuyzyC={c8U-e5x^tqt-c}YFAYk&9jp{ zUVWNh$5IvZcr>5e#9t!H&!y~SIT*8dU1ULNA6nl5w$x2t5ANr)Js~_^+=?iFzg8n< z@d0*%zTCqILnB6iTx@t(j(lk5_{}O-)cCusFUS8;L_E+JM1e7!AyTXm)4q<_7wh! oFK0=Ycy$+ktlT z>SNhO5KHG2A}Yj`NYcV-_QJiieVCoEeSlsVCxxK%PQXcg4D&PFrP{ zr^D8nLR(?tD-*^H?yLYSMy!XNR2>s z(CjZ=XIX>H6&aQ1^KDrlVcc$e7aAhXdAEDq`^~2m>1snh@8Hrc?Wp;LNbT144Kk&D z_)YC3qYc8t$*Us^(;Rj3cDE}eNr#VXD!%~)P{9n5YP*B?KpuvS_K6*R7H zpXP4cMNIup5}tEX51t?F`{)4|=COCyMg>5828ia?EmRA*Lck)%z<}}t3!A8%5v2(S z+y-_FbaEiLC}mXQI=kA#_=mFnAs=T5jE0brqd&}FY8RrZl}X4BY&Jfa72*6x!Dj4u zu${JdySrad0(22|eR{of8f_zJ(?A;W^}fzAX9FPQQpLph3i=5|ErY4o03unCxFus4 zj|lOPMBnNe6n7Ho(~6E!O!!i^Nm^Ne1#J0>lo+x=3Ys}_fg{xzp+-y=Ubz*+$jUz; z^>T|CrgZ6}zQ{i5kB~HNV8`OA{MzB%*ov_QIBI#P0^7)rxafbTm) zEMnCDGiAgTX&R@L9v|5LB3DmtuUv4sd`Gp)9&t#Sady&xRf04IzZ+iIMOimw8oxtg zjiF{19Wr`LY1%+W-XOz!y*w)#fa#p@=hSPz@T*#7nKKrmU(3+RXaT7@?<)ba9;!hO z?vrM95Vt1ua;)#`q1;x_6w zUPW}iTocg_v1spV#H|+3X6LJ>sEN(q&Oa;1peC>`SX}I39*#}IN~szvGgh0ayM&!_ zhHS51M zYnJxZYWClXC#q3M7|eRNYK*=4aoWm1!-xQHAtVW1>4SsrxSs=w#IkXdsv`qrcGhcz zK&XmR4@}jvzz-w!+|QGbN25`>&#O;g`R+ta$$wJ z#o>s`L$wOXY}vvlsBA;zcH^C^rG|w%+=!}~Is(0;K^oy^@}LWtB1kuqrcYLiMYXF> z<@f-Cmcd<7+e+$kh0JBkxyocuR+d`QWR!bVGqU0+A$iJU?RACfGT2*{_2kSYH~g4B z?54Gz)#bY6_S57^;STa}&@fy!Af_wz$8o91m2>uTS5P+26i{Muj7o~IG@T8&7XV}L z37={5X95)7ET;{}?d19zlAUtm^)X?;c9NIRQ_*t0o^EYnCBpK1jWAC!?RUpp3z1(Sy@(Ko4TU*~NO^4j0|TvLN# zS+bLT&Lmpu9;YUo7(4q`+ZWPskE^#CIz*V{)u2AI)7UR0N&YaBS~o%4La-e?$!4;H zGm)!y3p0jb6Xhz>A=(|Z~ca~%d zsY2R>f-b0V`PskTZq=NKo0vn7GL12fbKx;I(Rg$pm}n{m6YQ}(G@esQ9%H0AWl)j; zWs!I>)T2n^GPl37krk?tJ7Grtfra?tHYe?`VHP~BRO7fB2(l?ka?K1Hr4Hk*c-sG# zUiq4#UYM_W=sk~shO@x>09K)4t%buICzHHRUh2_xwkpp?XfQ5fp<0L0Doxo(hofgZ zojp^?9hCm*#avhf17@ICdX7c8WMa}nJAn9V1##*R_Y>!iT+ad7-jYzSs0x?WnD&Jms2o-+iky-{D;wv*CzlS#37| zwX%TR>r6=+SNj7~I%-VozFjqa-`YaHWR;7^c%cY>*bZ#hW!{V7wfp6Zewzrgr#(1h z6o#pL9_~41-U5I%L{fgP*AELbx1slUjHHOq_9!L$yV zf>!g3Os~3wVulX*Zf#tL8S(mbQoxT4%X&WWy4S}Jyv8^$r{B`aPcX(jY;$Z0n=3eT z@@c1a$R47C7&JSd>pFT}Z$?8|3e?-s4aOwK232aR9FS320zewkmu%-)>S>9CeG?`t zI{K;WJ!MM<&y%mEqpba{c(|WU*wE<|Q^z9hMLCs_?WQ?DXBcRK)SA7i8!U%X+<+md zxFUEfwUUlMaAWRyz55@>7Gl%CJGTB>t^DJ* zqWfdTb2c+D`ZvqgpA1mvAk*4E7S^jjypHsL$@tsH{IC4K+d-g`hQ&HPiuZFhdy0VJ z$c1(E)Ld{83g-y9tBnDOG_OpTjn*F%V-g%kAZJP1J{K@kWZ;KTA(LvTXIDeLPKwQZumwzLLyiwE(Yb3#* zHQyNIVCHWRPP!Rh2}WA?u#97EDAxnj5R4>SB3MaO#Ggr-8;_WJt@mBxs+&d0P;EdY zR6&EBB>^(G*i{;(V~Ml4-|h&X82~QeG&`X)0Er8l{17Qbhn~}g!Fl0(pW+D3C)eb1 z8LXYZAe^6^3lt=$IfHh`*86*S?=&1?g)#LodYRpg=(q{=D|Wq;f7ggDB5kA!j)-q@ zCAi;DVob*IBl=eO{@Ij;sGh43LynVpwsr&@o4njSIWS^~=hr6qpvnfNl|ff7Xh1mk z4$cdqz;F)5LjnZuC>trYiioDrC|8uOpkFRrPIS#E)h}<xj%pm(FzQ-Jmya(}-Bt=Okj)*K zGh5YJhAeXVNuwU?`;hCIh5A>mmLrhznXtSHWpcl>07h?mg7kK-0QM(i!$JyJM1qzU zU%cQvVZ1qlqOli|++gRgFEyif^J1!N^RJWlkGJ2{)M%8(NGR@h+_2V++%?by_2b`U zE-TS(GQu{dwu#8MPS>f&De%4LTrI7<1#$bEYf=~2H`Enr-v>Q<4-lDTh|=-YSSKdK zb|Upw!6BkrDsWyuSoj|jbxBUw-alD|!xrp{U;Uz->N}`|R)4xK3m>k+geA=~5bE~% zr%e94+yB2A8(qu{O^h7>YnA+^d^8x7A-w)nN$nrM|9@Lf{wv|H6P$Wx*8gdEja0Jw zQy>UmSvqzp0+y&3m3Ufx+Kg?HosJcLUg$*RPbA2(l^2dzzx==7W&W@SQZtJhnoXYP zli5x2wv}J{=r+nt@)lC6MfxHVb>`s%Z4Gu+A;W0-iai3Os#Fe8%_1@fUVWV-)Ovg5 z7Nmi5ho!!iP8o&)b(T!jmQkd3i*fYREbW0>G?c*)8?Iyq)LPTb5fS^u^(65uV520s zd#F6)h@O&dbA^mvQ}V_M4z`hl{jrdU*7K@CwCNgSozX05JKX zYFJqoq*_(EN}$@6oCHX?AUw^eN5NOOEA39Ba=NubtMc*(<=ZtV4X1B5Blw2$kPQZ&A~jMNvdRLmRDXhoTtCd;%ct(!~}s;4%hE}Z$$ z>|p+ZE9gb}5$kI+@=a}(rjDKgTZb%UWy+>IDYAD|2iXgcKSr@#_NlQZ~+Uq z_im=K3mi;yY@RN#LORYDTol>4)%Q$!MQ2_sj)r#)Xj?Ma7<-$X;>B@HNpGR{83|%0!>MVv zup6bLjs6VMuMH>9?uEU-qLrOz@Nr_>-CmLWmnvuy_2%pOA~E|qETby;7JG>l&IfN% z&K8@%ewpaw3Ew|-?=S4Wb1FwF{lPBsAM8^7dx!rQb{+oZ+Wz|-@;@jJRFV&$|B2vz zU5(zyk4zo9Wf1NEs}w3vADTBL!HTg`lC9mz*&Y}B^?HhwRa)?hnmKjzwJX)e#5jLE zo+#*4{7}@X=XFbILQGvdGe%7#88yt+oy`qOjnd8BD60=AhUxWtxN{Om?})+%wQ%l< zqHok~%3eTxJ*ALp#;|J*5jD`b+v(lvFgECedD7k_HG@Hn7h1FX@-kwlvw;8HV>Ju# zvQfDxI`u%r4gS2IkpZOKMND#(l1vE5Q@0_|R}BgM;j5jt0N{ol&=jaoHMHeIsZW@m zdZ#ntQOS;47ad^VkhFa?lY2>O4Y#KZsr!P9MIbsaOGEO&;@J^%V~sIEjkSyl>q+NH zT{ybl^bZST_qJ?6`l@R1_4P5!hD(?f!3TCSh{+mR;Y<=ecw zyn$k`c^AV`G92G#8#3e2K^T?SY#Pg|rjpN>Us+84sAeGn%hRD+ZFZG{`c}mouDQA1 z+NSt!-%qH%fHCT5ro*$1=|@y9Z_+MQkI4aFRMaG8lr`95XdF``lUhCEmIxfd3x6-2 zYpd|`i1#s(E1-P)E`e3@*T&u+(!gpY=Oh8ID%A(AfyZWWBy#=2gq}OvHtt$&ST+6V zrW!gQ3?ku(qca0oFm|;4Pi>6C(dQUD__Aj^oi?3VD!vw^?NQCcj4i6udHr4`y*WGD z;-I6Tx^UH9N^n2BIcRMcRLtn<+vG`bau7AT{$&yC<~%{=JR^(}ylUgw$$edjGjdC~ zFgZ~^(Ys}4io%e^xr71-AI5sf5lTChI7aCb+nDaEKuu<# zoI!o0UdS{zc0}LO3)(vFgJdNLY$ok!7;|$2Y=byd!FGZXZ!K3IMv+3oAd^X|;SFGR z+6yl@caVKl0E6)cw(3AxJ2{<@64qTv&J{@<88P&_7M{e#1j9&?U?tmtA)ZhY=sNvw2t#uTylN2+Y;4{K8Vc@s%0RJ2sexqSK7w7^oVIBh;YT^_vOZOpy z49ZJhv50B?6^)^CaV4DMd?mu?^G@yS4To;r?X34>-YuibtJS4}O!)KeC(Pq)@B7)U z!Sl?u&UbjQQ#&21_`8Lq3mvj>;Y+ zOxS%3gAR>nFIA1J)^`&JXN&%4zNgucm|&|S)Az6 zeCxcoaZ-!8<1#Wt5+o*eQhq?MEU1&&BEvA++dqPoxb*ZcD&B4E9N|?o&wq+sPP2Wh zd+ZHJDq}{v(4Q1EUf!0?6mkpnVc%Sk+C5CM^EVm;>{wOADPuX^FZ=&f$C*Gwy@dgM zyk{4Z$eJ}GOWH758Wo|E2ALsCjD0NGDk)D9H5epgw1_NOpS3|DvSw%$8rjFbM0qk1 z-i$eO-d{8CKWFBinK|eF&iCE#{_p*N-~GNOj?*Pa6!2$?T=v~hc`;)Y@c34s0l1s@ zJkJ^!QMB0F;lnMcW)ggD;h<2$gYh@4k&|d6EaBIgBGH1;OV|*5_E1^ySN}CidvZFuZ=oM6l_c z8s<&}Uh`E=_veT}fd;ERKZ^O!*C}>I3TXPSJ~R$&e&E(`C0TIgi|UcvQvaB6OMN?# zjd{rjCnckP=okxzkDO>2Pla?%n+iL$=#RQf46{CE?`uiSC&H#um5dv7Ah19!oWa@t zk?=S5gao&U02kRk>?1SMwo!qeb)#rIciUo9FO~INIJp2H zAJ&SB;3oI^jUc=?=J<-$)!4d)m-RQ=VeYvLf=>#naN?x)IQ1or&9|}re4Yo6rrcs{ z1c7Z@Jr~lojgUUQ`K(P}OGv>;On}Z#VJDc|TiZeQ7GD3!n(`L6if2<^W90`WiE!6k ze(ZZUq5Tm5>4kPmaGu}qT5|!+(?cUOv8P)DYJJzg6(u6GE<8LCd?3DPC|2S93nQOC|l`z-mv#qP0P7Evj_B?=|mf zdpP^}NPgO$D}`lv^%{ONcp7;3RZ{00k;WO@S`9?Woy^8}u&j!g5apLh#j1Pg{ePT= z$l{&`MyH6$Mj{?uH*pTX1A=O@bb1|nI|5tQQ10bNJ(`3^BY((1<5NDg7`ar&bc??+ z)6lm&zd3`(YR&N&PiC`~B(L`&WfE7BH(`oN5lK*N{rd!)4nv24%huq`u0JDq!5<%{ z76qCUF~XBg$Gr>6Do!pv>QGXBCD30fIhB~*Kl+K_V>|y4q;XyBQ_rsVb@)iZ<5|2H z+s}!eZdRT8e3mT{yb9~Lv%znWFT?-##^J-;?!Fj5F@6pkk@AKw{Xm8UC*uc}!c6u^!LUS|4R?fw5UI30 ziO=I_!qH3jst_(?^Rpl|nc><rU$a)XGbn`5+sw z3*Jw@ndiCLJ>=usddObl@;-S!s;ZLiv#BWgpYk~X1EL7Gjpx902REY&H;hTbli8(Z^~Ai?%~ z`{%lz_x*~Fd-9<^@r1a1Zl?mJ#S(oVLAEv;+*3{*Jw}TO;m8;2r50|J5 zDzq8y$wG@-uqOe7hLd{HmZ>!+X{nTyQ9Fvq`L{Ai^c`N1yEDSa-q+11z~{FIkkE^7 zJxC{ZyjeL=mVHASMa&CvOZ_U`CJg_4l0Q?W)M=l9XeaLQ_k;`I7rNiY&P!A{jGel1 zqbuYp%uYfoEsHmPq~vIjXODG(Z8NI4?DB*7R*uW5Ey+9@rRlnP1G%l;z#62EO(W^z0MHf2G#oB z=C$cpKhG6K9yEU^cI~9GnGGt+pK?wl*eq}I?4TSUxAGa{V}q!?_QJx|uKTb3(8~ic zuMfs(oy14qt3OA+r*Sc6eF!o(6l;4~#lJm&l=K-)h0v*mW=Y zhOGWPNx6kco4WDMo7tO9gblP{qH4l>mADWoisIkXe@5jDKaXFV71CDu>W{JTXv=(j z^ToR%yV~06o<&2|Q@FX}9n~SD{nMWe@P>ctaJH5>@zro{XtzBX=S~WA8zp32`I-{W zhCX7^Tp`P$oz26QggrCV#F8y88Tk%XQQP?8D16oaH28{Ka@F{)XWHKuxun$eDEoMp zKcqmE1KX7Acb^K+R$PJl&ra$GK5-NvM3~vO4m?YoP+n) zcVOp?1-Pg|3+hGfT#51qbPlMar49sj`fqSh0dIY1psEHSh;|B{v1k_^Bu#GQTK=L7eW@NfAWP1iXkbH|2&?`of`I8n+<#g2pt(Gx=nog!#tu&>^2w*hn z+0;PDGLf(QHNaw__h>MnVi|L;=@6>Lnlp#|$K7(}1DU30I|C{yOnM70WMnetY5HAQS625NE+pWVw_M z{)Z~=F%w26<9Qb#^B`pQf4{TRWF5n3R4z}o4qmwoahv) zy|QXDr!a<}fZQB?8dnQ1zcpWi;Xp3)^+|Bst@Wq&HZFZv0)%X+25ea+eVwAOTR`C$ z;{-tAl)+3P+oJ|Rq>? imgAndVideoList; List? allPosts; DiscoverData({required this.allPosts, required this.imgAndVideoList}); - + // ===================================this function used to===========================================// +//=================to change json to model===========================// DiscoverData.fromJson(Map json) { List> tempDataImagesAndVideo = []; List tempData = []; @@ -23,12 +24,9 @@ class DiscoverData { 'Url': imagesAndVideos['video'] }); } - PostModel temp = PostModel(); temp.fromJson(imagesAndVideos); tempData.add(temp); - - //PostModel(approved: ,author: ,commentCount: ,createdAt: ,flairId: ,images: ,isDeleted: ,isSaved: ,isHidden: ,isModerator: ,isSpam: ,kind: ,locked: ,nsfw: ,maxHeightImageSize: ) }); imgAndVideoList = tempDataImagesAndVideo; allPosts = tempData; diff --git a/lib/discover/providers/discover_provider.dart b/lib/discover/providers/discover_provider.dart index 712dff5f..1f374cd1 100644 --- a/lib/discover/providers/discover_provider.dart +++ b/lib/discover/providers/discover_provider.dart @@ -12,15 +12,17 @@ class DiscoverProvider with ChangeNotifier { DiscoverData? get gettingImagesAndVideo { return imgAndVideoList; } - +// ===================================this function used to===========================================// +//==================fetch and set date===========================// +//topics==> topics of comminty +//page==> page number to fetch +//limit> limit of size data return Future fetchAndSetDiscover(String topic,int page, int limit,BuildContext context) async { try { final prefs = await SharedPreferences.getInstance(); DioClient.init(prefs); - print('====================in discover================================'); - print('/subreddits/${topic}/posts/like_reels$page p $limit L'); await DioClient.get( - path: '/subreddits/study/posts/like_reels', + path: '/subreddits/${topic}/posts/like_reels', query: {'page': page, 'limit': limit}).then((response) { print(response.data); imgAndVideoList = DiscoverData.fromJson(response.data); diff --git a/lib/discover/screens/discover_screen.dart b/lib/discover/screens/discover_screen.dart index b02e2fe8..d90eda0f 100644 --- a/lib/discover/screens/discover_screen.dart +++ b/lib/discover/screens/discover_screen.dart @@ -18,9 +18,8 @@ class DiscoverScreen extends StatefulWidget { class _DiscoverScreenState extends State with TickerProviderStateMixin { + //========================================ButtonsTabBar============================================// TabController? _controller; - - ButtonsTabBar get _tabBar => ButtonsTabBar( controller: _controller, backgroundColor: Colors.grey, @@ -35,6 +34,7 @@ class _DiscoverScreenState extends State radius: 200, tabs: tabs, ); + //===============================================================================================// // drawer functions void showDrawer(BuildContext context) { Scaffold.of(context).openDrawer(); @@ -133,7 +133,6 @@ class _DiscoverScreenState extends State children: topicsName .map((topic) => Tab( child: GridViewDiscover( - // userName: widget.userName, topic:topic, ))) .toList())) diff --git a/lib/discover/widgets/grid_view_discover.dart b/lib/discover/widgets/grid_view_discover.dart index 1e916406..2eafa2a1 100644 --- a/lib/discover/widgets/grid_view_discover.dart +++ b/lib/discover/widgets/grid_view_discover.dart @@ -24,47 +24,7 @@ class GridViewDiscover extends StatefulWidget { } class _GridViewDiscoverState extends State { - DiscoverData? imgAndVideoList - // = DiscoverData( - // imgAndVideoList: ([ - // { - // 'Url': - // 'http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerBlazes.mp4', - // 'subredditName': 'subredditName' - // }, - // { - // 'Url': - // 'http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerBlazes.mp4', - // 'subredditName': 'subredditName' - // }, - // { - // 'Url': - // 'http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerBlazes.mp4', - // 'subredditName': 'subredditName' - // }, - // { - // 'Url': - // 'https://images.unsplash.com/photo-1447752875215-b2761acb3c5d?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=870&q=80', - // 'subredditName': 'subredditName' - // }, - // { - // 'Url': - // 'https://images.unsplash.com/photo-1447752875215-b2761acb3c5d?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=870&q=80', - // 'subredditName': 'subredditName' - // }, - // { - // 'Url': - // 'https://images.unsplash.com/photo-1531804226530-70f8004aa44e?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=869&q=80', - // 'subredditName': 'subredditName' - // }, - // { - // 'Url': - // 'https://images.unsplash.com/photo-1465056836041-7f43ac27dcb5?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=871&q=80', - // 'subredditName': 'subredditName' - // }, - // ])) - ; - + DiscoverData? imgAndVideoList; int _page = 1; final int _limit = 25; bool _isInit = true; @@ -72,6 +32,8 @@ class _GridViewDiscoverState extends State { bool _isLoadMoreRunning = false; ScrollController _scrollController = new ScrollController(); var userName; + //=====================================this function is used to======================================// + //=================Loading More Data====================// void loadMore() async { if (_isLoading == false && _isLoadMoreRunning == false && @@ -99,8 +61,11 @@ class _GridViewDiscoverState extends State { }); } } - + //=====================================this function is used to======================================// + //=================Start loading More Data====================// bool toggleLoadingMore() => _isLoadMoreRunning = !_isLoadMoreRunning; + //=====================================this function is used to======================================// + //=================Loading Data for first time====================// @override void didChangeDependencies() async { // TODO: implement didChangeDependencies @@ -142,11 +107,8 @@ class _GridViewDiscoverState extends State { ? MasonryGridView.count( controller: _scrollController, itemCount: imgAndVideoList!.imgAndVideoList!.length, - //No of column crossAxisCount: 2, - // Vertical gap between two items mainAxisSpacing: 0, - // horizontal gap between two items crossAxisSpacing: 0, itemBuilder: (context, index) { return InkWell( diff --git a/lib/discover/widgets/post_video_in_discover.dart b/lib/discover/widgets/post_video_in_discover.dart index 8db5e3e1..7bf49f90 100644 --- a/lib/discover/widgets/post_video_in_discover.dart +++ b/lib/discover/widgets/post_video_in_discover.dart @@ -5,7 +5,6 @@ import 'package:chewie/chewie.dart'; class PostVideoInDiscover extends StatefulWidget { final String url; - // final VideoPlayerController videoController; final double height; final bool inView; const PostVideoInDiscover( @@ -22,6 +21,8 @@ class _PostVideoInDiscoverState extends State { late ChewieController chewieController; bool autoPlay = true; late VideoPlayerController videoPlayerController; + //=====================================this function is used to======================================// + //=================initilize Video====================// @override void initState() { super.initState(); diff --git a/lib/logins/providers/authentication.dart b/lib/logins/providers/authentication.dart index b62dcd9e..63f98bb4 100644 --- a/lib/logins/providers/authentication.dart +++ b/lib/logins/providers/authentication.dart @@ -7,6 +7,7 @@ import 'package:flutter_dotenv/flutter_dotenv.dart'; import '../../main.dart'; import '../../notification/models/notification_class_model.dart'; import '../../notification/provider/notification_provider.dart'; +import '../../shared/constants.dart'; import './notification.dart'; import 'package:firebase_core/firebase_core.dart'; import 'package:firebase_messaging/firebase_messaging.dart'; @@ -149,9 +150,13 @@ class Auth with ChangeNotifier { await prefs.setString('token', token); await prefs.setString('expiresIn', expiresIn.toString()); await prefs.setString('userName', query['userName'] as String); - // await Firebase.initializeApp(); - // final notificationToken = prefs.get('notificationToken'); - // await NotificationToken.sendTokenToDatabase(notificationToken); + await Firebase.initializeApp(options: FirebaseOptions( + apiKey: Constants.apiKey, + appId: Constants.appId, + messagingSenderId: Constants.messagingSenderId, + projectId: Constants.projectId)); + final notificationToken = prefs.get('notificationToken'); + await NotificationToken.sendTokenToDatabase(notificationToken); // FirebaseMessaging.onBackgroundMessage( // _firebaseMessagingBackgroundHandler); preparePrefs(); @@ -186,10 +191,14 @@ class Auth with ChangeNotifier { await prefs.setString('token', token); await prefs.setString('expiresIn', expiresIn.toString()); await prefs.setString('userName', query['userName'] as String); - // await Firebase.initializeApp(); - // final notificationToken = prefs.get('notificationToken'); + await Firebase.initializeApp(options: FirebaseOptions( + apiKey: Constants.apiKey, + appId: Constants.appId, + messagingSenderId: Constants.messagingSenderId, + projectId: Constants.projectId)); + final notificationToken = prefs.get('notificationToken'); // await NotificationToken.getTokenOfNotification(); - // await NotificationToken.sendTokenToDatabase(notificationToken); + await NotificationToken.sendTokenToDatabase(notificationToken); // await NotificationToken.refreshToken(); // FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler); preparePrefs(); diff --git a/lib/logins/screens/forgot_password.dart b/lib/logins/screens/forgot_password.dart index 6492391a..be4a6ce5 100644 --- a/lib/logins/screens/forgot_password.dart +++ b/lib/logins/screens/forgot_password.dart @@ -10,7 +10,10 @@ import '../../moderation_settings/widgets/status.dart'; import 'package:email_validator/email_validator.dart'; import '../providers/authentication.dart'; import 'package:provider/provider.dart'; +import 'package:flutter/foundation.dart' show kIsWeb; +import './signup.dart'; import './login.dart'; +import '../widgets/web_image.dart'; class ForgotPassword extends StatefulWidget { static const routeName = '/ForgotPassword'; @@ -115,153 +118,263 @@ class ForgotPasswordState extends State { onTap: () { FocusScope.of(context).requestFocus(new FocusNode()); }, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, + child: Row( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, children: [ - UpperBar(UpperbarStatus.login), - Expanded( - child: SingleChildScrollView( + if (kIsWeb) WebImageLoging(), + SizedBox( + width: (kIsWeb) ? 25.w : null, child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - UpperText('Forgot your password?'), - SizedBox(height: 4.h), - TextInput( - changeInput: () { - setState(() { - changeInput(); - }); - }, - ontap: (hasFocus) {}, - inputController: inputUserNameController, - lable: 'Username', - ), - TextInput( - changeInput: () { - setState(() { - changeInput(); - }); - }, - currentStatus: inputEmailStatus, - ontap: (hasFocus) { - setState(() { - controlEmailStatus(hasFocus); - }); - }, - inputController: inputEmailController, - lable: 'Email', - ), - if (inputEmailStatus == InputStatus.failed) - Padding( - padding: const EdgeInsets.only(left: 25), + mainAxisAlignment: (kIsWeb) + ? MainAxisAlignment.center + : MainAxisAlignment.start, + crossAxisAlignment: (kIsWeb) + ? CrossAxisAlignment.start + : CrossAxisAlignment.center, + children: [ + if (!kIsWeb) UpperBar(UpperbarStatus.login), + Expanded( + flex: (kIsWeb) ? 0 : 1, + child: SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (kIsWeb) + Padding( + padding: const EdgeInsets.all(8.0), + child: Image.asset( + 'assets/images/reddit.png', + width: 50, + height: 50, + ), + ), + if (!kIsWeb) UpperText('Forgot your password?'), + if (kIsWeb) + Padding( + padding: const EdgeInsets.all(8.0), + child: Text( + 'Reset your password', + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 16), + ), + ), + if (kIsWeb) + const Padding( + padding: EdgeInsets.only(left: 8.0), + child: Text( + 'Tell us the username and email address associated with your Reddit account, and we’ll send you an email with a link to reset your password.'), + ), + SizedBox(height: 4.h), + TextInput( + changeInput: () { + setState(() { + changeInput(); + }); + }, + ontap: (hasFocus) {}, + inputController: inputUserNameController, + lable: 'Username', + ), + TextInput( + changeInput: () { + setState(() { + changeInput(); + }); + }, + currentStatus: inputEmailStatus, + ontap: (hasFocus) { + setState(() { + controlEmailStatus(hasFocus); + }); + }, + inputController: inputEmailController, + lable: 'Email', + ), + if (inputEmailStatus == InputStatus.failed) + Padding( + padding: const EdgeInsets.only(left: 25), + child: Text( + emailErrorMessage, + style: TextStyle( + color: Theme.of(context).errorColor), + ), + ), + SizedBox(height: 2.h), + if (!kIsWeb) + Padding( + padding: const EdgeInsets.all(10.0), + child: Text( + style: TextStyle(color: Colors.black54), + 'Unfortunately, if you have never given us your email, we will not able to reset your password'), + ), + SizedBox( + height: 1.h, + ), + Padding( + padding: EdgeInsets.all(0), + child: TextButton( + onPressed: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (BuildContext context) => + ResponsiveSizer( + builder: (cntx, orientation, + Screentype) { + // Device.deviceType == DeviceType.web; + return Scaffold( + body: ForgotUserName(), + ); + }, + ), + )); + }, + child: Text( + style: TextStyle( + color: (kIsWeb) + ? Colors.blue + : Colors.red), + 'Forget Username?'))), + SizedBox( + height: 2.h, + ), + if (!kIsWeb) + Padding( + padding: EdgeInsets.all(10), + child: RichText( + text: TextSpan( + text: 'Having trouble? ', + style: TextStyle( + color: + Theme.of(context).primaryColor), + recognizer: TapGestureRecognizer() + ..onTap = () async { + //on tap code here, you can navigate to other page or URL + String url = + "https://www.reddithelp.com/hc/en-us/articles/205240005"; + var urllaunchable = await canLaunch( + url); //canLaunch is from url_launcher package + if (urllaunchable) { + await launch( + url); //launch is from url_launcher package to launch URL + } else { + print("URL can't be launched."); + } + }, + ), + )), + if (kIsWeb) + Padding( + padding: EdgeInsets.all(10), + child: RichText( + text: TextSpan( + children: [ + TextSpan( + text: + 'Don\'t have an email or need assistance logging in?', + style: + TextStyle(color: Colors.black), + ), + TextSpan( + text: ' Get help', + style: + TextStyle(color: Colors.blue), + recognizer: TapGestureRecognizer() + ..onTap = () async { + //on tap code here, you can navigate to other page or URL + String url = + "https://reddithelp.com/hc/en-us/sections/360008917491-Account-Security"; + var urllaunchable = await canLaunch( + url); //canLaunch is from url_launcher package + if (urllaunchable) { + await launch( + url); //launch is from url_launcher package to launch URL + } else { + print( + "URL can't be launched."); + } + }, + ), + ], + ), + )), + ]), + )), + if (isSubmit && isError) + Padding( + padding: EdgeInsets.all(5.w), + child: Center( child: Text( - emailErrorMessage, - style: TextStyle(color: Theme.of(context).errorColor), - ), + textAlign: TextAlign.center, + errorMessage, + style: TextStyle( + fontSize: 18, + // fontWeight: FontWeight.w500, + color: Theme.of(context).errorColor, + )), ), - SizedBox(height: 2.h), + ), + if (isSubmit && !isError) Padding( - padding: const EdgeInsets.all(10.0), + padding: EdgeInsets.all(5.w), child: Text( - style: TextStyle(color: Colors.black54), - 'Unfortunately, if you have never given us your email, we will not able to reset your password'), - ), - SizedBox( - height: 1.h, + textAlign: TextAlign.center, + 'you will receve if that adress maches your mail', + style: TextStyle( + fontSize: 18, + // fontWeight: FontWeight.w500, + color: Colors.green, + )), ), - Padding( - padding: EdgeInsets.all(0), - child: TextButton( + Container( + height: (kIsWeb) ? 40 : null, + width: double.infinity, + child: Padding( + padding: EdgeInsets.symmetric( + horizontal: 10.0, + ), + child: ElevatedButton( + style: ElevatedButton.styleFrom( + onPrimary: Colors.white, + primary: (kIsWeb) ? Colors.blue : Colors.red, + onSurface: Colors.grey[700], + shape: (kIsWeb) + ? null + : RoundedRectangleBorder( + borderRadius: BorderRadius.circular(30)), + ), + onPressed: isFinished ? submitForgorPasssword : null, + child: Text(kIsWeb ? 'REST PASSWORD' : 'Continue'), + ), + )), + if (kIsWeb) + Row( + children: [ + TextButton( + child: Text( + 'Log in', + style: TextStyle(color: Colors.blue), + ), + onPressed: () { + Navigator.pushNamed(context, Login.routeName); + }, + ), + Text(' • '), + TextButton( onPressed: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (BuildContext context) => - ResponsiveSizer( - builder: (cntx, orientation, Screentype) { - // Device.deviceType == DeviceType.web; - return Scaffold( - body: ForgotUserName(), - ); - }, - ), - )); + Navigator.pushNamed(context, SignUp.routeName); }, child: Text( - style: TextStyle(color: Colors.red), - 'Forget Username?'))), - SizedBox( - height: 4.h, + 'Sign Up', + style: TextStyle(color: Colors.blue), + )) + ], ), - Padding( - padding: EdgeInsets.all(10), - child: RichText( - text: TextSpan( - text: 'Having trouble? ', - style: TextStyle( - color: Theme.of(context).primaryColor), - recognizer: TapGestureRecognizer() - ..onTap = () async { - //on tap code here, you can navigate to other page or URL - String url = - "https://www.reddithelp.com/hc/en-us/articles/205240005"; - var urllaunchable = await canLaunch( - url); //canLaunch is from url_launcher package - if (urllaunchable) { - await launch( - url); //launch is from url_launcher package to launch URL - } else { - print("URL can't be launched."); - } - }, - ), - )) - ]), - )), - if (isSubmit && isError) - Padding( - padding: EdgeInsets.all(5.w), - child: Center( - child: Text( - textAlign: TextAlign.center, - errorMessage, - style: TextStyle( - fontSize: 18, - // fontWeight: FontWeight.w500, - color: Theme.of(context).errorColor, - )), - ), + ], ), - if (isSubmit && !isError) - Padding( - padding: EdgeInsets.all(5.w), - child: Text( - textAlign: TextAlign.center, - 'you will receve if that adress maches your mail', - style: TextStyle( - fontSize: 18, - // fontWeight: FontWeight.w500, - color: Colors.green, - )), - ), - Container( - width: double.infinity, - child: Padding( - padding: EdgeInsets.symmetric( - horizontal: 10.0, - ), - child: ElevatedButton( - style: ElevatedButton.styleFrom( - onPrimary: Colors.white, - primary: Colors.red, - onSurface: Colors.grey[700], - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(30)), - ), - onPressed: isFinished ? submitForgorPasssword : null, - child: Text('Continue'), - ), - )) + ), ], ), ), diff --git a/lib/logins/screens/forgot_username.dart b/lib/logins/screens/forgot_username.dart index db5605d1..dfb6b9d4 100644 --- a/lib/logins/screens/forgot_username.dart +++ b/lib/logins/screens/forgot_username.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:post/logins/screens/login.dart'; import '../widgets/text_input.dart'; import '../widgets/upper_bar.dart'; import '../widgets/upper_text.dart'; @@ -10,6 +9,10 @@ import 'package:email_validator/email_validator.dart'; import '../../moderation_settings/widgets/status.dart'; import '../providers/authentication.dart'; import 'package:provider/provider.dart'; +import 'package:flutter/foundation.dart' show kIsWeb; +import 'signup.dart'; +import 'login.dart'; +import '../widgets/web_image.dart'; class ForgotUserName extends StatefulWidget { static const routeName = '/ForgotUserName'; @@ -104,108 +107,202 @@ class ForgotUserNameState extends State { onTap: () { FocusScope.of(context).requestFocus(new FocusNode()); }, - child: Column( + child: Row( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, children: [ - UpperBar(UpperbarStatus.login), - Expanded( - child: SingleChildScrollView( + if (kIsWeb) WebImageLoging(), + SizedBox( + width: (kIsWeb) ? 25.w : null, child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - UpperText('Recover username'), - TextInput( - lable: 'Email', - ontap: (hasFocus) { - setState(() { - controlEmailStatus(hasFocus); - }); - }, - currentStatus: inputEmailStatus, - changeInput: () { - setState(() { - changeInput(); - }); - }, - inputController: inputEmailController), - SizedBox(height: 2.h), + mainAxisAlignment: (kIsWeb) + ? MainAxisAlignment.center + : MainAxisAlignment.start, + crossAxisAlignment: (kIsWeb) + ? CrossAxisAlignment.start + : CrossAxisAlignment.center, + children: [ + if (!kIsWeb) UpperBar(UpperbarStatus.login), + Expanded( + flex: (kIsWeb) ? 0 : 1, + child: SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (kIsWeb) + Padding( + padding: const EdgeInsets.all(8.0), + child: Image.asset( + 'assets/images/reddit.png', + width: 50, + height: 50, + ), + ), + UpperText('Recover username'), + if (kIsWeb) + const Padding( + padding: EdgeInsets.only(left: 8.0), + child: Text( + 'Tell us the email address associated with your Reddit account, and we’ll send you an email with your username.'), + ), + TextInput( + lable: 'Email', + ontap: (hasFocus) { + setState(() { + controlEmailStatus(hasFocus); + }); + }, + currentStatus: inputEmailStatus, + changeInput: () { + setState(() { + changeInput(); + }); + }, + inputController: inputEmailController), + SizedBox(height: 2.h), + if (!kIsWeb) + Padding( + padding: const EdgeInsets.all(10.0), + child: Text( + style: TextStyle(color: Colors.black54), + 'Unfortunately, if you have never given us your email, we will not able to reset your password'), + ), + if (!kIsWeb) + SizedBox( + height: 4.h, + ), + if (!kIsWeb) + Padding( + padding: EdgeInsets.all(10), + child: RichText( + text: TextSpan( + text: 'Having trouble? ', + style: TextStyle( + color: + Theme.of(context).primaryColor), + recognizer: TapGestureRecognizer() + ..onTap = () async { + //on tap code here, you can navigate to other page or URL + String url = + "https://www.reddithelp.com/hc/en-us/articles/205240005"; + var urllaunchable = await canLaunch( + url); //canLaunch is from url_launcher package + if (urllaunchable) { + await launch( + url); //launch is from url_launcher package to launch URL + } else { + print("URL can't be launched."); + } + }, + ), + )) + ]), + )), + if (isSubmit && isError) Padding( - padding: const EdgeInsets.all(10.0), - child: Text( - style: TextStyle(color: Colors.black54), - 'Unfortunately, if you have never given us your email, we will not able to reset your password'), + padding: EdgeInsets.all(5.w), + child: Center( + child: Text( + textAlign: TextAlign.center, + errorMessage, + style: TextStyle( + fontSize: 18, + // fontWeight: FontWeight.w500, + color: Theme.of(context).errorColor, + )), + ), ), - SizedBox( - height: 4.h, + if (isSubmit && !isError) + Padding( + padding: EdgeInsets.all(5.w), + child: Text( + textAlign: TextAlign.center, + 'you will receve if that adress maches your mail', + style: TextStyle( + fontSize: 18, + // fontWeight: FontWeight.w500, + color: Colors.green, + )), ), + Container( + height: (kIsWeb) ? 40 : null, + width: (kIsWeb) ? 200 : double.infinity, + child: Padding( + padding: EdgeInsets.symmetric( + horizontal: 10.0, + ), + child: ElevatedButton( + style: ElevatedButton.styleFrom( + onPrimary: Colors.white, + primary: (kIsWeb) ? Colors.blue : Colors.red, + onSurface: Colors.grey[700], + shape: (kIsWeb) + ? null + : RoundedRectangleBorder( + borderRadius: BorderRadius.circular(30)), + ), + onPressed: isFinished ? submitForgorUserName : null, + child: (kIsWeb) ? Text('Email me') : Text('Continue'), + ), + )), + if (kIsWeb) Padding( padding: EdgeInsets.all(10), child: RichText( text: TextSpan( - text: 'Having trouble? ', - style: TextStyle( - color: Theme.of(context).primaryColor), - recognizer: TapGestureRecognizer() - ..onTap = () async { - //on tap code here, you can navigate to other page or URL - String url = - "https://www.reddithelp.com/hc/en-us/articles/205240005"; - var urllaunchable = await canLaunch( - url); //canLaunch is from url_launcher package - if (urllaunchable) { - await launch( - url); //launch is from url_launcher package to launch URL - } else { - print("URL can't be launched."); - } - }, + children: [ + TextSpan( + text: + 'Don\'t have an email or need assistance logging in?', + style: TextStyle(color: Colors.black), + ), + TextSpan( + text: ' Get help', + style: TextStyle(color: Colors.blue), + recognizer: TapGestureRecognizer() + ..onTap = () async { + //on tap code here, you can navigate to other page or URL + String url = + "https://reddithelp.com/hc/en-us/sections/360008917491-Account-Security"; + var urllaunchable = await canLaunch( + url); //canLaunch is from url_launcher package + if (urllaunchable) { + await launch( + url); //launch is from url_launcher package to launch URL + } else { + print("URL can't be launched."); + } + }, + ), + ], ), - )) - ]), - )), - if (isSubmit && isError) - Padding( - padding: EdgeInsets.all(5.w), - child: Center( - child: Text( - textAlign: TextAlign.center, - errorMessage, - style: TextStyle( - fontSize: 18, - // fontWeight: FontWeight.w500, - color: Theme.of(context).errorColor, - )), - ), - ), - if (isSubmit && !isError) - Padding( - padding: EdgeInsets.all(5.w), - child: Text( - textAlign: TextAlign.center, - 'you will receve if that adress maches your mail', - style: TextStyle( - fontSize: 18, - // fontWeight: FontWeight.w500, - color: Colors.green, - )), - ), - Container( - width: double.infinity, - child: Padding( - padding: EdgeInsets.symmetric( - horizontal: 10.0, - ), - child: ElevatedButton( - style: ElevatedButton.styleFrom( - onPrimary: Colors.white, - primary: Colors.red, - onSurface: Colors.grey[700], - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(30)), + )), + if (kIsWeb) + Row( + children: [ + TextButton( + child: Text( + 'Log in', + style: TextStyle(color: Colors.blue), + ), + onPressed: () { + Navigator.pushNamed(context, Login.routeName); + }, + ), + Text(' • '), + TextButton( + onPressed: () { + Navigator.pushNamed(context, SignUp.routeName); + }, + child: Text( + 'Sign Up', + style: TextStyle(color: Colors.blue), + )) + ], ), - onPressed: isFinished ? submitForgorUserName : null, - child: Text('Continue'), - ), - )) + ], + ), + ), ], ), ), diff --git a/lib/logins/screens/login.dart b/lib/logins/screens/login.dart index d7cc088f..943fb761 100644 --- a/lib/logins/screens/login.dart +++ b/lib/logins/screens/login.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:post/logins/screens/forgot_username.dart'; import 'package:provider/provider.dart'; import '../widgets/text_input.dart'; import '../widgets/upper_bar.dart'; @@ -18,6 +19,8 @@ import '../api/google_sign_in.dart'; import '../../moderation_settings/widgets/status.dart'; import 'package:http/http.dart' as http; import 'dart:convert'; +import 'signup.dart'; +import '../widgets/web_image.dart'; class Login extends StatefulWidget { // Login({Key? key}) : super(key: key); @@ -136,208 +139,280 @@ class LoginState extends State { kIsWeb; final mediaQuery = MediaQuery.of(context); return Scaffold( - body: Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, + body: Row( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, children: [ - if (!kIsWeb) UpperBar(UpperbarStatus.signup), - Expanded( - child: Container( - child: SingleChildScrollView( - child: Column(children: [ - UpperText('Log in to Reddit'), - SizedBox( - height: 4.h, - ), - ContinueWithGoogle(handler: () async { - // final user = await GoogleSignInApi.login(); - // user!.id; - // final provider = Provider.of(context, listen: false); - // await provider.GoogleAuth({ - // "id": user.id, - // "email": user.email, - // "displayName": user.displayName! - // }); - // if (provider.error == false) { - // Navigator.of(context) - // .pushReplacementNamed(HomeLayoutScreen.routeName); - // } - }), - ContinueWithFacebook(handler: () {}), - Padding( - padding: const EdgeInsets.all(8.0), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - Container( - width: 20.h, - child: Divider( - thickness: 1, - color: Colors.black26, - ), + if (kIsWeb) WebImageLoging(), + SizedBox( + width: (kIsWeb) ? 25.w : null, + child: Column( + mainAxisAlignment: + (kIsWeb) ? MainAxisAlignment.center : MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (!kIsWeb) UpperBar(UpperbarStatus.signup), + Expanded( + flex: (kIsWeb) ? 0 : 1, + child: SingleChildScrollView( + child: Column(children: [ + UpperText('Log in to Reddit'), + SizedBox( + height: 4.h, + ), + ContinueWithGoogle(handler: () async { + final user = await GoogleSignInApi.login(); + user!.id; + final provider = + Provider.of(context, listen: false); + await provider.GoogleAuth({ + "id": user.id, + "email": user.email, + "displayName": user.displayName! + }); + if (provider.error == false) { + Navigator.of(context) + .pushReplacementNamed(HomeLayoutScreen.routeName); + } + }), + ContinueWithFacebook(handler: () {}), + Padding( + padding: const EdgeInsets.all(8.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + Container( + width: (kIsWeb) ? 10.w : 20.h, + child: Divider( + thickness: 1, + color: Colors.black26, + ), + ), + Container( + child: Text( + style: TextStyle(color: Colors.black), 'OR'), + ), + Container( + width: (kIsWeb) ? 10.w : 20.h, + child: Divider( + thickness: 1, + color: Colors.black26, + ), + ) + ], ), - Container( - child: - Text(style: TextStyle(color: Colors.black), 'OR'), + ), + SizedBox( + height: 3.h, + ), + TextInput( + lable: 'Username', + ontap: (hasfocus) {}, + changeInput: () { + setState(() { + changeInput(); + }); + }, + inputController: inputUserNameController), + PasswordInput( + isVisable: isVisable, + lable: 'Password', + ontap: (hasfocus) {}, + changeInput: () { + setState(() { + changeInput(); + }); + }, + inputController: inputPasswardController), + SizedBox( + height: 2.h, + ), + Align( + alignment: Alignment.centerLeft, + child: Row( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Align( + alignment: Alignment.centerLeft, + child: TextButton( + onPressed: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (BuildContext context) => + ResponsiveSizer( + builder: (cntx, orientation, + Screentype) { + // Device.deviceType == DeviceType.web; + return Scaffold( + body: ForgotPassword()); + }, + ), + )); + }, + child: Text( + style: TextStyle( + color: (kIsWeb) + ? Colors.blue + : Colors.red), + 'Forget passward'))), + if (kIsWeb) + Align( + alignment: Alignment.centerLeft, + child: TextButton( + onPressed: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (BuildContext context) => + ResponsiveSizer( + builder: (cntx, orientation, + Screentype) { + // Device.deviceType == DeviceType.web; + return Scaffold( + body: ForgotUserName()); + }, + ), + )); + }, + child: Text( + style: TextStyle( + color: (kIsWeb) + ? Colors.blue + : Colors.red), + 'Forget Username'))), + ], ), - Container( - width: 20.h, - child: Divider( - thickness: 1, - color: Colors.black26, + ), + RichText( + textAlign: TextAlign.center, + text: TextSpan(children: [ + TextSpan( + style: TextStyle(color: Colors.black), + text: 'By continuing, you agree to our '), + TextSpan( + text: 'User Agreement ', + style: TextStyle( + decoration: TextDecoration.underline, + color: (kIsWeb) ? Colors.blue : Colors.red), + recognizer: TapGestureRecognizer() + ..onTap = () async { + //on tap code here, you can navigate to other page or URL + String url = + "https://www.redditinc.com/policies/user-agreement"; + var urllaunchable = await canLaunch( + url); //canLaunch is from url_launcher package + if (urllaunchable) { + await launch( + url); //launch is from url_launcher package to launch URL + } else { + print("URL can't be launched."); + } + }, ), - ) - ], - ), + TextSpan( + style: TextStyle(color: Colors.black), + text: 'and '), + TextSpan( + text: 'Privacy Policy', + style: TextStyle( + decoration: TextDecoration.underline, + color: (kIsWeb) ? Colors.blue : Colors.red), + recognizer: TapGestureRecognizer() + ..onTap = () async { + //on tap code here, you can navigate to other page or URL + String url = + "https://www.reddit.com/policies/privacy-policy"; + var urllaunchable = await canLaunch( + url); //canLaunch is from url_launcher package + if (urllaunchable) { + await launch( + url); //launch is from url_launcher package to launch URL + } else { + print("URL can't be launched."); + } + }, + ), + ]), + ), + ]), ), - SizedBox( - height: 3.h, + ), + if (isSubmit && isError) + Padding( + padding: EdgeInsets.all(5.w), + child: Center( + child: Text( + textAlign: TextAlign.center, + errorMessage, + style: TextStyle( + fontSize: 18, + // fontWeight: FontWeight.w500, + color: Theme.of(context).errorColor, + )), + ), ), - TextInput( - lable: 'Username', - ontap: (hasfocus) {}, - changeInput: () { - setState(() { - changeInput(); - }); - }, - inputController: inputUserNameController), - PasswordInput( - isVisable: isVisable, - lable: 'Password', - ontap: (hasfocus) {}, - changeInput: () { - setState(() { - changeInput(); - }); - }, - inputController: inputPasswardController), - SizedBox( - height: 2.h, + if (isSubmit && !isError) + Padding( + padding: EdgeInsets.all(5.w), + child: Text( + textAlign: TextAlign.center, + 'you will receve if that adress maches your mail', + style: TextStyle( + fontSize: 18, + // fontWeight: FontWeight.w500, + color: Colors.green, + )), ), - Align( - alignment: Alignment.centerLeft, - child: TextButton( + if (kIsWeb) SizedBox(height: 5.h), + Container( + height: (kIsWeb) ? 40 : null, + width: double.infinity, + child: Padding( + padding: EdgeInsets.only( + left: 8.0, + right: 8.0, + ), + child: ElevatedButton( + style: ElevatedButton.styleFrom( + onPrimary: Colors.white, + primary: (kIsWeb) ? Colors.blue : Colors.red, + onSurface: Colors.grey[700], + shape: (kIsWeb) + ? null + : RoundedRectangleBorder( + borderRadius: BorderRadius.circular(30)), + ), + onPressed: isFinished + ? () { + checkLogin(context); + } + : null, + child: (kIsWeb) ? Text('Log in') : Text('Continue'), + ), + )), + if (kIsWeb) SizedBox(height: 5.h), + if (kIsWeb) + Row( + children: [ + Text( + 'New to Reddit? ', + style: TextStyle(color: Colors.black), + ), + TextButton( onPressed: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (BuildContext context) => - ResponsiveSizer( - builder: (cntx, orientation, Screentype) { - // Device.deviceType == DeviceType.web; - return Scaffold(body: ForgotPassword()); - }, - ), - )); + Navigator.pushNamed(context, SignUp.routeName); }, child: Text( - style: TextStyle(color: Colors.red), - 'Forget passward'))), - RichText( - textAlign: TextAlign.center, - text: TextSpan(children: [ - TextSpan( - style: TextStyle(color: Colors.black), - text: 'By continuing, you agree to our '), - TextSpan( - text: 'User Agreement ', - style: TextStyle( - decoration: TextDecoration.underline, - color: Theme.of(context).primaryColor), - recognizer: TapGestureRecognizer() - ..onTap = () async { - //on tap code here, you can navigate to other page or URL - String url = - "https://www.redditinc.com/policies/user-agreement"; - var urllaunchable = await canLaunch( - url); //canLaunch is from url_launcher package - if (urllaunchable) { - await launch( - url); //launch is from url_launcher package to launch URL - } else { - print("URL can't be launched."); - } - }, - ), - TextSpan( - style: TextStyle(color: Colors.black), text: 'and '), - TextSpan( - text: 'Privacy Policy', - style: TextStyle( - decoration: TextDecoration.underline, - color: Theme.of(context).primaryColor), - recognizer: TapGestureRecognizer() - ..onTap = () async { - //on tap code here, you can navigate to other page or URL - String url = - "https://www.reddit.com/policies/privacy-policy"; - var urllaunchable = await canLaunch( - url); //canLaunch is from url_launcher package - if (urllaunchable) { - await launch( - url); //launch is from url_launcher package to launch URL - } else { - print("URL can't be launched."); - } - }, - ), - ]), + 'Sign Up', + style: TextStyle(color: Colors.blue), + )) + ], ), - ]), - ), + ], ), ), - // Spacer(), - if (isSubmit && isError) - Padding( - padding: EdgeInsets.all(5.w), - child: Center( - child: Text( - textAlign: TextAlign.center, - errorMessage, - style: TextStyle( - fontSize: 18, - // fontWeight: FontWeight.w500, - color: Theme.of(context).errorColor, - )), - ), - ), - if (isSubmit && !isError) - Padding( - padding: EdgeInsets.all(5.w), - child: Text( - textAlign: TextAlign.center, - 'you will receve if that adress maches your mail', - style: TextStyle( - fontSize: 18, - // fontWeight: FontWeight.w500, - color: Colors.green, - )), - ), - Container( - width: double.infinity, - child: Padding( - padding: EdgeInsets.only( - left: 8.0, - right: 8.0, - ), - child: ElevatedButton( - style: ElevatedButton.styleFrom( - onPrimary: Colors.white, - primary: Colors.red, - onSurface: Colors.grey[700], - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(30)), - ), - onPressed: isFinished - ? () { - checkLogin(context); - } - : null, - child: Text('Continue'), - ), - )), ], ), ); diff --git a/lib/logins/screens/signup.dart b/lib/logins/screens/signup.dart index c327e4c0..68440838 100644 --- a/lib/logins/screens/signup.dart +++ b/lib/logins/screens/signup.dart @@ -14,7 +14,12 @@ import 'package:email_validator/email_validator.dart'; import 'gender.dart'; import '../providers/authentication.dart'; import 'package:provider/provider.dart'; +import 'package:flutter/foundation.dart' show kIsWeb; +import 'login.dart'; +import '../widgets/web_image.dart'; +// import 'package:webview_flutter_plus/webview_flutter_plus.dart'; +// (kIsWeb) ? Colors.blue : Colors.red class SignUp extends StatefulWidget { // const SignUp({Key? key}) : super(key: key); static const routeName = '/SignUp'; @@ -201,199 +206,238 @@ class SignUpState extends State { Widget build(BuildContext context) { final mediaQuery = MediaQuery.of(context); return Scaffold( - body: Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, + body: Row( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, children: [ - UpperBar(UpperbarStatus.login), - Expanded( - child: Container( - // height: 80.h, - child: SingleChildScrollView( - child: Column(children: [ - UpperText('Hi new friend, welcome to Reddit'), - SizedBox( - height: 4.h, - ), - ContinueWithGoogle(handler: () {}), - ContinueWithFacebook(handler: () {}), - Padding( - padding: const EdgeInsets.all(8.0), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - Container( - width: 20.h, - child: Divider( - // height: 2, - thickness: 1, - color: Colors.black26, + if (kIsWeb) WebImageLoging(), + SizedBox( + width: (kIsWeb) ? 25.w : null, + child: Column( + mainAxisAlignment: + (kIsWeb) ? MainAxisAlignment.center : MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (!kIsWeb) UpperBar(UpperbarStatus.login), + Expanded( + flex: (kIsWeb) ? 0 : 1, + child: Container( + // height: 80.h, + child: SingleChildScrollView( + child: Column(children: [ + UpperText('Hi new friend, welcome to Reddit'), + SizedBox( + height: 4.h, + ), + ContinueWithGoogle(handler: () {}), + ContinueWithFacebook(handler: () {}), + Padding( + padding: const EdgeInsets.all(8.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + Container( + width: (kIsWeb) ? 10.w : 20.h, + child: Divider( + thickness: 1, + color: Colors.black26, + ), + ), + Container( + child: Text( + style: TextStyle(color: Colors.black), + 'OR'), + ), + Container( + width: (kIsWeb) ? 10.w : 20.h, + child: Divider( + thickness: 1, + color: Colors.black26, + ), + ) + ], ), ), - Container( - child: - Text(style: TextStyle(color: Colors.black), 'OR'), + SizedBox( + height: 3.h, ), - Container( - width: 20.h, - child: Divider( - thickness: 1, - color: Colors.black26, + TextInput( + currentStatus: inputEmailStatus, + lable: 'Email', + ontap: (focus) { + controlEmailStatus(focus); + setState(() {}); + }, + changeInput: changeInput, + inputController: inputEmailController), + if (inputEmailStatus == InputStatus.failed) + Text( + emailErrorMessage, + style: + TextStyle(color: Theme.of(context).errorColor), + ), + TextInput( + currentStatus: inputUsernameStatus, + lable: 'Username', + ontap: (focus) { + controlUsernameStatus(focus); + setState(() {}); + }, + changeInput: changeInput, + inputController: inputUserNameController), + if (inputUsernameStatus == InputStatus.failed) + Text( + usernameErrorMessage, + style: + TextStyle(color: Theme.of(context).errorColor), ), - ) - ], + PasswordInput( + lable: 'Passward', + currentStatus: inputPasswardStatus, + ontap: (focus) { + controlPasswordStatus(focus); + setState(() {}); + }, + isVisable: isVisable, + changeInput: () { + setState(() { + changeInput(); + }); + }, + inputController: inputPasswardController), + if (inputPasswardStatus == InputStatus.failed) + Text( + passwordErrorMessage, + style: + TextStyle(color: Theme.of(context).errorColor), + ), + SizedBox( + height: 2.h, + ), + RichText( + textAlign: TextAlign.center, + text: TextSpan(children: [ + TextSpan( + style: TextStyle(color: Colors.black), + text: 'By continuing, you agree to our '), + TextSpan( + text: 'User Agreement ', + style: TextStyle( + decoration: TextDecoration.underline, + color: (kIsWeb) ? Colors.blue : Colors.red), + recognizer: TapGestureRecognizer() + ..onTap = () async { + //on tap code here, you can navigate to other page or URL + String url = + "https://www.redditinc.com/policies/user-agreement"; + var urllaunchable = await canLaunch( + url); //canLaunch is from url_launcher package + if (urllaunchable) { + await launch( + url); //launch is from url_launcher package to launch URL + } else { + print("URL can't be launched."); + } + }, + ), + TextSpan( + style: TextStyle(color: Colors.black), + text: 'and '), + TextSpan( + text: 'Privacy Policy', + style: TextStyle( + decoration: TextDecoration.underline, + color: (kIsWeb) ? Colors.blue : Colors.red), + recognizer: TapGestureRecognizer() + ..onTap = () async { + //on tap code here, you can navigate to other page or URL + String url = + "https://www.reddit.com/policies/privacy-policy"; + var urllaunchable = await canLaunch( + url); //canLaunch is from url_launcher package + if (urllaunchable) { + await launch( + url); //launch is from url_launcher package to launch URL + } else { + print("URL can't be launched."); + } + }, + ), + ]), + ), + ]), ), ), - SizedBox( - height: 3.h, - ), - TextInput( - currentStatus: inputEmailStatus, - lable: 'Email', - ontap: (focus) { - controlEmailStatus(focus); - setState(() {}); - }, - changeInput: changeInput, - inputController: inputEmailController), - if (inputEmailStatus == InputStatus.failed) - Text( - emailErrorMessage, - style: TextStyle(color: Theme.of(context).errorColor), - ), - TextInput( - currentStatus: inputUsernameStatus, - lable: 'Username', - ontap: (focus) { - controlUsernameStatus(focus); - setState(() {}); - }, - changeInput: changeInput, - inputController: inputUserNameController), - if (inputUsernameStatus == InputStatus.failed) - Text( - usernameErrorMessage, - style: TextStyle(color: Theme.of(context).errorColor), - ), - PasswordInput( - lable: 'Passward', - currentStatus: inputPasswardStatus, - ontap: (focus) { - controlPasswordStatus(focus); - setState(() {}); - }, - isVisable: isVisable, - changeInput: () { - setState(() { - changeInput(); - }); - }, - inputController: inputPasswardController), - if (inputPasswardStatus == InputStatus.failed) - Text( - passwordErrorMessage, - style: TextStyle(color: Theme.of(context).errorColor), + ), + if (isSubmit && isError) + Padding( + padding: EdgeInsets.all(5.w), + child: Center( + child: Text( + textAlign: TextAlign.center, + errorMessage, + style: TextStyle( + fontSize: 18, + color: Theme.of(context).errorColor, + )), ), - SizedBox( - height: 2.h, ), - RichText( - textAlign: TextAlign.center, - text: TextSpan(children: [ - TextSpan( - style: TextStyle(color: Colors.black), - text: 'By continuing, you agree to our '), - TextSpan( - text: 'User Agreement ', + if (isSubmit && !isError) + Padding( + padding: EdgeInsets.all(5.w), + child: Text( + textAlign: TextAlign.center, + 'you will receve if that adress maches your mail', style: TextStyle( - decoration: TextDecoration.underline, - color: Theme.of(context).primaryColor), - recognizer: TapGestureRecognizer() - ..onTap = () async { - //on tap code here, you can navigate to other page or URL - String url = - "https://www.redditinc.com/policies/user-agreement"; - var urllaunchable = await canLaunch( - url); //canLaunch is from url_launcher package - if (urllaunchable) { - await launch( - url); //launch is from url_launcher package to launch URL - } else { - print("URL can't be launched."); - } - }, + fontSize: 18, + color: Colors.green, + )), + ), + if (kIsWeb) SizedBox(height: 5.h), + Container( + height: (kIsWeb) ? 40 : null, + width: double.infinity, + child: Padding( + padding: EdgeInsets.only( + left: 8.0, + right: 8.0, ), - TextSpan( - style: TextStyle(color: Colors.black), text: 'and '), - TextSpan( - text: 'Privacy Policy', - style: TextStyle( - decoration: TextDecoration.underline, - color: Theme.of(context).primaryColor), - recognizer: TapGestureRecognizer() - ..onTap = () async { - //on tap code here, you can navigate to other page or URL - String url = - "https://www.reddit.com/policies/privacy-policy"; - var urllaunchable = await canLaunch( - url); //canLaunch is from url_launcher package - if (urllaunchable) { - await launch( - url); //launch is from url_launcher package to launch URL - } else { - print("URL can't be launched."); - } - }, + child: ElevatedButton( + style: ElevatedButton.styleFrom( + onPrimary: Colors.white, + primary: (kIsWeb) ? Colors.blue : Colors.red, + onSurface: Colors.grey[700], + shape: (kIsWeb) + ? null + : RoundedRectangleBorder( + borderRadius: BorderRadius.circular(30)), + ), + // onPressed: () {}, + onPressed: isFinished ? submitSignUp : null, + child: Text('Continue'), ), - ]), + )), + if (kIsWeb) SizedBox(height: 5.h), + if (kIsWeb) + Row( + children: [ + Text( + 'alreday has account? ', + style: TextStyle(color: Colors.black), + ), + TextButton( + onPressed: () { + Navigator.pushNamed(context, Login.routeName); + }, + child: Text( + 'Log in', + style: TextStyle(color: Colors.blue), + )) + ], ), - ]), - ), + ], ), ), - if (isSubmit && isError) - Padding( - padding: EdgeInsets.all(5.w), - child: Center( - child: Text( - textAlign: TextAlign.center, - errorMessage, - style: TextStyle( - fontSize: 18, - color: Theme.of(context).errorColor, - )), - ), - ), - if (isSubmit && !isError) - Padding( - padding: EdgeInsets.all(5.w), - child: Text( - textAlign: TextAlign.center, - 'you will receve if that adress maches your mail', - style: TextStyle( - fontSize: 18, - color: Colors.green, - )), - ), - Container( - width: double.infinity, - child: Padding( - padding: EdgeInsets.only( - left: 8.0, - right: 8.0, - ), - child: ElevatedButton( - style: ElevatedButton.styleFrom( - onPrimary: Colors.white, - primary: Colors.red, - onSurface: Colors.grey[700], - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(30)), - ), - onPressed: isFinished ? submitSignUp : null, - child: Text('Continue'), - ), - )) ], ), ); diff --git a/lib/logins/widgets/text_input.dart b/lib/logins/widgets/text_input.dart index 3dfc67f7..bf2ee0fd 100644 --- a/lib/logins/widgets/text_input.dart +++ b/lib/logins/widgets/text_input.dart @@ -1,3 +1,4 @@ +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import '../../moderation_settings/widgets/status.dart'; diff --git a/lib/logins/widgets/web_image.dart b/lib/logins/widgets/web_image.dart new file mode 100644 index 00000000..d248db77 --- /dev/null +++ b/lib/logins/widgets/web_image.dart @@ -0,0 +1,18 @@ +import 'package:flutter/src/widgets/container.dart'; +import 'package:flutter/src/widgets/framework.dart'; +import 'package:responsive_sizer/responsive_sizer.dart'; +import 'package:flutter/material.dart'; + +class WebImageLoging extends StatelessWidget { + const WebImageLoging({super.key}); + + @override + Widget build(BuildContext context) { + return Image.asset( + 'assets/images/reddit-logins.png', + width: 10.w, + height: 100.h, + fit: BoxFit.fill, + ); + } +} diff --git a/lib/main.dart b/lib/main.dart index 88c1fc6b..f0be5e8e 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,6 +1,7 @@ import 'dart:convert'; -// import 'package:firebase_core/firebase_core.dart'; -// import 'package:firebase_messaging/firebase_messaging.dart'; +import 'package:firebase_core/firebase_core.dart'; +import 'package:firebase_messaging/firebase_messaging.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:post/comments/providers/comments_provider.dart'; @@ -8,6 +9,8 @@ import 'package:post/create_community/widgets/community_type.dart'; import 'package:post/messages/screens/reply_message_screen.dart'; import 'package:post/messages/screens/show_message_body.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart'; +import './messages/screens/web_message_all_screen.dart'; +import './messages/screens/web_message_screen.dart'; import 'package:post/providers/global_settings.dart'; import 'package:responsive_sizer/responsive_sizer.dart'; import 'package:provider/provider.dart'; @@ -37,9 +40,12 @@ import './logins/screens/forgot_username.dart'; import './screens/emptyscreen.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'logins/providers/notification.dart'; -import 'messages/Provider/message_provider.dart'; +import 'messages/provider/message_provider.dart'; import 'messages/models/user_message.dart'; import 'messages/screens/new_message_screen.dart'; +import 'messages/screens/unread_message_screen.dart'; +import 'messages/screens/web_new_message_screen.dart'; +import 'messages/screens/web_sent_message.dart'; import 'notification/models/notification_class_model.dart'; import 'show_post/screens/show_post.dart'; import 'show_post/widgets/edit_post.dart'; @@ -68,10 +74,12 @@ import 'moderation_settings/screens/post_flair.dart'; import 'moderation_settings/screens/post_flair_settings.dart'; import './search/screens/search.dart'; import './search/screens/search_inside.dart'; +import './moderation_settings/screens/traffic_state.dart'; +import './moderation_settings/screens/traffic_table.dart'; //=====================================Providers====================================================// import './myprofile/providers/myprofile_provider.dart'; import './other_profile/providers/other_profile_provider.dart'; -import './providers/Profile_provider.dart'; +import 'providers/profile_provider.dart'; import 'providers/subreddit_posts_provider.dart'; import './subreddit/providers/subreddit_provider.dart'; import './moderated_subreddit/providers/moderated_subreddit_provider.dart'; @@ -84,7 +92,10 @@ import './moderation_settings/provider/change_user_management.dart'; import './settings/provider/user_settings_provider.dart'; import './search/provider/search_provider.dart'; import './discover/providers/discover_provider.dart'; +import './moderation_settings/provider/moderation_general_data.dart'; +import 'widgets/custom_snack_bar.dart'; //import './models/push_notification_model.dart'; +import './shared/constants.dart'; String returnCorrectText(type, name, user) { String text = ''; @@ -153,14 +164,14 @@ String returnCorrectDescription(type, description, name) { } //@pragma('vm:entry-point') -NotificationModel notificationModel = NotificationModel(); -NotificationProvider provider = NotificationProvider(); -ShowMessagesModel messageModel = ShowMessagesModel(); -final GlobalKey navState = GlobalKey(); +// NotificationModel notificationModel = NotificationModel(); +// NotificationProvider provider = NotificationProvider(); +// ShowMessagesModel messageModel = ShowMessagesModel(); +// final GlobalKey navState = GlobalKey(); // Future _firebaseMessagingBackgroundHandler(RemoteMessage message) async { -// // await Firebase.initializeApp(); -// // await FirebaseMessaging.instance.getToken(); -// // await setupFlutterNotifications(); +// await Firebase.initializeApp(); +// await FirebaseMessaging.instance.getToken(); +// await setupFlutterNotifications(); // RemoteNotification? notification = message.notification; // if (json.decode(message.data['val'])['type'] == 'userMention' || // json.decode(message.data['val'])['type'] == 'follow' || @@ -207,16 +218,16 @@ final GlobalKey navState = GlobalKey(); // } // } -// bool isFlutterLocalNotificationsInitialized = false; -// FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = -// FlutterLocalNotificationsPlugin(); -// AndroidNotificationChannel channel = const AndroidNotificationChannel( -// 'high_importance_channel', // id -// 'High Importance Notifications', // title -// description: -// 'This channel is used for important notifications.', // description -// importance: Importance.high, -// ); +bool isFlutterLocalNotificationsInitialized = false; +FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = + FlutterLocalNotificationsPlugin(); +AndroidNotificationChannel channel = const AndroidNotificationChannel( + 'high_importance_channel', // id + 'High Importance Notifications', // title + description: + 'This channel is used for important notifications.', // description + importance: Importance.high, +); // Future setupFlutterNotifications() async { // if (isFlutterLocalNotificationsInitialized) { @@ -227,11 +238,11 @@ final GlobalKey navState = GlobalKey(); // .resolvePlatformSpecificImplementation< // AndroidFlutterLocalNotificationsPlugin>() // ?.createNotificationChannel(channel); -// // await FirebaseMessaging.instance.setForegroundNotificationPresentationOptions( -// // alert: true, -// // badge: true, -// // sound: true, -// // ); +// await FirebaseMessaging.instance.setForegroundNotificationPresentationOptions( +// alert: true, +// badge: true, +// sound: true, +// ); // isFlutterLocalNotificationsInitialized = true; // } @@ -243,16 +254,26 @@ Future main() async { final int? cont = prefs.getInt('counter'); await prefs.setInt('not', 0); provider.initState(); - // await Firebase.initializeApp(); - // await NotificationToken.getTokenOfNotification(); + //if(!kIsWeb){ + //await Firebase.initializeApp(); + //await NotificationToken.getTokenOfNotification(); // final RemoteMessage? remoteMessage = // await FirebaseMessaging.instance.getInitialMessage(); - // FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler); //FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler); + //FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler); + // } + //} + await Firebase.initializeApp( + options: FirebaseOptions( + apiKey: Constants.apiKey, + appId: Constants.appId, + messagingSenderId: Constants.messagingSenderId, + projectId: Constants.projectId)); + await NotificationToken.getTokenOfNotification(); runApp( ChangeNotifierProvider( create: (context) => GlobalSettings(true, true), - child: MyApp(), + child: MaterialApp(home: MyApp()), ), ); } @@ -266,111 +287,155 @@ class MyApp extends StatefulWidget { class _MyAppState extends State { int counter = 0; - // @override - // void initState() { - // //AndroidNotificationChannel channel; - // super.initState(); - // var initializationSettingsAndroid = - // AndroidInitializationSettings('@mipmap/ic_launcher'); - // var initializationSettings = - // InitializationSettings(android: initializationSettingsAndroid); - // flutterLocalNotificationsPlugin.initialize(initializationSettings); - // FirebaseMessaging.onMessage.listen((RemoteMessage message) async { - // //print(message.data); + @override + void initState() { + //AndroidNotificationChannel channel; + super.initState(); + var initializationSettingsAndroid = + AndroidInitializationSettings('@mipmap/ic_launcher'); + var initializationSettings = + InitializationSettings(android: initializationSettingsAndroid); + flutterLocalNotificationsPlugin.initialize(initializationSettings); + FirebaseMessaging.onMessage.listen((RemoteMessage message) async { + RemoteNotification? notification = message.notification; + AndroidNotification? android = message.notification?.android; - // RemoteNotification? notification = message.notification; - // AndroidNotification? android = message.notification?.android; - // // print(notification.hashCode); - // if (message.data != null) { - // print(message.data['val']); - // if (json.decode(message.data['val'])['type'] == 'userMention' || - // json.decode(message.data['val'])['type'] == 'follow' || - // json.decode(message.data['val'])['type'] == 'postReply' || - // json.decode(message.data['val'])['type'] == 'commentReply') { - // notificationModel = - // NotificationModel.fromJson(json.decode(message.data['val'])); - // //provider.incrementCounter(); - // flutterLocalNotificationsPlugin.show( - // notification.hashCode, - // returnCorrectText( - // notificationModel.type, - // notificationModel.requiredName, - // notificationModel.followeruserName), - // returnCorrectDescription( - // notificationModel.type, - // notificationModel.description, - // notificationModel.requiredName), - // NotificationDetails( - // android: AndroidNotificationDetails( - // channel.id, - // channel.name, - // channelDescription: channel.description, - // color: Colors.blue, - // playSound: true, - // // icon: ('assets/images/reddit.png'), - // ))); - // } else { - // // messageModel = - // // ShowMessagesModel.fromJson(json.decode(message.data['val'])); - // final json1 = json.decode(message.data['val']); - // print(json1); - // String text = (json1['subject'] == null) - // ? returnText(json1['type']) - // : json1['subject']['text']; - // print(text); - // String body = (json1['text'] == null) - // ? returnBody(json1['type']) - // : json1['text']; - // print(body); - // flutterLocalNotificationsPlugin.show( - // notification.hashCode, - // text, - // body, - // NotificationDetails( - // android: AndroidNotificationDetails( - // channel.id, - // channel.name, - // channelDescription: channel.description, - // color: Colors.blue, - // playSound: true, - // // icon: ('assets/images/reddit.png'), - // ))); - // } - // FirebaseMessaging.onMessageOpenedApp.listen( - // (RemoteMessage message) async { - // print('BYEEEEEEEEEEEEEEEEEEEEEEE'); - // print(message.data); - // RemoteNotification? notification = message.notification; - // AndroidNotification? android = message.notification?.android; - // if (message.data['val'] != null) { - // print('heereeeeeeeeeeeeee'); - // Navigator.of(navState.currentState!.context) - // .pushNamed(NavigateToCorrectScreen.routeName); - // } - // }, - // ); - // } - // }); - // initializationSettingsAndroid = - // AndroidInitializationSettings('@mipmap/ic_launcher'); - // initializationSettings = - // InitializationSettings(android: initializationSettingsAndroid); - // flutterLocalNotificationsPlugin.initialize(initializationSettings); - // FirebaseMessaging.instance.getInitialMessage(); - // FirebaseMessaging.onMessageOpenedApp.listen( - // (RemoteMessage message) async { - // print('BYEEEEEEEEEEEEEEEEEEEEEEE'); - // print(message.data); - // RemoteNotification? notification = message.notification; - // AndroidNotification? android = message.notification?.android; - // if (message.data['val'] != null) { - // print('heereeeeeeeeeeeeee'); - // Navigator.of(navState.currentState!.context) - // .pushNamed(NavigateToCorrectScreen.routeName); - // } - // }, - // ); - // } + if (message.data != null) { + print(message.data['val']); + if (json.decode(message.data['val'])['type'] == 'userMention' || + json.decode(message.data['val'])['type'] == 'follow' || + json.decode(message.data['val'])['type'] == 'postReply' || + json.decode(message.data['val'])['type'] == 'commentReply') { + notificationModel = + NotificationModel.fromJson(json.decode(message.data['val'])); + if (!kIsWeb) { + flutterLocalNotificationsPlugin.show( + notification.hashCode, + returnCorrectText( + notificationModel.type, + notificationModel.requiredName, + notificationModel.followeruserName), + returnCorrectDescription( + notificationModel.type, + notificationModel.description, + notificationModel.requiredName), + NotificationDetails( + android: AndroidNotificationDetails( + channel.id, + channel.name, + channelDescription: channel.description, + color: Colors.blue, + playSound: true, + ))); + } else { + print('nameeeeeeeeeeeeeeeeeeeeeeeeeeeeeee'); + showDialog( + context: context, + builder: ((context) { + return AlertDialog( + title: const Text( + 'New notification arrived', + style: TextStyle(fontSize: 15, fontWeight: FontWeight.bold), + ), + insetPadding: EdgeInsets.zero, + content: SizedBox( + width: 40.h, + child: Text( + returnCorrectText( + notificationModel.type, + notificationModel.requiredName, + notificationModel.followeruserName), + style: TextStyle(fontSize: 11), + ), + ), + actionsAlignment: MainAxisAlignment.spaceBetween, + ); + }), + ); + print(notificationModel.requiredName); + } + } else { + final json1 = json.decode(message.data['val']); + String text = (json1['subject'] == null) + ? returnText(json1['type']) + : json1['subject']['text']; + print(text); + String body = (json1['text'] == null) + ? returnBody(json1['type']) + : json1['text']; + print(body); + if (!kIsWeb) { + flutterLocalNotificationsPlugin.show( + notification.hashCode, + text, + body, + NotificationDetails( + android: AndroidNotificationDetails( + channel.id, + channel.name, + channelDescription: channel.description, + color: Colors.blue, + playSound: true, + // icon: ('assets/images/reddit.png'), + ))); + } else { + showDialog( + context: context, + builder: ((context) { + return AlertDialog( + title: const Text( + 'New notification arrived', + style: TextStyle(fontSize: 15, fontWeight: FontWeight.bold), + ), + insetPadding: EdgeInsets.zero, + content: SizedBox( + width: 40.h, + child: Text( + text, + style: TextStyle(fontSize: 11), + ), + ), + actionsAlignment: MainAxisAlignment.spaceBetween, + ); + }), + ); + } + } + // FirebaseMessaging.onMessageOpenedApp.listen( + // (RemoteMessage message) async { + // print('BYEEEEEEEEEEEEEEEEEEEEEEE'); + // print(message.data); + // RemoteNotification? notification = message.notification; + // AndroidNotification? android = message.notification?.android; + // if (message.data['val'] != null) { + // print('heereeeeeeeeeeeeee'); + // Navigator.of(navState.currentState!.context) + // .pushNamed(NavigateToCorrectScreen.routeName); + // } + // }, + // ); + } + }); + // initializationSettingsAndroid = + // AndroidInitializationSettings('@mipmap/ic_launcher'); + // initializationSettings = + // InitializationSettings(android: initializationSettingsAndroid); + // flutterLocalNotificationsPlugin.initialize(initializationSettings); + // FirebaseMessaging.instance.getInitialMessage(); + // FirebaseMessaging.onMessageOpenedApp.listen( + // (RemoteMessage message) async { + // print('BYEEEEEEEEEEEEEEEEEEEEEEE'); + // print(message.data); + // RemoteNotification? notification = message.notification; + // AndroidNotification? android = message.notification?.android; + // if (message.data['val'] != null) { + // print('heereeeeeeeeeeeeee'); + // Navigator.of(navState.currentState!.context) + // .pushNamed(NavigateToCorrectScreen.routeName); + // } + // }, + // ); + } //push.onMessageListener(); // push.onOpeningMessage(context); @@ -392,6 +457,8 @@ class _MyAppState extends State { ChangeNotifierProvider.value(value: PostCommentsProvider()), ///////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////// + ChangeNotifierProvider.value( + value: ModerationGeneralDataProvider()), ChangeNotifierProvider.value(value: SearchProvider()), ChangeNotifierProvider.value(value: UserSettingsProvider()), ChangeNotifierProvider.value(value: ChangeUserManagementProvider()), @@ -417,7 +484,7 @@ class _MyAppState extends State { ], child: GetMaterialApp( debugShowCheckedModeBanner: false, - navigatorKey: navState, + // navigatorKey: navState, title: 'Logins', theme: theme.copyWith( primaryColor: Colors.red, @@ -430,15 +497,15 @@ class _MyAppState extends State { onSurface: Colors.white), ), // home: NotificationScreen(), - // home: HomeLayoutScreen(), + // home: HomeLayoutScreen(), // home: Description(), // home: HomeScreen(), - //home: NotificationScreen(), + // home: NotificationScreen(), // home:ShowPostDetails(), - // home: CreateCommunity(), - // home: NewMessageScreen(), + //home: CreateCommunity(), + //home: NewMessageScreen(), // home: EditPost(), - home: Login(), + // home: WebMessageScreen(), // home: SearchInside(quiry: 'mohab'), //home: const DiscoverScreen(), // home: homeLayoutScreen(), @@ -450,11 +517,14 @@ class _MyAppState extends State { // home: CreateCommunity(), //home: homeLayoutScreen(), // home: HomeScreen(), - // home: Login(), + home: Login(), // home: CreateCommunity(), - // home: Login(), + //home: Login(), // home: ForgotUserName(), - //home: SignUp(), + // home: ForgotPassword(), + // home: TraficState(), + // home: TrafficTable(), + // home: SignUp(), // home: Gender(), // home: ModeratorTools(), // home: Settings(), @@ -470,6 +540,11 @@ class _MyAppState extends State { // home: EditModeratorScreen(subredditName: 'Cooking'), // home: Search(), routes: { + TraficState.routeName: (context) => TraficState(), + AllMessageScreen.routeName : (context) => AllMessageScreen(), + WebNewMessageScreen.routeName : (context) => WebNewMessageScreen(), + SentMessage.routeName : (context) => SentMessage(), + UnreadMessageScreen.routeName : (context) => UnreadMessageScreen(), ReplyMessageScreen.routeName: (context) => ReplyMessageScreen(), ShowMessageBody.routeName: (context) => ShowMessageBody(), MessageMainScreen.routeName: (context) => MessageMainScreen(), @@ -481,7 +556,7 @@ class _MyAppState extends State { Search.routeName: (context) => Search(), SearchInside.routeName: (context) => SearchInside(), MutedScreen.routeName: (context) => MutedScreen(), - + WebMessageScreen.routeName: (context) => WebMessageScreen(), EditApprovedScreen.routeName: (context) => EditApprovedScreen(subredditName: ''), EditBannedScreen.routeName: (context) => diff --git a/lib/messages/Provider/message_provider.dart b/lib/messages/Provider/message_provider.dart index f0b5bbb0..32dee617 100644 --- a/lib/messages/Provider/message_provider.dart +++ b/lib/messages/Provider/message_provider.dart @@ -8,6 +8,9 @@ import '../models/user_message.dart'; class MessageProvider with ChangeNotifier { Map> messageShow = {}; + List allMessage = []; + List unreadMessage = []; + List sentMessage = []; //http://localhost:8000/api/v1/subreddits/{subredditName}/moderators/{moderatorName} Future getAllMessages(context, page, limit) async { try { @@ -23,43 +26,89 @@ class MessageProvider with ChangeNotifier { String id; final message = ShowMessagesModel.fromJson(element); print('herrree'); - // print(element); - - // if(newList == null){ - //newList = List; if (message.type == 'userMessage') { id = message.subjectId!; } else { id = message.sId!; } - // var newList = messageShow.entries - // .firstWhere((element) => element.value == id) - // .value; - // if (newList == null) { - // newList = []; - // } - //List newList = []; - // newList.add(message); print('**********************IDS*****************************'); print(message.sId); messageShow.putIfAbsent(id, () => []).add(message); - // } else{ + }); + notifyListeners(); + }); + } on DioError catch (e) { + HandleError.errorHandler(e, context); + //return false; + } catch (error) { + HandleError.handleError(error.toString(), context); + //return false; + } + } - // } + Future getUnreadMessages(context, page, limit) async { + try { + final prefs = await SharedPreferences.getInstance(); + DioClient.init(prefs); + unreadMessage = []; + await DioClient.get( + path: '/messages/unread', + query: {'page': page, 'limit': limit}).then((value) { + print(value.data['data']); + value.data['data'].forEach((element) { + final message = ShowMessagesModel.fromJson(element); + unreadMessage.add(message); + }); + notifyListeners(); + }); + } on DioError catch (e) { + HandleError.errorHandler(e, context); + //return false; + } catch (error) { + HandleError.handleError(error.toString(), context); + //return false; + } + } - // messageShow.addEntries(newEntries) - // print(messageShow[message.subjectId].runtimeType); - // messageShow. - // messageShow.addEntries('${message.subjectId}':message); - // messageShow.update(message.subjectId!, - // (value) => (value.add(message)) as List); - // messageShow.putIfAbsent( - // message.subjectId!, - // () => messageShow[message.subjectId!]!.add(message) - // as List); - // messageShow[message.subjectId!]!.add(message); + Future getSentMessages(context, page, limit) async { + try { + final prefs = await SharedPreferences.getInstance(); + DioClient.init(prefs); + sentMessage = []; + await DioClient.get( + path: '/messages/sent', + query: {'page': page, 'limit': limit}).then((value) { + print(value.data['data']); + value.data['data'].forEach((element) { + final message = ShowMessagesModel.fromJson(element); + print('**********************IDS*****************************'); + print(message.sId); + sentMessage.add(message); + }); + notifyListeners(); + }); + } on DioError catch (e) { + HandleError.errorHandler(e, context); + //return false; + } catch (error) { + HandleError.handleError(error.toString(), context); + //return false; + } + } + + Future getMessages(context, page, limit) async { + try { + final prefs = await SharedPreferences.getInstance(); + DioClient.init(prefs); + allMessage = []; + await DioClient.get( + path: '/messages/all', + query: {'page': page, 'limit': limit}).then((value) { + print(value.data['data']); + value.data['data'].forEach((element) { + final message = ShowMessagesModel.fromJson(element); - // print(messageShow[message.subjectId]); + allMessage.add(message); }); notifyListeners(); }); @@ -133,4 +182,34 @@ class MessageProvider with ChangeNotifier { //return false; } } + + Future deleteMessage(context, messageId) async { + try { + final prefs = await SharedPreferences.getInstance(); + DioClient.init(prefs); + print(messageId); + + DioClient.delete(path: '/messages/$messageId'); + } on DioError catch (e) { + HandleError.errorHandler(e, context); + //return false; + } catch (error) { + HandleError.handleError(error.toString(), context); + //return false; + } + } + + Future markAllAsRead(context) async { + try { + final prefs = await SharedPreferences.getInstance(); + DioClient.init(prefs); + DioClient.patch(path: '/messages/mark_as_read'); + } on DioError catch (e) { + HandleError.errorHandler(e, context); + //return false; + } catch (error) { + HandleError.handleError(error.toString(), context); + //return false; + } + } } diff --git a/lib/messages/screens/message_main_screen.dart b/lib/messages/screens/message_main_screen.dart index 28fcc787..65b19f38 100644 --- a/lib/messages/screens/message_main_screen.dart +++ b/lib/messages/screens/message_main_screen.dart @@ -4,7 +4,7 @@ import 'package:post/widgets/loading_reddit.dart'; import 'package:provider/provider.dart'; import 'package:responsive_sizer/responsive_sizer.dart'; -import '../Provider/message_provider.dart'; +import '../provider/message_provider.dart'; import '../models/user_message.dart'; class MessageMainScreen extends StatefulWidget { diff --git a/lib/messages/screens/new_message_screen.dart b/lib/messages/screens/new_message_screen.dart index 104a0af9..4a338273 100644 --- a/lib/messages/screens/new_message_screen.dart +++ b/lib/messages/screens/new_message_screen.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import '../../moderation_settings/widgets/alert_dialog.dart'; -import '../Provider/message_provider.dart'; +import '../provider/message_provider.dart'; class NewMessageScreen extends StatefulWidget { NewMessageScreen({super.key, this.userName, this.userNameAvailable}); diff --git a/lib/messages/screens/show_message_body.dart b/lib/messages/screens/show_message_body.dart index 5c7391ee..18b4a7e3 100644 --- a/lib/messages/screens/show_message_body.dart +++ b/lib/messages/screens/show_message_body.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:post/messages/Provider/message_provider.dart'; +import '../../messages/provider/message_provider.dart'; import 'package:provider/provider.dart'; import '../models/user_message.dart'; diff --git a/lib/messages/screens/unread_message_screen.dart b/lib/messages/screens/unread_message_screen.dart new file mode 100644 index 00000000..09eb5b74 --- /dev/null +++ b/lib/messages/screens/unread_message_screen.dart @@ -0,0 +1,204 @@ +import 'package:flutter/material.dart'; +import 'package:responsive_sizer/responsive_sizer.dart'; + +import 'web_message_all_screen.dart'; +import 'web_message_screen.dart'; +import 'web_new_message_screen.dart'; +import 'web_sent_message.dart'; + +class UnreadMessageScreen extends StatefulWidget { + const UnreadMessageScreen({super.key}); + static const routeName = '/unread-message-screen'; + + @override + State createState() => _UnreadMessageScreenState(); +} + +class _UnreadMessageScreenState extends State { + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(title: Text('Hiiiii')), + body: SingleChildScrollView( + scrollDirection: Axis.vertical, + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + color: Colors.blue.shade800, + child: Row( + children: [ + Padding( + padding: EdgeInsets.only( + top: 13, bottom: 13, left: 100, right: 20), + child: TextButton( + onPressed: () { + Navigator.of(context) + .pushNamed(WebNewMessageScreen.routeName); + }, + child: Text( + 'Send a Private Message', + style: TextStyle( + color: Colors.grey.shade400, + fontSize: 15, + fontWeight: FontWeight.bold), + )), + ), + Padding( + padding: EdgeInsets.only( + top: 13, bottom: 13, left: 8, right: 20), + child: TextButton( + onPressed: () { + Navigator.of(context) + .pushNamed(WebMessageScreen.routeName); + }, + child: Text( + 'Inbox', + style: TextStyle( + color: Colors.grey.shade400, + fontSize: 15, + fontWeight: FontWeight.bold), + )), + ), + Padding( + padding: EdgeInsets.only( + top: 13, bottom: 13, left: 8, right: 20), + child: TextButton( + onPressed: () { + Navigator.of(context) + .pushNamed(SentMessage.routeName); + }, + child: Text( + 'Sent', + style: TextStyle( + color: Colors.grey.shade400, + fontSize: 15, + fontWeight: FontWeight.bold), + )), + ), + ], + ), + ), + Container( + color: Colors.blue.shade800, + child: Row( + children: [ + Padding( + padding: EdgeInsets.only( + top: 10, bottom: 8, left: 100, right: 20), + child: TextButton( + onPressed: () { + Navigator.of(context) + .pushNamed(AllMessageScreen.routeName); + }, + child: Text('All')), + ), + Padding( + padding: + EdgeInsets.only(top: 10, bottom: 8, left: 8, right: 20), + child: TextButton( + onPressed: () { + Navigator.of(context) + .pushNamed(SentMessage.routeName); + }, + child: Text('Unread')), + ), + Padding( + padding: + EdgeInsets.only(top: 10, bottom: 8, left: 8, right: 20), + child: TextButton( + onPressed: () { + Navigator.of(context) + .pushNamed(WebMessageScreen.routeName); + }, + child: Text('Messages')), + ), + ], + ), + ), + Container( + color: Colors.indigo[50], + margin: EdgeInsets.only(left: 10.w, right: 10.w, top: 10), + child: Container( + color: Colors.indigo[50], + child: Column( + children: [ + ListView.builder( + physics: const ClampingScrollPhysics(), + shrinkWrap: true, + itemBuilder: (context, index) { + return Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ////subjectccccccccccccccccc + Padding( + padding: const EdgeInsets.only(left: 10,top: 5), + child: Text('Jooko:'), + ), + Row( + children: [ + Padding( + padding: const EdgeInsets.only(left: 18), + child: Text('from '), + ), + TextButton( + style: TextButton.styleFrom( + padding: EdgeInsets.zero, + ), + onPressed: () {}, + child: Text( + 'Eman Shahda', + style: TextStyle(color: Colors.blue), + )), + Text(' sent 3 days ago'), + ], + ), + Padding( + padding: const EdgeInsets.only( + left: 23, right: 8, bottom: 8, top: 8), + child: Text( + 'hiiiiiiiii bgfsvbhszdfkz hadsafjdnkd bhjknkamkfnk arhfbagjarehk dhfjskfa ajfbfrak dfjsk bfdjksml,;htgfjkdl;sp gfdjksl; mrtgwirjawelf gsduhskerjgesl sdgjhrkgl'), + ), + Padding( + padding:const EdgeInsets.only( + left: 16, right: 8, bottom: 8, top: 8), + child: Row( + children: [ + TextButton( + onPressed: () {}, + child: Text('Reply', + style: TextStyle(color: Colors.grey))), + TextButton( + onPressed: () {}, + child: Text('Delete', + style: TextStyle(color: Colors.grey))), + TextButton( + onPressed: () {}, + child: Text('BlockUser', + style: TextStyle(color: Colors.grey))), + ], + ), + ), + Divider( + color: Colors.transparent, + ) + ], + ); + }, + itemCount: 5, + ), + Divider( + color: Colors.grey, + ), + ], + ), + ), + ), + ], + ), + ), + ); + } +} \ No newline at end of file diff --git a/lib/messages/screens/web_message_all_screen.dart b/lib/messages/screens/web_message_all_screen.dart new file mode 100644 index 00000000..b55984b6 --- /dev/null +++ b/lib/messages/screens/web_message_all_screen.dart @@ -0,0 +1,287 @@ +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:responsive_sizer/responsive_sizer.dart'; + +import '../../widgets/loading_reddit.dart'; +import '../models/user_message.dart'; +import '../provider/message_provider.dart'; +import 'web_message_all_screen.dart'; +import 'web_message_screen.dart'; +import 'web_new_message_screen.dart'; +import 'web_sent_message.dart'; + +class AllMessageScreen extends StatefulWidget { + const AllMessageScreen({super.key}); + static const routeName = '/all-message-screen'; + + @override + State createState() => _AllMessageScreenState(); +} + +class _AllMessageScreenState extends State { + String getTimeOfNotification(date) { + String howOld; + final difference = DateTime.now().difference(DateTime.parse(date)); + if (difference.inDays >= 365) { + howOld = '${difference.inDays ~/ 365}y'; + } else if (difference.inDays >= 30) { + howOld = '${difference.inDays ~/ 30}mo'; + } else if (difference.inDays >= 1) { + howOld = '${difference.inDays}d'; + } else if (difference.inMinutes >= 60) { + howOld = '${difference.inHours}h'; + } else if (difference.inSeconds >= 60) { + howOld = '${difference.inMinutes}m'; + } else { + howOld = '${difference.inSeconds}s'; + } + return howOld; + } + + bool _isInit = true; + bool returned = false; + bool tried = false; + + List allMessage = []; + void didChangeDependencies() async { + if (_isInit) { + setState(() { + returned = false; + }); + // _updateCount(); + await Provider.of(context, listen: false) + .getMessages(context, 0, 10) + .then((value) { + allMessage = + Provider.of(context, listen: false).allMessage; + setState(() { + returned = true; + }); + }); + } + _isInit = false; + + super.didChangeDependencies(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(title: Text('Hiiiii')), + body: (!returned) + ? const LoadingReddit() + : (allMessage.isEmpty) + ? Container( + color: Colors.grey[100], + child: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Image.asset('assets/images/emptyNotification.png'), + Text( + 'Wow,such empty', + style: TextStyle( + color: Colors.grey.shade500, + fontSize: 13, + fontWeight: FontWeight.w300), + ), + ], + )), + ) + : SingleChildScrollView( + scrollDirection: Axis.vertical, + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + color: Colors.blue.shade800, + child: Row( + children: [ + Padding( + padding: EdgeInsets.only( + top: 13, bottom: 13, left: 100, right: 20), + child: TextButton( + onPressed: () { + Navigator.of(context).pushNamed( + WebNewMessageScreen.routeName); + }, + child: Text( + 'Send a Private Message', + style: TextStyle( + color: Colors.grey.shade400, + fontSize: 15, + fontWeight: FontWeight.bold), + )), + ), + Padding( + padding: EdgeInsets.only( + top: 13, bottom: 13, left: 8, right: 20), + child: TextButton( + onPressed: () { + Navigator.of(context) + .pushNamed(WebMessageScreen.routeName); + }, + child: Text( + 'Inbox', + style: TextStyle( + color: Colors.grey.shade400, + fontSize: 15, + fontWeight: FontWeight.bold), + )), + ), + Padding( + padding: EdgeInsets.only( + top: 13, bottom: 13, left: 8, right: 20), + child: TextButton( + onPressed: () { + Navigator.of(context) + .pushNamed(SentMessage.routeName); + }, + child: Text( + 'Sent', + style: TextStyle( + color: Colors.grey.shade400, + fontSize: 15, + fontWeight: FontWeight.bold), + )), + ), + ], + ), + ), + Container( + color: Colors.blue.shade800, + child: Row( + children: [ + Padding( + padding: EdgeInsets.only( + top: 10, bottom: 8, left: 100, right: 20), + child: TextButton( + onPressed: () { + Navigator.of(context) + .pushNamed(AllMessageScreen.routeName); + }, + child: Text('All')), + ), + Padding( + padding: EdgeInsets.only( + top: 10, bottom: 8, left: 8, right: 20), + child: TextButton( + onPressed: () { + Navigator.of(context) + .pushNamed(SentMessage.routeName); + }, + child: Text('Unread')), + ), + Padding( + padding: EdgeInsets.only( + top: 10, bottom: 8, left: 8, right: 20), + child: TextButton( + onPressed: () { + Navigator.of(context) + .pushNamed(WebMessageScreen.routeName); + }, + child: Text('Messages')), + ), + ], + ), + ), + Container( + color: Colors.indigo[50], + margin: + EdgeInsets.only(left: 10.w, right: 10.w, top: 10), + child: Container( + color: Colors.indigo[50], + child: Column( + children: [ + ListView.builder( + physics: const ClampingScrollPhysics(), + shrinkWrap: true, + itemBuilder: (context, index) { + return Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + ////subjectccccccccccccccccc + Padding( + padding: const EdgeInsets.only( + left: 10, top: 5), + child: Text( + allMessage[index].subjectText!), + ), + Row( + children: [ + Padding( + padding: + const EdgeInsets.only(left: 18), + child: Text('from '), + ), + TextButton( + style: TextButton.styleFrom( + padding: EdgeInsets.zero, + ), + onPressed: () {}, + child: Text( + allMessage[index].toUsername!, + style: TextStyle( + color: Colors.blue), + )), + Text(' sent 3 days ago'), + ], + ), + Padding( + padding: const EdgeInsets.only( + left: 23, + right: 8, + bottom: 8, + top: 8), + child: Text(allMessage[index].text!), + ), + Padding( + padding: const EdgeInsets.only( + left: 16, + right: 8, + bottom: 8, + top: 8), + child: Row( + children: [ + TextButton( + onPressed: () {}, + child: Text('Reply', + style: TextStyle( + color: Colors.grey))), + TextButton( + onPressed: () {}, + child: Text('Delete', + style: TextStyle( + color: Colors.grey))), + TextButton( + onPressed: () {}, + child: Text('BlockUser', + style: TextStyle( + color: Colors.grey))), + ], + ), + ), + Divider( + color: Colors.transparent, + ) + ], + ); + }, + itemCount: allMessage.length, + ), + Divider( + color: Colors.grey, + ), + ], + ), + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/messages/screens/web_message_screen.dart b/lib/messages/screens/web_message_screen.dart new file mode 100644 index 00000000..fc47082d --- /dev/null +++ b/lib/messages/screens/web_message_screen.dart @@ -0,0 +1,322 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/src/widgets/container.dart'; +import 'package:flutter/src/widgets/framework.dart'; +import '../../messages/provider/message_provider.dart'; +import '../../messages/screens/web_message_all_screen.dart'; +import '../../messages/screens/web_sent_message.dart'; +import 'package:provider/provider.dart'; +import 'package:responsive_sizer/responsive_sizer.dart'; + +import '../../widgets/loading_reddit.dart'; +import '../models/user_message.dart'; +import 'web_new_message_screen.dart'; + +class WebMessageScreen extends StatefulWidget { + const WebMessageScreen({super.key}); + static const routeName = 'web-message-screen'; + + @override + State createState() => _WebMessageScreenState(); +} + +class _WebMessageScreenState extends State + with TickerProviderStateMixin { + String getTimeOfNotification(date) { + String howOld; + final difference = DateTime.now().difference(DateTime.parse(date)); + if (difference.inDays >= 365) { + howOld = '${difference.inDays ~/ 365}y'; + } else if (difference.inDays >= 30) { + howOld = '${difference.inDays ~/ 30}mo'; + } else if (difference.inDays >= 1) { + howOld = '${difference.inDays}d'; + } else if (difference.inMinutes >= 60) { + howOld = '${difference.inHours}h'; + } else if (difference.inSeconds >= 60) { + howOld = '${difference.inMinutes}m'; + } else { + howOld = '${difference.inSeconds}s'; + } + return howOld; + } + + bool _isInit = true; + bool returned = false; + bool tried = false; + + Map> messageShow = {}; + void didChangeDependencies() async { + if (_isInit) { + setState(() { + returned = false; + }); + // _updateCount(); + await Provider.of(context, listen: false) + .getAllMessages(context, 0, 10) + .then((value) { + messageShow = + Provider.of(context, listen: false).messageShow; + setState(() { + returned = true; + }); + }); + } + _isInit = false; + + super.didChangeDependencies(); + } + + @override + Widget build(BuildContext context) { + TabController _tabController = TabController(length: 3, vsync: this); + return Scaffold( + appBar: AppBar(title: Text('Hiiiii')), + body: (!returned) + ? const LoadingReddit() + : (messageShow.isEmpty) + ? Container( + color: Colors.grey[100], + child: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Image.asset('assets/images/emptyNotification.png'), + Text( + 'Wow,such empty', + style: TextStyle( + color: Colors.grey.shade500, + fontSize: 13, + fontWeight: FontWeight.w300), + ), + ], + )), + ) + : + SingleChildScrollView( + scrollDirection: Axis.vertical, + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + color: Colors.blue.shade800, + child: Row( + children: [ + Padding( + padding: EdgeInsets.only( + top: 13, bottom: 13, left: 100, right: 20), + child: TextButton( + onPressed: () { + Navigator.of(context) + .pushNamed(WebNewMessageScreen.routeName); + }, + child: Text( + 'Send a Private Message', + style: TextStyle( + color: Colors.grey.shade400, + fontSize: 15, + fontWeight: FontWeight.bold), + )), + ), + Padding( + padding: EdgeInsets.only( + top: 13, bottom: 13, left: 8, right: 20), + child: TextButton( + onPressed: () { + Navigator.of(context) + .pushNamed(WebMessageScreen.routeName); + }, + child: Text( + 'Inbox', + style: TextStyle( + color: Colors.grey.shade400, + fontSize: 15, + fontWeight: FontWeight.bold), + )), + ), + Padding( + padding: EdgeInsets.only( + top: 13, bottom: 13, left: 8, right: 20), + child: TextButton( + onPressed: () { + Navigator.of(context) + .pushNamed(SentMessage.routeName); + }, + child: Text( + 'Sent', + style: TextStyle( + color: Colors.grey.shade400, + fontSize: 15, + fontWeight: FontWeight.bold), + )), + ), + ], + ), + ), + Container( + color: Colors.blue.shade800, + child: Row( + children: [ + Padding( + padding: EdgeInsets.only( + top: 10, bottom: 8, left: 100, right: 20), + child: TextButton( + onPressed: () { + Navigator.of(context) + .pushNamed(AllMessageScreen.routeName); + }, + child: Text('All')), + ), + Padding( + padding: + EdgeInsets.only(top: 10, bottom: 8, left: 8, right: 20), + child: TextButton( + onPressed: () { + Navigator.of(context) + .pushNamed(SentMessage.routeName); + }, + child: Text('Unread')), + ), + Padding( + padding: + EdgeInsets.only(top: 10, bottom: 8, left: 8, right: 20), + child: TextButton( + onPressed: () { + Navigator.of(context) + .pushNamed(WebMessageScreen.routeName); + }, + child: Text('Messages')), + ), + ], + ), + ), + Container( + color: Colors.indigo[50], + margin: EdgeInsets.only(left: 10.w, right: 10.w, top: 10), + child: Container( + color: Colors.indigo[50], + child: Column( + children: [ + ListView.builder( + physics: const ClampingScrollPhysics(), + shrinkWrap: true, + itemBuilder: (context, index) { + String key = messageShow.keys.elementAt(index); + final message = messageShow[key]!.last; + return Container( + padding: EdgeInsets.only(top: 8), + child: Column( + children: [ + Row( + children: [ + //ALways will be toUsername + Padding( + padding: const EdgeInsets.only( + left: 8, right: 8), + child: OutlinedButton( + style: OutlinedButton.styleFrom( + padding: EdgeInsets.only( + left: 7, right: 7), + side: + BorderSide(color: Colors.black26), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.all( + Radius.circular(10), + )), + ), + //style: , + onPressed: () {}, + child: Text( + message.toUsername!, + style: TextStyle( + color: Colors.blue.shade300), + )), + ), + //subject of message + Text(message.subjectText!), + ], + ), + Padding( + padding: const EdgeInsets.only(left: 18), + child: ListView.builder( + physics: const ClampingScrollPhysics(), + shrinkWrap: true, + itemBuilder: (context, index) { + return Column( + mainAxisAlignment: + MainAxisAlignment.start, + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Row( + children: [ + Padding( + padding: const EdgeInsets.only( + left: 8), + child: Text('from '), + ), + TextButton( + style: TextButton.styleFrom( + padding: EdgeInsets.zero, + ), + onPressed: () {}, + child: Text( + messageShow[key]![index].fromUsername!, + style: TextStyle( + color: Colors.blue), + )), + Text(' sent ${getTimeOfNotification(messageShow[key]![index].createdAt!)}'), + ], + ), + Padding( + padding: const EdgeInsets.all(8.0), + child: Text( + messageShow[key]![index].subjectText!), + ), + Row( + children: [ + TextButton( + onPressed: () {}, + child: Text('Reply', + style: TextStyle( + color: Colors.grey))), + TextButton( + onPressed: () {}, + child: Text('Delete', + style: TextStyle( + color: Colors.grey))), + TextButton( + onPressed: () {}, + child: Text('BlockUser', + style: TextStyle( + color: Colors.grey))), + ], + ), + Divider( + color: Colors.transparent, + ) + ], + ); + }, + itemCount: messageShow[key]!.length, + ), + ), + Divider( + color: Colors.grey, + ), + ], + ), + ); + }, + itemCount: messageShow.length, + ), + ], + ), + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/messages/screens/web_new_message_screen.dart b/lib/messages/screens/web_new_message_screen.dart new file mode 100644 index 00000000..0b284458 --- /dev/null +++ b/lib/messages/screens/web_new_message_screen.dart @@ -0,0 +1,241 @@ + +import 'package:flutter/material.dart'; + +import 'web_message_screen.dart'; +import 'web_sent_message.dart'; + +class WebNewMessageScreen extends StatefulWidget { + WebNewMessageScreen({super.key, this.userName}); + static const routeName = '/web-new-message-screen'; + String? userName; + @override + State createState() => _WebNewMessageScreenState(); +} + +class _WebNewMessageScreenState extends State { + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(title: Text('Hiiiii')), + body: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + color: Colors.blue.shade800, + child: Row( + children: [ + Padding( + padding: + EdgeInsets.only(top: 13, bottom: 13, left: 10, right: 20), + child: TextButton( + onPressed: () { + Navigator.of(context) + .pushNamed(WebNewMessageScreen.routeName); + }, + child: Text( + 'Send a Private Message', + style: TextStyle( + color: Colors.grey.shade400, + fontSize: 15, + fontWeight: FontWeight.bold), + )), + ), + Padding( + padding: + EdgeInsets.only(top: 13, bottom: 13, left: 8, right: 20), + child: TextButton( + onPressed: () { + Navigator.of(context) + .pushNamed(WebMessageScreen.routeName); + }, + child: Text( + 'Inbox', + style: TextStyle( + color: Colors.grey.shade400, + fontSize: 15, + fontWeight: FontWeight.bold), + )), + ), + Padding( + padding: + EdgeInsets.only(top: 13, bottom: 13, left: 8, right: 20), + child: TextButton( + onPressed: () { + Navigator.of(context).pushNamed(SentMessage.routeName); + }, + child: Text( + 'Sent', + style: TextStyle( + color: Colors.grey.shade400, + fontSize: 15, + fontWeight: FontWeight.bold), + )), + ), + ], + ), + ), + Container( + margin: EdgeInsets.all(25), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.all(8.0), + child: Text( + 'Send a Private Message', + style: TextStyle(fontSize: 25), + ), + ), + Padding( + padding: const EdgeInsets.all(8.0), + child: Text('from'), + ), + SizedBox( + width: MediaQuery.of(context).size.width * 0.4, + child: TextFormField( + enabled: false, + initialValue: widget.userName, + textAlignVertical: TextAlignVertical.center, + cursorColor: Colors.black, + onChanged: (value) { + // print(value); + // username = value; + // _changeStateOfUserName(); + }, + decoration: const InputDecoration( + labelStyle: TextStyle(color: Colors.black54), + isDense: true, + contentPadding: EdgeInsets.only( + bottom: 3, left: 5, top: 25, right: 5), + floatingLabelBehavior: FloatingLabelBehavior.never, + border: OutlineInputBorder( + borderSide: BorderSide(color: Colors.black)), + focusedBorder: OutlineInputBorder( + borderSide: BorderSide(color: Colors.black)), + enabledBorder: OutlineInputBorder( + borderSide: BorderSide(color: Colors.black)), + ), + ), + ), + Padding( + padding: const EdgeInsets.all(8.0), + child: Text('to'), + ), + SizedBox( + width: MediaQuery.of(context).size.width * 0.4, + child: TextFormField( + // / enabled: (widget.userName != null) ? false : true, + // initialValue: + // (widget.userName != null) ? widget.userName : null, + textAlignVertical: TextAlignVertical.center, + cursorColor: Colors.black, + onChanged: (value) { + // print(value); + // username = value; + // _changeStateOfUserName(); + }, + decoration: const InputDecoration( + labelStyle: TextStyle(color: Colors.black54), + isDense: true, + contentPadding: EdgeInsets.only( + bottom: 3, left: 5, top: 25, right: 5), + floatingLabelBehavior: FloatingLabelBehavior.never, + border: OutlineInputBorder( + borderSide: BorderSide(color: Colors.black)), + focusedBorder: OutlineInputBorder( + borderSide: BorderSide(color: Colors.black)), + enabledBorder: OutlineInputBorder( + borderSide: BorderSide(color: Colors.black)), + ), + ), + ), + Padding( + padding: const EdgeInsets.all(8.0), + child: Text('Subject'), + ), + SizedBox( + width: MediaQuery.of(context).size.width * 0.4, + child: TextFormField( + // / enabled: (widget.userName != null) ? false : true, + // initialValue: + // (widget.userName != null) ? widget.userName : null, + textAlignVertical: TextAlignVertical.center, + cursorColor: Colors.black, + onChanged: (value) { + // print(value); + // username = value; + // _changeStateOfUserName(); + }, + decoration: const InputDecoration( + labelStyle: TextStyle(color: Colors.black54), + isDense: true, + contentPadding: EdgeInsets.only( + bottom: 3, left: 5, top: 25, right: 5), + floatingLabelBehavior: FloatingLabelBehavior.never, + border: OutlineInputBorder( + borderSide: BorderSide(color: Colors.black)), + focusedBorder: OutlineInputBorder( + borderSide: BorderSide(color: Colors.black)), + enabledBorder: OutlineInputBorder( + borderSide: BorderSide(color: Colors.black)), + ), + ), + ), + Padding( + padding: const EdgeInsets.all(8.0), + child: Text('Message'), + ), + SizedBox( + width: MediaQuery.of(context).size.width * 0.4, + height: 100, + child: SingleChildScrollView( + scrollDirection: Axis.vertical, + child: TextFormField( + textInputAction: TextInputAction.newline, + // keyboardType: TextInputType.multiline, + maxLines: null, + // / enabled: (widget.userName != null) ? false : true, + // initialValue: + // (widget.userName != null) ? widget.userName : null, + textAlignVertical: TextAlignVertical.center, + cursorColor: Colors.black, + onChanged: (value) { + // print(value); + // username = value; + // _changeStateOfUserName(); + }, + decoration: const InputDecoration( + labelStyle: TextStyle(color: Colors.black54), + isDense: true, + contentPadding: EdgeInsets.only( + bottom: 3, left: 5, top: 25, right: 5), + floatingLabelBehavior: FloatingLabelBehavior.never, + border: OutlineInputBorder( + borderSide: BorderSide(color: Colors.black)), + focusedBorder: OutlineInputBorder( + borderSide: BorderSide(color: Colors.black)), + enabledBorder: OutlineInputBorder( + borderSide: BorderSide(color: Colors.black)), + ), + ), + ), + ), + ElevatedButton( + onPressed: () {}, + child: Text( + 'Send', + style: TextStyle( + color: Colors.white, fontWeight: FontWeight.bold), + ), + style: ElevatedButton.styleFrom(primary: Colors.blue), + ) + ], + ), + ) + ], + ), + ); + } +} diff --git a/lib/messages/screens/web_sent_message.dart b/lib/messages/screens/web_sent_message.dart new file mode 100644 index 00000000..f69093d7 --- /dev/null +++ b/lib/messages/screens/web_sent_message.dart @@ -0,0 +1,168 @@ +import 'package:flutter/material.dart'; +import 'package:responsive_sizer/responsive_sizer.dart'; + +import 'web_message_screen.dart'; +import 'web_new_message_screen.dart'; + +class SentMessage extends StatefulWidget { + const SentMessage({super.key}); + static const routeName = '/sent-message'; + + @override + State createState() => _SentMessageState(); +} + +class _SentMessageState extends State { + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(title: Text('Hiiiii')), + body: SingleChildScrollView( + scrollDirection: Axis.vertical, + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + color: Colors.blue.shade800, + child: Row( + children: [ + Padding( + padding: EdgeInsets.only( + top: 13, bottom: 13, left: 100, right: 20), + child: TextButton( + onPressed: () { + Navigator.of(context) + .pushNamed(WebNewMessageScreen.routeName); + }, + child: Text( + 'Send a Private Message', + style: TextStyle( + color: Colors.grey.shade400, + fontSize: 15, + fontWeight: FontWeight.bold), + )), + ), + Padding( + padding: EdgeInsets.only( + top: 13, bottom: 13, left: 8, right: 20), + child: TextButton( + onPressed: () { + Navigator.of(context) + .pushNamed(WebMessageScreen.routeName); + }, + child: Text( + 'Inbox', + style: TextStyle( + color: Colors.grey.shade400, + fontSize: 15, + fontWeight: FontWeight.bold), + )), + ), + Padding( + padding: EdgeInsets.only( + top: 13, bottom: 13, left: 8, right: 20), + child: TextButton( + onPressed: () { + Navigator.of(context) + .pushNamed(SentMessage.routeName); + }, + child: Text( + 'Sent', + style: TextStyle( + color: Colors.grey.shade400, + fontSize: 15, + fontWeight: FontWeight.bold), + )), + ), + ], + ), + ), + Container( + color: Colors.indigo[50], + margin: EdgeInsets.only(left: 10.w, right: 10.w, top: 10), + child: Container( + color: Colors.indigo[50], + child: Column( + children: [ + ListView.builder( + physics: const ClampingScrollPhysics(), + shrinkWrap: true, + itemBuilder: (context, index) { + return Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ////subjectccccccccccccccccc + Padding( + padding: const EdgeInsets.only(left: 10, top: 5), + child: Text('Jooko:'), + ), + Row( + children: [ + Padding( + padding: const EdgeInsets.only(left: 18), + child: Text('from '), + ), + TextButton( + style: TextButton.styleFrom( + padding: EdgeInsets.zero, + ), + onPressed: () {}, + child: Text( + 'Eman Shahda', + style: TextStyle(color: Colors.blue), + )), + Text(' sent 3 days ago'), + ], + ), + Padding( + padding: const EdgeInsets.only( + left: 23, right: 8, bottom: 8, top: 8), + child: Text( + 'hiiiiiiiii bgfsvbhszdfkz hadsafjdnkd bhjknkamkfnk arhfbagjarehk dhfjskfa ajfbfrak dfjsk bfdjksml,;htgfjkdl;sp gfdjksl; mrtgwirjawelf gsduhskerjgesl sdgjhrkgl'), + ), + Padding( + padding: const EdgeInsets.only( + left: 16, right: 8, bottom: 8, top: 8), + child: Row( + children: [ + TextButton( + onPressed: () {}, + child: Text('Reply', + style: + TextStyle(color: Colors.grey))), + TextButton( + onPressed: () {}, + child: Text('Delete', + style: + TextStyle(color: Colors.grey))), + TextButton( + onPressed: () {}, + child: Text('BlockUser', + style: + TextStyle(color: Colors.grey))), + ], + ), + ), + Divider( + color: Colors.transparent, + ) + ], + ); + }, + itemCount: 5, + ), + Divider( + color: Colors.grey, + ), + ], + ), + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/moderated_subreddit/models/moderated_subreddit_data.dart b/lib/moderated_subreddit/models/moderated_subreddit_data.dart deleted file mode 100644 index 481c008b..00000000 --- a/lib/moderated_subreddit/models/moderated_subreddit_data.dart +++ /dev/null @@ -1,60 +0,0 @@ -// //import 'package:flutter_code_style/analysis_options.yaml'; -// import '../../models/subreddit_about _rules.dart'; - -// class ModeratedSubredditData { -// //String? id; -// String? displayName; -// String? name; -// String? subredditPicture; -// String? subredditBackPicture; -// String? description; -// String? subredditLink; -// int? numOfMembers; -// int? numOfOnlines; -// List? rules; -// List? moderators; -// bool? isJoined; -// String? theme; -// ModeratedSubredditData( -// { -// //required this.id, -// required this.name, -// required this.displayName, -// required this.subredditPicture, -// required this.subredditBackPicture, -// required this.description, -// required this.subredditLink, -// required this.numOfMembers, -// required this.numOfOnlines, -// required this.isJoined, -// required this.rules, -// required this.moderators, -// required this.theme}); - -// ModeratedSubredditData.fromJson(Map json) { -// name = json['fixedName']; -// displayName = json['name']; -// subredditPicture = json['icon']; -// subredditBackPicture = json['backgroundImage']; -// description = json['description']; -// subredditLink = (json['subredditLink'] != null) -// ? json['subredditLink'] -// : 'https://www.reddit.com/r/${name.toString()}?utm_medium=android_app&utm_source=share'; -// numOfMembers = int.parse(json['membersCount'].toString()); -// numOfOnlines = 0; //int.parse(json['numOfOnlines'].toString()); -// isJoined = (json['isJoined']); -// final List loadedrule = []; -// json['rules'].forEach((rule) { -// loadedrule.add(SubredditAboutRules(rule['title'], -// (rule['description'] != null) ? rule['description'] : '')); -// }); -// rules = loadedrule; -// final List loadedmodrator = []; -// json['moderators'].forEach((moderator) { -// print( json['moderators']); -// loadedmodrator.add(moderator["userName"]); -// }); -// moderators = loadedmodrator; -// theme = json['theme']; -// } -// } diff --git a/lib/moderated_subreddit/providers/moderated_subreddit_provider.dart b/lib/moderated_subreddit/providers/moderated_subreddit_provider.dart index c94fbe78..388a2d1b 100644 --- a/lib/moderated_subreddit/providers/moderated_subreddit_provider.dart +++ b/lib/moderated_subreddit/providers/moderated_subreddit_provider.dart @@ -13,7 +13,13 @@ class ModeratedSubredditProvider with ChangeNotifier { SubredditData? get gettingSubredditeData { return loadSubreddit; } - + bool showTheme = false; + bool? get gettingTheme { + return showTheme; + } + // ===================================this function used to===========================================// +//==================fetch and set date===========================// +//moderatedSubredditUserName==> userName Of Subreddit Future fetchAndSetModeratedSubredddit( String moderatedSubredditUserName, BuildContext context) async { try { @@ -31,7 +37,10 @@ class ModeratedSubredditProvider with ChangeNotifier { HandleError.handleError(error.toString(), context); } } - + // ===================================this function used to===========================================// +//==================Join and Disjoin subbreddit ===========================// +//moderatedSubredditUserName==> userName Of Subreddit +//action==> action either sub or unsub Future joinAndDisjoinModeratedSubreddit( String moderatedSubredditUserName, String action, @@ -53,4 +62,8 @@ class ModeratedSubredditProvider with ChangeNotifier { return false; } } + Future togglingTheme() async { + showTheme = !showTheme; + notifyListeners(); + } } diff --git a/lib/moderated_subreddit/screens/moderated_subreddit_screen.dart b/lib/moderated_subreddit/screens/moderated_subreddit_screen.dart index 9fc1b2c0..4853b79c 100644 --- a/lib/moderated_subreddit/screens/moderated_subreddit_screen.dart +++ b/lib/moderated_subreddit/screens/moderated_subreddit_screen.dart @@ -58,11 +58,10 @@ SubredditData? loadedSubreddit; _controller!.dispose(); super.dispose(); } - + // ===================================this function used to===========================================// +//==================fetch date for first time===========================// @override void didChangeDependencies() { - // // TODO: implement didChangeDependencies - //===============================Fetch subreddit data =======================================// if (_isInit) { setState(() { _isLoading = true; @@ -81,7 +80,6 @@ SubredditData? loadedSubreddit; }); } _isInit = false; - //==================================================// super.didChangeDependencies(); } diff --git a/lib/moderated_subreddit/widgets/mod_subreddit_post_sort_bottom.dart b/lib/moderated_subreddit/widgets/mod_subreddit_post_sort_bottom.dart index 7dffd43a..1c16e41b 100644 --- a/lib/moderated_subreddit/widgets/mod_subreddit_post_sort_bottom.dart +++ b/lib/moderated_subreddit/widgets/mod_subreddit_post_sort_bottom.dart @@ -43,13 +43,9 @@ class ModSubredditPostSortBottomState @override Widget build(BuildContext context) { return Container( - // height: MediaQuery.of(context).size.height * 0.23, - // width: MediaQuery.of(context).size.width * 1, height: 23.h, width: 10.w, color: const Color.fromARGB(255, 240, 240, 240), - //padding: const EdgeInsets.all(20), - //color: Colors.black54, child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.end, @@ -68,7 +64,7 @@ class ModSubredditPostSortBottomState children: [ Icon( _icon, - color: Color.fromARGB(255, 121, 121, 121), + color: const Color.fromARGB(255, 121, 121, 121), size: 25, ), Text( @@ -79,7 +75,7 @@ class ModSubredditPostSortBottomState fontSize: 12), textAlign: TextAlign.end, ), - Icon( + const Icon( color: Color.fromARGB(255, 121, 121, 121), Icons.keyboard_arrow_down_rounded, size: 25, @@ -87,7 +83,7 @@ class ModSubredditPostSortBottomState ]), ), IconButton( - icon: Icon( + icon: const Icon( Icons.shield_outlined, color: Colors.grey, ), @@ -98,7 +94,8 @@ class ModSubredditPostSortBottomState ])); } -//Select the type of Posts + // ===================================this function used to===========================================// +//==================sorting action of Posts===========================// Future typesBottomSheet(BuildContext context) { return showModalBottomSheet( backgroundColor: Colors.transparent, @@ -108,8 +105,6 @@ class ModSubredditPostSortBottomState onTap: () {}, child: Container( padding: const EdgeInsets.all(20), - // height: MediaQuery.of(context).size.height * 0.35, - // width: MediaQuery.of(context).size.width * 0.30, height: 35.h, width: 30.w, margin: const EdgeInsets.all(5), @@ -139,7 +134,7 @@ class ModSubredditPostSortBottomState ), trailing: Visibility( visible: tappedIndex == index, - child: Icon( + child: const Icon( Icons.done, color: Colors.blue, ), @@ -171,7 +166,6 @@ class ModSubredditPostSortBottomState }, ); } - int choosePostType(int index) { tappedIndexTop = null; tappedIndex = index; @@ -221,10 +215,7 @@ class ModSubredditPostSortBottomState : Colors.black, ), onTap: () { - setState(() { - // topValue = - // litemsTop[ - // index1]; + setState(() {; chooseTimeTopPost(index, index1); }); return Navigator.popUntil(context, diff --git a/lib/moderated_subreddit/widgets/moderated_subreddit_app.dart b/lib/moderated_subreddit/widgets/moderated_subreddit_app.dart index c0a0c07e..2e151b8f 100644 --- a/lib/moderated_subreddit/widgets/moderated_subreddit_app.dart +++ b/lib/moderated_subreddit/widgets/moderated_subreddit_app.dart @@ -40,9 +40,6 @@ class ModeratedSubredditApp extends StatelessWidget { foregroundColor: Colors.white, backgroundColor: const Color.fromARGB(137, 33, 33, 33), - // innerBoxIsScrolled - // ? const Color.fromARGB(137, 33, 33, 33) - // : Colors.white, leading: IconButton( onPressed: () { Navigator.of(context).pop(context); @@ -50,7 +47,6 @@ class ModeratedSubredditApp extends StatelessWidget { icon: const Icon(Icons.arrow_back), color: Colors.white, ), - // for saeching in subreddit title: Container( decoration: BoxDecoration( color: const Color.fromARGB(137, 33, 33, 33), @@ -58,7 +54,6 @@ class ModeratedSubredditApp extends StatelessWidget { ), width: 60.w, height: 5.h, - //color: Color.fromARGB(157, 255, 245, 245), child: InkWell( onTap: () { Navigator.of(context) diff --git a/lib/moderated_subreddit/widgets/moderated_subreddit_card_information_web.dart b/lib/moderated_subreddit/widgets/moderated_subreddit_card_information_web.dart index 13c78e89..7899a78c 100644 --- a/lib/moderated_subreddit/widgets/moderated_subreddit_card_information_web.dart +++ b/lib/moderated_subreddit/widgets/moderated_subreddit_card_information_web.dart @@ -2,8 +2,10 @@ import 'package:flutter/material.dart'; import 'package:responsive_sizer/responsive_sizer.dart'; import 'package:intl/intl.dart'; import '../../models/subreddit_data.dart'; - -class ModeratedSubredditCardInformationWeb extends StatelessWidget { +import '../../moderation_settings/screens/traffic_state.dart'; +import '../providers/moderated_subreddit_provider.dart'; +import 'package:provider/provider.dart'; +class ModeratedSubredditCardInformationWeb extends StatefulWidget { const ModeratedSubredditCardInformationWeb({ Key? key, required this.loadedSubreddit, @@ -11,38 +13,50 @@ class ModeratedSubredditCardInformationWeb extends StatelessWidget { final SubredditData? loadedSubreddit; + @override + State createState() => + _ModeratedSubredditCardInformationWebState(); +} + +class _ModeratedSubredditCardInformationWebState + extends State { + bool chooseTheme = false; @override Widget build(BuildContext context) { return Expanded( child: Container( - margin: EdgeInsets.only(right: 180, top: 100), - width: 60.w, - height: 32.h, + margin: const EdgeInsets.only(right: 180, top: 100), + width: 50.w, + height: 43.h, color: Colors.white, child: Column( children: [ Container( width: 100.h, height: 6.h, - padding: EdgeInsets.only(top: 10,left: 10), + padding: const EdgeInsets.only(top: 10, left: 10), + color: Colors.blue, child: Row( - //mainAxisAlignment: MainAxisAlignment.start, - //crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text( + const Text( 'About Community', style: TextStyle( color: Colors.white, fontWeight: FontWeight.bold), ), - SizedBox(width: 7.h,), + SizedBox( + width: 7.h, + ), ElevatedButton( - onPressed: null, + onPressed: () { + Navigator.pushNamed(context, TraficState.routeName, + arguments: widget.loadedSubreddit!.name); + }, style: ButtonStyle( backgroundColor: MaterialStateProperty.all(Colors.transparent), ), child: Row( - children: [ + children: const [ Icon( Icons.shield_outlined, color: Colors.white, @@ -55,12 +69,11 @@ class ModeratedSubredditCardInformationWeb extends StatelessWidget { )) ], ), - color: Colors.blue, ), Container( width: 100.h, - height: 26.h, - padding: EdgeInsets.only(top: 20, left: 10), + height:32.h, + padding: const EdgeInsets.only(top: 20, left: 10), decoration: BoxDecoration( borderRadius: BorderRadius.circular(5), color: Colors.white), @@ -69,27 +82,27 @@ class ModeratedSubredditCardInformationWeb extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - '${loadedSubreddit!.description}', - style: TextStyle( + '${widget.loadedSubreddit!.description}', + style: const TextStyle( color: Colors.black, fontWeight: FontWeight.normal), ), Row( children: [ - Icon( + const Icon( Icons.cake_outlined, color: Colors.black, ), Text( - 'Created Apr 30, 2013', - style: TextStyle( + '${DateFormat.yMMMMd('en_US').format(DateTime.parse(widget.loadedSubreddit!.createdAt.toString()))}', + style: const TextStyle( color: Colors.grey, fontWeight: FontWeight.normal), ), ], ), - Divider(), + const Divider(), Container( - padding: EdgeInsets.only(top: 15), + padding: const EdgeInsets.only(top: 15), child: Row( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, @@ -99,14 +112,13 @@ class ModeratedSubredditCardInformationWeb extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.start, children: [ Text( - '${new NumberFormat.compact().format(loadedSubreddit!.numOfMembers) - //loadedSubreddit!.numOfMembers + '${new NumberFormat.compact().format(widget.loadedSubreddit!.numOfMembers) }', - style: TextStyle( + style: const TextStyle( color: Colors.black, fontWeight: FontWeight.bold), ), - Text( + const Text( 'Members', style: TextStyle( color: Colors.grey, @@ -121,8 +133,8 @@ class ModeratedSubredditCardInformationWeb extends StatelessWidget { children: [ Row( children: [ - Padding( - padding: const EdgeInsetsDirectional.only( + const Padding( + padding: EdgeInsetsDirectional.only( end: 2, bottom: 2), child: CircleAvatar( backgroundColor: Colors.green, @@ -130,16 +142,16 @@ class ModeratedSubredditCardInformationWeb extends StatelessWidget { ), ), Text( - '${new NumberFormat.compact().format(loadedSubreddit!.numOfOnlines) + '${new NumberFormat.compact().format(widget.loadedSubreddit!.numOfOnlines) //loadedSubreddit!.numOfMembers }', - style: TextStyle( + style: const TextStyle( color: Colors.black, fontWeight: FontWeight.bold), ), ], ), - Text( + const Text( 'Online', style: TextStyle( color: Colors.grey, @@ -150,9 +162,9 @@ class ModeratedSubredditCardInformationWeb extends StatelessWidget { ], ), ), - Divider(), + const Divider(), Container( - padding: EdgeInsets.only(top: 10, right: 6), + padding: const EdgeInsets.only(top: 10, right: 6), width: 100.w, child: ElevatedButton( style: ButtonStyle( @@ -165,7 +177,7 @@ class ModeratedSubredditCardInformationWeb extends StatelessWidget { borderRadius: BorderRadius.all(Radius.circular(22)))), ), - child: Text( + child: const Text( 'Create Post', style: TextStyle( fontSize: 13, @@ -174,7 +186,26 @@ class ModeratedSubredditCardInformationWeb extends StatelessWidget { onPressed: () {}, ), ), - Divider(), + const Divider(), + ListTile( + leading: (chooseTheme) + ? Icon(Icons.visibility_outlined) + : Icon(Icons.visibility_off_outlined), + title: Text('Community theme'), + trailing: Switch( + activeColor: Colors.white, + activeTrackColor: Colors.blue, + inactiveThumbColor: Colors.white, + inactiveTrackColor: Colors.grey.shade400, + splashRadius: 50.0, + value: chooseTheme, + onChanged: (value) { + setState(() => chooseTheme = value); + Provider.of(context, listen: true) + .togglingTheme(); + }, + ), + ) ], ), ) diff --git a/lib/moderated_subreddit/widgets/moderated_subreddit_pop_up_menu_button.dart b/lib/moderated_subreddit/widgets/moderated_subreddit_pop_up_menu_button.dart index f734b9a6..86bd42b8 100644 --- a/lib/moderated_subreddit/widgets/moderated_subreddit_pop_up_menu_button.dart +++ b/lib/moderated_subreddit/widgets/moderated_subreddit_pop_up_menu_button.dart @@ -96,7 +96,8 @@ class ModeratedSubredditPopupMenuButtonState ); } -// To copy Link of Moderated Subreddit + // ===================================this function used to===========================================// +//==================copy Link of Subreddit===========================// Future shareCommunitySheetButton(BuildContext context) { return showModalBottomSheet( backgroundColor: Colors.transparent, @@ -110,8 +111,10 @@ class ModeratedSubredditPopupMenuButtonState ); } -// to disjoin of subreddit + // ===================================the next three function used to===========================================// +//==================to disjoin of subreddit===========================// // have two option one:leave subreddit two: cancel +//communityName==> commuintyUserName you want to leave void _showLeaveDialog(String communityName) { showDialog( context: context, @@ -164,7 +167,9 @@ class ModeratedSubredditPopupMenuButtonState await Provider.of(context, listen: false) .joinAndDisjoinModeratedSubreddit(communityName, 'unsub', context); if (unSub) { - changeDisJoinStatus(); + setState(() { + changeDisJoinStatus(); + }); ScaffoldMessenger.of(context).showSnackBar( CustomSnackBar( isError: false, text: 'Leave Successfully', disableStatus: true), @@ -176,16 +181,14 @@ class ModeratedSubredditPopupMenuButtonState Navigator.of(ctx).pop(); } -// to disjoin change the isJoined status bool changeDisJoinStatus() { - setState(() { - isJoinedstate = false; - }); - + isJoinedstate = false; return isJoinedstate; } -// to join subreddit + // ===================================the next two function used to===========================================// +//==================to join subreddit===========================// +//communityName==> commuintyUserName you want to join void _join(String communityName) async { bool sub = await Provider.of(context, listen: false) @@ -205,12 +208,8 @@ class ModeratedSubredditPopupMenuButtonState } } -// to join change the isJoined status bool changeJoinStatus() { - setState(() { - isJoinedstate = true; - }); - + isJoinedstate = true; return isJoinedstate; } diff --git a/lib/moderated_subreddit/widgets/moderated_subreddit_web.dart b/lib/moderated_subreddit/widgets/moderated_subreddit_web.dart index 1d6b5996..e4080af3 100644 --- a/lib/moderated_subreddit/widgets/moderated_subreddit_web.dart +++ b/lib/moderated_subreddit/widgets/moderated_subreddit_web.dart @@ -1,11 +1,13 @@ import 'package:flutter/material.dart'; import 'package:responsive_sizer/responsive_sizer.dart'; +import 'package:post/widgets/loading_reddit.dart'; import '../../models/subreddit_about _rules.dart'; import '../../widgets/subreddit_about.dart'; import '../../widgets/subreddit_join_button_web.dart'; import '../../models/subreddit_data.dart'; import '../widgets/moderated_subreddite_post_web.dart'; import '../../widgets/back_to_button.dart'; + class ModeratedSubredditWeb extends StatelessWidget { String userName; ModeratedSubredditWeb( @@ -14,23 +16,20 @@ class ModeratedSubredditWeb extends StatelessWidget { required this.tabBar, required this.loadedSubreddit, required this.isLoading, - required this.controller}) { - print(loadedSubreddit!.name); - } - //===============Drawer Bar=====================// + required this.controller}); bool isOnline = true; SubredditData? loadedSubreddit; final TabBar tabBar; bool isLoading; TabController? controller; ScrollController scrollController = ScrollController(); - @override Widget build(BuildContext context) { final GlobalKey _scaffoldKey = GlobalKey(); return Scaffold( key: _scaffoldKey, - floatingActionButton:BackToTopButton(scrollController:scrollController ) , + floatingActionButton: + BackToTopButton(scrollController: scrollController), body: isLoading ? const Center( child: Icon( @@ -83,7 +82,6 @@ class ModeratedSubredditWeb extends StatelessWidget { left: 0, bottom: 0, child: Container( - // padding: EdgeInsets.only(left: 250), color: Colors.white, child: Column( mainAxisAlignment: @@ -92,10 +90,10 @@ class ModeratedSubredditWeb extends StatelessWidget { CrossAxisAlignment.start, children: [ Container( - //margin: EdgeInsets.only(top: 15), width: 100.w, height: 15.h, - padding: EdgeInsets.only(left: 250), + padding: const EdgeInsets.only( + left: 250), child: Row( crossAxisAlignment: CrossAxisAlignment.center, @@ -105,8 +103,6 @@ class ModeratedSubredditWeb extends StatelessWidget { Container( width: 10.w, height: 20.h, - // margin: EdgeInsets.only(bottom: 30,top: 40), - decoration: BoxDecoration( color: Colors.orange, shape: BoxShape.circle, @@ -120,17 +116,19 @@ class ModeratedSubredditWeb extends StatelessWidget { ), Container( height: 20.h, - margin: - EdgeInsets.only(top: 20), + margin: const EdgeInsets.only( + top: 20), child: Column( children: [ Text( '${loadedSubreddit!.name.toString()}', - style: TextStyle( - fontWeight: - FontWeight - .bold, - fontSize: 35)), + style: + const TextStyle( + fontWeight: + FontWeight + .bold, + fontSize: + 35)), Text( 'r/${loadedSubreddit!.name.toString()}', ), @@ -140,8 +138,6 @@ class ModeratedSubredditWeb extends StatelessWidget { SubredditJoinButtonWeb( isJoined: loadedSubreddit! .isJoined as bool, - // icon: icon, - //dropDownValue: dropDownValue, communityName: loadedSubreddit!.name .toString()), @@ -160,17 +156,11 @@ class ModeratedSubredditWeb extends StatelessWidget { ]; }, body: isLoading - ? const Center( - child: Icon( - Icons.reddit, - color: Colors.blue, - ), - ) + ? LoadingReddit() : TabBarView(controller: controller, children: [ ModeratedSubredditePostWeb( loadedSubreddit: loadedSubreddit, ), - // SubredditPosts(routeNamePop: SubredditScreen.routeName), SubredditAbout( rules: loadedSubreddit!.rules as List, diff --git a/lib/moderated_subreddit/widgets/moderated_subreddite_post_web.dart b/lib/moderated_subreddit/widgets/moderated_subreddite_post_web.dart index 92e0da93..cbfac880 100644 --- a/lib/moderated_subreddit/widgets/moderated_subreddite_post_web.dart +++ b/lib/moderated_subreddit/widgets/moderated_subreddite_post_web.dart @@ -1,81 +1,126 @@ import 'package:flutter/material.dart'; import 'package:responsive_sizer/responsive_sizer.dart'; +import 'package:provider/provider.dart'; +import 'package:shared_preferences/shared_preferences.dart'; import '../../widgets/sort_bottom_web.dart'; import '../../models/subreddit_data.dart'; import '../widgets/moderated_subreddit_card_information_web.dart'; +import '../../post/models/post_model.dart'; +import '../../providers/subreddit_posts_provider.dart'; +import '../../widgets/loading_reddit.dart'; +import '../../post/widgets/post_list.dart'; + class ModeratedSubredditePostWeb extends StatefulWidget { final SubredditData? loadedSubreddit; const ModeratedSubredditePostWeb({ Key? key, required this.loadedSubreddit, }) : super(key: key); - - // Posts(this.routeNamePop); @override - State< ModeratedSubredditePostWeb> createState() => _ModeratedSubredditePostWebState(); + State createState() => + _ModeratedSubredditePostWebState(); } -class _ModeratedSubredditePostWebState extends State { +class _ModeratedSubredditePostWebState + extends State { + int _page = 1; + List? posts = []; + bool _isLoadMoreRunning = false; + String? userName; + bool _isInit = true; + bool _isLoading = false; + late ScrollController _scrollController; + //=====================================this function is used to======================================// + //=================Loading More Data====================// + void _loadMore() { + if (_isLoading == false && _isLoadMoreRunning == false) { + setState(() { + toggleLoadingMore(); // Display a progress indicator at the bottom + }); + setState(() { + _page += 1; // Increase _page by one + }); + setState(() { + toggleLoadingMore(); + }); + } + } + //=====================================this function is used to======================================// + //=================Start loading More Data====================// + bool toggleLoadingMore() => _isLoadMoreRunning = !_isLoadMoreRunning; + //=====================================this function is used to======================================// + //=================Loading Data for first time====================// @override - Widget build(BuildContext context) { - return ListView( - scrollDirection: Axis.vertical, - children: [ - Row( - mainAxisSize: MainAxisSize.min, + Future didChangeDependencies() async { + // TODO: implement didChangeDependencies + if (_isInit) { + setState(() { + _isLoading = true; + }); + //New Hot Top + await Provider.of(context, listen: false) + .fetchSubredditePosts(widget.loadedSubreddit!.name.toString(), 'hot', + _page, 25, context) + .then((value) async { + posts = + await Provider.of(context, listen: false) + .gettingSubredditPostData; + }); + final prefs = await SharedPreferences.getInstance(); + userName = prefs.getString('userName'); + setState(() { + _isLoading = false; + }); + } + _isInit = false; + super.didChangeDependencies(); + } - mainAxisAlignment: MainAxisAlignment.spaceBetween, - //crossAxisAlignment: CrossAxisAlignment., - children: [ - Expanded( - flex: 2, - child: Column( - children: [ - SizedBox( - height: 10.h, - ), - Container( - height: 6.h, - width: 50.w, - margin: EdgeInsets.only(left: 100, top: 40), - child: SortBottomWeb(page: 1,userName: widget.loadedSubreddit!.name.toString(),), - color: Colors.white, - // width: 100.w, - ), - Container( - // padding: const EdgeInsets.only(bottom: 100,), - margin: EdgeInsets.only(left: 100, bottom: 90, top: 30), - height: 30.h, - width: 50.w, - // color: Colors.white, - decoration: BoxDecoration( - color: Colors.white, - border: Border.all( - color: Colors.blue, - width: 3, - )), - child: Column(children: [ - Expanded( - child: Row( - children: const [ - Expanded( - child: ListTile( - title: Text('Post'), - ), - ), - ], - ), - ) - ]), + @override + Widget build(BuildContext context) { + return (_isLoading || _isLoadMoreRunning) + ? LoadingReddit() + : (posts != null) + ? PostList( + leftMargin: 15.w, + rightMargin: 35.w, + topOfTheList: Container( + height: 65.h, + width: 50.h, + margin: EdgeInsets.only(left: 15.w), + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ModeratedSubredditCardInformationWeb( + loadedSubreddit: widget.loadedSubreddit), + SortBottomWeb( + page: 1, + userName: widget.loadedSubreddit!.name.toString()), + ], ), - ], - ), - ), - ModeratedSubredditCardInformationWeb(loadedSubreddit: widget.loadedSubreddit), - ], - ), - ], - ); + ), + userName: userName.toString(), + updateData: _loadMore, + data: posts as List, + type: 'community', + ) + : Center( + child: Column( + children: [ + SizedBox( + height: 30.h, + ), + const Icon( + Icons.reddit, + size: 100, + ), + const Text( + 'Wow,such empty', + style: TextStyle(color: Colors.grey), + ) + ], + ), + ); } } - diff --git a/lib/moderated_subreddit/widgets/moderated_subriddet_posts.dart b/lib/moderated_subreddit/widgets/moderated_subriddet_posts.dart deleted file mode 100644 index cfbe9d98..00000000 --- a/lib/moderated_subreddit/widgets/moderated_subriddet_posts.dart +++ /dev/null @@ -1,81 +0,0 @@ -// import 'package:flutter/material.dart'; -// //import 'package:flutter_code_style/analysis_options.yaml'; -// import 'package:responsive_sizer/responsive_sizer.dart'; -// import '../widgets/mod_subreddit_post_sort_bottom.dart'; -// import '../../post/widgets/post.dart'; -// import '../../post/test_data.dart'; -// import 'package:provider/provider.dart'; -// //import 'package:flutter_code_style/analysis_options.yaml'; -// import '../../post/models/post_model.dart'; -// import '../../providers/subreddit_post.dart'; - -// class ModeratedSubriddetPosts extends StatefulWidget { -// final String routeNamePop; -// final String subredditName; - -// ModeratedSubriddetPosts( -// {Key? key, required this.routeNamePop, required this.subredditName}) -// : super(key: key); -// // [ -// // {'username': 'ahmed', 'title': 'hello world1'}, -// // {'username': 'sayed', 'title': 'hello world2'}, -// // {'username': 'sayed', 'title': 'hello world3'}, -// // {'username': 'ahmed', 'title': 'hello world1'}, -// // {'username': 'sayed', 'title': 'hello world2'}, -// // {'username': 'sayed', 'title': 'hello world3'}, -// // {'username': 'ahmed', 'title': 'hello world1'}, -// // {'username': 'sayed', 'title': 'hello world2'}, -// // {'username': 'sayed', 'title': 'hello world3'} -// // ]; -// @override -// State createState() => _ModeratedSubriddetPosts(); -// } - -// class _ModeratedSubriddetPosts extends State { -// bool _isInit = true; -// bool _isLoading = false; -// List? posts = []; -// @override -// void didChangeDependencies() { -// if (_isInit) { -// setState(() { -// _isLoading = true; -// }); -// Provider.of(context, listen: false) -// .fetchNewProfilePosts(widget.subredditName) -// .then((value) { -// posts = Provider.of(context, listen: false) -// .gettingProfilePostData; -// setState(() { -// _isLoading = false; -// }); -// }); -// } -// _isInit = false; -// super.didChangeDependencies(); -// } - -// @override -// Widget build(BuildContext context) { -// return ListView( -// scrollDirection: Axis.vertical, -// children: [ -// ModSubredditPostSortBottom( -// widget.routeNamePop, -// //_dropDownValue, _icon -// ), -// //Select the type of Posts -// SingleChildScrollView( -// child: ListView.builder( -// physics: const ClampingScrollPhysics(), -// shrinkWrap: true, -// itemBuilder: ((context, index) => Post.community( -// data: posts![index], -// )), -// itemCount: posts!.length, -// ), -// ), -// ], -// ); -// } -// } diff --git a/lib/moderated_subreddit/widgets/position_for_moderated_subredditInfo.dart b/lib/moderated_subreddit/widgets/position_for_moderated_subredditInfo.dart index 3026d076..76bac0c3 100644 --- a/lib/moderated_subreddit/widgets/position_for_moderated_subredditInfo.dart +++ b/lib/moderated_subreddit/widgets/position_for_moderated_subredditInfo.dart @@ -1,5 +1,3 @@ -// ignore_for_file: file_names - import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; import 'package:post/moderation_settings/screens/moderator_tools_screen.dart'; @@ -32,8 +30,6 @@ class PositionForModeratedSubredditInfo extends StatelessWidget { margin: const EdgeInsets.only(top: 15), width: double.infinity, height: MediaQuery.of(context).size.height * 0.09, - // width: 100.w, - // height: 9.h, child: ListTile( title: Text('r/${loadedSubreddit!.name.toString()}', style: const TextStyle(fontWeight: FontWeight.bold)), @@ -44,8 +40,6 @@ class PositionForModeratedSubredditInfo extends StatelessWidget { margin: const EdgeInsets.only(bottom: 30), width: MediaQuery.of(context).size.width * 0.35, height: MediaQuery.of(context).size.height * 0.04, - // width: 35.w, - // height: 4.h, child: ElevatedButton( style: ButtonStyle( backgroundColor: MaterialStateProperty.all( diff --git a/lib/moderation_settings/models/traffic.dart b/lib/moderation_settings/models/traffic.dart new file mode 100644 index 00000000..416afbc5 --- /dev/null +++ b/lib/moderation_settings/models/traffic.dart @@ -0,0 +1,41 @@ +class Traffic { + List? data; + + Traffic({this.data}); + + Traffic.fromJson(Map json) { + if (json['data'] != null) { + data = []; + json['data'].forEach((v) { + data!.add(new TrafficData.fromJson(v)); + }); + } + } + + Map toJson() { + final Map data = new Map(); + if (this.data != null) { + data['data'] = this.data!.map((v) => v.toJson()).toList(); + } + return data; + } +} + +class TrafficData { + int? iId; + int? numOfUsers; + + TrafficData({this.iId, this.numOfUsers}); + + TrafficData.fromJson(Map json) { + iId = json['_id']; + numOfUsers = json['numOfUsers']; + } + + Map toJson() { + final Map data = new Map(); + data['_id'] = this.iId; + data['numOfUsers'] = this.numOfUsers; + return data; + } +} diff --git a/lib/moderation_settings/provider/moderation_general_data.dart b/lib/moderation_settings/provider/moderation_general_data.dart new file mode 100644 index 00000000..334b5c40 --- /dev/null +++ b/lib/moderation_settings/provider/moderation_general_data.dart @@ -0,0 +1,76 @@ +import 'dart:convert'; +import 'package:dio/dio.dart'; +import 'package:flutter/material.dart'; +import '../../networks/dio_client.dart'; +import '../../networks/const_endpoint_data.dart'; +import '../models/moderator_tools.dart'; +import '../models/moderators.dart'; +import '../models/approved.dart'; +import '../models/banned.dart'; +import '../models/muted.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import '../../widgets/handle_error.dart'; +import '../models/user.dart'; +import '../provider/moderation_settings_provider.dart'; +import '../models/traffic.dart'; + +class ModerationGeneralDataProvider with ChangeNotifier { + late Traffic dayTraffic; + late Traffic weekTraffic; + late Traffic monthTraffic; + late Traffic yearTraffic; + + bool isError = false; + String errorMessage = ''; + + Future gettrafficData( + String subRedditName, BuildContext context) async { + //If the topic changed call patch to update the community topic + subRedditName = 'khaled subreddit'; + try { + final path1 = '/subreddits/traffic/$subRedditName/day'; + final path2 = '/subreddits/traffic/$subRedditName/week'; + final path3 = '/subreddits/traffic/$subRedditName/month'; + final path4 = '/subreddits/traffic/$subRedditName/year'; + + final prefs = await SharedPreferences.getInstance(); + DioClient.init(prefs); + subredditName = userName; + + // final response = + Response response1 = await DioClient.get(path: path1); + Response response2 = await DioClient.get(path: path2); + Response response3 = await DioClient.get(path: path3); + Response response4 = await DioClient.get(path: path4); + print('asdasdasdasdsaasdsadasdsad'); + print(response1); + // print(response2); + + // print(response3); + // print(response4); + + isError = !(response1.statusCode! < 310) && + !(response2.statusCode! < 310) && + !(response3.statusCode! < 310) && + !(response4.statusCode! < 310); + // if (isError) { + // errorMessage = json.decode(response.data)['errorMessage']; + // print(isError); + // print(errorMessage); + // } else { + dayTraffic = Traffic.fromJson(response1.data); + weekTraffic = Traffic.fromJson(response2.data); + monthTraffic = Traffic.fromJson(response3.data); + yearTraffic = Traffic.fromJson(response4.data); + // } + + notifyListeners(); + } on DioError catch (e) { + if (e.response!.statusCode != 404) { + HandleError.errorHandler(e, context); + } + } catch (error) { + HandleError.handleError(error.toString(), context); + } + } +} diff --git a/lib/moderation_settings/provider/moderation_settings_provider.dart b/lib/moderation_settings/provider/moderation_settings_provider.dart index 0a823a56..407f0bfa 100644 --- a/lib/moderation_settings/provider/moderation_settings_provider.dart +++ b/lib/moderation_settings/provider/moderation_settings_provider.dart @@ -11,6 +11,8 @@ import '../models/muted.dart'; import 'package:shared_preferences/shared_preferences.dart'; import '../../widgets/handle_error.dart'; import '../models/user.dart'; +import '../provider/moderation_settings_provider.dart'; +import '../models/traffic.dart'; class ModerationSettingProvider with ChangeNotifier { ModeratorToolsModel? moderatorToolsModel1; @@ -18,6 +20,11 @@ class ModerationSettingProvider with ChangeNotifier { List? banned = []; List? muted = []; List? approved = []; + late Traffic dayTraffic; + late Traffic weekTraffic; + late Traffic monthTraffic; + late Traffic yearTraffic; + bool isError = false; String errorMessage = ''; ModeratorToolsModel? get moderatorToolsModel { @@ -107,7 +114,7 @@ class ModerationSettingProvider with ChangeNotifier { } }); print('after response'); - // notifyListeners(); + // notifyListeners(); } on DioError catch (e) { HandleError.errorHandler(e, context); } catch (error) { @@ -153,4 +160,70 @@ class ModerationSettingProvider with ChangeNotifier { HandleError.handleError(error.toString(), context); } } + + Future trafficData( + String type, String subRedditName, BuildContext context) async { + //If the topic changed call patch to update the community topic + try { + final path = '/subreddits/traffic/$subRedditName/$type'; + final prefs = await SharedPreferences.getInstance(); + DioClient.init(prefs); + subredditName = userName; + + // final response = + await DioClient.get(path: path).then((response) { + isError = !(response.statusCode! < 300); + if (isError) { + errorMessage = json.decode(response.data)['errorMessage']; + print(isError); + print(errorMessage); + } else {} + }); + notifyListeners(); + } on DioError catch (e) { + if (e.response!.statusCode != 404) { + HandleError.errorHandler(e, context); + } + } catch (error) { + HandleError.handleError(error.toString(), context); + } + } + + Future gettrafficData( + String type, String subRedditName, BuildContext context) async { + //If the topic changed call patch to update the community topic + try { + final path = '/subreddits/traffic/$subRedditName/$type'; + final prefs = await SharedPreferences.getInstance(); + DioClient.init(prefs); + subredditName = userName; + + // final response = + await DioClient.get(path: path).then((response) { + isError = !(response.statusCode! < 300); + if (isError) { + errorMessage = json.decode(response.data)['errorMessage']; + print(isError); + print(errorMessage); + } else { + if (type == 'day') { + dayTraffic = Traffic.fromJson(response.data); + } else if (type == 'week') { + weekTraffic = Traffic.fromJson(response.data); + } else if (type == 'month') { + monthTraffic = Traffic.fromJson(response.data); + } else if (type == 'year') { + yearTraffic = Traffic.fromJson(response.data); + } + } + }); + notifyListeners(); + } on DioError catch (e) { + if (e.response!.statusCode != 404) { + HandleError.errorHandler(e, context); + } + } catch (error) { + HandleError.handleError(error.toString(), context); + } + } } \ No newline at end of file diff --git a/lib/moderation_settings/screens/traffic_state.dart b/lib/moderation_settings/screens/traffic_state.dart new file mode 100644 index 00000000..7d816093 --- /dev/null +++ b/lib/moderation_settings/screens/traffic_state.dart @@ -0,0 +1,383 @@ +import 'dart:math'; + +import 'package:flutter/src/widgets/container.dart'; +import 'package:flutter/src/widgets/framework.dart'; +import 'package:draw_graph/draw_graph.dart'; +import 'package:draw_graph/models/feature.dart'; +import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; +import 'package:post/logins/providers/authentication.dart'; +import 'package:post/moderation_settings/models/traffic.dart'; +import '../../widgets/loading_reddit.dart'; +import 'package:responsive_sizer/responsive_sizer.dart'; +import '../provider/moderation_general_data.dart'; +import 'package:provider/provider.dart'; +import 'package:editable/editable.dart'; + +class TraficState extends StatefulWidget { + static const routeName = '/traficstate'; + const TraficState({super.key}); + + @override + State createState() => _TraficStateState(); +} + +class _TraficStateState extends State { + bool fetchingDone = true; + bool _isInit = true; + bool _isBuild = false; + int index = 0; + static const startYear = 1990; + List days = []; + List daysValues = List.generate(7, (int index) => 0); + int maxDay = 0; + + List weeks = []; + List weeksValues = List.generate(52, (int index) => 0); + int maxWeek = 0; + + List months = []; + List monthsValues = List.generate(12, (int index) => 0); + int maxMonth = 0; + + List years = []; + List yearsValues = List.generate( + DateTime.now().year - startYear + 1, (int index) => 0); + + int maxYear = 0; + + // int max = 1; + List features = []; + TrafficData maxi(TrafficData a, TrafficData b) { + return (a.numOfUsers! >= b.numOfUsers!) ? a : b; + } + + @override + void didChangeDependencies() { + final Map data = {}; + if (_isInit) { + setState(() { + fetchingDone = false; + }); + /////prepare days in xaxes and y axise + ///start with x axise + ///and the y axis will be inside the then of the provider + days = ['Sun', 'Mon', 'Tues', 'Wed', 'Thurs', 'Fri', 'Sat']; + for (int i = 1; i <= 52; i += 1) { + weeks.add('week $i'); + } + months = [ + 'Jan', + 'Feb', + 'Mar', + 'Apr', + 'May', + 'Jun', + 'Jul', + 'Aug', + 'Sep', + 'Oct', + 'Nov', + 'Dec' + ]; + for (int i = startYear; i <= DateTime.now().year; i += 1) { + years.add('$i'); + } + + final provider = + Provider.of(context, listen: false); + fetchingDone = false; + final subredditName = ModalRoute.of(context)?.settings.arguments != null + ? ModalRoute.of(context)?.settings.arguments as String + : ''; + + provider.gettrafficData(subredditName, context).then((value) { + print(provider.dayTraffic); + print(provider.weekTraffic); + print(provider.monthTraffic); + print(provider.yearTraffic); + maxDay = provider.dayTraffic.data!.reduce(maxi).numOfUsers!; + maxWeek = provider.weekTraffic.data!.reduce(maxi).numOfUsers!; + maxMonth = provider.monthTraffic.data!.reduce(maxi).numOfUsers!; + maxYear = provider.yearTraffic.data!.reduce(maxi).numOfUsers!; + + for (int i = 0; i < provider.dayTraffic.data!.length; i += 1) { + daysValues[provider.dayTraffic.data![i].iId! - 1] = + provider.dayTraffic.data![i].numOfUsers!.toDouble(); + if (maxDay != 0) { + daysValues[provider.dayTraffic.data![i].iId! - 1] /= + maxDay.toDouble(); + } + // if (provider.dayTraffic.data![i].numOfUsers! > maxDay) { + // maxDay = provider.dayTraffic.data![i].numOfUsers!; + // } + } + for (int i = 0; i < provider.weekTraffic.data!.length; i += 1) { + weeksValues[provider.weekTraffic.data![i].iId! - 1] = + provider.weekTraffic.data![i].numOfUsers!.toDouble(); + if (maxWeek != 0) { + weeksValues[provider.weekTraffic.data![i].iId! - 1] /= maxWeek; + } + // if (provider.weekTraffic.data![i].numOfUsers! > maxWeek) { + // maxWeek = provider.weekTraffic.data![i].numOfUsers!; + // } + } + for (int i = 0; i < provider.monthTraffic.data!.length; i += 1) { + monthsValues[provider.monthTraffic.data![i].iId! - 1] = + provider.monthTraffic.data![i].numOfUsers!.toDouble(); + if (maxMonth != 0) { + monthsValues[provider.monthTraffic.data![i].iId! - 1] /= maxMonth; + } + // if (provider.monthTraffic.data![i].numOfUsers! > maxMonth) { + // maxMonth = provider.monthTraffic.data![i].numOfUsers!; + // } + } + for (int i = 0; i < provider.yearTraffic.data!.length; i += 1) { + yearsValues[provider.yearTraffic.data![i].iId! - startYear] = + provider.yearTraffic.data![i].numOfUsers!.toDouble(); + if (maxYear != 0) { + yearsValues[provider.yearTraffic.data![i].iId! - startYear] /= + maxYear; + } + // if (provider.yearTraffic.data![i].numOfUsers! > maxYear) { + // maxYear = provider.yearTraffic.data![i].numOfUsers!; + // } + } + fetchingDone = true; + + if (_isBuild) setState(() {}); + }); + ; + } + _isInit = false; + super.didChangeDependencies(); + } + + List rows = [ + { + "name": 'James LongName Joe', + "date": '23/09/2020', + "month": 'June', + "status": 'completed' + }, + { + "name": 'Daniel Paul', + "month": 'March', + "status": 'new', + "date": '12/4/2020', + }, + { + "month": 'May', + "name": 'Mark Zuckerberg', + "date": '09/4/1993', + "status": 'expert' + }, + { + "name": 'Jack', + "status": 'legend', + "date": '01/7/1820', + "month": 'December', + }, + ]; + List cols = [ + {"title": 'Name', 'widthFactor': 0.2, 'key': 'name', 'editable': false}, + {"title": 'Date', 'widthFactor': 0.2, 'key': 'date'}, + {"title": 'Month', 'widthFactor': 0.2, 'key': 'month'}, + {"title": 'Status', 'key': 'status'}, + ]; + @override + Widget build(BuildContext context) { + _isBuild = true; + features = [ + Feature( + title: "joined members", + color: Colors.blue, + data: (index == 0) + ? daysValues + : (index == 1) + ? weeksValues + : (index == 2) + ? monthsValues + : yearsValues, + // :false; + ), + ]; + final maxmembers = (index == 0) + ? maxDay + : (index == 1) + ? maxWeek + : (index == 2) + ? maxMonth + : maxYear; + return (!fetchingDone) + ? const LoadingReddit() + //Calls TopicMainScreen widget to build Topics Screen + : Scaffold( + backgroundColor: Color.fromARGB(255, 228, 231, 239), + body: Row( + children: [ + SizedBox( + width: 50, + ), + SizedBox( + width: 60.w, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Center( + child: Row( + children: [ + Container( + width: 40.w, + color: Colors.white, + child: Padding( + padding: const EdgeInsets.all(12.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + LineGraph( + features: features, + size: Size(420, 450), + labelX: (index == 0) + ? days + : (index == 1) + ? weeks + : (index == 2) + ? months + : years, + labelY: [ + '${(maxmembers * .1)}', + '${(maxmembers * .2)}', + '${(maxmembers * .3)}', + '${(maxmembers * .4)}', + '${(maxmembers * .5)}', + '${(maxmembers * .6)}', + '${(maxmembers * .7)}', + '${(maxmembers * .8)}', + '${(maxmembers * .9)}', + '${(maxmembers * 1)}' + ], + showDescription: true, + graphColor: Colors.black87, + graphOpacity: 0, + ), + ], + ), + ), + ), + // SizedBox( + // width: 30, + // ), + Container( + // width: 15.w, + color: Colors.white, + child: Column( + children: [ + Row( + mainAxisSize: MainAxisSize.min, + children: [ + Checkbox( + value: index == 0, + onChanged: (newValue) { + if ((newValue) == true) index = 0; + setState(() {}); + }, + ), + Text('Day'), + ], + ), + Row( + mainAxisSize: MainAxisSize.min, + children: [ + Checkbox( + value: index == 1, + onChanged: (newValue) { + if ((newValue) == true) { + index = 1; + } + setState(() {}); + }, + ), + Text('week'), + ], + ), + Row( + mainAxisSize: MainAxisSize.min, + children: [ + Checkbox( + value: index == 2, + onChanged: (newValue) { + if ((newValue) == true) index = 2; + setState(() {}); + }, + ), + Text('month'), + ], + ), + Row( + mainAxisSize: MainAxisSize.min, + children: [ + Checkbox( + value: index == 3, + onChanged: (newValue) { + if ((newValue) == true) index = 3; + setState(() {}); + }, + ), + Text('year'), + ], + ) + ], + ), + ) + ], + ), + ), + // Container( + // color: Colors.white, + // child: Editable( + // // key: _editableKey, + // columns: cols, + // rows: rows, + // zebraStripe: true, + // stripeColor1: Colors.blue, + // stripeColor2: Colors.grey, + // onRowSaved: (value) { + // print(value); + // }, + // onSubmitted: (value) { + // print(value); + // }, + // borderColor: Colors.blueGrey, + // tdStyle: TextStyle(fontWeight: FontWeight.bold), + // trHeight: 80, + // thStyle: TextStyle( + // fontSize: 15, fontWeight: FontWeight.bold), + // thAlignment: TextAlign.center, + // thVertAlignment: CrossAxisAlignment.end, + // thPaddingBottom: 3, + // showSaveIcon: true, + // saveIconColor: Colors.black, + // showCreateButton: true, + // tdAlignment: TextAlign.left, + // tdEditableMaxLines: + // 100, // don't limit and allow data to wrap + // tdPaddingTop: 0, + // tdPaddingBottom: 14, + // tdPaddingLeft: 10, + // tdPaddingRight: 8, + // focusedBorder: OutlineInputBorder( + // borderSide: BorderSide(color: Colors.blue), + // borderRadius: + // BorderRadius.all(Radius.circular(0))), + // ), + // ) + ], + ), + ), + ], + ), + ); + } +} diff --git a/lib/moderation_settings/screens/traffic_table.dart b/lib/moderation_settings/screens/traffic_table.dart new file mode 100644 index 00000000..3668126c --- /dev/null +++ b/lib/moderation_settings/screens/traffic_table.dart @@ -0,0 +1,72 @@ +import 'package:flutter/src/widgets/container.dart'; +import 'package:flutter/src/widgets/framework.dart'; +import 'package:flutter/material.dart'; +import 'package:editable/editable.dart'; + +class TrafficTable extends StatelessWidget { + const TrafficTable({super.key}); + + @override + Widget build(BuildContext context) { + List rows = [ + { + "name": 'James LongName Joe', + "date": '23/09/2020', + "month": 'June', + "status": 'completed' + }, + { + "name": 'Daniel Paul', + "month": 'March', + "status": 'new', + "date": '12/4/2020', + }, + { + "month": 'May', + "name": 'Mark Zuckerberg', + "date": '09/4/1993', + "status": 'expert' + }, + { + "name": 'Jack', + "status": 'legend', + "date": '01/7/1820', + "month": 'December', + }, + ]; + List cols = [ + {"title": 'Name', 'widthFactor': 0.2, 'key': 'name', 'editable': false}, + {"title": 'Date', 'widthFactor': 0.2, 'key': 'date'}, + {"title": 'Month', 'widthFactor': 0.2, 'key': 'month'}, + {"title": 'Status', 'key': 'status'}, + ]; + return Table( + defaultColumnWidth: FixedColumnWidth(120.0), + border: TableBorder.all( + color: Colors.black, style: BorderStyle.solid, width: 2), + children: [ + TableRow(children: [ + Column(children: [Text('Website', style: TextStyle(fontSize: 20.0))]), + Column( + children: [Text('Tutorial', style: TextStyle(fontSize: 20.0))]), + Column(children: [Text('Review', style: TextStyle(fontSize: 20.0))]), + ]), + TableRow(children: [ + Column(children: [Text('Javatpoint')]), + Column(children: [Text('Flutter')]), + Column(children: [Text('5*')]), + ]), + TableRow(children: [ + Column(children: [Text('Javatpoint')]), + Column(children: [Text('MySQL')]), + Column(children: [Text('5*')]), + ]), + TableRow(children: [ + Column(children: [Text('Javatpoint')]), + Column(children: [Text('ReactJS')]), + Column(children: [Text('5*')]), + ]), + ], + ); + } +} diff --git a/lib/myprofile/models/myprofile_data.dart b/lib/myprofile/models/myprofile_data.dart index c4990771..90ac0c00 100644 --- a/lib/myprofile/models/myprofile_data.dart +++ b/lib/myprofile/models/myprofile_data.dart @@ -1,20 +1,16 @@ class MyProfileData { - //String? id; String? userName; String? email; String? profilePicture; String? profileBackPicture; String? description; String? displayName; - // DateTime? toDayTime; String? createdAt; - //int? numOfDaysInReddit; int? followersCount; int? postKarma; int? commentkarma; MyProfileData( { - //required this.id, required this.userName, required this.email, required this.profilePicture, @@ -22,52 +18,21 @@ class MyProfileData { required this.description, required this.displayName, required this.createdAt, - // required this.numOfDaysInReddit, required this.followersCount, required this.postKarma, required this.commentkarma}); - + // ===================================this function used to===========================================// +//=================to change json to model===========================// MyProfileData.fromJson(Map json) { - // print(json['id'].runtimeType); - // print(json['userName'].runtimeType); - // print(json['email'].runtimeType); - // print(json['profilePicture'].runtimeType); - // print(json['profileBackground'].runtimeType); - // print(json['followersCount'].runtimeType); - // print(json['createdAt'].runtimeType); - // print(json['postKarma'].runtimeType); - // print(json['commentKarma'].runtimeType); - // print(json['description'].runtimeType); - // print('heeee;ssssssssssssssssssssss'); - //id = json['id']; userName = json['userName']; email = json['email']; profilePicture = json['profilePicture']; profileBackPicture = json['profileBackground']; description = json['description']; - displayName = json['userName']; //json['displayName']; + displayName =json['displayName']??''; followersCount = int.parse(json['followersCount'].toString()); createdAt = json['createdAt']; - // numOfDaysInReddit = int.parse(json['numOfDaysInReddit'].toString()); postKarma = int.parse(json['postKarma'].toString()); - // displayName = json['displayName']; commentkarma = int.parse(json['commentKarma'].toString()); } - - Map toJson() { - final Map data = new Map(); - //data['id'] = this.id; - data['userName'] = this.userName; - data['email'] = this.email; - data['profilePicture'] = this.profilePicture; - data['profileBackPicture'] = this.profileBackPicture; - data['description'] = this.description; - data['followersCount'] = this.followersCount; - data['createdAt'] = this.createdAt; - // data['numOfDaysInReddit'] = this.numOfDaysInReddit; - data['postKarma'] = this.postKarma; - data['displayName'] = this.displayName; - data['commentkarma'] = this.commentkarma; - return data; - } } diff --git a/lib/myprofile/models/myprofile_followers_data.dart b/lib/myprofile/models/myprofile_followers_data.dart index f955d92b..a5d65925 100644 --- a/lib/myprofile/models/myprofile_followers_data.dart +++ b/lib/myprofile/models/myprofile_followers_data.dart @@ -11,6 +11,8 @@ class MyProfileFollowersData { required this.karama, required this.isFollowed, }); + // ===================================this function used to===========================================// +//=================to change json to model===========================// MyProfileFollowersData.fromJson(Map json) { profilePicture=json['profilePicture']; userName = json['userName']; diff --git a/lib/myprofile/providers/myprofile_provider.dart b/lib/myprofile/providers/myprofile_provider.dart index 44649750..a6e5dee3 100644 --- a/lib/myprofile/providers/myprofile_provider.dart +++ b/lib/myprofile/providers/myprofile_provider.dart @@ -19,7 +19,8 @@ class MyProfileProvider with ChangeNotifier { List? get gettingMyProfileFollowersData { return followersData; } - + // ===================================this function used to===========================================// +//==================fetch and set date===========================// Future fetchAndSetMyProfile(BuildContext context) async { try { final prefs = await SharedPreferences.getInstance(); @@ -34,7 +35,8 @@ class MyProfileProvider with ChangeNotifier { HandleError.handleError(error.toString(), context); } } - + // ===================================this function used to===========================================// +//==================fetch and set date of Followers===========================// Future fetchAndSetFollowersData(BuildContext context) async { try { final prefs = await SharedPreferences.getInstance(); diff --git a/lib/myprofile/screens/myprofile_screen.dart b/lib/myprofile/screens/myprofile_screen.dart index b5899bfd..b5166149 100644 --- a/lib/myprofile/screens/myprofile_screen.dart +++ b/lib/myprofile/screens/myprofile_screen.dart @@ -22,7 +22,6 @@ class _MyProfileState extends State var _isLoading = false; var _isInit = true; MyProfileData? loadProfile; - //=MyProfileData(userName: 'Zeinab', email:'Zeianb', profilePicture: '', profileBackPicture: '', description: 'My profile', displayName: 'Zeianb', createdAt: '2019-08-24T14:15:22Z', followersCount:1, postKarma: 0, commentkarma: 0); //=============Tab Bar======================// TabController? _controller; List tabs = [ @@ -62,7 +61,8 @@ class _MyProfileState extends State @override void didChangeDependencies() { // TODO: implement didChangeDependencies - // //===============================Fetch subreddit data =======================================// + // ===================================this function used to===========================================// +//==================fetch date for First time===========================// if (_isInit) { setState(() { _isLoading = true; diff --git a/lib/myprofile/screens/user_followers_screen.dart b/lib/myprofile/screens/user_followers_screen.dart index f01d5542..c0b283ba 100644 --- a/lib/myprofile/screens/user_followers_screen.dart +++ b/lib/myprofile/screens/user_followers_screen.dart @@ -16,38 +16,7 @@ class UserFollowersScreen extends StatefulWidget { } class _UserFollowersScreenState extends State { - List? followersData - // = [ - // MyProfileFollowersData( - // profilePicture: - // 'https://media.istockphoto.com/id/1243055987/photo/woman-silhouette-with-sun-in-head-with-copy-space-multiple-exposure-image.jpg?s=612x612&w=is&k=20&c=Nxu72uSlbHMuAiaGv33LITFTPYqagd5rlE1j0J61yyw=', - // userName: 'userName', - // displayName: 'displayName', - // karama: 4, - // isFollowed: true), - // MyProfileFollowersData( - // profilePicture: - // 'https://media.istockphoto.com/id/1243055987/photo/woman-silhouette-with-sun-in-head-with-copy-space-multiple-exposure-image.jpg?s=612x612&w=is&k=20&c=Nxu72uSlbHMuAiaGv33LITFTPYqagd5rlE1j0J61yyw=', - // userName: 'userName', - // displayName: 'displayName', - // karama: 4, - // isFollowed: false), - // MyProfileFollowersData( - // profilePicture: - // 'https://media.istockphoto.com/id/1243055987/photo/woman-silhouette-with-sun-in-head-with-copy-space-multiple-exposure-image.jpg?s=612x612&w=is&k=20&c=Nxu72uSlbHMuAiaGv33LITFTPYqagd5rlE1j0J61yyw=', - // userName: 'userName', - // displayName: 'displayName', - // karama: 4, - // isFollowed: true), - // MyProfileFollowersData( - // profilePicture: - // 'https://media.istockphoto.com/id/1243055987/photo/woman-silhouette-with-sun-in-head-with-copy-space-multiple-exposure-image.jpg?s=612x612&w=is&k=20&c=Nxu72uSlbHMuAiaGv33LITFTPYqagd5rlE1j0J61yyw=', - // userName: 'userName', - // displayName: 'displayName', - // karama: 4, - // isFollowed: false) - // ] - ; + List? followersData; bool _isInit = true; bool _isLoading = false; @override @@ -95,7 +64,6 @@ class _UserFollowersScreenState extends State { Icons.search, size: 30, ), - //border: OutlineInputBorder(), hintText: 'Search for aspecific userName', ), ), @@ -122,7 +90,6 @@ class _UserFollowersScreenState extends State { itemBuilder: ((context, index) => InkWell( child: Container( width: double.infinity, - // height: 15.h, color: const Color.fromARGB(255, 255, 255, 255), child: Column( diff --git a/lib/myprofile/widgets/myprofile_about.dart b/lib/myprofile/widgets/myprofile_about.dart index 380d61fe..41ad8678 100644 --- a/lib/myprofile/widgets/myprofile_about.dart +++ b/lib/myprofile/widgets/myprofile_about.dart @@ -48,7 +48,7 @@ class MyProfileAbout extends StatelessWidget { )), Container( - margin: EdgeInsets.only(right: 10,left: 10,top: 0), + margin: const EdgeInsets.only(right: 10,left: 10,top: 0), width: 100.w, height: (description == null||description == '') ? 0.h diff --git a/lib/myprofile/widgets/myprofile_app.dart b/lib/myprofile/widgets/myprofile_app.dart index f29c138e..c4eb05f8 100644 --- a/lib/myprofile/widgets/myprofile_app.dart +++ b/lib/myprofile/widgets/myprofile_app.dart @@ -39,15 +39,10 @@ class MyProfileApp extends StatelessWidget { elevation: 4, foregroundColor: Colors.white, backgroundColor: Colors.blue, - // innerBoxIsScrolled ? Colors.blue : Colors.white, title: - // Visibility( - // visible: innerBoxIsScrolled, - // child: Text('u/${loadProfile.displayName}', style: const TextStyle( color: Colors.white, fontWeight: FontWeight.bold)), - //), expandedHeight: (loadProfile.description == null || loadProfile.description == '') ? 54.h @@ -71,7 +66,6 @@ class MyProfileApp extends StatelessWidget { children: [ //Profile back ground Container( - // color: Colors.blue, height: (loadProfile.description == null || loadProfile.description == '') ? 56.5.h @@ -115,7 +109,6 @@ class MyProfileApp extends StatelessWidget { ProfilePosts( routeNamePop: MyProfileScreen.routeName, userName: userName, - //controller: _scrollController, ), ProfileComments(userName: userName), MyProfileAbout( diff --git a/lib/myprofile/widgets/myprofile_card_information_web.dart b/lib/myprofile/widgets/myprofile_card_information_web.dart index f4f41d08..061ac359 100644 --- a/lib/myprofile/widgets/myprofile_card_information_web.dart +++ b/lib/myprofile/widgets/myprofile_card_information_web.dart @@ -17,7 +17,7 @@ class MyProfileCardInformationWeb extends StatelessWidget { decoration: BoxDecoration( borderRadius: BorderRadius.circular(5), color: Colors.white), margin: EdgeInsets.only(right: 180, bottom: 25, top: 30), - width: 60.w, + width: 50.w, height: (loadProfile.description == null || loadProfile.description == '') ? 43.h @@ -27,7 +27,6 @@ class MyProfileCardInformationWeb extends StatelessWidget { children: [ //Profile back ground Container( - // color: Colors.blue, height: (loadProfile.description == null || loadProfile.description == '') ? 40.h diff --git a/lib/myprofile/widgets/position_in_flex_app_bar_myprofile.dart b/lib/myprofile/widgets/position_in_flex_app_bar_myprofile.dart index 72ea0305..3653773c 100644 --- a/lib/myprofile/widgets/position_in_flex_app_bar_myprofile.dart +++ b/lib/myprofile/widgets/position_in_flex_app_bar_myprofile.dart @@ -64,13 +64,11 @@ class PositionInFlexAppBarMyProfile extends StatelessWidget { color: Colors.white, fontSize: 15, fontWeight: FontWeight.bold), - // style: Theme.of(context).textTheme.headline6, ), )), const SizedBox( height: 10, ), - //username Text( loadProfile.displayName.toString(), style: const TextStyle( @@ -78,7 +76,6 @@ class PositionInFlexAppBarMyProfile extends StatelessWidget { fontWeight: FontWeight.bold, fontSize: 22), ), - // On Click Display all followers of users Container( padding: const EdgeInsets.all(0), alignment: Alignment.bottomLeft, @@ -97,7 +94,6 @@ class PositionInFlexAppBarMyProfile extends StatelessWidget { Text( '${loadProfile.followersCount} followers', style: const TextStyle( - //backgroundColor: Colors.orange, color: Colors.white, fontWeight: FontWeight.bold, fontSize: 13), diff --git a/lib/myprofile/widgets/position_myprofile_web.dart b/lib/myprofile/widgets/position_myprofile_web.dart index 2c59e5b4..b0a550a8 100644 --- a/lib/myprofile/widgets/position_myprofile_web.dart +++ b/lib/myprofile/widgets/position_myprofile_web.dart @@ -19,7 +19,6 @@ class PositionMyProfileWeb extends StatelessWidget { height: 100.h, child: Container( color: Colors.white, - // height: 50.h, padding: const EdgeInsets.only(left: 10, top: 5), child: Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -116,9 +115,6 @@ class PositionMyProfileWeb extends StatelessWidget { //followers Container( padding: const EdgeInsets.all(0), - // alignment: Alignment.bottomLeft, - //color: Colors.amber, - // margin: EdgeInsets.all(30), height: 7.5.h, width: 40.w, child: TextButton( @@ -126,7 +122,6 @@ class PositionMyProfileWeb extends StatelessWidget { .pushNamed(UserFollowersScreen.routeName), style: ButtonStyle( foregroundColor: MaterialStateProperty.all(Colors.black), - // backgroundColor: MaterialStateProperty.all(Colors.black), ), child: Column( mainAxisAlignment: MainAxisAlignment.start, @@ -146,9 +141,7 @@ class PositionMyProfileWeb extends StatelessWidget { Text( '${loadProfile.followersCount} ', style: const TextStyle( - //backgroundColor: Colors.orange, color: Colors.black, - //fontWeight: FontWeight.bold, fontSize: 13), textAlign: TextAlign.justify, ), diff --git a/lib/notification/models/notification_class_model.dart b/lib/notification/models/notification_class_model.dart index bab6e9e1..e481581a 100644 --- a/lib/notification/models/notification_class_model.dart +++ b/lib/notification/models/notification_class_model.dart @@ -34,38 +34,10 @@ class NotificationModel { this.followeruserName}); NotificationModel.fromJson(Map json) { - print(json); - // print('hh'); - // print(json['_id'].runtimeType); - // print('hh'); - // print(json['followedUser'].runtimeType); - // print('hh'); - // print(json['followedSubreddit'].runtimeType); - // print('hh'); - // print(json['followerUser'].runtimeType); - // print('hh'); - // //print(json['post']['_id'].runtimeType); - // print('hh'); - // print(json['comment']['_id'].runtimeType); - // print('hh'); - // print(json['createdAt'].runtimeType); - // print('hh'); - // print(json['seen'].runtimeType); - // print('hh'); - // print(json['hidden'].runtimeType); - // print(json['_id'].runtimeType); - // print(json['_id'].runtimeType); - // print(json['_id'].runtimeType); - // print(json['_id']); - print('idddddddd'); + sId = (json['_id'] == null) ? '' : json['_id'] as String; - print('hello'); type = json['type'] ?? ''; - print('hello'); - // print(json['followedUser'].runtimeType); - //print(json['followedUser']); if (json['followedSubreddit'] != null) { - print('in followeeeeeeeeeeeeeeeeedddddddddd'); requiredId = (json['followedSubreddit']['_id'] == null) ? '' : json['followedSubreddit']['_id'] as String; diff --git a/lib/notification/screens/notifications_main_screen.dart b/lib/notification/screens/notifications_main_screen.dart index a7ffb688..95c1440a 100644 --- a/lib/notification/screens/notifications_main_screen.dart +++ b/lib/notification/screens/notifications_main_screen.dart @@ -34,7 +34,6 @@ class _NotificationsMainScreenState extends State { List list = [ 'Hide this notification', - //'Disable updates from this community' ]; _markAsRead(NotificationModel index, context) async { @@ -60,8 +59,9 @@ class _NotificationsMainScreenState extends State { Navigator.popAndPushNamed(context, OthersProfileScreen.routeName, arguments: index.requiredName!.substring(2, index.requiredName!.length)); + } else { + Navigator.of(context).pushNamed(NavigateToCorrectScreen.routeName); } - Navigator.of(context).pushNamed(NavigateToCorrectScreen.routeName); } } @@ -116,6 +116,7 @@ class _NotificationsMainScreenState extends State { } _hideThisNotification(notificationId, i) async { + print('Hide notification'); await Provider.of(context, listen: false) .markAndHideThisNotification(context, notificationId, 'hide', i) .then((value) { @@ -193,7 +194,29 @@ class _NotificationsMainScreenState extends State { ]), ), ), - // if (kIsWeb) notificationBody(height, width), + if (kIsWeb) + if (!widget.usersNotificationToday.isEmpty) + const Text(' Today', + style: TextStyle( + fontSize: 16, fontWeight: FontWeight.bold)), + if (kIsWeb) + if (!widget.usersNotificationToday.isEmpty) + notificationBody( + height, width, widget.usersNotificationToday, 0), + if (kIsWeb) + if (!widget.usersNotificationEarlier.isEmpty) + SizedBox( + height: height * 0.02, + child: const Text( + ' Earlier', + style: TextStyle( + fontSize: 16, fontWeight: FontWeight.bold), + ), + ), + if (kIsWeb) + if (!widget.usersNotificationEarlier.isEmpty) + notificationBody( + height, width, widget.usersNotificationEarlier, 1), ]), ); } @@ -213,42 +236,53 @@ class _NotificationsMainScreenState extends State { color: (!usersAllNotificatiion[index].seen!) ? Colors.lightBlue[50] : Colors.white, - // width: double.infinity, + width: double.infinity, child: Row( children: [ - Column( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - Row( - children: [ - notificationMain( - returnCorrectText( - usersAllNotificatiion[index].type, - usersAllNotificatiion[index].requiredName, - usersAllNotificatiion[index].followeruserName, - ), - returnCorrectDescription( + Expanded( + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Flexible( + child: notificationMain( + returnCorrectText( + usersAllNotificatiion[index].type, + usersAllNotificatiion[index].requiredName, + usersAllNotificatiion[index] + .followeruserName, + ), + returnCorrectDescription( + usersAllNotificatiion[index].type, + usersAllNotificatiion[index] + .description, + usersAllNotificatiion[index] + .followeruserName), usersAllNotificatiion[index].type, - usersAllNotificatiion[index].description, - usersAllNotificatiion[index].requiredName), - usersAllNotificatiion[index].type, - getTimeOfNotification( - usersAllNotificatiion[index].createdAt), - NotificationImage( - usersAllNotificatiion: typeOfNotification[ - usersAllNotificatiion[index].type]!, - userPhoto: usersAllNotificatiion[index] - .followerIcon!, - height: height, - width: width), - width, - usersAllNotificatiion[index].commentId, - usersAllNotificatiion[index].postId)!, - ], - ), - ], + getTimeOfNotification( + usersAllNotificatiion[index].createdAt), + NotificationImage( + usersAllNotificatiion: + typeOfNotification[ + usersAllNotificatiion[index] + .type]!, + userPhoto: usersAllNotificatiion[index] + .followerIcon!, + height: height, + width: width), + width, + usersAllNotificatiion[index].commentId, + usersAllNotificatiion[index].postId)!, + ), + ], + ), + ], + ), ), - const Spacer(), + if (!kIsWeb) Spacer(), + //: (!kIsWeb) ? ThreeDotsWidget( optional: const BarWidget(), @@ -274,13 +308,6 @@ class _NotificationsMainScreenState extends State { onpressed: () => hideThisNotification( usersAllNotificatiion[index].sId, i), ), - // //only in community notifications this option is shown - // if (usersAllNotificatiion[index]['type'] == - // 'community') - // ListTileWidget( - // icon: Icons.cancel_outlined, - // title: - // 'Disable updates from this community'), SizedBox( height: height * 0.05, child: ElevatedButton( @@ -296,15 +323,12 @@ class _NotificationsMainScreenState extends State { ), ) ], - // height: (usersAllNotificatiion[index]['type'] == - // 'community') - // ? 31 - // : height: height * 0.025) : PopupMenuButton( - // elevation: -1, position: PopupMenuPosition.under, icon: const Icon(Icons.more_horiz), + onSelected: (value) => hideThisNotification( + usersAllNotificatiion[index].sId, i), itemBuilder: (context) { //return list.map((e) { return [ @@ -313,21 +337,14 @@ class _NotificationsMainScreenState extends State { list[0], style: const TextStyle(fontSize: 12), )), - // if (usersAllNotificatiion[index].type == - // 'community') - // PopupMenuItem( - // child: Text( - // list[1], - // style: const TextStyle(fontSize: 12), - // )), ]; }, ), - if (kIsWeb) const Divider(color: Colors.green), ], ), ), ), + if (kIsWeb) Divider(color: Colors.grey.shade100), ], ); }, diff --git a/lib/notification/screens/notifications_screen.dart b/lib/notification/screens/notifications_screen.dart index bf5644fd..a4cc0491 100644 --- a/lib/notification/screens/notifications_screen.dart +++ b/lib/notification/screens/notifications_screen.dart @@ -1,7 +1,8 @@ import 'package:flutter/material.dart'; import 'package:badges/badges.dart'; import 'package:get/get.dart'; -import '../../messages/Provider/message_provider.dart'; +import '../../messages/provider/message_provider.dart'; +import '../../messages/screens/web_message_screen.dart'; import '../models/notification_class_model.dart'; import 'package:provider/provider.dart'; @@ -29,11 +30,6 @@ class NotificationScreen extends StatefulWidget { } class _NotificationScreenState extends State { - // Icons when expansion - // dynamic icRecent = Icon(IconBroken.Arrow___Right_2); - // dynamic icModerating = Icon(IconBroken.Arrow___Right_2); - // dynamic icYourCommunities = Icon(IconBroken.Arrow___Right_2); - // bools final HomeController controller = Get.put( HomeController(), ); @@ -42,38 +38,6 @@ class _NotificationScreenState extends State { bool isOnline = true; bool hover = false; bool markAsRead = false; - //Keys - // var scaffoldKey = GlobalKey(); - // var formKey = GlobalKey(); - // var drawerKey = GlobalKey(); - //Model Reccent visited - //List recentlyVisited = [ - // //ListTile( - // leading: CircleAvatar( - // radius: 10, - // backgroundColor: Colors.blue, - // ), - // title: Text("r/" + "Cross_platform"), - // horizontalTitleGap: 0, - // ), - //ListTile( - // leading: CircleAvatar( - // radius: 10, - // backgroundColor: Colors.blue, - // ), - // title: Text("r/" + "Egypt"), - // horizontalTitleGap: 0, - // ), - //ListTile( - // leading: CircleAvatar( - // radius: 10, - // backgroundColor: Colors.blue, - // ), - // title: Text("r/" + "memes"), - // horizontalTitleGap: 0, - // ), - //]; - // drawer functions void showDrawer(BuildContext context) { Scaffold.of(context).openDrawer(); } @@ -82,48 +46,6 @@ class _NotificationScreenState extends State { Scaffold.of(context).openEndDrawer(); } - // List Communoties = [ - // ListTile( - // trailing: IconButton( - // onPressed: () {}, - // icon: Icon( - // IconBroken.Star, - // )), - // leading: CircleAvatar( - // radius: 10, - // backgroundColor: Colors.blue, - // ), - // title: Text("r/" + "Cross_platform"), - // horizontalTitleGap: 0, - // ), - // ListTile( - // trailing: IconButton( - // onPressed: () {}, - // icon: Icon( - // IconBroken.Star, - // )), - // leading: CircleAvatar( - // radius: 10, - // backgroundColor: Colors.blue, - // ), - // title: Text("r/" + "Egypt"), - // horizontalTitleGap: 0, - // ), - // ListTile( - // trailing: IconButton( - // onPressed: () {}, - // icon: Icon( - // IconBroken.Star, - // )), - // leading: CircleAvatar( - // radius: 10, - // backgroundColor: Colors.blue, - // ), - // title: Text("r/" + "memes"), - // horizontalTitleGap: 0, - // ), - // ]; - int unreadNotification = 0; bool returned = false; List usersNotificationEarlier = []; @@ -149,7 +71,6 @@ class _NotificationScreenState extends State { setState(() { returned = false; }); - // _updateCount(); await Provider.of(context, listen: false) .getNotification(context) .then((value) { @@ -181,7 +102,6 @@ class _NotificationScreenState extends State { markAllAsRead() { _markAsRead(); - print('hiiiiiiiiiiiii'); usersNotificationEarlier.forEach((element) { print(element.seen); if (!element.seen!) { @@ -191,14 +111,13 @@ class _NotificationScreenState extends State { } }); usersNotificationToday.forEach((element) { - print(element); if (!element.seen!) { setState(() { element.seen = true; }); } }); - Navigator.of(context).pop(); + if (!kIsWeb) Navigator.of(context).pop(); } _saveNewMessage(username, subject, message, messageShow) { @@ -211,9 +130,6 @@ class _NotificationScreenState extends State { @override Widget build(BuildContext context) { - //_updateCount(); - //final data = Provider.of(context,listen: false); - //var cubit =layoutCubit.get(context); MediaQueryData queryData = MediaQuery.of(context); final height = queryData.size.height; final width = queryData.size.width; @@ -327,7 +243,7 @@ class _NotificationScreenState extends State { usersNotificationEarlier: usersNotificationEarlier, usersNotificationToday: usersNotificationToday, changeNumOfNotification: _changeNumOfNotification), - const MessageMainScreen(), + MessageMainScreen(), ]), ), ) @@ -335,116 +251,112 @@ class _NotificationScreenState extends State { appBar: AppBar(title: Text('Hello')), body: Container( color: Colors.indigo[50], + height: MediaQuery.of(context).size.height, child: Container( width: double.infinity, - margin: EdgeInsets.only(left: 25.w, right: 25.w), - child: Center( - child: SingleChildScrollView( - child: Column( - // mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Divider( - color: Colors.transparent, - height: 6.h, - ), - const Text( - 'Notifications', - style: TextStyle( - fontWeight: FontWeight.bold, fontSize: 20), - ), - Divider( - color: Colors.transparent, - height: 4.h, - ), - Row( - children: [ - InkWell( - onTap: () { - Navigator.of(context) - .pushNamed(NotificationScreen.routeName); - }, - child: Container( - decoration: BoxDecoration( - border: Border( - bottom: BorderSide( - color: Colors.blue, width: 0.2.h))), - child: const Text( - 'Activity', - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.bold), - ), + margin: EdgeInsets.only(left: 20.w, right: 20.w), + child: SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Divider( + color: Colors.transparent, + height: 6.h, + ), + const Text( + 'Notifications', + style: TextStyle( + fontWeight: FontWeight.bold, fontSize: 20), + ), + Divider( + color: Colors.transparent, + height: 4.h, + ), + Row( + children: [ + InkWell( + onTap: () { + Navigator.of(context) + .pushNamed(NotificationScreen.routeName); + }, + child: Container( + decoration: BoxDecoration( + border: Border( + bottom: BorderSide( + color: Colors.blue, width: 0.2.h))), + child: const Text( + 'Activity', + style: TextStyle( + fontSize: 16, fontWeight: FontWeight.bold), ), ), - SizedBox( - width: 6.h, + ), + SizedBox( + width: 6.h, + ), + InkWell( + onTap: () { + Navigator.of(context) + .pushNamed(WebMessageScreen.routeName); + }, + onHover: (value) { + setState(() { + hover = value; + }); + }, + child: Text( + 'Messages', + style: TextStyle( + color: hover ? Colors.black : Colors.grey, + fontSize: 16, + fontWeight: FontWeight.bold), ), - InkWell( + ), + const Spacer( + flex: 1, + ), + InkWell( onTap: () { - Navigator.of(context) - .pushNamed(MessageMainScreen.routeName); + //TO DO MARK ALL AS READ + markAllAsRead(); }, onHover: (value) { setState(() { - hover = value; + markAsRead = value; }); }, - child: Text( - 'Messages', - style: TextStyle( - color: hover ? Colors.black : Colors.grey, - fontSize: 16, - fontWeight: FontWeight.bold), - ), - ), - const Spacer( - flex: 1, - ), - InkWell( - onTap: () { - //TO DO MARK ALL AS READ - }, - onHover: (value) { - setState(() { - markAsRead = value; - }); - }, - child: Row( - children: [ - Image.asset( - 'assets/images/envelope.png', - color: Colors.grey, - height: 3.h, - ), - Text( - 'Mark as read', - style: TextStyle( - color: markAsRead - ? Colors.black - : Colors.grey, - fontSize: 16, - fontWeight: FontWeight.bold), - ), - ], - )) - ], - ), - Divider( - color: Colors.transparent, - height: 4.h, - ), - Container( - color: Colors.white, - child: NotificationsMainScreen( - usersNotificationEarlier: - usersNotificationEarlier, - usersNotificationToday: usersNotificationToday, - changeNumOfNotification: - _changeNumOfNotification), - ), - ], - ), + child: Row( + children: [ + Image.asset( + 'assets/images/envelope.png', + color: Colors.grey, + height: 3.h, + ), + Text( + 'Mark as read', + style: TextStyle( + color: markAsRead + ? Colors.black + : Colors.grey, + fontSize: 16, + fontWeight: FontWeight.bold), + ), + ], + )) + ], + ), + Divider( + color: Colors.transparent, + height: 4.h, + ), + Container( + color: Colors.white, + child: NotificationsMainScreen( + usersNotificationEarlier: usersNotificationEarlier, + usersNotificationToday: usersNotificationToday, + changeNumOfNotification: _changeNumOfNotification), + ), + ], ), ), ), diff --git a/lib/notification/widgets/notification_image.dart b/lib/notification/widgets/notification_image.dart index 00c1f49b..df7bf7ba 100644 --- a/lib/notification/widgets/notification_image.dart +++ b/lib/notification/widgets/notification_image.dart @@ -34,13 +34,12 @@ class NotificationImage extends StatelessWidget { )), ), Positioned( - bottom: (kIsWeb) ? 2.h : height * 0.018, - right: (kIsWeb) ? 0.3.w : width * 0.005, + bottom: (kIsWeb) ? 0.1.h : height * 0.018, + right: (kIsWeb) ? 2.5.w : width * 0.005, child: CircleAvatar( backgroundColor: Colors.white, radius: 11, child: CircleAvatar( - //minRadius: 2, backgroundColor: Colors.white, backgroundImage: AssetImage(usersAllNotificatiion), radius: 7, diff --git a/lib/notification/widgets/notification_text.dart b/lib/notification/widgets/notification_text.dart index 0da60a6d..32fb0012 100644 --- a/lib/notification/widgets/notification_text.dart +++ b/lib/notification/widgets/notification_text.dart @@ -1,3 +1,4 @@ +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; class NotificationText extends StatelessWidget { @@ -27,11 +28,12 @@ class NotificationText extends StatelessWidget { Row( children: [ image, + if (!kIsWeb) + SizedBox( + width: width * 0.02, + ), SizedBox( - width: width * 0.02, - ), - SizedBox( - width: width * 0.7, + width: (kIsWeb) ?width *0.3 : width * 0.7, child: Column( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, @@ -58,8 +60,6 @@ class NotificationText extends StatelessWidget { style: const TextStyle( color: Colors.grey, fontSize: 10)) ]) - - //overflow: TextOverflow.clip, ), const Divider( height: 6, @@ -78,19 +78,6 @@ class NotificationText extends StatelessWidget { ], ), ), - - // const Icon( - // Icons.circle, - // color: Colors.grey, - // size: 4, - // ), - // SizedBox( - // width: width * 0.01, - // ), - // Text( - // time, - // style: const TextStyle(color: Colors.grey, fontSize: 10), - // ), ], ), ], diff --git a/lib/notification/widgets/reply_back.dart b/lib/notification/widgets/reply_back.dart index cc3cf976..77e083e7 100644 --- a/lib/notification/widgets/reply_back.dart +++ b/lib/notification/widgets/reply_back.dart @@ -16,7 +16,7 @@ ReplyBack({ return Padding( padding: const EdgeInsets.only(top: 2), child: SizedBox( - width: (kIsWeb) ? 30.w : 75.w, + width: (kIsWeb) ? 10.w : 75.w, height: 3.h, child: OutlinedButton( onPressed: () {}, diff --git a/lib/other_profile/models/moderated_subreddit_user_data.dart b/lib/other_profile/models/moderated_subreddit_user_data.dart index dcbfe931..05179295 100644 --- a/lib/other_profile/models/moderated_subreddit_user_data.dart +++ b/lib/other_profile/models/moderated_subreddit_user_data.dart @@ -4,7 +4,8 @@ class ModeratedSubbredditUserData { ModeratedSubbredditUserData( {required this.icon, required this.subredditName, }); - + // ===================================this function used to===========================================// +//=================to change json to model===========================// ModeratedSubbredditUserData.fromJson(Map json) { icon = json['icon'].toString(); subredditName = json['fixedName'].toString(); diff --git a/lib/other_profile/models/others_profile_data.dart b/lib/other_profile/models/others_profile_data.dart index 774b0256..48827d16 100644 --- a/lib/other_profile/models/others_profile_data.dart +++ b/lib/other_profile/models/others_profile_data.dart @@ -1,62 +1,30 @@ class OtherProfileData { - //String? id; String? userName; String? email; String? profilePicture; String? profileBackPicture; String? description; String? displayName; - // DateTime? toDayTime; String? createdAt; - //int? numOfDaysInReddit; int? followersCount; int? postKarma; int? commentkarma; bool isFollowed = false; OtherProfileData( - { - //required this.id, - required this.userName, + {required this.userName, required this.email, required this.profilePicture, required this.profileBackPicture, required this.description, required this.displayName, required this.createdAt, - //required this.numOfDaysInReddit, required this.followersCount, required this.postKarma, required this.commentkarma, required this.isFollowed}); - -// { -// "id": 10, -// "userName": "Zeinab_maoawad", -// "email": "user@email.com", -// "profilePicture": "https://militaryhealthinstitute.org/wp-content/uploads/sites/37/2019/10/blank-person-icon-9.jpg", -// "profileBackPicture": "https://preview.redd.it/vqqv5xbfezp91.jpg?width=4096&format=pjpg&auto=webp&s=54acda24af01e2de60e98603e3e29e8db381ebac", -// "description": "I'm Student", -// "createdAt": "2022-11-09T00:19:45.186+00:00", -// "followersCount": 0, -// "numOfDaysInReddit": 0, -// "displayName": "Zeinab_maoawad", -// "postKarma": 1, -// "commentkarma": 1, -// "isFollowed": true -// } + // ===================================this function used to===========================================// +//=================to change json to model===========================// OtherProfileData.fromJson(Map json) { - // print(json['id'].runtimeType); - // print(json['userName'].runtimeType); - // print(json['email'].runtimeType); - // print(json['profilePicture'].runtimeType); - // print(json['profileBackground'].runtimeType); - // print(json['followersCount'].runtimeType); - // print(json['createdAt'].runtimeType); - // print(json['postKarma'].runtimeType); - // print(json['commentKarma'].runtimeType); - // print(json['description'].runtimeType); - // print(json['isFollowed'].runtimeType); - //id = json['id'].toString(); userName = json['userName']; email = json['email']; profilePicture = json['profilePicture']; @@ -69,34 +37,4 @@ class OtherProfileData { commentkarma = int.parse(json['commentKarma'].toString()); isFollowed = json['isFollowed']; } - - Map toJson() { - final Map data = new Map(); - //data['id'] = this.id; - data['userName'] = this.userName; - data['email'] = this.email; - data['profilePicture'] = this.profilePicture; - data['profileBackPicture'] = this.profileBackPicture; - data['description'] = this.description; - data['toDayTime'] = this.createdAt; - data['followersCount'] = this.followersCount; - data['toDayTime'] = this.createdAt; - //data['numOfDaysInReddit'] = this.numOfDaysInReddit; - data['postKarma'] = this.postKarma; - data['displayName'] = this.displayName; - data['commentkarma'] = this.commentkarma; - data['isFollowed'] = this.isFollowed; - data['createdAt'] = this.createdAt; - return data; - } } - -// { -// "contentvisibility": true, -// "canbeFollowed": true, -// "lastUpdatedPassword": "2022-10-22-06-12", -// "friendsCount": 0, -// "accountActivated": true, -// "gender": "male", -// "karma": 1 -// } \ No newline at end of file diff --git a/lib/other_profile/providers/other_profile_provider.dart b/lib/other_profile/providers/other_profile_provider.dart index 9c399c03..41df49c1 100644 --- a/lib/other_profile/providers/other_profile_provider.dart +++ b/lib/other_profile/providers/other_profile_provider.dart @@ -17,7 +17,9 @@ class OtherProfileprovider with ChangeNotifier { List? get gettingModeratedSubreddit { return moderatedSubbredditUserData; } - + // ===================================this function used to===========================================// +//==================fetch and set date===========================// +//moderatedSubredditUserName==> userName Of Subreddit Future fetchAndSetOtherProfile( String otherUserName, BuildContext context) async { try { @@ -34,7 +36,8 @@ class OtherProfileprovider with ChangeNotifier { HandleError.handleError(error.toString(), context); } } - + // ===================================this function used to===========================================// +//==================fetch and set date moderated Subreddit of user===========================// Future fetchAndSetModeratedSubredditUser(BuildContext context) async { try { final prefs = await SharedPreferences.getInstance(); @@ -55,7 +58,10 @@ class OtherProfileprovider with ChangeNotifier { HandleError.handleError(error.toString(), context); } } - + // ===================================this function used to===========================================// +//==================invite otherusers to my moderated subreddit===========================// +//subredditName=> userName Of Subreddit +//userName=> user who i invite Future invitation( String subredditName, String userName, BuildContext context) async { try { @@ -83,7 +89,9 @@ class OtherProfileprovider with ChangeNotifier { HandleError.handleError(error.toString(), context); return false; } - } + } // ===================================this function used to===========================================// +//==================block otherusers===========================// +//userName=> user who i block Future blockUser(String userName, BuildContext context) async { try { final prefs = await SharedPreferences.getInstance(); @@ -99,7 +107,10 @@ class OtherProfileprovider with ChangeNotifier { return false; } } - + // ===================================the next two function used to===========================================// +//==================follow and unfollow otherusers===========================// +//subredditName=> userName Of Subreddit +//userName=> user who i follow/unfollow Future followUser(String userName, BuildContext context) async { try { final prefs = await SharedPreferences.getInstance(); diff --git a/lib/other_profile/screens/others_profile_screen.dart b/lib/other_profile/screens/others_profile_screen.dart index c287f6af..b6e3bbea 100644 --- a/lib/other_profile/screens/others_profile_screen.dart +++ b/lib/other_profile/screens/others_profile_screen.dart @@ -55,12 +55,11 @@ class _OthersProfileScreenState extends State _controller!.dispose(); super.dispose(); } - + // ===================================this function used to===========================================// +//==================fetch date for first time===========================// @override void didChangeDependencies() { // TODO: implement didChangeDependencies - //===============================doing fetch=======================================// - if (_isInit) { setState(() { _isLoading = true; diff --git a/lib/other_profile/widgets/invite_button.dart b/lib/other_profile/widgets/invite_button.dart index 5be4adcb..41312f2a 100644 --- a/lib/other_profile/widgets/invite_button.dart +++ b/lib/other_profile/widgets/invite_button.dart @@ -25,11 +25,9 @@ class InviteButtonState extends State { void initState() { // TODO: implement initState super.initState(); - // textMessage = ''; - // // textMessage = 'Message ${widget.userName}'; - // message = TextEditingController(); } - + // ===================================this function used to===========================================// +//==================fetch date for Moderated Subreddit For User===========================// @override void didChangeDependencies() { // TODO: implement didChangeDependencies @@ -38,7 +36,6 @@ class InviteButtonState extends State { setState(() { _isLoading = true; }); - // print('12 '); Provider.of(context, listen: false) .fetchAndSetModeratedSubredditUser(context) .then((value) { @@ -50,8 +47,6 @@ class InviteButtonState extends State { }); } _isInit = false; - - //==================================================// super.didChangeDependencies(); } @@ -170,19 +165,16 @@ class InviteButtonState extends State { SizedBox( width: 50.w, ), - Icon( + const Icon( Icons.reddit_outlined, color: Colors.deepOrange, size: 50, ), Container( - // color: Colors.amber, height: 6.h, width: 30.w, child: ElevatedButton( style: ButtonStyle( - //shape: Outlin, - side: MaterialStateProperty.all( const BorderSide(color: Colors.white)), shape: MaterialStateProperty.all( @@ -223,7 +215,8 @@ class InviteButtonState extends State { }, ); } - + // ===================================this function used to===========================================// +//==================To Invit Other Users===========================// Future invite(BuildContext context) async { bool invite = await Provider.of(context, listen: false) @@ -238,7 +231,7 @@ class InviteButtonState extends State { } else { ScaffoldMessenger.of(context).showSnackBar( CustomSnackBar( - isError: true, text:'Invitation Faild' , disableStatus: true), + isError: true, text:'Invitation Failed' , disableStatus: true), ); } return false; diff --git a/lib/other_profile/widgets/options_button.dart b/lib/other_profile/widgets/options_button.dart deleted file mode 100644 index 4e8ad010..00000000 --- a/lib/other_profile/widgets/options_button.dart +++ /dev/null @@ -1,96 +0,0 @@ -// import 'package:flutter/material.dart'; -// //import 'package:flutter_code_style/analysis_options.yaml'; -// import 'package:responsive_sizer/responsive_sizer.dart'; -// import './other_profile_card_information_web.dart'; - -// class OptionsButton extends StatefulWidget { -// bool moreOptions; -// OptionsButton({ -// Key? key, -// required this.moreOptions, -// }) : super(key: key); -// @override -// State createState() => _OptionsButtonState(); -// } - -// class _OptionsButtonState extends State { -// void initState() { -// //moreOptions = false; -// super.initState(); -// } - -// @override -// Widget build(BuildContext context) { -// return Positioned( -// top: 340, -// child: Column( -// children: [ -// Visibility( -// visible:OtherProfileCardInformationWeb.moreOptions, -// child: Container( -// child: Column( -// children: [ -// TextButton( -// onPressed: null, -// child: Text( -// 'Send Message', -// style: TextStyle( -// color: Colors.blue, -// fontWeight: FontWeight.bold, -// fontSize: 15), -// )), -// TextButton( -// onPressed: null, -// child: Text( -// 'Block User', -// style: TextStyle( -// color: Colors.blue, -// fontWeight: FontWeight.bold, -// fontSize: 15), -// )), -// TextButton( -// onPressed: null, -// child: Text( -// 'Get Them Help and Support', -// style: TextStyle( -// color: Colors.blue, -// fontWeight: FontWeight.bold, -// fontSize: 15), -// )), -// TextButton( -// onPressed: null, -// child: Text( -// 'Report User', -// style: TextStyle( -// color: Colors.blue, -// fontWeight: FontWeight.bold, -// fontSize: 15), -// )) -// ], -// )), -// ), -// Container( -// width: 15.w, -// height: 6.h, -// child: TextButton( -// onPressed: () { -// setState(() { -// OtherProfileCardInformationWeb.moreOptions= !OtherProfileCardInformationWeb.moreOptions; -// }); -// }, -// style: ButtonStyle( -// // backgroundColor: MaterialStateProperty.all(Colors.blue) -// ), -// child: Text( -// OtherProfileCardInformationWeb.moreOptions? 'More Options' : 'Less Options', -// style: TextStyle( -// color: Colors.blue, -// fontWeight: FontWeight.bold, -// fontSize: 15), -// ), -// )), -// ], -// )); - -// } -// } diff --git a/lib/other_profile/widgets/other_profile_app.dart b/lib/other_profile/widgets/other_profile_app.dart index c9656885..5f7bd595 100644 --- a/lib/other_profile/widgets/other_profile_app.dart +++ b/lib/other_profile/widgets/other_profile_app.dart @@ -39,15 +39,10 @@ class OtherProfileApp extends StatelessWidget { foregroundColor: Colors.white, elevation: 4, backgroundColor:Colors.blue, - // innerBoxIsScrolled?Colors.blue:Colors.white, title: - // Visibility( - // visible: innerBoxIsScrolled, - // child: Text('u/${loadProfile.displayName}', style: const TextStyle( color: Colors.white, fontWeight: FontWeight.bold)), - //), expandedHeight: (loadProfile.description == null || loadProfile.description == '') ? 54.h @@ -74,7 +69,6 @@ class OtherProfileApp extends StatelessWidget { children: [ //Profile back ground Container( - // color: Colors.blue, height: (loadProfile.description == null || loadProfile.description == '') ? 56.h diff --git a/lib/other_profile/widgets/other_profile_card_information_web.dart b/lib/other_profile/widgets/other_profile_card_information_web.dart index 36d44c56..a2147ba1 100644 --- a/lib/other_profile/widgets/other_profile_card_information_web.dart +++ b/lib/other_profile/widgets/other_profile_card_information_web.dart @@ -28,8 +28,8 @@ class _OtherProfileCardInformationWebState return Expanded( child: Container( margin: const EdgeInsets.only(right: 180, bottom: 25, top: 30), - width: 60.w, - height: widget.moreOptions ? 65.h : 50.h, + width: 50.w, + height: widget.moreOptions ? 80.h : 70.h, color: Colors.white, child: Column(children: [ Stack( @@ -59,10 +59,7 @@ class _OtherProfileCardInformationWebState ), //tomake widget position PositionOtherProfileWeb(loadProfile: widget.loadProfile), - // OptionsButton( - // // moreOptions: OtherProfileCardInformationWeb.moreOptions, - // ), - Positioned( + Positioned( top: 340, child: Column( children: [ @@ -83,7 +80,7 @@ class _OtherProfileCardInformationWebState fontSize: 15), )), TextButton( - onPressed: ()=> _showLeaveDialog(), + onPressed: ()=> _showBlockDialog(), child: const Text( 'Block User', style: TextStyle( @@ -140,8 +137,10 @@ class _OtherProfileCardInformationWebState ]), )); } - - void _showLeaveDialog() { + // ===================================the next three function used to===========================================// +//==================Block user===========================// +// have two option one:blocksubreddit two: cancel + void _showBlockDialog() { showDialog( context: context, builder: (ctx) => AlertDialog( @@ -186,7 +185,7 @@ class _OtherProfileCardInformationWebState ), ), Container( - width: 35.w, + width: 15.w, height: 6.h, child: ElevatedButton( style: ButtonStyle( @@ -206,13 +205,13 @@ class _OtherProfileCardInformationWebState if (!block) { ScaffoldMessenger.of(context).showSnackBar(CustomSnackBar( isError: false, - text: 'Invitation Successfully', + text: 'Block Successfully', disableStatus: true)); } else { ScaffoldMessenger.of(context).showSnackBar( CustomSnackBar( isError: false, - text: 'Invitation Successfully', + text: 'Block Failed', disableStatus: true), ); } diff --git a/lib/other_profile/widgets/others_profile_about.dart b/lib/other_profile/widgets/others_profile_about.dart index 8887951b..8c80d83c 100644 --- a/lib/other_profile/widgets/others_profile_about.dart +++ b/lib/other_profile/widgets/others_profile_about.dart @@ -6,14 +6,8 @@ class OthersProfileAbout extends StatelessWidget { final int numOfPosts; final int numOfComments; - - // final int numOfAwarder; - - // final int numOfAwardee; final String description; - OthersProfileAbout(this.numOfPosts, this.numOfComments, - // this.numOfAwarder, - // this.numOfAwardee, + OthersProfileAbout(this.numOfPosts, this.numOfComments, this.description); @override Widget build(BuildContext context) { @@ -22,7 +16,6 @@ class OthersProfileAbout extends StatelessWidget { children: [ Container( padding: const EdgeInsets.only(top: 120), - // height: 32.h, height: (description == null||description == '') ? 36.h : (36 + (description.length / 42) + 4).h, @@ -51,28 +44,8 @@ class OthersProfileAbout extends StatelessWidget { ), ], )), - // Expanded( - // child: Row( - // children: [ - // Expanded( - // child: ListTile( - // title: Text('$numOfAwarder'), - // subtitle: const Text('Awarder Karma'), - // ), - // ), - // Expanded( - // child: ListTile( - // title: Text('$numOfAwardee'), - // subtitle: const Text('Awardee Karma'), - // ), - // ) - // ], - // ), - // ), Container( - //color: Colors.lightBlue, - // padding: EdgeInsets.all(20), - margin: EdgeInsets.only(right: 15,left: 15,top: 15), + margin: const EdgeInsets.only(right: 15,left: 15,top: 15), width: 100.w, height: (description == null||description == '') ? 0.h @@ -89,7 +62,7 @@ class OthersProfileAbout extends StatelessWidget { ]), ), ListTile( - leading: Icon( + leading: const Icon( size: 25, Icons.local_post_office_outlined, color: Colors.grey, @@ -106,39 +79,6 @@ class OthersProfileAbout extends StatelessWidget { ), ]), ), - // Container( - // padding: const EdgeInsets.all(10), - // // height: MediaQuery.of(context).size.height * 0.05, - // // width: MediaQuery.of(context).size.height * 1, - // height: 5.h, - // width: 100.h, - // child: const Text( - // 'TROPHIES', - // textAlign: TextAlign.start, - // style: TextStyle(color: Color.fromARGB(255, 134, 133, 133)), - // )), - // Container( - // padding: const EdgeInsets.only(bottom: 10, top: 100), - // height: 100.h, - // width: 100.h, - // color: Colors.white, - // child: Column(children: [ - // Expanded( - // child: Row( - // children: const [ - // Expanded( - // child: ListTile( - // title: Text(''), - // subtitle: Text( - // '', - // ), - // ), - // ), - // ], - // ), - // ) - // ]), - // ), ], ); } diff --git a/lib/other_profile/widgets/overview_other_profile_web.dart b/lib/other_profile/widgets/overview_other_profile_web.dart index 8dcb6d34..fe92e0d0 100644 --- a/lib/other_profile/widgets/overview_other_profile_web.dart +++ b/lib/other_profile/widgets/overview_other_profile_web.dart @@ -1,80 +1,168 @@ import 'package:flutter/material.dart'; import 'package:responsive_sizer/responsive_sizer.dart'; +import 'package:provider/provider.dart'; import '../../widgets/sort_bottom_web.dart'; import '../models/others_profile_data.dart'; import '../widgets/other_profile_card_information_web.dart'; +import '../../providers/profile_provider.dart'; +import '../../widgets/loading_reddit.dart'; +import '../../widgets/sort_bottom_web.dart'; +import '../../post/widgets/post_comment_list.dart'; class OverviewOtherProfileWeb extends StatefulWidget { final OtherProfileData loadProfile; - final ScrollController scrollController; - OverviewOtherProfileWeb({ + final ScrollController scrollController; + OverviewOtherProfileWeb({ Key? key, - required this.scrollController, + required this.scrollController, required this.loadProfile, }) : super(key: key); // Posts(this.routeNamePop); @override - State createState() => _OverviewOtherProfileWebState(); + State createState() => + _OverviewOtherProfileWebState(); } class _OverviewOtherProfileWebState extends State { + int _page = 1; + final int _limit = 25; + bool _isInit = true; + bool _isLoading = false; + bool _isLoadMoreRunning = false; + List>? commentsAndPosts; + String dateOfcomment(String date) { + final data1 = DateTime.parse(date); + final date2 = DateTime.now(); + final difference = date2.difference(data1); + final differenceMonth = date2.month - data1.month; + final differenceYear = date2.year - data1.year; + + if (difference.inHours < 24) { + return '${difference.inHours}h'; + } else if (difference.inDays < 30) { + final numOfWeeks = difference.inDays ~/ 7; + if (numOfWeeks > 0) { + return '${numOfWeeks}w'; + } else { + return '${difference.inDays}d'; + } + } else if (differenceYear > 0) { + return '${differenceYear}y'; + } else { + return '${differenceMonth}mon'; + } + } + + ScrollController _scrollController = new ScrollController(); + void _loadMore() { + if (_isLoading == false && _isLoadMoreRunning == false) { + setState(() { + toggleLoadingMore(); // Display a progress indicator at the bottom + }); + setState(() { + _page += 1; + }); + // Increase _page by + + setState(() { + toggleLoadingMore(); + }); + } + } + + bool toggleLoadingMore() => _isLoadMoreRunning = !_isLoadMoreRunning; + + @override + void initState() { + // TODO: implement initState + super.initState(); + _scrollController = ScrollController()..addListener(_loadMore); + } + // ===================================this function used to===========================================// +//==================fetch date for first time===========================// + @override + Future didChangeDependencies() async { + // TODO: implement didChangeDependencies + if (_isInit) { + setState(() { + _isLoading = true; + }); + //New Hot Top + await Provider.of(context, listen: false) + .fetchandSetProfilePostsAndComments( + widget.loadProfile.userName.toString(), + 'New', + _page, + _limit, + context) + .then((value) async { + commentsAndPosts = + await Provider.of(context, listen: false) + .gettingPostCommentData; + setState(() { + _isLoading = false; + }); + }); + } + _isInit = false; + super.didChangeDependencies(); + } + + @override + void dispose() { + _scrollController.dispose(); + super.dispose(); + } + @override Widget build(BuildContext context) { - return ListView( - scrollDirection: Axis.vertical, - controller: widget.scrollController, - children: [ - Row( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Expanded( - flex: 2, - child: Column( - children: [ - // SizedBox(height: 4.h,), - Container( - height: 6.h, - width: 50.w, - margin: EdgeInsets.only(left: 100, bottom: 10, top: 30), - child: SortBottomWeb(page: 1 ,userName: widget.loadProfile.userName.toString()), - color: Colors.white, - // width: 100.w, - ), - Container( - // padding: const EdgeInsets.only(bottom: 100,), - margin: EdgeInsets.only(left: 100, bottom: 90, top: 30), - height: 30.h, - width: 50.w, - // color: Colors.white, - decoration: BoxDecoration( - color: Colors.white, - border: Border.all( - color: Colors.blue, - width: 3, - )), - child: Column(children: [ - Expanded( - child: Row( - children: const [ - Expanded( - child: ListTile( - title: Text('Post'), - ), - ), - ], - ), - ) - ]), + commentsAndPosts = Provider.of(context, listen: true) + .gettingPostCommentData; + return (_isLoading || _isLoadMoreRunning) + ? const LoadingReddit() + : (commentsAndPosts == null || commentsAndPosts!.isEmpty) + ? Center( + child: Column( + children: [ + SizedBox( + height: 30.h, + ), + const Icon( + Icons.reddit, + size: 100, + color: Colors.black, + ), + const Text( + 'Wow,such empty', + style: TextStyle(color: Colors.grey), + ) + ], + ), + ) + : PostCommentList( + leftMargin: 15.w, + rightMargin: 35.w, + topOfTheList: Container( + height: 80.h, + width: 50.h, + margin: EdgeInsets.only(left: 15.w), + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + OtherProfileCardInformationWeb( + loadProfile: widget.loadProfile), + SortBottomWeb( + page: 1, + userName: widget.loadProfile.userName.toString()), + ], ), - ], - ), - ), - OtherProfileCardInformationWeb(loadProfile: widget.loadProfile), - ], - ), - ], - ); + ), + userName: widget.loadProfile.userName.toString(), + updateData: _loadMore, + data: commentsAndPosts as List>, + type: 'Profile', + ); } } diff --git a/lib/other_profile/widgets/pop_down_menu.dart b/lib/other_profile/widgets/pop_down_menu.dart index a57e5f6d..5fe3a1c1 100644 --- a/lib/other_profile/widgets/pop_down_menu.dart +++ b/lib/other_profile/widgets/pop_down_menu.dart @@ -71,7 +71,7 @@ class _PopDownMenuState extends State { color: Colors.black, ), onTap: () { - _showLeaveDialog(); + _showBlockDialog(); }, title: const Text( 'Block account', @@ -94,8 +94,10 @@ class _PopDownMenuState extends State { ), ]); } - - void _showLeaveDialog() { + // ===================================the next three function used to===========================================// +//==================Block user===========================// +// have two option one:blocksubreddit two: cancel + void _showBlockDialog() { showDialog( context: context, builder: (ctx) => AlertDialog( diff --git a/lib/other_profile/widgets/position_other_profile_web.dart b/lib/other_profile/widgets/position_other_profile_web.dart index 912d7445..deccd4b6 100644 --- a/lib/other_profile/widgets/position_other_profile_web.dart +++ b/lib/other_profile/widgets/position_other_profile_web.dart @@ -3,6 +3,7 @@ import 'package:responsive_sizer/responsive_sizer.dart'; import '../models/others_profile_data.dart'; import 'package:provider/provider.dart'; import '../providers/other_profile_provider.dart'; +import 'package:intl/intl.dart'; import '../../widgets/custom_snack_bar.dart'; class PositionOtherProfileWeb extends StatefulWidget { PositionOtherProfileWeb({ @@ -14,13 +15,14 @@ class PositionOtherProfileWeb extends StatefulWidget { @override State createState() => - _PositionOtherProfileWebState(); + PositionOtherProfileWebState(); } -class _PositionOtherProfileWebState extends State { +class PositionOtherProfileWebState extends State { bool moreOptions = false; bool isFollowedstate = false; - + // ===================================the next two function used to===========================================// +//==================To follow users===========================// void _follow() async { bool follow = await Provider.of(context, listen: false) @@ -45,7 +47,8 @@ class _PositionOtherProfileWebState extends State { isFollowedstate = true; return isFollowedstate; } - + // ===================================the next two function used to===========================================// +//==================To unfollow users===========================// void _unFollow() async { bool unfollow = await Provider.of(context, listen: false) @@ -65,7 +68,6 @@ class _PositionOtherProfileWebState extends State { ); } } - bool unFollowsucceeded() { isFollowedstate = false; return isFollowedstate; @@ -82,11 +84,9 @@ class _PositionOtherProfileWebState extends State { top: 100, height: 100.h, child: Container( - // width: 100.w, height: 100.h, - - padding: EdgeInsets.only(left: 20, top: 15), - margin: EdgeInsets.only(top: 10), + padding: const EdgeInsets.only(left: 20, top: 15), + margin: const EdgeInsets.only(top: 10), color: Colors.white, child: Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -109,7 +109,6 @@ class _PositionOtherProfileWebState extends State { ), //name and discibtions Text( - // ${loadProfile.numOfDaysInReddit} .${int.parse(loadProfile.postKarma.toString()) + int.parse(loadProfile.commentkarma.toString())}.${loadProfile.createdAt.toString()} 'u/${widget.loadProfile.displayName}', style: const TextStyle( color: Colors.black, @@ -138,18 +137,17 @@ class _PositionOtherProfileWebState extends State { height: 7, ), Row( - //mainAxisAlignment: MainAxisAlignment, children: [ Column( children: [ - Text('Karma', - style: const TextStyle( + const Text('Karma', + style: TextStyle( color: Colors.black, fontWeight: FontWeight.bold, fontSize: 13)), Row( children: [ - Icon( + const Icon( Icons.settings, color: Colors.blue, ), @@ -168,18 +166,18 @@ class _PositionOtherProfileWebState extends State { ), Column( children: [ - Text('CakeDay', - style: const TextStyle( + const Text('CakeDay', + style: TextStyle( color: Colors.black, fontWeight: FontWeight.bold, fontSize: 13)), Row( children: [ - Icon( + const Icon( Icons.cake, color: Colors.blue, ), - Text('${widget.loadProfile.createdAt.toString()}', + Text('${DateFormat.yMMMMd('en_US').format(DateTime.parse(widget.loadProfile.createdAt.toString()))}', style: const TextStyle( color: Colors.black, fontWeight: FontWeight.normal, @@ -200,9 +198,6 @@ class _PositionOtherProfileWebState extends State { child: ElevatedButton( onPressed:()=> (isFollowedstate) ? _unFollow() : _follow(), style: ButtonStyle( - // side: MaterialStateProperty.all( - // const BorderSide( - // color: Colors.white)), shape: MaterialStateProperty.all( const RoundedRectangleBorder( borderRadius: @@ -211,54 +206,12 @@ class _PositionOtherProfileWebState extends State { foregroundColor: MaterialStateProperty.all(Colors.white)), child: Text( isFollowedstate ? 'Following' : 'Follow', - style: TextStyle( + style: const TextStyle( color: Colors.white, fontWeight: FontWeight.bold, fontSize: 15), ), )), - // Visibility( - // visible: moreOptions, - // child: Container( - // child: Column( - // children: [ - // TextButton(onPressed: null, child: Text('Send Message')), - // TextButton(onPressed: null, child: Text('Block User')), - // TextButton( - // onPressed: null, - // child: Text('Get Them Help and Support')), - // TextButton(onPressed: null, child: Text('Report User')) - // ], - // )), - // ), - // Container( - // width: 15.w, - // height: 6.h, - // child: TextButton( - // onPressed: () { - // setState(() { - // moreOptions = true; - // }); - // }, - // style: ButtonStyle( - // // side: MaterialStateProperty.all( - // // const BorderSide( - // // color: Colors.white)), - // shape: MaterialStateProperty.all( - // const RoundedRectangleBorder( - // borderRadius: - // BorderRadius.all(Radius.circular(30)))), - // backgroundColor: MaterialStateProperty.all(Colors.blue), - // foregroundColor: MaterialStateProperty.all(Colors.white)), - // child: Text( - // moreOptions ? 'More Options' : 'Less Options', - // style: TextStyle( - // color: Colors.white, - // fontWeight: FontWeight.bold, - // fontSize: 15), - // ), - // )), - const SizedBox( height: 7, ) diff --git a/lib/post/screens/post_screen.dart b/lib/post/screens/post_screen.dart index 9656a2db..e4fe0d2c 100644 --- a/lib/post/screens/post_screen.dart +++ b/lib/post/screens/post_screen.dart @@ -1,10 +1,13 @@ //import 'package:animated_tree_view/animated_tree_view.dart'; import 'package:flutter/material.dart'; -import 'package:post/comments/models/comment_model.dart'; -import 'package:post/comments/providers/comments_provider.dart'; +import 'package:post/post/widgets/post_list.dart'; +import 'package:post/widgets/loading_reddit.dart'; import 'package:provider/provider.dart'; +import 'package:responsive_sizer/responsive_sizer.dart'; import 'package:video_player/video_player.dart'; import '../../comments/widgets/comment.dart'; +import '../../providers/profile_provider.dart'; +import '../models/post_model.dart'; class PostScreen extends StatefulWidget { static const routeName = '/post'; @@ -23,20 +26,19 @@ class _PostScreenState extends State { late VideoPlayerController controller1; bool _isInit = true; bool _isLoading = false; - List? commentsData = []; + List? posts = []; @override void didChangeDependencies() { - Provider test; if (_isInit) { setState(() { _isLoading = true; }); - Provider.of(context, listen: false) - .fetchPostComments('s', 1, 2) + Provider.of(context, listen: false) + .fetchProfilePosts('Amr', 'Hot', 1, 25, context) .then((value) { - commentsData = Provider.of(context, listen: false) - .gettingPostComments; + posts = Provider.of(context, listen: false) + .gettingProfilePostData; setState(() { _isLoading = false; }); @@ -97,8 +99,10 @@ class _PostScreenState extends State { @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar(), - body: SingleChildScrollView( + appBar: AppBar(), + body: Center( + child: Container( + width: 40.w, child: Column( children: [ // Post.home( @@ -106,10 +110,14 @@ class _PostScreenState extends State { // inView: false, // userName: 'Amr', // ) - Comment( - data: commentsData![0], - userName: 'Amr', - ), + _isLoading + ? LoadingReddit() + : Container( + child: PostList( + userName: 'Amr', + updateData: updateData, + data: posts as List), + ) // Comment( // data: commentsData![2], // userName: 'Amr', @@ -180,6 +188,8 @@ class _PostScreenState extends State { // ), ], ), - )); + ), + ), + ); } } diff --git a/lib/post/widgets/post.dart b/lib/post/widgets/post.dart index 898e60d4..ca3bc888 100644 --- a/lib/post/widgets/post.dart +++ b/lib/post/widgets/post.dart @@ -137,6 +137,7 @@ class _PostState extends State { data: data, ), PostFooter( + userName: widget.userName, inScreen: widget.inScreen, data: widget.data, isMyPost: (widget.data.author?.name == widget.userName), diff --git a/lib/post/widgets/post_body.dart b/lib/post/widgets/post_body.dart index 6084c7d5..160a797f 100644 --- a/lib/post/widgets/post_body.dart +++ b/lib/post/widgets/post_body.dart @@ -2,11 +2,18 @@ import 'package:flutter/material.dart'; import 'package:post/post/models/post_model.dart'; import 'package:post/post/widgets/post_card.dart'; import 'package:post/post/widgets/post_images.dart'; +import 'package:post/post/widgets/post_images_web.dart'; +import 'package:post/post/widgets/post_link_in_screen.dart'; +import 'package:post/post/widgets/post_pop_up_web.dart'; import 'package:post/post/widgets/post_tags_and_title.dart'; +import 'package:post/post/widgets/post_video_in_widget_web.dart'; import 'package:video_player/video_player.dart'; import '../../show_post/screens/show_post.dart'; +import '../../show_post/screens/show_post_web.dart'; +import '../../widgets/loading_reddit.dart'; import 'post_video_in_widget.dart'; +import 'package:flutter/foundation.dart' show kIsWeb; /// This Widget is responsible for the body of the post. @@ -29,10 +36,23 @@ class _PostBodyState extends State { @override Widget build(BuildContext context) { return Flexible( - child: GestureDetector( + child: InkWell( onTap: () { - Navigator.of(context).pushNamed(ShowPostDetails.routeName, - arguments: {'data': widget.data, 'userName': widget.userName}); + kIsWeb + ? Navigator.of(context).pushNamed(ShowPostDetailsWeb.routeName, + arguments: {'data': widget.data, 'userName': widget.userName}) + // ? showDialog( + // context: context, + // builder: (context) => PostPopUpWeb( + // data: widget.data, + // userName: widget.userName, + // ), + // ) + : Navigator.of(context).pushNamed(ShowPostDetails.routeName, + arguments: { + 'data': widget.data, + 'userName': widget.userName + }); }, child: Container( color: (widget.data.isSpam ?? false) @@ -54,55 +74,86 @@ class _PostBodyState extends State { title: widget.data.title as String, ), ), - - // Container( - // padding: const EdgeInsetsDirectional.only( - // start: 10, end: 10, top: 10), - // child: Text( - // widget.data.text as String, - // maxLines: 3, - // style: TextStyle( - // color: Theme.of(context).colorScheme.secondary, - // fontSize: 12, - // ), - // textAlign: TextAlign.start, - // overflow: TextOverflow.ellipsis, - // ), - // ), + Container( + padding: const EdgeInsetsDirectional.only( + start: 10, end: 10, top: 10), + child: Text( + widget.data.text as String, + maxLines: 3, + style: TextStyle( + color: Theme.of(context).colorScheme.secondary, + fontSize: 12, + ), + textAlign: TextAlign.start, + overflow: TextOverflow.ellipsis, + ), + ), ], ) : (widget.data.kind == 'image') - ? PostImages( - data: widget.data, - flair: widget.data.flairId, - nsfw: widget.data.nsfw as bool, - spoiler: widget.data.spoiler as bool, - title: widget.data.title as String, - imageNumber: widget.data.imageNumber, - links: widget.data.images!.cast(), - maxHeightImageSize: - widget.data.maxHeightImageSize as Size, - ) - : (widget.data.kind == 'link') - ? PostCard( + ? kIsWeb + ? PostImagesWeb( + data: widget.data, flair: widget.data.flairId, nsfw: widget.data.nsfw as bool, spoiler: widget.data.spoiler as bool, - link: widget.data.url as String, title: widget.data.title as String, - type: widget.data.kind as String, + imageNumber: widget.data.imageNumber, + links: widget.data.images!.cast(), + maxHeightImageSize: + widget.data.maxHeightImageSize as Size, ) - : (widget.data.kind == 'video') - ? PostVideoInWidget( + : PostImages( + data: widget.data, + flair: widget.data.flairId, + nsfw: widget.data.nsfw as bool, + spoiler: widget.data.spoiler as bool, + title: widget.data.title as String, + imageNumber: widget.data.imageNumber, + links: widget.data.images!.cast(), + maxHeightImageSize: + widget.data.maxHeightImageSize as Size, + ) + : (widget.data.kind == 'link') + ? kIsWeb + ? PostLinkInScreen( + flair: widget.data.flairId, + nsfw: widget.data.nsfw as bool, + spoiler: widget.data.spoiler as bool, + link: widget.data.url as String, title: widget.data.title as String, + type: widget.data.kind as String, + ) + : PostCard( flair: widget.data.flairId, nsfw: widget.data.nsfw as bool, spoiler: widget.data.spoiler as bool, - inView: widget.inView, - url: widget.data.video as String, - videoController: widget.data.videoController - as VideoPlayerController, + link: widget.data.url as String, + title: widget.data.title as String, + type: widget.data.kind as String, ) + : (widget.data.kind == 'video') + ? kIsWeb + ? PostVideoInWidgetWeb( + title: widget.data.title as String, + flair: widget.data.flairId, + nsfw: widget.data.nsfw as bool, + spoiler: widget.data.spoiler as bool, + inView: widget.inView, + url: widget.data.video as String, + videoController: widget.data.videoController + as VideoPlayerController, + ) + : PostVideoInWidget( + title: widget.data.title as String, + flair: widget.data.flairId, + nsfw: widget.data.nsfw as bool, + spoiler: widget.data.spoiler as bool, + inView: widget.inView, + url: widget.data.video as String, + videoController: widget.data.videoController + as VideoPlayerController, + ) : const SizedBox(), ), ), diff --git a/lib/post/widgets/post_body_in_screen.dart b/lib/post/widgets/post_body_in_screen.dart index a919fa4a..18645f83 100644 --- a/lib/post/widgets/post_body_in_screen.dart +++ b/lib/post/widgets/post_body_in_screen.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:post/post/models/post_model.dart'; import 'package:post/post/widgets/post_images.dart'; +import 'package:post/post/widgets/post_images_in_screen.dart'; import 'package:post/post/widgets/post_link_in_screen.dart'; import 'package:post/post/widgets/post_tags_and_title.dart'; import 'package:video_player/video_player.dart'; @@ -62,7 +63,7 @@ class _PostBodyInScreenState extends State { ], ) : (widget.data.kind == 'image') - ? PostImages( + ? PostImagesInScreen( data: widget.data, flair: widget.data.flairId, nsfw: widget.data.nsfw as bool, @@ -70,8 +71,7 @@ class _PostBodyInScreenState extends State { title: widget.data.title as String, imageNumber: widget.data.imageNumber, links: widget.data.images!.cast(), - maxHeightImageSize: - widget.data.maxHeightImageSize as Size, + maxHeightImageSize: widget.data.maxHeightImageSize as Size, ) : (widget.data.kind == 'link') ? PostLinkInScreen( diff --git a/lib/post/widgets/post_comment_list.dart b/lib/post/widgets/post_comment_list.dart new file mode 100644 index 00000000..e2128004 --- /dev/null +++ b/lib/post/widgets/post_comment_list.dart @@ -0,0 +1,160 @@ +import 'package:flutter/material.dart'; +import 'package:fluttericon/typicons_icons.dart'; +import 'package:inview_notifier_list/inview_notifier_list.dart'; +import '../models/post_model.dart'; +import './post.dart'; + +class PostCommentList extends StatefulWidget { + final String userName; + final Widget topOfTheList; + final Function updateData; + final List> data; + final double leftMargin; + final double rightMargin; + + final String type; + const PostCommentList({ + super.key, + required this.userName, + this.topOfTheList = const SizedBox(), + required this.updateData, + required this.data, + this.type = 'home', + this.leftMargin = 0, + this.rightMargin = 0, + }); + + @override + State createState() => _PostCommentListState(); +} + +class _PostCommentListState extends State { + String dateOfcomment(String date) { + final data1 = DateTime.parse(date); + final date2 = DateTime.now(); + final difference = date2.difference(data1); + final differenceMonth = date2.month - data1.month; + final differenceYear = date2.year - data1.year; + + if (difference.inHours < 24) { + return '${difference.inHours}h'; + } else if (difference.inDays < 30) { + final numOfWeeks = difference.inDays ~/ 7; + if (numOfWeeks > 0) { + return '${numOfWeeks}w'; + } else { + return '${difference.inDays}d'; + } + } else if (differenceYear > 0) { + return '${differenceYear}y'; + } else { + return '${differenceMonth}mon'; + } + } + + @override + Widget build(BuildContext context) { + return Flexible( + child: InViewNotifierCustomScrollView( + physics: const AlwaysScrollableScrollPhysics(), + shrinkWrap: true, + onListEndReached: () => widget.updateData(), + scrollDirection: Axis.vertical, + initialInViewIds: const ['0'], + isInViewPortCondition: + (double deltaTop, double deltaBottom, double viewPortDimension) { + return deltaTop < (0.5 * viewPortDimension) && + deltaBottom > (0.5 * viewPortDimension); + }, + slivers: [ + // Container(), + SliverList( + delegate: SliverChildBuilderDelegate( + childCount: 1, + (context, index) => widget.topOfTheList, + ), + ), + SliverList( + delegate: SliverChildBuilderDelegate(childCount: widget.data.length, + (context, index) { + return Container( + margin: EdgeInsetsDirectional.only( + start: widget.leftMargin, end: widget.rightMargin), + child: InViewNotifierWidget( + id: '$index', + builder: (context, isInView, child) { + if (widget.data[index]['type'] == 'comment') { + return Container( + color: Colors.white, + margin: + EdgeInsetsDirectional.only(top: 10, bottom: 10), + child: ListTile( + title: Text( + widget.data[index]['data'].title.toString()), + subtitle: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Text.rich( + TextSpan( + children: [ + TextSpan( + text: + '${(widget.data[index]['data'].ownerType == 'User') ? 'u/' : 'r/'}${widget.data[index]['data'].owner}.${dateOfcomment(widget.data[index]['data'].createdAt.toString())} .${widget.data[index]['data'].votes}'), + const WidgetSpan( + child: Icon( + Typicons.up, + size: 15, + )), + ], + ), + ), + Text( + widget.data[index]['data'].text.toString()), + ], + ), + ), + ); + } else { + if (widget.type == 'profile') { + return Post.profile( + userName: widget.userName, + inView: isInView, + data: widget.data[index]['data'], + ); + } else if (widget.type == 'community') { + return Post.community( + userName: widget.userName, + inView: isInView, + data: widget.data[index]['data'], + ); + } else { + return Post.home( + userName: widget.userName, + inView: isInView, + data: widget.data[index]['data'], + ); + } + } + }), + ); + }), + ) + ], + ), + ); + } + + @override + void dispose() { + if (widget.data != null) { + for (var d in widget.data) { + d['type'] == 'comment'; + if (d['data'].kind == 'video') { + d['data'].videoController?.dispose(); + } + } + } + super.dispose(); + } +} \ No newline at end of file diff --git a/lib/post/widgets/post_footer.dart b/lib/post/widgets/post_footer.dart index 9cf0e87b..0556c639 100644 --- a/lib/post/widgets/post_footer.dart +++ b/lib/post/widgets/post_footer.dart @@ -1,3 +1,4 @@ +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:fluttericon/font_awesome_icons.dart'; import 'package:fluttericon/typicons_icons.dart'; @@ -7,6 +8,9 @@ import 'package:post/post/provider/post_provider.dart'; import 'package:provider/provider.dart'; import 'package:hexcolor/hexcolor.dart'; +import '../../show_post/screens/show_post.dart'; +import '../../show_post/screens/show_post_web.dart'; + /// This Widget is responsible for the footer of the post. class PostFooter extends StatefulWidget { @@ -23,6 +27,7 @@ class PostFooter extends StatefulWidget { final String id; final PostModel data; final bool inScreen; + final String userName; const PostFooter( {super.key, required this.votes, @@ -31,7 +36,8 @@ class PostFooter extends StatefulWidget { required this.id, required this.isMyPost, required this.data, - required this.inScreen}); + required this.inScreen, + required this.userName}); @override State createState() => _PostFooterState(postVoteStatus: postVoteStatus, votes: votes); @@ -170,7 +176,30 @@ class _PostFooterState extends State { ], ), InkWell( - onTap: null, + onTap: widget.inScreen + ? null + : () { + kIsWeb + ? Navigator.of(context).pushNamed( + ShowPostDetailsWeb.routeName, + arguments: { + 'data': widget.data, + 'userName': widget.userName + }) + // ? showDialog( + // context: context, + // builder: (context) => PostPopUpWeb( + // data: widget.data, + // userName: widget.userName, + // ), + // ) + : Navigator.of(context).pushNamed( + ShowPostDetails.routeName, + arguments: { + 'data': widget.data, + 'userName': widget.userName + }); + }, child: Row( children: [ Icon( diff --git a/lib/post/widgets/post_header.dart b/lib/post/widgets/post_header.dart index 371f147b..fad14bd7 100644 --- a/lib/post/widgets/post_header.dart +++ b/lib/post/widgets/post_header.dart @@ -5,6 +5,7 @@ import 'package:post/post/widgets/user_info_popup.dart'; import 'package:post/subreddit/screens/subreddit_screen.dart'; import '../../moderated_subreddit/screens/moderated_subreddit_screen.dart'; import '../models/post_model.dart'; +import 'package:flutter/foundation.dart' show kIsWeb; /// This Widget is responsible for the header of the post. @@ -109,7 +110,7 @@ class _PostHeaderState extends State { size: 18, ) : const SizedBox(), - !widget.inScreen + !widget.inScreen || kIsWeb ? PostPopupMenu( isMyPost: widget.isMyPost, data: widget.data, @@ -183,7 +184,6 @@ class _PostHeaderBasicState extends State { widget.ownerType == 'User' && !widget.inProfile ? const SizedBox() : InkWell( - onTap: (!widget.inProfile) ? () { print( @@ -197,17 +197,16 @@ class _PostHeaderBasicState extends State { ), ); } - : (){ - - print( + : () { + print( '===============================Is mod:${widget.isModerator}============================='); - - Navigator.of(context).pushNamed( - widget.isModerator - ? ModeratedSubredditScreen.routeName - : SubredditScreen.routeName, - arguments: widget.ownerName);}, + Navigator.of(context).pushNamed( + widget.isModerator + ? ModeratedSubredditScreen.routeName + : SubredditScreen.routeName, + arguments: widget.ownerName); + }, child: Row( children: [ if (widget.inProfile) diff --git a/lib/post/widgets/post_images_in_screen.dart b/lib/post/widgets/post_images_in_screen.dart new file mode 100644 index 00000000..ddc9b328 --- /dev/null +++ b/lib/post/widgets/post_images_in_screen.dart @@ -0,0 +1,153 @@ +import 'dart:async'; +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:dots_indicator/dots_indicator.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_image_slideshow/flutter_image_slideshow.dart'; +import 'package:post/post/models/post_model.dart'; +import 'package:post/post/widgets/post_tags_and_title.dart'; +import 'package:post/widgets/loading_reddit.dart'; + +class PostImagesInScreen extends StatefulWidget { + final PostModel data; + final List links; + final Size maxHeightImageSize; + final int imageNumber; + final String title; + final bool spoiler; + final bool nsfw; + final FlairId? flair; + const PostImagesInScreen({ + super.key, + required this.links, + required this.maxHeightImageSize, + required this.imageNumber, + required this.title, + required this.spoiler, + required this.nsfw, + required this.flair, + required this.data, + }); + + @override + State createState() => + _PostImagesInScreenState(imageNumber); +} + +class _PostImagesInScreenState extends State { + int imageNumber; + bool imageCounterVisible = false; + late List images; + _PostImagesInScreenState(this.imageNumber); + + void updateImageNumber(value) => setState(() { + imageNumber = value; + widget.data.imageNumber = value; + imageCounterVisible = true; + var timer; + timer = Timer(const Duration(milliseconds: 3000), () { + if (!mounted) { + timer.cancel(); + } else { + setState(() { + imageCounterVisible = false; + }); + } + }); + }); + @override + Widget build(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + PostTagsAndTitle( + flair: widget.flair, + isSpoiler: widget.spoiler, + isNSFW: widget.nsfw, + title: widget.title, + ), + Container( + margin: const EdgeInsetsDirectional.only(top: 10, bottom: 10), + child: Stack( + alignment: Alignment.topRight, + children: [ + Stack( + alignment: Alignment.bottomCenter, + children: [ + ImageSlideshow( + initialPage: imageNumber, + height: !(widget.maxHeightImageSize.height * + (MediaQuery.of(context).size.width / + widget.maxHeightImageSize.width)) + .isNaN + ? (widget.maxHeightImageSize.height * + (MediaQuery.of(context).size.width / + widget.maxHeightImageSize.width) < + .5 * MediaQuery.of(context).size.height) + ? (widget.maxHeightImageSize.height * + (MediaQuery.of(context).size.width / + widget.maxHeightImageSize.width)) + : .5 * MediaQuery.of(context).size.height + : .5 * MediaQuery.of(context).size.height, + indicatorColor: Colors.white, + indicatorRadius: 0, + onPageChanged: (value) => updateImageNumber(value), + children: widget.links.map((String link) { + CachedNetworkImage image = CachedNetworkImage( + fit: BoxFit.contain, + imageUrl: link, + placeholder: (context, url) => const LoadingReddit(), + ); + return image; + }).toList(), + ), + AnimatedOpacity( + opacity: imageCounterVisible ? 1 : 0, + duration: const Duration(milliseconds: 500), + child: Container( + margin: const EdgeInsetsDirectional.only(bottom: 8), + child: DotsIndicator( + decorator: const DotsDecorator( + size: Size.square(8), + activeSize: Size.square(8), + activeColor: Colors.white, + color: Colors.transparent, + shape: CircleBorder( + side: BorderSide(width: 1, color: Colors.white), + ), + spacing: EdgeInsets.all(4.0)), + dotsCount: + widget.links.isEmpty ? 1 : widget.links.length, + position: imageNumber.toDouble(), + ), + ), + ), + ], + ), + AnimatedOpacity( + opacity: imageCounterVisible ? 1 : 0, + duration: const Duration(milliseconds: 500), + child: Container( + margin: const EdgeInsetsDirectional.only(top: 8, end: 8), + child: ClipRRect( + borderRadius: BorderRadius.circular(20.0), + child: Container( + padding: const EdgeInsetsDirectional.only( + start: 10, end: 10, top: 5, bottom: 5), + color: Colors.black.withOpacity(0.6), + child: Text( + '${(imageNumber + 1).toString()}/${widget.links.length}', + style: const TextStyle( + color: Colors.white, + overflow: TextOverflow.ellipsis), + ), + ), + ), + ), + ) + ], + ), + ), + ], + ); + } +} diff --git a/lib/post/widgets/post_images_web.dart b/lib/post/widgets/post_images_web.dart new file mode 100644 index 00000000..976b54c7 --- /dev/null +++ b/lib/post/widgets/post_images_web.dart @@ -0,0 +1,152 @@ +import 'dart:async'; +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:dots_indicator/dots_indicator.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_image_slideshow/flutter_image_slideshow.dart'; +import 'package:post/post/models/post_model.dart'; +import 'package:post/post/widgets/post_tags_and_title.dart'; +import 'package:post/widgets/loading_reddit.dart'; + +class PostImagesWeb extends StatefulWidget { + final PostModel data; + final List links; + final Size maxHeightImageSize; + final int imageNumber; + final String title; + final bool spoiler; + final bool nsfw; + final FlairId? flair; + const PostImagesWeb({ + super.key, + required this.links, + required this.maxHeightImageSize, + required this.imageNumber, + required this.title, + required this.spoiler, + required this.nsfw, + required this.flair, + required this.data, + }); + + @override + State createState() => _PostImagesWebState(imageNumber); +} + +class _PostImagesWebState extends State { + int imageNumber; + bool imageCounterVisible = false; + late List images; + _PostImagesWebState(this.imageNumber); + + void updateImageNumber(value) => setState(() { + imageNumber = value; + widget.data.imageNumber = value; + imageCounterVisible = true; + var timer; + timer = Timer(const Duration(milliseconds: 3000), () { + if (!mounted) { + timer.cancel(); + } else { + setState(() { + imageCounterVisible = false; + }); + } + }); + }); + @override + Widget build(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + PostTagsAndTitle( + flair: widget.flair, + isSpoiler: widget.spoiler, + isNSFW: widget.nsfw, + title: widget.title, + ), + Container( + margin: const EdgeInsetsDirectional.only(top: 10, bottom: 10), + child: Stack( + alignment: Alignment.topRight, + children: [ + Stack( + alignment: Alignment.bottomCenter, + children: [ + ImageSlideshow( + initialPage: imageNumber, + height: !(widget.maxHeightImageSize.height * + (MediaQuery.of(context).size.width / + widget.maxHeightImageSize.width)) + .isNaN + ? (widget.maxHeightImageSize.height * + (MediaQuery.of(context).size.width / + widget.maxHeightImageSize.width) < + .5 * MediaQuery.of(context).size.height) + ? (widget.maxHeightImageSize.height * + (MediaQuery.of(context).size.width / + widget.maxHeightImageSize.width)) + : .5 * MediaQuery.of(context).size.height + : .5 * MediaQuery.of(context).size.height, + indicatorColor: Colors.white, + indicatorRadius: 0, + onPageChanged: (value) => updateImageNumber(value), + children: widget.links.map((String link) { + CachedNetworkImage image = CachedNetworkImage( + fit: BoxFit.cover, + imageUrl: link, + placeholder: (context, url) => const LoadingReddit(), + ); + return image; + }).toList(), + ), + AnimatedOpacity( + opacity: imageCounterVisible ? 1 : 0, + duration: const Duration(milliseconds: 500), + child: Container( + margin: const EdgeInsetsDirectional.only(bottom: 8), + child: DotsIndicator( + decorator: const DotsDecorator( + size: Size.square(8), + activeSize: Size.square(8), + activeColor: Colors.white, + color: Colors.transparent, + shape: CircleBorder( + side: BorderSide(width: 1, color: Colors.white), + ), + spacing: EdgeInsets.all(4.0)), + dotsCount: + widget.links.isEmpty ? 1 : widget.links.length, + position: imageNumber.toDouble(), + ), + ), + ), + ], + ), + AnimatedOpacity( + opacity: imageCounterVisible ? 1 : 0, + duration: const Duration(milliseconds: 500), + child: Container( + margin: const EdgeInsetsDirectional.only(top: 8, end: 8), + child: ClipRRect( + borderRadius: BorderRadius.circular(20.0), + child: Container( + padding: const EdgeInsetsDirectional.only( + start: 10, end: 10, top: 5, bottom: 5), + color: Colors.black.withOpacity(0.6), + child: Text( + '${(imageNumber + 1).toString()}/${widget.links.length}', + style: const TextStyle( + color: Colors.white, + overflow: TextOverflow.ellipsis), + ), + ), + ), + ), + ) + ], + ), + ), + ], + ); + } +} diff --git a/lib/post/widgets/post_list.dart b/lib/post/widgets/post_list.dart index b39b87e5..4f900379 100644 --- a/lib/post/widgets/post_list.dart +++ b/lib/post/widgets/post_list.dart @@ -9,13 +9,18 @@ class PostList extends StatefulWidget { final Function updateData; final List data; final String type; - const PostList( - {super.key, - required this.userName, - this.topOfTheList = const SizedBox(), - required this.updateData, - required this.data, - this.type = 'home'}); + final double leftMargin; + final double rightMargin; + const PostList({ + super.key, + required this.userName, + this.topOfTheList = const SizedBox(), + required this.updateData, + required this.data, + this.type = 'home', + this.leftMargin = 0, + this.rightMargin = 0, + }); @override State createState() => _PostListState(); @@ -26,6 +31,7 @@ class _PostListState extends State { Widget build(BuildContext context) { return Flexible( child: InViewNotifierCustomScrollView( + shrinkWrap: true, onListEndReached: () => widget.updateData(), scrollDirection: Axis.vertical, initialInViewIds: const ['0'], @@ -44,29 +50,33 @@ class _PostListState extends State { SliverList( delegate: SliverChildBuilderDelegate(childCount: widget.data.length, (context, index) { - return InViewNotifierWidget( - id: '$index', - builder: (context, isInView, child) { - if (widget.type == 'profile') { - return Post.profile( - userName: widget.userName, - inView: isInView, - data: widget.data[index], - ); - } else if (widget.type == 'community') { - return Post.community( - userName: widget.userName, - inView: isInView, - data: widget.data[index], - ); - } else { - return Post.home( - userName: widget.userName, - inView: isInView, - data: widget.data[index], - ); - } - }); + return Container( + margin: EdgeInsetsDirectional.only( + start: widget.leftMargin, end: widget.rightMargin), + child: InViewNotifierWidget( + id: '$index', + builder: (context, isInView, child) { + if (widget.type == 'profile') { + return Post.profile( + userName: widget.userName, + inView: isInView, + data: widget.data[index], + ); + } else if (widget.type == 'community') { + return Post.community( + userName: widget.userName, + inView: isInView, + data: widget.data[index], + ); + } else { + return Post.home( + userName: widget.userName, + inView: isInView, + data: widget.data[index], + ); + } + }), + ); }), ) ], @@ -85,4 +95,4 @@ class _PostListState extends State { } super.dispose(); } -} +} \ No newline at end of file diff --git a/lib/post/widgets/post_pop_up_web.dart b/lib/post/widgets/post_pop_up_web.dart new file mode 100644 index 00000000..7715c246 --- /dev/null +++ b/lib/post/widgets/post_pop_up_web.dart @@ -0,0 +1,53 @@ +import 'package:flutter/material.dart'; +import 'package:responsive_sizer/responsive_sizer.dart'; + +import '../../comments/widgets/comments_list.dart'; +import '../../post/models/post_model.dart'; +import 'post.dart'; + +//import '../widgets/edit_post.dart'; + +class PostPopUpWeb extends StatefulWidget { + final String userName; + final PostModel data; + static const routeName = '/showpost-screen'; + + const PostPopUpWeb({ + super.key, + required this.userName, + required this.data, + }); + + @override + State createState() => _PostPopUpWebState(); +} + +class _PostPopUpWebState extends State { + @override + Widget build(BuildContext context) { + return AlertDialog( + content: Container( + height: MediaQuery.of(context).size.width * .8, + width: MediaQuery.of(context).size.width * .8, + child: SingleChildScrollView( + child: Container( + width: 40.w, + child: Column( + children: [ + Post.home( + data: widget.data, + userName: widget.userName, + inView: true, + inScreen: true), + CommentsList( + postId: widget.data.sId ?? '', + userName: widget.userName, + ) + ], + ), + ), + ), + ), + ); + } +} diff --git a/lib/post/widgets/post_video_in_widget_web.dart b/lib/post/widgets/post_video_in_widget_web.dart new file mode 100644 index 00000000..f5423670 --- /dev/null +++ b/lib/post/widgets/post_video_in_widget_web.dart @@ -0,0 +1,186 @@ +import 'package:flutter/material.dart'; +import 'package:post/post/widgets/post_tags_and_title.dart'; +import 'package:post/providers/global_settings.dart'; +import 'package:provider/provider.dart'; +import 'package:video_player/video_player.dart'; +import 'package:chewie/chewie.dart'; + +import '../models/post_model.dart'; + +class PostVideoInWidgetWeb extends StatefulWidget { + /// The url of the video + final String url; + final VideoPlayerController videoController; + final bool inView; + final String title; + final bool spoiler; + final bool nsfw; + final FlairId? flair; + const PostVideoInWidgetWeb({ + super.key, + required this.url, + required this.videoController, + required this.inView, + required this.title, + required this.spoiler, + required this.nsfw, + required this.flair, + }); + + @override + State createState() => _PostVideoInWidgetWebState(); +} + +class _PostVideoInWidgetWebState extends State { + late ChewieController chewieController; + bool isMuted = true; + bool autoPlay = true; + @override + void initState() { + super.initState(); + chewieController = ChewieController( + videoPlayerController: widget.videoController, + autoInitialize: true, + autoPlay: false, + looping: true, + showControls: false, + ); + } + + updateMuted() { + if (Provider.of(context, listen: false).getIsMuted) { + widget.videoController.setVolume(0); + } else { + widget.videoController.setVolume(1); + } + setState(() { + isMuted = Provider.of(context, listen: false).getIsMuted; + }); + } + + play() { + if (Provider.of(context, listen: false).getIsMuted) { + widget.videoController.setVolume(0); + } else { + widget.videoController.setVolume(1); + } + widget.videoController.play(); + setState(() { + isMuted = Provider.of(context, listen: false).getIsMuted; + }); + } + + toggleSound() { + if (Provider.of(context, listen: false).getIsMuted) { + Provider.of(context, listen: false).unMute(); + updateMuted(); + } else { + Provider.of(context, listen: false).mute(); + updateMuted(); + } + } + + @override + Widget build(BuildContext context) { + if (widget.inView && !widget.videoController.value.isPlaying && autoPlay) { + { + play(); + } + } else if (!widget.inView && widget.videoController.value.isPlaying) { + { + widget.videoController.pause(); + widget.videoController.seekTo(const Duration(seconds: 0)); + updateMuted(); + } + } + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + PostTagsAndTitle( + flair: widget.flair, + isSpoiler: widget.spoiler, + isNSFW: widget.nsfw, + title: widget.title, + ), + Center( + child: widget.videoController.value.isInitialized + ? Container( + margin: const EdgeInsetsDirectional.only( + start: 20, end: 20, top: 10), + height: (widget.videoController.value.size.height * + (MediaQuery.of(context).size.width / + widget.videoController.value.size.width) < + MediaQuery.of(context).size.height * .5) + ? widget.videoController.value.size.height * + (MediaQuery.of(context).size.width / + widget.videoController.value.size.width) + : MediaQuery.of(context).size.height * 0.5, + child: Stack( + alignment: Alignment.center, + children: [ + Stack( + alignment: Alignment.bottomRight, + children: [ + Align( + alignment: Alignment.center, + child: AspectRatio( + aspectRatio: + widget.videoController.value.aspectRatio, + child: Chewie( + controller: chewieController, + ), + ), + ), + GestureDetector( + onTap: () => toggleSound(), + child: Container( + margin: const EdgeInsetsDirectional.all(10), + child: ClipRRect( + borderRadius: BorderRadius.circular(20.0), + child: Container( + padding: const EdgeInsetsDirectional.all(2), + color: Colors.black.withOpacity(0.5), + child: Icon( + isMuted + ? Icons.volume_off + : Icons.volume_up, + color: Colors.white, + size: 15, + ), + ), + ), + ), + ), + ], + ), + !autoPlay + ? GestureDetector( + onTap: () { + autoPlay = true; + play(); + }, + child: Container( + margin: const EdgeInsetsDirectional.all(10), + child: const Icon( + Icons.play_circle_outline_rounded, + color: Colors.white, + size: 50, + ), + ), + ) + : const SizedBox(), + ], + ), + ) + : Container(), + ), + ], + ); + } + + @override + void dispose() { + chewieController.dispose(); + super.dispose(); + } +} diff --git a/lib/shared/constants.dart b/lib/shared/constants.dart new file mode 100644 index 00000000..6bac0a0e --- /dev/null +++ b/lib/shared/constants.dart @@ -0,0 +1,6 @@ +class Constants { + static String appId = "1:82072335604:web:fd17be6c3765b21236daa1"; + static String apiKey = "AIzaSyAYaIh5gzJ42Nj_iIevGsh36EuXk14BDqg"; + static String messagingSenderId = "82072335604"; + static String projectId = "nonlegit-df8a9"; +} diff --git a/lib/show_post/screens/show_post_web.dart b/lib/show_post/screens/show_post_web.dart new file mode 100644 index 00000000..099e0348 --- /dev/null +++ b/lib/show_post/screens/show_post_web.dart @@ -0,0 +1,150 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:post/comments/screens/add_comment_screen.dart'; +import 'package:responsive_sizer/responsive_sizer.dart'; + +import '../../comments/widgets/comments_list.dart'; +import '../../notification/widgets/list_tile_widget.dart'; +import '../../post/models/post_model.dart'; +import '../../post/widgets/post.dart'; +import '../widgets/edit_post.dart'; + +class ShowPostDetailsWeb extends StatefulWidget { + static const routeName = '/showpost-screen'; + + const ShowPostDetailsWeb({ + super.key, + }); + + @override + State createState() => _ShowPostDetailsWebState(); +} + +class _ShowPostDetailsWebState extends State { + late String userName; + late PostModel data; + @override + void didChangeDependencies() { + // TODO: implement didChangeDependencies + //===============================Fetch subreddit data =======================================// + + var temp = + ModalRoute.of(context)?.settings.arguments as Map; + userName = temp['userName']; + data = temp['data']; + super.didChangeDependencies(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + backgroundColor: Colors.blue, + actions: [ + // IconButton( + // onPressed: () { + PopupMenuButton( + // elevation: -1, + // position: PopupMenuPosition.under, + constraints: BoxConstraints.expand(width: 60.w, height: 55.h), + icon: const Icon(Icons.more_vert, color: Colors.white), + itemBuilder: (context) { + //return list.map((e) { + return [ + PopupMenuItem( + child: ListTileWidget(icon: Icons.share, title: 'Share')), + PopupMenuItem( + child: ListTileWidget(icon: Icons.save, title: 'Save')), + PopupMenuItem( + child: + ListTileWidget(icon: Icons.copy, title: 'Copy text')), + PopupMenuItem( + child: ListTileWidget( + icon: Icons.edit, + title: 'Edit', + onpressed: () => Navigator.of(context) + .popAndPushNamed(EditPost.routeName))), + PopupMenuItem( + child: ListTileWidget( + icon: Icons.tag, title: 'Add post flair')), + PopupMenuItem( + child: ListTileWidget( + icon: Icons.warning, title: 'Mark spoiler')), + PopupMenuItem( + child: ListTileWidget( + icon: Icons.plus_one, title: 'Mark NSFW')), + PopupMenuItem( + child: ListTileWidget(icon: Icons.delete, title: 'Delete')), + ]; + }, + ), + ], + ), + body: Center( + child: Container( + width: 50.w, + child: SingleChildScrollView( + child: Column( + children: [ + Post.home( + data: data, + userName: userName, + inView: true, + inScreen: true), + CommentsList( + postId: data.sId ?? '', + userName: userName, + ) + ], + ), + ), + ), + ), + bottomNavigationBar: Padding( + padding: const EdgeInsetsDirectional.all(5), + child: GestureDetector( + onTap: () { + Get.to(AddCommentScreen(), + arguments: {'parentId': data.sId, 'title': data.title}); + }, + child: Container( + decoration: BoxDecoration( + color: Colors.grey[100], + border: Border.all( + color: Colors.grey, + ), + ), + child: const Padding( + padding: EdgeInsetsDirectional.all(5), + child: Text( + 'Add a comment', + style: TextStyle(color: Colors.grey), + ), + ), + ), + ) + + // TextFormField( + // onChanged: (value) {}, + // onTap: () { + // Get.to(AddCommentScreen(), + // arguments: {'parentId': data.sId, 'title': data.title}); + // }, + // enabled: false, + // style: const TextStyle(fontSize: 14.0, fontWeight: FontWeight.w700), + // showCursor: true, + + // textAlign: TextAlign.start, + // decoration: InputDecoration( + // hintText: 'Add a comment', + // enabledBorder: OutlineInputBorder( + // borderSide: + // BorderSide(width: 2, color: Colors.grey), //<-- SEE HERE + // borderRadius: BorderRadius.circular(10.0), + // ), + // ), + // ), + ), + ); + } +} diff --git a/lib/subreddit/models/subreddit_data.dart b/lib/subreddit/models/subreddit_data.dart index a63bb27d..5925d81d 100644 --- a/lib/subreddit/models/subreddit_data.dart +++ b/lib/subreddit/models/subreddit_data.dart @@ -28,25 +28,6 @@ class SubredditData { required this.rules, required this.moderators}); SubredditData.fromJson(Map json) { - // id = json['_id']; - // name = json['fixedName']; - // displayName = json['name']; - // subredditPicture = json['icon']; - // subredditBackPicture = json['backgroundImage']; - // description = json['description']; - // subredditLink = json['subredditLink']; - // numOfMembers = int.parse(json['membersCount'].toString()); - // numOfOnlines = int.parse(json['numOfOnlines'].toString()); - // isJoined = (json['isJoined']); - // final List loadedrule = []; - // json['rules'].forEach((rule) { - // loadedrule.add(SubredditAboutRules(rule['title'], rule['description'])); - // }); - // rules = loadedrule; - // final List loadedmodrator = []; - // json['moderators'].forEach((moderator) { - // loadedmodrator.add(moderator["userName"]); - print(json); id = json['_id']; name = json['fixedName']; displayName = json['name']; @@ -54,7 +35,7 @@ class SubredditData { subredditBackPicture = json['backgroundImage']; description = json['description']; subredditLink = json['subredditLink']; - numOfMembers = 0; //int.parse(json['membersCount'].toString()); + numOfMembers = int.parse(json['membersCount'].toString()); numOfOnlines = 0; //int.parse(json['numOfOnlines'].toString()); isJoined = (json['isJoined']); final List loadedrule = []; @@ -69,57 +50,4 @@ class SubredditData { moderators = loadedmodrator; } -// { -// "id": 10, -// "name": "Cooking", -// "displayName": "Cooking Good", -// "subredditPicture": "https://previews.123rf.com/images/seamartini/seamartini1609/seamartini160900764/64950290-chef-toque-vector-sketch-icon-cook-cap-kitchen-cooking-hat-emblem-for-restaurant-design-element-bake.jpg", -// "subredditBackPicture": "https://imagesvc.meredithcorp.io/v3/mm/image?url=https%3A%2F%2Fassets.marthastewart.com%2Fstyles%2Fwmax-750%2Fd30%2Feasy-basic-pancakes-horiz-1022%2Feasy-basic-pancakes-horiz-1022_0.jpg%3Fitok%3DXQMZkp_j", -// "description": "Iam Chef", -// "subredditLink": "https://previews.123rf.com/images/seamartini/seamartini1609/seamartini160900764/64950290-chef-toque-vector-sketch-icon-cook-cap-kitchen-cooking-hat-emblem-for-restaurant-design-element-bake.jpg", -// "numOfMembers": 10398, -// "numOfOnlines": 1789, -// "rules": [{ -// "title": "no codeing", -// "description": "i hate coding" -// }, -// { -// "title": "no codeing", -// "description": "i hate coding" -// }, -// { -// "title": "no codeing", -// "description": "i hate coding" -// } -// ], -// "moderators": [{ -// "userName": "Ali" -// }, -// { -// "userName": "omer" -// }, -// { -// "userName": "zeinab" -// }, -// { -// "userName": "mazen" -// } -// ], -// "isJoined": true -// } - // Map toJson() { - // final Map data = new Map(); - // data['id'] = this.id; - // data['name'] = this.name; - // data['subredditPicture'] = this.subredditPicture; - // data['subredditBackPicture'] = this.subredditBackPicture; - // data['subredditLink'] = this.subredditLink; - // data['description'] = this.description; - // data['numOfMembers'] = this.numOfMembers; - // data['numOfOnlines'] = this.numOfOnlines; - // data['isJoined'] = this.isJoined; - // data['rules'] = this.rules; - // // data['moderators'] = this.moderators; - // return data; - // } } diff --git a/lib/subreddit/providers/subreddit_provider.dart b/lib/subreddit/providers/subreddit_provider.dart index 3e40434d..7bd7bc13 100644 --- a/lib/subreddit/providers/subreddit_provider.dart +++ b/lib/subreddit/providers/subreddit_provider.dart @@ -17,7 +17,9 @@ class SubredditProvider with ChangeNotifier { bool? get gettingTheme { return showTheme; } - + // ===================================this function used to===========================================// +//==================fetch and set date===========================// +//moderatedSubredditUserName==> userName Of Subreddit Future fetchAndSetSubredddit( String subredditUserName, BuildContext context) async { try { @@ -26,8 +28,6 @@ class SubredditProvider with ChangeNotifier { DioClient.init(prefs); await DioClient.get(path: '/subreddits/${subredditUserName}') .then((response) { - print('lllllllllllllllllllllllllllllllllllll'); - print(response.data['data']); loadSubreddit = SubredditData.fromJson(response.data['data']); notifyListeners(); }); @@ -37,7 +37,10 @@ class SubredditProvider with ChangeNotifier { HandleError.handleError(error.toString(), context); } } - + // ===================================this function used to===========================================// +//==================Join and Disjoin subbreddit ===========================// +//moderatedSubredditUserName==> userName Of Subreddit +//action==> action either sub or unsub Future joinAndDisjoinSubreddit( String subredditUserName, String action, BuildContext context) async { try { diff --git a/lib/subreddit/screens/subreddit_screen.dart b/lib/subreddit/screens/subreddit_screen.dart index 43e98eb9..2e4e4b30 100644 --- a/lib/subreddit/screens/subreddit_screen.dart +++ b/lib/subreddit/screens/subreddit_screen.dart @@ -56,11 +56,11 @@ SubredditData? loadedSubreddit; _controller!.dispose(); super.dispose(); } - + // ===================================this function used to===========================================// +//==================fetch date for first time===========================// @override void didChangeDependencies() { // TODO: implement didChangeDependencies - //===============================doing fetch=======================================// if (_isInit) { setState(() { _isLoading = true; @@ -77,8 +77,6 @@ SubredditData? loadedSubreddit; }); } _isInit = false; - - //==================================================// super.didChangeDependencies(); } diff --git a/lib/subreddit/widgets/notify_button_web.dart b/lib/subreddit/widgets/notify_button_web.dart index f57180a5..3462eab2 100644 --- a/lib/subreddit/widgets/notify_button_web.dart +++ b/lib/subreddit/widgets/notify_button_web.dart @@ -42,12 +42,11 @@ class _NotifyButtonWebState extends State { PopupMenuItem( value: "Off", child: Row( - children: [ + children: const [ Icon( Icons.notifications_off, ), SizedBox( - // sized box with width 10 width: 4, ), Text("Off") @@ -57,10 +56,9 @@ class _NotifyButtonWebState extends State { PopupMenuItem( value: "Low", child: Row( - children: [ + children: const [ Icon(Icons.notifications), SizedBox( - // sized box with width 10 width: 4, ), Text("Low") @@ -70,10 +68,9 @@ class _NotifyButtonWebState extends State { PopupMenuItem( value: 'Frequent', child: Row( - children: [ + children: const [ Icon(Icons.notifications_active), SizedBox( - // sized box with width 10 width: 4, ), Text('Frequent') diff --git a/lib/subreddit/widgets/subreddit_card_information_web.dart b/lib/subreddit/widgets/subreddit_card_information_web.dart index fded8f46..327260c6 100644 --- a/lib/subreddit/widgets/subreddit_card_information_web.dart +++ b/lib/subreddit/widgets/subreddit_card_information_web.dart @@ -24,7 +24,7 @@ class _SubredditCardInformationWebState Widget build(BuildContext context) { return Container( margin: const EdgeInsets.only(right: 180, top: 100), - width: 60.w, + width: 50.w, height: 40.h, decoration: BoxDecoration( borderRadius: BorderRadius.circular(5), color: Colors.white), @@ -36,15 +36,15 @@ class _SubredditCardInformationWebState width: 100.h, height: 6.h, padding: const EdgeInsets.only(top: 20, left: 10), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(5), + color: Colors.blue, + ), child: const Text( 'About Community', style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold), ), - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(5), - color: Colors.blue, - ), ), Container( width: 100.h, @@ -173,7 +173,6 @@ class _SubredditCardInformationWebState value: chooseTheme, onChanged: (value) { setState(() => chooseTheme = value); - print('==========================in theme============================='); Provider.of(context, listen: true) .togglingTheme(); }, diff --git a/lib/subreddit/widgets/subreddit_join_buttons.dart b/lib/subreddit/widgets/subreddit_join_buttons.dart index 72391b88..c1c3e34c 100644 --- a/lib/subreddit/widgets/subreddit_join_buttons.dart +++ b/lib/subreddit/widgets/subreddit_join_buttons.dart @@ -3,6 +3,7 @@ import 'package:responsive_sizer/responsive_sizer.dart'; import 'package:provider/provider.dart'; import '../providers/subreddit_provider.dart'; import '../../widgets/custom_snack_bar.dart'; + class JoinButtons extends StatefulWidget { bool isJoined; String communityuserName; @@ -83,28 +84,29 @@ class JoinButtonsState extends State { )); } + // ===================================the next two function used to===========================================// +//==================to join subreddit===========================// +//communityName==> commuintyUserName you want to join Future subscribe(BuildContext context) async { - print('===============================Subcribe=================================='); bool sub = await Provider.of(context, listen: false) .joinAndDisjoinSubreddit(widget.communityuserName, 'sub', context); if (sub) { - join(); - ScaffoldMessenger.of(context).showSnackBar( + setState(() { + join(); + }); + ScaffoldMessenger.of(context).showSnackBar( CustomSnackBar( isError: false, text: 'Join Successfully', disableStatus: true), ); - } - else{ - ScaffoldMessenger.of(context).showSnackBar( + } else { + ScaffoldMessenger.of(context).showSnackBar( CustomSnackBar(isError: true, text: 'Join Failed', disableStatus: true), - );} + ); + } } bool join() { - setState(() { - isJoinedstate = true; - }); - + isJoinedstate = true; return isJoinedstate; } @@ -186,7 +188,10 @@ class JoinButtonsState extends State { return tappedIndex; } - //to Disjoin from subreddit + // ===================================the next three function used to===========================================// +//==================to disjoin of subreddit===========================// +// have two option one:leave subreddit two: cancel +//communityName==> commuintyUserName you want to leave void _showLeaveDialog() { showDialog( context: context, @@ -225,7 +230,7 @@ class JoinButtonsState extends State { ), child: const Text('Leave'), onPressed: () async { - await unSubescribe( ctx); + await unSubescribe(ctx); }, ), ) @@ -235,28 +240,24 @@ class JoinButtonsState extends State { } Future unSubescribe(BuildContext ctx) async { - print('===============================Un Subcribe=================================='); bool unSub = await Provider.of(context, listen: false) .joinAndDisjoinSubreddit(widget.communityuserName, 'unsub', context); if (unSub) { - disJoin(); - ScaffoldMessenger.of(context).showSnackBar( + setState(() { + disJoin(); + }); + ScaffoldMessenger.of(context).showSnackBar( CustomSnackBar( isError: false, text: 'Leave Successfully', disableStatus: true), ); - } - else{ - ScaffoldMessenger.of(context).showSnackBar(CustomSnackBar( + } else { + ScaffoldMessenger.of(context).showSnackBar(CustomSnackBar( isError: true, text: 'Leave Failed', disableStatus: true)); } Navigator.of(ctx).pop(); } - bool disJoin() { - setState(() { - isJoinedstate = false; - }); - + isJoinedstate = false; return isJoinedstate; } } diff --git a/lib/subreddit/widgets/subreddit_pop_up_menu_button.dart b/lib/subreddit/widgets/subreddit_pop_up_menu_button.dart index 7bd8127e..4a5adbde 100644 --- a/lib/subreddit/widgets/subreddit_pop_up_menu_button.dart +++ b/lib/subreddit/widgets/subreddit_pop_up_menu_button.dart @@ -19,7 +19,6 @@ class _SubredditPopupMenuButtonState extends State { return PopupMenuButton( onSelected: (value) { if (value.toString() == '/communitymodmessage' - // ||value.toString() == '/communityinfo' ) { Navigator.pushNamed(context, value.toString()); } else if (value.toString() == 'Share') { @@ -47,7 +46,7 @@ class _SubredditPopupMenuButtonState extends State { ); } // to copy Link of Subreddit -//byu using copy clipboard +//by using copy clipboard Future shareCommunitySheetButton(BuildContext context) { return showModalBottomSheet( backgroundColor: Colors.transparent, diff --git a/lib/subreddit/widgets/subreddit_post_web.dart b/lib/subreddit/widgets/subreddit_post_web.dart index afd7ae6a..52cf339a 100644 --- a/lib/subreddit/widgets/subreddit_post_web.dart +++ b/lib/subreddit/widgets/subreddit_post_web.dart @@ -1,9 +1,15 @@ import 'package:flutter/material.dart'; //import 'package:flutter_code_style/analysis_options.yaml'; import 'package:responsive_sizer/responsive_sizer.dart'; +import 'package:shared_preferences/shared_preferences.dart'; import '../../widgets/sort_bottom_web.dart'; import '../../models/subreddit_data.dart'; import '../widgets/subreddit_card_information_web.dart'; +import '../../widgets/loading_reddit.dart'; +import '../../post/models/post_model.dart'; +import '../../post/widgets/post_list.dart'; +import 'package:provider/provider.dart'; +import '../../providers/subreddit_posts_provider.dart'; class SubredditePostWeb extends StatefulWidget { final SubredditData? loadedSubreddit; @@ -11,72 +17,112 @@ class SubredditePostWeb extends StatefulWidget { Key? key, required this.loadedSubreddit, }) : super(key: key); - - // Posts(this.routeNamePop); @override State createState() => _SubredditePostWebState(); } class _SubredditePostWebState extends State { + int _page = 1; + List? posts = []; + bool _isLoadMoreRunning = false; + String? userName; + bool _isInit = true; + bool _isLoading = false; + late ScrollController _scrollController; + //=====================================this function is used to======================================// + //=================Loading More Data====================// + void _loadMore() { + if (_isLoading == false && _isLoadMoreRunning == false) { + setState(() { + toggleLoadingMore(); // Display a progress indicator at the bottom + }); + setState(() { + _page += 1; + }); + // Increase _page by + + setState(() { + toggleLoadingMore(); + }); + } + } + //=====================================this function is used to======================================// + //=================Start loading More Data====================// + bool toggleLoadingMore() => _isLoadMoreRunning = !_isLoadMoreRunning; +//=====================================this function is used to======================================// + //=================Loading Data for first time====================// + @override + Future didChangeDependencies() async { + // TODO: implement didChangeDependencies + if (_isInit) { + setState(() { + _isLoading = true; + }); + //New Hot Top + await Provider.of(context, listen: false) + .fetchSubredditePosts(widget.loadedSubreddit!.name.toString(), 'hot', + _page, 25, context) + .then((value) async { + posts = + await Provider.of(context, listen: false) + .gettingSubredditPostData; + }); + final prefs = await SharedPreferences.getInstance(); + userName = prefs.getString('userName'); + setState(() { + _isLoading = false; + }); + } + _isInit = false; + super.didChangeDependencies(); + } + @override Widget build(BuildContext context) { - return ListView( - scrollDirection: Axis.vertical, - children: [ - Row( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Expanded( - flex: 2, - child: Column( - children: [ - SizedBox( - height: 10.h, - ), - Container( - height: 6.h, - width: 50.w, - margin: EdgeInsets.only(left: 100, top: 40), - child: SortBottomWeb(page: 1,userName:widget.loadedSubreddit!.name.toString()), - color: Colors.white, - // width: 100.w, - ), - Container( - margin: EdgeInsets.only(left: 100, bottom: 90, top: 30), - height: 30.h, - width: 50.w, - // color: Colors.white, - decoration: BoxDecoration( - color: Colors.white, - border: Border.all( - color: Colors.blue, - width: 3, - )), - child: Column(children: [ - Expanded( - child: Row( - children: const [ - Expanded( - child: ListTile( - title: Text('Post'), - ), - ), - ], - ), - ) - ]), + return (_isLoading || _isLoadMoreRunning) + ? LoadingReddit() + : (posts != null) + ? PostList( + leftMargin: 15.w, + rightMargin: 35.w, + topOfTheList: Container( + height: 65.h, + width: 50.h, + margin: EdgeInsets.only(left: 15.w), + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SubredditCardInformationWeb( + loadedSubreddit: widget.loadedSubreddit), + SortBottomWeb( + page: 1, + userName: widget.loadedSubreddit!.name.toString()), + ], ), - ], - ), - ), - Expanded( - child: SubredditCardInformationWeb( - loadedSubreddit: widget.loadedSubreddit, - )), - ], - ), - ], - ); + ), + userName: userName.toString(), + updateData: _loadMore, + data: posts as List, + type: 'community', + ) + + : Center( + child: Column( + children: [ + SizedBox( + height: 30.h, + ), + const Icon( + Icons.reddit, + size: 100, + ), + const Text( + 'Wow,such empty', + style: TextStyle(color: Colors.grey), + ) + ], + ), + ); } } diff --git a/lib/subreddit/widgets/subreddit_posts.dart b/lib/subreddit/widgets/subreddit_posts.dart deleted file mode 100644 index fe85a06b..00000000 --- a/lib/subreddit/widgets/subreddit_posts.dart +++ /dev/null @@ -1,87 +0,0 @@ -// import 'package:flutter/material.dart'; -// import 'package:post/widgets/loading_reddit.dart'; -// import 'package:provider/provider.dart'; -// //import 'package:flutter_code_style/analysis_options.yaml'; -// import 'package:responsive_sizer/responsive_sizer.dart'; -// import '../../post/models/post_model.dart'; -// import '../../providers/subreddit_post.dart'; -// import '../../widgets/post_sort_bottom.dart'; -// import '../../post/widgets/post.dart'; -// import '../../post/test_data.dart'; - -// class SubredditPosts extends StatefulWidget { -// final String routeNamePop; -// final String subredditName; -// SubredditPosts({ -// Key? key, -// required this.routeNamePop, -// required this.subredditName, -// }) : super(key: key); -// // [ -// // {'username': 'ahmed', 'title': 'hello world1'}, -// // {'username': 'sayed', 'title': 'hello world2'}, -// // {'username': 'sayed', 'title': 'hello world3'}, -// // {'username': 'ahmed', 'title': 'hello world1'}, -// // {'username': 'sayed', 'title': 'hello world2'}, -// // {'username': 'sayed', 'title': 'hello world3'}, -// // {'username': 'ahmed', 'title': 'hello world1'}, -// // {'username': 'sayed', 'title': 'hello world2'}, -// // {'username': 'sayed', 'title': 'hello world3'} -// // ]; -// // Posts(this.routeNamePop); -// @override -// State createState() => _SubredditPosts(); -// } - -// class _SubredditPosts extends State { -// // final String routeNamePop; -// // _PostsState(this.routeNamePop); -// bool _isInit = true; -// bool _isLoading = false; -// List? posts = []; -// @override -// void didChangeDependencies() { -// if (_isInit) { -// setState(() { -// _isLoading = true; -// }); -// Provider.of(context, listen: false) -// .fetchNewProfilePosts(widget.subredditName) -// .then((value) { -// posts = Provider.of(context, listen: false) -// .gettingProfilePostData; -// setState(() { -// _isLoading = false; -// }); -// }); -// } -// _isInit = false; -// super.didChangeDependencies(); -// } - -// @override -// Widget build(BuildContext context) { -// return _isLoading -// ? LoadingReddit() -// : ListView( -// scrollDirection: Axis.vertical, -// children: [ -// PostSortBottom( -// widget.routeNamePop, -// //_dropDownValue, _icon -// ), -// //Select the type of Posts -// SingleChildScrollView( -// child: ListView.builder( -// physics: const ClampingScrollPhysics(), -// shrinkWrap: true, -// itemBuilder: ((context, index) => Post.community( -// data: posts![index], -// )), -// itemCount: posts?.length, -// ), -// ), -// ], -// ); -// } -// } diff --git a/lib/subreddit/widgets/subreddit_web.dart b/lib/subreddit/widgets/subreddit_web.dart index 126ac63e..0fb225a3 100644 --- a/lib/subreddit/widgets/subreddit_web.dart +++ b/lib/subreddit/widgets/subreddit_web.dart @@ -8,6 +8,7 @@ import '../providers/subreddit_provider.dart'; import 'package:provider/provider.dart'; import '../widgets/subreddit_post_web.dart'; import '../../widgets/subreddit_join_button_web.dart'; + extension ColorExtension on String { toColor() { var hexString = this; @@ -17,6 +18,7 @@ extension ColorExtension on String { return Color(int.parse(buffer.toString(), radix: 16)); } } + class SubredditWeb extends StatelessWidget { String userName; SubredditWeb( @@ -34,16 +36,20 @@ class SubredditWeb extends StatelessWidget { final TabBar tabBar; bool isLoading; TabController? controller; - + @override Widget build(BuildContext context) { - bool showTheme= Provider.of(context, listen: true).gettingTheme as bool; + bool showTheme = Provider.of(context, listen: false) + .gettingTheme as bool; final GlobalKey _scaffoldKey = GlobalKey(); return Scaffold( key: _scaffoldKey, - backgroundColor: (showTheme) + backgroundColor: + (showTheme) ? Color.fromARGB(255, 218, 224, 230) - : (loadedSubreddit!.theme!.contains('https'))?Image.network(loadedSubreddit!.theme.toString()).color:'#6ae792'.toColor(), + : (loadedSubreddit!.theme!.contains('https')) + ? Image.network(loadedSubreddit!.theme.toString()).color + : '#6ae792'.toColor(), body: isLoading ? const Center( child: Icon( @@ -96,7 +102,6 @@ class SubredditWeb extends StatelessWidget { left: 0, bottom: 0, child: Container( - // padding: EdgeInsets.only(left: 250), color: Colors.white, child: Column( mainAxisAlignment: @@ -105,7 +110,6 @@ class SubredditWeb extends StatelessWidget { CrossAxisAlignment.start, children: [ Container( - //margin: EdgeInsets.only(top: 15), width: 100.w, height: 15.h, padding: EdgeInsets.only(left: 250), @@ -118,8 +122,6 @@ class SubredditWeb extends StatelessWidget { Container( width: 10.w, height: 20.h, - // margin: EdgeInsets.only(bottom: 30,top: 40), - decoration: BoxDecoration( color: Colors.orange, shape: BoxShape.circle, @@ -155,8 +157,6 @@ class SubredditWeb extends StatelessWidget { SubredditJoinButtonWeb( isJoined: loadedSubreddit! .isJoined as bool, - // icon: icon, - //dropDownValue: dropDownValue, communityName: loadedSubreddit!.name .toString()), @@ -180,7 +180,6 @@ class SubredditWeb extends StatelessWidget { SubredditePostWeb( loadedSubreddit: loadedSubreddit, ), - // SubredditPosts(routeNamePop: SubredditScreen.routeName), SubredditAbout( rules: loadedSubreddit!.rules as List, diff --git a/lib/widgets/back_to_button.dart b/lib/widgets/back_to_button.dart index 3a4f35ef..49007c91 100644 --- a/lib/widgets/back_to_button.dart +++ b/lib/widgets/back_to_button.dart @@ -61,7 +61,7 @@ class _BackToTopButtonState extends State { shape: StadiumBorder(), ), margin: EdgeInsets.only(right: width * 0.20), - child: Text( + child: const Text( 'Back to Top', style:TextStyle(color: Colors.white), ), diff --git a/lib/widgets/myprofile_comment_web.dart b/lib/widgets/myprofile_comment_web.dart index 4655d27d..d7a7b936 100644 --- a/lib/widgets/myprofile_comment_web.dart +++ b/lib/widgets/myprofile_comment_web.dart @@ -3,7 +3,7 @@ import 'package:responsive_sizer/responsive_sizer.dart'; import 'package:provider/provider.dart'; import 'package:fluttericon/typicons_icons.dart'; import '../models/comments_data.dart'; -import '../providers/Profile_provider.dart'; +import '../providers/profile_provider.dart'; import '../widgets/loading_reddit.dart'; class MyProfileCommentWeb extends StatefulWidget { @@ -47,6 +47,8 @@ class _MyProfileCommentWebState extends State { } ScrollController _scrollController = new ScrollController(); + //=====================================this function is used to======================================// + //=================Loading More Data====================// void loadMore() async { if (_isLoading == false && _isLoadMoreRunning == false && @@ -75,7 +77,8 @@ class _MyProfileCommentWebState extends State { }); } } - +//=====================================this function is used to======================================// + //=================Start loading More Data====================// bool toggleLoadingMore() => _isLoadMoreRunning = !_isLoadMoreRunning; @override @@ -84,7 +87,8 @@ class _MyProfileCommentWebState extends State { super.initState(); _scrollController = ScrollController()..addListener(loadMore); } - + //=====================================this function is used to======================================// + //=================Loading Data for first time====================// @override Future didChangeDependencies() async { // TODO: implement didChangeDependencies diff --git a/lib/widgets/myprofile_post_web.dart b/lib/widgets/myprofile_post_web.dart index fa0b0a8b..81984007 100644 --- a/lib/widgets/myprofile_post_web.dart +++ b/lib/widgets/myprofile_post_web.dart @@ -2,40 +2,64 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:responsive_sizer/responsive_sizer.dart'; import '../post/models/post_model.dart'; -import '../post/widgets/post.dart'; import 'loading_reddit.dart'; import 'sort_bottom_web.dart'; -import '../myprofile/models/myprofile_data.dart'; +import '../providers/profile_provider.dart'; +import '../post/widgets/post_list.dart'; class MyProfilePostWeb extends StatefulWidget { final String userName; - MyProfilePostWeb({ - Key? key, - required this.userName - }) : super(key: key); + MyProfilePostWeb({Key? key, required this.userName}) : super(key: key); @override State createState() => _MyProfilePostWebState(); } class _MyProfilePostWebState extends State { + int _page = 1; + + bool _isLoadMoreRunning = false; + bool _isInit = true; bool _isLoading = false; List? posts = []; + late ScrollController _scrollController; + //=====================================this function is used to======================================// + //=================Loading More Data====================// + void _loadMore() { + if (_isLoading == false && _isLoadMoreRunning == false) { + setState(() { + toggleLoadingMore(); // Display a progress indicator at the bottom + }); + setState(() { + _page += 1; + }); + // Increase _page by + + setState(() { + toggleLoadingMore(); + }); + } + } + //=====================================this function is used to======================================// + //=================Start loading More Data====================// + bool toggleLoadingMore() => _isLoadMoreRunning = !_isLoadMoreRunning; + //=====================================this function is used to======================================// + //=================Loading Data for first time====================// @override void didChangeDependencies() { if (_isInit) { setState(() { _isLoading = true; }); - // Provider.of(context, listen: false) - // .fetchProfilePosts('Amr') - // .then((value) { - // posts = Provider.of(context, listen: false) - // .gettingProfilePostData; - // setState(() { - // _isLoading = false; - // }); - // }); + Provider.of(context, listen: false) + .fetchProfilePosts(widget.userName, 'Hot', _page, 25, context) + .then((value) { + posts = Provider.of(context, listen: false) + .gettingProfilePostData; + setState(() { + _isLoading = false; + }); + }); } _isInit = false; super.didChangeDependencies(); @@ -43,91 +67,46 @@ class _MyProfilePostWebState extends State { @override Widget build(BuildContext context) { - return ListView( - scrollDirection: Axis.vertical, - children: [ - Expanded( - flex: 2, - child: Column( - children: [ - Container( - height: 6.h, - width: 50.w, - margin: EdgeInsets.only(left: 100, bottom: 10, top: 30), - child: SortBottomWeb(page: 1,userName: widget.userName), - color: Colors.white, - ), - Container( - margin: EdgeInsets.only(left: 100, bottom: 90, top: 30), - height: 30.h, - width: 50.w, - // color: Colors.white, - decoration: BoxDecoration( - color: Colors.white, - border: Border.all( - color: Colors.blue, - width: 3, - )), - child: Column(children: [ - Expanded( - child: Row( - children: [ - const Expanded( - child: ListTile( - title: Text('Post'), - ), - ), - if (!_isLoading) - ListView( - scrollDirection: Axis.vertical, - children: [ - SingleChildScrollView( - child: ListView.builder( - physics: const ClampingScrollPhysics(), - shrinkWrap: true, - itemBuilder: ((context, index) => Container( - margin: const EdgeInsets.only( - left: 100, bottom: 90, top: 30), - height: 30.h, - width: 50.w, - // color: Colors.white, - decoration: BoxDecoration( - color: Colors.white, - border: Border.all( - color: Colors.blue, - width: 3, - )), - child: Column(children: [ - Expanded( - child: Row( - children: const [ - Expanded( - child: ListTile( - title: Text('Post'), - ), - ), - ], - ), - ) - ]), - )), - itemCount: posts?.length, - ), - ), - ], - ) - ], + posts = Provider.of(context, listen: true) + .gettingProfilePostData; + return (_isLoading || _isLoadMoreRunning) + ? LoadingReddit() + : (posts != null) + ? + PostList( + leftMargin: 15.w, + rightMargin: 35.w, + topOfTheList:Container( + height: 10.h, + width: 50.h, + margin: EdgeInsets.only(left: 15.w,right: 37.w), + child: SortBottomWeb( + page: _page, + userName: widget.userName, ), - ) - ]), - ), - ], - ), - ), + ), + userName: widget.userName, + updateData: _loadMore, + data: posts as List, + type: 'profile', + ) - // ], - // ), - ], - ); + : Center( + child: Column( + children: [ + SizedBox( + height: 30.h, + ), + const Icon( + Icons.reddit, + size: 100, + ), + const Text( + 'Wow,such empty', + style: TextStyle(color: Colors.grey), + ) + ], + ), + ); } -} +} \ No newline at end of file diff --git a/lib/widgets/overview_myprofile_web.dart b/lib/widgets/overview_myprofile_web.dart index d1e415f1..5bc0542c 100644 --- a/lib/widgets/overview_myprofile_web.dart +++ b/lib/widgets/overview_myprofile_web.dart @@ -2,18 +2,22 @@ import 'package:flutter/material.dart'; import 'package:responsive_sizer/responsive_sizer.dart'; import 'package:provider/provider.dart'; import 'package:fluttericon/typicons_icons.dart'; -import '../providers/Profile_provider.dart'; +import '../providers/profile_provider.dart'; import '../myprofile/models/myprofile_data.dart'; import '../myprofile/widgets/myprofile_card_information_web.dart'; import '../widgets/loading_reddit.dart'; import '../widgets/sort_bottom_web.dart'; +import '../post/widgets/post_comment_list.dart'; class OverviewMyProfileWeb extends StatefulWidget { final MyProfileData loadProfile; final ScrollController scrollController; final String type; - OverviewMyProfileWeb( - {Key? key, required this.loadProfile, required this.scrollController,required this.type}) + OverviewMyProfileWeb( + {Key? key, + required this.loadProfile, + required this.scrollController, + required this.type}) : super(key: key); @override State createState() => _OverviewMyProfileWebState(); @@ -50,6 +54,8 @@ class _OverviewMyProfileWebState extends State { } ScrollController _scrollController = new ScrollController(); + //=====================================this function is used to======================================// + //=================Loading More Data====================// void _loadMore() { if (_isLoading == false && _isLoadMoreRunning == false) { setState(() { @@ -66,6 +72,8 @@ class _OverviewMyProfileWebState extends State { } } +//=====================================this function is used to======================================// + //=================Start loading More Data====================// bool toggleLoadingMore() => _isLoadMoreRunning = !_isLoadMoreRunning; @override @@ -75,6 +83,8 @@ class _OverviewMyProfileWebState extends State { _scrollController = ScrollController()..addListener(_loadMore); } +//=====================================this function is used to======================================// + //=================Loading Data for first time====================// @override Future didChangeDependencies() async { // TODO: implement didChangeDependencies @@ -113,147 +123,50 @@ class _OverviewMyProfileWebState extends State { Widget build(BuildContext context) { commentsAndPosts = Provider.of(context, listen: true) .gettingPostCommentData; - return ListView( - scrollDirection: Axis.vertical, - controller: widget.scrollController, - children: [ - Row( - // mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Expanded( - flex: 2, - child: Column( - children: [ - SizedBox( - height: 4.h, + return (_isLoading || _isLoadMoreRunning) + ? const LoadingReddit() + : (commentsAndPosts == null || commentsAndPosts!.isEmpty) + ? Center( + child: Column( + children: [ + SizedBox( + height: 30.h, + ), + const Icon( + Icons.reddit, + size: 100, + color: Colors.black, + ), + const Text( + 'Wow,such empty', + style: TextStyle(color: Colors.grey), + ) + ], + ), + ) + : PostCommentList( + leftMargin: 15.w, + rightMargin: 35.w, + topOfTheList: Container( + height: 65.h, + width: 50.h, + margin: EdgeInsets.only(left: 15.w), + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + MyProfileCardInformationWeb( + loadProfile: widget.loadProfile), + SortBottomWeb( + page: 1, + userName: widget.loadProfile.userName.toString()), + ], ), - Container( - height: 6.h, - width: 50.w, - margin: EdgeInsets.only(left: 100, bottom: 10, top: 30), - child: SortBottomWeb( - page: 1, - userName: widget.loadProfile.userName.toString()), - color: Colors.white, - ), - (_isLoading || _isLoadMoreRunning) - ? const LoadingReddit() - : (commentsAndPosts == null || commentsAndPosts!.isEmpty) - ? Center( - child: Column( - children: [ - SizedBox( - height: 30.h, - ), - const Icon( - Icons.reddit, - size: 100, - color: Colors.black, - ), - const Text( - 'Wow,such empty', - style: TextStyle(color: Colors.grey), - ) - ], - ), - ) - : SingleChildScrollView( - child: ListView.builder( - physics: const ClampingScrollPhysics(), - shrinkWrap: true, - scrollDirection: Axis.vertical, - itemBuilder: ((context, index) => InkWell( - child: Container( - padding: const EdgeInsets.all(10), - width: double.infinity, - margin: EdgeInsets.all(20), - color: const Color.fromARGB( - 255, 255, 255, 255), - child: Column( - children: [ - (commentsAndPosts![index]['type'] == - 'comment') - ? ListTile( - title: Text( - commentsAndPosts![index] - ['data'] - .title - .toString()), - subtitle: Column( - crossAxisAlignment: - CrossAxisAlignment - .start, - mainAxisAlignment: - MainAxisAlignment - .start, - children: [ - Text.rich( - TextSpan( - children: [ - TextSpan( - text: - '${(commentsAndPosts![index]['data'].ownerType == 'User') ? 'u/' : 'r/'}${commentsAndPosts![index]['data'].owner}.${dateOfcomment(commentsAndPosts![index]['data'].createdAt.toString())} .${commentsAndPosts![index]['data'].votes}'), - const WidgetSpan( - child: Icon( - Typicons.up, - size: 15, - )), - ], - ), - ), - Text(commentsAndPosts![ - index]['data'] - .text - .toString()), - ], - ), - ) - : Container( - margin: EdgeInsets.only( - left: 100, - bottom: 90, - top: 30), - height: 30.h, - width: 50.w, - decoration: BoxDecoration( - color: Colors.white, - border: Border.all( - color: Colors.blue, - width: 3, - )), - child: Column(children: [ - Expanded( - child: Row( - children: const [ - Expanded( - child: ListTile( - title: Text( - 'Post'), - ), - ), - ], - ), - ) - ]), - ), - const Divider() - ], - ), - ), - onTap: () {}, - )), - itemCount: commentsAndPosts?.length, - ), - ) - ], - ), - ), - MyProfileCardInformationWeb(loadProfile: widget.loadProfile), - ], - ), - ], - ); + ), + userName: widget.loadProfile.userName.toString(), + updateData: _loadMore, + data: commentsAndPosts as List>, + type: 'Profile', + ); } } diff --git a/lib/widgets/post_sort_bottom.dart b/lib/widgets/post_sort_bottom.dart index c12142f5..769e3084 100644 --- a/lib/widgets/post_sort_bottom.dart +++ b/lib/widgets/post_sort_bottom.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; -import '../providers/Profile_provider.dart'; +import '../providers/profile_provider.dart'; import '../providers/subreddit_posts_provider.dart'; import 'package:responsive_sizer/responsive_sizer.dart'; @@ -50,12 +50,6 @@ class PostSortBottomState extends State bool _isLoading = false; @override void didChangeDependencies() { - if (_isInit) { - print( - '===============================insideSortButton============================='); - // tappedIndex = 0; - } - _isInit = false; super.didChangeDependencies(); } diff --git a/lib/widgets/profile_comments.dart b/lib/widgets/profile_comments.dart index 3d929596..273c4482 100644 --- a/lib/widgets/profile_comments.dart +++ b/lib/widgets/profile_comments.dart @@ -2,7 +2,7 @@ import 'package:responsive_sizer/responsive_sizer.dart'; import 'package:provider/provider.dart'; import 'package:fluttericon/typicons_icons.dart'; import 'package:flutter/material.dart'; -import '../providers/Profile_provider.dart'; +import '../providers/profile_provider.dart'; import '../models/comments_data.dart'; import '../widgets/loading_reddit.dart'; @@ -45,6 +45,8 @@ class ProfileCommentsState extends State { } ScrollController _scrollController = new ScrollController(); + //=====================================this function is used to======================================// + //=================Loading More Data====================// void loadMore() async { if (_isLoading == false && _isLoadMoreRunning == false && @@ -73,19 +75,20 @@ class ProfileCommentsState extends State { }); } } - +//=====================================this function is used to======================================// + //=================Start loading More Data====================// bool toggleLoadingMore() => _isLoadMoreRunning = !_isLoadMoreRunning; @override void initState() { // TODO: implement initState super.initState(); - print( - '==============================inint profile Post======================'); _scrollController = ScrollController()..addListener(loadMore); } - +//=====================================this function is used to======================================// + //=================Loading Data for first time====================// + @override Future didChangeDependencies() async { // TODO: implement didChangeDependencies @@ -154,7 +157,7 @@ class ProfileCommentsState extends State { padding: const EdgeInsets.all(10), width: double.infinity, // height: 15.h, - color: const Color.fromARGB(255, 255, 255, 255), + color: Color.fromARGB(255, 107, 38, 38), child: Column( children: [ ListTile( diff --git a/lib/widgets/profile_posts.dart b/lib/widgets/profile_posts.dart index faa756d0..7b083ee9 100644 --- a/lib/widgets/profile_posts.dart +++ b/lib/widgets/profile_posts.dart @@ -8,7 +8,7 @@ import 'package:responsive_sizer/responsive_sizer.dart'; import './post_sort_bottom.dart'; import '../post/widgets/post_list.dart'; import 'package:provider/provider.dart'; -import '../providers/Profile_provider.dart'; +import '../providers/profile_provider.dart'; import '../post/models/post_model.dart'; // import '..' @@ -35,6 +35,8 @@ class ProfilePostsState extends State { bool _isLoading = false; List? posts = []; late ScrollController _scrollController; + //=====================================this function is used to======================================// + //=================Loading More Data====================// void _loadMore() { if (_isLoading == false && _isLoadMoreRunning == false) { setState(() { @@ -50,19 +52,20 @@ class ProfilePostsState extends State { }); } } - +//=====================================this function is used to======================================// + //=================Start loading More Data====================// + bool toggleLoadingMore() => _isLoadMoreRunning = !_isLoadMoreRunning; @override void initState() { // TODO: implement initState super.initState(); - print( - '==============================inint profile Post======================'); - print(widget.userName); - // _scrollController = ScrollController()..addListener(_loadMore); - } + } +//=====================================this function is used to======================================// + //=================Loading Data for first time====================// + @override void didChangeDependencies() { if (_isInit) { diff --git a/lib/widgets/sort_bottom_web.dart b/lib/widgets/sort_bottom_web.dart index 8eb96ef8..7fa99c80 100644 --- a/lib/widgets/sort_bottom_web.dart +++ b/lib/widgets/sort_bottom_web.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; -import '../providers/Profile_provider.dart'; +import '../providers/profile_provider.dart'; import 'package:responsive_sizer/responsive_sizer.dart'; class SortBottomWeb extends StatefulWidget { @@ -27,7 +27,7 @@ class SortBottomWebState extends State { return Container( height: 10.h, width: 50.w, - margin: const EdgeInsets.only(left: 30), + // margin: const EdgeInsets.only(left: 30), child: Row( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, @@ -37,7 +37,7 @@ class SortBottomWebState extends State { color: Colors.white, // width: 10.w, height: 8.h, - margin: const EdgeInsets.only(left: 5), + // margin: const EdgeInsets.only(left: 5), child: Row( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.center, diff --git a/lib/widgets/subreddit_about.dart b/lib/widgets/subreddit_about.dart index 8ca78af5..2e85401b 100644 --- a/lib/widgets/subreddit_about.dart +++ b/lib/widgets/subreddit_about.dart @@ -25,17 +25,17 @@ class _SubredditAboutState extends State { child: ListView(scrollDirection: Axis.vertical, children: [ Container( color: Colors.white, - margin: EdgeInsets.only(top: 120), - padding: EdgeInsets.only(left: 10), + margin: const EdgeInsets.only(top: 120), + padding: const EdgeInsets.only(left: 10), child: Column( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ Container( - margin: EdgeInsets.all(10), - child: Text('Subreddit Rules', + margin: const EdgeInsets.all(10), + child: const Text('Subreddit Rules', style: TextStyle(fontWeight: FontWeight.bold))), - Divider(), + const Divider(), _renderrules(), ], ), @@ -52,25 +52,25 @@ class _SubredditAboutState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Container( - margin: EdgeInsets.all(4), - padding: EdgeInsets.all(3), + margin: const EdgeInsets.all(4), + padding: const EdgeInsets.all(3), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text('Moderators', + const Text('Moderators', textAlign: TextAlign.center, style: TextStyle( fontWeight: FontWeight.bold, fontSize: 15)), IconButton( onPressed: () => Navigator.pushNamed( context, ContactModMessageScreen.routeName), - icon: Icon( + icon: const Icon( Icons.mail_outlined, color: Colors.grey, )), ], )), - Divider(), + const Divider(), Expanded( child: ListView.builder( itemCount: widget.moderators.length, diff --git a/lib/widgets/subreddit_copy_share.dart b/lib/widgets/subreddit_copy_share.dart index 6bae8296..d1ec2a0f 100644 --- a/lib/widgets/subreddit_copy_share.dart +++ b/lib/widgets/subreddit_copy_share.dart @@ -23,7 +23,6 @@ class CopyShare extends StatelessWidget { child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, - // mainAxisSize: MainAxisSize.min, children: [ const BarWidget(), Container( diff --git a/lib/widgets/subreddit_join_button_web.dart b/lib/widgets/subreddit_join_button_web.dart index d6a08200..3747d767 100644 --- a/lib/widgets/subreddit_join_button_web.dart +++ b/lib/widgets/subreddit_join_button_web.dart @@ -7,13 +7,9 @@ import '../../widgets/custom_snack_bar.dart'; class SubredditJoinButtonWeb extends StatefulWidget { bool isJoined; - //String dropDownValue; String communityName; - //IconData icon; SubredditJoinButtonWeb( {required this.isJoined, - //required this.icon, - //required this.dropDownValue, required this.communityName}); @override @@ -35,7 +31,6 @@ class SubredditJoinButtonWebState extends State { return Container( width: 35.w, height: 6.h, - // color: Colors.yellow, child: Row( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, @@ -81,11 +76,12 @@ class SubredditJoinButtonWebState extends State { ], )); } + // ===================================the next two function used to===========================================// +//==================to join subreddit===========================// +//communityName==> commuintyUserName you want to join Future subscribe(BuildContext context) async { - print( - '===============================Subcribe=================================='); - bool sub = await Provider.of(context, listen: false) + bool sub = await Provider.of(context, listen: false) .joinAndDisjoinSubreddit(widget.communityName, 'sub', context); if (sub) { setState(() { @@ -103,14 +99,16 @@ class SubredditJoinButtonWebState extends State { } bool join() { - isJoinedstate = false; + isJoinedstate = true; return isJoinedstate; } - + // ===================================the next three function used to===========================================// +//==================to disjoin of subreddit===========================// +// have two option one:leave subreddit two: cancel +//communityName==> commuintyUserName you want to leave + Future unSubescribe(BuildContext ctx) async { - print( - '===============================Un Subcribe=================================='); - bool unSub = await Provider.of(context, listen: false) + bool unSub = await Provider.of(context, listen: false) .joinAndDisjoinSubreddit(widget.communityName, 'unsub', context); if (unSub) { setState(() { diff --git a/lib/widgets/subreddit_posts.dart b/lib/widgets/subreddit_posts.dart index 3a02202d..f83766b6 100644 --- a/lib/widgets/subreddit_posts.dart +++ b/lib/widgets/subreddit_posts.dart @@ -30,6 +30,9 @@ class _SubredditPosts extends State { bool _isLoading = false; List? posts = []; late ScrollController _scrollController; + //=====================================this function is used to======================================// + //=================Loading More Data====================// + void _loadMore() { if (_isLoading == false && _isLoadMoreRunning == false) { setState(() { @@ -52,7 +55,8 @@ class _SubredditPosts extends State { super.initState(); _scrollController = ScrollController()..addListener(_loadMore); } - +//=====================================this function is used to======================================// + //=================Loading Data for first time====================// @override void didChangeDependencies() async { if (_isInit) { @@ -121,35 +125,5 @@ class _SubredditPosts extends State { data: posts as List, type: 'community', ); - // ListView( - // controller: _scrollController, - // scrollDirection: Axis.vertical, - // physics: const ClampingScrollPhysics(), - // addAutomaticKeepAlives: true, - // shrinkWrap: true, - // children: [ - // PostSortBottom( - // page: _page, - // type: 'Subreddit', - // routeNamePop: widget.routeNamePop, - // userName: subredditName - // //_dropDownValue, _icon - // ), - // //Select the type of Posts - - // SingleChildScrollView( - // child: ListView.builder( - // physics: const ClampingScrollPhysics(), - // shrinkWrap: true, - // itemBuilder: ((context, index) => Post.community( - // data: posts![index], - // inView: false, - // updateDate: () {}, - // )), - // itemCount: posts?.length, - // ), - // ) - // ], - // ); } } diff --git a/test/moderated_subreddit/widgets/moderated_subreddit_pop_up_menu_button_test.dart b/test/moderated_subreddit/widgets/moderated_subreddit_pop_up_menu_button_test.dart index 06dff201..0adaa818 100644 --- a/test/moderated_subreddit/widgets/moderated_subreddit_pop_up_menu_button_test.dart +++ b/test/moderated_subreddit/widgets/moderated_subreddit_pop_up_menu_button_test.dart @@ -13,6 +13,16 @@ void main() { int result = subredditpopupButtonState.chooseNotificationType(1); expect(result, 1); }); + test('Subscribe ', () { + // TODO: Implement test + bool result = subredditpopupButtonState.changeJoinStatus(); + expect(result, true); + }); + test('UnSubcribe ', () { + // TODO: Implement test + bool result = subredditpopupButtonState.changeDisJoinStatus(); + expect(result, false); + }); }, ); } diff --git a/test/other_profile/widgets/follow_button_test.dart b/test/other_profile/widgets/follow_button_test.dart new file mode 100644 index 00000000..af5adb54 --- /dev/null +++ b/test/other_profile/widgets/follow_button_test.dart @@ -0,0 +1,21 @@ +import 'package:flutter_test/flutter_test.dart'; +import '../../../lib/widgets/follow_button.dart'; +void main() { + group( + 'follow button ...', + () { + // final createCommunity = CreateCommunity(); + final followButtonsState = FollowButtonState(); + test('Follow test', () { + // TODO: Implement test + bool result = followButtonsState.followSucceeded(); + expect(result, true); + }); + test('unFollow', () { + // TODO: Implement test + bool result = followButtonsState.unFollowsucceeded(); + expect(result, false); + }); + }, + ); +} \ No newline at end of file diff --git a/test/other_profile/widgets/invite_button_test.dart b/test/other_profile/widgets/invite_button_test.dart deleted file mode 100644 index 5de2a396..00000000 --- a/test/other_profile/widgets/invite_button_test.dart +++ /dev/null @@ -1,19 +0,0 @@ -// import 'package:flutter_test/flutter_test.dart'; -// import 'package:post/other_profile/widgets/invite_button.dart'; -// import '../../other_profile/widgets/invite_button_test.dart'; -// void main() { -// group( -// 'invite button ...', -// () { -// // final createCommunity = CreateCommunity(); -// final inviteButtonState = InviteButtonState(); -// test('changePost', () { -// // TODO: Implement test -// int result = inviteButtonState.invite(context); -// expect(result, 1); -// }); - - -// }, -// ); -// } \ No newline at end of file diff --git a/test/widgets/barwidget_test.dart b/test/other_profile/widgets/pop_down_menu_test.dart similarity index 65% rename from test/widgets/barwidget_test.dart rename to test/other_profile/widgets/pop_down_menu_test.dart index 54d9c171..227d8716 100644 --- a/test/widgets/barwidget_test.dart +++ b/test/other_profile/widgets/pop_down_menu_test.dart @@ -1,7 +1,7 @@ import 'package:flutter_test/flutter_test.dart'; void main() { - testWidgets('barwidget ...', (tester) async { + testWidgets('pop down menu ...', (tester) async { // TODO: Implement test }); } \ No newline at end of file diff --git a/test/other_profile/widgets/position_other_profile_web_test.dart b/test/other_profile/widgets/position_other_profile_web_test.dart new file mode 100644 index 00000000..0d143cac --- /dev/null +++ b/test/other_profile/widgets/position_other_profile_web_test.dart @@ -0,0 +1,22 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:post/other_profile/widgets/position_other_profile_web.dart'; +import '../../other_profile/widgets/position_other_profile_web_test.dart'; +void main() { + group( + 'follow button web ...', + () { + // final createCommunity = CreateCommunity(); + final positionOtherProfileWebState =PositionOtherProfileWebState(); + test('Follow test', () { + // TODO: Implement test + bool result = positionOtherProfileWebState.followSucceeded(); + expect(result, true); + }); + test('unFollow', () { + // TODO: Implement test + bool result = positionOtherProfileWebState.unFollowsucceeded(); + expect(result, false); + }); + }, + ); +} \ No newline at end of file diff --git a/test/subreddit/widgets/subreddit_join_buttons_test.dart b/test/subreddit/widgets/subreddit_join_buttons_test.dart index db7fcf72..47aa6305 100644 --- a/test/subreddit/widgets/subreddit_join_buttons_test.dart +++ b/test/subreddit/widgets/subreddit_join_buttons_test.dart @@ -14,11 +14,17 @@ void main() { expect(result, 1); }); - test('subreddit join buttons test ', () { + test('subreddit Disjoin buttons test ', () { // TODO: Implement test bool result = subredditJoinButtonState.disJoin(); expect(result, false); }); + + test('subreddit join buttons test ', () { + // TODO: Implement test + bool result = subredditJoinButtonState.join(); + expect(result, true); + }); }, ); } diff --git a/test/widgets/myprofile_comment_web_test.dart b/test/widgets/myprofile_comment_web_test.dart new file mode 100644 index 00000000..ebddb744 --- /dev/null +++ b/test/widgets/myprofile_comment_web_test.dart @@ -0,0 +1,7 @@ +import 'package:flutter_test/flutter_test.dart'; + +void main() { + testWidgets('myprofile comment web ...', (tester) async { + // TODO: Implement test + }); +} \ No newline at end of file diff --git a/test/widgets/post_sort_bottom_test.dart b/test/widgets/post_sort_bottom_test.dart index 7c22315c..9fc3c25e 100644 --- a/test/widgets/post_sort_bottom_test.dart +++ b/test/widgets/post_sort_bottom_test.dart @@ -8,17 +8,22 @@ void main() { () { // final createCommunity = CreateCommunity(); final posttypeButtonState = PostSortBottomState(); - test('changePost', () { + test('changePost to Hot', () { + // TODO: Implement test + int result = posttypeButtonState.choosePostType(0); + expect(result, 0); + }); + test('changePost to New', () { // TODO: Implement test int result = posttypeButtonState.choosePostType(1); expect(result, 1); }); - - test('changetopPost', () { + test('changePost to Top', () { // TODO: Implement test - int result = posttypeButtonState.chooseTimeOfTopPosts(2,1); - expect(result, 1); + int result = posttypeButtonState.choosePostType(2); + expect(result, 2); }); + }, ); } diff --git a/test/widgets/profile_comments_test.dart b/test/widgets/profile_comments_test.dart new file mode 100644 index 00000000..897e3165 --- /dev/null +++ b/test/widgets/profile_comments_test.dart @@ -0,0 +1,32 @@ +import 'package:flutter_test/flutter_test.dart'; +import '../../lib/widgets/profile_comments.dart'; +void main() { + group( + 'profile comments ...', + () { + // final createCommunity = CreateCommunity(); + final profileCommentsState = ProfileCommentsState(); + test('Comment date1', () { + // TODO: Implement test + String result = profileCommentsState.dateOfcomment('2022-09-24T14:15:22Z'); + expect(result, '3mon'); + }); + test('Comment date2', () { + // TODO: Implement test + String result = profileCommentsState.dateOfcomment('2021-08-24T14:15:22'); + expect(result, '1y'); + }); + test('Comment date3', () { + // TODO: Implement test + String result = profileCommentsState.dateOfcomment('2022-11-24T14:15:22Z'); + expect(result, '3w'); + }); + + test('Loading more comments', () { + // TODO: Implement test + bool result = profileCommentsState.toggleLoadingMore(); + expect(result, true); + }); + }, + ); +} diff --git a/test/widgets/profile_posts_test.dart b/test/widgets/profile_posts_test.dart new file mode 100644 index 00000000..b3fd885a --- /dev/null +++ b/test/widgets/profile_posts_test.dart @@ -0,0 +1,16 @@ +// import 'package:flutter_test/flutter_test.dart'; +// import '../../lib/widgets/profile_posts.dart'; +// void main() { +// group( +// 'profile posts ...', +// () { +// // final createCommunity = CreateCommunity(); +// final profilePostsState = ProfilePostsState(); +// test('Loading more comments', () { +// // TODO: Implement test +// bool result = profilePostsState.toggleLoadingMore(); +// expect(result, true); +// }); +// }, +// ); +// } diff --git a/test/widgets/subreddit_join_button_web_test.dart b/test/widgets/subreddit_join_button_web_test.dart index 0b4aa4cb..204b3f53 100644 --- a/test/widgets/subreddit_join_button_web_test.dart +++ b/test/widgets/subreddit_join_button_web_test.dart @@ -7,11 +7,17 @@ void main() { () { // final createCommunity = CreateCommunity(); final joinButtonState = SubredditJoinButtonWebState(); - test(' joinButtonSubredditWeb', () { + test(' disjoinButtonSubredditWeb', () { // TODO: Implement test bool result = joinButtonState.disJoin(); expect(result, false); }); + + test(' joinButtonSubredditWeb', () { + // TODO: Implement test + bool result = joinButtonState.join(); + expect(result, true); + }); }, ); } From bbf5d591d4c5d43d810eed44e2b1f74722f31040 Mon Sep 17 00:00:00 2001 From: zeinabmoawad Date: Fri, 23 Dec 2022 22:35:17 +0200 Subject: [PATCH 12/16] Deleted Message File --- lib/messages/provider/message_provider.dart | 192 -------------------- 1 file changed, 192 deletions(-) delete mode 100644 lib/messages/provider/message_provider.dart diff --git a/lib/messages/provider/message_provider.dart b/lib/messages/provider/message_provider.dart deleted file mode 100644 index d3cdb0be..00000000 --- a/lib/messages/provider/message_provider.dart +++ /dev/null @@ -1,192 +0,0 @@ -import 'package:dio/dio.dart'; -import 'package:flutter/material.dart'; -import 'package:shared_preferences/shared_preferences.dart'; - -import '../../networks/dio_client.dart'; -import '../../widgets/handle_error.dart'; -import '../models/user_message.dart'; - -class MessageProvider with ChangeNotifier { - Map> messageShow = {}; - List allMessage = []; - List unreadMessage = []; - List sentMessage = []; -//http://localhost:8000/api/v1/subreddits/{subredditName}/moderators/{moderatorName} - Future getAllMessages(context, page, limit) async { - try { - final prefs = await SharedPreferences.getInstance(); - DioClient.init(prefs); - messageShow = {}; - await DioClient.get( - path: '/messages', - query: {'page': page, 'limit': limit}).then((value) { - value.data['data'].forEach((element) { - String id; - final message = ShowMessagesModel.fromJson(element); - if (message.type == 'userMessage') { - id = message.subjectId!; - } else { - id = message.sId!; - } - messageShow.putIfAbsent(id, () => []).add(message); - }); - notifyListeners(); - }); - } on DioError catch (e) { - HandleError.errorHandler(e, context); - } catch (error) { - HandleError.handleError(error.toString(), context); - } - } - - Future getUnreadMessages(context, page, limit) async { - try { - final prefs = await SharedPreferences.getInstance(); - DioClient.init(prefs); - unreadMessage = []; - await DioClient.get( - path: '/messages/unread', - query: {'page': page, 'limit': limit}).then((value) { - value.data['data'].forEach((element) { - if (element['type'] != 'postReply') { - final message = ShowMessagesModel.fromJson(element); - unreadMessage.add(message); - } - }); - notifyListeners(); - }); - } on DioError catch (e) { - HandleError.errorHandler(e, context); - } catch (error) { - HandleError.handleError(error.toString(), context); - } - } - - Future getSentMessages(context, page, limit) async { - try { - final prefs = await SharedPreferences.getInstance(); - DioClient.init(prefs); - sentMessage = []; - await DioClient.get( - path: '/messages/sent', - query: {'page': page, 'limit': limit}).then((value) { - value.data['data'].forEach((element) { - if (element['type'] != 'postReply') { - final message = ShowMessagesModel.fromJson(element); - sentMessage.add(message); - } - }); - notifyListeners(); - }); - } on DioError catch (e) { - HandleError.errorHandler(e, context); - } catch (error) { - HandleError.handleError(error.toString(), context); - } - } - - Future getMessages(context, page, limit) async { - try { - final prefs = await SharedPreferences.getInstance(); - DioClient.init(prefs); - allMessage = []; - await DioClient.get( - path: '/messages/all', - query: {'page': page, 'limit': limit}).then((value) { - value.data['data'].forEach((element) { - if (element['type'] != 'postReply') { - final message = ShowMessagesModel.fromJson(element); - allMessage.add(message); - } - }); - notifyListeners(); - }); - } on DioError catch (e) { - HandleError.errorHandler(e, context); - } catch (error) { - HandleError.handleError(error.toString(), context); - } - } - - Future createMessage(data, context) async { - try { - final prefs = await SharedPreferences.getInstance(); - DioClient.init(prefs); - DioClient.post(path: '/messages', data: data); - notifyListeners(); - } on DioError catch (e) { - HandleError.errorHandler(e, context); - } catch (error) { - HandleError.handleError(error.toString(), context); - } - } - - Future replyMessage(data, context, messageId) async { - try { - final prefs = await SharedPreferences.getInstance(); - DioClient.init(prefs); - - DioClient.post(path: '/messages/$messageId/reply', data: data); - notifyListeners(); - } on DioError catch (e) { - HandleError.errorHandler(e, context); - } catch (error) { - HandleError.handleError(error.toString(), context); - } - } - - Future blockUser(context, userName) async { - //http://localhost:8000/api/v1/users/{userName}/block_user - try { - final prefs = await SharedPreferences.getInstance(); - DioClient.init(prefs); - DioClient.post(path: '/users/$userName/block_user', data: {}); - notifyListeners(); - } on DioError catch (e) { - HandleError.errorHandler(e, context); - } catch (error) { - HandleError.handleError(error.toString(), context); - } - } - - Future acceptSubredditInvite(context, subredditName) async { - try { - final prefs = await SharedPreferences.getInstance(); - DioClient.init(prefs); - DioClient.post( - path: '/subreddits/$subredditName/accept/invitation', data: {}); - notifyListeners(); - } on DioError catch (e) { - HandleError.errorHandler(e, context); - } catch (error) { - HandleError.handleError(error.toString(), context); - } - } - - Future deleteMessage(context, messageId) async { - try { - final prefs = await SharedPreferences.getInstance(); - DioClient.init(prefs); - - DioClient.delete(path: '/messages/$messageId'); - notifyListeners(); - } on DioError catch (e) { - HandleError.errorHandler(e, context); - } catch (error) { - HandleError.handleError(error.toString(), context); - } - } - - Future markAllAsRead(context) async { - try { - final prefs = await SharedPreferences.getInstance(); - DioClient.init(prefs); - DioClient.patch(path: '/messages/mark_as_read'); - notifyListeners(); - } on DioError catch (e) { - HandleError.errorHandler(e, context); - } catch (error) { - HandleError.handleError(error.toString(), context); - } - } -} From da661ed777d0d39a15db948a64817f52544a0469 Mon Sep 17 00:00:00 2001 From: ahmedmadbouly186 <66012617+ahmedmadbouly186@users.noreply.github.com> Date: Fri, 23 Dec 2022 22:53:46 +0200 Subject: [PATCH 13/16] Add files via upload --- lib/messages/provider/message_provider.dart | 192 ++++++++++++++++++++ 1 file changed, 192 insertions(+) create mode 100644 lib/messages/provider/message_provider.dart diff --git a/lib/messages/provider/message_provider.dart b/lib/messages/provider/message_provider.dart new file mode 100644 index 00000000..4c68236c --- /dev/null +++ b/lib/messages/provider/message_provider.dart @@ -0,0 +1,192 @@ +import 'package:dio/dio.dart'; +import 'package:flutter/material.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +import '../../networks/dio_client.dart'; +import '../../widgets/handle_error.dart'; +import '../models/user_message.dart'; + +class MessageProvider with ChangeNotifier { + Map> messageShow = {}; + List allMessage = []; + List unreadMessage = []; + List sentMessage = []; +//http://localhost:8000/api/v1/subreddits/{subredditName}/moderators/{moderatorName} + Future getAllMessages(context, page, limit) async { + try { + final prefs = await SharedPreferences.getInstance(); + DioClient.init(prefs); + messageShow = {}; + await DioClient.get( + path: '/messages', + query: {'page': page, 'limit': limit}).then((value) { + value.data['data'].forEach((element) { + String id; + final message = ShowMessagesModel.fromJson(element); + if (message.type == 'userMessage') { + id = message.subjectId!; + } else { + id = message.sId!; + } + messageShow.putIfAbsent(id, () => []).add(message); + }); + notifyListeners(); + }); + } on DioError catch (e) { + HandleError.errorHandler(e, context); + } catch (error) { + HandleError.handleError(error.toString(), context); + } + } + + Future getUnreadMessages(context, page, limit) async { + try { + final prefs = await SharedPreferences.getInstance(); + DioClient.init(prefs); + unreadMessage = []; + await DioClient.get( + path: '/messages/unread', + query: {'page': page, 'limit': limit}).then((value) { + value.data['data'].forEach((element) { + if (element['type'] != 'postReply') { + final message = ShowMessagesModel.fromJson(element); + unreadMessage.add(message); + } + }); + notifyListeners(); + }); + } on DioError catch (e) { + HandleError.errorHandler(e, context); + } catch (error) { + HandleError.handleError(error.toString(), context); + } + } + + Future getSentMessages(context, page, limit) async { + try { + final prefs = await SharedPreferences.getInstance(); + DioClient.init(prefs); + sentMessage = []; + await DioClient.get( + path: '/messages/sent', + query: {'page': page, 'limit': limit}).then((value) { + value.data['data'].forEach((element) { + if (element['type'] != 'postReply') { + final message = ShowMessagesModel.fromJson(element); + sentMessage.add(message); + } + }); + notifyListeners(); + }); + } on DioError catch (e) { + HandleError.errorHandler(e, context); + } catch (error) { + HandleError.handleError(error.toString(), context); + } + } + + Future getMessages(context, page, limit) async { + try { + final prefs = await SharedPreferences.getInstance(); + DioClient.init(prefs); + allMessage = []; + await DioClient.get( + path: '/messages/all', + query: {'page': page, 'limit': limit}).then((value) { + value.data['data'].forEach((element) { + if (element['type'] != 'postReply') { + final message = ShowMessagesModel.fromJson(element); + allMessage.add(message); + } + }); + notifyListeners(); + }); + } on DioError catch (e) { + HandleError.errorHandler(e, context); + } catch (error) { + HandleError.handleError(error.toString(), context); + } + } + + Future createMessage(data, context) async { + try { + final prefs = await SharedPreferences.getInstance(); + DioClient.init(prefs); + DioClient.post(path: '/messages', data: data); + notifyListeners(); + } on DioError catch (e) { + HandleError.errorHandler(e, context); + } catch (error) { + HandleError.handleError(error.toString(), context); + } + } + + Future replyMessage(data, context, messageId) async { + try { + final prefs = await SharedPreferences.getInstance(); + DioClient.init(prefs); + + DioClient.post(path: '/messages/$messageId/reply', data: data); + notifyListeners(); + } on DioError catch (e) { + HandleError.errorHandler(e, context); + } catch (error) { + HandleError.handleError(error.toString(), context); + } + } + + Future blockUser(context, userName) async { + //http://localhost:8000/api/v1/users/{userName}/block_user + try { + final prefs = await SharedPreferences.getInstance(); + DioClient.init(prefs); + DioClient.post(path: '/users/$userName/block_user', data: {}); + notifyListeners(); + } on DioError catch (e) { + HandleError.errorHandler(e, context); + } catch (error) { + HandleError.handleError(error.toString(), context); + } + } + + Future acceptSubredditInvite(context, subredditName) async { + try { + final prefs = await SharedPreferences.getInstance(); + DioClient.init(prefs); + DioClient.post( + path: '/subreddits/$subredditName/accept/invitation', data: {}); + notifyListeners(); + } on DioError catch (e) { + HandleError.errorHandler(e, context); + } catch (error) { + HandleError.handleError(error.toString(), context); + } + } + + Future deleteMessage(context, messageId) async { + try { + final prefs = await SharedPreferences.getInstance(); + DioClient.init(prefs); + + DioClient.delete(path: '/messages/$messageId'); + notifyListeners(); + } on DioError catch (e) { + HandleError.errorHandler(e, context); + } catch (error) { + HandleError.handleError(error.toString(), context); + } + } + + Future markAllAsRead(context) async { + try { + final prefs = await SharedPreferences.getInstance(); + DioClient.init(prefs); + DioClient.patch(path: '/messages/mark_as_read'); + notifyListeners(); + } on DioError catch (e) { + HandleError.errorHandler(e, context); + } catch (error) { + HandleError.handleError(error.toString(), context); + } + } +} From 1ad8ecd1eab9a95585d64954de32f388b010fdba Mon Sep 17 00:00:00 2001 From: ahmedmadbouly186 <66012617+ahmedmadbouly186@users.noreply.github.com> Date: Fri, 23 Dec 2022 23:01:44 +0200 Subject: [PATCH 14/16] change the provider name --- lib/providers/{Profile_provider.dart => profile_provider.dart} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename lib/providers/{Profile_provider.dart => profile_provider.dart} (100%) diff --git a/lib/providers/Profile_provider.dart b/lib/providers/profile_provider.dart similarity index 100% rename from lib/providers/Profile_provider.dart rename to lib/providers/profile_provider.dart From 81b802db2944d64e7953438fb37bb3a026f21d4c Mon Sep 17 00:00:00 2001 From: ahmedmadbouly186 <66012617+ahmedmadbouly186@users.noreply.github.com> Date: Fri, 23 Dec 2022 23:07:23 +0200 Subject: [PATCH 15/16] remove error include --- lib/createpost/screens/createpost.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/createpost/screens/createpost.dart b/lib/createpost/screens/createpost.dart index 8e968364..8a829e6e 100644 --- a/lib/createpost/screens/createpost.dart +++ b/lib/createpost/screens/createpost.dart @@ -9,7 +9,6 @@ import 'package:flutter/services.dart'; import 'package:get/get.dart'; import 'package:image_picker/image_picker.dart'; import 'package:multi_image_picker/multi_image_picker.dart'; -import 'package:post/discover/discover.dart'; import 'package:post/home/screens/home_layout.dart'; import 'dart:async'; import '../../home/controller/home_controller.dart'; From e5783bd11f22a6d140337b6357386d0f0cf5703f Mon Sep 17 00:00:00 2001 From: zeinabmoawad Date: Fri, 23 Dec 2022 23:52:20 +0200 Subject: [PATCH 16/16] final web --- lib/comments/screens/add_comment_screen.dart | 17 +- lib/comments/screens/add_reply_screen.dart | 19 +- lib/comments/widgets/comment.dart | 15 +- lib/comments/widgets/comment_footer.dart | 72 +- lib/comments/widgets/comment_popup_menu.dart | 2 +- lib/comments/widgets/comments_list.dart | 7 + lib/comments/widgets/view_comment.dart | 4 +- .../provider/create_community_provider.dart | 2 - .../screens/create_community.dart | 10 - .../widgets/clear_text_field.dart | 1 - .../widgets/community_type.dart | 1 - .../widgets/create_community_web.dart | 9 - .../controllers/posts_controllers.dart | 153 +-- lib/createpost/screens/createpost.dart | 1084 +++++++++++------ lib/createpost/screens/finalpost.dart | 614 +++++----- lib/createpost/services/post_services.dart | 22 +- lib/createpost/widgets/type_of_post_web.dart | 265 ++++ lib/home/controller/home_controller.dart | 201 ++- lib/home/screens/all.dart | 712 +++++++---- lib/home/screens/history.dart | 15 +- lib/home/screens/home_layout.dart | 383 +++++- lib/home/screens/saved.dart | 108 +- lib/home/screens/saved_comments.dart | 105 +- lib/home/screens/saved_posts.dart | 107 +- lib/home/widgets/buttom_nav_bar.dart | 5 +- lib/home/widgets/custom_upper_bar.dart | 443 +++++++ lib/home/widgets/end_drawer.dart | 309 ++--- lib/home/widgets/new_drawer.dart | 2 +- lib/logins/providers/authentication.dart | 104 +- lib/logins/screens/forgot_password.dart | 25 +- lib/logins/screens/forgot_username.dart | 23 +- lib/logins/screens/gender.dart | 4 +- lib/logins/screens/login.dart | 33 +- lib/logins/screens/signup.dart | 37 +- lib/logins/widgets/basic_buttom.dart | 3 + lib/logins/widgets/continue_with_email.dart | 4 +- .../widgets/continue_with_facebook.dart | 4 +- lib/logins/widgets/continue_with_google.dart | 4 +- lib/logins/widgets/hyperlink_text.dart | 6 +- lib/logins/widgets/password_input.dart | 19 +- lib/logins/widgets/text_input.dart | 14 +- lib/logins/widgets/upper_bar.dart | 15 +- lib/logins/widgets/upper_text.dart | 4 +- lib/logins/widgets/web_image.dart | 2 - lib/main.dart | 16 +- lib/messages/models/user_message.dart | 11 +- lib/messages/screens/message_main_screen.dart | 24 +- lib/messages/screens/new_message_screen.dart | 30 +- .../screens/reply_message_screen.dart | 2 +- lib/messages/screens/show_message_body.dart | 33 +- .../screens/unread_message_screen.dart | 400 +++--- .../screens/web_message_all_screen.dart | 181 ++- lib/messages/screens/web_message_screen.dart | 466 ++++--- .../screens/web_new_message_screen.dart | 409 +++---- lib/messages/screens/web_sent_message.dart | 360 +++--- lib/messages/widgets/first_nav_bar.dart | 71 ++ lib/messages/widgets/second_nav_bar.dart | 53 + lib/models/subreddit_data.dart | 2 +- .../screens/moderated_subreddit_screen.dart | 15 +- ...erated_subreddit_card_information_web.dart | 7 +- .../widgets/moderated_subreddit_web.dart | 19 +- .../position_for_moderated_subredditInfo.dart | 1 - .../models/moderation_posts.dart | 262 ++++ .../models/post_flair_model.dart | 3 - .../provider/moderation_general_data.dart | 73 +- .../provider/post_flair_provider.dart | 16 - .../screens/Edited_posts.dart | 0 .../screens/add_edit_aproved_screen.dart | 43 +- .../screens/add_edit_banned_screen.dart | 95 +- .../screens/add_edit_moderator_screen.dart | 111 +- .../screens/add_edit_muted_screen.dart | 20 +- .../screens/add_edit_post_flair.dart | 45 +- .../screens/approved_users_screen.dart | 42 +- .../screens/banned_user_sceen.dart | 42 +- .../screens/community_type_screen.dart | 77 +- .../screens/description_screen.dart | 48 +- .../screens/location_screen.dart | 57 +- .../screens/moderator_tools_screen.dart | 12 +- .../screens/moderators_screen.dart | 44 +- .../screens/muted_user_screen.dart | 38 +- .../screens/post_flair.dart | 21 +- .../screens/post_flair_settings.dart | 5 +- .../screens/post_types_screen.dart | 77 +- .../screens/spam_posts.dart | 0 .../screens/topics_screen.dart | 2 - .../screens/traffic_state.dart | 116 +- .../screens/un_moderated_posts.dart | 16 + .../widgets/alert_dialog.dart | 2 - .../widgets/topics_button.dart | 1 - lib/myprofile/screens/myprofile_screen.dart | 15 +- lib/myprofile/widgets/myprofile_web.dart | 28 +- lib/networks/const_endpoint_data.dart | 3 - lib/networks/dio_client.dart | 3 - .../models/notification_class_model.dart | 489 -------- .../provider/notification_provider.dart | 43 - .../screens/notifications_main_screen.dart | 4 - .../screens/notifications_screen.dart | 14 +- .../widgets/main_modal_bottom_sheet.dart | 2 - .../screens/others_profile_screen.dart | 15 +- .../widgets/other_profile_web.dart | 20 +- lib/post/models/post_model.dart | 24 +- lib/post/widgets/post.dart | 14 +- lib/post/widgets/post_body.dart | 8 +- lib/post/widgets/post_body_in_screen.dart | 39 +- lib/post/widgets/post_card.dart | 6 +- lib/post/widgets/post_comment_list.dart | 26 +- lib/post/widgets/post_footer.dart | 6 +- lib/post/widgets/post_header.dart | 10 +- lib/post/widgets/post_link_in_screen.dart | 2 +- lib/post/widgets/post_list.dart | 17 + lib/post/widgets/post_mod_popup.dart | 28 +- lib/post/widgets/post_mod_tools.dart | 31 +- ..._popup_menu.dart => post_pop_up_menu.dart} | 9 + lib/post/widgets/post_pop_up_web.dart | 4 +- lib/post/widgets/post_tags_and_title.dart | 12 + lib/post/widgets/post_video_in_widget.dart | 15 + .../widgets/post_video_in_widget_web.dart | 26 +- lib/post/widgets/user_info_popup.dart | 11 +- lib/providers/Profile_provider.dart | 29 +- lib/providers/subreddit_posts_provider.dart | 14 +- lib/search/provider/search_provider.dart | 12 +- lib/search/screens/search_inside.dart | 297 +++-- lib/subreddit/screens/subreddit_screen.dart | 37 +- lib/subreddit/widgets/subreddit_web.dart | 27 +- lib/widgets/subreddit_join_button_web.dart | 2 +- 125 files changed, 5977 insertions(+), 3721 deletions(-) create mode 100644 lib/createpost/widgets/type_of_post_web.dart create mode 100644 lib/home/widgets/custom_upper_bar.dart create mode 100644 lib/messages/widgets/first_nav_bar.dart create mode 100644 lib/messages/widgets/second_nav_bar.dart create mode 100644 lib/moderation_settings/models/moderation_posts.dart create mode 100644 lib/moderation_settings/screens/Edited_posts.dart create mode 100644 lib/moderation_settings/screens/spam_posts.dart create mode 100644 lib/moderation_settings/screens/un_moderated_posts.dart rename lib/post/widgets/{post_popup_menu.dart => post_pop_up_menu.dart} (96%) diff --git a/lib/comments/screens/add_comment_screen.dart b/lib/comments/screens/add_comment_screen.dart index 16ae09ff..f1020e2c 100644 --- a/lib/comments/screens/add_comment_screen.dart +++ b/lib/comments/screens/add_comment_screen.dart @@ -15,9 +15,6 @@ class _AddCommentScreenState extends State { TextEditingController controller = TextEditingController(); @override void didChangeDependencies() { - // TODO: implement didChangeDependencies - //===============================Fetch subreddit data =======================================// - var temp = ModalRoute.of(context)?.settings.arguments as Map; parentId = temp['parentId']; @@ -27,9 +24,7 @@ class _AddCommentScreenState extends State { createComment(String text) async { if (await Provider.of(context, listen: false) - .postComment(parentId, 'Post', text)) { - print('created comment'); - } + .postComment(parentId, 'Post', text)) {} } @override @@ -37,7 +32,7 @@ class _AddCommentScreenState extends State { return Scaffold( appBar: AppBar( leading: IconButton( - icon: Icon( + icon: const Icon( Icons.close, size: 20, ), @@ -45,7 +40,7 @@ class _AddCommentScreenState extends State { Navigator.pop(context); }, ), - title: Text( + title: const Text( "Add a comment", style: TextStyle(fontSize: 20, color: Colors.black), ), @@ -57,7 +52,7 @@ class _AddCommentScreenState extends State { Navigator.pop(context); } : null, - child: Text( + child: const Text( "Post", style: TextStyle( color: Colors.blue, @@ -67,11 +62,11 @@ class _AddCommentScreenState extends State { ], ), body: Padding( - padding: EdgeInsetsDirectional.all(10), + padding: const EdgeInsetsDirectional.all(10), child: Column( children: [ Text(title), - Divider( + const Divider( height: 10, ), Expanded( diff --git a/lib/comments/screens/add_reply_screen.dart b/lib/comments/screens/add_reply_screen.dart index 2d69cd5c..b737df6a 100644 --- a/lib/comments/screens/add_reply_screen.dart +++ b/lib/comments/screens/add_reply_screen.dart @@ -37,9 +37,6 @@ class _AddReplyScreenState extends State { @override void didChangeDependencies() { - // TODO: implement didChangeDependencies - //===============================Fetch subreddit data =======================================// - var temp = ModalRoute.of(context)?.settings.arguments as Map; parentId = temp['parentId']; @@ -52,9 +49,7 @@ class _AddReplyScreenState extends State { createComment(String text) async { if (await Provider.of(context, listen: false) - .postComment(parentId, 'Comment', text)) { - print('created comment'); - } + .postComment(parentId, 'Comment', text)) {} } @override @@ -62,7 +57,7 @@ class _AddReplyScreenState extends State { return Scaffold( appBar: AppBar( leading: IconButton( - icon: Icon( + icon: const Icon( Icons.close, size: 20, ), @@ -70,7 +65,7 @@ class _AddReplyScreenState extends State { Navigator.pop(context); }, ), - title: Text( + title: const Text( "Reply", style: TextStyle(fontSize: 20, color: Colors.black), ), @@ -82,7 +77,7 @@ class _AddReplyScreenState extends State { Navigator.pop(context); } : null, - child: Text( + child: const Text( "Post", style: TextStyle( color: Colors.blue, @@ -92,7 +87,7 @@ class _AddReplyScreenState extends State { ], ), body: Padding( - padding: EdgeInsetsDirectional.all(10), + padding: const EdgeInsetsDirectional.all(10), child: Column( children: [ Row( @@ -125,11 +120,11 @@ class _AddReplyScreenState extends State { ), ], ), - SizedBox( + const SizedBox( height: 10, ), Text(comment), - Divider( + const Divider( height: 10, ), Expanded( diff --git a/lib/comments/widgets/comment.dart b/lib/comments/widgets/comment.dart index f42776fc..ee627a82 100644 --- a/lib/comments/widgets/comment.dart +++ b/lib/comments/widgets/comment.dart @@ -4,9 +4,16 @@ import '../models/comment_model.dart'; import 'comment_body.dart'; import 'comment_header.dart'; +/// A widget to display a comment in tree + class Comment extends StatefulWidget { + /// The data of the commment final CommentModel data; + + /// The userName final String userName; + + /// The level of comment final int level; const Comment( {super.key, required this.data, required this.userName, this.level = 0}); @@ -20,7 +27,7 @@ class _CommentState extends State { @override Widget build(BuildContext context) { return widget.data.type != null - ? Text('more comments') + ? const Text('more comments') : Container( margin: EdgeInsets.only(bottom: widget.level != 0 ? 0 : 10), child: Material( @@ -61,8 +68,8 @@ class _CommentState extends State { text: widget.data.text ?? '', ), CommentFooter( - commentVoteStatus: 1, - votes: 20, + commentVoteStatus: 0, + votes: widget.data.votes ?? 0, data: widget.data, ) ], @@ -87,7 +94,7 @@ class _CommentState extends State { level: widget.level + 1, )) .toList()) - : SizedBox() + : const SizedBox() ]), ), ); diff --git a/lib/comments/widgets/comment_footer.dart b/lib/comments/widgets/comment_footer.dart index 34df3f45..57dbbc42 100644 --- a/lib/comments/widgets/comment_footer.dart +++ b/lib/comments/widgets/comment_footer.dart @@ -27,64 +27,6 @@ class _CommentFooterState extends State { int commentVoteStatus; int votes; _CommentFooterState(this.commentVoteStatus, this.votes); -// upVote() async { -// if (postVoteStatus != 1) { -// if (await Provider.of(context, listen: false) -// .updateVotes(widget.id, 1)) { -// setState(() { -// if (postVoteStatus == -1) { -// votes = votes + 2; -// widget.data.votes = (widget.data.votes! + 2); -// } else { -// ++votes; -// widget.data.votes = (widget.data.votes! + 1); -// } -// postVoteStatus = 1; -// widget.data.postVoteStatus = 1.toString(); -// }); -// } -// } else { -// if (await Provider.of(context, listen: false) -// .updateVotes(widget.id, 0)) { -// setState(() { -// postVoteStatus = 0; -// widget.data.postVoteStatus = 0.toString(); -// widget.data.votes = (widget.data.votes! - 1); -// --votes; -// }); -// } -// } -// } - -// downVote() async { -// if (postVoteStatus != -1) { -// if (await Provider.of(context, listen: false) -// .updateVotes(widget.id, -1)) { -// setState(() { -// if (postVoteStatus == 1) { -// votes = votes - 2; -// widget.data.votes = (widget.data.votes! - 2); -// } else { -// --votes; -// widget.data.votes = (widget.data.votes! - 1); -// } -// postVoteStatus = -1; -// widget.data.postVoteStatus = (-1).toString(); -// }); -// } -// } else { -// if (await Provider.of(context, listen: false) -// .updateVotes(widget.id, 0)) { -// setState(() { -// postVoteStatus = 0; -// widget.data.postVoteStatus = 0.toString(); - -// ++votes; -// widget.data.votes = (widget.data.votes! + 1); -// }); -// } -// } -// } upVote() {} downVote() {} @@ -92,21 +34,17 @@ class _CommentFooterState extends State { @override Widget build(BuildContext context) { return Container( - margin: EdgeInsetsDirectional.only(start: 10, end: 10), + margin: const EdgeInsetsDirectional.only(start: 10, end: 10), child: Row( mainAxisAlignment: MainAxisAlignment.end, children: [ - CommentPopUpMenu(isSaved: true), + const CommentPopUpMenu(isSaved: true), const SizedBox( width: 20, ), InkWell( onTap: () { - print(widget.data.sId); - print(widget.data.text); - print(widget.data.author?.userName); - print(widget.data.createdAt); - Get.to(AddReplyScreen(), arguments: { + Get.to(const AddReplyScreen(), arguments: { 'parentId': widget.data.sId, 'comment': widget.data.text, 'authorName': widget.data.author?.userName, @@ -138,7 +76,7 @@ class _CommentFooterState extends State { child: Tooltip( message: 'Upvote', child: Container( - padding: EdgeInsetsDirectional.all(8), + padding: const EdgeInsetsDirectional.all(8), child: (commentVoteStatus != 1) ? Icon( Typicons.up_outline, @@ -164,7 +102,7 @@ class _CommentFooterState extends State { child: Tooltip( message: 'Downvote', child: Container( - padding: EdgeInsetsDirectional.all(8), + padding: const EdgeInsetsDirectional.all(8), child: (commentVoteStatus != -1) ? Icon( Typicons.down_outline, diff --git a/lib/comments/widgets/comment_popup_menu.dart b/lib/comments/widgets/comment_popup_menu.dart index bc96b074..50be1330 100644 --- a/lib/comments/widgets/comment_popup_menu.dart +++ b/lib/comments/widgets/comment_popup_menu.dart @@ -55,7 +55,7 @@ class CommentPopUpMenu extends StatelessWidget { children: [ Container( margin: const EdgeInsetsDirectional.only(end: 5), - child: Icon(Icons.share_outlined), + child: const Icon(Icons.share_outlined), ), Text( 'Share', diff --git a/lib/comments/widgets/comments_list.dart b/lib/comments/widgets/comments_list.dart index 6fe194c7..6c1d63d2 100644 --- a/lib/comments/widgets/comments_list.dart +++ b/lib/comments/widgets/comments_list.dart @@ -6,8 +6,15 @@ import '../models/comment_model.dart'; import '../providers/comments_provider.dart'; import 'comment.dart'; +/// This widget displays the comments tree of a post + class CommentsList extends StatefulWidget { + /// The ID of the post to get its comments + final String postId; + + /// The user name + final String userName; const CommentsList({super.key, required this.postId, required this.userName}); diff --git a/lib/comments/widgets/view_comment.dart b/lib/comments/widgets/view_comment.dart index 21fcd198..e22c0c92 100644 --- a/lib/comments/widgets/view_comment.dart +++ b/lib/comments/widgets/view_comment.dart @@ -13,12 +13,12 @@ class _ViewCommentState extends State { Widget build(BuildContext context) { return Container( height: 50, - margin: EdgeInsetsDirectional.only(start: 10), + margin: const EdgeInsetsDirectional.only(start: 10), child: Row( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ for (int i = 0; i < widget.level; i++) - VerticalDivider( + const VerticalDivider( color: Colors.grey, thickness: 2, ), diff --git a/lib/create_community/provider/create_community_provider.dart b/lib/create_community/provider/create_community_provider.dart index 6e950da7..9c0d1323 100644 --- a/lib/create_community/provider/create_community_provider.dart +++ b/lib/create_community/provider/create_community_provider.dart @@ -12,10 +12,8 @@ class CreateCommunityProvider with ChangeNotifier { // return true if existed // false if it doesn't exist before Future getCommunity(String userName, BuildContext context) async { - print(userName); try { final prefs = await SharedPreferences.getInstance(); - print(prefs); DioClient.init(prefs); subredditName = userName; final response = await DioClient.get(path: '/subreddits/$userName'); diff --git a/lib/create_community/screens/create_community.dart b/lib/create_community/screens/create_community.dart index 3c7d5452..93473ff6 100644 --- a/lib/create_community/screens/create_community.dart +++ b/lib/create_community/screens/create_community.dart @@ -167,9 +167,7 @@ class CreateCommunityState extends State { ), SizedBox( height: 10.h, - // width: 60.h, child: Form( - //key:Key('Create-Community'), key: _textFieldKey, child: TextFormField( textAlignVertical: TextAlignVertical.center, @@ -359,16 +357,8 @@ class CreateCommunityState extends State { await Provider.of(context, listen: false) .postCommunity(createCommunityModel.toJson(), context) .then((value) { - //if(value) - // print('Community $value'); Navigator.of(context).popAndPushNamed(ModeratedSubredditScreen.routeName, - // arguments: 'Cooking' arguments: _communityNameController.text); }); - // if (postCommunity) { - // setState(() { - // done = true; - // }); - //} } } diff --git a/lib/create_community/widgets/clear_text_field.dart b/lib/create_community/widgets/clear_text_field.dart index 4c90fb5b..5d847a41 100644 --- a/lib/create_community/widgets/clear_text_field.dart +++ b/lib/create_community/widgets/clear_text_field.dart @@ -16,7 +16,6 @@ class ClearTextField extends StatelessWidget { @override Widget build(BuildContext context) { return Container( - //=> extract to widget width: 15.h, padding: const EdgeInsets.all(5), child: Row( diff --git a/lib/create_community/widgets/community_type.dart b/lib/create_community/widgets/community_type.dart index 1852e94a..720399da 100644 --- a/lib/create_community/widgets/community_type.dart +++ b/lib/create_community/widgets/community_type.dart @@ -18,7 +18,6 @@ class CommunityType extends StatelessWidget { @override Widget build(BuildContext context) { return SizedBox( - ///extract this to widget height: 40.h, child: Column( children: [ diff --git a/lib/create_community/widgets/create_community_web.dart b/lib/create_community/widgets/create_community_web.dart index 18246568..31e25cd0 100644 --- a/lib/create_community/widgets/create_community_web.dart +++ b/lib/create_community/widgets/create_community_web.dart @@ -75,15 +75,12 @@ class _CreateCommunityWebState extends State { } _toggleSwitch(value) { - print(value); - print(widget.checked); //used to toggle the switch of 18+ to true or false //return type void setState(() { toggleSwitch = value; }); - print(widget.checked); } _onClickWeb(index, type) { @@ -111,8 +108,6 @@ class _CreateCommunityWebState extends State { uniqueCommunityName = false; }); if (name.length >= 3 - //&& - //widget._formKey.currentState!.validate() ) { try { bool found = @@ -123,17 +118,14 @@ class _CreateCommunityWebState extends State { uniqueCommunityName = false; widget.errorMessage = 'Sorry,${widget._communityNameController.text} is taken.Try another.'; - //validating = false; }); } else { setState(() { uniqueCommunityName = true; widget.errorMessage = ''; - //validating = false; }); } } catch (error) { - //print(error); } } } @@ -179,7 +171,6 @@ class _CreateCommunityWebState extends State { .postCommunity(createCommunityModel.toJson(), context) .then((value) { Navigator.of(context).pushNamed(ModeratedSubredditScreen.routeName, - // arguments: 'Cooking' arguments: widget._communityNameController.text); }); } diff --git a/lib/createpost/controllers/posts_controllers.dart b/lib/createpost/controllers/posts_controllers.dart index 3f7c3857..ee03ba31 100644 --- a/lib/createpost/controllers/posts_controllers.dart +++ b/lib/createpost/controllers/posts_controllers.dart @@ -1,4 +1,5 @@ import 'dart:io'; +import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:image_picker/image_picker.dart'; @@ -18,21 +19,21 @@ class PostController extends GetxController { List moderatedSubreddits = [].obs; RxString subredditToSubmitPost = "".obs; - RxString iconOfSubredditToSubmittPost = "".obs; - RxString idOfSubredditToSubmittPost = "".obs; - RxBool showMore = false.obs; + RxString iconOfSubredditToSubmittPost="".obs; + RxString idOfSubredditToSubmittPost="".obs; + RxBool showMore=false.obs; RxBool isPostSpoiler = false.obs; RxBool isPostNSFW = false.obs; RxBool isLoading = true.obs; final services = PostServices(); Rx postTitle = TextEditingController().obs; - Rx textPost = quill.QuillController.basic().obs; + Rx textPost=quill.QuillController.basic().obs; Rx urlPost = TextEditingController().obs; RxString typeOfPost = ''.obs; GlobalKey formKey = GlobalKey(); GlobalKey formKeyUrl = GlobalKey(); - List? imageFileList = [].obs; + List? imageFileList = [].obs; Future? initializeVideoPlayerFuture; @@ -40,23 +41,22 @@ class PostController extends GetxController { final videoFile = Rx(null); final videoController = Rx(null); //Flairs belonging to subreddit - List flairsOfSubreddit = [].obs; - RxString idOfFlair = "".obs; - RxString textOfFlair = "".obs; - RxString textColorOfFlair = "None".obs; - RxString backgroundColorOfFlair = "None".obs; - RxBool isSubredditHasFlair = false.obs; - RxBool isFromHomeDirect = true.obs; - List checking = [].obs; - RxInt checkFromWhich = 0.obs; + ListflairsOfSubreddit=[].obs; + RxString idOfFlair="".obs; + RxString textOfFlair="".obs; + RxString textColorOfFlair="None".obs; + RxString backgroundColorOfFlair="None".obs; + RxBool isSubredditHasFlair=false.obs; + RxBool isFromHomeDirect=true.obs; + Listchecking=[].obs; + RxInt checkFromWhich=0.obs; dynamic argumentData = Get.arguments; @override - void onInit() { - _fetchHouses(); + void onInit() + { getSubreddits(); super.onInit(); } - // @override // onClose() // { @@ -64,33 +64,32 @@ class PostController extends GetxController { // typeOfPost.value=''; // super.onClose(); // } - getFlairsOfSubreddit() async { + getFlairsOfSubreddit() async{ final prefs = await SharedPreferences.getInstance(); DioClient.init(prefs); - try { + try{ print('/subreddit/${subredditToSubmitPost}/flairs'); - await DioClient.get(path: '/subreddits/${subredditToSubmitPost}/flair') - .then((value) { + await DioClient.get(path:'/subreddits/${subredditToSubmitPost}/flair').then((value){ print("Flairs returned are $value"); - value.data['data'].forEach((val) { + value.data['data'].forEach((val) + { flairsOfSubreddit.add(FlairModel.fromJson(val)); }); }); - checking = List.filled(flairsOfSubreddit.length + 1, false); - print( - "the size of returned list of flairs of subreddit is ${flairsOfSubreddit.length}"); + checking = List.filled(flairsOfSubreddit.length+1, false); + print("the size of returned list of flairs of subreddit is ${flairsOfSubreddit.length}"); print("the size of checking list is ${checking.length}"); - } catch (e) { - print( - "error in fetching the flairs of ${subredditToSubmitPost} subreddit -> $e"); } - } + catch(e){ + print("error in fetching the flairs of ${subredditToSubmitPost} subreddit -> $e"); + } + } getSubreddits() async { final prefs = await SharedPreferences.getInstance(); DioClient.init(prefs); try { - await DioClient.get(path: '$mySubreddits/subscriber').then((value) { + await DioClient.get(path:'$mySubreddits/subscriber').then((value) { print("subs list $value"); value.data['data'].forEach((value1) { subscribedSubreddits.add(userSubredditsResponse.fromJson(value1)); @@ -98,7 +97,7 @@ class PostController extends GetxController { }); }); - await DioClient.get(path: '$mySubreddits/moderator').then((value) { + await DioClient.get(path:'$mySubreddits/moderator').then((value) { print("moderator list $value"); value.data['data'].forEach((value1) { moderatedSubreddits.add(userSubredditsResponse.fromJson(value1)); @@ -111,66 +110,73 @@ class PostController extends GetxController { print("error in fething subreddits of user $error"); } } - - sendPost(BuildContext context) async { + Future sendPost(BuildContext context, {required String title,required String text,required String kind ,required String url,required String owner,required String ownerType,required bool nsfw,required bool spoiler}) async { final prefs = await SharedPreferences.getInstance(); DioClient.init(prefs); - var response; + var response ; try { - response = await DioClient.post(path: "/posts", data: { - "title": "AHMED", - //postTitle.value.text , - "kind": typeOfPost.value as String, - "text": "KKKK", - //(DeltaToHTML.encodeJson(textPost.value.document.toDelta().toJson())) as String, - //"url":urlPost.value.text as String , - "owner": idOfSubredditToSubmittPost.value as String, - "ownerType": - (subredditToSubmitPost.value == "Myprofile") ? "User" : "Subreddit", - // "nsfw": isPostNSFW.value , - // "spoiler": isPostSpoiler.value , + response= await DioClient.post(path: "/posts", data:{ + "title": title , + "kind": kind, + "text":text, + "url":url, + "owner":owner, + "ownerType": ownerType, + "nsfw": nsfw , + "spoiler": spoiler , "sendReplies": true, - - // "flairId":idOfFlair.value as String , - // "flairText":textOfFlair.value as String, - - // "title": "post", - // "kind": "self", - // "text": "Test comment count", - // "owner": "63a193ffe3d2b7ad4a939978", - // "ownerType": "User", - // "nsfw": false, - // "spoiler": true, - // "sendReplies": true + "suggestedSort":"hot" }); - print("YA RAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB"); if (response.statusCode == 200 || response.statusCode == 201) { - print("response of sending post ${response.data}"); ScaffoldMessenger.of(context).showSnackBar( - SnackBar( + const SnackBar( content: Text("sent successfuly", style: TextStyle(color: Colors.white)), backgroundColor: Colors.green), ); - print(response.statusCode); - print("the id of post created ${response.data["data"]["_id"]}"); - //print(json.decode(response.data)['message']); } - // return response.data["data"]["_id"]; + return response.data["data"]["_id"]; } catch (e) { print("error in sending the post -> $e"); - // return 0; + return ""; } } - - Future _fetchHouses() async { + Future sendPostWithFlair(BuildContext context, {required String title,required String text,required String kind ,required String url,required String owner,required String ownerType,required bool nsfw,required bool spoiler,required String textFlair,required String idFlair}) async { + final prefs = await SharedPreferences.getInstance(); + DioClient.init(prefs); + var response ; try { - ///here fitch Your Posts - } finally { - isLoading(false); + response = await DioClient.post(path: "/posts", data: { + "title": title, + "kind": kind, + "text": text, + "url": url, + "owner": owner, + "ownerType": ownerType, + "nsfw": nsfw, + "spoiler": spoiler, + "sendReplies": true, + "flairId": idOfFlair, + "flairText": textOfFlair, + "suggestedSort":"hot" + }); + if (response.statusCode == 200 || response.statusCode == 201) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text("sent successfuly", + style: TextStyle(color: Colors.white)), + backgroundColor: Colors.green), + ); + } + return response.data["data"]["_id"]; + } + catch (e) { + print("error in sending the post -> $e"); + return ""; } } + Future createPost() async { try { isLoading.value = true; @@ -183,10 +189,9 @@ class PostController extends GetxController { isLoading(false); } } - Future getVideo() async { Future videoFiles = - ImagePicker().pickVideo(source: ImageSource.gallery); + ImagePicker().pickVideo(source: ImageSource.gallery); videoFiles.then((file) async { videoFile.value = File(file!.path); videoController.value = VideoPlayerController.file(videoFile.value!); @@ -219,4 +224,6 @@ class PostController extends GetxController { typeOfPost.close(); super.dispose(); } + + } diff --git a/lib/createpost/screens/createpost.dart b/lib/createpost/screens/createpost.dart index 38e8b99c..8a829e6e 100644 --- a/lib/createpost/screens/createpost.dart +++ b/lib/createpost/screens/createpost.dart @@ -1,22 +1,33 @@ ///////////////////////BY ME/////////////////////////////////////////// import 'dart:convert'; +import 'dart:io'; import 'package:post/delta_to_html.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart'; +import 'package:flutter/services.dart'; import 'package:get/get.dart'; import 'package:image_picker/image_picker.dart'; -// import 'package:post/discover/discover.dart'; +import 'package:multi_image_picker/multi_image_picker.dart'; import 'package:post/home/screens/home_layout.dart'; import 'dart:async'; +import '../../home/controller/home_controller.dart'; +import '../../home/widgets/community_container.dart'; +import '../../home/widgets/custom_upper_bar.dart'; import '../../icons/icon_broken.dart'; import '../controllers/posts_controllers.dart'; import '../screens/posttocommunity.dart'; import '../widgets/type_of_post.dart'; +import '../widgets/type_of_post_web.dart'; import 'finalpost.dart'; + + import 'package:flutter_quill/flutter_quill.dart' as quill; + class CreatePostSCreen extends StatefulWidget { + // const CreatePostSCreen({ // Key? key, // this.name="", @@ -26,443 +37,756 @@ class CreatePostSCreen extends StatefulWidget { // final String name; @override State createState() => _CreatePostSCreenState(); + } class _CreatePostSCreenState extends State { // const CreatePostSCreen({super.key}); final ImagePicker imagePicker = ImagePicker(); final ImagePicker videoPicker = ImagePicker(); - final PostController controller = Get.put(PostController(), permanent: false); - + final PostController controller = Get.put( + PostController(),permanent: false + ); + final HomeController controllerForHome = Get.put( + HomeController(),permanent: false + ); Future? initializeVideoPlayerFuture; @override - void initState() { + void initState() + { + // TODO: implement initState - if (Get.arguments[0] == 0) { - controller.isFromHomeDirect.value = true; - } else { - controller.isFromHomeDirect.value = false; - controller.iconOfSubredditToSubmittPost.value = Get.arguments[1]; - controller.subredditToSubmitPost.value = Get.arguments[2]; + if(Get.arguments[0]==0) + { + controller.isFromHomeDirect.value=true; + } + else + { + controller.isFromHomeDirect.value=false; + controller.iconOfSubredditToSubmittPost.value=Get.arguments[1] ; + controller.subredditToSubmitPost.value=Get.arguments[2] ; } super.initState(); } @override Widget build(BuildContext context) { - return Obx( - () => WillPopScope( - onWillPop: () async { - final value = (controller.imageFileList!.length > 0 || - controller.videoFile.value != null || - controller.urlPost.value.text != "") - ? await showDialog( - context: context, - builder: (context) { - return AlertDialog( - content: Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(20), - ), - width: MediaQuery.of(context).size.width / 0.001, - height: MediaQuery.of(context).size.height / 10, - child: (Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Text( - 'Discard Post Submission?', - style: TextStyle( - fontSize: 17, fontWeight: FontWeight.w500), - ), - SizedBox( - height: 10, - ), - Row( - children: [ - Expanded( - child: ElevatedButton( - onPressed: () { - Get.back(); - }, - style: ElevatedButton.styleFrom( - backgroundColor: Colors.grey[200], - shape: RoundedRectangleBorder( - borderRadius: - BorderRadius.circular(30), - ), - ), - child: const Text( - 'Cancel', - style: TextStyle( - color: Colors.grey, - fontWeight: FontWeight.w600, - ), - ), + double ScreenSizeWidth = MediaQuery.of(context).size.width; + + final isDesktop = ScreenSizeWidth >= 700; + final isMobile = ScreenSizeWidth < 700; + if (isDesktop){ + return Obx(()=> + Scaffold( + backgroundColor: const Color(0xA2D4E4FA), + appBar: PreferredSize( preferredSize: Size(700, 60),child: UpBar(controller: controllerForHome, controllerForCreatePost: controller,)), + body:SingleChildScrollView( + scrollDirection: Axis.vertical, + child: Column( + children: [ + SizedBox(height: 70,), + Padding( + padding: const EdgeInsetsDirectional.only(end: 600.0), + child: Text("Create a Post",style: TextStyle(fontSize: 18,fontWeight: FontWeight.bold),), + ), + SizedBox(height: 10,), + Padding( + padding: const EdgeInsetsDirectional.only(end: 600.0), + child: Container( + width: 300, + height: 50, + color: Colors.white, + child:PopupMenuButton( + + child: Row( + children: [ + // Icon(Icons.home_filled), + SizedBox(width: 20,), + Text("choose a community"), + SizedBox(width: 80,), + Icon(Icons.arrow_drop_down_sharp) + ], + ), + elevation: 0, + color: Colors.white, + offset:Offset(0,35) , + itemBuilder: (context)=>[ + PopupMenuItem( + value:1, + child: Container( + width: 300, + child: Column( + children:List.generate(controller.moderatedSubreddits.length, (index) => CommunityContainer(nameOfSubreddit: controller.moderatedSubreddits[index].subredditName!, iconOfSubreddit: controller.moderatedSubreddits[index].icon!)) ), - ), - SizedBox( - width: 5, - ), - Expanded( - child: ElevatedButton( - onPressed: () { - controller.textPost.value.clear(); - controller.postTitle.value.clear(); - controller.imageFileList!.clear(); - controller.videoFile.value = null; - controller.videoController.value = null; - controller.urlPost.value.clear(); - Get.to(HomeLayoutScreen()); - }, - style: ElevatedButton.styleFrom( - backgroundColor: Colors.red[700], - shape: RoundedRectangleBorder( - borderRadius: - BorderRadius.circular(30), - ), - ), - child: const Text( - 'Discard', - style: TextStyle( - color: Colors.white, - fontWeight: FontWeight.w600, - ), - ), + ) + + ), + PopupMenuItem( + value:2, + child: Container( + width: 300, + child: Column( + children:List.generate(controller.subscribedSubreddits.length, (index) => CommunityContainer(nameOfSubreddit: controller.subscribedSubreddits[index].subredditName!, iconOfSubreddit: controller.subscribedSubreddits[index].icon!)) ), - ), - ], - ) + ) + + ), ], - ))), - ); - }) - : null; - - if (value != null) { - return Future.value(value); - } else { - return Future.value(true); - } - }, - child: Scaffold( - backgroundColor: Colors.white, - body: Form( - key: controller.formKey, - child: Column( - children: [ - const SizedBox(height: 40.0), - Row( - children: [ - IconButton( - onPressed: () { - // Get.delete(); - Get.back(); - }, - color: Colors.black45, - icon: Icon( - Icons.close, - size: 32.0, + ) ), ), - const Spacer(), + SizedBox(height: 20,), Padding( - padding: const EdgeInsetsDirectional.only(end: 20.0), - child: controller.isLoading.value - ? const CircularProgressIndicator( - color: Colors.blue, - ) - : MaterialButton( - onPressed: () { - if (controller.formKeyUrl.currentState! - .validate() && - controller.postTitle.value.text != "") { - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar( - content: Text('Processing Data')), - ); - if (controller.isFromHomeDirect.value == - true) { - Get.to(BuildSubreddit()); - } else { - Get.to(FinalPost()); - } - } - }, - elevation: 0.0, - height: 35.0, - minWidth: 80.0, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(20.0)), - color: controller.postTitle.value.text == "" - ? Colors.grey[100] - : Colors.blue[900], - child: Text( - "Next", - style: TextStyle( - color: Colors.grey[400], + padding: const EdgeInsetsDirectional.only(start: 150), + child: Row( + children: [ + + InkWell( + onTap: (){ + controller.typeOfPost.value = "self"; + controller.typeOfPost.update((val) { }); + + }, + child: Container( + padding: EdgeInsetsDirectional.only(start: 10), + height: 50, + width: 100, + decoration: BoxDecoration( + color: Colors.white, + border: Border.all(color: Colors.black,width: 0.5) + ), + child: Center( + + child: Row( + children: [ + Icon( + Icons.post_add, + color: Colors.black, + ), + const SizedBox(width: 3,), + Text( + "Post", + style: TextStyle( + color: Colors.black + ) + ) + ], ), ), ), - ), - ], - ), - SizedBox( - height: 10, - ), - Visibility( - visible: !controller.isFromHomeDirect.value, - child: Padding( - padding: const EdgeInsetsDirectional.only(start: 14), - child: Row( - children: [ - GestureDetector( - onTap: () { - Get.to(BuildSubreddit()); - }, - child: Row( - children: [ - CircleAvatar( - backgroundImage: NetworkImage( - '${controller.iconOfSubredditToSubmittPost}'), - radius: 16.0, - ), - SizedBox( - width: 4.0, + ), + InkWell( + onTap: (){ + controller.imageFileList!.clear(); + selectImages(); + print( + "lenght of list is ${controller.imageFileList!.length}"); + controller.typeOfPost.value = "image"; + }, + child: Container( + padding: EdgeInsetsDirectional.only(start: 10), + height: 50, + width: 180, + decoration: BoxDecoration( + color: Colors.white, + border: Border.all(color: Colors.black,width: 0.5) ), - ElevatedButton.icon( - onPressed: () { - Get.to(BuildSubreddit()); - }, - icon: Text( - controller.subredditToSubmitPost.value, - style: const TextStyle(color: Colors.black), + child: Center( + + child: Row( + children: [ + Icon( + Icons.camera_alt, + color: Colors.black, + ), + const SizedBox(width: 3,), + Text( + "Images ", + style: TextStyle( + color: Colors.black + ) + ) + ], ), - label: const Icon( - IconBroken.Arrow___Down_2, - color: Colors.black, - size: 15, + ), + ), + ), + InkWell( + onTap: (){ + controller.videoFile.value=null; + controller.videoController.value=null; + controller.getVideo(); + controller.typeOfPost.value = "video"; + }, + child: Container( + padding: EdgeInsetsDirectional.only(start: 10), + height: 50, + width: 180, + decoration: BoxDecoration( + color: Colors.white, + border: Border.all(color: Colors.black,width: 0.5) + ), + child: Center( + + child: Row( + children: [ + Icon( + Icons.video_camera_back_outlined, + color: Colors.black, + ), + const SizedBox(width: 3,), + Text( + "Video", + style: TextStyle( + color: Colors.black + ) + ) + ], ), - style: ButtonStyle( - elevation: - MaterialStateProperty.all(0), - backgroundColor: - MaterialStateProperty.all(Colors.white), + ), + ), + ), + InkWell( + onTap: (){ + controller.typeOfPost.value = "link"; + controller.typeOfPost.update((val) { }); + }, + child: Container( + padding: EdgeInsetsDirectional.only(start: 10), + height: 50, + width: 100, + decoration: BoxDecoration( + color: Colors.white, + border: Border.all(color: Colors.black,width: 0.5) + ), + child: Center( + + child: Row( + children: [ + Icon( + Icons.link, + color: Colors.black, + ), + const SizedBox(width: 3,), + Text( + "Link", + style: TextStyle( + color: Colors.black + ) + ) + ], ), ), - ], + ), ), - ), - const Spacer(), - TextButton( - onPressed: () => showModalBottomSheet( - context: context, - builder: (context) => Center( - child: Column( - mainAxisAlignment: - MainAxisAlignment.center, - children: [ - const Expanded( - child: Center( - child: Text( - "Community Standards"))), - ElevatedButton( - style: ButtonStyle( - backgroundColor: - MaterialStateProperty.all( - Colors.blue[900], - ), - ), - onPressed: () { - Navigator.pop(context); - }, - child: Text( - "understand", - style: TextStyle( - color: Colors.white, - ), - ), - ) - ], - ), - )), - child: const Text( - "Rules", - style: TextStyle(color: Colors.blue), - )) - ], + + ], + ), ), - ), - ), - const SizedBox( - height: 10, - ), - Padding( - padding: const EdgeInsetsDirectional.only(start: 10.0), - child: TextFormField( - onChanged: (value) { - controller.postTitle.refresh(); - }, - validator: ((value) { - if (value == null || value.isEmpty) { - return 'Please enter some text'; - } - return null; - }), - controller: controller.postTitle.value, - enabled: true, - style: const TextStyle( - fontSize: 20.0, fontWeight: FontWeight.w700), - showCursor: true, - cursorColor: Colors.blue, - cursorHeight: 20.0, - textAlign: TextAlign.start, - decoration: const InputDecoration( - hintText: "An interesting title", - border: InputBorder.none), - ), - ), - const SizedBox( - height: 10.0, - ), - const SizedBox( - height: 10.0, - ), - Expanded( - child: BuildFormType( - controller: controller, - )), - MaterialButton( - child: Text("press"), - onPressed: () { - print(controller.textPost.value.document - .toDelta() - .toJson()); - print( - "==============================================================="); - print((DeltaToHTML.encodeJson(controller - .textPost.value.document - .toDelta() - .toJson()))); - print((jsonEncode(controller.textPost.value.document - .toDelta() - .toJson())) - .toString() - .runtimeType); - print(controller.textPost.value.getPlainText); - print(controller.textPost.value.getPlainText()); - print(controller.textPost.value.document.toPlainText()); - print(controller.textPost.value.document); - print(controller.urlPost.value.text); - print(controller.textPost.value.document - .toPlainText() - .runtimeType); - print(controller.textPost.value.document.runtimeType); - print(controller.textPost.value.runtimeType); - }), - Padding( - padding: - const EdgeInsetsDirectional.only(start: 20, bottom: 10), - child: Row( - children: [ - IconButton( - onPressed: () { - controller.typeOfPost.value = "self"; - }, - icon: const Icon(IconBroken.Document), + SizedBox(height: 10,), + Container( + width: 1000, + height: 50, + decoration: BoxDecoration( + border: Border.all(color: Colors.black), + color: Colors.white, ), - IconButton( - onPressed: () async { - controller.imageFileList!.clear(); - selectImages(); - print( - "lenght of list is ${controller.imageFileList!.length}"); - controller.typeOfPost.value = "image"; - }, - icon: const Icon(IconBroken.Image_2)), - IconButton( - onPressed: () { - controller.videoFile.value = null; - controller.videoController.value = null; - controller.getVideo(); - controller.typeOfPost.value = "video"; + + child:TextFormField( + onChanged: (value) { + controller.postTitle.refresh(); }, - icon: const Icon(IconBroken.Video), + validator: ((value) { + if (value == null || value.isEmpty) { + return 'Please enter some text'; + } + return null; + }), + controller: controller.postTitle.value, + enabled: true, + style: const TextStyle( + fontSize: 20.0, fontWeight: FontWeight.w700), + showCursor: true, + cursorColor: Colors.blue, + cursorHeight: 20.0, + textAlign: TextAlign.start, + decoration: const InputDecoration( + hintText: "Title", + border:OutlineInputBorder( + borderSide: BorderSide(width: 20,color: Colors.black) + )), ), - IconButton( - onPressed: () { - controller.typeOfPost.value = "link"; - }, - icon: const Icon(IconBroken.Bookmark), + ), + SizedBox(height: 20,), + BuildFormTypeWeb(controller:controller), + SizedBox(height: 10,), + Padding( + padding: const EdgeInsetsDirectional.only(start: 20.0), + child: Row( + children: [ + /// Spoiler + InkWell( + onTap: (){ + if (controller.isPostSpoiler.value == false) { + controller.isPostSpoiler.value = true; + controller.isPostSpoiler.refresh(); + } else { + controller.isPostSpoiler.value = false; + controller.isPostSpoiler.refresh(); + } + + }, + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 20,vertical: 5), + decoration: BoxDecoration( + color:(controller.isPostSpoiler.value==true)? Colors.red:Colors.white, + borderRadius: BorderRadius.circular(30), + border: Border.all( + color: Colors.grey + ) + ), + child: Row( + children: [ + Icon(Icons.add, + color: Colors.black, + size: 20,), + const SizedBox(width: 5,), + Text( + "Spoiler", + style:TextStyle(color: Colors.black) + ) + ], + ), + ), + ), + const SizedBox(width: 5,), + /// NSFW + InkWell( + onTap: (){ + if (controller.isPostNSFW.value == false) { + controller.isPostNSFW.value = true; + controller.isPostNSFW.refresh(); + } else { + controller.isPostNSFW.value = false; + controller.isPostNSFW.refresh(); + } + }, + + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 20,vertical: 5), + decoration: BoxDecoration( + color:(controller.isPostNSFW.value==true)? Colors.red:Colors.white, + borderRadius: BorderRadius.circular(30), + border: Border.all( + color: Colors.black + ) + ), + child: Row( + children: const [ + Icon(Icons.add, + color: Colors.black,size: 20,), + SizedBox(width: 5,), + Text( + "NSFW", + style: TextStyle(color: Colors.black), + ) + ], + ), + ), + ), + const SizedBox(width: 700,), + InkWell( + onTap: (){ + if(controller.postTitle.value.text!=""&&controller.formKeyUrl.currentState!.validate()) + { + controller.sendPost(context, + title: controller.postTitle.value.text, + text: (DeltaToHTML.encodeJson(controller.textPost.value.document.toDelta().toJson())).toString(), + kind: controller.typeOfPost.value, + url: controller.urlPost.value.text, + owner: (controller.idOfSubredditToSubmittPost.value), + ownerType:"Subreddit" , + nsfw: controller.isPostNSFW.value, + spoiler: controller.isPostSpoiler.value, + ); + controller.postTitle.value.clear(); + controller.urlPost.value.clear(); + controller.textPost.value.clear(); + controller.isPostSpoiler.value=false; + controller.isPostNSFW.value=false; + // Navigator.pop(context); + Get.to(HomeLayoutScreen()); + } + else return; + }, + + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 20,vertical: 5), + decoration: BoxDecoration( + color:(controller.postTitle.value.text=="")? Colors.white:Colors.blue, + borderRadius: BorderRadius.circular(30), + border: Border.all( + color: Colors.black + ) + ), + child: Row( + children: const [ + SizedBox(width: 5,), + Text( + "Post", + style: TextStyle(color: Colors.black), + ) + ], + ), + ), + ), + ], ), - ], - ), - ), - const SizedBox( - height: 10.0, - ), - const SizedBox( - height: 10.0, + ), + SizedBox(height: 100,) + ], ), - Expanded( - child: BuildFormType( - controller: controller, - )), - Padding( - padding: - const EdgeInsetsDirectional.only(start: 20, bottom: 10), - child: Row( + ) + ), + ); + } + return Obx(()=> + WillPopScope( + onWillPop: () async { + final value = ( controller.imageFileList!.length>0 || controller.videoFile.value != null || controller.urlPost.value.text != "")?await + showDialog(context: context, + builder: (context) + { + return AlertDialog( + content: Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(20), + ), + width: MediaQuery + .of(context) + .size + .width / 0.001, + height: MediaQuery + .of(context) + .size + .height / 10, + child: ( + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text( + 'Discard Post Submission?', + style: TextStyle( + fontSize: 17, fontWeight: FontWeight.w500), + ), + SizedBox(height: 10,), + Row( + children: [ + Expanded( + child: ElevatedButton( + onPressed: () { + Get.back(); + }, + style: ElevatedButton.styleFrom( + backgroundColor: Colors.grey[200], + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular( + 30), + ), + ), + child: + const Text( + 'Cancel', + style: TextStyle( + color: Colors.grey, + fontWeight: FontWeight.w600, + ), + ), + ), + ), + SizedBox(width: 5,), + Expanded( + child: ElevatedButton( + onPressed: () { + controller.textPost.value.clear(); + controller.postTitle.value.clear(); + controller.imageFileList!.clear(); + controller.videoFile.value=null; + controller.videoController.value=null; + controller.urlPost.value.clear(); + Get.to(HomeLayoutScreen()); + }, + style: ElevatedButton.styleFrom( + backgroundColor: Colors.red[700], + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular( + 30), + ), + ), + child: + const Text( + 'Discard', + style: TextStyle( + color: Colors.white, + fontWeight: FontWeight.w600, + ), + ), + ), + ), + ], + ) + ], + ) + ) + ), + ); + } + ) : null ; + + if (value!=null) + { + return Future.value(value); + }else + { + return Future.value(true); + } + }, + child: Scaffold( + backgroundColor: Colors.white, + body: Form( + key: controller.formKey, + child: Column( + children: [ + const SizedBox(height: 40.0), + Row( children: [ IconButton( onPressed: () { - controller.typeOfPost.value = "text"; + // Get.delete(); + Get.back(); }, - icon: const Icon(IconBroken.Document), + color: Colors.black45, icon: Icon( + Icons.close, + size: 32.0, ), - IconButton( - onPressed: () async { - controller.imageFileList!.clear(); - selectImages(); - print( - "lenght of list is ${controller.imageFileList!.length}"); - controller.typeOfPost.value = "image"; - }, - icon: const Icon(IconBroken.Image_2)), - IconButton( - onPressed: () { - controller.videoFile.value = null; - controller.videoController.value = null; - controller.getVideo(); - controller.typeOfPost.value = "video"; - }, - icon: const Icon(IconBroken.Video), ), - IconButton( - onPressed: () { - controller.typeOfPost.value = "url"; - }, - icon: const Icon(IconBroken.Bookmark), + const Spacer(), + Padding( + padding: const EdgeInsetsDirectional.only(end: 20.0), + child: controller.isLoading.value + ? const CircularProgressIndicator( + color: Colors.blue, + ) + : MaterialButton( + onPressed: () { + if ( controller.formKeyUrl.currentState!.validate() && controller.postTitle.value.text !="") { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('Processing Data')), + ); + if(controller.isFromHomeDirect.value==true) + { + Get.to(BuildSubreddit()); + } + else + { + Get.to(FinalPost()); + } + } + }, + elevation: 0.0, + height: 35.0, + minWidth: 80.0, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(20.0)), + color: controller.postTitle.value.text == "" + ? Colors.grey[100] + : Colors.blue[900], + child: Text( + "Next", + style: TextStyle( + color: Colors.grey[400], + ), + ), + ), ), + ], ), - ), - ], + SizedBox(height: 10,), + Visibility( + visible: !controller.isFromHomeDirect.value, + child: Padding( + padding: const EdgeInsetsDirectional.only(start: 14), + child: Row( + children: [ + GestureDetector( + onTap: () + { + Get.to(BuildSubreddit()); + }, + child: Row( + children: [ + CircleAvatar( + backgroundImage: NetworkImage('${controller.iconOfSubredditToSubmittPost}'), + radius: 16.0, + ), + SizedBox( + width: 4.0, + ), + ElevatedButton.icon( + onPressed: () { + Get.to(BuildSubreddit()); + }, + icon: Text( + controller.subredditToSubmitPost.value, + style: const TextStyle(color: Colors.black), + ), + label: const Icon( + IconBroken.Arrow___Down_2, + color: Colors.black, + size: 15, + ), + style: ButtonStyle( + elevation: MaterialStateProperty.all(0), + backgroundColor: + MaterialStateProperty.all(Colors.white), + ), + ), + ], + ), + ), + const Spacer(), + TextButton( + onPressed: () => showModalBottomSheet( + context: context, + builder: (context) => Center( + child: Column( + mainAxisAlignment: + MainAxisAlignment.center, + children: [ + const Expanded( + child: Center( + child: Text( + "Community Standards"))), + ElevatedButton( + style: ButtonStyle( + backgroundColor: + MaterialStateProperty.all( + Colors.blue[900], + ), + ), + onPressed: () { + Navigator.pop(context); + }, + child: Text( + "understand", + style: TextStyle( + color: Colors.white, + ), + ), + ) + ], + ), + )), + child: const Text( + "Rules", + style: TextStyle(color: Colors.blue), + )) + ], + ), + ), + ), + const SizedBox(height: 10,), + Padding( + padding: const EdgeInsetsDirectional.only(start: 10.0), + child: TextFormField( + onChanged: (value) { + controller.postTitle.refresh(); + }, + validator: ((value) { + if (value == null || value.isEmpty) { + return 'Please enter some text'; + } + return null; + }), + controller: controller.postTitle.value, + enabled: true, + style: const TextStyle( + fontSize: 20.0, fontWeight: FontWeight.w700), + showCursor: true, + cursorColor: Colors.blue, + cursorHeight: 20.0, + textAlign: TextAlign.start, + decoration: const InputDecoration( + hintText: "An interesting title", + border: InputBorder.none), + ), + ), + + const SizedBox( + height: 10.0, + ), + + const SizedBox( + height: 10.0, + ), + Expanded(child: BuildFormType(controller: controller,)), + Padding( + padding: const EdgeInsetsDirectional.only(start: 20, bottom: 10), + child: Row( + children: [ + IconButton( + onPressed: () { + controller.typeOfPost.value = "self"; + }, + icon: const Icon(IconBroken.Document), + ), + IconButton( + onPressed: () async { + controller.imageFileList!.clear(); + selectImages(); + print( + "lenght of list is ${controller.imageFileList!.length}"); + controller.typeOfPost.value = "image"; + }, + icon: const Icon(IconBroken.Image_2)), + IconButton( + onPressed: () { + controller.videoFile.value=null; + controller.videoController.value=null; + controller.getVideo(); + controller.typeOfPost.value = "video"; + }, + icon: const Icon(IconBroken.Video), + ), + IconButton( + onPressed: () { + controller.typeOfPost.value = "link"; + }, + icon: const Icon(IconBroken.Bookmark), + ), + ], + ), + ), + + + + ], + ), ), ), ), - ), ); } - @override void dispose() { // TODO: implement dispose Get.delete(); super.dispose(); } - void selectImages() async { - final List? selectedImages = await imagePicker.pickMultiImage(); + final List? selectedImages = (await imagePicker.pickMultiImage()).cast(); if (selectedImages!.isNotEmpty) { controller.imageFileList!.addAll(selectedImages); } diff --git a/lib/createpost/screens/finalpost.dart b/lib/createpost/screens/finalpost.dart index e1f60995..f2c1673b 100644 --- a/lib/createpost/screens/finalpost.dart +++ b/lib/createpost/screens/finalpost.dart @@ -1,5 +1,8 @@ +import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:get/get.dart'; +import 'package:get/get_core/src/get_main.dart'; import 'package:hexcolor/hexcolor.dart'; import 'package:post/createpost/model/send_post_model.dart'; import '../../delta_to_html.dart'; @@ -8,6 +11,7 @@ import '../../icons/icon_broken.dart'; import '../controllers/posts_controllers.dart'; import 'package:responsive_sizer/responsive_sizer.dart'; +import '../services/post_services.dart'; import 'flair_list.dart'; class FinalPost extends StatefulWidget { @@ -17,347 +21,331 @@ class FinalPost extends StatefulWidget { class _FinalPostState extends State { // const FinalPost({Key? key}) : super(key: key); - final PostController controller = Get.put(PostController(), permanent: false); - + final PostController controller = Get.put( + PostController(),permanent: false + ); + final services = PostServices(); @override Widget build(BuildContext context) { return Obx(() => Scaffold( - backgroundColor: Colors.white, - body: Padding( - padding: EdgeInsets.only(top: MediaQuery.of(context).padding.top), - child: Column( + backgroundColor: Colors.white, + body: Padding( + padding: EdgeInsets.only(top: MediaQuery.of(context).padding.top), + child: Column( + children: [ + // SizedBox(height: 40.0), + Row( children: [ - // SizedBox(height: 40.0), - Row( - children: [ - Padding( - padding: const EdgeInsetsDirectional.only(start: 10.0), - child: IconButton( - color: Colors.black, - onPressed: () { - Get.back(); - }, - icon: Icon(IconBroken.Arrow___Left_2, size: 32.0), - ), - ), - SizedBox( - width: 250.0, - ), - Padding( - padding: EdgeInsetsDirectional.only(end: 1.w), - child: MaterialButton( - onPressed: () { - print(controller.postTitle.value.text); - print((controller.postTitle.value.text as String) - .runtimeType); - print(controller.typeOfPost.value); - print((DeltaToHTML.encodeJson(controller - .textPost.value.document - .toDelta() - .toJson())) - .toString()); - print(controller.urlPost.value.text); - print(controller.idOfSubredditToSubmittPost.value); - print((controller.subredditToSubmitPost.value == - "Myprofile") - ? "User" - : "Subreddit"); - print(controller.isPostNSFW.value); - print(controller.isPostSpoiler.value); - print("send replies ->true"); - print("flair id ${controller.idOfFlair.value}"); - print( - "text of flair ${controller.textOfFlair.value}"); - print("suggested sort hot"); - print("scheduled false"); - print("print data on submit"); - controller.sendPost(context); - controller.postTitle.value.clear(); - controller.urlPost.value.clear(); - controller.textPost.value.clear(); - controller.isPostSpoiler.value = false; - controller.isPostNSFW.value = false; - // Navigator.pop(context); - Get.to(HomeLayoutScreen()); - }, - elevation: 0.0, - height: 40.0, - minWidth: 80.0, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(20.0)), - color: Colors.blue[900], - child: Text( - "post", - style: TextStyle( - color: Colors.white, - ), - ), - ), - ) - ], - ), - SizedBox( - height: 25.0, - ), Padding( - padding: const EdgeInsetsDirectional.only(start: 20.0), - child: Row( - children: [ - Row( - children: [ - CircleAvatar( - backgroundImage: NetworkImage( - '${controller.iconOfSubredditToSubmittPost}'), - radius: 16.0, - ), - SizedBox( - width: 4.0, - ), - ElevatedButton.icon( - onPressed: () { - Get.back(); - controller.isPostNSFW.value = false; - controller.isPostSpoiler.value = false; - }, - icon: Text( - controller.subredditToSubmitPost.value, - style: TextStyle(color: Colors.black), - ), - label: Icon( - IconBroken.Arrow___Down_2, - color: Colors.black, - size: 15, - ), - style: ButtonStyle( - elevation: MaterialStateProperty.all(0), - backgroundColor: - MaterialStateProperty.all(Colors.white), - ), - ), - ], - ), - Spacer(), - TextButton( - onPressed: () => showModalBottomSheet( - context: context, - builder: (context) => Center( - child: Column( - mainAxisAlignment: - MainAxisAlignment.center, - children: [ - Expanded( - child: Center( - child: Text( - "Community Standards"))), - ElevatedButton( - style: ButtonStyle( - backgroundColor: - MaterialStateProperty.all( - Colors.blue[900], - ), - ), - onPressed: () { - Navigator.pop(context); - }, - child: Text( - "understand", - style: TextStyle( - color: Colors.white, - ), - ), - ) - ], - ), - )), - child: Text( - "Rules", - style: TextStyle(color: Colors.blue), - )) - ], + padding: const EdgeInsetsDirectional.only(start: 10.0), + child: IconButton( + + color: Colors.black, onPressed: () { + Get.back(); + }, icon:Icon( IconBroken.Arrow___Left_2,size: 32.0), ), ), + SizedBox( + width: 250.0, + ), Padding( - padding: const EdgeInsets.all(16.0), - child: TextFormField( - onChanged: (value) { - controller.postTitle.value; + padding: EdgeInsetsDirectional.only(end: 1.w), + child: MaterialButton( + onPressed: () { + print( controller.postTitle.value.text); + print(( controller.postTitle.value.text as String).runtimeType); + print(controller.typeOfPost.value); + print((DeltaToHTML.encodeJson(controller.textPost.value.document.toDelta().toJson())).toString()); + print(controller.urlPost.value.text); + print(controller.idOfSubredditToSubmittPost.value); + print((controller.subredditToSubmitPost.value == "Myprofile")?"User":"Subreddit"); + print(controller.isPostNSFW.value); + print(controller.isPostSpoiler.value); + print("send replies ->true"); + print("flair id ${controller.idOfFlair.value}"); + print("text of flair ${controller.textOfFlair.value}"); + print("suggested sort hot"); + print("scheduled false"); + print("print data on submit"); + Future str; + if(controller.idOfFlair.value=="") + { + str=controller.sendPost(context, + title: controller.postTitle.value.text, + text: (DeltaToHTML.encodeJson( + controller.textPost.value.document.toDelta() + .toJson())).toString(), + kind: controller.typeOfPost.value, + url: controller.urlPost.value.text, + owner: (controller.idOfSubredditToSubmittPost + .value), + ownerType: controller.subredditToSubmitPost.value=="Myprofile"?"User":"Subreddit", + nsfw: controller.isPostNSFW.value, + spoiler: controller.isPostSpoiler.value, + ); + } + else + { + str= controller.sendPostWithFlair(context, + title: controller.postTitle.value.text, + text: (DeltaToHTML.encodeJson( + controller.textPost.value.document.toDelta() + .toJson())).toString(), + kind: controller.typeOfPost.value, + url: controller.urlPost.value.text, + owner: (controller.idOfSubredditToSubmittPost + .value), + ownerType:controller.subredditToSubmitPost.value=="Myprofile"?"User":"Subreddit", + nsfw: controller.isPostNSFW.value, + spoiler: controller.isPostSpoiler.value, textFlair: controller.textOfFlair.value, idFlair: controller.idOfFlair.value, + ); + } + if(controller.typeOfPost.value=="image") + { + for(int i=0;i(0), - fixedSize: - MaterialStateProperty.all(Size(130.0, 20.0)), - backgroundColor: (controller.isPostNSFW.value) - ? MaterialStateProperty.all( - Colors.black87, - ) - : MaterialStateProperty.all( - Colors.grey[100], - ), - ), - label: Text( - "NSFW", - style: TextStyle( - color: Colors.grey[400], - ), + backgroundColor: + MaterialStateProperty.all(Colors.white), ), - icon: Text( - "18", - style: TextStyle( - color: Colors.grey[400], - ), - )), - SizedBox( - width: 10.0, - ), - ElevatedButton.icon( - onPressed: () { - if (controller.isPostSpoiler.value == false) { - controller.isPostSpoiler.value = true; - controller.isPostSpoiler.refresh(); - } else { - controller.isPostSpoiler.value = false; - controller.isPostSpoiler.refresh(); - } - }, - style: ButtonStyle( - elevation: MaterialStateProperty.all(0), - fixedSize: MaterialStateProperty.all(Size(122.0, 20.0)), - backgroundColor: (controller.isPostSpoiler.value) - ? MaterialStateProperty.all( - Colors.black87, - ) - : MaterialStateProperty.all( - Colors.grey[100], - ), ), - label: Text( - "Spoiler", - style: TextStyle( - color: Colors.grey[400], - ), + ], + ), + Spacer(), + TextButton( + onPressed: () => showModalBottomSheet( + context: context, + builder: (context) => Center( + child: Column( + mainAxisAlignment: + MainAxisAlignment.center, + children: [ + Expanded( + child: Center( + child: Text( + "Community Standards"))), + ElevatedButton( + style: ButtonStyle( + backgroundColor: + MaterialStateProperty.all( + Colors.blue[900], + ), + ), + onPressed: () { + Navigator.pop(context); + }, + child: Text( + "understand", + style: TextStyle( + color: Colors.white, + ), + ), + ) + ], + ), + )), + child: Text( + "Rules", + style: TextStyle(color: Colors.blue), + )) + ], + ), + ), + Padding( + padding: const EdgeInsets.all(16.0), + child: TextFormField( + onChanged: (value) { + controller.postTitle.value; + }, + controller: controller.postTitle.value, + readOnly: true, + style: TextStyle(fontSize: 20.0), + maxLines: 2, + decoration: InputDecoration( + contentPadding: const EdgeInsets.all(10.0), + border: OutlineInputBorder( + borderSide: BorderSide(color: Colors.black), + borderRadius: BorderRadius.circular(50.0), + )), + ), + ), + Row( + children: [ + SizedBox( + width: 20.0, + ), + ElevatedButton.icon( + onPressed: () { + if (controller.isPostNSFW.value == false) { + controller.isPostNSFW.value = true; + controller.isPostNSFW.refresh(); + } else { + controller.isPostNSFW.value = false; + controller.isPostNSFW.refresh(); + } + }, + style: ButtonStyle( + elevation: MaterialStateProperty.all(0), + fixedSize: + MaterialStateProperty.all(Size(130.0, 20.0)), + backgroundColor: (controller.isPostNSFW.value) + ? MaterialStateProperty.all( + Colors.black87, + ) + : MaterialStateProperty.all( + Colors.grey[100], ), - icon: Icon( - IconBroken.Danger, + ), + label: Text( + "NSFW", + style: TextStyle( color: Colors.grey[400], ), ), - ], - ), + icon: Text( + "18", + style: TextStyle( + color: Colors.grey[400], + ), + )), SizedBox( - height: 8.0, + width: 10.0, ), - Obx( - () => Visibility( - visible: (controller.flairsOfSubreddit.length > 0) - ? true - : false, - child: ListTile( - onTap: () { - Get.to(FlairList()); - }, - horizontalTitleGap: 0.0, - title: Text( - controller.textOfFlair.isEmpty - ? "Add Flar" - : controller.textOfFlair.value, - style: TextStyle( - color: controller.textColorOfFlair.value == "None" - ? Colors.black87 - : HexColor(controller.textColorOfFlair.value), - backgroundColor: - controller.backgroundColorOfFlair.value == "None" - ? Colors.white - : HexColor( - controller.backgroundColorOfFlair.value), - ), - ), - // Text("Add flair"), - leading: Icon(IconBroken.Edit), - trailing: Icon(IconBroken.Arrow___Right_2), + ElevatedButton.icon( + onPressed: () { + if (controller.isPostSpoiler.value == false) { + controller.isPostSpoiler.value = true; + controller.isPostSpoiler.refresh(); + } else { + controller.isPostSpoiler.value = false; + controller.isPostSpoiler.refresh(); + } + }, + style: ButtonStyle( + elevation: MaterialStateProperty.all(0), + fixedSize: MaterialStateProperty.all(Size(122.0, 20.0)), + backgroundColor: (controller.isPostSpoiler.value) + ? MaterialStateProperty.all( + Colors.black87, + ) + : MaterialStateProperty.all( + Colors.grey[100], ), ), - ), - const Divider( - height: 10.0, - color: Colors.grey, + label: Text( + "Spoiler", + style: TextStyle( + color: Colors.grey[400], + ), + ), + icon: Icon( + IconBroken.Danger, + color: Colors.grey[400], + ), ), ], ), - ), - )); - } - - savePost() async { - print(controller.postTitle.value.text); - print(controller.typeOfPost.value); - print((DeltaToHTML.encodeJson( - controller.textPost.value.document.toDelta().toJson())) - .toString()); - print(controller.urlPost.value.text); - print(controller.idOfSubredditToSubmittPost.value); - print((controller.subredditToSubmitPost.value == "Myprofile") - ? "User" - : "Subreddit"); - print(controller.isPostNSFW.value); - print(controller.isPostSpoiler.value); - print("send replies ->true"); - print("flair id ${controller.idOfFlair.value}"); - print("text of flair ${controller.textOfFlair.value}"); - print("suggested sort hot"); - print("scheduled false"); - - final model = SendPostModel( - title: controller.postTitle.value.text, - kind: controller.typeOfPost.value, - text: (DeltaToHTML.encodeJson( - controller.textPost.value.document.toDelta().toJson())) - .toString(), - url: (controller.typeOfPost.value == "link") - ? controller.urlPost.value.text - : "", - owner: controller.idOfSubredditToSubmittPost.value, - ownerType: (controller.subredditToSubmitPost.value == "Myprofile") - ? "User" - : "Subreddit", - nsfw: controller.isPostNSFW.value, - spoiler: controller.isPostSpoiler.value, - sendReplies: true, - flairId: controller.idOfFlair.value, - flairText: controller.textOfFlair.value, - suggestedSort: "hot", - scheduled: false, - ); - print("AFTER TO JASON ${model.toJson()}"); - Get.to(HomeLayoutScreen()); + SizedBox( + height: 8.0, + ), + Obx(()=> + Visibility( + visible: (controller.flairsOfSubreddit.length>0)?true:false, + child: ListTile( + onTap: () + { + Get.to(FlairList()); + }, + horizontalTitleGap: 0.0, + title:Text(controller.textOfFlair.isEmpty?"Add Flar": + controller.textOfFlair.value, + style: TextStyle( + color: controller.textColorOfFlair.value=="None"? + Colors.black87: + HexColor(controller.textColorOfFlair.value), + backgroundColor: controller.backgroundColorOfFlair.value=="None"? + Colors.white: + HexColor(controller.backgroundColorOfFlair.value), + ), + ), + // Text("Add flair"), + leading: Icon(IconBroken.Edit), + trailing: Icon(IconBroken.Arrow___Right_2), + ), + ), + ), + const Divider( + height: 10.0, + color: Colors.grey, + ), + ], + ), + ), + )); } - @override void dispose() { // TODO: implement dispose diff --git a/lib/createpost/services/post_services.dart b/lib/createpost/services/post_services.dart index bfaa06b3..0f821bfb 100644 --- a/lib/createpost/services/post_services.dart +++ b/lib/createpost/services/post_services.dart @@ -1,3 +1,5 @@ +import 'dart:io'; + import 'package:dio/dio.dart'; import 'package:flutter/material.dart'; import 'package:image_picker/image_picker.dart'; @@ -5,10 +7,8 @@ import 'package:shared_preferences/shared_preferences.dart'; import '../../networks/const_endpoint_data.dart'; import '../../networks/dio_client.dart'; import '../model/send_post_model.dart'; - -class PostServices { - // were final instead of static var - +class PostServices +{ static var dio = Dio(); //static var client =http.Client(); sendPost(SendPostModel model, BuildContext context) async { @@ -16,29 +16,27 @@ class PostServices { DioClient.init(prefs); try { final response = - await DioClient.post(path: createPost, data: model.toJson()); + await DioClient.post(path: createPost, data: model.toJson()); if (response.statusCode == 200 || response.statusCode == 201) { print("response of sending post ${response.data}"); ScaffoldMessenger.of(context).showSnackBar( - SnackBar( + const SnackBar( content: Text("sent successfuly", style: TextStyle(color: Colors.white)), backgroundColor: Colors.green), ); - - // print(response.statusCode); - // print(json.decode(response.data)['message']); } } catch (e) { print("error in sending the post -> $e"); } } - - Future uploadImage(XFile file, String id) async { + Future uploadImage(File file,Future id) async { String fileName = file.path.split('/').last; FormData formData = FormData.fromMap({ - "fileName": await MultipartFile.fromFile(file.path, filename: fileName), + "file": + await MultipartFile.fromFile(file.path, filename:fileName), }); final response = await dio.post("/posts/$id/images", data: formData); } + } diff --git a/lib/createpost/widgets/type_of_post_web.dart b/lib/createpost/widgets/type_of_post_web.dart new file mode 100644 index 00000000..bb620039 --- /dev/null +++ b/lib/createpost/widgets/type_of_post_web.dart @@ -0,0 +1,265 @@ +import 'dart:io'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_quill/flutter_quill.dart' as quill; +import 'package:get/get.dart'; +import 'package:get/get_state_manager/src/rx_flutter/rx_obx_widget.dart'; +// import 'package:html_editor_enhanced/html_editor.dart'; +import 'package:video_player/video_player.dart'; +import '../../createpost/controllers/posts_controllers.dart'; + +class BuildFormTypeWeb extends StatelessWidget { + BuildFormTypeWeb({ + Key? key, + required this.controller, + }) : super(key: key); + + final PostController controller; + @override + Widget build(BuildContext context) { + + return Obx( + () => Form( + key: controller.formKeyUrl, + child: Padding( + padding: const EdgeInsetsDirectional.only(start: 10.0), + child: controller.typeOfPost.value == "link" + ? Padding( + padding: const EdgeInsetsDirectional.only(start: 10.0), + child: Container( + width: 1000, + height: 50, + decoration: BoxDecoration( + border: Border.all(color: Colors.black), + color: Colors.white, + ), + + child: TextFormField( + keyboardType: TextInputType.url, + // inputFormatters: [ + // FilteringTextInputFormatter.allow( + // RegExp(r'^[a-zA-Z0-9_\-=@,\.;]+$')) + // ], + onChanged: (value) { + controller.urlPost.refresh(); + }, + validator: (value) { + // String pattern = + // r"((https?:www\.)|(https?:\/\/)|(www\.))[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9]{1,6}(\/[-a-zA-Z0-9()@:%_\+.~#?&\/=]*)?"; + RegExp regExp = RegExp( + "((http|https)://)(www.)?[a-zA-Z0-9@:%._\\+~#?&//=]{2,256}\\.[a-z]{2,6}\\b([-a-zA-Z0-9@:%._\\+~#?&//=]*)"); + if (value!.isEmpty) { + return "Please enter your website"; + } else if (!(regExp.hasMatch(value))) { + return "Website Url must be started from www"; + } else { + return null; + } + }, + controller: controller.urlPost.value, + enabled: true, + style: const TextStyle(fontSize: 14.0), + showCursor: true, + cursorColor: Colors.blue, + cursorHeight: 20.0, + toolbarOptions: const ToolbarOptions( + copy: true, cut: true, paste: true), + textAlign: TextAlign.start, + decoration: const InputDecoration( + hintText: "URL", border: InputBorder.none + //onfieldsubmitted : (string value ) {}دي بتاخد انونيمس فانكشن عشان لو اما اضغط علي سابمت يعمل اكشن معين + ), + ), + ), + ) + : controller.typeOfPost.value == "image" + ? (controller.imageFileList!.length == 0) + ? SizedBox() + : Container( + width: 1000, + height: 500, + decoration: BoxDecoration( + border: Border.all(color: Colors.black), + color: Colors.white, + ), + + child: ListView.separated( + physics: BouncingScrollPhysics(), + scrollDirection: Axis.horizontal, + separatorBuilder: (BuildContext context, index) => + const SizedBox( + width: 20, + ), + itemBuilder: (BuildContext context, index) => + SizedBox( + height: 250, + width: 120, + child: Stack( + //alignment: AlignmentDirectional.topEnd, + children: [ + SizedBox( + height: 200, + width: 100, + ), + Container( + width: 100, + height: 150, + child: Image.file( + File(controller + .imageFileList![index].path), + fit: BoxFit.cover, + )), + Positioned( + top: -20, + right: -15, + child: IconButton( + onPressed: () { + controller.imageFileList! + .removeAt(index); + }, + icon: Icon( + Icons.close, + color: Colors.black, + size: 30, + )), + ), + ], + ), + ), + itemCount: controller.imageFileList!.length, + ), + ) + : (controller.typeOfPost == "video") + ? (controller.videoController.value != null) + ? Container( + width: 1000, + height: 500, + decoration: BoxDecoration( + border: Border.all(color: Colors.black), + color: Colors.white, + ), + + child: FutureBuilder( + future: controller.initializeVideoPlayerFuture, + builder: (context, snapshot) { + if (snapshot.connectionState == + ConnectionState.done) { + // If the VideoPlayerController has finished initialization, use + // the data it provides to limit the aspect ratio of the video. + return AspectRatio( + aspectRatio: controller.videoController + .value!.value.aspectRatio, + // Use the VideoPlayer widget to display the video. + child: Stack( + alignment: + AlignmentDirectional.topEnd, + children: [ + Stack( + children: [ + VideoPlayer(controller + .videoController.value!), + Center( + child: IconButton( + onPressed: () { + controller.playVideo(); + }, + icon: controller + .videoController + .value! + .value + .isPlaying + ? const Icon( + Icons.pause, + color: Colors.white, + size: 30, + ) + : const Icon( + Icons.play_arrow, + color: Colors.white, + size: 30, + ), + ), + ) + ], + ), + IconButton(onPressed: (){ + controller.videoFile.value=null; + controller.videoController.value=null; + }, icon: Icon(Icons.close)) + ]), + ); + } else { + // If the VideoPlayerController is still initializing, show a + // loading spinner. + return const Center( + child: CircularProgressIndicator()); + } + }, + ), + ) + : SizedBox() + : (controller.typeOfPost == "self") + ? Padding( + padding: const EdgeInsetsDirectional.only( + start: 10.0), + child: Container( + width: 1000, + height: 500, + decoration: BoxDecoration( + border: Border.all(color: Colors.black), + color: Colors.white, + ), + child: Column( + children: [ + quill.QuillToolbar.basic( + multiRowsDisplay: true, + controller: controller.textPost.value, + ), + Expanded( + child: Padding( + padding: const EdgeInsets.all(30.0), + child: quill.QuillEditor( + customStyles: quill.DefaultStyles( + color: Colors.red), + showCursor: true, + paintCursorAboveText: true, + focusNode: FocusNode(), + scrollController: ScrollController(), + scrollable: true, + padding: EdgeInsetsDirectional.only( + start: 10.0), + autoFocus: true, + expands: true, + controller: controller.textPost.value, + readOnly: false, + ), + ), + ) + ], + ), + ) + + // child: TextFormField( + // controller: controller.textPost.value, + // enabled: true, + // style: const TextStyle(fontSize: 16.0), + // showCursor: true, + // cursorColor: Colors.blue, + // cursorHeight: 20.0, + // toolbarOptions: + // const ToolbarOptions(copy: true, cut: true, paste: true), + // keyboardType: TextInputType.multiline, + // textInputAction: TextInputAction.newline, + // autofocus: true, + // maxLines: null, + // textAlign: TextAlign.start, + // decoration: const InputDecoration( + // hintText: "Add optional body text", + // border: InputBorder.none), + // ), + ) + : SizedBox()), + ), + ); + } +} diff --git a/lib/home/controller/home_controller.dart b/lib/home/controller/home_controller.dart index f6d30f0f..05290a4a 100644 --- a/lib/home/controller/home_controller.dart +++ b/lib/home/controller/home_controller.dart @@ -1,3 +1,6 @@ +import 'dart:collection'; +import 'dart:io'; +import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:fluttertoast/fluttertoast.dart'; import 'package:get/get.dart'; @@ -6,36 +9,38 @@ import 'package:post/myprofile/models/myprofile_data.dart'; import 'package:post/networks/const_endpoint_data.dart'; import 'package:shared_preferences/shared_preferences.dart'; import '../../comments/models/comment_model.dart'; +import '../../createpost/controllers/posts_controllers.dart'; import '../../networks/dio_client.dart'; import '../../post/models/post_model.dart'; class HomeController extends GetxController with StateMixin> { - RxString sortHistoryBy = "upvoted".obs; - RxString sortHomePostsBy = "best".obs; - RxString sortAllBy = "hot".obs; - - Rx allIcon = Icons.local_fire_department_rounded.obs; - RxInt pageNumber = 1.obs; - List homePosts = [].obs; - List allPosts = [].obs; - List userSavedPosts = [].obs; - List userSavedComments = [].obs; + RxString sortHistoryBy="upvoted".obs; + RxString sortHomePostsBy="best".obs; + RxString sortAllBy="hot".obs; + + RxallIcon=Icons.local_fire_department_rounded.obs; + RxInt pageNumber=1.obs; + ListhomePosts=[].obs; + ListallPosts=[].obs; + ListuserSavedPosts=[].obs; + ListuserSavedComments=[].obs; // ListupvotedPosts=[].obs; // ListdownvotedPosts=[].obs; // ListhiddenPosts=[].obs; - List historyPosts = [].obs; - RxBool isRecentlyVisitedDrawer = false.obs; - List recentlyVisited = [].obs; + ListhistoryPosts=[].obs; + RxBool isRecentlyVisitedDrawer=false.obs; + List recentlyVisited = [ + ].obs; //ListmyCommunities=[].obs; List following = [].obs; RxBool isRecentlyVisitedPannelExpanded = true.obs; RxBool isModeratingPannelExpanded = true.obs; RxBool isFollowingPannelExpanded = true.obs; RxBool isYourCommunitiesPannelExpanded = true.obs; - MyProfileData? myProfile; + MyProfileData? myProfile ; - ///////// for scrol in home////////////////////////// - ScrollController myScroll = ScrollController(); + ///////// for scrol in home////////////////////////// + ScrollController myScroll =ScrollController(); /// true when posts are loading. RxBool isLoading = false.obs; @@ -43,25 +48,22 @@ class HomeController extends GetxController with StateMixin> { /// true when error occurred RxBool error = false.obs; ///////////////////////for in all////////////// - ScrollController myScrollAll = ScrollController(); - RxInt pageNumberAll = 1.obs; - + ScrollController myScrollAll =ScrollController(); +RxInt pageNumberAll=1.obs; /// true when posts are loading. RxBool isLoadingAll = false.obs; /// true when error occurred RxBool errorAll = false.obs; //////History////////////// - RxInt pageNumberHistory = 1.obs; - + RxInt pageNumberHistory=1.obs; /// true when posts are loading. RxBool isLoadingHistory = false.obs; /// true when error occurred RxBool errorHistory = false.obs; //////////////SAVED///////////// - RxInt pageNumberSaved = 1.obs; - + RxInt pageNumberSaved=1.obs; /// true when posts are loading. RxBool isLoadingSaved = false.obs; @@ -69,20 +71,19 @@ class HomeController extends GetxController with StateMixin> { RxBool errorSaved = false.obs; //////////////SAVEDCOMMENTS///////////// - RxInt pageNumberComment = 1.obs; - + RxInt pageNumberComment=1.obs; /// true when posts are loading. RxBool isLoadingCommetns = false.obs; /// true when error occurred RxBool errorComment = false.obs; + @override void onInit() { getInfoOfMe(); super.onInit(); } - Future getInfoOfMe() async { try { final prefs = await SharedPreferences.getInstance(); @@ -92,106 +93,111 @@ class HomeController extends GetxController with StateMixin> { myProfile = MyProfileData.fromJson(response.data['user']); }); } catch (error) { - print( - "there is an error in fetching the data of my profile the error is -> $error"); + print("there is an error in fetching the data of my profile the error is -> $error"); } } - - Future getSavedPosts() async { + Future getSavedPosts({required int p,}) async { final prefs = await SharedPreferences.getInstance(); DioClient.init(prefs); try { - final response = await DioClient.get( - path: '/users/saved?page=${pageNumberSaved}&limit=20', + final response =await DioClient.get( + path:'/users/saved?page=${p}&limit=20', ); - if (response.statusCode == 200) { - for (var post in response.data['savedPosts']) { - PostModel temp = PostModel(); + if(response.statusCode==200) + { + for (var post in response.data['savedPosts']) + { + PostModel temp =PostModel(); await temp.fromJson(post); userSavedPosts.add(temp); } - } else { + } + else { await showToast(response.statusMessage.toString()); - errorSaved.value = true; + errorSaved.value=true; } - isLoadingSaved.value = false; + isLoadingSaved.value=false; } catch (error) { print("error in fetching history posts $error"); change([], status: RxStatus.error(error.toString())); } } - - Future getSavedComments() async { + Future getSavedComments({required int p}) async { final prefs = await SharedPreferences.getInstance(); DioClient.init(prefs); try { - final response = await DioClient.get( - path: '/users/saved?page=${pageNumberComment}&limit=20', + final response =await DioClient.get( + path:'/users/saved?page=${p}&limit=20', ); - if (response.statusCode == 200) { - for (var comment in response.data['savedComments']) { + if(response.statusCode==200) + { + for (var comment in response.data['savedComments']) + { userSavedComments.add(CommentModel.fromJson(comment)); } - } else { + } + else { await showToast(response.statusMessage.toString()); - errorComment.value = true; + errorComment.value=true; } - isLoadingCommetns.value = false; + isLoadingCommetns.value=false; } catch (error) { print("error in fetching saved comments $error"); change([], status: RxStatus.error(error.toString())); } } - - Future getHistory() async { + Future getHistory({required String sort,required int p}) async { final prefs = await SharedPreferences.getInstance(); DioClient.init(prefs); try { - final response = await DioClient.get( - path: '/users/${sortHistoryBy}?page=${pageNumberHistory}&limit=20', + final response =await DioClient.get( + path:'/users/${sort}?page=${p}&limit=20', ); - if (response.statusCode == 200) { - for (var post in response.data['posts']) { - PostModel temp = PostModel(); + if(response.statusCode==200) + { + for (var post in response.data['posts']) + { + PostModel temp =PostModel(); await temp.fromJson(post); historyPosts.add(temp); } - } else { + } + else { await showToast(response.statusMessage.toString()); - errorHistory.value = true; + errorHistory.value=true; } - isLoadingHistory.value = false; + isLoadingHistory.value=false; } catch (error) { print("error in fetching history posts $error"); change([], status: RxStatus.error(error.toString())); } } - - Future getPosts() async { - isLoading.value = true; - error.value = false; + Future getPosts({required String sort,required int p}) async { final prefs = await SharedPreferences.getInstance(); DioClient.init(prefs); try { - final response = await DioClient.get( - path: '/users/${sortHomePostsBy}?page=${pageNumberAll}&limit=20', + final response =await DioClient.get( + path:'/users/${sort}?page=${p}&limit=20', ); - if (response.statusCode == 200) { - for (var post in response.data['data']) { - PostModel temp = PostModel(); - await temp.fromJson(post); - homePosts.add(temp); + if(response.statusCode==200) + { + for (var post in response.data['data']) + { + PostModel temp =PostModel(); + await temp.fromJson(post); + homePosts.add(temp); + } + // response.data["data"].forEach((value1) async{ + // PostModel temp =PostModel(); + // await temp.fromJson(value1); + // homePosts.add(temp); + // }); } - // response.data["data"].forEach((value1) async{ - // PostModel temp =PostModel(); - // await temp.fromJson(value1); - // homePosts.add(temp); - // }); - } else { + else { await showToast(response.statusMessage.toString()); - error.value = true; + error.value=true; } - isLoading.value = false; + isLoading.value=false; print("the length of returned list in home is ${homePosts.length}"); print("${homePosts[0]}"); } catch (error) { @@ -199,41 +205,29 @@ class HomeController extends GetxController with StateMixin> { change([], status: RxStatus.error(error.toString())); } } - - Future getPostsInAll() async { + Future getPostsInAll({required String sort,required int p}) async { // isLoading.value=true; // error.value=false; final prefs = await SharedPreferences.getInstance(); DioClient.init(prefs); try { - final response = await DioClient.get( - path: '/users/${sortAllBy}?page=${pageNumber}&limit=20', + final response =await DioClient.get( + path:'/users/${sort}?page=${p}&limit=20', ); - // .then((value) { - // print(value); - // // List data = value.data.map((e) => PostModel.fromJson(value)); - // // change(data, status: RxStatus.success()); - // value.data["data"].forEach((value1){ - // homePosts.add(PostModel.fromJson(value1)); - // }); - // }); - - if (response.statusCode == 200) { - for (var post in response.data['data']) { - PostModel temp = PostModel(); - await temp.fromJson(post); - allPosts.add(temp); - } - // response.data["data"].forEach((value1) async{ - // PostModel temp =PostModel(); - // await temp.fromJson(value1); - // allPosts.add(temp); - // }); - } else { + if(response.statusCode==200) + { + for (var post in response.data['data']) + { + PostModel temp =PostModel(); + await temp.fromJson(post); + allPosts.add(temp); + } + } + else { await showToast(response.statusMessage.toString()); - error.value = true; + error.value=true; } - isLoading.value = false; + isLoading.value=false; print("the length of returned list in all is ${homePosts.length}"); print("${homePosts[0]}"); } catch (error) { @@ -241,7 +235,6 @@ class HomeController extends GetxController with StateMixin> { change([], status: RxStatus.error(error.toString())); } } - Future showToast(final String msg) async { await Fluttertoast.showToast( msg: msg, diff --git a/lib/home/screens/all.dart b/lib/home/screens/all.dart index 990a6eb4..273e8d3e 100644 --- a/lib/home/screens/all.dart +++ b/lib/home/screens/all.dart @@ -1,11 +1,19 @@ +import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; +import 'package:fluttericon/typicons_icons.dart'; import 'package:get/get.dart'; import 'package:post/home/widgets/buttom_nav_bar.dart'; import 'package:responsive_sizer/responsive_sizer.dart'; +import '../../create_community/screens/create_community.dart'; +import '../../createpost/controllers/posts_controllers.dart'; +import '../../createpost/screens/createpost.dart'; import '../../icons/icon_broken.dart'; +import '../../post/widgets/post.dart'; import '../../post/widgets/post_list.dart'; import '../../widgets/loading_reddit.dart'; import '../controller/home_controller.dart'; +import '../widgets/custom_upper_bar.dart'; +import 'home_layout.dart'; class All extends StatefulWidget { const All({Key? key}) : super(key: key); @@ -14,7 +22,12 @@ class All extends StatefulWidget { } class _AllState extends State with TickerProviderStateMixin { - final HomeController controller = Get.put(HomeController()); + final HomeController controller = Get.put( + HomeController(), + ); + final PostController controllerForPost = Get.put( + PostController(), + ); late AnimationController loadingSpinnerAnimationController; @override void initState() { @@ -23,10 +36,9 @@ class _AllState extends State with TickerProviderStateMixin { AnimationController(duration: const Duration(seconds: 2), vsync: this); loadingSpinnerAnimationController.repeat(); if (controller.allPosts.isEmpty) { - controller.getPostsInAll(); + controller.getPostsInAll(sort: controller.sortAllBy.value,p: controller.pageNumberAll.value); } } - void dispose() { loadingSpinnerAnimationController.dispose(); super.dispose(); @@ -34,13 +46,273 @@ class _AllState extends State with TickerProviderStateMixin { @override Widget build(BuildContext context) { - return Obx( - () => Scaffold( + + double ScreenSizeWidth = MediaQuery.of(context).size.width; + + final isDesktop = ScreenSizeWidth >= 700; + final isMobile = ScreenSizeWidth < 700; + + if (isDesktop) { + return + Obx(()=> + Scaffold( + appBar: PreferredSize( + preferredSize: Size(700, 60), + child: UpBar(controller: controller, controllerForCreatePost: controllerForPost,)), + backgroundColor: const Color(0xA2D4E4FA), + body: + SingleChildScrollView( + scrollDirection: Axis.vertical, + child: Column( + children: [ + Row( + children: [ + Padding( + padding: EdgeInsetsDirectional.only(start: 200), + child: Column( + children: [ + SizedBox(height: 2,), + Container( + color: Colors.white, + height: 50, + width: 650, + child: Row( + children: [ + Padding( + padding: const EdgeInsets.all(8.0), + child: Container( + child: Image.asset('assets/images/1.png'), + height: 40, + width: 40, + ), + ), + Container( + width: 450.0, + height: 35.0, + color: const Color(0xA2F2F4F6), + child: TextFormField( + onTap: ()=> Get.to(CreatePostSCreen()), + enabled: false, + decoration: const InputDecoration( + border: InputBorder.none, + labelText: 'Create Post', + labelStyle: TextStyle( + color: Colors.grey, + fontSize: 18.0, + ), + ), + ), + ), + SizedBox(width: 10,), + IconButton( + icon:Icon(Icons.photo), + onPressed: () { + Get.to(CreatePostSCreen(), arguments: [ + 0, 0, + 0 + ]); + }, + + ), + const SizedBox( + width: 20, + ), + IconButton( + onPressed: () + { + Get.to(CreatePostSCreen(), arguments: [ + 0, 0, + 0 + ]); + }, + icon:Icon(Icons.insert_link)), + ], + ), + ), + SizedBox(height: 10,), + Container( + color: Colors.white, + height: 50, + width: 650, + child: Row( + children: [ + Padding( + padding: const EdgeInsets.all(8.0), + child: Container( + child: ElevatedButton.icon( + style: ElevatedButton.styleFrom(shape: StadiumBorder()), + onPressed: () { + controller.sortHomePostsBy.value="best"; + controller.sortHomePostsBy.update((val) { }); + }, + icon: Icon( // <-- Icon + Icons.add_alert_rounded, + size: 15.0, + ), + label: Text('Best'), // <-- Text + ), + height: 80, + width: 80, + ), + ), + + ElevatedButton.icon( + style: ElevatedButton.styleFrom(shape: StadiumBorder()), + onPressed: () { + controller.sortHomePostsBy.value="hot"; + controller.sortHomePostsBy.update((val) { }); + }, + icon: Icon( // <-- Icon + Icons.fireplace, + size: 15.0, + ), + label: Text('Hot'), // <-- Text + ), + SizedBox( + width: 10, + ), + ElevatedButton.icon( + style: ElevatedButton.styleFrom(shape: StadiumBorder(),shadowColor: Colors.blue), + onPressed: () { + controller.sortHomePostsBy.value="new"; + controller.sortHomePostsBy.update((val) { }); + }, + icon: Icon( // <-- Icon + Icons.new_releases_outlined, + size: 15.0, + ), + label: Text('New'), // <-- Text + ), + SizedBox( + width: 10, + ), + ElevatedButton.icon( + style: ElevatedButton.styleFrom(shape: StadiumBorder()), + onPressed: () { + controller.sortHomePostsBy.value="top"; + controller.sortHomePostsBy.update((val) { }); + }, + icon: Icon( // <-- Icon + Icons.topic, + size: 15.0, + ), + label: Text('Top'), // <-- Text + ), + SizedBox( + width: 50, + ), + + ], + ), + ), + ], + ), + ), + SizedBox(width:100 ,), + Container( + margin:EdgeInsetsDirectional.only(top: 100) , + height: 300, + width: 350, + color: Colors.white, + child: Column(mainAxisAlignment: MainAxisAlignment.center, + children: [ + Padding( + padding: const EdgeInsets.all(30), + child: Text("Your personal Reddit frontpage. Come here to check in with your favorite communities."), + ), + SizedBox(height: 50,), + Container( + width: 300, + child: ElevatedButton( + onPressed: () { + Get.to(CreatePostSCreen(),arguments: [0,0,0]); + }, + child: Text(' Create post ',style: TextStyle(color: Colors.white),), + style: ElevatedButton.styleFrom(shape: StadiumBorder(),backgroundColor: Colors.blue), + ), + ), + SizedBox(height: 10,), + Container( + width: 300, + child: OutlinedButton( + onPressed: () { + Get.to(CreateCommunity()); + }, + child: Text('Create Community',style: TextStyle(color: Colors.blue),), + style: OutlinedButton.styleFrom( + shape: StadiumBorder(), + side: BorderSide(width: 2.0, color: Colors.blue), + + ), + ), + ) + ],), + ) + ], + ), + RefreshIndicator( + backgroundColor: Colors.white, + color: Colors.blue[900], + onRefresh: () async { + controller.allPosts.clear(); + controller.pageNumberAll.value = 1; + controller.pageNumberAll.update((val) {}); + controller.getPostsInAll(sort: controller.sortAllBy.value,p: controller.pageNumberAll.value); + controller.update(); + }, + child: controller.isLoading.value + ? Center( + child: CircularProgressIndicator( + valueColor: loadingSpinnerAnimationController.drive( + ColorTween( + //begin: Colors.blueAccent, + end: Colors.blueAccent, + ), + ), + ), + ) + : controller.error.value + ? Text("Unexpected Error Try Again..") + : controller.homePosts.isEmpty + ? LoadingReddit() + //const Text( "No Posts to show") + : PostList( + leftMargin: 200.0, + rightMargin: 650.0, + userName: '${controller.myProfile!.userName}', + updateData: () + { + controller.pageNumberAll.value++; + controller.pageNumberAll.update((val) { }); + }, + data: controller.homePosts, + ), + ), + ], + ), + ), + floatingActionButtonLocation:FloatingActionButtonLocation.miniEndFloat , + floatingActionButton: Padding( + padding: const EdgeInsetsDirectional.only(end: 320.0), + child: ElevatedButton( + onPressed: () { + Get.to(HomeLayoutScreen()); + }, + child: Text(' Back to top ',style: TextStyle(color: Colors.white),), + style: ElevatedButton.styleFrom(shape: StadiumBorder(),backgroundColor: Colors.blue), + ), + ), + + + ), + ); + } + return + Obx(()=> Scaffold( bottomNavigationBar: const buttomNavBar( fromProfile: 0, icon: '', - nameOfSubreddit: '', - x: 1, + nameOfSubreddit: '', x: 1, ), backgroundColor: Colors.grey[300], appBar: AppBar( @@ -56,250 +328,218 @@ class _AllState extends State with TickerProviderStateMixin { style: TextStyle(fontSize: 18, fontWeight: FontWeight.w700), ), ), - body: RefreshIndicator( + body: + RefreshIndicator( backgroundColor: Colors.white, color: Colors.blue[900], onRefresh: () async { controller.allPosts.clear(); controller.pageNumberAll.value = 1; - controller.pageNumber.update((val) {}); - controller.getPostsInAll(); + controller.pageNumberAll.update((val) {}); + controller.getPostsInAll(sort: controller.sortAllBy.value, p: controller.pageNumberAll.value); controller.update(); }, child: controller.isLoadingAll.value ? Center( - child: CircularProgressIndicator( - valueColor: loadingSpinnerAnimationController.drive( - ColorTween( - //begin: Colors.blueAccent, - end: Colors.blueAccent, - ), - ), - ), - ) + child: CircularProgressIndicator( + valueColor: loadingSpinnerAnimationController.drive( + ColorTween( + //begin: Colors.blueAccent, + end: Colors.blueAccent, + ), + ), + ), + ) : controller.error.value - ? ListView( - children: const [ - Padding( - padding: EdgeInsets.all(150), - child: Text("Unexpected Error Try Again.."), - ), - ], - ) - : controller.allPosts.isEmpty - ? LoadingReddit() - : PostList( - userName: '${controller.myProfile!.userName}', - updateData: () { - controller.pageNumberAll.value++; - controller.pageNumberAll.update((val) {}); - controller.getPostsInAll(); - }, - data: controller.allPosts, - topOfTheList: Padding( - padding: const EdgeInsetsDirectional.only( - start: 15, top: 5), - child: Row( - children: [ - Icon( - controller.allIcon.value, - color: Colors.grey, - size: 20, - ), - ElevatedButton.icon( - onPressed: () { - showModalBottomSheet( - backgroundColor: Colors.transparent, - context: context, - builder: (context) => GestureDetector( - onTap: () {}, - child: Container( - padding: - const EdgeInsets.all(30), - height: 30.h, - width: 30.w, - margin: const EdgeInsets.all(5), - decoration: BoxDecoration( - borderRadius: - BorderRadius.circular( - 5), - color: Colors.white), - child: Column( - mainAxisAlignment: - MainAxisAlignment.start, - crossAxisAlignment: - CrossAxisAlignment.start, - mainAxisSize: - MainAxisSize.min, - children: [ - const Text('SORT POSTS BY', - style: TextStyle( - color: Colors.grey, - )), - const Divider( - height: 5, - ), - ListTile( - onTap: () { - controller.sortAllBy - .value = "hot"; - controller - .allIcon.value = - Icons - .local_fire_department_rounded; - controller.sortHistoryBy - .update((val) {}); - Get.back(); - // Get.forceAppUpdate(); - }, - leading: Icon( - Icons - .local_fire_department_rounded, - color: (controller - .sortAllBy - .value == - "hot") - ? Colors.black - : Colors - .grey[500]), - title: Text( - "Hot", - style: TextStyle( - color: (controller - .sortAllBy - .value == - "hot") - ? Colors.black - : Colors - .grey[500]), - ), - trailing: (controller - .sortAllBy - .value == - "hot") - ? Icon( - Icons.done, - color: - Colors.green, - ) - : null, - ), - ListTile( - onTap: () { - controller.sortAllBy - .value = "new"; - controller - .allIcon.value = - Icons - .brightness_low; - controller.sortHistoryBy - .update((val) {}); - Get.back(); - // Get.forceAppUpdate(); - }, - leading: Icon( - Icons.brightness_low, - color: (controller - .sortAllBy - .value == - "new") - ? Colors.black - : Colors - .grey[500]), - title: Text( - "New", - style: TextStyle( - color: (controller - .sortAllBy - .value == - "new") - ? Colors.black - : Colors - .grey[500]), - ), - trailing: (controller - .sortAllBy - .value == - "new") - ? Icon( - Icons.done, - color: - Colors.green, - ) - : null, - ), - ListTile( - onTap: () { - controller.sortAllBy - .value = "top"; - controller - .allIcon.value = - Icons - .turn_sharp_right_sharp; - controller.sortHistoryBy - .update((val) {}); - Get.back(); - // Get.forceAppUpdate(); - }, - leading: Icon( - Icons - .turn_sharp_right_sharp, - color: (controller - .sortAllBy - .value == - "top") - ? Colors.black - : Colors - .grey[500]), - title: Text( - "Top", - style: TextStyle( - color: (controller - .sortAllBy - .value == - "top") - ? Colors.black - : Colors - .grey[500]), - ), - trailing: (controller - .sortAllBy - .value == - "top") - ? Icon( - Icons.done, - color: - Colors.green, - ) - : null, - ), - ], - ), - ), - )); - }, - icon: Text( - "${controller.sortAllBy.value.toUpperCase()}", - style: const TextStyle(color: Colors.grey), + ? ListView( + children: const [ + Padding( + padding: EdgeInsets.all(150), + child: Text("Unexpected Error Try Again.."), + ), + ], + ) + : controller.allPosts.isEmpty + ? LoadingReddit() + : + PostList( + userName: '${controller.myProfile!.userName}', updateData: (){ + controller.pageNumberAll.value++; + controller.pageNumberAll.update((val) { }); + controller.getPostsInAll(sort: controller.sortAllBy.value,p: controller.pageNumberAll.value); + }, data: controller.allPosts, + topOfTheList: + Padding( + padding: const EdgeInsetsDirectional.only(start: 15, top: 5), + child: Row( + children: [ + Icon( + controller.allIcon.value, + color: Colors.grey, + size: 20, + ), + ElevatedButton.icon( + onPressed: () { + showModalBottomSheet( + backgroundColor: Colors.transparent, + context: context, + builder: (context) => GestureDetector( + onTap: () {}, + child: Container( + padding: const EdgeInsets.all(30), + height: 30.h, + width: 30.w, + margin: const EdgeInsets.all(5), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(5), + color: Colors.white), + child: Column( + mainAxisAlignment: + MainAxisAlignment.start, + crossAxisAlignment: + CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + const Text('SORT POSTS BY', + style: TextStyle( + color: Colors.grey, + )), + const Divider( + height: 5, ), - label: const Icon( - IconBroken.Arrow___Down_2, - color: Colors.grey, - size: 15, + ListTile( + onTap: () { + controller.sortAllBy.value = "hot"; + controller.allIcon.value = Icons + .local_fire_department_rounded; + controller.sortHistoryBy.update((val) { }); + Get.back(); + // Get.forceAppUpdate(); + + }, + leading: Icon( + Icons + .local_fire_department_rounded, + color: + (controller.sortAllBy.value == + "hot") + ? Colors.black + : Colors.grey[500]), + title: Text( + "Hot", + style: TextStyle( + color: (controller + .sortAllBy.value == + "hot") + ? Colors.black + : Colors.grey[500]), + ), + trailing: + (controller.sortAllBy.value == + "hot") + ? Icon( + Icons.done, + color: Colors.green, + ) + : null, ), - style: ButtonStyle( - elevation: - MaterialStateProperty.all(0), - backgroundColor: MaterialStateProperty.all( - Colors.grey[300]), + ListTile( + onTap: () { + controller.sortAllBy.value = "new"; + controller.allIcon.value = + Icons.brightness_low; + controller.sortHistoryBy.update((val) { }); + Get.back(); + // Get.forceAppUpdate(); + + }, + leading: Icon(Icons.brightness_low, + color: + (controller.sortAllBy.value == + "new") + ? Colors.black + : Colors.grey[500]), + title: Text( + "New", + style: TextStyle( + color: (controller + .sortAllBy.value == + "new") + ? Colors.black + : Colors.grey[500]), + ), + trailing: + (controller.sortAllBy.value == + "new") + ? Icon( + Icons.done, + color: Colors.green, + ) + : null, ), - ), - ], + ListTile( + onTap: () { + controller.sortAllBy.value = "top"; + controller.allIcon.value = + Icons.turn_sharp_right_sharp; + controller.sortHistoryBy.update((val) { }); + Get.back(); + // Get.forceAppUpdate(); + + }, + leading: Icon( + Icons.turn_sharp_right_sharp, + color: + (controller.sortAllBy.value == + "top") + ? Colors.black + : Colors.grey[500]), + title: Text( + "Top", + style: TextStyle( + color: (controller + .sortAllBy.value == + "top") + ? Colors.black + : Colors.grey[500]), + ), + trailing: + (controller.sortAllBy.value == + "top") + ? Icon( + Icons.done, + color: Colors.green, + ) + : null, + ), + ], + ), ), - ), - ), + )); + }, + icon: Text( + "${controller.sortAllBy.value.toUpperCase()}", + style: const TextStyle(color: Colors.grey), + ), + label: const Icon( + IconBroken.Arrow___Down_2, + color: Colors.grey, + size: 15, + ), + style: ButtonStyle( + elevation: MaterialStateProperty.all(0), + backgroundColor: + MaterialStateProperty.all(Colors.grey[300]), + ), + ), + ], + ), + ), + ), ), + + ), - ); + ); } } diff --git a/lib/home/screens/history.dart b/lib/home/screens/history.dart index e031c7d6..400ffb98 100644 --- a/lib/home/screens/history.dart +++ b/lib/home/screens/history.dart @@ -1,11 +1,16 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; import 'package:flutter/material.dart'; import 'package:fluttericon/typicons_icons.dart'; import 'package:get/get.dart'; +import 'package:get/get_core/src/get_main.dart'; import 'package:post/home/controller/home_controller.dart'; import 'package:post/icons/icon_broken.dart'; import 'package:responsive_sizer/responsive_sizer.dart'; import '../../post/widgets/post_list.dart'; import '../../widgets/loading_reddit.dart'; +import '../controller/home_controller.dart'; class History extends StatefulWidget { History({Key? key}) : super(key: key); @@ -26,7 +31,7 @@ class _HistoryState extends Statewith TickerProviderStateMixin { AnimationController(duration: const Duration(seconds: 2), vsync: this); loadingSpinnerAnimationController.repeat(); if (controller.allPosts.isEmpty) { - controller.getHistory(); + controller.getHistory(sort: controller.sortHistoryBy.value, p: controller.pageNumberHistory.value,); } } void dispose() { @@ -61,6 +66,10 @@ class _HistoryState extends Statewith TickerProviderStateMixin { return PopupMenuItem( value: choice, child: ListTile( + onTap: () + { + controller.historyPosts.clear(); + }, leading: Icon(Icons.close_outlined), title: Text(choice), ), @@ -77,7 +86,7 @@ class _HistoryState extends Statewith TickerProviderStateMixin { controller.historyPosts.clear(); controller.pageNumberHistory.value = 1; controller.pageNumberHistory.update((val) {}); - controller.getHistory(); + controller.getHistory(sort: controller.sortHistoryBy.value, p: controller.pageNumberHistory.value,); controller.update(); //Get.forceAppUpdate(); }, @@ -108,7 +117,7 @@ class _HistoryState extends Statewith TickerProviderStateMixin { userName: '${controller.myProfile!.userName}', updateData: (){ controller.pageNumberHistory.value++; controller.pageNumberHistory.update((val) { }); - controller.getHistory(); + controller.getHistory(sort: controller.sortHistoryBy.value, p: controller.pageNumberHistory.value,); }, data: controller.historyPosts, topOfTheList: Padding( diff --git a/lib/home/screens/home_layout.dart b/lib/home/screens/home_layout.dart index 06271396..3d3e6142 100644 --- a/lib/home/screens/home_layout.dart +++ b/lib/home/screens/home_layout.dart @@ -1,16 +1,24 @@ +import 'package:custom_refresh_indicator/custom_refresh_indicator.dart'; import 'package:flutter/services.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; +import 'package:post/create_community/screens/create_community.dart'; import 'package:post/createpost/controllers/posts_controllers.dart'; +import 'package:post/createpost/screens/createpost.dart'; import 'package:post/home/controller/home_controller.dart'; +import 'package:post/home/widgets/community_container.dart'; import 'package:post/home/widgets/new_drawer.dart'; +import 'package:post/post/models/post_model.dart'; import 'package:post/widgets/loading_reddit.dart'; import '../../icons/icon_broken.dart'; +import '../../post/widgets/post.dart'; import '../../post/widgets/post_list.dart'; import '../widgets/buttom_nav_bar.dart'; +import '../widgets/custom_upper_bar.dart'; import '../widgets/end_drawer.dart'; +import '../controller/home_controller.dart'; +import '../../createpost/controllers/posts_controllers.dart'; import '../widgets/recently_visited_list.dart'; -import '../../search/screens/search.dart'; class HomeLayoutScreen extends StatefulWidget { static const routeName = '/homepage'; @@ -69,7 +77,7 @@ class _HomeLayoutScreenState extends State AnimationController(duration: const Duration(seconds: 2), vsync: this); loadingSpinnerAnimationController.repeat(); if (controller.homePosts.isEmpty) { - controller.getPosts(); + controller.getPosts(sort: controller.sortHomePostsBy.value,p: controller.pageNumber.value); } } @@ -78,32 +86,286 @@ class _HomeLayoutScreenState extends State super.dispose(); } - void updateData() { - controller.pageNumber.value++; - controller.getPosts(); - } + // for dropdown list - String dropValue = "Home"; - // Lists for DropDown Menu at appBar List dropdownItems = [ DropdownMenuItem( child: ButtonBar( - children: [Text("Home")], - )), + children: [Text("Home")], + )), DropdownMenuItem( child: ButtonBar( - children: [Text("Popular")], - )) + children: [Text("Popular")], + )) ]; @override Widget build(BuildContext context) { + double ScreenSizeWidth = MediaQuery.of(context).size.width; + + final isDesktop = ScreenSizeWidth >= 700; + final isMobile = ScreenSizeWidth < 700; + + if (isDesktop) { + return + Obx(()=> + Scaffold( + appBar: PreferredSize( + preferredSize: Size(700, 60), + child: UpBar(controller: controller, controllerForCreatePost: controllerForPost,)), + backgroundColor: const Color(0xA2D4E4FA), + body:SingleChildScrollView( + scrollDirection: Axis.vertical, + child: Column( + children: [ + Row( + children: [ + Padding( + padding: EdgeInsetsDirectional.only(start: 200), + child: Column( + children: [ + SizedBox(height: 2,), + Container( + color: Colors.white, + height: 50, + width: 650, + child: Row( + children: [ + Padding( + padding: const EdgeInsets.all(8.0), + child: Container( + child: Image.asset('assets/images/1.png'), + height: 40, + width: 40, + ), + ), + Container( + width: 450.0, + height: 35.0, + color: const Color(0xA2F2F4F6), + child: TextFormField( + onTap: ()=> Get.to(CreatePostSCreen()), + enabled: false, + decoration: const InputDecoration( + border: InputBorder.none, + labelText: 'Create Post', + labelStyle: TextStyle( + color: Colors.grey, + fontSize: 18.0, + ), + ), + ), + ), + SizedBox(width: 10,), + IconButton( + icon:Icon(Icons.photo), + onPressed: () { + Get.to(CreatePostSCreen(), arguments: [ + 0, 0, + 0 + ]); + }, + + ), + const SizedBox( + width: 20, + ), + IconButton( + onPressed: () + { + Get.to(CreatePostSCreen(), arguments: [ + 0, 0, + 0 + ]); + }, + icon:Icon(Icons.insert_link)), + ], + ), + ), + SizedBox(height: 10,), + Container( + color: Colors.white, + height: 50, + width: 650, + child: Row( + children: [ + Padding( + padding: const EdgeInsets.all(8.0), + child: Container( + child: ElevatedButton.icon( + style: ElevatedButton.styleFrom(shape: StadiumBorder()), + onPressed: () { + controller.sortHomePostsBy.value="best"; + controller.sortHomePostsBy.update((val) { }); + }, + icon: Icon( // <-- Icon + Icons.add_alert_rounded, + size: 15.0, + ), + label: Text('Best'), // <-- Text + ), + height: 80, + width: 80, + ), + ), + + ElevatedButton.icon( + style: ElevatedButton.styleFrom(shape: StadiumBorder()), + onPressed: () { + controller.sortHomePostsBy.value="hot"; + controller.sortHomePostsBy.update((val) { }); + }, + icon: Icon( // <-- Icon + Icons.fireplace, + size: 15.0, + ), + label: Text('Hot'), // <-- Text + ), + SizedBox( + width: 10, + ), + ElevatedButton.icon( + style: ElevatedButton.styleFrom(shape: StadiumBorder(),shadowColor: Colors.blue), + onPressed: () { + controller.sortHomePostsBy.value="new"; + controller.sortHomePostsBy.update((val) { }); + }, + icon: Icon( // <-- Icon + Icons.new_releases_outlined, + size: 15.0, + ), + label: Text('New'), // <-- Text + ), + SizedBox( + width: 10, + ), + ElevatedButton.icon( + style: ElevatedButton.styleFrom(shape: StadiumBorder()), + onPressed: () { + controller.sortHomePostsBy.value="top"; + controller.sortHomePostsBy.update((val) { }); + }, + icon: Icon( // <-- Icon + Icons.topic, + size: 15.0, + ), + label: Text('Top'), // <-- Text + ), + SizedBox( + width: 50, + ), + + ], + ), + ), + ], + ), + ), + SizedBox(width:100 ,), + Container( + margin:EdgeInsetsDirectional.only(top: 100) , + height: 300, + width: 350, + color: Colors.white, + child: Column(mainAxisAlignment: MainAxisAlignment.center, + children: [ + Padding( + padding: const EdgeInsets.all(30), + child: Text("Your personal Reddit frontpage. Come here to check in with your favorite communities."), + ), + SizedBox(height: 50,), + Container( + width: 300, + child: ElevatedButton( + onPressed: () { + Get.to(CreatePostSCreen(),arguments: [0,0,0]); + }, + child: Text(' Create post ',style: TextStyle(color: Colors.white),), + style: ElevatedButton.styleFrom(shape: StadiumBorder(),backgroundColor: Colors.blue), + ), + ), + SizedBox(height: 10,), + Container( + width: 300, + child: OutlinedButton( + onPressed: () { + Get.to(CreateCommunity()); + }, + child: Text('Create Community',style: TextStyle(color: Colors.blue),), + style: OutlinedButton.styleFrom( + shape: StadiumBorder(), + side: BorderSide(width: 2.0, color: Colors.blue), + + ), + ), + ) + ],), + ) + ], + ), + RefreshIndicator( + backgroundColor: Colors.white, + color: Colors.blue[900], + onRefresh: () async { + controller.homePosts.clear(); + controller.pageNumber.value = 1; + controller.pageNumber.update((val) {}); + controller.getPosts(sort: controller.sortHomePostsBy.value,p: controller.pageNumber.value); + controller.update(); + }, + child: controller.isLoading.value + ? Center( + child: CircularProgressIndicator( + valueColor: loadingSpinnerAnimationController.drive( + ColorTween( + //begin: Colors.blueAccent, + end: Colors.blueAccent, + ), + ), + ), + ) + : controller.error.value + ? Text("Unexpected Error Try Again..") + : controller.homePosts.isEmpty + ? LoadingReddit() + //const Text( "No Posts to show") + : PostList( + leftMargin: 200.0, + rightMargin: 650.0, + userName: '${controller.myProfile!.userName}', + updateData: () + { + controller.pageNumber.value++; + controller.pageNumber.update((val) { }); + }, + data: controller.homePosts, + ), + ), + ], + ), + ), + floatingActionButtonLocation:FloatingActionButtonLocation.miniEndFloat , + floatingActionButton: Padding( + padding: const EdgeInsetsDirectional.only(end: 320.0), + child: ElevatedButton( + onPressed: () { + Get.to(HomeLayoutScreen()); + }, + child: Text(' Back to top ',style: TextStyle(color: Colors.white),), + style: ElevatedButton.styleFrom(shape: StadiumBorder(),backgroundColor: Colors.blue), + ), + ), + + + ), + ); + } return Obx( - () => Scaffold( + () => Scaffold( appBar: AppBar( - // To make style for status bar - systemOverlayStyle: SystemUiOverlayStyle( + // To make style for status bar + systemOverlayStyle: const SystemUiOverlayStyle( statusBarColor: Colors.white, statusBarIconBrightness: Brightness.dark, ), @@ -131,14 +393,14 @@ class _HomeLayoutScreenState extends State "modsub length ${controllerForPost.moderatedSubreddits.length}"); print("name of ${controllerForPost.moderatedSubreddits[0]}"); }, - icon: Text( + icon: const Text( "Home", style: TextStyle( color: Colors.black, fontSize: 17.0, fontWeight: FontWeight.w600), ), - label: Icon( + label: const Icon( IconBroken.Arrow___Down_2, color: Colors.black, ), @@ -150,9 +412,7 @@ class _HomeLayoutScreenState extends State ), actions: [ IconButton( - onPressed: () { - Navigator.pushNamed(context, Search.routeName); - }, + onPressed: () {}, icon: Icon( IconBroken.Search, color: Colors.black87, @@ -198,13 +458,13 @@ class _HomeLayoutScreenState extends State controller: controller, ), drawer: //RecentlyVisitedDrawer(controller: this.controller,), - (controller.isRecentlyVisitedDrawer.value == true) - ? RecentlyVisitedDrawer( - controller: this.controller, - ) - : MyDrawer( - controller: this.controller, - controllerForCreatePost: this.controllerForPost), + (controller.isRecentlyVisitedDrawer.value == true) + ? RecentlyVisitedDrawer( + controller: this.controller, + ) + : MyDrawer( + controller: this.controller, + controllerForCreatePost: this.controllerForPost), body: RefreshIndicator( backgroundColor: Colors.white, color: Colors.blue[900], @@ -212,50 +472,41 @@ class _HomeLayoutScreenState extends State controller.homePosts.clear(); controller.pageNumber.value = 1; controller.pageNumber.update((val) {}); - controller.getPosts(); + controller.getPosts(sort: controller.sortHomePostsBy.value,p: controller.pageNumber.value); controller.update(); }, child: controller.isLoading.value ? Center( - child: CircularProgressIndicator( - valueColor: loadingSpinnerAnimationController.drive( - ColorTween( - //begin: Colors.blueAccent, - end: Colors.blueAccent, - ), - ), - ), - ) + child: CircularProgressIndicator( + valueColor: loadingSpinnerAnimationController.drive( + ColorTween( + //begin: Colors.blueAccent, + end: Colors.blueAccent, + ), + ), + ), + ) : controller.error.value - ? ListView( - children: const [ - Padding( - padding: EdgeInsets.all(150), - child: Text("Unexpected Error Try Again.."), - ), - ], - ) - : controller.homePosts.isEmpty - ? LoadingReddit() - //const Text( "No Posts to show") - : PostList( - userName: '${controller.myProfile!.userName}', - updateData: updateData, - data: controller.homePosts, - ), - - // : ListView.builder( - // controller: controller.myScroll, - // itemCount: controller.homePosts.length, - // itemBuilder: ( - // final BuildContext ctx, - // final int index, - // ) { - // return Post.home( - // data: controller.homePosts[index], - // ); - // }, - // ), + ? ListView( + children: const [ + Padding( + padding: EdgeInsets.all(150), + child: Text("Unexpected Error Try Again.."), + ), + ], + ) + : controller.homePosts.isEmpty + ? LoadingReddit() + //const Text( "No Posts to show") + : PostList( + userName: '${controller.myProfile!.userName}', + updateData: () + { + controller.pageNumber.value++; + controller.getPosts(sort: controller.sortHomePostsBy.value,p: controller.pageNumber.value); + }, + data: controller.homePosts, + ), ), ), ); diff --git a/lib/home/screens/saved.dart b/lib/home/screens/saved.dart index 79a7b4ae..45ce4179 100644 --- a/lib/home/screens/saved.dart +++ b/lib/home/screens/saved.dart @@ -1,67 +1,71 @@ +import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:post/home/screens/saved_comments.dart'; import 'package:post/home/screens/saved_posts.dart'; + class Saved extends StatelessWidget { const Saved({Key? key}) : super(key: key); @override Widget build(BuildContext context) { - late TabController controller; - return DefaultTabController( - length: 2, - child: Scaffold( - appBar: PreferredSize( - preferredSize: const Size.fromHeight(100), - child: NestedScrollView( - headerSliverBuilder: - (BuildContext context, bool innerBoxIsScrolled) => [ - SliverAppBar( - backgroundColor: Colors.white, - titleSpacing: 0, - elevation: 2, - titleTextStyle: const TextStyle( - color: Colors.black, - fontWeight: FontWeight.w500, - fontSize: 18), - shadowColor: Colors.white, - title: const Text('Saved'), - bottom: PreferredSize( - preferredSize: const Size.fromHeight(40), - child: TabBar( - indicatorWeight: 3, - indicatorColor: Colors.blue, - indicatorSize: TabBarIndicatorSize.tab, - labelPadding: const EdgeInsets.symmetric(horizontal: 60), - isScrollable: true, - labelStyle: const TextStyle( - fontWeight: FontWeight.w700, fontSize: 14), - labelColor: Colors.black, - unselectedLabelColor: Colors.grey, - tabs: [ - Text( - 'Posts', - style: TextStyle(fontSize: 15), - ), - const Padding( - padding: EdgeInsets.only(bottom: 6), - child: Text( - 'Comments', - ), + late TabController controller; + return + DefaultTabController( + length: 2, + child: Scaffold( + appBar: PreferredSize( + preferredSize: const Size.fromHeight(100), + child: NestedScrollView( + headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) =>[ + SliverAppBar( + backgroundColor: Colors.white, + titleSpacing: 0, + elevation: 2, + titleTextStyle: const TextStyle( + color: Colors.black, + fontWeight: FontWeight.w500, + fontSize: 18), + shadowColor: Colors.white, + title: const Text('Saved'), + bottom: PreferredSize( + preferredSize: const Size.fromHeight(40), + child: TabBar( + indicatorWeight: 3, + indicatorColor: Colors.blue, + indicatorSize: TabBarIndicatorSize.tab, + labelPadding: const EdgeInsets.symmetric(horizontal: 60), + isScrollable: true, + labelStyle: const TextStyle( + fontWeight: FontWeight.w700, fontSize: 14), + labelColor: Colors.black, + unselectedLabelColor: Colors.grey, + tabs: [ + Text( + 'Posts', + style: TextStyle(fontSize: 15), + ), + const Padding( + padding: EdgeInsets.only(bottom: 6), + child: Text( + 'Comments', + ), + ), + ], ), - ], - ), + ), ), - ), - ], - body: SizedBox(), + ], + body: SizedBox(), + ), ), + + body: TabBarView( + children: [ + SavedPosts(), + SavedCommentView(), + ]), ), - body: TabBarView(children: [ - SavedPosts(), - SavedCommentView(), - ]), - ), - ); + ); } } diff --git a/lib/home/screens/saved_comments.dart b/lib/home/screens/saved_comments.dart index adcd89bf..25f1981c 100644 --- a/lib/home/screens/saved_comments.dart +++ b/lib/home/screens/saved_comments.dart @@ -1,6 +1,13 @@ +import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:post/comments/widgets/comment.dart'; +import 'package:responsive_sizer/responsive_sizer.dart'; + + +import '../../networks/const_endpoint_data.dart'; +import '../../post/widgets/post.dart'; +import '../../post/widgets/post_list.dart'; import '../../widgets/loading_reddit.dart'; import '../controller/home_controller.dart'; @@ -11,75 +18,75 @@ class SavedCommentView extends StatefulWidget { State createState() => _SavedCommentViewState(); } -class _SavedCommentViewState extends State - with TickerProviderStateMixin { +class _SavedCommentViewState extends State with TickerProviderStateMixin{ final HomeController controller = Get.put(HomeController()); late AnimationController loadingSpinnerAnimationController; @override - void initState() { + void initState() + { super.initState(); loadingSpinnerAnimationController = AnimationController(duration: const Duration(seconds: 2), vsync: this); loadingSpinnerAnimationController.repeat(); if (controller.userSavedComments.isEmpty) { - controller.getSavedComments(); + controller.getSavedComments(p: controller.pageNumberComment.value, ); } } - void dispose() { loadingSpinnerAnimationController.dispose(); super.dispose(); } - @override Widget build(BuildContext context) { - return Obx( - () => Scaffold( - body: RefreshIndicator( - backgroundColor: Colors.white, - color: Colors.blue[900], - onRefresh: () async { - controller.userSavedComments.clear(); - controller.pageNumberComment.value = 1; - controller.pageNumberComment.update((val) {}); - controller.getSavedComments(); - controller.update(); - //Get.forceAppUpdate(); - }, - child: controller.isLoadingCommetns.value - ? Center( - child: CircularProgressIndicator( - valueColor: loadingSpinnerAnimationController.drive( - ColorTween( - //begin: Colors.blueAccent, - end: Colors.blueAccent, - ), + return Obx(()=> + Scaffold( + body: + RefreshIndicator( + backgroundColor: Colors.white, + color: Colors.blue[900], + onRefresh: () async { + controller.userSavedComments.clear(); + controller.pageNumberComment.value = 1; + controller.pageNumberComment.update((val) {}); + controller.getSavedComments(p: controller.pageNumberComment.value, ); + controller.update(); + //Get.forceAppUpdate(); + }, + child: controller.isLoadingCommetns.value + ? Center( + child: CircularProgressIndicator( + valueColor: loadingSpinnerAnimationController.drive( + ColorTween( + //begin: Colors.blueAccent, + end: Colors.blueAccent, ), ), - ) - : controller.errorComment.value + ), + ) + : controller.errorComment.value ? ListView( - children: const [ - Padding( - padding: EdgeInsets.all(150), - child: Text("Unexpected Error Try Again.."), - ), - ], - ) + children: const [ + Padding( + padding: EdgeInsets.all(150), + child: Text("Unexpected Error Try Again.."), + ), + ], + ) : controller.userSavedComments.isEmpty - ? LoadingReddit() - : ListView.builder( - itemCount: controller.userSavedComments.length, - itemBuilder: ( - final BuildContext ctx, - final int index, - ) { - return Comment( - data: controller.userSavedComments[index], - userName: '${controller.myProfile!.userName}'); - }, - ), - )), + ? LoadingReddit() + + : ListView.builder( + itemCount: controller.userSavedComments.length, + itemBuilder: ( + final BuildContext ctx, + final int index, + ) { + return Comment(data:controller.userSavedComments[index], userName: '${controller.myProfile!.userName}' + ); + }, + ), + ) + ), ); } } diff --git a/lib/home/screens/saved_posts.dart b/lib/home/screens/saved_posts.dart index eca4e9d1..0b7722cf 100644 --- a/lib/home/screens/saved_posts.dart +++ b/lib/home/screens/saved_posts.dart @@ -1,82 +1,87 @@ +import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; +import 'package:responsive_sizer/responsive_sizer.dart'; + + +import '../../post/widgets/post.dart'; import '../../post/widgets/post_list.dart'; import '../../widgets/loading_reddit.dart'; import '../controller/home_controller.dart'; class SavedPosts extends StatefulWidget { - SavedPosts({Key? key}) : super(key: key); + SavedPosts({Key? key}) : super(key: key); @override State createState() => _SavedPostsState(); } -class _SavedPostsState extends State with TickerProviderStateMixin { +class _SavedPostsState extends State with TickerProviderStateMixin{ final HomeController controller = Get.put(HomeController()); late AnimationController loadingSpinnerAnimationController; @override - void initState() { + void initState() + { super.initState(); loadingSpinnerAnimationController = AnimationController(duration: const Duration(seconds: 2), vsync: this); loadingSpinnerAnimationController.repeat(); if (controller.userSavedPosts.isEmpty) { - controller.getSavedPosts(); + controller.getSavedPosts(p: controller.pageNumberSaved.value, ); } } - void dispose() { loadingSpinnerAnimationController.dispose(); super.dispose(); } - @override Widget build(BuildContext context) { - return Obx( - () => Scaffold( - body: RefreshIndicator( - backgroundColor: Colors.white, - color: Colors.blue[900], - onRefresh: () async { - controller.userSavedPosts.clear(); - controller.pageNumberSaved.value = 1; - controller.pageNumberSaved.update((val) {}); - controller.getSavedPosts(); - controller.update(); - //Get.forceAppUpdate(); - }, - child: controller.isLoadingSaved.value - ? Center( - child: CircularProgressIndicator( - valueColor: loadingSpinnerAnimationController.drive( - ColorTween( - //begin: Colors.blueAccent, - end: Colors.blueAccent, - ), - ), - ), - ) - : controller.errorSaved.value - ? ListView( - children: const [ - Padding( - padding: EdgeInsets.all(150), - child: Text("Unexpected Error Try Again.."), - ), - ], - ) - : controller.userSavedPosts.isEmpty - ? LoadingReddit() - : PostList( - userName: '${controller.myProfile!.userName}', - updateData: () { - controller.pageNumberSaved.value++; - controller.pageNumberSaved.update((val) {}); - controller.getSavedPosts(); - }, - data: controller.userSavedPosts, - ), - ), + return Obx(()=> + Scaffold( + body: + RefreshIndicator( + backgroundColor: Colors.white, + color: Colors.blue[900], + onRefresh: () async { + controller.userSavedPosts.clear(); + controller.pageNumberSaved.value = 1; + controller.pageNumberSaved.update((val) {}); + controller.getSavedPosts(p: controller.pageNumberSaved.value,); + controller.update(); + //Get.forceAppUpdate(); + }, + child: controller.isLoadingSaved.value + ? Center( + child: CircularProgressIndicator( + valueColor: loadingSpinnerAnimationController.drive( + ColorTween( + //begin: Colors.blueAccent, + end: Colors.blueAccent, + ), + ), + ), + ) + : controller.errorSaved.value + ? ListView( + children: const [ + Padding( + padding: EdgeInsets.all(150), + child: Text("Unexpected Error Try Again.."), + ), + ], + ) + : controller.userSavedPosts.isEmpty + ? LoadingReddit() + : + PostList( + userName: '${controller.myProfile!.userName}', updateData: (){ + controller.pageNumberSaved.value++; + controller.pageNumberSaved.update((val) { }); + controller.getSavedPosts(p: controller.pageNumberSaved.value,); + }, data: controller.userSavedPosts, + ), + ) , + ), ); } diff --git a/lib/home/widgets/buttom_nav_bar.dart b/lib/home/widgets/buttom_nav_bar.dart index 8ccf952b..3bf4908d 100644 --- a/lib/home/widgets/buttom_nav_bar.dart +++ b/lib/home/widgets/buttom_nav_bar.dart @@ -8,7 +8,6 @@ import '../../notification/screens/notifications_screen.dart'; import '../../icons/icon_broken.dart'; import '../controller/home_controller.dart'; import '../screens/home_layout.dart'; -import '../../discover/screens/discover_screen.dart'; class buttomNavBar extends StatefulWidget { const buttomNavBar( @@ -50,13 +49,13 @@ class _buttomNavBarState extends State { // duration: Duration(seconds: 2), // curve: Curves.easeOut); // } else { - Get.to(HomeLayoutScreen()); + Get.to(HomeLayoutScreen()); //} } break; case 1: { - Get.to(DiscoverScreen()); + Get.to(HomeLayoutScreen()); } break; case 2: diff --git a/lib/home/widgets/custom_upper_bar.dart b/lib/home/widgets/custom_upper_bar.dart new file mode 100644 index 00000000..86b246cb --- /dev/null +++ b/lib/home/widgets/custom_upper_bar.dart @@ -0,0 +1,443 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:post/create_community/screens/create_community.dart'; +import 'package:post/createpost/screens/createpost.dart'; +import 'package:post/logins/screens/login.dart'; +import 'package:post/notification/screens/notifications_screen.dart'; +import 'package:post/settings/screens/settings.dart'; +import '../../logins/providers/authentication.dart'; +import '../../createpost/controllers/posts_controllers.dart'; +import '../../icons/icon_broken.dart'; +import '../../logins/providers/authentication.dart'; +import '../../myprofile/screens/myprofile_screen.dart'; +import '../../search/screens/search.dart'; +import '../controller/home_controller.dart'; +import '../screens/all.dart'; +import '../screens/home_layout.dart'; +import 'community_container.dart'; + +class UpBar extends StatelessWidget { + final HomeController controller; + final PostController controllerForCreatePost; + UpBar({ + Key? key, + required this.controller, + required this.controllerForCreatePost, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return AppBar( + backgroundColor: Colors.white, + elevation: 0, + leading: GestureDetector( + child: Padding( + padding: const EdgeInsetsDirectional.only(start: 20.0), + child: Image.asset('assets/images/redditlogo.png'), + )), + title: Row( + children: [ + const Text( + 'Reddit', + style: TextStyle(fontSize: 20.0, fontWeight: FontWeight.bold), + ), + const SizedBox( + width: 30, + ), + Container( + width: 220, + child: PopupMenuButton( + child: Row( + children: [ + Icon(Icons.home_filled), + SizedBox( + width: 20, + ), + Text("Home"), + SizedBox( + width: 80, + ), + Icon(Icons.arrow_drop_down_sharp) + ], + ), + elevation: 0, + color: Colors.white, + offset: Offset(0, 43), + itemBuilder: (context) => [ + PopupMenuItem( + value: 1, + child: Container( + width: 220, + child: Text( + "MODERATING", + style: TextStyle(color: Colors.grey, fontSize: 10), + )), + ), + PopupMenuItem( + value: 2, + child: Container( + width: 220, + child: Column( + children: List.generate( + controllerForCreatePost + .moderatedSubreddits.length, + (index) => CommunityContainer( + nameOfSubreddit: controllerForCreatePost + .moderatedSubreddits[index] + .subredditName!, + iconOfSubreddit: (controllerForCreatePost + .moderatedSubreddits[index] + .icon != + null) + ? controllerForCreatePost + .moderatedSubreddits[index].icon! + : '')), + )), + ), + PopupMenuItem( + value: 3, + child: Container( + width: 220, + child: Text( + "YOUR COMMUNITIES", + style: TextStyle(color: Colors.grey, fontSize: 10), + )), + ), + PopupMenuItem( + value: 4, + child: ListTile( + onTap: () { + Get.to(CreateCommunity()); + }, + horizontalTitleGap: 0, + leading: Icon( + Icons.add, + color: Colors.grey, + ), + title: Text( + "Create Community", + style: TextStyle(color: Colors.grey), + ), + ), + ), + PopupMenuItem( + value: 5, + child: Container( + width: 220, + child: Column( + children: List.generate( + controllerForCreatePost + .subscribedSubreddits.length, + (index) => CommunityContainer( + nameOfSubreddit: controllerForCreatePost + .subscribedSubreddits[index] + .subredditName!, + iconOfSubreddit: (controllerForCreatePost + .subscribedSubreddits[index] + .icon != + null) + ? controllerForCreatePost + .subscribedSubreddits[index].icon! + : '')), + )), + ), + PopupMenuItem( + value: 6, + child: Container( + width: 220, + child: Text( + "FEEDS", + style: TextStyle(color: Colors.grey, fontSize: 10), + )), + ), + PopupMenuItem( + value: 7, + child: Column( + children: [ + ListTile( + onTap: () { + Get.to(HomeLayoutScreen()); + }, + horizontalTitleGap: 0, + leading: Icon(Icons.home_filled), + title: Text("Home"), + ), + ListTile( + onTap: () { + Get.to(All()); + }, + horizontalTitleGap: 0, + leading: Icon(Icons.stacked_bar_chart), + title: Text("All"), + ) + ], + )), + ], + )), + Expanded( + child: Container( + width: 350.0, + height: 35.0, + child: TextFormField( + onTap: () { + Get.to(Search()); + }, + enabled: false, + decoration: const InputDecoration( + border: OutlineInputBorder( + borderRadius: BorderRadius.all(Radius.circular(20))), + labelText: 'Search Reddit', + prefixIcon: Icon(Icons.search), + labelStyle: TextStyle( + color: Colors.grey, + fontSize: 18.0, + ), + ), + ), + ), + ), + const SizedBox( + width: 20, + ), + IconButton( + onPressed: () { + Get.to(All()); + }, + icon: const Icon( + Icons.arrow_circle_up, + size: 25, + )), + const SizedBox( + width: 20, + ), + IconButton( + onPressed: () { + Get.to(NotificationScreen()); + }, + icon: const Icon( + IconBroken.Notification, + size: 25, + )), + const SizedBox( + width: 20, + ), + IconButton( + onPressed: () { + Get.to(CreatePostSCreen(), arguments: [0, 0, 0]); + }, + icon: const Icon( + Icons.add_sharp, + size: 25, + )), + const SizedBox( + width: 20, + ), + ], + ), + actions: [ + Container( + padding: EdgeInsetsDirectional.only(end: 20, start: 10), + child: PopupMenuButton( + child: Container( + decoration: BoxDecoration( + border: Border.all(color: Colors.black54, width: 0.25)), + width: 270, + height: 20, + child: Padding( + padding: const EdgeInsetsDirectional.only(start: 10.0), + child: Row( + children: [ + Stack( + alignment: AlignmentDirectional.bottomEnd, + children: const [ + CircleAvatar( + backgroundImage: + AssetImage("assets/images/reddit.gif"), + radius: 18.0, + ), + CircleAvatar( + backgroundColor: Colors.white, + radius: 6, + ), + Padding( + padding: + EdgeInsetsDirectional.only(end: 2, bottom: 2), + child: CircleAvatar( + backgroundColor: Colors.green, + radius: 4, + ), + ) + ], + ), + const SizedBox( + width: 10, + ), + Padding( + padding: const EdgeInsetsDirectional.only(top: 10.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: const [ + Text("Ahmed"), + Text("karma"), + ], + ), + ), + const SizedBox( + width: 100, + ), + const Icon(IconBroken.Arrow___Down_2) + ], + ), + ), + ), + itemBuilder: (context) => [ + PopupMenuItem( + value: 1, + child: Container( + padding: EdgeInsetsDirectional.only(end: 80), + height: 40, + width: 220, + child: ListTile( + horizontalTitleGap: 0, + leading: Icon( + IconBroken.Profile, + color: Colors.grey, + ), + title: Text( + "My Stuff", + style: TextStyle(color: Colors.grey), + ), + ), + ), + ), + PopupMenuItem( + value: 2, + child: Container( + padding: EdgeInsetsDirectional.only(start: 30, bottom: 10), + height: 30, + width: 220, + child: ListTile( + onTap: () { + Navigator.of(context).pushNamed(MyProfileScreen.routeName, + arguments: controller.myProfile!.userName); + // Get.to(MyProfileScreen, + // arguments: controller.myProfile!.userName); + }, + title: Text( + "Profile", + style: TextStyle( + color: Colors.black, + fontSize: 16, + fontWeight: FontWeight.bold), + ), + ), + ), + ), + PopupMenuItem( + value: 3, + child: Container( + padding: EdgeInsetsDirectional.only(start: 30, bottom: 10), + height: 30, + width: 220, + child: ListTile( + onTap: () { + Get.to(Settings()); + }, + title: Text( + "User settings", + style: TextStyle( + color: Colors.black, + fontSize: 16, + fontWeight: FontWeight.bold), + ), + ), + ), + ), + PopupMenuItem(child: Divider()), + PopupMenuItem( + value: 4, + child: Container( + padding: EdgeInsetsDirectional.only(end: 0), + height: 40, + width: 220, + child: ListTile( + onTap: () { + Get.to(CreateCommunity()); + }, + horizontalTitleGap: 0, + leading: Padding( + padding: const EdgeInsetsDirectional.only(top: 12.0), + child: Icon( + Icons.r_mobiledata_outlined, + color: Colors.black, + size: 30, + ), + ), + title: Text( + "Create a Community", + style: TextStyle( + color: Colors.black, + fontSize: 16, + fontWeight: FontWeight.bold), + ), + ), + ), + ), + PopupMenuItem( + value: 5, + child: Container( + padding: EdgeInsetsDirectional.only(start: 40), + height: 40, + width: 220, + child: ListTile( + horizontalTitleGap: 0, + title: Text( + "Privacy Policy", + style: TextStyle( + color: Colors.black, + fontWeight: FontWeight.bold, + fontSize: 16), + ), + ), + ), + ), + PopupMenuItem(child: Divider()), + PopupMenuItem( + value: 6, + child: Container( + padding: EdgeInsetsDirectional.only(start: 10, bottom: 1), + height: 30, + width: 220, + child: ListTile( + onTap: () { + Auth().logOut(context); + Navigator.of(context).pushNamed(Login.routeName); + }, + horizontalTitleGap: 0, + leading: Icon( + Icons.logout, + size: 25, + color: Colors.black, + ), + title: Text( + "Log out", + style: TextStyle( + color: Colors.black, + fontSize: 16, + fontWeight: FontWeight.bold), + ), + ), + ), + ), + PopupMenuItem(child: Divider()), + ], + offset: Offset(0, 59), + color: Colors.white, + elevation: 0, + ), + ), + ], + ); + } +} diff --git a/lib/home/widgets/end_drawer.dart b/lib/home/widgets/end_drawer.dart index b44eea49..b7228032 100644 --- a/lib/home/widgets/end_drawer.dart +++ b/lib/home/widgets/end_drawer.dart @@ -4,7 +4,6 @@ import 'package:get/get.dart'; import 'package:get/get_core/src/get_main.dart'; import 'package:post/home/controller/home_controller.dart'; import 'package:post/home/screens/saved.dart'; -import 'package:post/logins/screens/login.dart'; import 'package:responsive_sizer/responsive_sizer.dart'; import '../../home/screens/history.dart'; import '../../create_community/screens/create_community.dart'; @@ -12,10 +11,11 @@ import '../../icons/icon_broken.dart'; import '../../myprofile/screens/myprofile_screen.dart'; import '../../settings/screens/settings.dart'; import 'package:shared_preferences/shared_preferences.dart'; -import '../../logins/providers/authentication.dart'; - class endDrawer extends StatelessWidget { - endDrawer({required this.controller, Key? key}) : super(key: key); + + endDrawer({ + required this.controller + ,Key? key}) : super(key: key); final HomeController controller; bool isOnline = true; @@ -84,17 +84,17 @@ class endDrawer extends StatelessWidget { icon: CircleAvatar( radius: 4, backgroundColor: - isOnline ? Colors.green : Colors.grey[200], + isOnline ? Colors.green : Colors.grey[200], ), style: ButtonStyle( elevation: MaterialStateProperty.all(0), backgroundColor: - MaterialStateProperty.all(Colors.grey[200]), + MaterialStateProperty.all(Colors.grey[200]), shape: MaterialStateProperty.all< - RoundedRectangleBorder>( + RoundedRectangleBorder>( RoundedRectangleBorder( borderRadius: - BorderRadius.circular(20.0), + BorderRadius.circular(20.0), side: BorderSide( color: isOnline ? Colors.green @@ -103,7 +103,7 @@ class endDrawer extends StatelessWidget { "Online Status: " + "${isOnline ? "On" : "Off"}", style: TextStyle( color: - isOnline ? Colors.green : Colors.black54), + isOnline ? Colors.green : Colors.black54), ), ), ), @@ -122,172 +122,121 @@ class endDrawer extends StatelessWidget { child: Row( mainAxisAlignment: MainAxisAlignment.start, children: [ - GestureDetector( - onTap: () { - showDialog( - context: context, - builder: (ctx) => AlertDialog( - content: Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(20), - ), - width: - MediaQuery.of(context).size.width / 1, - height: - MediaQuery.of(context).size.height / 2, - child: (Column( - mainAxisAlignment: - MainAxisAlignment.center, - crossAxisAlignment: - CrossAxisAlignment.center, - children: [ - SizedBox( - height: 30, - ), - CircleAvatar( - radius: 70, - backgroundImage: NetworkImage( - "${controller.myProfile!.profilePicture}"), - ), - SizedBox( - height: 130, - ), - Padding( - padding: - const EdgeInsetsDirectional.only( - end: 30), - child: Text( - 'u/${controller.myProfile!.displayName}', - style: TextStyle( - fontSize: 17, - fontWeight: FontWeight.w500), - ), - ), - SizedBox( - height: 10, - ), - Expanded( - child: Row( - children: [ - Expanded( - child: Column( - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - Text( - "${controller.myProfile!.postKarma}"), - Text( - "Post Karma", - style: TextStyle( - color: - Colors.grey[400], - fontSize: 14), - ), - ], - ), - ), - SizedBox( - width: 20, - ), - Column( - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - Text( - "${controller.myProfile!.commentkarma}"), - Text( - "Comment Karma", - style: TextStyle( - color: Colors.grey[400], - fontSize: 14), - ), - ], - ), - ], - ), - ), - SizedBox( - height: 10, - ), - Padding( - padding: - const EdgeInsetsDirectional.only( - end: 10), - child: ListTile( - onTap: () { - Navigator.of(context).pushNamed( - MyProfileScreen.routeName, - arguments: userName); - }, - leading: Icon( - Icons.account_circle, - color: Colors.black, - ), - title: Text( - "View profile", - style: TextStyle( - color: Colors.black, - fontSize: 16, - fontWeight: FontWeight.w600), - ), - horizontalTitleGap: 0, - ), - ) - ], - )))), - ); - }, - child: Row( - children: [ - Icon( - Icons.ac_unit_outlined, - color: Colors.blue[700], - ), - SizedBox( - width: 5, - ), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - "${controller.myProfile!.postKarma}", - style: TextStyle( - color: Colors.black, - fontWeight: FontWeight.bold), - ), - Text( - "karma", - style: TextStyle(color: Colors.grey), - ) - ], - ) - ], - )), - SizedBox( - width: 10, - ), - SizedBox( - width: 10, - ), - GestureDetector( - child: Row( + GestureDetector( + onTap: (){ + showDialog( + context: context, + builder: (ctx) => AlertDialog( + content: Container( + decoration: BoxDecoration( + borderRadius:BorderRadius.circular(20), + ), + width: MediaQuery.of(context).size.width/1, + height: MediaQuery.of(context).size.height/2, + child: ( + Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + SizedBox(height: 30,), + CircleAvatar( + radius:70, + backgroundImage: NetworkImage("${controller.myProfile!.profilePicture}"), + ), + SizedBox(height: 130,), + Padding( + padding: const EdgeInsetsDirectional.only(end: 30), + child: Text( + 'u/${controller.myProfile!.displayName}', + style: TextStyle(fontSize: 17, fontWeight: FontWeight.w500), + ), + ), + SizedBox(height: 10,), + Expanded( + child: Row( + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("${controller.myProfile!.postKarma}"), + Text("Post Karma", + style: TextStyle( + color: Colors.grey[400], + fontSize: 14 + ), + ), + ], + ), + ), + SizedBox(width:20,), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("${controller.myProfile!.commentkarma}"), + Text("Comment Karma", + style: TextStyle( + color: Colors.grey[400], + fontSize: 14 + ), + ), + ], + ), + ], + ), + ), + SizedBox(height: 10,), + Padding( + padding: const EdgeInsetsDirectional.only(end: 10), + child: ListTile( + onTap: () + { + Navigator.of(context).pushNamed(MyProfileScreen.routeName, + arguments: userName); + }, + leading: Icon(Icons.account_circle,color: Colors.black,), + title: Text("View profile",style: TextStyle(color: Colors.black,fontSize: 16,fontWeight: FontWeight.w600),), + horizontalTitleGap: 0, + ), + ) + ], + ) + ) + ) + ), + ); + }, + child: Row( + children: [ + Icon(Icons.ac_unit_outlined,color: Colors.blue[700],), + SizedBox(width: 5,), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("${controller.myProfile!.postKarma}",style: TextStyle(color: Colors.black,fontWeight: FontWeight.bold),), + Text("karma", + style: TextStyle( + color: Colors.grey + ), + ) + ], + ) + ], + )), + SizedBox(width: 10,), + + SizedBox(width: 10,), + GestureDetector(child: Row( children: [ - Icon( - Icons.text_snippet_rounded, - color: Colors.blue[700], - ), + Icon(Icons.text_snippet_rounded,color: Colors.blue[700],), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text( - "1m 2d", + Text("1m 2d",style: TextStyle(color: Colors.black,fontWeight: FontWeight.bold),), + Text("Reddit ago", style: TextStyle( - color: Colors.black, - fontWeight: FontWeight.bold), - ), - Text( - "Reddit ago", - style: TextStyle(color: Colors.grey), + color: Colors.grey + ), ) ], ) @@ -321,6 +270,7 @@ class endDrawer extends StatelessWidget { onTap: () { Navigator.of(context).pushNamed(CreateCommunity.routeName); // Navigator.pop(context); + }, ), ListTile( @@ -331,7 +281,7 @@ class endDrawer extends StatelessWidget { style: TextStyle(fontSize: 14, fontWeight: FontWeight.w600), ), onTap: () { - Get.to(Saved()); + Get.to(Saved()); }, ), ListTile( @@ -342,24 +292,13 @@ class endDrawer extends StatelessWidget { style: TextStyle(fontSize: 14, fontWeight: FontWeight.w600), ), onTap: () { - Get.to(History()); + Get.to(History()); }, ), SizedBox( height: 20.h, ), - ListTile( - horizontalTitleGap: 3, - leading: Icon(IconBroken.Setting), - title: Text( - 'Log out', - style: TextStyle(fontSize: 14, fontWeight: FontWeight.w600), - ), - onTap: () { - Auth().logOut(context); - Navigator.of(context).pushNamed(Login.routeName); - }, - ), + ListTile( horizontalTitleGap: 3, leading: Icon(IconBroken.Setting), @@ -377,4 +316,4 @@ class endDrawer extends StatelessWidget { ), ); } -} +} \ No newline at end of file diff --git a/lib/home/widgets/new_drawer.dart b/lib/home/widgets/new_drawer.dart index f1667eab..3eff29a3 100644 --- a/lib/home/widgets/new_drawer.dart +++ b/lib/home/widgets/new_drawer.dart @@ -245,4 +245,4 @@ class MyDrawer extends StatelessWidget { ), ); } -} +} \ No newline at end of file diff --git a/lib/logins/providers/authentication.dart b/lib/logins/providers/authentication.dart index 63f98bb4..47eaba0a 100644 --- a/lib/logins/providers/authentication.dart +++ b/lib/logins/providers/authentication.dart @@ -80,14 +80,33 @@ Future setupFlutterNotifications() async { } class Auth with ChangeNotifier { + ///the base URL of the backend server final url = dotenv.env['API'] as String; + + /// Whether ther is error in the calling or not bool error = false; + + ///the error message when there is an error String errorMessage = ''; + + /// the token saved in login and sign up String token = ''; + + /// the expire time of the token DateTime expiresIn = DateTime.now(); + + /// Whether the user is already aluthed bool alreadyAuthed = false; + ///check if the user is already Auth or not + /// + ///look in the shared prefrence and check + ///if ther is token already saved or not + ///if the token saved then check on its expire date + ///if this isnot expired then enter the user Future alreadyAuth() async { + //Input : none + //Output: return whether the user Auth or not final prefs = await SharedPreferences.getInstance(); if (prefs.getString('token') != null && prefs.getString('expiresIn') != null) { @@ -104,10 +123,17 @@ class Auth with ChangeNotifier { } } return alreadyAuthed; - // alreadyAuthed = prefs.getString('token') != null; } + ///log out the user and unAuth him + /// + ///delete the token and its expire date from the prfrence + ///then go to the login Future logOut(context) async { + ///Input : + /// the Build context of the calling screen\ + ///Output :none + try { final prefs = await SharedPreferences.getInstance(); await prefs.remove('token'); @@ -129,7 +155,15 @@ class Auth with ChangeNotifier { } } + ///sig up the user and Authenticate him + /// + ///take the data from sign up screen and send it by API to the server + ///if sign uo done successfuly , then save the token and its expire date + ///and prepare the shered prefrence by puting some intiall data to it Future sinUp(Map query) async { + ///Input : + /// query: the data of the user we want to sign him up + ///Output :none try { final http.Response response = await http.post( Uri.parse(url + '/users/signup'), @@ -155,20 +189,28 @@ class Auth with ChangeNotifier { appId: Constants.appId, messagingSenderId: Constants.messagingSenderId, projectId: Constants.projectId)); + //Get token from fire base and send it to backend final notificationToken = prefs.get('notificationToken'); await NotificationToken.sendTokenToDatabase(notificationToken); - // FirebaseMessaging.onBackgroundMessage( - // _firebaseMessagingBackgroundHandler); preparePrefs(); } notifyListeners(); } catch (error) { print('error: $error'); } - print(token); } + ///log in the user and Authenticate him + /// + ///take the data from log in screen and send it by API to the server + ///if sign uo done successfuly , then save the token and its expire date + ///and prepare the shered prefrence by puting some intiall data to it + Future login(Map query) async { + ///Input : + /// query: the data of the user we want to sign him up + ///Output :none + try { final http.Response response = await http.post( Uri.parse(url + '/users/login'), @@ -177,7 +219,6 @@ class Auth with ChangeNotifier { 'Content-type': 'application/json', 'Accept': 'application/json', }); - print(response); error = response.statusCode != 200; if (error) { errorMessage = json.decode(response.body)['errorMessage']; @@ -196,27 +237,39 @@ class Auth with ChangeNotifier { appId: Constants.appId, messagingSenderId: Constants.messagingSenderId, projectId: Constants.projectId)); - final notificationToken = prefs.get('notificationToken'); - // await NotificationToken.getTokenOfNotification(); + //Get token from fire base and send it to backend + await NotificationToken.getTokenOfNotification(); + final notificationToken = prefs.get('notificationToken'); await NotificationToken.sendTokenToDatabase(notificationToken); - // await NotificationToken.refreshToken(); - // FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler); preparePrefs(); } notifyListeners(); } catch (error) { print('error: $error'); } - print(token); } + ///prepare the prefrense by set default values to it + /// + ///function used when Authenticate the user + ///to put default value for some data like(sort home posts - isAuth) Future preparePrefs() async { + ///Input : none + ///Output : none final prefs = await SharedPreferences.getInstance(); await prefs.setString('sortHomePosts', 'Best'); await prefs.setBool('isAuth', true); } + ///check Whether the user name is avaliable or not + /// + ///take the user name and send it by API to the server + ///retrn ture if the user name is not used and false otherwise + Future availableUserName(userName) async { + ///Input: + /// userName :- the user name of our user + /// Output: return whether the name is avaliable or not http.Response? response = null; try { response = await http.get( @@ -228,17 +281,23 @@ class Auth with ChangeNotifier { } catch (error) { print('error: $error'); } - // print(response!.body); return jsonDecode(response!.body)['available']; } + ///change gender of the user + /// + ///take the gender and send it by API to the server + ///used in the sign up or in settings when change the gneder + Future changeGender(String Gender) async { + ///Input : + /// gender: the new gender of the user + /// output: return Whether the change gender done successfully or not http.Response? response = null; try { final prefs = await SharedPreferences.getInstance(); token = prefs.getString('token') as String; - print(token); /// change geneder need cookies response = await http.patch(Uri.parse(url + '/users/me/prefs'), @@ -256,7 +315,6 @@ class Auth with ChangeNotifier { try { final prefs = await SharedPreferences.getInstance(); token = prefs.getString('token') as String; - print(token); /// change geneder need cookies response = await http.get(Uri.parse(url + '/users/me/prefs'), headers: { @@ -271,7 +329,14 @@ class Auth with ChangeNotifier { return response!.statusCode == 200; } + ///change the userName of the user + /// + ///take the new user name and send it by API to the server + Future forgetUserName(Map query) async { + ///Input: + /// query:- map that contain the the data that will be send to the server + /// outPut: none try { final http.Response response = await http.post( Uri.parse(url + '/users/forgot_username'), @@ -290,7 +355,15 @@ class Auth with ChangeNotifier { } } + ///change the Password of the user + /// + ///take the new password and send it by API to the server + Future forgetPassword(Map query) async { + ///Input: + /// query:- map that contain the the data that will be send to the server + /// outPut: none + try { final http.Response response = await http.post( Uri.parse(url + '/users/forgot_username'), @@ -309,7 +382,12 @@ class Auth with ChangeNotifier { } } + ///Authenticate the user by google API Future GoogleAuth(Map query) async { + ///Input: + /// query:- map that contain the the data that will be send to the server + /// outPut: none + try { final http.Response response = await http.post(Uri.parse(url + '/users/google/'), body: { diff --git a/lib/logins/screens/forgot_password.dart b/lib/logins/screens/forgot_password.dart index be4a6ce5..c2338c9f 100644 --- a/lib/logins/screens/forgot_password.dart +++ b/lib/logins/screens/forgot_password.dart @@ -16,12 +16,13 @@ import './login.dart'; import '../widgets/web_image.dart'; class ForgotPassword extends StatefulWidget { + /// the route name of the screen static const routeName = '/ForgotPassword'; - final String url = - 'https://abf8b3a8-af00-46a9-ba71-d2c4eac785ce.mock.pstmn.io'; /// variable to check if the backend finish the actual server of work with the mock final bool isMock = true; + + const ForgotPassword({super.key}); @override State createState() => ForgotPasswordState(); } @@ -57,6 +58,8 @@ class ForgotPasswordState extends State { ///check the changes and detect when the finish flag is true ///and then activate the continue bottom void changeInput() { + //Input: none + //Output: none isFinished = (!inputUserNameController.text.isEmpty) && (!inputEmailController.text.isEmpty) && (validateEmail() == InputStatus.sucess); @@ -69,11 +72,15 @@ class ForgotPasswordState extends State { /// the validator will return original if the field is empty /// otherwise the status will be faild and put an error message InputStatus validateEmail() { - if (inputEmailController.text.isEmpty) + //Input: none + //Output: The status of the Email input field + + if (inputEmailController.text.isEmpty) { return InputStatus.original; - else if (EmailValidator.validate(inputEmailController.text.toLowerCase())) + } else if (EmailValidator.validate( + inputEmailController.text.toLowerCase())) { return InputStatus.sucess; - else { + } else { emailErrorMessage = 'Not a valid email address'; return InputStatus.failed; } @@ -84,6 +91,9 @@ class ForgotPasswordState extends State { /// when user tap in the email textfailed mark it as taped /// when tap out check the validation using [validateEmail()] void controlEmailStatus(hasFocus) { + //Input: Whether the email input field is clicked or not + //Output: none + if (hasFocus) inputEmailStatus = InputStatus.taped; else @@ -96,6 +106,9 @@ class ForgotPasswordState extends State { /// if the server return failed response then there is error message will appare /// other show sucess message void submitForgorPasssword() async { + //Input: none + //Output: none + final provider = Provider.of(context, listen: false); await provider.forgetPassword({ "email": inputEmailController.text, @@ -112,8 +125,6 @@ class ForgotPasswordState extends State { @override Widget build(BuildContext context) { return Scaffold( - //GestureDetector to hide the soft keyboard - //by clicking outside of TextField or anywhere on the screen body: GestureDetector( onTap: () { FocusScope.of(context).requestFocus(new FocusNode()); diff --git a/lib/logins/screens/forgot_username.dart b/lib/logins/screens/forgot_username.dart index dfb6b9d4..f084786b 100644 --- a/lib/logins/screens/forgot_username.dart +++ b/lib/logins/screens/forgot_username.dart @@ -15,7 +15,11 @@ import 'login.dart'; import '../widgets/web_image.dart'; class ForgotUserName extends StatefulWidget { + /// the route name of the screen + static const routeName = '/ForgotUserName'; + + const ForgotUserName({super.key}); @override State createState() => ForgotUserNameState(); } @@ -59,11 +63,15 @@ class ForgotUserNameState extends State { /// the validator will return original if the field is empty /// otherwise the status will be faild and put an error message InputStatus validateEmail() { - if (inputEmailController.text.isEmpty) + //Input : none + //output: return the status of the Email input field + + if (inputEmailController.text.isEmpty) { return InputStatus.original; - else if (EmailValidator.validate(inputEmailController.text.toLowerCase())) + } else if (EmailValidator.validate( + inputEmailController.text.toLowerCase())) { return InputStatus.sucess; - else { + } else { emailErrorMessage = 'Not a valid email address'; return InputStatus.failed; } @@ -74,6 +82,10 @@ class ForgotUserNameState extends State { /// when user tap in the email textfailed mark it as taped /// when tap out check the validation using [validateEmail()] void controlEmailStatus(hasFocus) { + //Input : + // hasFocuns : Whether the Email input field is clicked or not + //output: none + if (hasFocus) inputEmailStatus = InputStatus.taped; else @@ -86,6 +98,9 @@ class ForgotUserNameState extends State { /// if the server return failed response then there is error message will appare /// other show sucess message void submitForgorUserName() async { + //Input : none + //output: none + final provider = Provider.of(context, listen: false); await provider.forgetUserName({ "email": inputEmailController.text, @@ -101,8 +116,6 @@ class ForgotUserNameState extends State { @override Widget build(BuildContext context) { return Scaffold( - //GestureDetector to hide the soft keyboard - //by clicking outside of TextField or anywhere on the screen body: GestureDetector( onTap: () { FocusScope.of(context).requestFocus(new FocusNode()); diff --git a/lib/logins/screens/gender.dart b/lib/logins/screens/gender.dart index 23d623cb..d080c7f3 100644 --- a/lib/logins/screens/gender.dart +++ b/lib/logins/screens/gender.dart @@ -9,9 +9,11 @@ import '../../home/screens/home_layout.dart'; import '../providers/authentication.dart'; class Gender extends StatefulWidget { - // const Gender({Key? key}) : super(key: key); + /// the route name of the screen static const routeName = '/Gender'; + const Gender({super.key}); + @override State createState() => GenderState(); } diff --git a/lib/logins/screens/login.dart b/lib/logins/screens/login.dart index 943fb761..e0ef1a73 100644 --- a/lib/logins/screens/login.dart +++ b/lib/logins/screens/login.dart @@ -23,9 +23,12 @@ import 'signup.dart'; import '../widgets/web_image.dart'; class Login extends StatefulWidget { - // Login({Key? key}) : super(key: key); + /// the route name of the screen + static const routeName = '/Login'; + const Login({super.key}); + @override State createState() => LoginState(); } @@ -66,20 +69,17 @@ class LoginState extends State { /// error message to view when the log in is failed String errorMessage = ''; - /// variable to contain the url of the server - // final String url = 'https://api.nonlegit.click/api/v1'; - - /// variable to check if the backend finish the actual server of work with the mock - // final bool isMock = true; - ///post the login info to the backend server /// /// take the data from inputs listener and sent it to the server /// if the server return failed response then there is error message will appare /// other wise jump on the homeScreen - // Auth authentication = Auth(); void checkLogin(context) async { + //Input : + // context:- the Build context of the current screen + //output: none + final provider = Provider.of(context, listen: false); provider.login({ 'userName': inputUserNameController.text, @@ -95,8 +95,15 @@ class LoginState extends State { }); } - // + ///check if the user is already Authenticated + /// + ///if the user is authenticated , then send him to the home page + ///other wise continue to the login Future checkAut(context) async { + //Input : + // context:- the Build context of the current screen + //output: return bool Whether the user is authorised or not + String isLog = ModalRoute.of(context)?.settings.arguments != null ? ModalRoute.of(context)?.settings.arguments as String : ''; @@ -120,19 +127,13 @@ class LoginState extends State { }); checkAut(context).then((value) { fetchingDone = true; - setState(() {}); + if (_isBuild) setState(() {}); }); } _isInit = false; super.didChangeDependencies(); } - // @override - // void initState() { - // checkAut(context); - // super.initState(); - // } - @override Widget build(BuildContext context) { _isBuild = true; diff --git a/lib/logins/screens/signup.dart b/lib/logins/screens/signup.dart index 68440838..f24da642 100644 --- a/lib/logins/screens/signup.dart +++ b/lib/logins/screens/signup.dart @@ -17,11 +17,13 @@ import 'package:provider/provider.dart'; import 'package:flutter/foundation.dart' show kIsWeb; import 'login.dart'; import '../widgets/web_image.dart'; -// import 'package:webview_flutter_plus/webview_flutter_plus.dart'; // (kIsWeb) ? Colors.blue : Colors.red class SignUp extends StatefulWidget { - // const SignUp({Key? key}) : super(key: key); + const SignUp({Key? key}) : super(key: key); + + /// the route name of the screen + static const routeName = '/SignUp'; @override State createState() => SignUpState(); @@ -79,8 +81,11 @@ class SignUpState extends State { ///check the changes and detect when the finish flag is true ///and then activate the continue bottom void changeInput() { - isFinished = (validateEmail() == InputStatus.sucess) & - (validateUsername() == InputStatus.sucess) & + //Input : none + //output: none + + isFinished = (validateEmail() == InputStatus.sucess) && + (validateUsername() == InputStatus.sucess) && (validatePassword() == InputStatus.sucess); } @@ -91,6 +96,9 @@ class SignUpState extends State { /// the validator will return original if the field is empty /// otherwise the status will be faild and put an error message InputStatus validateEmail() { + //Input : none + //output: return the status of the Email input field + if (inputEmailController.text.isEmpty) return InputStatus.original; else if (EmailValidator.validate(inputEmailController.text.toLowerCase())) @@ -103,6 +111,8 @@ class SignUpState extends State { /// check if the username is taken or not Future checkUnique() async { + //Input : none + //output: none final provider = Provider.of(context, listen: false); provider.availableUserName(inputUserNameController.text).then((value) { isUnige = value; @@ -117,6 +127,8 @@ class SignUpState extends State { /// the validator will return original if the field is empty /// otherwise the status will be faild and put an error message InputStatus validateUsername() { + //Input : none + //output: return the status of the username input field if (inputUserNameController.text.isEmpty) { return InputStatus.original; } else if (inputUserNameController.text.length < 3 || @@ -140,6 +152,9 @@ class SignUpState extends State { /// the validator will return original if the field is empty /// otherwise the status will be faild and put an error message InputStatus validatePassword() { + //Input : none + //output: return the status of the Password input field + if (inputPasswardController.text.isEmpty) return InputStatus.original; else if (inputPasswardController.text.length >= 8) @@ -155,6 +170,9 @@ class SignUpState extends State { /// when user tap in the email textfailed mark it as taped /// when tap out check the validation using [validateEmail()] void controlEmailStatus(hasFocus) { + //Input : + // hasFocus:- Whether the Email input field is clikced or not + //output : none if (hasFocus == true) inputEmailStatus = InputStatus.taped; else @@ -165,6 +183,9 @@ class SignUpState extends State { /// /// Similar the [controlEmailStatus()] in the methodology void controlUsernameStatus(hasFocus) { + //Input : + // hasFocus:- Whether the UserName input field is clikced or not + //output: none if (hasFocus) inputUsernameStatus = InputStatus.taped; else @@ -175,6 +196,10 @@ class SignUpState extends State { /// /// Similar the [controlEmailStatus()] in the methodology void controlPasswordStatus(hasFocus) { + //Input : + // hasFocus:- Whether the password input field is clikced or not + //output: none + if (hasFocus) inputPasswardStatus = InputStatus.taped; else @@ -185,6 +210,9 @@ class SignUpState extends State { /// /// take the data from inputs listener and sent it to the server void submitSignUp() async { + //inpit : none + //output: none + final provider = Provider.of(context, listen: false); await provider.sinUp({ 'userName': inputUserNameController.text, @@ -204,7 +232,6 @@ class SignUpState extends State { @override Widget build(BuildContext context) { - final mediaQuery = MediaQuery.of(context); return Scaffold( body: Row( mainAxisSize: MainAxisSize.min, diff --git a/lib/logins/widgets/basic_buttom.dart b/lib/logins/widgets/basic_buttom.dart index 478a4d5c..37f3ff1a 100644 --- a/lib/logins/widgets/basic_buttom.dart +++ b/lib/logins/widgets/basic_buttom.dart @@ -2,7 +2,10 @@ import 'package:flutter/material.dart'; import 'package:responsive_sizer/responsive_sizer.dart'; class BasicBottom extends StatelessWidget { + ///the basic lable of the input text field final String lable; + + ///handler func to be called when onpressed event called final Function handler; const BasicBottom({this.lable = '', required this.handler}); @override diff --git a/lib/logins/widgets/continue_with_email.dart b/lib/logins/widgets/continue_with_email.dart index e0f57b3f..679f9523 100644 --- a/lib/logins/widgets/continue_with_email.dart +++ b/lib/logins/widgets/continue_with_email.dart @@ -1,10 +1,10 @@ import 'package:flutter/material.dart'; class ContinueWithEmail extends StatelessWidget { - // const MyWidget({Key? key}) : super(key: key); + ///handler func to be called when onpressed event called final VoidCallback handler; - ContinueWithEmail({required this.handler}); + const ContinueWithEmail({super.key, required this.handler}); @override Widget build(BuildContext context) { return Padding( diff --git a/lib/logins/widgets/continue_with_facebook.dart b/lib/logins/widgets/continue_with_facebook.dart index c097f89e..ccf244ce 100644 --- a/lib/logins/widgets/continue_with_facebook.dart +++ b/lib/logins/widgets/continue_with_facebook.dart @@ -2,9 +2,9 @@ import '../../icons/google_facebook_icons.dart'; import 'package:flutter/material.dart'; class ContinueWithFacebook extends StatelessWidget { - // const ContinueWithFacebook({Key? key}) : super(key: key); + ///handler func to be called when onpressed event called final VoidCallback handler; - ContinueWithFacebook({required this.handler}); + const ContinueWithFacebook({super.key, required this.handler}); @override Widget build(BuildContext context) { return Padding( diff --git a/lib/logins/widgets/continue_with_google.dart b/lib/logins/widgets/continue_with_google.dart index 851928a2..608dd01d 100644 --- a/lib/logins/widgets/continue_with_google.dart +++ b/lib/logins/widgets/continue_with_google.dart @@ -2,9 +2,9 @@ import '../../icons/google_facebook_icons.dart'; import 'package:flutter/material.dart'; class ContinueWithGoogle extends StatelessWidget { - // const MyWidget({Key? key}) : super(key: key); + ///handler func to be called when onpressed event called final VoidCallback handler; - ContinueWithGoogle({required this.handler}); + const ContinueWithGoogle({super.key, required this.handler}); @override Widget build(BuildContext context) { return Padding( diff --git a/lib/logins/widgets/hyperlink_text.dart b/lib/logins/widgets/hyperlink_text.dart index c4a1b895..4760ccb5 100644 --- a/lib/logins/widgets/hyperlink_text.dart +++ b/lib/logins/widgets/hyperlink_text.dart @@ -4,10 +4,12 @@ import 'package:flutter/gestures.dart'; import 'package:url_launcher/url_launcher.dart'; class HyperLinkText extends StatelessWidget { - // const HyperLinkText({Key? key}) : super(key: key); + ///the basic lable of the input text field final String label; + + /// the url that will be lanched when click in the URl text final String url; - HyperLinkText({required this.label, required this.url}); + const HyperLinkText({super.key, required this.label, required this.url}); @override Widget build(BuildContext context) { return RichText( diff --git a/lib/logins/widgets/password_input.dart b/lib/logins/widgets/password_input.dart index 80aef7f2..4192a753 100644 --- a/lib/logins/widgets/password_input.dart +++ b/lib/logins/widgets/password_input.dart @@ -3,15 +3,30 @@ import '../../moderation_settings/widgets/status.dart'; import '../../models/wrapper.dart'; class PasswordInput extends StatefulWidget { - // const PasswordInput({Key? key}) : super(key: key); + ///the basic lable of the input text field + final String lable; + + /// the input contrroler to listen to the password input field TextEditingController inputController; + + ///handler func to be called when change input event called + final Function changeInput; + + ///handler func to be called when onpressed event called + final Function ontap; + + ///wheter the password text is visible or not + BoolWrapper isVisable; + + /// the current status of the poassword input field if it origin or taped or error InputStatus currentStatus; PasswordInput( - {this.lable = '', + {super.key, + this.lable = '', this.currentStatus = InputStatus.original, required this.ontap, required this.isVisable, diff --git a/lib/logins/widgets/text_input.dart b/lib/logins/widgets/text_input.dart index bf2ee0fd..ace6e8c5 100644 --- a/lib/logins/widgets/text_input.dart +++ b/lib/logins/widgets/text_input.dart @@ -3,11 +3,23 @@ import 'package:flutter/material.dart'; import '../../moderation_settings/widgets/status.dart'; class TextInput extends StatefulWidget { - // const TextInput({Key? key}) : super(key: key); + ///the basic lable of the input text field final String lable; + + /// the input contrroler to listen to the password input field + TextEditingController inputController; + + ///handler func to be called when change input event called + final Function changeInput; + + ///handler func to be called when onpressed event called + final Function ontap; + + /// the current status of the poassword input field if it origin or taped or error + InputStatus currentStatus; TextInput( diff --git a/lib/logins/widgets/upper_bar.dart b/lib/logins/widgets/upper_bar.dart index 4e1e63ee..b439b7e9 100644 --- a/lib/logins/widgets/upper_bar.dart +++ b/lib/logins/widgets/upper_bar.dart @@ -8,14 +8,19 @@ import '../../screens/emptyscreen.dart'; import '../../home/screens/home_layout.dart'; class UpperBar extends StatelessWidget { - // UpperBar({Key? key}) : super(key: key); - // final String openningScreen; + /// the current status of the poassword input field if it origin or taped or error + final UpperbarStatus currentStatus; - UpperBar(this.currentStatus); + const UpperBar(this.currentStatus, {super.key}); + + ///replace the current screen with other on + /// + ///if the screen was login then pop it and go to sign up + ///else if it was sign up pop the sign uo and go to login + ///else if it was skiped then go to home page + ///else go to no where void _pushScreen(context) { Navigator.of(context).pop(context); - // Navigator.of(context).pushNamed(homeLayoutScreen.routeName); - Navigator.push( context, MaterialPageRoute( diff --git a/lib/logins/widgets/upper_text.dart b/lib/logins/widgets/upper_text.dart index b0a6b836..ffdb83bd 100644 --- a/lib/logins/widgets/upper_text.dart +++ b/lib/logins/widgets/upper_text.dart @@ -1,9 +1,9 @@ import 'package:flutter/material.dart'; class UpperText extends StatelessWidget { - // const UpperText({Key? key}) : super(key: key); + ///text that will be appare final String data; - UpperText(this.data); + const UpperText(this.data, {super.key}); @override Widget build(BuildContext context) { return Center( diff --git a/lib/logins/widgets/web_image.dart b/lib/logins/widgets/web_image.dart index d248db77..89e7c676 100644 --- a/lib/logins/widgets/web_image.dart +++ b/lib/logins/widgets/web_image.dart @@ -1,5 +1,3 @@ -import 'package:flutter/src/widgets/container.dart'; -import 'package:flutter/src/widgets/framework.dart'; import 'package:responsive_sizer/responsive_sizer.dart'; import 'package:flutter/material.dart'; diff --git a/lib/main.dart b/lib/main.dart index f0be5e8e..c1d391e1 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -93,6 +93,7 @@ import './settings/provider/user_settings_provider.dart'; import './search/provider/search_provider.dart'; import './discover/providers/discover_provider.dart'; import './moderation_settings/provider/moderation_general_data.dart'; +import './show_post/screens/show_post_web.dart'; import 'widgets/custom_snack_bar.dart'; //import './models/push_notification_model.dart'; import './shared/constants.dart'; @@ -497,15 +498,14 @@ class _MyAppState extends State { onSurface: Colors.white), ), // home: NotificationScreen(), - // home: HomeLayoutScreen(), + // home: HomeLayoutScreen(), // home: Description(), // home: HomeScreen(), // home: NotificationScreen(), // home:ShowPostDetails(), //home: CreateCommunity(), - //home: NewMessageScreen(), + //home: NewMessageScreen(), // home: EditPost(), - // home: WebMessageScreen(), // home: SearchInside(quiry: 'mohab'), //home: const DiscoverScreen(), // home: homeLayoutScreen(), @@ -519,7 +519,6 @@ class _MyAppState extends State { // home: HomeScreen(), home: Login(), // home: CreateCommunity(), - //home: Login(), // home: ForgotUserName(), // home: ForgotPassword(), // home: TraficState(), @@ -541,10 +540,10 @@ class _MyAppState extends State { // home: Search(), routes: { TraficState.routeName: (context) => TraficState(), - AllMessageScreen.routeName : (context) => AllMessageScreen(), - WebNewMessageScreen.routeName : (context) => WebNewMessageScreen(), - SentMessage.routeName : (context) => SentMessage(), - UnreadMessageScreen.routeName : (context) => UnreadMessageScreen(), + AllMessageScreen.routeName: (context) => AllMessageScreen(), + WebNewMessageScreen.routeName: (context) => WebNewMessageScreen(), + SentMessage.routeName: (context) => SentMessage(), + UnreadMessageScreen.routeName: (context) => UnreadMessageScreen(), ReplyMessageScreen.routeName: (context) => ReplyMessageScreen(), ShowMessageBody.routeName: (context) => ShowMessageBody(), MessageMainScreen.routeName: (context) => MessageMainScreen(), @@ -621,6 +620,7 @@ class _MyAppState extends State { ModNotificationScreen(), ModeratedSubredditScreen.routeName: (context) => ModeratedSubredditScreen(), + ShowPostDetailsWeb.routeName: (context) => ShowPostDetailsWeb(), // LoginPage.routeName: (context) => LoginPage(), }, ), diff --git a/lib/messages/models/user_message.dart b/lib/messages/models/user_message.dart index 1f4e46fa..bcbcc5f2 100644 --- a/lib/messages/models/user_message.dart +++ b/lib/messages/models/user_message.dart @@ -95,7 +95,6 @@ class ShowMessagesModel { (json['subject']['text'] == null) ? '' : json['subject']['text']; //invitation,approved } else { - print('hi'); subredditName = json['subreddit']['fixedName'] ?? ''; subjectId = (json['subreddit']['_id'] == null) ? '' @@ -103,7 +102,6 @@ class ShowMessagesModel { subjectText = getText(json['type'], json['to']['userName'], json['subreddit']['fixedName']); } - print('helllloo'); if (json['type'] == 'userMessage') { text = json['text']; } else { @@ -113,14 +111,7 @@ class ShowMessagesModel { json['subreddit']['fixedName'] ?? '', json['subreddit']['name'] ?? ''); } - // text = getBody( - // json['type'], - // toUsername, - // json['subreddit']['fixedName'] ?? '', - // json['subreddit']['name'] ?? '') == - // '' - // ? json['text'] - // : //json['text'] ?? ''; + type = json['type']; createdAt = json['createdAt']; diff --git a/lib/messages/screens/message_main_screen.dart b/lib/messages/screens/message_main_screen.dart index 65b19f38..7e8e27de 100644 --- a/lib/messages/screens/message_main_screen.dart +++ b/lib/messages/screens/message_main_screen.dart @@ -16,6 +16,9 @@ class MessageMainScreen extends StatefulWidget { } class _MessageMainScreenState extends State { + //Parse String to Date Time and get actual time + //Input String + //Return type string String getTimeOfNotification(date) { String howOld; final difference = DateTime.now().difference(DateTime.parse(date)); @@ -39,11 +42,6 @@ class _MessageMainScreenState extends State { bool returned = false; bool tried = false; - @override - void initState() { - // TODO: implement initState - super.initState(); - } Map> messageShow = {}; void didChangeDependencies() async { @@ -66,21 +64,22 @@ class _MessageMainScreenState extends State { super.didChangeDependencies(); } - + //Used to check whether the logged in use is the to username or the from username + //Return type string with the name of ther user String getUsername(me, to, from) { return (me == to) ? from : to; } - + //Return type void + // Parameter => username of required user to be blocked _blockUser(userName) { Provider.of(context, listen: false) .blockUser(context, userName); } - + //Function called to reply on user message + //Allow user only to reply on other user only _onclick(index) { - // int id; final get = messageShow[index]!.firstWhere((element) { return element.fromUsername != element.me; - // element.toUsername != element.me; }); if (get != null) { Navigator.push( @@ -135,15 +134,10 @@ class _MessageMainScreenState extends State { shrinkWrap: true, itemBuilder: (context, index) { String key = messageShow.keys.elementAt(index); - print(key); final message = messageShow[key]!.last; - print(message.subjectText); - print(message.text); return GestureDetector( onTap: () { _onclick(key); - // Navigator.of(context) - // .pushNamed(ShowMessageBody.routeName); }, child: Column( crossAxisAlignment: CrossAxisAlignment.start, diff --git a/lib/messages/screens/new_message_screen.dart b/lib/messages/screens/new_message_screen.dart index 4a338273..8d1ef075 100644 --- a/lib/messages/screens/new_message_screen.dart +++ b/lib/messages/screens/new_message_screen.dart @@ -16,33 +16,29 @@ class NewMessageScreen extends StatefulWidget { class _NewMessageScreenState extends State { bool _iselected = false; - // TextEditingController? _nameController = null; - // TextEditingController? _subjectController = null; - //TextEditingController? _bodyController = null; bool subjectAvailable = false; bool messageAvailable = false; String subject = ''; String message = ''; String? username; + //Validate text field to check if it is empty or not + //_iselected is a boolean type variable used to check on before sending new message _changeStateOfUserName() { - //print(widget.userName); setState(() { if (username!.isNotEmpty) { widget.userNameAvailable = true; } else { widget.userNameAvailable = false; } + _iselected = widget.userNameAvailable! && subjectAvailable && messageAvailable; - // if(widget.userNameAvailable){ - - // } }); } - + //Validate text field to check if it is empty or not + //_iselected is a boolean type variable used to check on before sending new message _changeStateOfSubject() { - print(subject); setState(() { if (subject.isNotEmpty) { subjectAvailable = true; @@ -53,9 +49,9 @@ class _NewMessageScreenState extends State { widget.userNameAvailable! && subjectAvailable && messageAvailable; }); } - + //Validate text field to check if it is empty or not + //_iselected is a boolean type variable used to check on before sending new message _changeStateOfMessage() { - print(message); setState(() { if (message.isNotEmpty) { messageAvailable = true; @@ -66,7 +62,9 @@ class _NewMessageScreenState extends State { widget.userNameAvailable! && subjectAvailable && messageAvailable; }); } - + //Parameters userName ==> whom to send to the message + // subject ==> Message related to which topic + // message ==> The main message you want to send _saveNewMessage(username, subject, message) { Provider.of(context, listen: false).createMessage( {'to': username, 'text': message, 'subject': subject}, @@ -75,13 +73,6 @@ class _NewMessageScreenState extends State { }); } - // @override - // void initState() { - // // TODO: implement initState - // super.initState(); - // //_nameController.value = null; - - // } @override void initState() { @@ -145,7 +136,6 @@ class _NewMessageScreenState extends State { textAlignVertical: TextAlignVertical.center, cursorColor: Colors.black, onChanged: (value) { - print(value); username = value; _changeStateOfUserName(); }, diff --git a/lib/messages/screens/reply_message_screen.dart b/lib/messages/screens/reply_message_screen.dart index 0b97c055..d3e15c7e 100644 --- a/lib/messages/screens/reply_message_screen.dart +++ b/lib/messages/screens/reply_message_screen.dart @@ -10,7 +10,7 @@ class ReplyMessageScreen extends StatefulWidget { class _ReplyMessageScreenState extends State { String messageBody = ''; - + //Set the variable messageBody while changing the value of text field _onChangeTextField(value) { setState(() { messageBody = value; diff --git a/lib/messages/screens/show_message_body.dart b/lib/messages/screens/show_message_body.dart index 18b4a7e3..5fae4b5f 100644 --- a/lib/messages/screens/show_message_body.dart +++ b/lib/messages/screens/show_message_body.dart @@ -17,27 +17,21 @@ class ShowMessageBody extends StatefulWidget { } class _ShowMessageBodyState extends State { - //List messageBody = []; - //String replyMessage = ''; + + + //Function called to save reply of message + //Parameter replyMessage ==> the message required to be sent to user _saveReply(replyMessage) async { - //print(replyMessage); - widget.list!.forEach((element) { - print(element.sId); - }); - print('hiiiiiiiiiiii heal'); - print(widget.messageId); Provider.of(context, listen: false).replyMessage( {'text': replyMessage}, context, widget.messageId).then((value) { setState(() { - // widget.list!.add(ShowMessagesModel( - // toUsername: widget.appBarText, - // text: replyMessage, - // createdAt: DateTime.now().toString(), - // type: 'userMessage')); + }); }); } - + //Parse String to Date Time and get actual time + //Input String + //Return type string String getTimeOfNotification(date) { String howOld; final difference = DateTime.now().difference(DateTime.parse(date)); @@ -56,9 +50,9 @@ class _ShowMessageBodyState extends State { } return howOld; } - + //Called when user accepts to be moderator in the subreddit + //Parameter subreddit name that user will accept invitation in _acceptModeration(subredditName) { - print(subredditName); Provider.of(context, listen: false) .acceptSubredditInvite(context, subredditName) .then((value) { @@ -71,7 +65,6 @@ class _ShowMessageBodyState extends State { return Scaffold( appBar: AppBar( title: Text(widget.appBarText! - // 'u/BasmaElhoseny01' ), titleTextStyle: const TextStyle( color: Colors.black, fontWeight: FontWeight.w500, fontSize: 18), @@ -109,7 +102,6 @@ class _ShowMessageBodyState extends State { child: RichText( maxLines: 2, overflow: TextOverflow.ellipsis, - //softWrap: true, text: TextSpan( text: '${widget.list![index].fromUsername}', style: const TextStyle( @@ -130,8 +122,6 @@ class _ShowMessageBodyState extends State { style: const TextStyle( color: Colors.grey, fontSize: 10)) ]) - - //overflow: TextOverflow.clip, ), ), Padding( @@ -178,7 +168,6 @@ class _ShowMessageBodyState extends State { builder: (context) => ReplyMessageScreen( savePost: _saveReply, ))); - //Navigator.of(context).pushNamed(ReplyMessageScreen.routeName); }, child: const Align( alignment: Alignment.centerLeft, @@ -186,8 +175,6 @@ class _ShowMessageBodyState extends State { style: TextStyle( fontSize: 15, color: Colors.grey, - - //backgroundColor: Colors.grey )), ), ), diff --git a/lib/messages/screens/unread_message_screen.dart b/lib/messages/screens/unread_message_screen.dart index 09eb5b74..adbd3516 100644 --- a/lib/messages/screens/unread_message_screen.dart +++ b/lib/messages/screens/unread_message_screen.dart @@ -1,6 +1,13 @@ import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; import 'package:responsive_sizer/responsive_sizer.dart'; +import '../../other_profile/screens/others_profile_screen.dart'; +import '../../widgets/loading_reddit.dart'; +import '../models/user_message.dart'; +import '../provider/message_provider.dart'; +import '../widgets/first_nav_bar.dart'; +import '../widgets/second_nav_bar.dart'; import 'web_message_all_screen.dart'; import 'web_message_screen.dart'; import 'web_new_message_screen.dart'; @@ -15,190 +22,225 @@ class UnreadMessageScreen extends StatefulWidget { } class _UnreadMessageScreenState extends State { + bool _isInit = true; + bool returned = false; + bool tried = false; + + List unreadMessage = []; + void didChangeDependencies() async { + if (_isInit) { + setState(() { + returned = false; + }); + await Provider.of(context, listen: false) + .getUnreadMessages(context, 0, 10) + .then((value) async { + unreadMessage = + Provider.of(context, listen: false).unreadMessage; + await Provider.of(context, listen: false) + .markAllAsRead(context); + setState(() { + returned = true; + }); + }); + } + _isInit = false; + + super.didChangeDependencies(); + } + //deleteMessage => parameter take id of required messaged to be deleted + _deleteMessage(id) async { + await Provider.of(context, listen: false) + .deleteMessage(context, id); + } + //blockUser => parameter take username of required user to be blocked + _blockUser(userName) async { + await Provider.of(context, listen: false) + .blockUser(context, userName); + } + //Parse String to Date Time and get actual time + //Input String + //Return type string + String getTimeOfNotification(date) { + String howOld; + final difference = DateTime.now().difference(DateTime.parse(date)); + if (difference.inDays >= 365) { + howOld = '${difference.inDays ~/ 365}y'; + } else if (difference.inDays >= 30) { + howOld = '${difference.inDays ~/ 30}mo'; + } else if (difference.inDays >= 1) { + howOld = '${difference.inDays}d'; + } else if (difference.inMinutes >= 60) { + howOld = '${difference.inHours}h'; + } else if (difference.inSeconds >= 60) { + howOld = '${difference.inMinutes}m'; + } else { + howOld = '${difference.inSeconds}s'; + } + return howOld; + } + //Called when user accepts to be moderator in the subreddit + //Parameter subreddit name that user will accept invitation in + _acceptModeration(subredditName) { + Provider.of(context, listen: false) + .acceptSubredditInvite(context, subredditName) + .then((value) { + Navigator.of(context).pop(); + }); + } + @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Hiiiii')), - body: SingleChildScrollView( - scrollDirection: Axis.vertical, - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Container( - color: Colors.blue.shade800, - child: Row( - children: [ - Padding( - padding: EdgeInsets.only( - top: 13, bottom: 13, left: 100, right: 20), - child: TextButton( - onPressed: () { - Navigator.of(context) - .pushNamed(WebNewMessageScreen.routeName); - }, - child: Text( - 'Send a Private Message', - style: TextStyle( - color: Colors.grey.shade400, - fontSize: 15, - fontWeight: FontWeight.bold), - )), - ), - Padding( - padding: EdgeInsets.only( - top: 13, bottom: 13, left: 8, right: 20), - child: TextButton( - onPressed: () { - Navigator.of(context) - .pushNamed(WebMessageScreen.routeName); - }, - child: Text( - 'Inbox', - style: TextStyle( - color: Colors.grey.shade400, - fontSize: 15, - fontWeight: FontWeight.bold), - )), - ), - Padding( - padding: EdgeInsets.only( - top: 13, bottom: 13, left: 8, right: 20), - child: TextButton( - onPressed: () { - Navigator.of(context) - .pushNamed(SentMessage.routeName); - }, - child: Text( - 'Sent', - style: TextStyle( - color: Colors.grey.shade400, - fontSize: 15, - fontWeight: FontWeight.bold), - )), - ), - ], - ), - ), - Container( - color: Colors.blue.shade800, - child: Row( - children: [ - Padding( - padding: EdgeInsets.only( - top: 10, bottom: 8, left: 100, right: 20), - child: TextButton( - onPressed: () { - Navigator.of(context) - .pushNamed(AllMessageScreen.routeName); - }, - child: Text('All')), - ), - Padding( - padding: - EdgeInsets.only(top: 10, bottom: 8, left: 8, right: 20), - child: TextButton( - onPressed: () { - Navigator.of(context) - .pushNamed(SentMessage.routeName); - }, - child: Text('Unread')), - ), - Padding( - padding: - EdgeInsets.only(top: 10, bottom: 8, left: 8, right: 20), - child: TextButton( - onPressed: () { - Navigator.of(context) - .pushNamed(WebMessageScreen.routeName); - }, - child: Text('Messages')), - ), - ], - ), - ), - Container( - color: Colors.indigo[50], - margin: EdgeInsets.only(left: 10.w, right: 10.w, top: 10), - child: Container( - color: Colors.indigo[50], - child: Column( - children: [ - ListView.builder( - physics: const ClampingScrollPhysics(), - shrinkWrap: true, - itemBuilder: (context, index) { - return Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - ////subjectccccccccccccccccc - Padding( - padding: const EdgeInsets.only(left: 10,top: 5), - child: Text('Jooko:'), - ), - Row( - children: [ - Padding( - padding: const EdgeInsets.only(left: 18), - child: Text('from '), - ), - TextButton( - style: TextButton.styleFrom( - padding: EdgeInsets.zero, - ), - onPressed: () {}, - child: Text( - 'Eman Shahda', - style: TextStyle(color: Colors.blue), - )), - Text(' sent 3 days ago'), - ], - ), - Padding( - padding: const EdgeInsets.only( - left: 23, right: 8, bottom: 8, top: 8), - child: Text( - 'hiiiiiiiii bgfsvbhszdfkz hadsafjdnkd bhjknkamkfnk arhfbagjarehk dhfjskfa ajfbfrak dfjsk bfdjksml,;htgfjkdl;sp gfdjksl; mrtgwirjawelf gsduhskerjgesl sdgjhrkgl'), - ), - Padding( - padding:const EdgeInsets.only( - left: 16, right: 8, bottom: 8, top: 8), - child: Row( - children: [ - TextButton( - onPressed: () {}, - child: Text('Reply', - style: TextStyle(color: Colors.grey))), - TextButton( - onPressed: () {}, - child: Text('Delete', - style: TextStyle(color: Colors.grey))), - TextButton( - onPressed: () {}, - child: Text('BlockUser', - style: TextStyle(color: Colors.grey))), - ], + body: (!returned) + ? LoadingReddit() + : (unreadMessage.isEmpty) + ? Container( + color: Colors.grey[100], + child: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Image.asset('assets/images/emptyNotification.png'), + Text( + 'Wow,such empty', + style: TextStyle( + color: Colors.grey.shade500, + fontSize: 13, + fontWeight: FontWeight.w300), + ), + ], + )), + ) + : SingleChildScrollView( + scrollDirection: Axis.vertical, + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + FirstNavBar(), + SecondNavBar(), + Container( + color: Colors.indigo[50], + margin: + EdgeInsets.only(left: 10.w, right: 10.w, top: 10), + child: Container( + color: Colors.indigo[50], + child: Column( + children: [ + ListView.builder( + physics: const ClampingScrollPhysics(), + shrinkWrap: true, + itemBuilder: (context, index) { + return Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + ////subjectccccccccccccccccc + Padding( + padding: const EdgeInsets.only( + left: 20, top: 10), + child: Text( + unreadMessage[index].subjectText!, + style: TextStyle( + fontWeight: FontWeight.bold), + ), + ), + Row( + children: [ + Padding( + padding: + const EdgeInsets.only(left: 18), + child: Text('from '), + ), + TextButton( + style: TextButton.styleFrom( + padding: EdgeInsets.zero, + ), + onPressed: () { + Navigator.of(context).pushNamed( + OthersProfileScreen + .routeName, + arguments: + unreadMessage[index] + .toUsername); + }, + child: Text( + 'u/${unreadMessage[index].fromUsername!}', + style: TextStyle( + color: Colors.blue), + )), + Text( + ' sent ${getTimeOfNotification(unreadMessage[index].createdAt!)}'), + ], + ), + Padding( + padding: const EdgeInsets.only( + left: 25, + right: 8, + bottom: 8, + top: 8), + child: Text(unreadMessage[index].text!), + ), + if (unreadMessage[index].type == + 'subredditModeratorInvite') + TextButton( + onPressed: () { + _acceptModeration( + unreadMessage[index] + .subredditName); + }, + child: Text( + 'Click to accept', + style: TextStyle( + color: Colors.blue, + ), + )), + Padding( + padding: const EdgeInsets.only( + left: 19, + right: 8, + bottom: 8, + top: 8), + child: Row( + children: [ + TextButton( + onPressed: () { + _deleteMessage( + unreadMessage[index].sId); + }, + child: Text('Delete', + style: TextStyle( + color: Colors.grey))), + TextButton( + onPressed: () { + _blockUser( + unreadMessage[index] + .toUsername); + }, + child: Text('BlockUser', + style: TextStyle( + color: Colors.grey))), + ], + ), + ), + Divider( + color: Colors.grey, + ) + ], + ); + }, + itemCount: unreadMessage.length, ), - ), - Divider( - color: Colors.transparent, - ) - ], - ); - }, - itemCount: 5, - ), - Divider( - color: Colors.grey, - ), - ], + ], + ), + ), + ), + ], + ), ), - ), - ), - ], - ), - ), ); } -} \ No newline at end of file +} diff --git a/lib/messages/screens/web_message_all_screen.dart b/lib/messages/screens/web_message_all_screen.dart index b55984b6..4205bd82 100644 --- a/lib/messages/screens/web_message_all_screen.dart +++ b/lib/messages/screens/web_message_all_screen.dart @@ -1,7 +1,10 @@ import 'package:flutter/material.dart'; +import 'package:post/messages/widgets/first_nav_bar.dart'; +import 'package:post/messages/widgets/second_nav_bar.dart'; import 'package:provider/provider.dart'; import 'package:responsive_sizer/responsive_sizer.dart'; +import '../../other_profile/screens/others_profile_screen.dart'; import '../../widgets/loading_reddit.dart'; import '../models/user_message.dart'; import '../provider/message_provider.dart'; @@ -19,6 +22,9 @@ class AllMessageScreen extends StatefulWidget { } class _AllMessageScreenState extends State { + //Parse String to Date Time and get actual time + //Input String + //Return type string String getTimeOfNotification(date) { String howOld; final difference = DateTime.now().difference(DateTime.parse(date)); @@ -63,6 +69,25 @@ class _AllMessageScreenState extends State { super.didChangeDependencies(); } + //Called when user accepts to be moderator in the subreddit + //Parameter subreddit name that user will accept invitation in + _acceptModeration(subredditName) { + Provider.of(context, listen: false) + .acceptSubredditInvite(context, subredditName) + .then((value) { + Navigator.of(context).pop(); + }); + } + //deleteMessage => parameter take id of required messaged to be deleted + _deleteMessage(id) async { + await Provider.of(context, listen: false) + .deleteMessage(context, id); + } + //blockUser => parameter take username of required user to be blocked + _blockUser(userName) async { + await Provider.of(context, listen: false) + .blockUser(context, userName); + } @override Widget build(BuildContext context) { @@ -94,98 +119,8 @@ class _AllMessageScreenState extends State { mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ - Container( - color: Colors.blue.shade800, - child: Row( - children: [ - Padding( - padding: EdgeInsets.only( - top: 13, bottom: 13, left: 100, right: 20), - child: TextButton( - onPressed: () { - Navigator.of(context).pushNamed( - WebNewMessageScreen.routeName); - }, - child: Text( - 'Send a Private Message', - style: TextStyle( - color: Colors.grey.shade400, - fontSize: 15, - fontWeight: FontWeight.bold), - )), - ), - Padding( - padding: EdgeInsets.only( - top: 13, bottom: 13, left: 8, right: 20), - child: TextButton( - onPressed: () { - Navigator.of(context) - .pushNamed(WebMessageScreen.routeName); - }, - child: Text( - 'Inbox', - style: TextStyle( - color: Colors.grey.shade400, - fontSize: 15, - fontWeight: FontWeight.bold), - )), - ), - Padding( - padding: EdgeInsets.only( - top: 13, bottom: 13, left: 8, right: 20), - child: TextButton( - onPressed: () { - Navigator.of(context) - .pushNamed(SentMessage.routeName); - }, - child: Text( - 'Sent', - style: TextStyle( - color: Colors.grey.shade400, - fontSize: 15, - fontWeight: FontWeight.bold), - )), - ), - ], - ), - ), - Container( - color: Colors.blue.shade800, - child: Row( - children: [ - Padding( - padding: EdgeInsets.only( - top: 10, bottom: 8, left: 100, right: 20), - child: TextButton( - onPressed: () { - Navigator.of(context) - .pushNamed(AllMessageScreen.routeName); - }, - child: Text('All')), - ), - Padding( - padding: EdgeInsets.only( - top: 10, bottom: 8, left: 8, right: 20), - child: TextButton( - onPressed: () { - Navigator.of(context) - .pushNamed(SentMessage.routeName); - }, - child: Text('Unread')), - ), - Padding( - padding: EdgeInsets.only( - top: 10, bottom: 8, left: 8, right: 20), - child: TextButton( - onPressed: () { - Navigator.of(context) - .pushNamed(WebMessageScreen.routeName); - }, - child: Text('Messages')), - ), - ], - ), - ), + FirstNavBar(), + SecondNavBar(), Container( color: Colors.indigo[50], margin: @@ -206,58 +141,82 @@ class _AllMessageScreenState extends State { ////subjectccccccccccccccccc Padding( padding: const EdgeInsets.only( - left: 10, top: 5), + left: 20, top: 10), child: Text( - allMessage[index].subjectText!), + allMessage[index].subjectText!,style: TextStyle( + fontWeight: FontWeight.bold),), ), Row( children: [ - Padding( + const Padding( padding: - const EdgeInsets.only(left: 18), + EdgeInsets.only(left: 22,top: 3), child: Text('from '), ), TextButton( style: TextButton.styleFrom( padding: EdgeInsets.zero, ), - onPressed: () {}, + onPressed: () { + Navigator.of(context).pushNamed( + OthersProfileScreen + .routeName, + arguments: allMessage[index].toUsername + ); + }, child: Text( allMessage[index].toUsername!, - style: TextStyle( + style: const TextStyle( color: Colors.blue), )), - Text(' sent 3 days ago'), + Text( + ' sent ${getTimeOfNotification(allMessage[index].createdAt!)}'), ], ), Padding( padding: const EdgeInsets.only( - left: 23, + left: 25, right: 8, bottom: 8, top: 8), child: Text(allMessage[index].text!), ), + if (allMessage[index].type == + 'subredditModeratorInvite') + TextButton( + onPressed: () { + _acceptModeration( + allMessage[index] + .subredditName); + }, + child: Text( + 'Click to accept', + style: TextStyle( + color: Colors.blue, + ), + )), Padding( padding: const EdgeInsets.only( - left: 16, + left: 19, right: 8, bottom: 8, top: 8), child: Row( children: [ TextButton( - onPressed: () {}, - child: Text('Reply', - style: TextStyle( - color: Colors.grey))), - TextButton( - onPressed: () {}, + onPressed: () { + _deleteMessage( + allMessage[index] + .sId); + }, child: Text('Delete', style: TextStyle( color: Colors.grey))), TextButton( - onPressed: () {}, + onPressed: () { + _blockUser(allMessage[index] + .toUsername); + }, child: Text('BlockUser', style: TextStyle( color: Colors.grey))), @@ -265,16 +224,14 @@ class _AllMessageScreenState extends State { ), ), Divider( - color: Colors.transparent, + color: Colors.grey, ) ], ); }, itemCount: allMessage.length, ), - Divider( - color: Colors.grey, - ), + ], ), ), diff --git a/lib/messages/screens/web_message_screen.dart b/lib/messages/screens/web_message_screen.dart index fc47082d..6d5757c9 100644 --- a/lib/messages/screens/web_message_screen.dart +++ b/lib/messages/screens/web_message_screen.dart @@ -1,12 +1,17 @@ import 'package:flutter/material.dart'; import 'package:flutter/src/widgets/container.dart'; import 'package:flutter/src/widgets/framework.dart'; +import 'package:post/messages/widgets/first_nav_bar.dart'; +import 'package:post/messages/widgets/second_nav_bar.dart'; +import 'package:post/other_profile/widgets/other_profile_web.dart'; +import 'package:shared_preferences/shared_preferences.dart'; import '../../messages/provider/message_provider.dart'; import '../../messages/screens/web_message_all_screen.dart'; import '../../messages/screens/web_sent_message.dart'; import 'package:provider/provider.dart'; import 'package:responsive_sizer/responsive_sizer.dart'; +import '../../other_profile/screens/others_profile_screen.dart'; import '../../widgets/loading_reddit.dart'; import '../models/user_message.dart'; import 'web_new_message_screen.dart'; @@ -21,7 +26,10 @@ class WebMessageScreen extends StatefulWidget { class _WebMessageScreenState extends State with TickerProviderStateMixin { - String getTimeOfNotification(date) { + //Parse String to Date Time and get actual time + //Input String + //Return type string + String getTimeOfNotification(date) { String howOld; final difference = DateTime.now().difference(DateTime.parse(date)); if (difference.inDays >= 365) { @@ -40,6 +48,8 @@ class _WebMessageScreenState extends State return howOld; } + String me = ''; + bool _isInit = true; bool returned = false; bool tried = false; @@ -50,21 +60,40 @@ class _WebMessageScreenState extends State setState(() { returned = false; }); - // _updateCount(); - await Provider.of(context, listen: false) - .getAllMessages(context, 0, 10) - .then((value) { - messageShow = - Provider.of(context, listen: false).messageShow; - setState(() { - returned = true; + await Provider.of(context, listen: false) + .getAllMessages(context, 0, 10) + .then((value) { + messageShow = + Provider.of(context, listen: false).messageShow; + setState(() { + returned = true; + }); }); - }); + } _isInit = false; super.didChangeDependencies(); } + //deleteMessage => parameter take id of required messaged to be deleted + _deleteMessage(id) async { + await Provider.of(context, listen: false) + .deleteMessage(context, id); + } +//blockUser => parameter take username of required user to be blocked + _blockUser(userName) async { + await Provider.of(context, listen: false) + .blockUser(context, userName); + } +//Called when user accepts to be moderator in the subreddit + //Parameter subreddit name that user will accept invitation in + _acceptModeration(subredditName) { + Provider.of(context, listen: false) + .acceptSubredditInvite(context, subredditName) + .then((value) { + Navigator.of(context).pop(); + }); + } @override Widget build(BuildContext context) { @@ -72,251 +101,214 @@ class _WebMessageScreenState extends State return Scaffold( appBar: AppBar(title: Text('Hiiiii')), body: (!returned) - ? const LoadingReddit() - : (messageShow.isEmpty) - ? Container( - color: Colors.grey[100], - child: Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Image.asset('assets/images/emptyNotification.png'), - Text( - 'Wow,such empty', - style: TextStyle( - color: Colors.grey.shade500, - fontSize: 13, - fontWeight: FontWeight.w300), - ), - ], - )), - ) - : - SingleChildScrollView( - scrollDirection: Axis.vertical, - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Container( - color: Colors.blue.shade800, - child: Row( - children: [ - Padding( - padding: EdgeInsets.only( - top: 13, bottom: 13, left: 100, right: 20), - child: TextButton( - onPressed: () { - Navigator.of(context) - .pushNamed(WebNewMessageScreen.routeName); - }, - child: Text( - 'Send a Private Message', - style: TextStyle( - color: Colors.grey.shade400, - fontSize: 15, - fontWeight: FontWeight.bold), - )), - ), - Padding( - padding: EdgeInsets.only( - top: 13, bottom: 13, left: 8, right: 20), - child: TextButton( - onPressed: () { - Navigator.of(context) - .pushNamed(WebMessageScreen.routeName); - }, - child: Text( - 'Inbox', - style: TextStyle( - color: Colors.grey.shade400, - fontSize: 15, - fontWeight: FontWeight.bold), - )), - ), - Padding( - padding: EdgeInsets.only( - top: 13, bottom: 13, left: 8, right: 20), - child: TextButton( - onPressed: () { - Navigator.of(context) - .pushNamed(SentMessage.routeName); - }, - child: Text( - 'Sent', - style: TextStyle( - color: Colors.grey.shade400, - fontSize: 15, - fontWeight: FontWeight.bold), - )), - ), - ], - ), - ), - Container( - color: Colors.blue.shade800, - child: Row( - children: [ - Padding( - padding: EdgeInsets.only( - top: 10, bottom: 8, left: 100, right: 20), - child: TextButton( - onPressed: () { - Navigator.of(context) - .pushNamed(AllMessageScreen.routeName); - }, - child: Text('All')), - ), - Padding( - padding: - EdgeInsets.only(top: 10, bottom: 8, left: 8, right: 20), - child: TextButton( - onPressed: () { - Navigator.of(context) - .pushNamed(SentMessage.routeName); - }, - child: Text('Unread')), - ), - Padding( - padding: - EdgeInsets.only(top: 10, bottom: 8, left: 8, right: 20), - child: TextButton( - onPressed: () { - Navigator.of(context) - .pushNamed(WebMessageScreen.routeName); - }, - child: Text('Messages')), - ), - ], - ), - ), - Container( - color: Colors.indigo[50], - margin: EdgeInsets.only(left: 10.w, right: 10.w, top: 10), - child: Container( - color: Colors.indigo[50], - child: Column( - children: [ - ListView.builder( - physics: const ClampingScrollPhysics(), - shrinkWrap: true, - itemBuilder: (context, index) { - String key = messageShow.keys.elementAt(index); - final message = messageShow[key]!.last; - return Container( - padding: EdgeInsets.only(top: 8), + ? const LoadingReddit() + : (messageShow.isEmpty) + ? Container( + color: Colors.grey[100], + child: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Image.asset('assets/images/emptyNotification.png'), + Text( + 'Wow,such empty', + style: TextStyle( + color: Colors.grey.shade500, + fontSize: 13, + fontWeight: FontWeight.w300), + ), + ], + )), + ) + : SingleChildScrollView( + scrollDirection: Axis.vertical, + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + FirstNavBar(), + SecondNavBar(), + Container( + color: Colors.indigo[50], + margin: + EdgeInsets.only(left: 10.w, right: 10.w, top: 10), + child: Container( + color: Colors.indigo[50], child: Column( children: [ - Row( - children: [ - //ALways will be toUsername - Padding( - padding: const EdgeInsets.only( - left: 8, right: 8), - child: OutlinedButton( - style: OutlinedButton.styleFrom( - padding: EdgeInsets.only( - left: 7, right: 7), - side: - BorderSide(color: Colors.black26), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.all( - Radius.circular(10), - )), - ), - //style: , - onPressed: () {}, - child: Text( - message.toUsername!, - style: TextStyle( - color: Colors.blue.shade300), - )), - ), - //subject of message - Text(message.subjectText!), - ], - ), - Padding( - padding: const EdgeInsets.only(left: 18), - child: ListView.builder( - physics: const ClampingScrollPhysics(), - shrinkWrap: true, - itemBuilder: (context, index) { - return Column( - mainAxisAlignment: - MainAxisAlignment.start, - crossAxisAlignment: - CrossAxisAlignment.start, + ListView.builder( + physics: const ClampingScrollPhysics(), + shrinkWrap: true, + itemBuilder: (context, index) { + String key = + messageShow.keys.elementAt(index); + final message = messageShow[key]!.last; + return Container( + padding: EdgeInsets.only(top: 8), + child: Column( children: [ Row( children: [ + //ALways will be toUsername Padding( padding: const EdgeInsets.only( - left: 8), - child: Text('from '), + left: 8, right: 8), + child: OutlinedButton( + style: + OutlinedButton.styleFrom( + padding: EdgeInsets.only( + left: 7, right: 7), + side: BorderSide( + color: Colors.black26), + shape: + RoundedRectangleBorder( + borderRadius: + BorderRadius + .all( + Radius.circular(10), + )), + ), + //style: , + onPressed: () {}, + child: Text( + 'u/${message.toUsername!}', + style: TextStyle( + color: Colors + .blue.shade300), + )), ), - TextButton( - style: TextButton.styleFrom( - padding: EdgeInsets.zero, - ), - onPressed: () {}, - child: Text( - messageShow[key]![index].fromUsername!, - style: TextStyle( - color: Colors.blue), - )), - Text(' sent ${getTimeOfNotification(messageShow[key]![index].createdAt!)}'), + //subject of message + Text(message.subjectText!), ], ), Padding( - padding: const EdgeInsets.all(8.0), - child: Text( - messageShow[key]![index].subjectText!), - ), - Row( - children: [ - TextButton( - onPressed: () {}, - child: Text('Reply', - style: TextStyle( - color: Colors.grey))), - TextButton( - onPressed: () {}, - child: Text('Delete', - style: TextStyle( - color: Colors.grey))), - TextButton( - onPressed: () {}, - child: Text('BlockUser', - style: TextStyle( - color: Colors.grey))), - ], + padding: + const EdgeInsets.only(left: 18), + child: ListView.builder( + physics: + const ClampingScrollPhysics(), + shrinkWrap: true, + itemBuilder: (context, index) { + return Column( + mainAxisAlignment: + MainAxisAlignment.start, + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Row( + children: [ + Padding( + padding: + const EdgeInsets + .only(left: 8), + child: Text( + (messageShow[key]![ + index] + .i == + 0) + ? 'from ' + : 'to '), + ), + TextButton( + style: TextButton + .styleFrom( + padding: + EdgeInsets.zero, + ), + onPressed: () { + Navigator.of(context).pushNamed( + OthersProfileScreen + .routeName, + arguments: messageShow[ + key]![ + index] + .fromUsername!); + }, + child: Text( + 'u/${messageShow[key]![index].fromUsername!}', + style: TextStyle( + color: Colors + .blue), + )), + Text( + ' sent ${getTimeOfNotification(messageShow[key]![index].createdAt!)}'), + ], + ), + Padding( + padding: + const EdgeInsets.all( + 8.0), + child: Text( + messageShow[key]![index] + .subjectText!), + ), + if (messageShow[key]![index] + .type == + 'subredditModeratorInvite') + TextButton( + onPressed: () { + _acceptModeration( + messageShow[key]![ + index] + .subredditName); + }, + child: Text( + 'Click to accept', + style: TextStyle( + color: Colors.blue, + ), + )), + Row( + children: [ + TextButton( + onPressed: () { + _deleteMessage( + messageShow[key]![ + index] + .sId); + }, + child: Text('Delete', + style: TextStyle( + color: Colors + .grey))), + TextButton( + onPressed: () { + _blockUser(messageShow[ + key]![index] + .toUsername); + }, + child: Text( + 'BlockUser', + style: TextStyle( + color: Colors + .grey))), + ], + ), + Divider( + color: Colors.transparent, + ) + ], + ); + }, + itemCount: messageShow[key]!.length, + ), ), Divider( - color: Colors.transparent, - ) + color: Colors.grey, + ), ], - ); - }, - itemCount: messageShow[key]!.length, - ), - ), - Divider( - color: Colors.grey, + ), + ); + }, + itemCount: messageShow.length, ), ], ), - ); - }, - itemCount: messageShow.length, - ), - ], + ), + ), + ], + ), ), - ), - ), - ], - ), - ), ); } } diff --git a/lib/messages/screens/web_new_message_screen.dart b/lib/messages/screens/web_new_message_screen.dart index 0b284458..6f79efe4 100644 --- a/lib/messages/screens/web_new_message_screen.dart +++ b/lib/messages/screens/web_new_message_screen.dart @@ -1,240 +1,209 @@ - import 'package:flutter/material.dart'; +import 'package:post/messages/widgets/first_nav_bar.dart'; +import 'package:post/networks/const_endpoint_data.dart'; +import 'package:provider/provider.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import '../provider/message_provider.dart'; import 'web_message_screen.dart'; import 'web_sent_message.dart'; class WebNewMessageScreen extends StatefulWidget { - WebNewMessageScreen({super.key, this.userName}); + WebNewMessageScreen({super.key}); static const routeName = '/web-new-message-screen'; - String? userName; + //String? userName; @override State createState() => _WebNewMessageScreenState(); } class _WebNewMessageScreenState extends State { + String? sentUsername; + String? toSendUsername; + String? message; + String? subject; + bool userNameAvailable = false; + bool subjectAvailable = false; + bool messageAvailable = false; + bool _iselected = false; + //Parameters userName ==> whom to send to the message + // subject ==> Message related to which topic + // message ==> The main message you want to send + _saveNewMessage(username, subject, message) { + Provider.of(context, listen: false).createMessage( + {'to': username, 'text': message, 'subject': subject}, + context).then((value) { + Navigator.of(context).pushNamed(WebMessageScreen.routeName); + }); + } + + //Function to get user name of logged in user + Future getUserName() async { + final prefs = await SharedPreferences.getInstance(); + sentUsername = 'u/${prefs.getString('userName') as String}'; + } + @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Hiiiii')), - body: Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Container( - color: Colors.blue.shade800, - child: Row( - children: [ - Padding( - padding: - EdgeInsets.only(top: 13, bottom: 13, left: 10, right: 20), - child: TextButton( - onPressed: () { - Navigator.of(context) - .pushNamed(WebNewMessageScreen.routeName); - }, - child: Text( - 'Send a Private Message', - style: TextStyle( - color: Colors.grey.shade400, - fontSize: 15, - fontWeight: FontWeight.bold), - )), - ), - Padding( - padding: - EdgeInsets.only(top: 13, bottom: 13, left: 8, right: 20), - child: TextButton( - onPressed: () { - Navigator.of(context) - .pushNamed(WebMessageScreen.routeName); - }, - child: Text( - 'Inbox', - style: TextStyle( - color: Colors.grey.shade400, - fontSize: 15, - fontWeight: FontWeight.bold), - )), - ), - Padding( - padding: - EdgeInsets.only(top: 13, bottom: 13, left: 8, right: 20), - child: TextButton( - onPressed: () { - Navigator.of(context).pushNamed(SentMessage.routeName); - }, - child: Text( - 'Sent', - style: TextStyle( - color: Colors.grey.shade400, - fontSize: 15, - fontWeight: FontWeight.bold), - )), - ), - ], - ), - ), - Container( - margin: EdgeInsets.all(25), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.start, - children: [ - Padding( - padding: const EdgeInsets.all(8.0), - child: Text( - 'Send a Private Message', - style: TextStyle(fontSize: 25), - ), - ), - Padding( - padding: const EdgeInsets.all(8.0), - child: Text('from'), - ), - SizedBox( - width: MediaQuery.of(context).size.width * 0.4, - child: TextFormField( - enabled: false, - initialValue: widget.userName, - textAlignVertical: TextAlignVertical.center, - cursorColor: Colors.black, - onChanged: (value) { - // print(value); - // username = value; - // _changeStateOfUserName(); - }, - decoration: const InputDecoration( - labelStyle: TextStyle(color: Colors.black54), - isDense: true, - contentPadding: EdgeInsets.only( - bottom: 3, left: 5, top: 25, right: 5), - floatingLabelBehavior: FloatingLabelBehavior.never, - border: OutlineInputBorder( - borderSide: BorderSide(color: Colors.black)), - focusedBorder: OutlineInputBorder( - borderSide: BorderSide(color: Colors.black)), - enabledBorder: OutlineInputBorder( - borderSide: BorderSide(color: Colors.black)), - ), - ), - ), - Padding( - padding: const EdgeInsets.all(8.0), - child: Text('to'), - ), - SizedBox( - width: MediaQuery.of(context).size.width * 0.4, - child: TextFormField( - // / enabled: (widget.userName != null) ? false : true, - // initialValue: - // (widget.userName != null) ? widget.userName : null, - textAlignVertical: TextAlignVertical.center, - cursorColor: Colors.black, - onChanged: (value) { - // print(value); - // username = value; - // _changeStateOfUserName(); - }, - decoration: const InputDecoration( - labelStyle: TextStyle(color: Colors.black54), - isDense: true, - contentPadding: EdgeInsets.only( - bottom: 3, left: 5, top: 25, right: 5), - floatingLabelBehavior: FloatingLabelBehavior.never, - border: OutlineInputBorder( - borderSide: BorderSide(color: Colors.black)), - focusedBorder: OutlineInputBorder( - borderSide: BorderSide(color: Colors.black)), - enabledBorder: OutlineInputBorder( - borderSide: BorderSide(color: Colors.black)), - ), - ), - ), - Padding( - padding: const EdgeInsets.all(8.0), - child: Text('Subject'), - ), - SizedBox( - width: MediaQuery.of(context).size.width * 0.4, - child: TextFormField( - // / enabled: (widget.userName != null) ? false : true, - // initialValue: - // (widget.userName != null) ? widget.userName : null, - textAlignVertical: TextAlignVertical.center, - cursorColor: Colors.black, - onChanged: (value) { - // print(value); - // username = value; - // _changeStateOfUserName(); - }, - decoration: const InputDecoration( - labelStyle: TextStyle(color: Colors.black54), - isDense: true, - contentPadding: EdgeInsets.only( - bottom: 3, left: 5, top: 25, right: 5), - floatingLabelBehavior: FloatingLabelBehavior.never, - border: OutlineInputBorder( - borderSide: BorderSide(color: Colors.black)), - focusedBorder: OutlineInputBorder( - borderSide: BorderSide(color: Colors.black)), - enabledBorder: OutlineInputBorder( - borderSide: BorderSide(color: Colors.black)), - ), - ), - ), - Padding( - padding: const EdgeInsets.all(8.0), - child: Text('Message'), - ), - SizedBox( - width: MediaQuery.of(context).size.width * 0.4, - height: 100, - child: SingleChildScrollView( - scrollDirection: Axis.vertical, - child: TextFormField( - textInputAction: TextInputAction.newline, - // keyboardType: TextInputType.multiline, - maxLines: null, - // / enabled: (widget.userName != null) ? false : true, - // initialValue: - // (widget.userName != null) ? widget.userName : null, - textAlignVertical: TextAlignVertical.center, - cursorColor: Colors.black, - onChanged: (value) { - // print(value); - // username = value; - // _changeStateOfUserName(); - }, - decoration: const InputDecoration( - labelStyle: TextStyle(color: Colors.black54), - isDense: true, - contentPadding: EdgeInsets.only( - bottom: 3, left: 5, top: 25, right: 5), - floatingLabelBehavior: FloatingLabelBehavior.never, - border: OutlineInputBorder( - borderSide: BorderSide(color: Colors.black)), - focusedBorder: OutlineInputBorder( - borderSide: BorderSide(color: Colors.black)), - enabledBorder: OutlineInputBorder( - borderSide: BorderSide(color: Colors.black)), - ), + body: FutureBuilder( + future: getUserName(), + builder: (context, snapshot) => (snapshot.connectionState == + ConnectionState.done) + ? Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + FirstNavBar(), + Container( + margin: const EdgeInsets.all(25), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + const Padding( + padding: EdgeInsets.all(8.0), + child: Text( + 'Send a Private Message', + style: TextStyle(fontSize: 25), + ), + ), + const Padding( + padding: EdgeInsets.all(8.0), + child: Text('from'), + ), + SizedBox( + width: MediaQuery.of(context).size.width * 0.4, + child: TextFormField( + enabled: false, + initialValue: sentUsername, + textAlignVertical: TextAlignVertical.center, + cursorColor: Colors.black, + decoration: const InputDecoration( + labelStyle: TextStyle(color: Colors.black54), + isDense: true, + contentPadding: EdgeInsets.only( + bottom: 3, left: 5, top: 25, right: 5), + floatingLabelBehavior: + FloatingLabelBehavior.never, + border: OutlineInputBorder( + borderSide: BorderSide(color: Colors.black)), + focusedBorder: OutlineInputBorder( + borderSide: BorderSide(color: Colors.black)), + enabledBorder: OutlineInputBorder( + borderSide: BorderSide(color: Colors.black)), + ), + ), + ), + const Padding( + padding: EdgeInsets.all(8.0), + child: Text('to'), + ), + SizedBox( + width: MediaQuery.of(context).size.width * 0.4, + child: TextFormField( + textAlignVertical: TextAlignVertical.center, + cursorColor: Colors.black, + onChanged: (value) { + toSendUsername = value; + }, + decoration: const InputDecoration( + labelStyle: TextStyle(color: Colors.black54), + isDense: true, + contentPadding: EdgeInsets.only( + bottom: 3, left: 5, top: 25, right: 5), + floatingLabelBehavior: + FloatingLabelBehavior.never, + border: OutlineInputBorder( + borderSide: BorderSide(color: Colors.black)), + focusedBorder: OutlineInputBorder( + borderSide: BorderSide(color: Colors.black)), + enabledBorder: OutlineInputBorder( + borderSide: BorderSide(color: Colors.black)), + ), + ), + ), + const Padding( + padding: EdgeInsets.all(8.0), + child: Text('Subject'), + ), + SizedBox( + width: MediaQuery.of(context).size.width * 0.4, + child: TextFormField( + textAlignVertical: TextAlignVertical.center, + cursorColor: Colors.black, + onChanged: (value) { + subject = value; + }, + decoration: const InputDecoration( + labelStyle: TextStyle(color: Colors.black54), + isDense: true, + contentPadding: EdgeInsets.only( + bottom: 3, left: 5, top: 25, right: 5), + floatingLabelBehavior: + FloatingLabelBehavior.never, + border: OutlineInputBorder( + borderSide: BorderSide(color: Colors.black)), + focusedBorder: OutlineInputBorder( + borderSide: BorderSide(color: Colors.black)), + enabledBorder: OutlineInputBorder( + borderSide: BorderSide(color: Colors.black)), + ), + ), + ), + const Padding( + padding: EdgeInsets.all(8.0), + child: Text('Message'), + ), + SizedBox( + width: MediaQuery.of(context).size.width * 0.4, + height: 100, + child: TextFormField( + textInputAction: TextInputAction.newline, + maxLines: null, + textAlignVertical: TextAlignVertical.center, + cursorColor: Colors.black, + onChanged: (value) { + message = value; + }, + decoration: const InputDecoration( + contentPadding: EdgeInsets.only( + bottom: 3, left: 5, top: 25, right: 5), + floatingLabelBehavior: + FloatingLabelBehavior.never, + border: OutlineInputBorder( + borderSide: BorderSide(color: Colors.black)), + focusedBorder: OutlineInputBorder( + borderSide: BorderSide(color: Colors.black)), + enabledBorder: OutlineInputBorder( + borderSide: BorderSide(color: Colors.black)), + ), + ), + ), + ElevatedButton( + onPressed: () { + if (toSendUsername!.isNotEmpty && + subject!.isNotEmpty && + message!.isNotEmpty) { + _saveNewMessage(toSendUsername, subject, message); + } + }, + style: ElevatedButton.styleFrom(primary: Colors.blue), + child: const Text( + 'Send', + style: TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold), + ), + ) + ], ), - ), - ), - ElevatedButton( - onPressed: () {}, - child: Text( - 'Send', - style: TextStyle( - color: Colors.white, fontWeight: FontWeight.bold), - ), - style: ElevatedButton.styleFrom(primary: Colors.blue), - ) - ], - ), - ) - ], + ) + ], + ) + : Container( + child: Text('Still loading'), + ), ), ); } diff --git a/lib/messages/screens/web_sent_message.dart b/lib/messages/screens/web_sent_message.dart index f69093d7..3c274b8e 100644 --- a/lib/messages/screens/web_sent_message.dart +++ b/lib/messages/screens/web_sent_message.dart @@ -1,6 +1,13 @@ import 'package:flutter/material.dart'; +import 'package:post/messages/widgets/first_nav_bar.dart'; +import 'package:post/messages/widgets/second_nav_bar.dart'; +import 'package:provider/provider.dart'; import 'package:responsive_sizer/responsive_sizer.dart'; +import '../../other_profile/screens/others_profile_screen.dart'; +import '../../widgets/loading_reddit.dart'; +import '../models/user_message.dart'; +import '../provider/message_provider.dart'; import 'web_message_screen.dart'; import 'web_new_message_screen.dart'; @@ -13,156 +20,221 @@ class SentMessage extends StatefulWidget { } class _SentMessageState extends State { + //Parse String to Date Time and get actual time + //Input String + //Return type string + String getTimeOfNotification(date) { + String howOld; + final difference = DateTime.now().difference(DateTime.parse(date)); + if (difference.inDays >= 365) { + howOld = '${difference.inDays ~/ 365}y'; + } else if (difference.inDays >= 30) { + howOld = '${difference.inDays ~/ 30}mo'; + } else if (difference.inDays >= 1) { + howOld = '${difference.inDays}d'; + } else if (difference.inMinutes >= 60) { + howOld = '${difference.inHours}h'; + } else if (difference.inSeconds >= 60) { + howOld = '${difference.inMinutes}m'; + } else { + howOld = '${difference.inSeconds}s'; + } + return howOld; + } + + bool _isInit = true; + bool returned = false; + bool tried = false; + + List sentMessage = []; + void didChangeDependencies() async { + if (_isInit) { + setState(() { + returned = false; + }); + // _updateCount(); + await Provider.of(context, listen: false) + .getSentMessages(context, 0, 10) + .then((value) { + sentMessage = + Provider.of(context, listen: false).sentMessage; + setState(() { + returned = true; + }); + }); + } + + _isInit = false; + + super.didChangeDependencies(); + } + //Called when user accepts to be moderator in the subreddit + //Parameter subreddit name that user will accept invitation in + _acceptModeration(subredditName) { + Provider.of(context, listen: false) + .acceptSubredditInvite(context, subredditName) + .then((value) { + Navigator.of(context).pop(); + }); + } + //deleteMessage => parameter take id of required messaged to be deleted + _deleteMessage(id) async { + await Provider.of(context, listen: false) + .deleteMessage(context, id); + } + //blockUser => parameter take username of required user to be blocked + _blockUser(userName) async { + await Provider.of(context, listen: false) + .blockUser(context, userName); + } + @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Hiiiii')), - body: SingleChildScrollView( - scrollDirection: Axis.vertical, - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Container( - color: Colors.blue.shade800, - child: Row( - children: [ - Padding( - padding: EdgeInsets.only( - top: 13, bottom: 13, left: 100, right: 20), - child: TextButton( - onPressed: () { - Navigator.of(context) - .pushNamed(WebNewMessageScreen.routeName); - }, - child: Text( - 'Send a Private Message', - style: TextStyle( - color: Colors.grey.shade400, - fontSize: 15, - fontWeight: FontWeight.bold), - )), - ), - Padding( - padding: EdgeInsets.only( - top: 13, bottom: 13, left: 8, right: 20), - child: TextButton( - onPressed: () { - Navigator.of(context) - .pushNamed(WebMessageScreen.routeName); - }, - child: Text( - 'Inbox', - style: TextStyle( - color: Colors.grey.shade400, - fontSize: 15, - fontWeight: FontWeight.bold), - )), - ), - Padding( - padding: EdgeInsets.only( - top: 13, bottom: 13, left: 8, right: 20), - child: TextButton( - onPressed: () { - Navigator.of(context) - .pushNamed(SentMessage.routeName); - }, - child: Text( - 'Sent', - style: TextStyle( - color: Colors.grey.shade400, - fontSize: 15, - fontWeight: FontWeight.bold), - )), - ), - ], - ), - ), - Container( - color: Colors.indigo[50], - margin: EdgeInsets.only(left: 10.w, right: 10.w, top: 10), - child: Container( - color: Colors.indigo[50], - child: Column( - children: [ - ListView.builder( - physics: const ClampingScrollPhysics(), - shrinkWrap: true, - itemBuilder: (context, index) { - return Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - ////subjectccccccccccccccccc - Padding( - padding: const EdgeInsets.only(left: 10, top: 5), - child: Text('Jooko:'), - ), - Row( - children: [ - Padding( - padding: const EdgeInsets.only(left: 18), - child: Text('from '), - ), - TextButton( - style: TextButton.styleFrom( - padding: EdgeInsets.zero, - ), - onPressed: () {}, - child: Text( - 'Eman Shahda', - style: TextStyle(color: Colors.blue), - )), - Text(' sent 3 days ago'), - ], - ), - Padding( - padding: const EdgeInsets.only( - left: 23, right: 8, bottom: 8, top: 8), - child: Text( - 'hiiiiiiiii bgfsvbhszdfkz hadsafjdnkd bhjknkamkfnk arhfbagjarehk dhfjskfa ajfbfrak dfjsk bfdjksml,;htgfjkdl;sp gfdjksl; mrtgwirjawelf gsduhskerjgesl sdgjhrkgl'), - ), - Padding( - padding: const EdgeInsets.only( - left: 16, right: 8, bottom: 8, top: 8), - child: Row( - children: [ - TextButton( - onPressed: () {}, - child: Text('Reply', - style: - TextStyle(color: Colors.grey))), - TextButton( - onPressed: () {}, - child: Text('Delete', - style: - TextStyle(color: Colors.grey))), - TextButton( - onPressed: () {}, - child: Text('BlockUser', - style: - TextStyle(color: Colors.grey))), - ], + body: (!returned) + ? const LoadingReddit() + : (sentMessage.isEmpty) + ? Container( + color: Colors.grey[100], + child: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Image.asset('assets/images/emptyNotification.png'), + Text( + 'Wow,such empty', + style: TextStyle( + color: Colors.grey.shade500, + fontSize: 13, + fontWeight: FontWeight.w300), + ), + ], + )), + ) + : SingleChildScrollView( + scrollDirection: Axis.vertical, + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + FirstNavBar(), + Container( + color: Colors.indigo[50], + margin: + EdgeInsets.only(left: 10.w, right: 10.w, top: 10), + child: Container( + color: Colors.indigo[50], + child: Column( + children: [ + ListView.builder( + physics: const ClampingScrollPhysics(), + shrinkWrap: true, + itemBuilder: (context, index) { + return Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + ////subjectccccccccccccccccc + Padding( + padding: const EdgeInsets.only( + left: 20, top: 10), + child: Text( + sentMessage[index].subjectText!,style: TextStyle( + fontWeight: FontWeight.bold),), + ), + Row( + children: [ + Padding( + padding: + const EdgeInsets.only(left: 18), + child: Text('from '), + ), + TextButton( + style: TextButton.styleFrom( + padding: EdgeInsets.zero, + ), + onPressed: () { + Navigator.of(context).pushNamed( + OthersProfileScreen + .routeName, + arguments: sentMessage[index].toUsername + ); + }, + child: Text( + 'u/${sentMessage[index].toUsername!}', + style: TextStyle( + color: Colors.blue), + )), + Text( + ' sent ${getTimeOfNotification(sentMessage[index].createdAt!)}'), + ], + ), + Padding( + padding: const EdgeInsets.only( + left: 25, + right: 8, + bottom: 8, + top: 8), + child: Text(sentMessage[index].text!), + ), + if (sentMessage[index].type == + 'subredditModeratorInvite') + TextButton( + onPressed: () { + _acceptModeration( + sentMessage[index] + .subredditName); + }, + child: Text( + 'Click to accept', + style: TextStyle( + color: Colors.blue, + ), + )), + Padding( + padding: const EdgeInsets.only( + left: 19, + right: 8, + bottom: 8, + top: 8), + child: Row( + children: [ + TextButton( + onPressed: () { + _deleteMessage( + sentMessage[index].sId); + }, + child: Text('Delete', + style: TextStyle( + color: Colors.grey))), + TextButton( + onPressed: () { + _blockUser(sentMessage[index] + .toUsername); + }, + child: Text('BlockUser', + style: TextStyle( + color: Colors.grey))), + ], + ), + ), + Divider( + color: Colors.grey, + ) + ], + ); + }, + itemCount: sentMessage.length, ), - ), - Divider( - color: Colors.transparent, - ) - ], - ); - }, - itemCount: 5, - ), - Divider( - color: Colors.grey, - ), - ], + ], + ), + ), + ), + ], + ), ), - ), - ), - ], - ), - ), ); } } diff --git a/lib/messages/widgets/first_nav_bar.dart b/lib/messages/widgets/first_nav_bar.dart new file mode 100644 index 00000000..763cbf62 --- /dev/null +++ b/lib/messages/widgets/first_nav_bar.dart @@ -0,0 +1,71 @@ +import 'package:flutter/material.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +import '../screens/web_message_screen.dart'; +import '../screens/web_new_message_screen.dart'; +import '../screens/web_sent_message.dart'; +class FirstNavBar extends StatelessWidget { + FirstNavBar({ + Key? key, + }) : super(key: key); + + + @override + Widget build(BuildContext context) { + return Container( + color: Colors.blue.shade800, + child: Row( + children: [ + Padding( + padding: EdgeInsets.only( + top: 13, bottom: 13, left: 100, right: 20), + child: TextButton( + onPressed: () { + Navigator.of(context) + .popAndPushNamed(WebNewMessageScreen.routeName); + }, + child: Text( + 'Send a Private Message', + style: TextStyle( + color: Colors.grey.shade400, + fontSize: 15, + fontWeight: FontWeight.bold), + )), + ), + Padding( + padding: EdgeInsets.only( + top: 13, bottom: 13, left: 8, right: 20), + child: TextButton( + onPressed: () { + Navigator.of(context) + .popAndPushNamed(WebMessageScreen.routeName); + }, + child: Text( + 'Inbox', + style: TextStyle( + color: Colors.grey.shade400, + fontSize: 15, + fontWeight: FontWeight.bold), + )), + ), + Padding( + padding: EdgeInsets.only( + top: 13, bottom: 13, left: 8, right: 20), + child: TextButton( + onPressed: () { + Navigator.of(context) + .popAndPushNamed(SentMessage.routeName); + }, + child: Text( + 'Sent', + style: TextStyle( + color: Colors.grey.shade400, + fontSize: 15, + fontWeight: FontWeight.bold), + )), + ), + ], + ), + ); + } +} \ No newline at end of file diff --git a/lib/messages/widgets/second_nav_bar.dart b/lib/messages/widgets/second_nav_bar.dart new file mode 100644 index 00000000..510a7fc7 --- /dev/null +++ b/lib/messages/widgets/second_nav_bar.dart @@ -0,0 +1,53 @@ +import 'package:flutter/material.dart'; + +import '../screens/web_message_all_screen.dart'; +import '../screens/web_message_screen.dart'; +import '../screens/web_new_message_screen.dart'; +import '../screens/web_sent_message.dart'; +import '../screens/unread_message_screen.dart'; +class SecondNavBar extends StatelessWidget { + const SecondNavBar({ + Key? key, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Container( + color: Colors.blue.shade800, + child: Row( + children: [ + Padding( + padding: EdgeInsets.only( + top: 10, bottom: 8, left: 100, right: 20), + child: TextButton( + onPressed: () { + Navigator.of(context) + .popAndPushNamed(AllMessageScreen.routeName); + }, + child: Text('All')), + ), + Padding( + padding: + EdgeInsets.only(top: 10, bottom: 8, left: 8, right: 20), + child: TextButton( + onPressed: () { + Navigator.of(context) + .popAndPushNamed(UnreadMessageScreen.routeName); + }, + child: Text('Unread')), + ), + Padding( + padding: + const EdgeInsets.only(top: 10, bottom: 8, left: 8, right: 20), + child: TextButton( + onPressed: () { + Navigator.of(context) + .popAndPushNamed(WebMessageScreen.routeName); + }, + child: Text('Messages')), + ), + ], + ), + ); + } +} \ No newline at end of file diff --git a/lib/models/subreddit_data.dart b/lib/models/subreddit_data.dart index ec1c08b2..1f27ea86 100644 --- a/lib/models/subreddit_data.dart +++ b/lib/models/subreddit_data.dart @@ -1,4 +1,4 @@ -import '../../models/subreddit_about _rules.dart'; +import './subreddit_about _rules.dart'; class SubredditData { //String? id; diff --git a/lib/moderated_subreddit/screens/moderated_subreddit_screen.dart b/lib/moderated_subreddit/screens/moderated_subreddit_screen.dart index 4853b79c..398f49a6 100644 --- a/lib/moderated_subreddit/screens/moderated_subreddit_screen.dart +++ b/lib/moderated_subreddit/screens/moderated_subreddit_screen.dart @@ -3,6 +3,7 @@ import 'package:get/get.dart'; import 'package:responsive_sizer/responsive_sizer.dart'; import 'package:provider/provider.dart'; import 'package:flutter/foundation.dart' show kIsWeb; +import 'package:get/get.dart'; import '../../home/controller/home_controller.dart'; import '../widgets/moderated_subreddit_web.dart'; import '../../models/subreddit_data.dart'; @@ -10,7 +11,9 @@ import '../../widgets/loading_reddit.dart'; import '../widgets/moderated_subreddit_app.dart'; import '../providers/moderated_subreddit_provider.dart'; import '../../home/widgets/end_drawer.dart'; - +import '../../home/controller/home_controller.dart'; +import '../../createpost/controllers/posts_controllers.dart'; +import '../../home/widgets/custom_upper_bar.dart'; class ModeratedSubredditScreen extends StatefulWidget { static const routeName = '/moderatedsubreddit'; @override @@ -82,12 +85,20 @@ SubredditData? loadedSubreddit; _isInit = false; super.didChangeDependencies(); } - + final HomeController controllerHome = Get.put( + HomeController(), + ); + final PostController controllerForPost = Get.put( + PostController(), + ); @override Widget build(BuildContext context) { final GlobalKey _scaffoldKey = GlobalKey(); return Scaffold( key: _scaffoldKey, + appBar: ( kIsWeb)?PreferredSize( + preferredSize: Size(700, 60), + child: UpBar(controller: controllerHome, controllerForCreatePost: controllerForPost,)):null, body: _isLoading ? LoadingReddit() : kIsWeb diff --git a/lib/moderated_subreddit/widgets/moderated_subreddit_card_information_web.dart b/lib/moderated_subreddit/widgets/moderated_subreddit_card_information_web.dart index 7899a78c..444007d4 100644 --- a/lib/moderated_subreddit/widgets/moderated_subreddit_card_information_web.dart +++ b/lib/moderated_subreddit/widgets/moderated_subreddit_card_information_web.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import 'package:responsive_sizer/responsive_sizer.dart'; import 'package:intl/intl.dart'; import '../../models/subreddit_data.dart'; -import '../../moderation_settings/screens/traffic_state.dart'; +import '../../moderation_settings/screens/moderator_tools_screen.dart'; import '../providers/moderated_subreddit_provider.dart'; import 'package:provider/provider.dart'; class ModeratedSubredditCardInformationWeb extends StatefulWidget { @@ -48,8 +48,9 @@ class _ModeratedSubredditCardInformationWebState ), ElevatedButton( onPressed: () { - Navigator.pushNamed(context, TraficState.routeName, - arguments: widget.loadedSubreddit!.name); + Navigator.of(context).pushNamed( + ModeratorTools.routeName, + arguments: widget.loadedSubreddit!.name); }, style: ButtonStyle( backgroundColor: diff --git a/lib/moderated_subreddit/widgets/moderated_subreddit_web.dart b/lib/moderated_subreddit/widgets/moderated_subreddit_web.dart index e4080af3..3f27da71 100644 --- a/lib/moderated_subreddit/widgets/moderated_subreddit_web.dart +++ b/lib/moderated_subreddit/widgets/moderated_subreddit_web.dart @@ -7,6 +7,7 @@ import '../../widgets/subreddit_join_button_web.dart'; import '../../models/subreddit_data.dart'; import '../widgets/moderated_subreddite_post_web.dart'; import '../../widgets/back_to_button.dart'; +import '../screens/moderated_subreddit_screen.dart'; class ModeratedSubredditWeb extends StatelessWidget { String userName; @@ -28,8 +29,22 @@ class ModeratedSubredditWeb extends StatelessWidget { final GlobalKey _scaffoldKey = GlobalKey(); return Scaffold( key: _scaffoldKey, - floatingActionButton: - BackToTopButton(scrollController: scrollController), + floatingActionButtonLocation: FloatingActionButtonLocation.miniEndFloat, + floatingActionButton: Padding( + padding: const EdgeInsetsDirectional.only(end: 320.0), + child: ElevatedButton( + onPressed: () { + Navigator.of(context).pushNamed(ModeratedSubredditScreen.routeName, + arguments:userName); + }, + child: Text( + ' Back to top ', + style: TextStyle(color: Colors.white), + ), + style: ElevatedButton.styleFrom( + shape: StadiumBorder(), backgroundColor: Colors.blue), + ), + ), body: isLoading ? const Center( child: Icon( diff --git a/lib/moderated_subreddit/widgets/position_for_moderated_subredditInfo.dart b/lib/moderated_subreddit/widgets/position_for_moderated_subredditInfo.dart index 76bac0c3..846931ad 100644 --- a/lib/moderated_subreddit/widgets/position_for_moderated_subredditInfo.dart +++ b/lib/moderated_subreddit/widgets/position_for_moderated_subredditInfo.dart @@ -3,7 +3,6 @@ import 'package:intl/intl.dart'; import 'package:post/moderation_settings/screens/moderator_tools_screen.dart'; import 'package:responsive_sizer/responsive_sizer.dart'; import '../../models/subreddit_data.dart'; - class PositionForModeratedSubredditInfo extends StatelessWidget { final SubredditData? loadedSubreddit; final String? userName; diff --git a/lib/moderation_settings/models/moderation_posts.dart b/lib/moderation_settings/models/moderation_posts.dart new file mode 100644 index 00000000..0df216cb --- /dev/null +++ b/lib/moderation_settings/models/moderation_posts.dart @@ -0,0 +1,262 @@ +class ModerationPosts { + List? data; + + ModerationPosts({this.data}); + + ModerationPosts.fromJson(Map json) { + if (json['data'] != null) { + data = []; + json['data'].forEach((v) { + data!.add(new Data.fromJson(v)); + }); + } + } + + Map toJson() { + final Map data = new Map(); + if (this.data != null) { + data['data'] = this.data!.map((v) => v.toJson()).toList(); + } + return data; + } +} + +class Data { + String? sId; + Owner? owner; + String? ownerType; + Author? author; + FlairId? flairId; + String? title; + String? kind; + String? text; + List? images; + String? createdAt; + bool? locked; + bool? isDeleted; + bool? sendReplies; + bool? nsfw; + bool? spoiler; + int? votes; + int? views; + int? commentCount; + int? shareCount; + String? suggestedSort; + bool? scheduled; + String? modState; + int? spamCount; + double? sortOnHot; + double? sortOnBest; + int? iV; + + Data( + {this.sId, + this.owner, + this.ownerType, + this.author, + this.flairId, + this.title, + this.kind, + this.text, + this.images, + this.createdAt, + this.locked, + this.isDeleted, + this.sendReplies, + this.nsfw, + this.spoiler, + this.votes, + this.views, + this.commentCount, + this.shareCount, + this.suggestedSort, + this.scheduled, + this.modState, + this.spamCount, + this.sortOnHot, + this.sortOnBest, + this.iV}); + + Data.fromJson(Map json) { + sId = json['_id']; + owner = json['owner'] != null ? new Owner.fromJson(json['owner']) : null; + ownerType = json['ownerType']; + author = + json['author'] != null ? new Author.fromJson(json['author']) : null; + flairId = + json['flairId'] != null ? new FlairId.fromJson(json['flairId']) : null; + title = json['title']; + kind = json['kind']; + text = json['text']; + images = json['images'].cast(); + createdAt = json['createdAt']; + locked = json['locked']; + isDeleted = json['isDeleted']; + sendReplies = json['sendReplies']; + nsfw = json['nsfw']; + spoiler = json['spoiler']; + votes = json['votes']; + views = json['views']; + commentCount = json['commentCount']; + shareCount = json['shareCount']; + suggestedSort = json['suggestedSort']; + scheduled = json['scheduled']; + modState = json['modState']; + spamCount = json['spamCount']; + sortOnHot = json['sortOnHot']; + sortOnBest = json['sortOnBest']; + iV = json['__v']; + } + + Map toJson() { + final Map data = new Map(); + data['_id'] = this.sId; + if (this.owner != null) { + data['owner'] = this.owner!.toJson(); + } + data['ownerType'] = this.ownerType; + if (this.author != null) { + data['author'] = this.author!.toJson(); + } + if (this.flairId != null) { + data['flairId'] = this.flairId!.toJson(); + } + data['title'] = this.title; + data['kind'] = this.kind; + data['text'] = this.text; + data['images'] = this.images; + data['createdAt'] = this.createdAt; + data['locked'] = this.locked; + data['isDeleted'] = this.isDeleted; + data['sendReplies'] = this.sendReplies; + data['nsfw'] = this.nsfw; + data['spoiler'] = this.spoiler; + data['votes'] = this.votes; + data['views'] = this.views; + data['commentCount'] = this.commentCount; + data['shareCount'] = this.shareCount; + data['suggestedSort'] = this.suggestedSort; + data['scheduled'] = this.scheduled; + data['modState'] = this.modState; + data['spamCount'] = this.spamCount; + data['sortOnHot'] = this.sortOnHot; + data['sortOnBest'] = this.sortOnBest; + data['__v'] = this.iV; + return data; + } +} + +class Owner { + String? sId; + String? fixedName; + String? icon; + List? rules; + String? backgroundImage; + + Owner( + {this.sId, this.fixedName, this.icon, this.rules, this.backgroundImage}); + + Owner.fromJson(Map json) { + sId = json['_id']; + fixedName = json['fixedName']; + icon = json['icon']; + if (json['rules'] != null) { + rules = []; + json['rules'].forEach((v) { + rules!.add(new Rules.fromJson(v)); + }); + } + backgroundImage = json['backgroundImage']; + } + + Map toJson() { + final Map data = new Map(); + data['_id'] = this.sId; + data['fixedName'] = this.fixedName; + data['icon'] = this.icon; + if (this.rules != null) { + data['rules'] = this.rules!.map((v) => v.toJson()).toList(); + } + data['backgroundImage'] = this.backgroundImage; + return data; + } +} + +class Rules { + String? createdAt; + + Rules({this.createdAt}); + + Rules.fromJson(Map json) { + createdAt = json['createdAt']; + } + + Map toJson() { + final Map data = new Map(); + data['createdAt'] = this.createdAt; + return data; + } +} + +class Author { + String? sId; + String? userName; + String? profilePicture; + String? profileBackground; + + Author( + {this.sId, this.userName, this.profilePicture, this.profileBackground}); + + Author.fromJson(Map json) { + sId = json['_id']; + userName = json['userName']; + profilePicture = json['profilePicture']; + profileBackground = json['profileBackground']; + } + + Map toJson() { + final Map data = new Map(); + data['_id'] = this.sId; + data['userName'] = this.userName; + data['profilePicture'] = this.profilePicture; + data['profileBackground'] = this.profileBackground; + return data; + } +} + +class FlairId { + String? sId; + String? text; + String? textColor; + String? backgroundColor; + String? permissions; + int? iV; + + FlairId( + {this.sId, + this.text, + this.textColor, + this.backgroundColor, + this.permissions, + this.iV}); + + FlairId.fromJson(Map json) { + sId = json['_id']; + text = json['text']; + textColor = json['textColor']; + backgroundColor = json['backgroundColor']; + permissions = json['permissions']; + iV = json['__v']; + } + + Map toJson() { + final Map data = new Map(); + data['_id'] = this.sId; + data['text'] = this.text; + data['textColor'] = this.textColor; + data['backgroundColor'] = this.backgroundColor; + data['permissions'] = this.permissions; + data['__v'] = this.iV; + return data; + } +} diff --git a/lib/moderation_settings/models/post_flair_model.dart b/lib/moderation_settings/models/post_flair_model.dart index d55fbafb..447b917d 100644 --- a/lib/moderation_settings/models/post_flair_model.dart +++ b/lib/moderation_settings/models/post_flair_model.dart @@ -3,7 +3,6 @@ class PostFlairModel { String? text; String? backgroundColor; String? textColor; - //String? modOnly; bool? deleteFlair; PostFlairModel({this.sId, this.text, this.backgroundColor, this.textColor,this.deleteFlair}); @@ -13,7 +12,6 @@ class PostFlairModel { text = json['text']; backgroundColor = json['backgroundColor']; textColor = json['textColor']; - // modOnly = json['permissions'] ?? false; } Map toJson() { @@ -21,7 +19,6 @@ class PostFlairModel { data['text'] = this.text; data['backgroundColor'] = this.backgroundColor; data['textColor'] = this.textColor; - // data['permissions'] = this.modOnly; return data; } } diff --git a/lib/moderation_settings/provider/moderation_general_data.dart b/lib/moderation_settings/provider/moderation_general_data.dart index 334b5c40..5510024a 100644 --- a/lib/moderation_settings/provider/moderation_general_data.dart +++ b/lib/moderation_settings/provider/moderation_general_data.dart @@ -3,30 +3,24 @@ import 'package:dio/dio.dart'; import 'package:flutter/material.dart'; import '../../networks/dio_client.dart'; import '../../networks/const_endpoint_data.dart'; -import '../models/moderator_tools.dart'; -import '../models/moderators.dart'; -import '../models/approved.dart'; -import '../models/banned.dart'; -import '../models/muted.dart'; import 'package:shared_preferences/shared_preferences.dart'; import '../../widgets/handle_error.dart'; -import '../models/user.dart'; -import '../provider/moderation_settings_provider.dart'; import '../models/traffic.dart'; +import '../models/moderation_posts.dart'; class ModerationGeneralDataProvider with ChangeNotifier { late Traffic dayTraffic; late Traffic weekTraffic; late Traffic monthTraffic; late Traffic yearTraffic; - + late ModerationPosts posts; bool isError = false; String errorMessage = ''; Future gettrafficData( String subRedditName, BuildContext context) async { - //If the topic changed call patch to update the community topic - subRedditName = 'khaled subreddit'; + // If the topic changed call patch to update the community topic + // subRedditName = 'khaled subreddit'; try { final path1 = '/subreddits/traffic/$subRedditName/day'; final path2 = '/subreddits/traffic/$subRedditName/week'; @@ -73,4 +67,63 @@ class ModerationGeneralDataProvider with ChangeNotifier { HandleError.handleError(error.toString(), context); } } + + Future patchPosts( + String postId, String action, BuildContext context) async { + //If the topic changed call patch to update the community topic + try { + String path = '/posts/$postId/moderate/$action'; + final prefs = await SharedPreferences.getInstance(); + DioClient.init(prefs); + subredditName = userName; + // final response = + await DioClient.patch(path: path, data: {}).then((response) { + isError = !(response.statusCode! < 300); + if (isError) { + errorMessage = json.decode(response.data)['errorMessage']; + print(isError); + print(errorMessage); + } + }); + notifyListeners(); + } on DioError catch (e) { + if (e.response!.statusCode != 404) { + HandleError.errorHandler(e, context); + } + } catch (error) { + HandleError.handleError(error.toString(), context); + } + } + + Future getPosts( + String name, String location, sortType, BuildContext context) async { + //If the topic changed call patch to update the community topic + // subRedditName = 'khaled subreddit'; + try { + final path = + '/subreddits/$name/about/posts/${location.toLowerCase()}?sort=$sortType'; + + final prefs = await SharedPreferences.getInstance(); + DioClient.init(prefs); + + final Response response = await DioClient.get( + path: path, + ); + + isError = !(response.statusCode! < 310); + if (isError) { + errorMessage = json.decode(response.data)['errorMessage']; + } else { + posts = ModerationPosts.fromJson(response.data); + } + + notifyListeners(); + } on DioError catch (e) { + if (e.response!.statusCode != 404) { + HandleError.errorHandler(e, context); + } + } catch (error) { + HandleError.handleError(error.toString(), context); + } + } } diff --git a/lib/moderation_settings/provider/post_flair_provider.dart b/lib/moderation_settings/provider/post_flair_provider.dart index 0f548bd1..dd52e064 100644 --- a/lib/moderation_settings/provider/post_flair_provider.dart +++ b/lib/moderation_settings/provider/post_flair_provider.dart @@ -12,25 +12,18 @@ class PostFlairProvider with ChangeNotifier { List get flairList { return [..._flairsList]; } - -//http://localhost:8000/api/v1/subreddits/{subredditName}/flairs Future addNewFlair(subredditName, data, context) async { try { final prefs = await SharedPreferences.getInstance(); DioClient.init(prefs); final PostFlairModel post = data; - print(post.toJson()); - // post.modOnly = ; await DioClient.post( path: '/subreddits/$subredditName/flair', data: post.toJson()); _flairsList.add(data); notifyListeners(); } on DioError catch (e) { - //print(e.error.toString()); - print(e.error.toString()); HandleError.errorHandler(e, context); } catch (error) { - //print(error.toString()); HandleError.handleError(error.toString(), context); } } @@ -44,7 +37,6 @@ class PostFlairProvider with ChangeNotifier { .then((value) { print(value); value.data['data'].forEach((element) { - print(element); _flairsList.add(PostFlairModel.fromJson(element)); }); }); @@ -56,22 +48,14 @@ class PostFlairProvider with ChangeNotifier { } } -//http://localhost:8000/api/v1/subreddits/{subredditName}/flairs/{flairId} Future editFlair(subredditName, context, flairId, data) async { try { final PostFlairModel post = data; final prefs = await SharedPreferences.getInstance(); DioClient.init(prefs); - print(post.toJson()); - print(_flairsList[ - _flairsList.indexWhere((element) => element.sId == flairId)] - .sId); - print('hiiiiiiiiiiiiiiiiiii $flairId'); - print(post.backgroundColor); await DioClient.patch( path: '/subreddits/$subredditName/flair/$flairId', data: post.toJson()); - //foods[foods.indexWhere((element) => element.uid == food.uid)] = food; _flairsList[_flairsList.indexWhere((element) => element.sId == flairId)] = data; notifyListeners(); diff --git a/lib/moderation_settings/screens/Edited_posts.dart b/lib/moderation_settings/screens/Edited_posts.dart new file mode 100644 index 00000000..e69de29b diff --git a/lib/moderation_settings/screens/add_edit_aproved_screen.dart b/lib/moderation_settings/screens/add_edit_aproved_screen.dart index 87cf32ca..93d17bb3 100644 --- a/lib/moderation_settings/screens/add_edit_aproved_screen.dart +++ b/lib/moderation_settings/screens/add_edit_aproved_screen.dart @@ -7,21 +7,36 @@ import '../../widgets/custom_snack_bar.dart'; import './approved_users_screen.dart'; class EditApprovedScreen extends StatefulWidget { - // final String? userName; - // final Baninfo? bannedInfo; + /// the route name of the screen + static const routeName = './editapproved'; + + /// the current Subrddit name of the screen + final String subredditName; const EditApprovedScreen({super.key, required this.subredditName}); @override - State createState() => _EditApprovedScreenState(); + State createState() => EditApprovedScreenState(); } -class _EditApprovedScreenState extends State { +class EditApprovedScreenState extends State { + /// Whether user enter data or not , then show the save buttom or not + bool isSlected = false; + + /// listener to the Username input field + final inputUserNameController = TextEditingController(); + + ///change the isSelcted when write data in input field + /// + ///when the input field is empty then the selected will be false + ///other wise it will be true void changeUserNmae() { /// this function will active only /// when the state is add new moderators + /// input: none + /// output: none if (inputUserNameController.text.isEmpty == false) { isSlected = true; } else { @@ -29,13 +44,15 @@ class _EditApprovedScreenState extends State { } } - @override - void initState() { - // TODO: implement initState - super.initState(); - } + ///post the Approved user info to the backend server + /// + /// take the data from inputs listener and sent it to the server + /// if the server return failed response then there is error message will appare + /// other show sucess message Future submitApproved() async { + ///input : none + ///output : none final provider = Provider.of(context, listen: false); await provider @@ -43,15 +60,7 @@ class _EditApprovedScreenState extends State { widget.subredditName, inputUserNameController.text, context, true) .then((value) {}); if (provider.isError == true) { - print('falied'); - // ignore: use_build_context_synchronously - // ScaffoldMessenger.of(context).showSnackBar( - // CustomSnackBar( - // isError: true, text: provider.errorMessage, disableStatus: true), - // ); } else { - // ignore: use_build_context_synchronously - print('sucess'); ScaffoldMessenger.of(context).showSnackBar( CustomSnackBar( isError: false, diff --git a/lib/moderation_settings/screens/add_edit_banned_screen.dart b/lib/moderation_settings/screens/add_edit_banned_screen.dart index 35fc51e4..aea9dba1 100644 --- a/lib/moderation_settings/screens/add_edit_banned_screen.dart +++ b/lib/moderation_settings/screens/add_edit_banned_screen.dart @@ -17,11 +17,19 @@ import '../models/moderator_tools.dart'; import '../provider/moderation_settings_provider.dart'; class EditBannedScreen extends StatefulWidget { + /// the route name of the screen + + static const routeName = './editbanned'; + + /// user name who will be banned final String? userName; + + /// all the info of banne Baninfo? bannedInfo; - final String subredditName; - static const routeName = './editbanned'; + /// the current Subrddit name of the screen + + final String subredditName; EditBannedScreen( {super.key, this.userName = '', @@ -29,23 +37,61 @@ class EditBannedScreen extends StatefulWidget { this.bannedInfo = null}); @override - State createState() => _EditBannedScreenState(); + State createState() => EditBannedScreenState(); } -class _EditBannedScreenState extends State { +class EditBannedScreenState extends State { + /// Whether the banned user is new or no the 3 inputs + bool isNew = true; + + /// Whether fetching the data from server done or not + bool fetchingDone = false; + + /// Whether the didChangeDependencies is called for the first time or not + bool _isInit = true; + + /// Whether the build fuction calling at least one time or not + bool isBuild = false; + + ///the title of the screen String title = 'Add a banned user'; + + /// Whether user enter data or not , then show the save buttom or not + bool isSlected = false; + + /// Whether user enter data or not , then show the save buttom or not + String reasonForBan = ''; + + /// Whether the ban duration is permenant or not + bool permenant = true; + + /// list of all rules in sub reddit to chose from it for ban reson List rules = []; + + /// the description of the current subreddit List rulesDescription = []; + + /// listener to the Username input field + final inputUserNameController = TextEditingController(); + + /// listener to the Mod note input field + final inputModNoteController = TextEditingController(); + + /// listener to the Duration input field + final inputDurationController = TextEditingController(); + + /// listener to the User note input field + final inputUserNoteController = TextEditingController(); @override @@ -102,9 +148,17 @@ class _EditBannedScreenState extends State { super.didChangeDependencies(); } + ///change the isSelcted when write data in input field + /// + ///when the UserNmae input field and reason both are empty then the selected will be false + ///other wise it will be true + void changNmaeReason() { /// this function will active only /// when the state is add new moderators + /// input: none + /// output: none + if (inputUserNameController.text.isEmpty == false && reasonForBan.isEmpty == false) { isSlected = true; @@ -113,23 +167,36 @@ class _EditBannedScreenState extends State { } } + ///clear the duration field if the check box of the permenation is select void changePermenant(bool changeedPermenant) { + /// input: + /// changeedPermenant :-whether the changeedPermenant is true or not + /// output: none + if (isNew == true) { if (changeedPermenant == true) { inputDurationController.clear(); } - print(changeedPermenant); permenant = changeedPermenant; } } + ///clear the permenant checkbox when user write data in the input field + void changeDuration() { + /// input: none + /// output: none + if (inputDurationController.text.isEmpty == false) { permenant = false; } } + ///put the data from text field to model baninfo void prepareBanInfo() { + /// input: none + /// output: none + widget.bannedInfo!.punishType = reasonForBan; widget.bannedInfo!.note = inputModNoteController.text; widget.bannedInfo!.punishReason = inputUserNoteController.text; @@ -137,25 +204,28 @@ class _EditBannedScreenState extends State { if (permenant == false) { widget.bannedInfo!.duration = int.parse(inputDurationController.text); } - print(widget.bannedInfo!.punishType); - print(widget.bannedInfo!.note); - print(widget.bannedInfo!.punishReason); - print(widget.bannedInfo!.duration); } + ///post the banned user info to the backend server + /// + /// take the data from ban info model + /// if the server return failed response then there is error message will appare + /// other show sucess message + Future addBaned() async { + /// input: none + /// output: none + prepareBanInfo(); String sucessMessage = 'bane ${inputUserNameController.text} done succesfully'; final provider = Provider.of(context, listen: false); - print('new Banned'); await provider .addRemoveBanned(widget.subredditName, inputUserNameController.text, context, widget.bannedInfo!, true) .then((value) {}); if (provider.isError == false) { - print('sucess'); ScaffoldMessenger.of(context).showSnackBar( CustomSnackBar( isError: false, text: sucessMessage, disableStatus: true), @@ -240,14 +310,11 @@ class _EditBannedScreenState extends State { // After selecting the desired option,it will // change button value to selected value onChanged: (newValue) { - // print((newValue as Rules).description!); if (isNew) { reasonForBan = (newValue as String); - // print(reasonForBan); } changNmaeReason(); setState(() {}); - // changeLocation(newValue! as String); }), ), ), diff --git a/lib/moderation_settings/screens/add_edit_moderator_screen.dart b/lib/moderation_settings/screens/add_edit_moderator_screen.dart index 5167b017..ea550703 100644 --- a/lib/moderation_settings/screens/add_edit_moderator_screen.dart +++ b/lib/moderation_settings/screens/add_edit_moderator_screen.dart @@ -9,29 +9,49 @@ import './moderators_screen.dart'; import '../widgets/input_text_field.dart'; class EditModeratorScreen extends StatefulWidget { + /// the route name of the screen + + static const routeName = './editmoderator'; + + /// user name who will be Aadded as moderators + final String? userName; - // final Baninfo? bannedInfo; + + /// all the permissions of the moderator + final ModeratorPermissions? moderatorPermissions; - // final String subreddit; - final String subredditName; - static const routeName = './editmoderator'; + /// the current Subrddit name of the screen + + final String subredditName; const EditModeratorScreen( {super.key, required this.subredditName, - // required this.subreddit, this.userName = '', this.moderatorPermissions = null}); @override - State createState() => _EditModeratorScreenState(); + State createState() => EditModeratorScreenState(); } -class _EditModeratorScreenState extends State { +class EditModeratorScreenState extends State { + /// Whether the banned user is new or no the 3 inputs + bool isNew = true; + + ///the title of the screen + String title = 'Add a moderator'; + + /// Whether user enter data or not , then show the save buttom or not + bool isSlected = false; + + /// listener to the Username input field + final inputUserNameController = TextEditingController(); + + ///all permissions of the moderator ModeratorPermissions? permissions = ModeratorPermissions( all: true, access: true, config: true, flair: true, posts: true); @@ -49,15 +69,21 @@ class _EditModeratorScreenState extends State { permissions!.config = widget.moderatorPermissions!.config; permissions!.flair = widget.moderatorPermissions!.flair; permissions!.posts = widget.moderatorPermissions!.posts; - - print(inputUserNameController.text); } super.initState(); } + ///change the isSelcted when write data in input field + /// + ///when the UserNmae input field is empty then the selected will be false + ///other wise it will be true + void changeUserNmae() { /// this function will active only /// when the state is add new moderators + /// input: none + /// output: none + if (inputUserNameController.text.isEmpty == false) { isSlected = true; } else { @@ -65,7 +91,12 @@ class _EditModeratorScreenState extends State { } } + ///select all permision be true when the all box selected void changePermossionAll(bool value) { + /// input: + /// value:whether the all box selected or not + /// output: none + if (permissions!.all == false || value == true) { permissions!.access = true; permissions!.config = true; @@ -76,7 +107,16 @@ class _EditModeratorScreenState extends State { changePermissions(); } + ///control checkboxed when access box change + /// + ///if the new value be false then permissions all will be false + ///if the new value will be true and other check boxes was true + ///then make all box true void changePermossionAccess(bool value) { + /// input: + /// value:whether the Access box selected or not + /// output: none + if (value == false) { permissions!.all = false; } else if (permissions!.flair == true && @@ -88,7 +128,17 @@ class _EditModeratorScreenState extends State { changePermissions(); } + ///control checkboxed when flair box change + /// + ///if the new value be false then permissions all will be false + ///if the new value will be true and other check boxes was true + ///then make all box true + void changePermossionFlair(bool value) { + /// input: + /// value:whether the Flair box selected or not + /// output: none + if (value == false) { permissions!.all = false; } else if (permissions!.access == true && @@ -100,7 +150,17 @@ class _EditModeratorScreenState extends State { changePermissions(); } + ///control checkboxed when Config box change + /// + ///if the new value be false then permissions all will be false + ///if the new value will be true and other check boxes was true + ///then make all box true + void changePermossionConfig(bool value) { + /// input: + /// value:whether the Config box selected or not + /// output: none + if (value == false) { permissions!.all = false; } else if (permissions!.flair == true && @@ -112,7 +172,17 @@ class _EditModeratorScreenState extends State { changePermissions(); } + ///control checkboxed when Posts box change + /// + ///if the new value be false then permissions all will be false + ///if the new value will be true and other check boxes was true + ///then make all box true + void changePermossionPosts(bool value) { + /// input: + /// value:whether the Posts box selected or not + /// output: none + if (value == false) { permissions!.all = false; } else if (permissions!.flair == true && @@ -124,7 +194,13 @@ class _EditModeratorScreenState extends State { changePermissions(); } + ///chagne the isSelected when change any box + /// + ///if it is not the same as initiall , then is Selected will be true + ///other wise will be false void changePermissions() { + /// input: none + /// output: none if (!isNew) { if (permissions!.flair != widget.moderatorPermissions!.flair || permissions!.config != widget.moderatorPermissions!.config || @@ -138,6 +214,12 @@ class _EditModeratorScreenState extends State { } } + ///post the new Moderator info to the backend server + /// + /// take the data from input controller info and from permisions model + /// if the server return failed response then there is error message will appare + /// other show sucess message + Future doneChanges() async { String sucessMessage; final provider = @@ -159,15 +241,7 @@ class _EditModeratorScreenState extends State { .then((value) {}); } if (provider.isError == true) { - print('falied'); - // ignore: use_build_context_synchronously - // ScaffoldMessenger.of(context).showSnackBar( - // CustomSnackBar( - // isError: true, text: provider.errorMessage, disableStatus: true), - // ); } else { - // ignore: use_build_context_synchronously - print('sucess'); ScaffoldMessenger.of(context).showSnackBar( CustomSnackBar( isError: false, text: sucessMessage, disableStatus: true), @@ -176,8 +250,11 @@ class _EditModeratorScreenState extends State { // ignore: use_build_context_synchronously Navigator.of(context).pop(); + // ignore: use_build_context_synchronously Navigator.of(context).pop(); + // ignore: use_build_context_synchronously if (isNew == false) Navigator.of(context).pop(); + // ignore: use_build_context_synchronously Navigator.pushNamed(context, ModeratorsScreen.routeName, arguments: subredditName); } diff --git a/lib/moderation_settings/screens/add_edit_muted_screen.dart b/lib/moderation_settings/screens/add_edit_muted_screen.dart index bc2c72ac..8fedeb4d 100644 --- a/lib/moderation_settings/screens/add_edit_muted_screen.dart +++ b/lib/moderation_settings/screens/add_edit_muted_screen.dart @@ -8,10 +8,20 @@ import './muted_user_screen.dart'; import '../widgets/input_text_field.dart'; class EditMutedScreen extends StatefulWidget { + /// user name who will be muted + final String? userName; + + /// model carry the data of muted + MuteInfo? mutedInfo; + + /// the current Subrddit name of the screen + final String subredditName; + /// the route name of the screen + static const routeName = './editmuted'; EditMutedScreen( {super.key, @@ -20,10 +30,10 @@ class EditMutedScreen extends StatefulWidget { this.mutedInfo = null}); @override - State createState() => _EditMutedScreenState(); + State createState() => EditMutedScreenState(); } -class _EditMutedScreenState extends State { +class EditMutedScreenState extends State { bool isNew = true; String title = 'Add a muted user'; bool isSlected = false; @@ -57,6 +67,12 @@ class _EditMutedScreenState extends State { } } + /// post the Muted info to the backend server + /// + /// take the data from input controller info and from Mute model + /// if the server return failed response then there is error message will appare + /// other show sucess message + Future doneChanges() async { String sucessMessage; final provider = diff --git a/lib/moderation_settings/screens/add_edit_post_flair.dart b/lib/moderation_settings/screens/add_edit_post_flair.dart index 39a38994..e17ef4c1 100644 --- a/lib/moderation_settings/screens/add_edit_post_flair.dart +++ b/lib/moderation_settings/screens/add_edit_post_flair.dart @@ -9,6 +9,7 @@ import 'package:provider/provider.dart'; import '../widgets/alert_dialog.dart'; import 'package:responsive_sizer/responsive_sizer.dart'; +// ignore: must_be_immutable class AddAndEditPostFllair extends StatefulWidget { AddAndEditPostFllair( {super.key, @@ -19,6 +20,7 @@ class AddAndEditPostFllair extends StatefulWidget { this.post, this.textColor1, this.counter}); + static const routeName = '/add-edit-post-flair'; PostFlairModel? post; bool? deleteFlair; @@ -29,44 +31,47 @@ class AddAndEditPostFllair extends StatefulWidget { String? subredditName; int? counter = 0; @override - State createState() => _AddAndEditPostFllairState(); + State createState() => AddAndEditPostFllairState(); } -class _AddAndEditPostFllairState extends State { +class AddAndEditPostFllairState extends State { bool _iselected = false; TextEditingController? _editPost = null; bool showWidget = false; - //int counter = 0; - + //Used to change state of background color in text field + //return value void _changeBackgroundColor(color) { setState(() { widget.backgroundColorText = color; }); - print(color); } - + //get remaining of characters + //return value void _setCounterState() { setState(() { widget.counter = 64 - widget.changed!.length; }); } - + //Function called to show colors palette or not + //return value void _showColorWidget() { setState(() { showWidget = !showWidget; }); } - + //Used to change state of textColor in text field + //return value void _changedTextColor() { setState(() { widget.textColor = !widget.textColor!; widget.textColor1 = (!widget.textColor!) ? '#000000' : '#FFFFFF'; }); } - + //Used to change state of text typed in text field + //return value void _editText(value) { setState(() { widget.changed = value; @@ -82,23 +87,15 @@ class _AddAndEditPostFllairState extends State { _setCounterState(); } - // onClick() { - // setState(() { - // //_selectedIndex = index; - // _iselected = true; - // //choosenTopic = topics.keys.elementAt(_selectedIndex); - // }); - // } - -//SEND MODEL OR ID +//Call endpoint to clear selected flair _deletePostFlair() async { await Provider.of(context, listen: false) .deleteFlair(widget.subredditName, context, widget.post!.sId) .then((value) => Navigator.pop(context, true)); } - +//Save flair choose between whether flair was created recently to call addNewFlair or was being +//edited to call editFlair saveFlair() async { - print('helllllllllllllllo ${widget.backgroundColorText}'); PostFlairModel post = PostFlairModel( text: widget.changed, backgroundColor: widget.backgroundColorText, @@ -108,18 +105,12 @@ class _AddAndEditPostFllairState extends State { .addNewFlair(widget.subredditName, post, context) .then((value) { Navigator.pop(context, true); - //Navigator.popAndPushNamed(context, PostFlairModerator.routeName); }); } else { await Provider.of(context, listen: false) .editFlair(widget.subredditName, context, widget.post!.sId, post) .then((value) { Navigator.pop(context, true); - // Navigator.pushReplacement( - // context, - // MaterialPageRoute(builder: (context) => PostFlairModerator() - // // ModalRoute.withName(PostFlairModerator.routeName) - // )); }); } } @@ -387,7 +378,7 @@ class _AddAndEditPostFllairState extends State { ); } } - +//Build palette of color class ShowColorWidget extends StatelessWidget { ShowColorWidget({ super.key, diff --git a/lib/moderation_settings/screens/approved_users_screen.dart b/lib/moderation_settings/screens/approved_users_screen.dart index 598c101d..379dd25f 100644 --- a/lib/moderation_settings/screens/approved_users_screen.dart +++ b/lib/moderation_settings/screens/approved_users_screen.dart @@ -9,27 +9,44 @@ import '../models/approved.dart'; import './add_edit_aproved_screen.dart'; class ApprovedScreen extends StatefulWidget { + /// the route name of the screen + static const routeName = '/approved'; const ApprovedScreen({super.key}); - // postTypes({super.key}); @override State createState() => ApprovedScreenState(); } class ApprovedScreenState extends State { + /// Whether fetching the data from server done or not + bool fetchingDone = true; + + /// Whether the didChangeDependencies is called for the first time or not + bool _isInit = true; + + /// Whether the build fuction calling at least one time or not + bool isBuild = false; - // ignore: non_constant_identifier_names + + /// list of all approved users in sub reddit comming from the provider List? approved = []; + + ///approved as map to be send to edit screen List> allapproved = []; + + /// the current Subrddit name of the screen + String subredditName = ''; + ///prepare the map allapproved from the list approved void extractApproved() { + ///Input: none + ///output: none approved!.forEach((element) { - print(element); final _id = element.user!.sId as String; final _userName = element.user!.userName as String; String _joiningDate = ''; @@ -56,25 +73,6 @@ class ApprovedScreenState extends State { "profilePicture": _profilePicture as String, }); }); - print(allapproved); - } - - @override - void initState() { - fetchingDone = false; - // final provider = - // Provider.of(context, listen: false); - - // subredditName = provider.getSubredditName(context); - // provider.getUser(subredditName, UserCase.approved, context).then((value) { - // approved = provider.approved; - // print(approved); - // extractApproved(); - // fetchingDone = true; - // if (isBuild) setState(() {}); - // }); - - super.initState(); } @override diff --git a/lib/moderation_settings/screens/banned_user_sceen.dart b/lib/moderation_settings/screens/banned_user_sceen.dart index 14a540d9..a6178b4b 100644 --- a/lib/moderation_settings/screens/banned_user_sceen.dart +++ b/lib/moderation_settings/screens/banned_user_sceen.dart @@ -9,24 +9,42 @@ import '../models/banned.dart'; import 'add_edit_banned_screen.dart'; class BannedScreen extends StatefulWidget { + /// the route name of the screen + static const routeName = '/banned'; const BannedScreen({super.key}); - // postTypes({super.key}); @override State createState() => BannedScreenState(); } class BannedScreenState extends State { + /// Whether fetching the data from server done or not + bool fetchingDone = true; + + /// Whether the didChangeDependencies is called for the first time or not + bool _isInit = true; + + /// Whether the build fuction calling at least one time or not + bool isBuild = false; - // ignore: non_constant_identifier_names + + /// list of all banned users in sub reddit comming from the provider List? banned = []; + + ///banned users as map to be send to edit screen + List> allbanned = []; + + /// the current Subrddit name of the screen + String subredditName = ''; + ///prepare the map all banned from the list banned + void extractBanned() { banned!.forEach((element) { print(element); @@ -49,7 +67,6 @@ class BannedScreenState extends State { } else { _banDate = '${DateTime.now().minute - banDate.minute}min'; } - print(_baninfo.duration.toString()); _banDate += (_baninfo.duration == -1) ? ' ( permenant)' : ' (${_baninfo.duration.toString()} days) '; @@ -61,25 +78,6 @@ class BannedScreenState extends State { "baninfo": _baninfo, }); }); - print(allbanned); - } - - @override - void initState() { - // fetchingDone = false; - // final provider = - // Provider.of(context, listen: false); - - // subredditName = provider.getSubredditName(context); - // provider.getUser(subredditName, UserCase.banned, context).then((value) { - // banned = provider.banned; - // print(banned); - // extractBanned(); - // fetchingDone = true; - // if (isBuild) setState(() {}); - // }); - - super.initState(); } @override diff --git a/lib/moderation_settings/screens/community_type_screen.dart b/lib/moderation_settings/screens/community_type_screen.dart index 482f9d10..a2c7ae09 100644 --- a/lib/moderation_settings/screens/community_type_screen.dart +++ b/lib/moderation_settings/screens/community_type_screen.dart @@ -9,14 +9,17 @@ import '../../widgets/loading_reddit.dart'; import 'package:flutter/cupertino.dart'; class ComuunityTypesScreen extends StatefulWidget { + /// the route name of the screen + static const routeName = '/communitytypes'; - // postTypes({super.key}); @override State createState() => ComuunityTypesScreenState(); } class ComuunityTypesScreenState extends State { + ///values of text and color and sub title for the slider + ///these values will be used when cahnge the value of the slider final sliderValues = [ { 'color': Colors.green, @@ -36,34 +39,80 @@ class ComuunityTypesScreenState extends State { 'Only people you approved can see and participate in this community' } ]; + + ///the curret status of the post type input InputStatus postTypesStatus = InputStatus.original; + ///the initiall value of the post type text unput String? initialpostTypes; + + /// the current Subrddit name of the screen + String? subbredditName; + + /// Whether user enter data or not , then show the save buttom or not + bool isSlected = false; + + ///model will be filed from the provider to take the data ModeratorToolsModel? moderatorToolsModel; + ///the initiall value of the index of the slider + double initialIndex = 1; + + ///the initiall value of the plus 18 + bool initialIsPlus18 = false; + ///the initiall value index + double index = 1; + + /// Whether the plus 18 box selected or not + bool isPlus18 = false; + /// Whether fetching the data from server done or not + bool fetchingDone = true; + + /// Whether the didChangeDependencies is called for the first time or not + bool _isInit = true; + + /// Whether the build fuction calling at least one time or not + bool isBuild = false; + + /// change the index of the slider void changeType(newIndex) { + ///Input: + /// newindex : the new value of the index + ///output: none + index = newIndex; changeCommunityType(); } + /// change the pluse 18 + void changePlus18() { + /// input: none + /// output: none + isPlus18 = !isPlus18; - // print(isPlus18); changeCommunityType(); } + ///change the isSelcted when change the slider and is plus 18 + /// + ///when the UserNmae input field and reason both are empty then the selected will be false + ///other wise it will be true void changeCommunityType() { + /// input: none + /// output: none + if (index == initialIndex && isPlus18 == initialIsPlus18) { isSlected = false; } else { @@ -71,13 +120,6 @@ class ComuunityTypesScreenState extends State { } } - // void change - @override - void initState() { - //return hardcoded topics from constant folder - super.initState(); - } - @override void didChangeDependencies() { final Map data = {}; @@ -88,7 +130,6 @@ class ComuunityTypesScreenState extends State { subbredditName = ModalRoute.of(context)?.settings.arguments != null ? ModalRoute.of(context)?.settings.arguments as String : ''; - // subbredditName = 'Cooking'; Provider.of(context, listen: false) .getCommunity(subbredditName!, context) .then((_) { @@ -113,8 +154,16 @@ class ComuunityTypesScreenState extends State { super.didChangeDependencies(); } + ///post the community type changes to the sever + /// + /// take the data from ban info model + /// if the server return failed response then there is error message will appare + /// other show sucess message + Future saveCommunityTypes() async { - // //// + /// input: none + /// output: none + final provider = Provider.of(context, listen: false); await provider.patchCommunity({ @@ -122,11 +171,6 @@ class ComuunityTypesScreenState extends State { "nsfw": isPlus18, }, subbredditName!, context).then((response) {}); if (provider.isError == true) { - // ignore: use_build_context_synchronously - ScaffoldMessenger.of(context).showSnackBar( - CustomSnackBar( - isError: true, text: provider.errorMessage, disableStatus: true), - ); } else { // ignore: use_build_context_synchronously ScaffoldMessenger.of(context).showSnackBar( @@ -142,7 +186,6 @@ class ComuunityTypesScreenState extends State { } } - double _value = 3; @override Widget build(BuildContext context) { isBuild = true; diff --git a/lib/moderation_settings/screens/description_screen.dart b/lib/moderation_settings/screens/description_screen.dart index b023dda7..1d3da45f 100644 --- a/lib/moderation_settings/screens/description_screen.dart +++ b/lib/moderation_settings/screens/description_screen.dart @@ -9,6 +9,8 @@ import '../../widgets/custom_snack_bar.dart'; import '../../widgets/loading_reddit.dart'; class Description extends StatefulWidget { + /// the route name of the screen + static const routeName = '/description'; const Description({super.key}); @@ -17,26 +19,53 @@ class Description extends StatefulWidget { } class DescriptionState extends State { - // TextEditingController descriptionController = TextEditingController(); + //the value that will be puth in the description at the first String descriptionController = ''; + + /// the current status of the description input field InputStatus descriptionStatus = InputStatus.original; + + /// Whether the build fuction calling at least one time or not + bool isBuild = false; + /// the initiall value of the description String? initialDescription; + + /// the current Subrddit name of the screen + String? subbredditName; + + /// Whether user enter data or not , then show the save buttom or not + bool isSlected = false; + + ///model will be filed from the provider to take the data + ModeratorToolsModel? moderatorToolsModel; - ///change the status of password input field + /// Whether fetching the data from server done or not + + bool fetchingDone = false; + + /// Whether the didChangeDependencies is called for the first time or not + + bool _isInit = true; + + ///change the status of descriton input field /// ///make it taped or oreignal according to the focus listner void changeDescriptionFocus(focus) { descriptionStatus = (focus) ? InputStatus.taped : InputStatus.original; } + ///change the isSelcted when change the descriptopn input + /// + ///when the descrition is the same as the initall + ///other wise it will be true + void changeDescription(value) { descriptionController = value; - print(descriptionController); if (descriptionController == initialDescription) { isSlected = false; } else { @@ -44,8 +73,16 @@ class DescriptionState extends State { } } + ///post the Descrition to the sever + /// + /// take the data from descriptionController + /// if the server return failed response then there is error message will appare + /// other show sucess message + Future saveDescription() async { - //// + /// input: none + /// output: none + final provider = Provider.of(context, listen: false); await provider.patchCommunity({"description": '$descriptionController'}, @@ -72,9 +109,6 @@ class DescriptionState extends State { } } - bool fetchingDone = false; - bool _isInit = true; - @override void initState() { //return hardcoded topics from constant folder diff --git a/lib/moderation_settings/screens/location_screen.dart b/lib/moderation_settings/screens/location_screen.dart index 16ebd48e..cf5ed740 100644 --- a/lib/moderation_settings/screens/location_screen.dart +++ b/lib/moderation_settings/screens/location_screen.dart @@ -11,33 +11,61 @@ import '../../widgets/custom_snack_bar.dart'; import '../widgets/location_list.dart'; class LocationScreen extends StatefulWidget { + /// the route name of the screen + static const routeName = '/location'; const LocationScreen({super.key}); - // postTypes({super.key}); @override State createState() => LocationScreenState(); } class LocationScreenState extends State { - // TextEditingController postTypesController = TextEditingController(); - InputStatus postTypesStatus = InputStatus.original; - String? initialpostTypes; + /// the current Subrddit name of the screen + String? subbredditName; + + /// Whether user enter data or not , then show the save buttom or not + bool isSlected = false; + + /// Whether the build fuction calling at least one time or not + bool isBuild = false; + + ///model will be filed from the provider to take the data + ModeratorToolsModel? moderatorToolsModel; + ///the initiall value of the location String? initialLocation = ''; + + /// the current value of the location when the user change it String? location = ''; + + /// Whether fetching the data from server done or not + bool fetchingDone = true; + + /// Whether the didChangeDependencies is called for the first time or not + bool _isInit = true; + + ///list of all regions comming from a constat list final List regions = Countries().countries; + + ///change the isSelcted when change the location + /// + ///when the UserNmae input field and reason both are empty then the selected will be false + ///other wise it will be true + void changeLocation(String newLocation) { - print(location); + /// input: + /// newLocation:the new location value + /// output: none + location = newLocation; - print(location); if (location == initialLocation) { isSlected = false; } else { @@ -45,13 +73,6 @@ class LocationScreenState extends State { } } - // void change - @override - void initState() { - //return hardcoded topics from constant folder - super.initState(); - } - @override void didChangeDependencies() { final Map data = {}; @@ -83,8 +104,16 @@ class LocationScreenState extends State { super.didChangeDependencies(); } + ///post the Location changes to the sever + /// + /// take the data from location feild info model + /// if the server return failed response then there is error message will appare + /// other show sucess message + Future saveLocation() async { - //// + /// input: none + /// output: none + final provider = Provider.of(context, listen: false); await provider.patchCommunity({ diff --git a/lib/moderation_settings/screens/moderator_tools_screen.dart b/lib/moderation_settings/screens/moderator_tools_screen.dart index 58faa45a..1124526b 100644 --- a/lib/moderation_settings/screens/moderator_tools_screen.dart +++ b/lib/moderation_settings/screens/moderator_tools_screen.dart @@ -12,8 +12,10 @@ import './approved_users_screen.dart'; import './banned_user_sceen.dart'; import './muted_user_screen.dart'; import './moderators_screen.dart'; - +import './traffic_state.dart'; class ModeratorTools extends StatefulWidget { + /// the route name of the screen + static const routeName = '/moderatortools'; const ModeratorTools({super.key}); @@ -219,6 +221,14 @@ class _ModeratorToolsState extends State { ModalRoute.of(context)?.settings.arguments as String), 'Banned users', Icons.gavel_outlined), + buildGeneralOptions( + context, + () => Navigator.of(context).pushNamed(TraficState.routeName, + arguments: + //'Cooking' + ModalRoute.of(context)?.settings.arguments as String), + 'Traffic Status', + Icons.view_agenda_outlined), ], ), // ), diff --git a/lib/moderation_settings/screens/moderators_screen.dart b/lib/moderation_settings/screens/moderators_screen.dart index 9793aba3..bd77f9b0 100644 --- a/lib/moderation_settings/screens/moderators_screen.dart +++ b/lib/moderation_settings/screens/moderators_screen.dart @@ -11,6 +11,8 @@ import '../models/user.dart'; import 'add_edit_moderator_screen.dart'; class ModeratorsScreen extends StatefulWidget { + /// the route name of the screen + static const routeName = '/moderators'; const ModeratorsScreen({super.key}); @@ -21,20 +23,51 @@ class ModeratorsScreen extends StatefulWidget { } class ModeratorsScreenState extends State { + ///index control with list will be appare (all moderators or editable moderators) int index = 0; + + /// Whether fetching the data from server done or not + bool fetchingDone = true; + + /// Whether the didChangeDependencies is called for the first time or not + bool _isInit = true; + + /// Whether the build fuction calling at least one time or not + bool _isBuild = false; - // ignore: non_constant_identifier_names + + /// list of all moderators from the provider List? moderators = []; + + ///list of all moderators to this subredditto show in moderators list screen List> allModerators = []; + + ///list of editable moderators to this sub reddit List> editableModerators = []; + + /// the username of the current user String userName = ''; + + ///the join date of the user DateTime? userJoinDate; + + ///whether the full permision is checked as true bool userFullPermisions = false; + + ///the name of the current subreddit String subredditName = ''; + ///prepare the map allmoderators and editable from the list moderators + /// + ///process the data like prpare the data as String in formate %yr of %mon + ///and prepare the permision string + ///and fill the editable moderators list void extractModeratorsLists() { + ///Input: none + ///output: none + moderators!.forEach((element) { print(element); final _id = element.sId as String; @@ -88,7 +121,6 @@ class ModeratorsScreenState extends State { userFullPermisions = element.moderatorPermissions!.all!; } }); - print(allModerators); if (userFullPermisions) { int index = 0; moderators!.forEach((element) { @@ -102,11 +134,6 @@ class ModeratorsScreenState extends State { } } - @override - void initState() { - super.initState(); - } - @override void didChangeDependencies() { final Map data = {}; @@ -135,7 +162,10 @@ class ModeratorsScreenState extends State { super.didChangeDependencies(); } + ///get the user name from the provider Future getuserName() async { + ///input : none + ///out : the username of the cureent user final prefs = await SharedPreferences.getInstance(); print(prefs.getString('userName')); return prefs.getString('userName') as String; diff --git a/lib/moderation_settings/screens/muted_user_screen.dart b/lib/moderation_settings/screens/muted_user_screen.dart index 9d423548..f95921cd 100644 --- a/lib/moderation_settings/screens/muted_user_screen.dart +++ b/lib/moderation_settings/screens/muted_user_screen.dart @@ -9,27 +9,52 @@ import '../models/muted.dart'; import 'add_edit_muted_screen.dart'; class MutedScreen extends StatefulWidget { + /// the route name of the screen + static const routeName = '/muted'; const MutedScreen({super.key}); - // postTypes({super.key}); @override State createState() => MutedScreenState(); } class MutedScreenState extends State { + /// Whether fetching the data from server done or not + bool fetchingDone = true; + + /// Whether the didChangeDependencies is called for the first time or not + bool _isInit = true; + + /// Whether the build fuction calling at least one time or not + bool isBuild = false; - // ignore: non_constant_identifier_names + + /// list of all Muted from the provider List? muted = []; + + ///list of all Muted to this subreddit to show in muted list screen + List> allmuted = []; + + ///the name of the current subreddit + String subredditName = ''; + ///prepare the map allmuted from the list muted + /// + ///process the data like prpare the data as String in formate %yr of %mon + ///and prepare the muted info model for each user + ///and fill the editable moderators list + ///and prepare the _profilePicture of the each user + void extractmuted() { + ///Input: none + ///output: none + muted!.forEach((element) { - print(element); final _id = element.sId as String; final _userName = element.userName as String; String _joiningDate = ''; @@ -61,11 +86,6 @@ class MutedScreenState extends State { print(allmuted); } - @override - void initState() { - super.initState(); - } - @override void didChangeDependencies() { final Map data = {}; @@ -79,7 +99,6 @@ class MutedScreenState extends State { subredditName = provider.getSubredditName(context); provider.getUser(subredditName, UserCase.muted, context).then((value) { muted = provider.muted; - print(muted); extractmuted(); fetchingDone = true; if (isBuild) setState(() {}); @@ -94,7 +113,6 @@ class MutedScreenState extends State { isBuild = true; return (!fetchingDone) ? const LoadingReddit() - //Calls TopicMainScreen widget to build Topics Screen : Scaffold( backgroundColor: Color.fromARGB(255, 228, 231, 239), appBar: AppBar( diff --git a/lib/moderation_settings/screens/post_flair.dart b/lib/moderation_settings/screens/post_flair.dart index fe46d26b..679ee187 100644 --- a/lib/moderation_settings/screens/post_flair.dart +++ b/lib/moderation_settings/screens/post_flair.dart @@ -20,6 +20,10 @@ class _PostFlairState extends State { bool fetchingDone = false; bool _isInit = true; String? subredditName; + + //Function called when editing the post flair + //Navigate to Post Flair edit page + // return type void _toEdit(index) async { final reLoadPage = await Navigator.push( context, @@ -37,13 +41,6 @@ class _PostFlairState extends State { } } - @override - // void initState() { - // // TODO: implement initState - // super.initState(); - - // } - @override void didChangeDependencies() { if (_isInit) { @@ -61,8 +58,6 @@ class _PostFlairState extends State { flair = Provider.of(context, listen: false).flairList; - // choosenTopic = moderatorToolsModel!.choosenTopic1; - // print(choosenTopic); setState(() { fetchingDone = true; }); @@ -73,9 +68,6 @@ class _PostFlairState extends State { super.didChangeDependencies(); } - // String text = 'hii'; - // String backgroundColor = '#ac1291'; - // String textColor = '#ac1291'; @override Widget build(BuildContext context) { final data = Provider.of(context); @@ -102,7 +94,6 @@ class _PostFlairState extends State { if (reLoadPage) { setState(() {}); } - //Navigator.of(context).pushNamed(AddAndEditPostFllair.routeName,); }, icon: Icon(Icons.add)), ], @@ -166,7 +157,9 @@ class _PostFlairState extends State { ); } } - +//Funtion to convert string to hex color to display +//returns Hex Color +//Input String extension ColorExtension on String { toColor() { var hexStringColor = this; diff --git a/lib/moderation_settings/screens/post_flair_settings.dart b/lib/moderation_settings/screens/post_flair_settings.dart index 59347c75..940bc61f 100644 --- a/lib/moderation_settings/screens/post_flair_settings.dart +++ b/lib/moderation_settings/screens/post_flair_settings.dart @@ -11,12 +11,12 @@ class PostFlairSettings extends StatefulWidget { class _PostFlairSettingsState extends State { bool modOnlySwitch = false; + + //Used to change state of mod Only switch toggleSwitch(value) { - print('hi'); setState(() { modOnlySwitch = value; }); - print(modOnlySwitch); } @override @@ -35,7 +35,6 @@ class _PostFlairSettingsState extends State { ), body: Container( child: Row( - // mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Icon(Icons.shield, color: Colors.grey), Divider(), diff --git a/lib/moderation_settings/screens/post_types_screen.dart b/lib/moderation_settings/screens/post_types_screen.dart index a7c695f7..0e5072cc 100644 --- a/lib/moderation_settings/screens/post_types_screen.dart +++ b/lib/moderation_settings/screens/post_types_screen.dart @@ -9,6 +9,7 @@ import '../../widgets/loading_reddit.dart'; import '../../settings/widgets/swich_list_view.dart'; class PostTypesScreen extends StatefulWidget { + /// the route name of the screen static const routeName = '/posttypes'; // postTypes({super.key}); @@ -17,41 +18,99 @@ class PostTypesScreen extends StatefulWidget { } class PostTypesScreenState extends State { - // TextEditingController postTypesController = TextEditingController(); + /// the current status of the post type input field InputStatus postTypesStatus = InputStatus.original; + /// the inital value of the post type in the subreddit String? initialpostTypes; + + /// the current Subrddit name of the screen + String? subbredditName; + + /// Whether user enter data or not , then show the save buttom or not + bool isSlected = false; + + ///model will be filed from the provider to take the data + ModeratorToolsModel? moderatorToolsModel; + /// Whether the inital value of allow image check box is selected or not + bool initialAllowImages = false; + + /// Whether the inital value of allow videos check box is selected or not + bool initialAllowVideos = false; + + /// Whether the inital value of allow links check box is selected or not + bool initialAllowLinks = false; + /// Whether the current allow image check box is selected or not + bool allowImages = false; + + /// Whether the current allow videos check box is selected or not + bool allowVideos = false; + + /// Whether the current allow links check box is selected or not + bool allowLinks = false; + /// Whether fetching the data from server done or not + bool fetchingDone = false; + + /// Whether the didChangeDependencies is called for the first time or not + bool _isInit = true; + + /// Whether the build fuction calling at least one time or not + bool isBuild = false; + + /// toggle the value of allow image swich void changeAllowImage() { + /// input: none + /// output: none + allowImages = !allowImages; changePostTypes(); } + /// toggle the value of allow videos swich + void changeAllowVideos() { + /// input: none + /// output: none + allowVideos = !allowVideos; changePostTypes(); } + /// toggle the value of allow links swich + void changeAllowLinks() { + /// input: none + /// output: none + allowLinks = !allowLinks; changePostTypes(); } + ///change the isSelcted when change the values of the thre swiches + /// + ///when the any one from the 3 swiches changed from the inital state + ///then the is Selected will be true + ///other wise it will be true + void changePostTypes() { + /// input: none + /// output: none + if (allowImages == initialAllowImages && allowVideos == initialAllowVideos && allowLinks == initialAllowLinks) { @@ -61,13 +120,6 @@ class PostTypesScreenState extends State { } } - // void change - @override - void initState() { - //return hardcoded topics from constant folder - super.initState(); - } - @override void didChangeDependencies() { if (_isInit) { @@ -99,8 +151,15 @@ class PostTypesScreenState extends State { super.didChangeDependencies(); } + /// post the post type changes to the sever + /// + /// take the data from variables allowVideos-allowLinks-allowImages + /// if the server return failed response then there is error message will appare + /// other show sucess message + Future savepostTypes() async { - //// + /// input: none + /// output: none final provider = Provider.of(context, listen: false); await provider.patchCommunity({ diff --git a/lib/moderation_settings/screens/spam_posts.dart b/lib/moderation_settings/screens/spam_posts.dart new file mode 100644 index 00000000..e69de29b diff --git a/lib/moderation_settings/screens/topics_screen.dart b/lib/moderation_settings/screens/topics_screen.dart index c0d3b11c..67ffe1d9 100644 --- a/lib/moderation_settings/screens/topics_screen.dart +++ b/lib/moderation_settings/screens/topics_screen.dart @@ -46,7 +46,6 @@ class _TopicsScreenState extends State { Provider.of(context, listen: false) .getCommunity( ModalRoute.of(context)?.settings.arguments as String, context - // 'Cooking' ) .then((_) { moderatorToolsModel = @@ -54,7 +53,6 @@ class _TopicsScreenState extends State { .moderatorToolsModel; choosenTopic = moderatorToolsModel!.choosenTopic1; - print(choosenTopic); setState(() { fetchingDone = true; }); diff --git a/lib/moderation_settings/screens/traffic_state.dart b/lib/moderation_settings/screens/traffic_state.dart index 7d816093..bca50ee3 100644 --- a/lib/moderation_settings/screens/traffic_state.dart +++ b/lib/moderation_settings/screens/traffic_state.dart @@ -15,6 +15,8 @@ import 'package:provider/provider.dart'; import 'package:editable/editable.dart'; class TraficState extends StatefulWidget { + /// the route name of the screen + static const routeName = '/traficstate'; const TraficState({super.key}); @@ -23,32 +25,80 @@ class TraficState extends StatefulWidget { } class _TraficStateState extends State { + /// Whether fetching the data from server done or not + bool fetchingDone = true; + + /// Whether the didChangeDependencies is called for the first time or not + bool _isInit = true; + + /// Whether the build fuction calling at least one time or not + bool _isBuild = false; + + /// index will control wich graph will appare int index = 0; - static const startYear = 1990; + + /// the starting year of years graph + static const startYear = 2018; + + ///the list that will carry the name of 7 days of week List days = []; + + ///the list that will carry the number of members joined at each day + List daysValues = List.generate(7, (int index) => 0); + + ///the max number of members joined at any day of the week int maxDay = 0; + ///the list that will carry the 52 week of the year + List weeks = []; + + ///the list that will carry the number of members joined at each week + List weeksValues = List.generate(52, (int index) => 0); + + ///the max number of members joined at any week of the year + int maxWeek = 0; + ///the list that will carry the name of the 12s month of the year + List months = []; + + ///the list that will carry the number of members joined at each month + List monthsValues = List.generate(12, (int index) => 0); + + ///the max number of members joined at any month of the year + int maxMonth = 0; + ///the list that will carry a string represent each year from starting year until now + List years = []; + + ///the list that will carry the number of members joined at each year + List yearsValues = List.generate( DateTime.now().year - startYear + 1, (int index) => 0); + ///the max number of members joined at any year + int maxYear = 0; - // int max = 1; + ///list of feautures used in the linear graph + /// that single feauture represent the values of y access of the graph List features = []; + + /// get the maximum of two trafic data instance acording to its numOfUsers TrafficData maxi(TrafficData a, TrafficData b) { + ///Input : + /// a,b the two inputs we want to compare between them + /// return the max between a,b according the number of users return (a.numOfUsers! >= b.numOfUsers!) ? a : b; } @@ -92,14 +142,28 @@ class _TraficStateState extends State { : ''; provider.gettrafficData(subredditName, context).then((value) { - print(provider.dayTraffic); - print(provider.weekTraffic); - print(provider.monthTraffic); - print(provider.yearTraffic); - maxDay = provider.dayTraffic.data!.reduce(maxi).numOfUsers!; - maxWeek = provider.weekTraffic.data!.reduce(maxi).numOfUsers!; - maxMonth = provider.monthTraffic.data!.reduce(maxi).numOfUsers!; - maxYear = provider.yearTraffic.data!.reduce(maxi).numOfUsers!; + provider.dayTraffic.data = []; + maxDay = (provider.dayTraffic.data!.isEmpty) + ? 0 + : (provider.dayTraffic.data!.length == 1) + ? provider.dayTraffic.data![0].numOfUsers! + : provider.dayTraffic.data!.reduce(maxi).numOfUsers!; + + maxWeek = (provider.weekTraffic.data!.isEmpty) + ? 0 + : (provider.weekTraffic.data!.length == 1) + ? provider.weekTraffic.data![0].numOfUsers! + : provider.weekTraffic.data!.reduce(maxi).numOfUsers!; + maxMonth = (provider.monthTraffic.data!.isEmpty) + ? 0 + : (provider.monthTraffic.data!.length == 1) + ? provider.monthTraffic.data![0].numOfUsers! + : provider.monthTraffic.data!.reduce(maxi).numOfUsers!; + maxYear = (provider.yearTraffic.data!.isEmpty) + ? 0 + : (provider.yearTraffic.data!.length == 1) + ? provider.yearTraffic.data![0].numOfUsers! + : provider.yearTraffic.data!.reduce(maxi).numOfUsers!; for (int i = 0; i < provider.dayTraffic.data!.length; i += 1) { daysValues[provider.dayTraffic.data![i].iId! - 1] = @@ -108,9 +172,6 @@ class _TraficStateState extends State { daysValues[provider.dayTraffic.data![i].iId! - 1] /= maxDay.toDouble(); } - // if (provider.dayTraffic.data![i].numOfUsers! > maxDay) { - // maxDay = provider.dayTraffic.data![i].numOfUsers!; - // } } for (int i = 0; i < provider.weekTraffic.data!.length; i += 1) { weeksValues[provider.weekTraffic.data![i].iId! - 1] = @@ -118,9 +179,6 @@ class _TraficStateState extends State { if (maxWeek != 0) { weeksValues[provider.weekTraffic.data![i].iId! - 1] /= maxWeek; } - // if (provider.weekTraffic.data![i].numOfUsers! > maxWeek) { - // maxWeek = provider.weekTraffic.data![i].numOfUsers!; - // } } for (int i = 0; i < provider.monthTraffic.data!.length; i += 1) { monthsValues[provider.monthTraffic.data![i].iId! - 1] = @@ -128,9 +186,6 @@ class _TraficStateState extends State { if (maxMonth != 0) { monthsValues[provider.monthTraffic.data![i].iId! - 1] /= maxMonth; } - // if (provider.monthTraffic.data![i].numOfUsers! > maxMonth) { - // maxMonth = provider.monthTraffic.data![i].numOfUsers!; - // } } for (int i = 0; i < provider.yearTraffic.data!.length; i += 1) { yearsValues[provider.yearTraffic.data![i].iId! - startYear] = @@ -139,9 +194,6 @@ class _TraficStateState extends State { yearsValues[provider.yearTraffic.data![i].iId! - startYear] /= maxYear; } - // if (provider.yearTraffic.data![i].numOfUsers! > maxYear) { - // maxYear = provider.yearTraffic.data![i].numOfUsers!; - // } } fetchingDone = true; @@ -247,16 +299,16 @@ class _TraficStateState extends State { ? months : years, labelY: [ - '${(maxmembers * .1)}', - '${(maxmembers * .2)}', - '${(maxmembers * .3)}', - '${(maxmembers * .4)}', - '${(maxmembers * .5)}', - '${(maxmembers * .6)}', - '${(maxmembers * .7)}', - '${(maxmembers * .8)}', - '${(maxmembers * .9)}', - '${(maxmembers * 1)}' + '${double.parse(((maxmembers * .1)).toStringAsFixed(2))}', + '${double.parse(((maxmembers * .2)).toStringAsFixed(2))}', + '${double.parse(((maxmembers * .3)).toStringAsFixed(2))}', + '${double.parse(((maxmembers * .4)).toStringAsFixed(2))}', + '${double.parse(((maxmembers * .5)).toStringAsFixed(2))}', + '${double.parse(((maxmembers * .6)).toStringAsFixed(2))}', + '${double.parse(((maxmembers * .7)).toStringAsFixed(2))}', + '${double.parse(((maxmembers * .8)).toStringAsFixed(2))}', + '${double.parse(((maxmembers * .9)).toStringAsFixed(2))}', + '${double.parse(((maxmembers * 1)).toStringAsFixed(2))}', ], showDescription: true, graphColor: Colors.black87, diff --git a/lib/moderation_settings/screens/un_moderated_posts.dart b/lib/moderation_settings/screens/un_moderated_posts.dart new file mode 100644 index 00000000..f0ec57b1 --- /dev/null +++ b/lib/moderation_settings/screens/un_moderated_posts.dart @@ -0,0 +1,16 @@ +import 'package:flutter/material.dart'; + +class UnModerated extends StatefulWidget { + const UnModerated({super.key}); + + @override + State createState() => _UnModeratedState(); +} + +class _UnModeratedState extends State { + + @override + Widget build(BuildContext context) { + return Container(); + } +} diff --git a/lib/moderation_settings/widgets/alert_dialog.dart b/lib/moderation_settings/widgets/alert_dialog.dart index a4bbde3f..484a3b40 100644 --- a/lib/moderation_settings/widgets/alert_dialog.dart +++ b/lib/moderation_settings/widgets/alert_dialog.dart @@ -35,7 +35,6 @@ class AlertDialog1 extends StatelessWidget { borderRadius: BorderRadius.circular(30), ), primary: Colors.grey[200], - // backgroundColor: Colors.grey[200], ), child: const Text( 'Cancel', @@ -55,7 +54,6 @@ class AlertDialog1 extends StatelessWidget { borderRadius: BorderRadius.circular(30), ), primary: Colors.blue[800], - // backgroundColor: Colors.blue[800], ), child: const Text( 'Leave', diff --git a/lib/moderation_settings/widgets/topics_button.dart b/lib/moderation_settings/widgets/topics_button.dart index 0c6c66d3..9d127409 100644 --- a/lib/moderation_settings/widgets/topics_button.dart +++ b/lib/moderation_settings/widgets/topics_button.dart @@ -27,7 +27,6 @@ class TopicButton extends StatelessWidget { child: ElevatedButton( style: ElevatedButton.styleFrom( shape: const StadiumBorder(), - // backgroundColor primary: (selectedIndex == myIndex || selectedBefore == keyAns) ? Colors.blueGrey.shade100 : Colors.grey.shade50, diff --git a/lib/myprofile/screens/myprofile_screen.dart b/lib/myprofile/screens/myprofile_screen.dart index b5166149..99f39ded 100644 --- a/lib/myprofile/screens/myprofile_screen.dart +++ b/lib/myprofile/screens/myprofile_screen.dart @@ -2,12 +2,15 @@ import 'package:flutter/material.dart'; import 'package:flutter/foundation.dart' show kIsWeb; import 'package:responsive_sizer/responsive_sizer.dart'; import 'package:provider/provider.dart'; +import 'package:get/get.dart'; import '../../widgets/loading_reddit.dart'; import '../widgets/myprofile_web.dart'; import '../widgets/myprofile_app.dart'; import '../models/myprofile_data.dart'; import '../providers/myprofile_provider.dart'; - +import '../../home/widgets/custom_upper_bar.dart'; +import '../../home/controller/home_controller.dart'; +import '../../createpost/controllers/posts_controllers.dart'; class MyProfileScreen extends StatefulWidget { static const routeName = '/myprofile'; @@ -81,10 +84,18 @@ class _MyProfileState extends State _isInit = false; super.didChangeDependencies(); } - + final HomeController controller = Get.put( + HomeController(), + ); + final PostController controllerForPost = Get.put( + PostController(), + ); @override Widget build(BuildContext context) { return Scaffold( + appBar: ( kIsWeb)?PreferredSize( + preferredSize: Size(700, 60), + child: UpBar(controller: controller, controllerForCreatePost: controllerForPost,)):null, body: _isLoading ? LoadingReddit() : kIsWeb diff --git a/lib/myprofile/widgets/myprofile_web.dart b/lib/myprofile/widgets/myprofile_web.dart index 0b0de31c..4b7ed34b 100644 --- a/lib/myprofile/widgets/myprofile_web.dart +++ b/lib/myprofile/widgets/myprofile_web.dart @@ -5,6 +5,8 @@ import '../widgets/myprofile_about.dart'; import '../../widgets/myprofile_comment_web.dart'; import '../../widgets/back_to_button.dart'; import '../../widgets/myprofile_post_web.dart'; +import 'package:get/get.dart'; +import '../screens/myprofile_screen.dart'; class MyProfileWeb extends StatelessWidget { MyProfileWeb( @@ -31,8 +33,22 @@ class MyProfileWeb extends StatelessWidget { color: Colors.white, child: _tabBar, )), - floatingActionButton: - BackToTopButton(scrollController: scrollController), + floatingActionButtonLocation: FloatingActionButtonLocation.miniEndFloat, + floatingActionButton: Padding( + padding: const EdgeInsetsDirectional.only(end: 320.0), + child: ElevatedButton( + onPressed: () { + Navigator.of(context).pushNamed(MyProfileScreen.routeName, + arguments: loadProfile.userName); + }, + child: Text( + ' Back to top ', + style: TextStyle(color: Colors.white), + ), + style: ElevatedButton.styleFrom( + shape: StadiumBorder(), backgroundColor: Colors.blue), + ), + ), body: isLoading ? const Center( child: Icon( @@ -42,11 +58,13 @@ class MyProfileWeb extends StatelessWidget { ) : TabBarView(controller: controller, children: [ OverviewMyProfileWeb( - type: 'MyProfile', + type: 'MyProfile', loadProfile: loadProfile, scrollController: scrollController), - MyProfilePostWeb( userName: loadProfile.userName.toString()), - MyProfileCommentWeb(scrollController: scrollController,userName: loadProfile.userName.toString()), + MyProfilePostWeb(userName: loadProfile.userName.toString()), + MyProfileCommentWeb( + scrollController: scrollController, + userName: loadProfile.userName.toString()), MyProfileAbout( int.parse(loadProfile.postKarma.toString()), int.parse(loadProfile.commentkarma.toString()), diff --git a/lib/networks/const_endpoint_data.dart b/lib/networks/const_endpoint_data.dart index 43fdab26..6bc26843 100644 --- a/lib/networks/const_endpoint_data.dart +++ b/lib/networks/const_endpoint_data.dart @@ -4,11 +4,9 @@ var baseUrl = dotenv.env['API'] as String; //=========================Subreddit======================// var subredditName; const createCommunity = '/subreddits/'; -//var getCommunity = '/subreddits/{subredditName}'; const notificationResults = '/users/notifications'; const moderationTools = '/subreddits/{subredditName}'; var subreddit = '/subreddits/${subredditName}'; -//const moderators = '/subreddits/{subredditName}/about/moderators'; const mySubreddits = '/subreddits/mine'; const getPostsInHome = '/user/best/posts'; var getFlairsOfSubreddit = '/subreddits/${subredditName}/flairs'; @@ -16,7 +14,6 @@ var getFlairsOfSubreddit = '/subreddits/${subredditName}/flairs'; const myprofile = '/users/me'; var userName; var otherprofile = '/users/${userName}/about'; -//const myprofile = '/users/ZeinabMoawad/about'; var myprofilePosts = '/users/${userName}/posts'; //====================Posts======================// var createPost = '/posts'; diff --git a/lib/networks/dio_client.dart b/lib/networks/dio_client.dart index cf4545df..8a8afb5e 100644 --- a/lib/networks/dio_client.dart +++ b/lib/networks/dio_client.dart @@ -10,8 +10,6 @@ class DioClient { String token = ''; try { token = prefs.getString('token') as String; - print('hi'); - print(token); } catch (error) { print('dio error $error'); } @@ -36,7 +34,6 @@ class DioClient { {required String path, required Map? data, Map? query}) async { - print('datain dio post : $data'); return await dio! .post(path, data: json.encode(data), queryParameters: query); } diff --git a/lib/notification/models/notification_class_model.dart b/lib/notification/models/notification_class_model.dart index e481581a..983decfc 100644 --- a/lib/notification/models/notification_class_model.dart +++ b/lib/notification/models/notification_class_model.dart @@ -1,11 +1,6 @@ class NotificationModel { String? sId; String? type; - //FollowedUser? followedUser; - //FollowedUser? followerUser; - //FollowedSubreddit? followedSubreddit; - //Post? post; - //Comment? comment; String? createdAt; bool? seen; bool? hidden; @@ -21,11 +16,6 @@ class NotificationModel { NotificationModel( {this.sId, this.type, - // this.followedUser, - //this.followerUser, - //this.followedSubreddit, - //this.post, - //this.comment, this.createdAt, this.seen, this.hidden, @@ -43,16 +33,12 @@ class NotificationModel { : json['followedSubreddit']['_id'] as String; String? h = json['followedSubreddit']['fixedName'] ?? ''; requiredName = 'r/$h'; - print(requiredName); } else if (json['followedUser'] != null) { - print('folllllllllllllllllllllllllll'); requiredId = (json['followedUser']['_id'] == null) ? '' : json['followedUser']['_id'] as String; String? h = json['followedUser']['userName'] ?? ''; requiredName = 'u/$h'; - // requiredName = json['followedUser']['userName'] ?? ''; - //name = json['name']; } else { requiredId = ''; requiredName = ''; @@ -74,15 +60,12 @@ class NotificationModel { } if (json['post'] != null) { - print('In class Model'); - // print(json['post']['_id'].runtimeType); postId = (json['post'] == null) ? '' : json['post'] as String; } else { postId = ''; } // comment = if (json['comment'] != null) { - print('In mcommmmeeeeeeen'); commentId = (json['comment']['_id'] == null) ? '' : json['comment']['_id'] as String; @@ -95,476 +78,4 @@ class NotificationModel { seen = json['seen']; hidden = json['hidden']; } - -// Map toJson() { -// final Map data = Map(); -// data['_id'] = this.sId; -// data['type'] = this.type; -// // if (this.followedUser != null) { -// // data['followedUser'] = this.followedUser!.toJson(); -// // } -// // if (this.followerUser != null) { -// // data['followerUser'] = this.followerUser!.toJson(); -// // } -// // if (this.followedSubreddit != null) { -// // data['followedSubreddit'] = this.followedSubreddit!.toJson(); -// // } -// // if (this.post != null) { -// // data['post'] = this.post!.toJson(); -// // } -// // if (this.comment != null) { -// // data['comment'] = this.comment!.toJson(); -// // } -// data['createdAt'] = this.createdAt; -// data['seen'] = this.seen; -// data['hidden'] = this.hidden; -// return data; -// } -// } } - -// class FollowedUser { -// int? iId; -// String? userName; -// // String email; -// // String profilePicture; -// // String profileBackground; -// // bool adultContent; -// // bool autoplayMedia; -// // bool canbeFollowed; -// // String lastUpdatedPassword; -// // int followersCount; -// // int friendsCount; -// // bool accountActivated; -// // String gender; -// // String displayName; -// // int postKarma; -// // int commentKarma; -// // String createdAt; -// // String description; -// // List socialLinks; - -// FollowedUser({ -// this.iId, -// this.userName, -// // this.email, -// // this.profilePicture, -// // this.profileBackground, -// // this.adultContent, -// // this.autoplayMedia, -// // this.canbeFollowed, -// // this.lastUpdatedPassword, -// // this.followersCount, -// // this.friendsCount, -// // this.accountActivated, -// // this.gender, -// // this.displayName, -// // this.postKarma, -// // this.commentKarma, -// // this.createdAt, -// // this.description, -// // this.socialLinks -// }); - -// FollowedUser.fromJson(Map json) { -// iId = json['_id']; -// userName = json['userName']; -// // email = json['email']; -// // profilePicture = json['profilePicture']; -// // profileBackground = json['profileBackground']; -// // adultContent = json['adultContent']; -// // autoplayMedia = json['autoplayMedia']; -// // canbeFollowed = json['canbeFollowed']; -// // lastUpdatedPassword = json['lastUpdatedPassword']; -// // followersCount = json['followersCount']; -// // friendsCount = json['friendsCount']; -// // accountActivated = json['accountActivated']; -// // gender = json['gender']; -// // displayName = json['displayName']; -// // postKarma = json['postKarma']; -// // commentKarma = json['commentKarma']; -// // createdAt = json['createdAt']; -// // description = json['description']; -// // if (json['socialLinks'] != null) { -// // socialLinks = new List(); -// // json['socialLinks'].forEach((v) { -// // socialLinks.add(new SocialLinks.fromJson(v)); -// // }); -// // } -// } - -// Map toJson() { -// final Map data = new Map(); -// data['_id'] = this.iId; -// data['userName'] = this.userName; -// // data['email'] = this.email; -// // data['profilePicture'] = this.profilePicture; -// // data['profileBackground'] = this.profileBackground; -// // data['adultContent'] = this.adultContent; -// // data['autoplayMedia'] = this.autoplayMedia; -// // data['canbeFollowed'] = this.canbeFollowed; -// // data['lastUpdatedPassword'] = this.lastUpdatedPassword; -// // data['followersCount'] = this.followersCount; -// // data['friendsCount'] = this.friendsCount; -// // data['accountActivated'] = this.accountActivated; -// // data['gender'] = this.gender; -// // data['displayName'] = this.displayName; -// // data['postKarma'] = this.postKarma; -// // data['commentKarma'] = this.commentKarma; -// // data['createdAt'] = this.createdAt; -// // data['description'] = this.description; -// // if (this.socialLinks != null) { -// // data['socialLinks'] = this.socialLinks.map((v) => v.toJson()).toList(); -// // } -// return data; -// } -// } - -// class FollowedSubreddit { -// String? sId; -// // bool isJoined; -// // String createdAt; -// // String primaryTopic; -// // String backgroundImage; -// // String icon; -// // List rules; -// String? fixedName; -// String? name; -// // String description; -// // List topics; -// // String language; -// // String region; -// // String type; -// // bool nsfw; -// // String postType; -// // bool allowCrossposting; -// // bool allowArchivePosts; -// // bool allowSpoilerTag; -// // bool allowGif; -// // bool allowImageUploads; -// // bool allowMultipleImage; -// // List moderators; - -// FollowedSubreddit({ -// this.sId, -// // this.isJoined, -// // this.createdAt, -// // this.primaryTopic, -// // this.backgroundImage, -// // this.icon, -// // this.rules, -// this.fixedName, -// this.name, -// // this.description, -// // this.topics, -// // this.language, -// // this.region, -// // this.type, -// // this.nsfw, -// // this.postType, -// // this.allowCrossposting, -// // this.allowArchivePosts, -// // this.allowSpoilerTag, -// // this.allowGif, -// // this.allowImageUploads, -// // this.allowMultipleImage, -// // this.moderators -// }); - -// FollowedSubreddit.fromJson(Map json) { -// sId = json['_id']; -// // isJoined = json['isJoined']; -// // createdAt = json['createdAt']; -// // primaryTopic = json['primaryTopic']; -// // backgroundImage = json['backgroundImage']; -// // icon = json['icon']; -// // if (json['rules'] != null) { -// // rules = new List(); -// // json['rules'].forEach((v) { -// // rules.add(new Rules.fromJson(v)); -// // }); -// // } -// fixedName = json['fixedName']; -// name = json['name']; -// // description = json['description']; -// // if (json['topics'] != null) { -// // topics = new List(); -// // json['topics'].forEach((v) { -// // topics.add(new Null.fromJson(v)); -// // }); -// // } -// // language = json['language']; -// // region = json['region']; -// // type = json['type']; -// // nsfw = json['nsfw']; -// // postType = json['postType']; -// // allowCrossposting = json['allowCrossposting']; -// // allowArchivePosts = json['allowArchivePosts']; -// // allowSpoilerTag = json['allowSpoilerTag']; -// // allowGif = json['allowGif']; -// // allowImageUploads = json['allowImageUploads']; -// // allowMultipleImage = json['allowMultipleImage']; -// // if (json['moderators'] != null) { -// // moderators = new List(); -// // json['moderators'].forEach((v) { -// // moderators.add(new Moderators.fromJson(v)); -// // }); -// // } -// } - -// Map toJson() { -// final Map data = new Map(); -// data['_id'] = this.sId; -// // data['isJoined'] = this.isJoined; -// // data['createdAt'] = this.createdAt; -// // data['primaryTopic'] = this.primaryTopic; -// // data['backgroundImage'] = this.backgroundImage; -// // data['icon'] = this.icon; -// // if (this.rules != null) { -// // data['rules'] = this.rules.map((v) => v.toJson()).toList(); -// // } -// data['fixedName'] = this.fixedName; -// data['name'] = this.name; -// // data['description'] = this.description; -// // if (this.topics != null) { -// // data['topics'] = this.topics.map((v) => v.toJson()).toList(); -// // } -// // data['language'] = this.language; -// // data['region'] = this.region; -// // data['type'] = this.type; -// // data['nsfw'] = this.nsfw; -// // data['postType'] = this.postType; -// // data['allowCrossposting'] = this.allowCrossposting; -// // data['allowArchivePosts'] = this.allowArchivePosts; -// // data['allowSpoilerTag'] = this.allowSpoilerTag; -// // data['allowGif'] = this.allowGif; -// // data['allowImageUploads'] = this.allowImageUploads; -// // data['allowMultipleImage'] = this.allowMultipleImage; -// // if (this.moderators != null) { -// // data['moderators'] = this.moderators.map((v) => v.toJson()).toList(); -// // } -// return data; -// } -// } - -// class Post { -// String? sId; -// // String author; -// // String owner; -// // String title; -// // String kind; -// // String text; -// // String url; -// // List images; -// // String video; -// // int votes; -// // int shareCount; -// // int views; -// // int commentCount; -// // String createdAt; -// // String suggestedSort; -// // bool nsfw; -// // bool spoiler; -// // bool sendReplies; -// // bool locked; -// // String modState; -// // String flairId; -// // String flairText; - -// Post({ -// this.sId, -// // this.author, -// // this.owner, -// // this.title, -// // this.kind, -// // this.text, -// // this.url, -// // this.images, -// // this.video, -// // this.votes, -// // this.shareCount, -// // this.views, -// // this.commentCount, -// // this.createdAt, -// // this.suggestedSort, -// // this.nsfw, -// // this.spoiler, -// // this.sendReplies, -// // this.locked, -// // this.modState, -// // this.flairId, -// // this.flairText -// }); - -// Post.fromJson(Map json) { -// sId = json['_id']; -// // author = json['author']; -// // owner = json['owner']; -// // title = json['title']; -// // kind = json['kind']; -// // text = json['text']; -// // url = json['url']; -// // images = json['images'].cast(); -// // video = json['video']; -// // votes = json['votes']; -// // shareCount = json['shareCount']; -// // views = json['views']; -// // commentCount = json['commentCount']; -// // createdAt = json['createdAt']; -// // suggestedSort = json['suggestedSort']; -// // nsfw = json['nsfw']; -// // spoiler = json['spoiler']; -// // sendReplies = json['sendReplies']; -// // locked = json['locked']; -// // modState = json['modState']; -// // flairId = json['flairId']; -// // flairText = json['flairText']; -// } - -// Map toJson() { -// final Map data = new Map(); -// data['_id'] = this.sId; -// // data['author'] = this.author; -// // data['owner'] = this.owner; -// // data['title'] = this.title; -// // data['kind'] = this.kind; -// // data['text'] = this.text; -// // data['url'] = this.url; -// // data['images'] = this.images; -// // data['video'] = this.video; -// // data['votes'] = this.votes; -// // data['shareCount'] = this.shareCount; -// // data['views'] = this.views; -// // data['commentCount'] = this.commentCount; -// // data['createdAt'] = this.createdAt; -// // data['suggestedSort'] = this.suggestedSort; -// // data['nsfw'] = this.nsfw; -// // data['spoiler'] = this.spoiler; -// // data['sendReplies'] = this.sendReplies; -// // data['locked'] = this.locked; -// // data['modState'] = this.modState; -// // data['flairId'] = this.flairId; -// // data['flairText'] = this.flairText; -// return data; -// } -// } - -// class Comment { -// String? sId; -// // String parent; -// // String parentType; -// // Author author; -// // Owner owner; -// // String ownerType; -// // String post; -// // String title; -// // String text; -// // String createdAt; -// // int votes; -// // String commentVoteStatus; -// // bool isDeleted; -// // List mentions; -// // bool isSaved; - -// Comment({ -// this.sId, -// // this.parent, -// // this.parentType, -// // this.author, -// // this.owner, -// // this.ownerType, -// // this.post, -// // this.title, -// // this.text, -// // this.createdAt, -// // this.votes, -// // this.commentVoteStatus, -// // this.isDeleted, -// // this.mentions, -// // this.isSaved -// }); - -// Comment.fromJson(Map json) { -// sId = json['_id']; -// // parent = json['parent']; -// // parentType = json['parentType']; -// // author = -// // json['author'] != null ? new Author.fromJson(json['author']) : null; -// // owner = json['owner'] != null ? new Owner.fromJson(json['owner']) : null; -// // ownerType = json['ownerType']; -// // post = json['post']; -// // title = json['title']; -// // text = json['text']; -// // createdAt = json['createdAt']; -// // votes = json['votes']; -// // commentVoteStatus = json['commentVoteStatus']; -// // isDeleted = json['isDeleted']; -// // mentions = json['mentions'].cast(); -// // isSaved = json['isSaved']; -// } - -// Map toJson() { -// final Map data = new Map(); -// data['_id'] = this.sId; -// // data['parent'] = this.parent; -// // data['parentType'] = this.parentType; -// // if (this.author != null) { -// // data['author'] = this.author.toJson(); -// // } -// // if (this.owner != null) { -// // data['owner'] = this.owner.toJson(); -// // } -// // data['ownerType'] = this.ownerType; -// // data['post'] = this.post; -// // data['title'] = this.title; -// // data['text'] = this.text; -// // data['createdAt'] = this.createdAt; -// // data['votes'] = this.votes; -// // data['commentVoteStatus'] = this.commentVoteStatus; -// // data['isDeleted'] = this.isDeleted; -// // data['mentions'] = this.mentions; -// // data['isSaved'] = this.isSaved; -// return data; -// } -// } - -// // class Author { -// // String sId; -// // String name; - -// // Author({this.sId, this.name}); - -// // Author.fromJson(Map json) { -// // sId = json['_id']; -// // name = json['name']; -// // } - -// // Map toJson() { -// // final Map data = new Map(); -// // data['_id'] = this.sId; -// // data['name'] = this.name; -// // return data; -// // } -// // } - -// // class Owner { -// // String sId; -// // String name; -// // String icon; - -// // Owner({this.sId, this.name, this.icon}); - -// // Owner.fromJson(Map json) { -// // sId = json['_id']; -// // name = json['name']; -// // icon = json['icon']; -// // } - -// // Map toJson() { -// // final Map data = new Map(); -// // data['_id'] = this.sId; -// // data['name'] = this.name; -// // data['icon'] = this.icon; -// // return data; -// // } -// // } diff --git a/lib/notification/provider/notification_provider.dart b/lib/notification/provider/notification_provider.dart index 8a34bd51..75cd4eed 100644 --- a/lib/notification/provider/notification_provider.dart +++ b/lib/notification/provider/notification_provider.dart @@ -38,16 +38,6 @@ class NotificationProvider with ChangeNotifier { notifyListeners(); } - // void incrementCounter() { - // count = count!+1; - // notifyListeners(); - // } - - // void decrementCounter() { - // counter = counter--; - // notifyListeners(); - // } - //Get notification Future getNotification(BuildContext context) async { try { @@ -57,25 +47,9 @@ class NotificationProvider with ChangeNotifier { listEariler = []; final response = await DioClient.get(path: notificationResults).then((value) { - // print('hiii'); - print(value); - print(value.data['data'].runtimeType); - // notificationClassModel = NotificationModel.fromJson(value.data['data']); - // if (DateTime.now() - // .difference(DateTime.parse( - // notificationClassModel!.createdAt.toString())) - // .inDays > - // 1) { - // listEariler.add(notificationClassModel!); - // } else { - // listToday.add(notificationClassModel!); - // } value.data['data'].forEach((value1) { - print(value1); - print('hoiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii'); notificationClassModel = NotificationModel.fromJson(value1); - print(notificationClassModel); if (DateTime.now() .difference(DateTime.parse( notificationClassModel!.createdAt.toString())) @@ -89,18 +63,13 @@ class NotificationProvider with ChangeNotifier { listToday.add(notificationClassModel!); } } - // print(value1.runtimeType); - // print('fghjkl'); }); }); notifyListeners(); - // return true; } on DioError catch (e) { HandleError.errorHandler(e, context); - //return false; } catch (error) { HandleError.handleError(error.toString(), context); - //return false; } } @@ -110,26 +79,17 @@ class NotificationProvider with ChangeNotifier { DioClient.init(prefs); await DioClient.patch(path: '/users/notifications/mark_as_read'); notifyListeners(); - // return true; } on DioError catch (e) { HandleError.errorHandler(e, context); - //return false; } catch (error) { HandleError.handleError(error.toString(), context); - //return false; } } - - //http://localhost:8000/api/v1/users/notifications/{notificationId}/hide Future markAndHideThisNotification( BuildContext context, notificationId, type, i) async { try { final prefs = await SharedPreferences.getInstance(); DioClient.init(prefs); - // print(notificationId); - // print(type); - // print('/users/notifications/{$notificationId}/$type'); - // type ==> hide or mark_as_read if (type == 'hide') { (i == 1) ? listEariler @@ -138,13 +98,10 @@ class NotificationProvider with ChangeNotifier { } await DioClient.patch(path: '/users/notifications/$notificationId/$type'); notifyListeners(); - // return true; } on DioError catch (e) { HandleError.errorHandler(e, context); - //return false; } catch (error) { HandleError.handleError(error.toString(), context); - //return false; } } } diff --git a/lib/notification/screens/notifications_main_screen.dart b/lib/notification/screens/notifications_main_screen.dart index 95c1440a..13b06002 100644 --- a/lib/notification/screens/notifications_main_screen.dart +++ b/lib/notification/screens/notifications_main_screen.dart @@ -54,7 +54,6 @@ class _NotificationsMainScreenState extends State { if (i == 0) { if (index.type == 'follow') { //Navigate to user profile - print(index.requiredName!.substring(2, index.requiredName!.length)); Navigator.popAndPushNamed(context, OthersProfileScreen.routeName, arguments: @@ -99,7 +98,6 @@ class _NotificationsMainScreenState extends State { String getTimeOfNotification(date) { String howOld; final difference = DateTime.now().difference(DateTime.parse(date)); - //print(DateTime.parse(date)); if (difference.inDays >= 30) { howOld = DateFormat.MMMMd().format(DateTime.parse(date)); } else if (difference.inDays >= 1) { @@ -126,7 +124,6 @@ class _NotificationsMainScreenState extends State { hideThisNotification(notificationId, i) { _hideThisNotification(notificationId, i); - print('in hides'); Navigator.of(context).pop(); } @@ -359,7 +356,6 @@ class _NotificationsMainScreenState extends State { //Reply back : Button reply back that appears to reply on a comment Widget? notificationMain( text, description, type, time, Widget image, width, commentId, postId) { - print(description); if (type == 'commentReply') { return Column( children: [ diff --git a/lib/notification/screens/notifications_screen.dart b/lib/notification/screens/notifications_screen.dart index a4cc0491..78e5a1c0 100644 --- a/lib/notification/screens/notifications_screen.dart +++ b/lib/notification/screens/notifications_screen.dart @@ -95,15 +95,18 @@ class _NotificationScreenState extends State { }); } + + //Call end point to mark notification as read + //return void _markAsRead() async { await Provider.of(context, listen: false) .markAllAsRead(context); } - + //Call end point to mark all notification as read + //return void markAllAsRead() { _markAsRead(); usersNotificationEarlier.forEach((element) { - print(element.seen); if (!element.seen!) { setState(() { element.seen = true; @@ -120,13 +123,6 @@ class _NotificationScreenState extends State { if (!kIsWeb) Navigator.of(context).pop(); } - _saveNewMessage(username, subject, message, messageShow) { - Provider.of(context, listen: false).createMessage( - {'to': username, 'text': message, 'subject': subject}, - context).then((value) { - setState(() {}); - }); - } @override Widget build(BuildContext context) { diff --git a/lib/notification/widgets/main_modal_bottom_sheet.dart b/lib/notification/widgets/main_modal_bottom_sheet.dart index 57e97a8b..1ac5bc0f 100644 --- a/lib/notification/widgets/main_modal_bottom_sheet.dart +++ b/lib/notification/widgets/main_modal_bottom_sheet.dart @@ -5,8 +5,6 @@ import 'package:responsive_sizer/responsive_sizer.dart'; class MainModalBottomSheet extends StatelessWidget { MainModalBottomSheet({super.key, required this.listOfWidgets}); List listOfWidgets; - //Map listOfMenu; - @override Widget build(BuildContext context) { return Expanded( diff --git a/lib/other_profile/screens/others_profile_screen.dart b/lib/other_profile/screens/others_profile_screen.dart index b6e3bbea..d2e3f666 100644 --- a/lib/other_profile/screens/others_profile_screen.dart +++ b/lib/other_profile/screens/others_profile_screen.dart @@ -2,12 +2,15 @@ import 'package:flutter/material.dart'; import 'package:flutter/foundation.dart' show kIsWeb; import 'package:post/other_profile/models/others_profile_data.dart'; import 'package:responsive_sizer/responsive_sizer.dart'; +import 'package:get/get.dart'; import 'package:provider/provider.dart'; import '../../widgets/loading_reddit.dart'; import '../widgets/other_profile_web.dart'; import '../widgets/other_profile_app.dart'; import '../providers/other_profile_provider.dart'; - +import '../../home/widgets/custom_upper_bar.dart'; +import '../../home/controller/home_controller.dart'; +import '../../createpost/controllers/posts_controllers.dart'; class OthersProfileScreen extends StatefulWidget { static const routeName = '/OthersProfileScreen'; @@ -81,10 +84,18 @@ class _OthersProfileScreenState extends State //==================================================// super.didChangeDependencies(); } - + final HomeController controller = Get.put( + HomeController(), + ); + final PostController controllerForPost = Get.put( + PostController(), + ); @override Widget build(BuildContext context) { return Scaffold( + appBar: ( kIsWeb)?PreferredSize( + preferredSize: Size(700, 60), + child: UpBar(controller: controller, controllerForCreatePost: controllerForPost,)):null, body: _isLoading ? LoadingReddit() : kIsWeb diff --git a/lib/other_profile/widgets/other_profile_web.dart b/lib/other_profile/widgets/other_profile_web.dart index c172b5bc..bb098298 100644 --- a/lib/other_profile/widgets/other_profile_web.dart +++ b/lib/other_profile/widgets/other_profile_web.dart @@ -5,7 +5,7 @@ import '../widgets/others_profile_about.dart'; import '../../widgets/myprofile_comment_web.dart'; import '../../widgets/back_to_button.dart'; import '../../widgets/myprofile_post_web.dart'; - +import '../screens/others_profile_screen.dart'; class OtherProfileWeb extends StatelessWidget { OtherProfileWeb( {Key? key, @@ -31,8 +31,22 @@ class OtherProfileWeb extends StatelessWidget { color: Colors.white, child: _tabBar, )), - floatingActionButton: - BackToTopButton(scrollController: scrollController), + floatingActionButtonLocation: FloatingActionButtonLocation.miniEndFloat, + floatingActionButton: Padding( + padding: const EdgeInsetsDirectional.only(end: 320.0), + child: ElevatedButton( + onPressed: () { + Navigator.of(context).pushNamed(OthersProfileScreen.routeName, + arguments: loadProfile.userName); + }, + child: Text( + ' Back to top ', + style: TextStyle(color: Colors.white), + ), + style: ElevatedButton.styleFrom( + shape: StadiumBorder(), backgroundColor: Colors.blue), + ), + ), body: isLoading ? const Center( child: Icon( diff --git a/lib/post/models/post_model.dart b/lib/post/models/post_model.dart index ff172d48..f84d6f0a 100644 --- a/lib/post/models/post_model.dart +++ b/lib/post/models/post_model.dart @@ -154,24 +154,24 @@ class PostModel { print(json['url'].runtimeType); sId = json['_id']; - ownerType = json['ownerType']; + ownerType = json['ownerType'] ?? 'Subreddit'; replies = json['replies']; - title = json['title']; - kind = json['kind']; - text = json['text']; - images = json['images']; - createdAt = json['createdAt']; + title = json['title'] ?? ''; + kind = json['kind'] ?? 'self'; + text = json['text'] ?? ''; + images = json['images'] ?? []; + createdAt = json['createdAt'] ?? '2017-07-21T17:32:28Z'; locked = json['locked'] ?? false; isDeleted = json['isDeleted'] ?? false; sendReplies = json['sendReplies'] ?? false; nsfw = json['nsfw'] ?? false; spoiler = json['spoiler'] ?? false; - votes = json['votes']; - views = json['views']; - commentCount = json['commentCount']; - shareCount = json['shareCount']; - suggestedSort = json['suggestedSort']; - scheduled = json['scheduled']; + votes = json['votes'] ?? 0; + views = json['views'] ?? 0; + commentCount = json['commentCount'] ?? 0; + shareCount = json['shareCount'] ?? 0; + suggestedSort = json['suggestedSort'] ?? ''; + scheduled = json['scheduled'] ?? false; flairId = json['flairId'] != null ? new FlairId.fromJson(json['flairId']) : null; isHidden = json['isHidden'] ?? false; diff --git a/lib/post/widgets/post.dart b/lib/post/widgets/post.dart index ca3bc888..eb6a0339 100644 --- a/lib/post/widgets/post.dart +++ b/lib/post/widgets/post.dart @@ -12,13 +12,18 @@ import './post_body.dart'; /// This is the main post Widget. /// -/// It takes a map of the post data. class Post extends StatefulWidget { + /// The data of the post final PostModel data; bool _inHome = false, _inProfile = false; //final Function updateDate; + /// A boolean to check if in view final bool inView; + + /// The user name final String userName; + + /// a boolean to determine if in the post screen final bool inScreen; /// This is the constructor for home page. @@ -70,11 +75,10 @@ class _PostState extends State { @override void didChangeDependencies() async { - // TODO: implement didChangeDependencies if (widget.data.isModerator == null && widget.data.ownerType == 'Subreddit') { final provider = - await Provider.of(context, listen: false); + Provider.of(context, listen: false); await provider .getUser( widget.data.owner?.name as String, UserCase.moderator, context) @@ -83,8 +87,6 @@ class _PostState extends State { for (var mod in moderators!) { widget.data.isModerator = false; - print('=============Is mod:${mod.userName}======================='); - if (mod.userName == widget.userName) { widget.data.isModerator = true; break; @@ -109,7 +111,7 @@ class _PostState extends State { // imageNumber: data.imageNumber, // ); return widget.data.isDeleted ?? false - ? SizedBox() + ? const SizedBox() : Container( margin: const EdgeInsetsDirectional.only(bottom: 10), child: Column( diff --git a/lib/post/widgets/post_body.dart b/lib/post/widgets/post_body.dart index 160a797f..1110854b 100644 --- a/lib/post/widgets/post_body.dart +++ b/lib/post/widgets/post_body.dart @@ -1,17 +1,16 @@ import 'package:flutter/material.dart'; +import 'package:html/parser.dart'; import 'package:post/post/models/post_model.dart'; import 'package:post/post/widgets/post_card.dart'; import 'package:post/post/widgets/post_images.dart'; import 'package:post/post/widgets/post_images_web.dart'; import 'package:post/post/widgets/post_link_in_screen.dart'; -import 'package:post/post/widgets/post_pop_up_web.dart'; import 'package:post/post/widgets/post_tags_and_title.dart'; import 'package:post/post/widgets/post_video_in_widget_web.dart'; import 'package:video_player/video_player.dart'; import '../../show_post/screens/show_post.dart'; import '../../show_post/screens/show_post_web.dart'; -import '../../widgets/loading_reddit.dart'; import 'post_video_in_widget.dart'; import 'package:flutter/foundation.dart' show kIsWeb; @@ -78,7 +77,10 @@ class _PostBodyState extends State { padding: const EdgeInsetsDirectional.only( start: 10, end: 10, top: 10), child: Text( - widget.data.text as String, + parse(widget.data.text as String) + .documentElement + ?.text ?? + '', maxLines: 3, style: TextStyle( color: Theme.of(context).colorScheme.secondary, diff --git a/lib/post/widgets/post_body_in_screen.dart b/lib/post/widgets/post_body_in_screen.dart index 18645f83..28351b38 100644 --- a/lib/post/widgets/post_body_in_screen.dart +++ b/lib/post/widgets/post_body_in_screen.dart @@ -1,18 +1,24 @@ import 'package:flutter/material.dart'; import 'package:post/post/models/post_model.dart'; -import 'package:post/post/widgets/post_images.dart'; import 'package:post/post/widgets/post_images_in_screen.dart'; import 'package:post/post/widgets/post_link_in_screen.dart'; import 'package:post/post/widgets/post_tags_and_title.dart'; +import 'package:post/post/widgets/post_video_in_widget_web.dart'; import 'package:video_player/video_player.dart'; import 'post_video_in_widget.dart'; import 'package:flutter_html/flutter_html.dart'; +import 'package:flutter/foundation.dart' show kIsWeb; /// This Widget is responsible for the body of the post. class PostBodyInScreen extends StatefulWidget { + /// The data of the post final PostModel data; + + /// The user name final String userName; + + /// a boolean to check if in screen final bool inView; const PostBodyInScreen( @@ -83,16 +89,27 @@ class _PostBodyInScreenState extends State { type: widget.data.kind as String, ) : (widget.data.kind == 'video') - ? PostVideoInWidget( - title: widget.data.title as String, - flair: widget.data.flairId, - nsfw: widget.data.nsfw as bool, - spoiler: widget.data.spoiler as bool, - inView: widget.inView, - url: widget.data.video as String, - videoController: widget.data.videoController - as VideoPlayerController, - ) + ? kIsWeb + ? PostVideoInWidgetWeb( + title: widget.data.title as String, + flair: widget.data.flairId, + nsfw: widget.data.nsfw as bool, + spoiler: widget.data.spoiler as bool, + inView: widget.inView, + url: widget.data.video as String, + videoController: widget.data.videoController + as VideoPlayerController, + ) + : PostVideoInWidget( + title: widget.data.title as String, + flair: widget.data.flairId, + nsfw: widget.data.nsfw as bool, + spoiler: widget.data.spoiler as bool, + inView: widget.inView, + url: widget.data.video as String, + videoController: widget.data.videoController + as VideoPlayerController, + ) : const SizedBox(), ), ); diff --git a/lib/post/widgets/post_card.dart b/lib/post/widgets/post_card.dart index 10e39d56..a7683227 100644 --- a/lib/post/widgets/post_card.dart +++ b/lib/post/widgets/post_card.dart @@ -43,11 +43,11 @@ class PostCard extends StatelessWidget { height: 80, child: LinkPreviewGenerator( showDescription: false, - link: 'https://github.com/ghpranav/link_preview_generator', - placeholderWidget: LoadingReddit(), + link: link, + placeholderWidget: const LoadingReddit(), backgroundColor: Colors.black.withOpacity(0.6), showTitle: false, - domainStyle: TextStyle(color: Colors.white, fontSize: 12), + domainStyle: const TextStyle(color: Colors.white, fontSize: 12), ), ), ], diff --git a/lib/post/widgets/post_comment_list.dart b/lib/post/widgets/post_comment_list.dart index e2128004..331ae588 100644 --- a/lib/post/widgets/post_comment_list.dart +++ b/lib/post/widgets/post_comment_list.dart @@ -1,15 +1,33 @@ import 'package:flutter/material.dart'; import 'package:fluttericon/typicons_icons.dart'; import 'package:inview_notifier_list/inview_notifier_list.dart'; -import '../models/post_model.dart'; import './post.dart'; +/// A margin to the left of the posts and comments + class PostCommentList extends StatefulWidget { + /// The user name + final String userName; + + /// A widget to be displayed above the posts + final Widget topOfTheList; + + /// A function which is invoked when the CustomScrollView reaches the end + final Function updateData; + + /// The list of posts and comments' data to be displayed + final List> data; + + /// A margin to the left of the posts + final double leftMargin; + + /// A margin to the right of the posts + final double rightMargin; final String type; @@ -86,8 +104,8 @@ class _PostCommentListState extends State { if (widget.data[index]['type'] == 'comment') { return Container( color: Colors.white, - margin: - EdgeInsetsDirectional.only(top: 10, bottom: 10), + margin: const EdgeInsetsDirectional.only( + top: 10, bottom: 10), child: ListTile( title: Text( widget.data[index]['data'].title.toString()), @@ -157,4 +175,4 @@ class _PostCommentListState extends State { } super.dispose(); } -} \ No newline at end of file +} diff --git a/lib/post/widgets/post_footer.dart b/lib/post/widgets/post_footer.dart index 0556c639..e887b626 100644 --- a/lib/post/widgets/post_footer.dart +++ b/lib/post/widgets/post_footer.dart @@ -117,7 +117,7 @@ class _PostFooterState extends State { ? Theme.of(context).colorScheme.primary : Theme.of(context).colorScheme.surface, child: Container( - margin: EdgeInsetsDirectional.only(start: 10, end: 10), + margin: const EdgeInsetsDirectional.only(start: 10, end: 10), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ @@ -134,7 +134,7 @@ class _PostFooterState extends State { child: Tooltip( message: 'Upvote', child: Container( - padding: EdgeInsetsDirectional.all(8), + padding: const EdgeInsetsDirectional.all(8), child: (postVoteStatus != 1) ? Icon( Typicons.up_outline, @@ -160,7 +160,7 @@ class _PostFooterState extends State { child: Tooltip( message: 'Downvote', child: Container( - padding: EdgeInsetsDirectional.all(8), + padding: const EdgeInsetsDirectional.all(8), child: (postVoteStatus != -1) ? Icon( Typicons.down_outline, diff --git a/lib/post/widgets/post_header.dart b/lib/post/widgets/post_header.dart index fad14bd7..7e75dba4 100644 --- a/lib/post/widgets/post_header.dart +++ b/lib/post/widgets/post_header.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; import 'package:fluttericon/font_awesome5_icons.dart'; -import 'package:post/post/widgets/post_popup_menu.dart'; +import 'package:post/post/widgets/post_pop_up_menu.dart'; import 'package:post/post/widgets/user_info_popup.dart'; import 'package:post/subreddit/screens/subreddit_screen.dart'; import '../../moderated_subreddit/screens/moderated_subreddit_screen.dart'; @@ -186,9 +186,6 @@ class _PostHeaderBasicState extends State { : InkWell( onTap: (!widget.inProfile) ? () { - print( - '===============================Is mod:${widget.isModerator}============================='); - showDialog( context: context, builder: (context) => UserInfoPopUp( @@ -198,9 +195,6 @@ class _PostHeaderBasicState extends State { ); } : () { - print( - '===============================Is mod:${widget.isModerator}============================='); - Navigator.of(context).pushNamed( widget.isModerator ? ModeratedSubredditScreen.routeName @@ -287,8 +281,6 @@ class _PostHeaderHome extends StatelessWidget { children: [ InkWell( onTap: () { - print( - '==========================On Tab==========================='); if (ownerType == 'User') { showDialog( context: context, diff --git a/lib/post/widgets/post_link_in_screen.dart b/lib/post/widgets/post_link_in_screen.dart index 65817dc3..dbf4a26e 100644 --- a/lib/post/widgets/post_link_in_screen.dart +++ b/lib/post/widgets/post_link_in_screen.dart @@ -39,7 +39,7 @@ class PostLinkInScreen extends StatelessWidget { margin: const EdgeInsetsDirectional.only(top: 10, start: 10, end: 10), child: LinkPreviewGenerator( showDescription: false, - link: 'https://github.com/ghpranav/link_preview_generator', + link: link, placeholderWidget: const LoadingReddit(), backgroundColor: Colors.black.withOpacity(0.6), showTitle: false, diff --git a/lib/post/widgets/post_list.dart b/lib/post/widgets/post_list.dart index 4f900379..b7082630 100644 --- a/lib/post/widgets/post_list.dart +++ b/lib/post/widgets/post_list.dart @@ -3,13 +3,29 @@ import 'package:inview_notifier_list/inview_notifier_list.dart'; import '../models/post_model.dart'; import './post.dart'; +/// This widget returns a CustomScrollView to display posts + class PostList extends StatefulWidget { + /// The user name final String userName; + + /// A widget to be displayed above the posts final Widget topOfTheList; + + /// A function which is invoked when the CustomScrollView reaches the end final Function updateData; + + /// The list of posts' data to be displayed final List data; + + /// The type of posts ["home", "profile", "community"] final String type; + + /// A margin to the left of the posts final double leftMargin; + + /// A margin to the right of the posts + final double rightMargin; const PostList({ super.key, @@ -31,6 +47,7 @@ class _PostListState extends State { Widget build(BuildContext context) { return Flexible( child: InViewNotifierCustomScrollView( + shrinkWrap: true, onListEndReached: () => widget.updateData(), scrollDirection: Axis.vertical, diff --git a/lib/post/widgets/post_mod_popup.dart b/lib/post/widgets/post_mod_popup.dart index fca8cbef..52c3c92d 100644 --- a/lib/post/widgets/post_mod_popup.dart +++ b/lib/post/widgets/post_mod_popup.dart @@ -106,11 +106,11 @@ class _PostModPopUpState extends State { Navigator.pop(context); }, child: Container( - margin: EdgeInsetsDirectional.only(bottom: 10, top: 10), + margin: const EdgeInsetsDirectional.only(bottom: 10, top: 10), child: Row( children: [ Container( - margin: EdgeInsetsDirectional.only(start: 2, end: 5), + margin: const EdgeInsetsDirectional.only(start: 2, end: 5), child: Icon( MfgLabs.attention, size: 18, @@ -136,11 +136,11 @@ class _PostModPopUpState extends State { Navigator.pop(context); }, child: Container( - margin: EdgeInsetsDirectional.only(bottom: 10, top: 10), + margin: const EdgeInsetsDirectional.only(bottom: 10, top: 10), child: Row( children: [ Container( - margin: EdgeInsetsDirectional.only(start: 2, end: 5), + margin: const EdgeInsetsDirectional.only(start: 2, end: 5), child: Stack( alignment: Alignment.center, children: [ @@ -185,11 +185,11 @@ class _PostModPopUpState extends State { Navigator.pop(context); }, child: Container( - margin: EdgeInsetsDirectional.only(bottom: 10, top: 10), + margin: const EdgeInsetsDirectional.only(bottom: 10, top: 10), child: Row( children: [ Container( - margin: EdgeInsetsDirectional.only(end: 5), + margin: const EdgeInsetsDirectional.only(end: 5), child: Icon( Icons.lock_outline, color: Theme.of(context).colorScheme.brightness == @@ -216,11 +216,11 @@ class _PostModPopUpState extends State { InkWell( onTap: null, child: Container( - margin: EdgeInsetsDirectional.only(bottom: 10, top: 10), + margin: const EdgeInsetsDirectional.only(bottom: 10, top: 10), child: Row( children: [ Container( - margin: EdgeInsetsDirectional.only(end: 5), + margin: const EdgeInsetsDirectional.only(end: 5), child: Icon( FontAwesome.mail, color: Theme.of(context).colorScheme.brightness == @@ -250,11 +250,11 @@ class _PostModPopUpState extends State { Navigator.pop(context); }, child: Container( - margin: EdgeInsetsDirectional.only(bottom: 10, top: 10), + margin: const EdgeInsetsDirectional.only(bottom: 10, top: 10), child: Row( children: [ Container( - margin: EdgeInsetsDirectional.only(end: 5), + margin: const EdgeInsetsDirectional.only(end: 5), child: Icon( Icons.block_flipped, color: widget.isRemoved @@ -288,11 +288,11 @@ class _PostModPopUpState extends State { Navigator.pop(context); }, child: Container( - margin: EdgeInsetsDirectional.only(bottom: 10, top: 10), + margin: const EdgeInsetsDirectional.only(bottom: 10, top: 10), child: Row( children: [ Container( - margin: EdgeInsetsDirectional.only(end: 5), + margin: const EdgeInsetsDirectional.only(end: 5), child: Icon( Icons.block_flipped, color: widget.data.isSpam ?? false @@ -326,11 +326,11 @@ class _PostModPopUpState extends State { Navigator.pop(context); }, child: Container( - margin: EdgeInsetsDirectional.only(bottom: 10, top: 10), + margin: const EdgeInsetsDirectional.only(bottom: 10, top: 10), child: Row( children: [ Container( - margin: EdgeInsetsDirectional.only(end: 5), + margin: const EdgeInsetsDirectional.only(end: 5), child: Icon( Icons.done, color: widget.isApproved diff --git a/lib/post/widgets/post_mod_tools.dart b/lib/post/widgets/post_mod_tools.dart index 6846bf35..f54c18d8 100644 --- a/lib/post/widgets/post_mod_tools.dart +++ b/lib/post/widgets/post_mod_tools.dart @@ -5,17 +5,40 @@ import 'package:provider/provider.dart'; import '../models/post_model.dart'; import '../provider/post_provider.dart'; +/// This widget displays the moderator tools of the post + class PostModTools extends StatefulWidget { /// Check if it's a post created by the user final bool isMyPost; + /// A boolean to determine if the post is approved + final bool isApproved; + + /// A boolean to determine if the post is NSFW + final bool isNSFW; + + /// A boolean to determine if the post is spoiler + final bool isSpoiler; + + /// A boolean to determine if the comments of the post is locked + final bool isCommentsLocked; + + /// A boolean to determine if the post is removed + final bool isRemoved; + + /// A function which is invoked when the state changes + final Function update; + + /// The post data final PostModel data; + + /// A boolean to check if in screen final bool inScreen; const PostModTools({ @@ -55,7 +78,7 @@ class _PostModTools extends State { @override Widget build(BuildContext context) { return Container( - margin: EdgeInsetsDirectional.only(top: 2), + margin: const EdgeInsetsDirectional.only(top: 2), child: Material( color: (!widget.inScreen && (widget.data.isSpam ?? false)) ? Colors.blueGrey[50] @@ -63,7 +86,7 @@ class _PostModTools extends State { ? Theme.of(context).colorScheme.primary : Theme.of(context).colorScheme.surface, child: Container( - margin: EdgeInsetsDirectional.only(start: 10, end: 10), + margin: const EdgeInsetsDirectional.only(start: 10, end: 10), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ @@ -75,7 +98,7 @@ class _PostModTools extends State { child: Tooltip( message: 'Approve', child: Container( - padding: EdgeInsetsDirectional.all(10), + padding: const EdgeInsetsDirectional.all(10), child: (!widget.isApproved) ? Icon( Icons.done, @@ -108,7 +131,7 @@ class _PostModTools extends State { child: Tooltip( message: 'More options', child: Container( - padding: EdgeInsetsDirectional.all(10), + padding: const EdgeInsetsDirectional.all(10), child: Icon( Icons.format_list_bulleted, color: Theme.of(context).colorScheme.secondary, diff --git a/lib/post/widgets/post_popup_menu.dart b/lib/post/widgets/post_pop_up_menu.dart similarity index 96% rename from lib/post/widgets/post_popup_menu.dart rename to lib/post/widgets/post_pop_up_menu.dart index 2f34599c..b8434476 100644 --- a/lib/post/widgets/post_popup_menu.dart +++ b/lib/post/widgets/post_pop_up_menu.dart @@ -5,10 +5,19 @@ import 'package:provider/provider.dart'; import '../provider/post_provider.dart'; +/// This widget shows the popup menu of the widget + class PostPopupMenu extends StatefulWidget { + /// A boolean to determine if the post is saved final bool isSaved; + + /// The post data final PostModel data; + + /// A function which is invoked when the state changes final Function update; + + /// A boolean to determine if it's the user's post final bool isMyPost; const PostPopupMenu( {super.key, diff --git a/lib/post/widgets/post_pop_up_web.dart b/lib/post/widgets/post_pop_up_web.dart index 7715c246..e7494ec4 100644 --- a/lib/post/widgets/post_pop_up_web.dart +++ b/lib/post/widgets/post_pop_up_web.dart @@ -26,11 +26,11 @@ class _PostPopUpWebState extends State { @override Widget build(BuildContext context) { return AlertDialog( - content: Container( + content: SizedBox( height: MediaQuery.of(context).size.width * .8, width: MediaQuery.of(context).size.width * .8, child: SingleChildScrollView( - child: Container( + child: SizedBox( width: 40.w, child: Column( children: [ diff --git a/lib/post/widgets/post_tags_and_title.dart b/lib/post/widgets/post_tags_and_title.dart index 7df42298..d07b6207 100644 --- a/lib/post/widgets/post_tags_and_title.dart +++ b/lib/post/widgets/post_tags_and_title.dart @@ -4,10 +4,22 @@ import 'package:fluttericon/mfg_labs_icons.dart'; import 'package:hexcolor/hexcolor.dart'; import 'package:post/post/models/post_model.dart'; +/// This widget displays the title and the spoiler and NSFW tags + class PostTagsAndTitle extends StatefulWidget { + /// The title of the post final String title; + + /// A boolean to determine if NSFW + final bool isNSFW; + + /// A boolean to determine if spoiler + final bool isSpoiler; + + /// The flair data + final FlairId? flair; const PostTagsAndTitle( diff --git a/lib/post/widgets/post_video_in_widget.dart b/lib/post/widgets/post_video_in_widget.dart index 46915031..a7f50103 100644 --- a/lib/post/widgets/post_video_in_widget.dart +++ b/lib/post/widgets/post_video_in_widget.dart @@ -7,14 +7,29 @@ import 'package:chewie/chewie.dart'; import '../models/post_model.dart'; +/// A widget to display a video in widget class PostVideoInWidget extends StatefulWidget { /// The url of the video final String url; + + /// The video controller final VideoPlayerController videoController; + + /// A boolean to check if in view final bool inView; + + /// The title of the post final String title; + + /// A boolean to determine if spoiler final bool spoiler; + + /// A boolean to determine if NSFW + final bool nsfw; + + /// The flair data + final FlairId? flair; const PostVideoInWidget({ super.key, diff --git a/lib/post/widgets/post_video_in_widget_web.dart b/lib/post/widgets/post_video_in_widget_web.dart index f5423670..e186e5e1 100644 --- a/lib/post/widgets/post_video_in_widget_web.dart +++ b/lib/post/widgets/post_video_in_widget_web.dart @@ -1,20 +1,41 @@ import 'package:flutter/material.dart'; import 'package:post/post/widgets/post_tags_and_title.dart'; import 'package:post/providers/global_settings.dart'; +import 'package:post/widgets/loading_reddit.dart'; import 'package:provider/provider.dart'; import 'package:video_player/video_player.dart'; import 'package:chewie/chewie.dart'; import '../models/post_model.dart'; +/// A widget to display a video in widget (web) + class PostVideoInWidgetWeb extends StatefulWidget { /// The url of the video final String url; + + /// The video controller + final VideoPlayerController videoController; + + /// A boolean to check if in view + final bool inView; + + /// The title of the post + final String title; + + /// A boolean to determine if spoiler + final bool spoiler; + + /// A boolean to determine if NSFW + final bool nsfw; + + /// The flair data + final FlairId? flair; const PostVideoInWidgetWeb({ super.key, @@ -43,7 +64,10 @@ class _PostVideoInWidgetWebState extends State { autoInitialize: true, autoPlay: false, looping: true, - showControls: false, + showControls: true, + allowMuting: false, + allowPlaybackSpeedChanging: true, + placeholder: const LoadingReddit(), ); } diff --git a/lib/post/widgets/user_info_popup.dart b/lib/post/widgets/user_info_popup.dart index 05603e72..f008d2c4 100644 --- a/lib/post/widgets/user_info_popup.dart +++ b/lib/post/widgets/user_info_popup.dart @@ -10,8 +10,12 @@ import '../../other_profile/models/others_profile_data.dart'; import '../../other_profile/providers/other_profile_provider.dart'; import '../../widgets/loading_reddit.dart'; +/// A pop up (AlertDialog) to show the user info class UserInfoPopUp extends StatefulWidget { + /// the name of the user to get his data final String authorName; + + /// A boolean to determine the look of the pop up final bool isMine; const UserInfoPopUp({ super.key, @@ -181,7 +185,6 @@ class _UserInfoPopUpState extends State { !widget.isMine ? InkWell( onTap: () { - _showLeaveDialog(); }, child: Container( @@ -282,7 +285,7 @@ class _UserInfoPopUpState extends State { context: context, builder: (ctx) => AlertDialog( //title:Text('Are you sure you want to leave the r/${widget.communityName.toString()} community?'), - content: Container( + content: SizedBox( //color: Colors.amber, height: 12.h, width: 100.w, @@ -304,7 +307,7 @@ class _UserInfoPopUpState extends State { ), ), actions: [ - Container( + SizedBox( width: 35.w, height: 6.h, child: ElevatedButton( @@ -321,7 +324,7 @@ class _UserInfoPopUpState extends State { }, ), ), - Container( + SizedBox( width: 35.w, height: 6.h, child: ElevatedButton( diff --git a/lib/providers/Profile_provider.dart b/lib/providers/Profile_provider.dart index f81e4952..52420ba8 100644 --- a/lib/providers/Profile_provider.dart +++ b/lib/providers/Profile_provider.dart @@ -1,18 +1,16 @@ -// ignore_for_file: file_names - import 'package:shared_preferences/shared_preferences.dart'; import 'package:flutter/material.dart'; import 'package:dio/dio.dart'; import '../post/models/post_model.dart'; import '../models/comments_data.dart'; -import '../../networks/dio_client.dart'; -import '../../widgets/handle_error.dart'; +import '../networks/dio_client.dart'; +import '../widgets/handle_error.dart'; //using in heighest widget to use class ProfileProvider with ChangeNotifier { List commentsdata = []; List data = []; - List> commentsAndPosts=[]; + List> commentsAndPosts = []; List? get gettingProfileComments { return commentsdata; } @@ -20,7 +18,8 @@ class ProfileProvider with ChangeNotifier { List? get gettingProfilePostData { return data; } - List>? get gettingPostCommentData { + + List>? get gettingPostCommentData { return commentsAndPosts; } @@ -75,25 +74,25 @@ class ProfileProvider with ChangeNotifier { } } - Future fetchandSetProfilePostsAndComments( - String userName,String postType, int page, int limit, BuildContext context) async { + Future fetchandSetProfilePostsAndComments(String userName, + String postType, int page, int limit, BuildContext context) async { try { final prefs = await SharedPreferences.getInstance(); DioClient.init(prefs); await DioClient.get( - path: '/users/${userName}/overview', - query: {'sort': postType,'page': page, 'limit': limit}).then((response) { - List>tempData=[]; + path: '/users/${userName}/overview', + query: {'sort': postType, 'page': page, 'limit': limit}) + .then((response) { + List> tempData = []; response.data['comments'].forEach((comment) { tempData .add({'type': 'comment', 'data': CommentsData.fromJson(comment)}); }); response.data['posts'].forEach((post) { - PostModel temp = PostModel(); - temp.fromJson(post); - tempData - .add({'type': 'post', 'data': temp}); + PostModel temp = PostModel(); + temp.fromJson(post); + tempData.add({'type': 'post', 'data': temp}); }); commentsAndPosts = tempData; diff --git a/lib/providers/subreddit_posts_provider.dart b/lib/providers/subreddit_posts_provider.dart index 7ad095ab..626c2984 100644 --- a/lib/providers/subreddit_posts_provider.dart +++ b/lib/providers/subreddit_posts_provider.dart @@ -1,21 +1,21 @@ import '../post/models/post_model.dart'; import 'package:flutter/material.dart'; -import '../../networks/dio_client.dart'; -import '../../widgets/handle_error.dart'; +import '../networks/dio_client.dart'; +import '../widgets/handle_error.dart'; import 'package:dio/dio.dart'; import 'package:shared_preferences/shared_preferences.dart'; //using in heighest widget to use class SubredditPostsProvider with ChangeNotifier { - List? postData=[]; + List? postData = []; List? get gettingSubredditPostData { return postData; } - Future fetchSubredditePosts( - String subredditName, String postType, int page, int limit,BuildContext context) async { + Future fetchSubredditePosts(String subredditName, String postType, + int page, int limit, BuildContext context) async { try { final prefs = await SharedPreferences.getInstance(); DioClient.init(prefs); @@ -29,11 +29,11 @@ class SubredditPostsProvider with ChangeNotifier { await temp.fromJson(post); tempData.add(temp); } - postData=tempData; + postData = tempData; notifyListeners(); }); } on DioError catch (e) { - HandleError.errorHandler(e, context); + HandleError.errorHandler(e, context); } catch (error) { HandleError.handleError(error.toString(), context); } diff --git a/lib/search/provider/search_provider.dart b/lib/search/provider/search_provider.dart index 7b8718fa..cabaf0fc 100644 --- a/lib/search/provider/search_provider.dart +++ b/lib/search/provider/search_provider.dart @@ -47,14 +47,7 @@ class SearchProvider with ChangeNotifier { print(path); // sort=$sort&time=$time&page=$page&limit=$limit - await DioClient.get(path: path, query: { - // "type": type, - // "q": quiry, - // 'sort': sort, - // 'time': time, - // 'page': page, - // 'limit': limit - }).then((response) { + await DioClient.get(path: path, query: {}).then((response) { print(response); isError = !(response.statusCode! < 310); if (isError == false) { @@ -71,7 +64,8 @@ class SearchProvider with ChangeNotifier { initPeople = false; } else if (type == 'posts') { searhPost!.data - ?..addAll(SearchPost.fromJson(response.data) as List); + ?..addAll( + SearchPost.fromJson(response.data).data as List); initPost = false; } else if (type == 'comments') { searhComment!.data diff --git a/lib/search/screens/search_inside.dart b/lib/search/screens/search_inside.dart index a4a78a6d..f98dab69 100644 --- a/lib/search/screens/search_inside.dart +++ b/lib/search/screens/search_inside.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; +import 'package:post/create_community/screens/create_community.dart'; import 'package:post/icons/arrow_head_down_word_icons.dart'; import 'package:responsive_sizer/responsive_sizer.dart'; import '../../models/wrapper.dart'; @@ -18,6 +19,7 @@ import './search.dart'; import '../widgets/people_list.dart'; import '../widgets/communities_list.dart'; import '../widgets/empty_search.dart'; +import 'package:flutter/foundation.dart' show kIsWeb; class SearchInside extends StatefulWidget { static const routeName = '/insidesearch'; @@ -35,7 +37,7 @@ class _SearchInsideState extends State { bool _isInit = true; bool isBuild = false; - int index = 1; + int index = 3; bool showNSFW = true; bool autoPlay = true; SearchProvider? provider = null; @@ -104,91 +106,240 @@ class _SearchInsideState extends State { ), ], ), - body: SingleChildScrollView( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Container( - color: Colors.transparent, - child: Row( + body: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + SingleChildScrollView( + child: Center( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ - Expanded( - flex: 2, - child: ElevatedButton( - onPressed: () { - index = 0; - setState(() {}); - }, - child: Text('Posts'), - style: ElevatedButton.styleFrom( - backgroundColor: - index == 0 ? Colors.blue : Colors.white, - elevation: 0, + Container( + color: Colors.transparent, + child: Center( + child: SizedBox( + width: (kIsWeb) ? 60.w : null, + child: Row( + children: [ + Expanded( + flex: 2, + child: ElevatedButton( + onPressed: () { + index = 0; + setState(() {}); + }, + child: Text('Posts'), + style: ElevatedButton.styleFrom( + backgroundColor: index == 0 + ? Colors.blue + : Colors.white, + elevation: 0, + ), + ), + ), + Expanded( + flex: 3, + child: ElevatedButton( + onPressed: () { + index = 1; + setState(() {}); + }, + child: Text('Comments'), + style: ElevatedButton.styleFrom( + backgroundColor: index == 1 + ? Colors.blue + : Colors.white, + elevation: 0, + ), + ), + ), + Expanded( + flex: 3, + child: ElevatedButton( + onPressed: () { + index = 2; + setState(() {}); + }, + child: Text('Communities'), + style: ElevatedButton.styleFrom( + backgroundColor: index == 2 + ? Colors.blue + : Colors.white, + elevation: 0, + ), + ), + ), + Expanded( + flex: 2, + child: ElevatedButton( + onPressed: () { + index = 3; + setState(() {}); + }, + child: Text('People'), + style: ElevatedButton.styleFrom( + backgroundColor: index == 3 + ? Colors.blue + : Colors.white, + elevation: 0, + ), + ), + ) + ], + ), ), ), ), - Expanded( - flex: 3, - child: ElevatedButton( - onPressed: () { - index = 1; - setState(() {}); - }, - child: Text('Comments'), - style: ElevatedButton.styleFrom( - backgroundColor: - index == 1 ? Colors.blue : Colors.white, - elevation: 0, + if (index == 0) + Center( + child: SizedBox( + width: (kIsWeb) + ? ((100.w < 900) ? 100.w : 900) + : null, + child: Padding( + padding: + const EdgeInsets.all((kIsWeb) ? 8.0 : 0), + child: PostsList( + provider: provider!, + limit: limit, + quiry: widget.quiry), + ), ), ), - ), - Expanded( - flex: 3, - child: ElevatedButton( - onPressed: () { - index = 2; - setState(() {}); - }, - child: Text('Communities'), - style: ElevatedButton.styleFrom( - backgroundColor: - index == 2 ? Colors.blue : Colors.white, - elevation: 0, + if (index == 1) + Center( + child: SizedBox( + width: (kIsWeb) + ? ((100.w < 900) ? 100.w : 900) + : null, + child: Padding( + padding: + const EdgeInsets.all((kIsWeb) ? 8.0 : 0), + child: CommentsList( + provider: provider!, + limit: limit, + quiry: widget.quiry), + ), ), ), - ), - Expanded( - flex: 2, - child: ElevatedButton( - onPressed: () { - index = 3; - setState(() {}); - }, - child: Text('People'), - style: ElevatedButton.styleFrom( - backgroundColor: - index == 3 ? Colors.blue : Colors.white, - elevation: 0, + if (index == 2) + Center( + child: SizedBox( + width: (kIsWeb) + ? ((100.w < 900) ? 100.w : 900) + : null, + child: Padding( + padding: + const EdgeInsets.all((kIsWeb) ? 8.0 : 0), + child: CommunitiesList( + provider: provider!, + limit: limit, + quiry: widget.quiry), + ), + ), + ), + if (index == 3) + Center( + child: SizedBox( + width: (kIsWeb) + ? ((100.w < 900) ? 100.w : 900) + : null, + child: Padding( + padding: + const EdgeInsets.all((kIsWeb) ? 8.0 : 0), + child: PeopleList( + provider: provider!, + limit: limit, + quiry: widget.quiry), + ), ), ), - ) ], ), ), - if (index == 0) - PostsList( - provider: provider!, limit: limit, quiry: widget.quiry), - if (index == 1) - CommentsList( - provider: provider!, limit: limit, quiry: widget.quiry), - if (index == 2) - CommunitiesList( - provider: provider!, limit: limit, quiry: widget.quiry), - if (index == 3) - PeopleList( - provider: provider!, limit: limit, quiry: widget.quiry), - ], - ), + ), + if (kIsWeb) SizedBox(width: 50), + if (kIsWeb && index == 0) + Container( + width: 30.w, + height: 80.h, + child: SingleChildScrollView( + child: Column( + children: [ + Container( + color: Colors.white, + child: Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Text('Peoples'), + PeopleList( + provider: provider!, + limit: 5, + quiry: widget.quiry), + ], + ), + ), + Container( + height: 50, + color: Color.fromARGB(255, 228, 231, 239)), + Container( + color: Colors.white, + child: Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Text('Communities'), + CommunitiesList( + provider: provider!, + limit: 5, + quiry: widget.quiry), + Container( + height: 50, + color: Color.fromARGB(255, 228, 231, 239)), + Container( + color: Colors.white, + child: Column( + children: [ + Image.asset( + 'assets/images/reddit_community_search.png', + width: 25.w, + height: 25.h, + fit: BoxFit.fill, + ), + Text('Have an idea for a new community?'), + ElevatedButton( + style: ElevatedButton.styleFrom( + onPrimary: Colors.white, + primary: (kIsWeb) + ? Colors.blue + : Colors.red, + onSurface: Colors.grey[700], + shape: RoundedRectangleBorder( + borderRadius: + BorderRadius.circular(30)), + ), + onPressed: () { + showDialog( + context: context, + builder: ((context) { + return const CreateCommunity(); + }), + ); + }, + child: Text('Create community'), + ), + ], + ), + ), + ], + ), + ), + ], + ), + ), + ) + ], ), ); } diff --git a/lib/subreddit/screens/subreddit_screen.dart b/lib/subreddit/screens/subreddit_screen.dart index 2e4e4b30..91ce6bb4 100644 --- a/lib/subreddit/screens/subreddit_screen.dart +++ b/lib/subreddit/screens/subreddit_screen.dart @@ -3,6 +3,7 @@ import 'package:get/get.dart'; import 'package:responsive_sizer/responsive_sizer.dart'; import 'package:flutter/foundation.dart' show kIsWeb; import 'package:provider/provider.dart'; +import 'package:get/get.dart'; import '../../home/controller/home_controller.dart'; import '../../models/subreddit_data.dart'; import '../../widgets/loading_reddit.dart'; @@ -10,6 +11,10 @@ import '../widgets/subreddit_web.dart'; import '../widgets/subreddit_app.dart'; import '../providers/subreddit_provider.dart'; import '../../home/widgets/end_drawer.dart'; +import '../../home/controller/home_controller.dart'; +import '../../createpost/controllers/posts_controllers.dart'; +import '../../home/widgets/custom_upper_bar.dart'; + class SubredditScreen extends StatefulWidget { static const routeName = '/Subreddit'; @@ -35,7 +40,7 @@ class _SubredditScreenState extends State isScrollable: true, tabs: tabs, labelColor: Colors.black, - labelPadding:EdgeInsets.symmetric(horizontal: 18.w), + labelPadding: EdgeInsets.symmetric(horizontal: 18.w), labelStyle: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold), indicatorColor: Colors.blue, ); @@ -43,7 +48,7 @@ class _SubredditScreenState extends State var _isLoading = false; var _isInit = true; var subredditUserName; -SubredditData? loadedSubreddit; + SubredditData? loadedSubreddit; //===============================================================================// @override void initState() { @@ -56,7 +61,8 @@ SubredditData? loadedSubreddit; _controller!.dispose(); super.dispose(); } - // ===================================this function used to===========================================// + + // ===================================this function used to===========================================// //==================fetch date for first time===========================// @override void didChangeDependencies() { @@ -67,7 +73,7 @@ SubredditData? loadedSubreddit; }); subredditUserName = ModalRoute.of(context)?.settings.arguments as String; Provider.of(context, listen: false) - .fetchAndSetSubredddit(subredditUserName,context) + .fetchAndSetSubredddit(subredditUserName, context) .then((value) { loadedSubreddit = Provider.of(context, listen: false) .gettingSubredditeData; @@ -80,11 +86,25 @@ SubredditData? loadedSubreddit; super.didChangeDependencies(); } + final HomeController controllerHome = Get.put( + HomeController(), + ); + final PostController controllerForPost = Get.put( + PostController(), + ); @override Widget build(BuildContext context) { final GlobalKey _scaffoldKey = GlobalKey(); return Scaffold( key: _scaffoldKey, + appBar: (kIsWeb) + ? PreferredSize( + preferredSize: Size(700, 60), + child: UpBar( + controller: controllerHome, + controllerForCreatePost: controllerForPost, + )) + : null, body: _isLoading ? LoadingReddit() : kIsWeb @@ -101,7 +121,10 @@ SubredditData? loadedSubreddit; tabBar: _tabBar, loadedSubreddit: loadedSubreddit as SubredditData, userName: subredditUserName), - endDrawer: _isLoading ? LoadingReddit() : endDrawer(controller: controller,)); + endDrawer: _isLoading + ? LoadingReddit() + : endDrawer( + controller: controller, + )); } - - } +} diff --git a/lib/subreddit/widgets/subreddit_web.dart b/lib/subreddit/widgets/subreddit_web.dart index 0fb225a3..f54b5d46 100644 --- a/lib/subreddit/widgets/subreddit_web.dart +++ b/lib/subreddit/widgets/subreddit_web.dart @@ -8,6 +8,8 @@ import '../providers/subreddit_provider.dart'; import 'package:provider/provider.dart'; import '../widgets/subreddit_post_web.dart'; import '../../widgets/subreddit_join_button_web.dart'; +import '../../widgets/back_to_button.dart'; +import '../screens/subreddit_screen.dart'; extension ColorExtension on String { toColor() { @@ -36,18 +38,33 @@ class SubredditWeb extends StatelessWidget { final TabBar tabBar; bool isLoading; TabController? controller; - + ScrollController scrollController = ScrollController(); @override Widget build(BuildContext context) { bool showTheme = Provider.of(context, listen: false) - .gettingTheme as bool; + .gettingTheme as bool; final GlobalKey _scaffoldKey = GlobalKey(); return Scaffold( key: _scaffoldKey, - backgroundColor: - (showTheme) + floatingActionButtonLocation: FloatingActionButtonLocation.miniEndFloat, + floatingActionButton: Padding( + padding: const EdgeInsetsDirectional.only(end: 320.0), + child: ElevatedButton( + onPressed: () { + Navigator.of(context).pushNamed(SubredditScreen.routeName, + arguments:userName); + }, + child: Text( + ' Back to top ', + style: TextStyle(color: Colors.white), + ), + style: ElevatedButton.styleFrom( + shape: StadiumBorder(), backgroundColor: Colors.blue), + ), + ), + backgroundColor: (showTheme) ? Color.fromARGB(255, 218, 224, 230) - : (loadedSubreddit!.theme!.contains('https')) + : (loadedSubreddit!.theme!.contains('https')) ? Image.network(loadedSubreddit!.theme.toString()).color : '#6ae792'.toColor(), body: isLoading diff --git a/lib/widgets/subreddit_join_button_web.dart b/lib/widgets/subreddit_join_button_web.dart index 3747d767..e9438461 100644 --- a/lib/widgets/subreddit_join_button_web.dart +++ b/lib/widgets/subreddit_join_button_web.dart @@ -3,7 +3,7 @@ import 'package:responsive_sizer/responsive_sizer.dart'; import 'package:provider/provider.dart'; import '../subreddit/providers/subreddit_provider.dart'; import '../subreddit/widgets/notify_button_web.dart'; -import '../../widgets/custom_snack_bar.dart'; +import './custom_snack_bar.dart'; class SubredditJoinButtonWeb extends StatefulWidget { bool isJoined;