feat: complete timeline userstory

This commit is contained in:
Freek van de Ven 2023-12-01 07:50:02 +01:00
parent a1ceee391a
commit 0035e1f4fb
6 changed files with 121 additions and 30 deletions

View file

@ -4,5 +4,6 @@
/// Flutter Timeline library
library flutter_timeline;
export 'package:flutter_timeline/src/flutter_timeline_userstory.dart';
export 'package:flutter_timeline_interface/flutter_timeline_interface.dart';
export 'package:flutter_timeline_view/flutter_timeline_view.dart';

View file

@ -5,9 +5,12 @@ import 'package:go_router/go_router.dart';
mixin TimelineUserStoryRoutes {
static const String timelineHome = '/timeline';
static String timelineCreate(String category) => '/timeline-create/$category';
static const String timelineCreate = '/timeline-create/:category';
static String timelineCreatePath(String category) =>
'/timeline-create/$category';
static const String timelineSelect = '/timeline-select';
static String timelineView(String postId) => '/timeline-view/$postId';
static const String timelineView = '/timeline-view/:post';
static String timelineViewPath(String postId) => '/timeline-view/$postId';
}
class TimelineUserStoryConfiguration {
@ -15,22 +18,31 @@ class TimelineUserStoryConfiguration {
required this.optionsBuilder,
required this.userId,
required this.service,
this.pageBuilder,
required this.userService,
this.mainPageBuilder,
this.postScreenBuilder,
this.postCreationScreenBuilder,
this.postSelectionScreenBuilder,
this.onUserTap,
this.timelinePostHeight,
});
final String userId;
final double? timelinePostHeight;
final Function(String userId)? onUserTap;
final Widget Function(Widget child)? pageBuilder;
final Widget Function(Widget filterBar, Widget child)? mainPageBuilder;
final Widget Function(Widget child)? postScreenBuilder;
final Widget Function(Widget child)? postCreationScreenBuilder;
final Widget Function(Widget child)? postSelectionScreenBuilder;
final TimelineService service;
final TimelineOptions Function(BuildContext context)
optionsBuilder; // New callback
final TimelineUserService userService;
final TimelineOptions Function(BuildContext context) optionsBuilder;
}
List<GoRoute> getTimelineStoryRoutes(
@ -39,22 +51,93 @@ List<GoRoute> getTimelineStoryRoutes(
<GoRoute>[
GoRoute(
path: TimelineUserStoryRoutes.timelineHome,
pageBuilder: (context, state) => buildScreenWithoutTransition(
context: context,
state: state,
child: Scaffold(
body: TimelineScreen(
userId: configuration.userId,
onUserTap: configuration.onUserTap,
service: configuration.service,
options: configuration.optionsBuilder(context),
timelinePostHeight: configuration.timelinePostHeight,
onPostTap: (_) async => context.push('/timeline-view/1'),
timelineCategoryFilter: 'category',
),
),
),
pageBuilder: (context, state) {
var timelineScreen = TimelineScreen(
userId: configuration.userId,
onUserTap: configuration.onUserTap,
service: configuration.service,
options: configuration.optionsBuilder(context),
onPostTap: (post) async =>
TimelineUserStoryRoutes.timelineViewPath(post.id),
timelineCategoryFilter: 'news',
);
return buildScreenWithoutTransition(
context: context,
state: state,
child: configuration.mainPageBuilder?.call(
Container(), // TODO(anyone): create a selection widget
timelineScreen,
) ??
Scaffold(
body: timelineScreen,
),
);
},
),
GoRoute(
path: TimelineUserStoryRoutes.timelineSelect,
pageBuilder: (context, state) {
var timelineSelectionWidget =
Container(); // TODO(anyone): create timeline selection screen
return buildScreenWithoutTransition(
context: context,
state: state,
child: configuration.postSelectionScreenBuilder?.call(
timelineSelectionWidget,
) ??
Scaffold(
body: timelineSelectionWidget,
),
);
},
),
GoRoute(
path: TimelineUserStoryRoutes.timelineCreate,
pageBuilder: (context, state) {
var timelineCreateWidget = TimelinePostCreationScreen(
userId: configuration.userId,
options: configuration.optionsBuilder(context),
postCategory: state.pathParameters['category'] ?? '',
service: configuration.service,
onPostCreated: (post) => context.go(
TimelineUserStoryRoutes.timelineViewPath(post.id),
),
);
return buildScreenWithoutTransition(
context: context,
state: state,
child: configuration.postCreationScreenBuilder
?.call(timelineCreateWidget) ??
Scaffold(
body: timelineCreateWidget,
),
);
},
),
GoRoute(
path: TimelineUserStoryRoutes.timelineView,
pageBuilder: (context, state) {
var timelinePostWidget = TimelinePostScreen(
userId: configuration.userId,
options: configuration.optionsBuilder(context),
service: configuration.service,
userService: configuration.userService,
post: configuration.service.getPost(state.pathParameters['post']!)!,
onPostDelete: () => context.go(
TimelineUserStoryRoutes.timelineHome,
),
onUserTap: configuration.onUserTap,
);
return buildScreenWithoutTransition(
context: context,
state: state,
child: configuration.postScreenBuilder?.call(
timelinePostWidget,
) ??
Scaffold(
body: timelinePostWidget,
),
);
},
),
/// Here come the other timeline screens that all use the same configuration
];

View file

@ -115,6 +115,12 @@ class FirebaseTimelineService with ChangeNotifier implements TimelineService {
return updatedPost;
}
@override
TimelinePost? getPost(String postId) =>
(_posts.any((element) => element.id == postId))
? _posts.firstWhere((element) => element.id == postId)
: null;
@override
List<TimelinePost> getPosts(String? category) => _posts
.where((element) => category == null || element.category == category)

View file

@ -13,6 +13,7 @@ abstract class TimelineService with ChangeNotifier {
Future<TimelinePost> createPost(TimelinePost post);
Future<List<TimelinePost>> fetchPosts(String? category);
Future<TimelinePost> fetchPost(TimelinePost post);
TimelinePost? getPost(String postId);
List<TimelinePost> getPosts(String? category);
Future<TimelinePost> fetchPostDetails(TimelinePost post);
Future<TimelinePost> reactToPost(

View file

@ -15,6 +15,7 @@ class TimelineOptions {
this.translations = const TimelineTranslations(),
this.imagePickerConfig = const ImagePickerConfig(),
this.imagePickerTheme = const ImagePickerTheme(),
this.timelinePostHeight,
this.allowAllDeletion = false,
this.sortCommentsAscending = true,
this.sortPostsAscending = false,
@ -44,6 +45,9 @@ class TimelineOptions {
/// only the posts of the current user
final bool allowAllDeletion;
/// The height of a post in the timeline
final double? timelinePostHeight;
final TimelineTranslations translations;
final ButtonBuilder? buttonBuilder;

View file

@ -19,7 +19,6 @@ class TimelineScreen extends StatefulWidget {
this.posts,
this.controller,
this.timelineCategoryFilter,
this.timelinePostHeight,
this.padding = const EdgeInsets.symmetric(vertical: 12.0),
super.key,
});
@ -38,9 +37,6 @@ class TimelineScreen extends StatefulWidget {
final String? timelineCategoryFilter;
/// The height of a post in the timeline
final double? timelinePostHeight;
/// This is used if you want to pass in a list of posts instead
/// of fetching them from the service
final List<TimelinePost>? posts;
@ -106,7 +102,7 @@ class _TimelineScreenState extends State<TimelineScreen> {
userId: widget.userId,
options: widget.options,
post: post,
height: widget.timelinePostHeight,
height: widget.options.timelinePostHeight,
onTap: () => widget.onPostTap.call(post),
onTapLike: () async =>
widget.service.likePost(widget.userId, post),