From a6e990b6f4bafe76ff775e95937aadd230065ca0 Mon Sep 17 00:00:00 2001 From: Harshith Goka Date: Fri, 28 Dec 2018 21:15:53 +0530 Subject: [PATCH] [App] merge NewsPage with BlogPage; [BlogPage] undelete click FAB to go top --- lib/main.dart | 2 + lib/src/api/apiclient.dart | 3 +- lib/src/api/model/news_article.dart | 27 ---- lib/src/api/model/news_article.jser.dart | 38 ----- .../api/model/{blogpost.dart => post.dart} | 18 ++- .../{blogpost.jser.dart => post.jser.dart} | 33 +++- lib/src/api/model/serializers.dart | 3 +- lib/src/blocs/blog_bloc.dart | 44 +++-- lib/src/blocs/ia_bloc.dart | 18 ++- lib/src/drawer.dart | 4 + lib/src/routes/blogpage.dart | 153 +++++++++++------- lib/src/routes/newspage.dart | 15 ++ lib/src/routes/placementblogpage.dart | 2 +- lib/src/routes/quicklinkspage.dart | 1 - lib/src/routes/trainingblogpage.dart | 2 +- 15 files changed, 203 insertions(+), 160 deletions(-) delete mode 100644 lib/src/api/model/news_article.dart delete mode 100644 lib/src/api/model/news_article.jser.dart rename lib/src/api/model/{blogpost.dart => post.dart} (60%) rename lib/src/api/model/{blogpost.jser.dart => post.jser.dart} (63%) create mode 100644 lib/src/routes/newspage.dart diff --git a/lib/main.dart b/lib/main.dart index da0dce37..534ad4b7 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,6 +1,7 @@ import 'package:InstiApp/src/routes/bodypage.dart'; import 'package:InstiApp/src/routes/eventpage.dart'; import 'package:InstiApp/src/routes/feedpage.dart'; +import 'package:InstiApp/src/routes/newspage.dart'; import 'package:InstiApp/src/routes/quicklinkspage.dart'; import 'package:InstiApp/src/routes/trainingblogpage.dart'; import 'package:InstiApp/src/routes/userpage.dart'; @@ -68,6 +69,7 @@ class MyAppState extends State { case "/trainblog": return _buildRoute(settings, TrainingBlogPage()); case "/feed": return _buildRoute(settings, FeedPage()); case "/quicklinks": return _buildRoute(settings, QuickLinksPage()); + case "/news": return _buildRoute(settings, NewsPage()); } } return _buildRoute(settings, MessPage()); diff --git a/lib/src/api/apiclient.dart b/lib/src/api/apiclient.dart index 73eb28f1..5c1192b1 100644 --- a/lib/src/api/apiclient.dart +++ b/lib/src/api/apiclient.dart @@ -1,6 +1,5 @@ import 'package:InstiApp/src/api/model/body.dart'; import 'package:InstiApp/src/api/model/event.dart'; -import 'package:InstiApp/src/api/model/news_article.dart'; import 'package:InstiApp/src/api/model/notification.dart'; import 'package:InstiApp/src/api/model/venter.dart'; import 'package:InstiApp/src/api/model/venue.dart'; @@ -15,7 +14,7 @@ import 'package:InstiApp/src/api/response/image_upload_response.dart'; import 'package:InstiApp/src/api/response/news_feed_response.dart'; import 'package:http/io_client.dart'; import 'package:InstiApp/src/api/model/mess.dart'; -import 'package:InstiApp/src/api/model/blogpost.dart'; +import 'package:InstiApp/src/api/model/post.dart'; import 'package:InstiApp/src/api/model/user.dart'; import 'package:jaguar_resty/jaguar_resty.dart'; import 'package:jaguar_resty/jaguar_resty.dart' as resty; diff --git a/lib/src/api/model/news_article.dart b/lib/src/api/model/news_article.dart deleted file mode 100644 index 5d6fd1d4..00000000 --- a/lib/src/api/model/news_article.dart +++ /dev/null @@ -1,27 +0,0 @@ -import 'package:InstiApp/src/api/model/body.dart'; -import 'package:jaguar_serializer/jaguar_serializer.dart'; - -part 'news_article.jser.dart'; - -class NewsArticle { - @Alias("id") - String articleID; - - @Alias("link") - String link; - - @Alias("title") - String title; - - @Alias("content") - String content; - - @Alias("published") - String published; - - @Alias("body") - Body body; -} - -@GenSerializer() -class NewsArticleSerializer extends Serializer with _$NewsArticleSerializer {} \ No newline at end of file diff --git a/lib/src/api/model/news_article.jser.dart b/lib/src/api/model/news_article.jser.dart deleted file mode 100644 index 709ebc75..00000000 --- a/lib/src/api/model/news_article.jser.dart +++ /dev/null @@ -1,38 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'news_article.dart'; - -// ************************************************************************** -// JaguarSerializerGenerator -// ************************************************************************** - -abstract class _$NewsArticleSerializer implements Serializer { - Serializer __bodySerializer; - Serializer get _bodySerializer => - __bodySerializer ??= new BodySerializer(); - @override - Map toMap(NewsArticle model) { - if (model == null) return null; - Map ret = {}; - setMapValue(ret, 'id', model.articleID); - setMapValue(ret, 'link', model.link); - setMapValue(ret, 'title', model.title); - setMapValue(ret, 'content', model.content); - setMapValue(ret, 'published', model.published); - setMapValue(ret, 'body', _bodySerializer.toMap(model.body)); - return ret; - } - - @override - NewsArticle fromMap(Map map) { - if (map == null) return null; - final obj = new NewsArticle(); - obj.articleID = map['id'] as String; - obj.link = map['link'] as String; - obj.title = map['title'] as String; - obj.content = map['content'] as String; - obj.published = map['published'] as String; - obj.body = _bodySerializer.fromMap(map['body'] as Map); - return obj; - } -} diff --git a/lib/src/api/model/blogpost.dart b/lib/src/api/model/post.dart similarity index 60% rename from lib/src/api/model/blogpost.dart rename to lib/src/api/model/post.dart index a3ff3bdf..2d5d8e1d 100644 --- a/lib/src/api/model/blogpost.dart +++ b/lib/src/api/model/post.dart @@ -1,8 +1,9 @@ +import 'package:InstiApp/src/api/model/body.dart'; import 'package:jaguar_serializer/jaguar_serializer.dart'; -part 'blogpost.jser.dart'; +part 'post.jser.dart'; -class BlogPost { +class Post { @Alias("id") String postID; @@ -19,8 +20,17 @@ class BlogPost { String published; } -class PlacementBlogPost extends BlogPost{} -class TrainingBlogPost extends BlogPost{} +class PlacementBlogPost extends Post{} +class TrainingBlogPost extends Post{} + + +class NewsArticle extends Post { + @Alias("body") + Body body; +} + +@GenSerializer() +class NewsArticleSerializer extends Serializer with _$NewsArticleSerializer {} @GenSerializer() class PlacementBlogPostSerializer extends Serializer with _$PlacementBlogPostSerializer {} diff --git a/lib/src/api/model/blogpost.jser.dart b/lib/src/api/model/post.jser.dart similarity index 63% rename from lib/src/api/model/blogpost.jser.dart rename to lib/src/api/model/post.jser.dart index a210c56e..52cd816a 100644 --- a/lib/src/api/model/blogpost.jser.dart +++ b/lib/src/api/model/post.jser.dart @@ -1,11 +1,42 @@ // GENERATED CODE - DO NOT MODIFY BY HAND -part of 'blogpost.dart'; +part of 'post.dart'; // ************************************************************************** // JaguarSerializerGenerator // ************************************************************************** +abstract class _$NewsArticleSerializer implements Serializer { + Serializer __bodySerializer; + Serializer get _bodySerializer => + __bodySerializer ??= new BodySerializer(); + @override + Map toMap(NewsArticle model) { + if (model == null) return null; + Map ret = {}; + setMapValue(ret, 'body', _bodySerializer.toMap(model.body)); + setMapValue(ret, 'postID', model.postID); + setMapValue(ret, 'link', model.link); + setMapValue(ret, 'title', model.title); + setMapValue(ret, 'content', model.content); + setMapValue(ret, 'published', model.published); + return ret; + } + + @override + NewsArticle fromMap(Map map) { + if (map == null) return null; + final obj = new NewsArticle(); + obj.body = _bodySerializer.fromMap(map['body'] as Map); + obj.postID = map['postID'] as String; + obj.link = map['link'] as String; + obj.title = map['title'] as String; + obj.content = map['content'] as String; + obj.published = map['published'] as String; + return obj; + } +} + abstract class _$PlacementBlogPostSerializer implements Serializer { @override diff --git a/lib/src/api/model/serializers.dart b/lib/src/api/model/serializers.dart index a3d1d353..64b29c92 100644 --- a/lib/src/api/model/serializers.dart +++ b/lib/src/api/model/serializers.dart @@ -1,7 +1,6 @@ // Copyright (c) 2015, Google Inc. Please see the AUTHORS file for details. library serializers; -import 'package:InstiApp/src/api/model/news_article.dart'; import 'package:InstiApp/src/api/model/notification.dart'; import 'package:InstiApp/src/api/model/role.dart'; import 'package:InstiApp/src/api/model/venter.dart'; @@ -13,7 +12,7 @@ import 'package:InstiApp/src/api/model/user.dart'; import 'package:InstiApp/src/api/model/body.dart'; import 'package:InstiApp/src/api/model/event.dart'; import 'package:InstiApp/src/api/model/mess.dart'; -import 'package:InstiApp/src/api/model/blogpost.dart'; +import 'package:InstiApp/src/api/model/post.dart'; JsonRepo standardSerializers = JsonRepo(serializers: [ // Mess menu diff --git a/lib/src/blocs/blog_bloc.dart b/lib/src/blocs/blog_bloc.dart index fa619530..fac6cf25 100644 --- a/lib/src/blocs/blog_bloc.dart +++ b/lib/src/blocs/blog_bloc.dart @@ -1,38 +1,33 @@ import 'dart:collection'; import 'package:flutter/foundation.dart'; -import 'package:InstiApp/src/api/model/blogpost.dart'; +import 'package:InstiApp/src/api/model/post.dart'; import 'package:InstiApp/src/blocs/ia_bloc.dart'; import 'package:rxdart/rxdart.dart'; import 'package:markdown/markdown.dart' as markdown; import 'dart:math'; -enum BlogType{ - Placement, - Training -} +enum PostType { Placement, Training, NewsArticle } -class BlogBloc { +class PostBloc { // Streams - Stream> get blog => - _blogSubject.stream; - final _blogSubject = - BehaviorSubject>(); + Stream> get blog => _blogSubject.stream; + final _blogSubject = BehaviorSubject>(); Sink get inPostIndex => _indexController.sink; PublishSubject _indexController = PublishSubject(); // Params int _noOfPostsPerPage = 20; - String query = ""; + String query = ""; // parent bloc InstiAppBloc bloc; - // Training or Placement - final BlogType blogType; + // Training or Placement or News Article + final PostType postType; - BlogBloc(this.bloc, {@required this.blogType}) { + PostBloc(this.bloc, {@required this.postType}) { _setIndexListener(); } @@ -43,7 +38,7 @@ class BlogBloc { .listen(_handleIndexes); } - final _fetchPages = >{}; + final _fetchPages = >{}; final _pagesBeingFetched = Set(); @@ -67,8 +62,12 @@ class BlogBloc { return "${week[dt.weekday - 1]}, ${month[dt.month - 1]} ${dt.day.toString()}${dt.year == DateTime.now().year ? "" : dt.year.toString()}, ${dt.hour.toString().padLeft(2, '0')}:${dt.minute.toString().padLeft(2, '0')}:${dt.second.toString().padLeft(2, '0')}"; } - Future> getBlogPage(int page) async { - var httpGetFunc = blogType == BlogType.Placement ? bloc.client.getPlacementBlogFeed : bloc.client.getTrainingBlogFeed; + Future> getBlogPage(int page) async { + var httpGetFunc = { + PostType.Placement: bloc.client.getPlacementBlogFeed, + PostType.Training: bloc.client.getTrainingBlogFeed, + PostType.NewsArticle: bloc.client.getNews + }[postType]; var posts = await httpGetFunc(bloc.getSessionIdHeader(), page * _noOfPostsPerPage, _noOfPostsPerPage, query); var tableParse = markdown.TableSyntax(); @@ -94,9 +93,8 @@ class BlogBloc { // Remember that we are fetching it _pagesBeingFetched.add(pageIndex); // Fetch it - getBlogPage(pageIndex) - .then((List fetchedPage) => - _handleFetchedPage(fetchedPage, pageIndex)); + getBlogPage(pageIndex).then((List fetchedPage) => + _handleFetchedPage(fetchedPage, pageIndex)); } } }); @@ -107,7 +105,7 @@ class BlogBloc { /// 1) record it /// 2) notify everyone who might be interested in knowing it /// - void _handleFetchedPage(List page, int pageIndex) { + void _handleFetchedPage(List page, int pageIndex) { // Remember the page _fetchPages[pageIndex] = page; // Remove it from the ones being fetched @@ -118,7 +116,7 @@ class BlogBloc { // which respect the sequence (since MovieCard are in sequence) // therefore, we need to iterate through the pages that are // actually fetched and stop if there is a gap. - List posts = []; + List posts = []; List pageIndexes = _fetchPages.keys.toList(); final int minPageIndex = pageIndexes.reduce(min); @@ -139,7 +137,7 @@ class BlogBloc { // Only notify when there are posts if (posts.length > 0) { - _blogSubject.add(UnmodifiableListView(posts)); + _blogSubject.add(UnmodifiableListView(posts)); } } diff --git a/lib/src/blocs/ia_bloc.dart b/lib/src/blocs/ia_bloc.dart index d5789f65..913e1c0d 100644 --- a/lib/src/blocs/ia_bloc.dart +++ b/lib/src/blocs/ia_bloc.dart @@ -24,8 +24,9 @@ class InstiAppBloc { final _eventsSubject = BehaviorSubject>(); // Sub Blocs - BlogBloc placementBloc; - BlogBloc trainingBloc; + PostBloc placementBloc; + PostBloc trainingBloc; + PostBloc newsBloc; DrawerBloc drawerState; // actual current state @@ -41,13 +42,18 @@ class InstiAppBloc { InstiAppBloc() { globalClient = IOClient(); - placementBloc = BlogBloc(this, blogType: BlogType.Placement); - trainingBloc = BlogBloc(this, blogType: BlogType.Training); + placementBloc = PostBloc(this, postType: PostType.Placement); + trainingBloc = PostBloc(this, postType: PostType.Training); + newsBloc = PostBloc(this, postType: PostType.NewsArticle); drawerState = DrawerBloc(homepageName, highlightPageIndexVal: 3); } - BlogBloc getBlogBloc(BlogType blogType) { - return blogType == BlogType.Placement ? placementBloc : trainingBloc; + PostBloc getPostsBloc(PostType blogType) { + return { + PostType.Placement: placementBloc, + PostType.Training: trainingBloc, + PostType.NewsArticle: newsBloc, + }[blogType]; } String getSessionIdHeader() { diff --git a/lib/src/drawer.dart b/lib/src/drawer.dart index 8be70978..a7134eab 100644 --- a/lib/src/drawer.dart +++ b/lib/src/drawer.dart @@ -221,8 +221,12 @@ class _BottomDrawerState extends State { 1: NavListTile( icon: OMIcons.rssFeed, title: "News", + selected: true, onTap: () { changeSelection(1, drawerState); + var navi = Navigator.of(context); + navi.pop(); + navi.pushNamed('/news'); }, ), 2: NavListTile( diff --git a/lib/src/routes/blogpage.dart b/lib/src/routes/blogpage.dart index 7f026bf3..dd8dc9fe 100644 --- a/lib/src/routes/blogpage.dart +++ b/lib/src/routes/blogpage.dart @@ -3,7 +3,7 @@ import 'dart:collection'; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; -import 'package:InstiApp/src/api/model/blogpost.dart'; +import 'package:InstiApp/src/api/model/post.dart'; import 'package:InstiApp/src/api/model/user.dart'; import 'package:InstiApp/src/bloc_provider.dart'; import 'package:InstiApp/src/blocs/blog_bloc.dart'; @@ -16,9 +16,9 @@ import 'package:flutter/foundation.dart'; class BlogPage extends StatefulWidget { final String title; - final BlogType blogType; + final PostType postType; - BlogPage({@required this.blogType, this.title}); + BlogPage({@required this.postType, this.title}); @override _BlogPageState createState() => _BlogPageState(); @@ -62,7 +62,7 @@ class _BlogPageState extends State { Widget build(BuildContext context) { var theme = Theme.of(context); var bloc = BlocProvider.of(context).bloc; - var blogBloc = bloc.getBlogBloc(widget.blogType); + var blogBloc = bloc.getPostsBloc(widget.postType); var footerButtons = searchMode ? [ @@ -140,9 +140,11 @@ class _BlogPageState extends State { .showBottomSheet((context) { BottomDrawer.setPageIndex( bloc, - widget.blogType == BlogType.Placement - ? 4 - : 5); + { + PostType.Placement: 4, + PostType.Training: 5, + PostType.NewsArticle: 1, + }[widget.postType]); return BottomDrawer(); }) .closed @@ -164,7 +166,6 @@ class _BlogPageState extends State { } else { actionIcon = OMIcons.close; } - searchMode = !searchMode; }); }, @@ -186,10 +187,10 @@ class _BlogPageState extends State { stream: bloc.session, builder: (BuildContext context, AsyncSnapshot snapshot) { if (snapshot.hasData && snapshot.data != null) { - return StreamBuilder( + return StreamBuilder>( stream: blogBloc.blog, builder: (BuildContext context, - AsyncSnapshot> snapshot) { + AsyncSnapshot> snapshot) { return RefreshIndicator( key: _refreshIndicatorKey, onRefresh: _handleRefresh, @@ -212,8 +213,7 @@ class _BlogPageState extends State { ), ); } else { - return _buildBlogPost( - blogBloc, index - 1, snapshot.data); + return _buildPost(blogBloc, index - 1, snapshot.data); } }, itemCount: @@ -253,18 +253,39 @@ class _BlogPageState extends State { }, ), ), + floatingActionButtonAnimator: FloatingActionButtonAnimator.scaling, + floatingActionButton: isFabVisible == 0 + ? null + : FloatingActionButton( + tooltip: "Go to the Top", + onPressed: () { + _hideButtonController + .animateTo(0.0, + curve: Curves.fastOutSlowIn, + duration: const Duration(milliseconds: 600)) + .then((_) { + setState(() { + isFabVisible = 0.0; + }); + }); + setState(() { + isFabVisible = 0.0; + }); + }, + child: Icon(OMIcons.keyboardArrowUp), + ), ); } Future _handleRefresh() { var bloc = BlocProvider.of(context).bloc; - return bloc.getBlogBloc(widget.blogType).refresh(); + return bloc.getPostsBloc(widget.postType).refresh(); } - Widget _buildBlogPost(BlogBloc bloc, int index, List posts) { + Widget _buildPost(PostBloc bloc, int index, List posts) { bloc.inPostIndex.add(index); - final BlogPost post = + final Post post = (posts != null && posts.length > index) ? posts[index] : null; if (post == null) { @@ -280,49 +301,73 @@ class _BlogPageState extends State { return _post(post); } - Widget _post(BlogPost post) { + Widget _post(Post post) { var theme = Theme.of(context); return Card( key: ValueKey(post.postID), - child: Padding( - padding: const EdgeInsets.all(12.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - post.title, - textAlign: TextAlign.start, - style: theme.textTheme.headline, - ), - Text( - post.published, - textAlign: TextAlign.start, - style: theme.textTheme.body1, - ), - SizedBox( - height: 8.0, - ), - Html( - data: post.content, - defaultTextStyle: theme.textTheme.subhead, - onLinkTap: (link) async { - print(link); - if (await canLaunch(link)) { - await launch(link); - } else { - throw "Couldn't launch $link"; - } - }, - customRender: (node, children) { - if (node is dom.Element) { - switch (node.localName) { - case "img": - return Text(node.attributes['href'] ?? ""); + child: InkWell( + onTap: () async { + if (await canLaunch(post.link)) { + await launch(post.link); + } + }, + child: Padding( + padding: const EdgeInsets.all(12.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + post.title, + textAlign: TextAlign.start, + style: theme.textTheme.headline + .copyWith(fontWeight: FontWeight.bold), + ), + Text( + widget.postType == PostType.NewsArticle + ? "${((post as NewsArticle).body.bodyName)} | ${post.published}" + : post.published, + textAlign: TextAlign.start, + style: theme.textTheme.subhead, + ), + SizedBox( + height: 8.0, + ), + Html( + data: post.content, + defaultTextStyle: theme.textTheme.subhead, + onLinkTap: (link) async { + print(link); + if (await canLaunch(link)) { + await launch(link); + } else { + throw "Couldn't launch $link"; } - } - }, - ), - ], + }, + customRender: (node, children) { + if (node is dom.Element) { + switch (node.localName) { + case "img": + return Text(node.attributes['href'] ?? ""); + case "a": + return InkWell( + onTap: () async { + if (await canLaunch(node.attributes['href'])) { + await launch(node.attributes['href']); + } + }, + child: Text( + node.innerHtml, + style: TextStyle( + color: Colors.lightBlue, + decoration: TextDecoration.underline), + ), + ); + } + } + }, + ), + ], + ), ), )); } diff --git a/lib/src/routes/newspage.dart b/lib/src/routes/newspage.dart new file mode 100644 index 00000000..b2defeec --- /dev/null +++ b/lib/src/routes/newspage.dart @@ -0,0 +1,15 @@ +import 'dart:core'; + +import 'package:InstiApp/src/blocs/blog_bloc.dart'; +import 'package:InstiApp/src/routes/blogpage.dart'; +import 'package:flutter/material.dart'; + +class NewsPage extends StatelessWidget { + @override + Widget build(BuildContext context) { + return BlogPage( + postType: PostType.NewsArticle, + title: "News", + ); + } +} diff --git a/lib/src/routes/placementblogpage.dart b/lib/src/routes/placementblogpage.dart index ffd7a0fd..a04e7aeb 100644 --- a/lib/src/routes/placementblogpage.dart +++ b/lib/src/routes/placementblogpage.dart @@ -8,7 +8,7 @@ class PlacementBlogPage extends StatelessWidget { @override Widget build(BuildContext context) { return BlogPage( - blogType: BlogType.Placement, + postType: PostType.Placement, title: "Placement Blog", ); } diff --git a/lib/src/routes/quicklinkspage.dart b/lib/src/routes/quicklinkspage.dart index 9ca8358c..9c56084e 100644 --- a/lib/src/routes/quicklinkspage.dart +++ b/lib/src/routes/quicklinkspage.dart @@ -56,7 +56,6 @@ class _QuickLinksPageState extends State { Widget build(BuildContext context) { var theme = Theme.of(context); var bloc = BlocProvider.of(context).bloc; - var footerButtons = []; return Scaffold( key: _scaffoldKey, diff --git a/lib/src/routes/trainingblogpage.dart b/lib/src/routes/trainingblogpage.dart index 00483a91..78303595 100644 --- a/lib/src/routes/trainingblogpage.dart +++ b/lib/src/routes/trainingblogpage.dart @@ -8,7 +8,7 @@ class TrainingBlogPage extends StatelessWidget { @override Widget build(BuildContext context) { return BlogPage( - blogType: BlogType.Training, + postType: PostType.Training, title: "Internship Blog", ); }