Skip to content

Commit 166f0df

Browse files
authored
Merge pull request #199 from GetStream/refactorflat-feed-core-builder
2 parents 31568fa + 90789df commit 166f0df

33 files changed

+488
-299
lines changed

.github/workflows/build.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ jobs:
6868
- uses: VeryGoodOpenSource/[email protected]
6969
with:
7070
path: packages/faye_dart/coverage/lcov.info
71-
min_coverage: 49
71+
min_coverage: 48
7272
- uses: VeryGoodOpenSource/[email protected]
7373
with:
7474
path: packages/stream_feed_flutter_core/coverage/lcov.info

packages/faye_dart/lib/src/client.dart

+12-2
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@ import 'dart:async';
22
import 'dart:convert';
33
import 'dart:math' as math;
44

5+
import 'package:equatable/equatable.dart';
56
import 'package:faye_dart/faye_dart.dart';
67
import 'package:faye_dart/src/channel.dart';
78
import 'package:faye_dart/src/message.dart';
8-
import 'package:faye_dart/src/subscription.dart';
99
import 'package:faye_dart/src/timeout_helper.dart';
1010
import 'package:logging/logging.dart';
1111
import 'package:meta/meta.dart';
@@ -47,7 +47,7 @@ const defaultConnectionTimeout = 60;
4747
const defaultConnectionInterval = 0;
4848
const bayeuxVersion = '1.0';
4949

50-
class FayeClient with Extensible, TimeoutHelper {
50+
class FayeClient with Extensible, TimeoutHelper, EquatableMixin {
5151
FayeClient(
5252
this.baseUrl, {
5353
this.protocols,
@@ -437,4 +437,14 @@ class FayeClient with Extensible, TimeoutHelper {
437437
}
438438
setTimeout(Duration(milliseconds: _advice.interval), () => connect());
439439
}
440+
441+
@override
442+
List<Object?> get props => [
443+
_clientId,
444+
_advice,
445+
_channels,
446+
_responseCallbacks,
447+
_connectRequestInProgress,
448+
_manuallyClosed,
449+
];
440450
}

packages/faye_dart/lib/src/subscription.dart

+5
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import 'package:equatable/equatable.dart';
2+
13
import 'client.dart';
24
import 'message.dart';
35

@@ -35,4 +37,7 @@ class Subscription {
3537
_client.unsubscribe(_channel, this);
3638
_cancelled = true;
3739
}
40+
41+
@override
42+
List<Object?> get props => [_client, _channel, _callback, _withChannel];
3843
}

packages/faye_dart/test/mock.dart

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import 'package:faye_dart/src/client.dart';
22
import 'package:mocktail/mocktail.dart';
33

4-
class MockClient extends Mock implements FayeClient {}
4+
class MockClient extends Mock implements FayeClient {
5+
bool operator ==(Object? other) => true;
6+
}

packages/stream_feed/lib/src/client/analytics_client.dart

+9-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import 'package:equatable/equatable.dart';
12
import 'package:stream_feed/src/core/api/analytics_api.dart';
23
import 'package:stream_feed/src/core/http/stream_http_client.dart';
34
import 'package:stream_feed/src/core/http/token.dart';
@@ -15,7 +16,7 @@ import 'package:stream_feed/src/core/util/token_helper.dart';
1516
/// - Viewing another user's profile page
1617
/// - Searching for a certain user/content/topic/etc.
1718
/// {@endtemplate}
18-
class StreamAnalytics {
19+
class StreamAnalytics extends Equatable {
1920
/// [StreamAnalytics] constructor:
2021
///
2122
/// {@macro analytics}
@@ -121,4 +122,11 @@ class StreamAnalytics {
121122
userToken ?? TokenHelper.buildAnalytics(secret!, TokenAction.write);
122123
return _analytics.trackEngagements(token, engagementDataList);
123124
}
125+
126+
@override
127+
List<Object?> get props => [
128+
secret,
129+
userToken,
130+
userData,
131+
];
124132
}

packages/stream_feed/lib/src/client/stream_feed_client_impl.dart

+20-2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import 'package:equatable/equatable.dart';
12
import 'package:faye_dart/faye_dart.dart';
23
import 'package:logging/logging.dart';
34
import 'package:stream_feed/src/client/aggregated_feed.dart';
@@ -32,7 +33,7 @@ final _levelEmojiMapper = {
3233
};
3334

3435
///{@macro stream_feed_client}
35-
class StreamFeedClientImpl implements StreamFeedClient {
36+
class StreamFeedClientImpl with EquatableMixin implements StreamFeedClient {
3637
/// Builds a [StreamFeedClientImpl].
3738
///{@macro stream_feed_client}
3839
StreamFeedClientImpl(
@@ -348,9 +349,23 @@ class StreamFeedClientImpl implements StreamFeedClient {
348349
userToken ?? TokenHelper.buildUsersToken(secret!, TokenAction.delete);
349350
return _api.users.delete(token, id);
350351
}
352+
353+
@override
354+
List<Object?> get props => [
355+
apiKey,
356+
appId,
357+
secret,
358+
userToken,
359+
_userConnected,
360+
_faye,
361+
_subscriptions,
362+
];
363+
364+
@override
365+
bool get stringify => true;
351366
}
352367

353-
class _FeedSubscription {
368+
class _FeedSubscription extends Equatable {
354369
const _FeedSubscription({
355370
required this.token,
356371
required this.userId,
@@ -371,4 +386,7 @@ class _FeedSubscription {
371386
final String token;
372387
final String userId;
373388
final Subscription? fayeSubscription;
389+
390+
@override
391+
List<Object?> get props => [token, userId, fayeSubscription];
374392
}

packages/stream_feed/pubspec.yaml

+2-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ dependencies:
1111
collection: ^1.15.0
1212
dio: ^4.0.0
1313
equatable: ^2.0.0
14-
faye_dart: ^0.1.0
14+
faye_dart:
15+
path: ../faye_dart
1516
http_parser: ^4.0.0
1617
intl: ^0.17.0
1718
jose: ^0.3.2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export 'empty_state.dart';
2+
export 'error_state.dart';
3+
export 'loading_state.dart';

packages/stream_feed_flutter_core/lib/src/states/empty.dart packages/stream_feed_flutter/lib/src/default/empty_state.dart

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@ import 'package:flutter/foundation.dart';
22
import 'package:flutter/material.dart';
33

44
/// {@template onEmptyWidget}
5-
/// A builder for building a widget when the state is empty.
6-
/// An Empty State Widget to be used for empty states
5+
/// A default widget to be displayed for empty state builders.
76
/// {@endtemplate}
87
class EmptyStateWidget extends StatelessWidget {
98
/// Builds an [EmptyStateWidget].
9+
///
1010
/// {@macro onEmptyWidget}
1111
const EmptyStateWidget({
1212
Key? key,

packages/stream_feed_flutter_core/lib/src/states/error.dart packages/stream_feed_flutter/lib/src/default/error_state.dart

+2-3
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,12 @@ import 'package:flutter/foundation.dart';
22
import 'package:flutter/material.dart';
33

44
/// {@template onErrorWidget}
5-
/// A builder for building a widget when an error occurs.
6-
///
7-
/// [ErrorStateWidget] is the default widget
5+
/// A default widget to display for error builders.
86
/// {@endtemplate}
97
108
class ErrorStateWidget extends StatelessWidget {
119
/// Builds an [ErrorStateWidget].
10+
///
1211
/// {@macro onErrorWidget}
1312
const ErrorStateWidget({
1413
Key? key,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import 'package:flutter/material.dart';
2+
3+
/// {@template onProgressWidget}
4+
/// A default loading widget to display for loading builders.
5+
/// {@endtemplate}
6+
class LoadingStateWidget extends StatelessWidget {
7+
/// Builds a [LoadingStateWidget].
8+
///
9+
/// {@macro onProgressWidget}
10+
const LoadingStateWidget({Key? key}) : super(key: key);
11+
12+
@override
13+
Widget build(BuildContext context) {
14+
return const Center(
15+
child: CircularProgressIndicator(),
16+
);
17+
}
18+
}

packages/stream_feed_flutter/lib/src/widgets/activity/activity.dart

+1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ class ActivityWidget extends StatelessWidget {
3030
}) : super(key: key);
3131

3232
/// The activity to display.
33+
/// {@macro enrichedActivity}
3334
final EnrichedActivity activity;
3435

3536
/// The json key for the user's handle.

packages/stream_feed_flutter/lib/src/widgets/dialogs/comment.dart

+3-3
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ class CommentView extends StatelessWidget {
131131
child: SingleChildScrollView(
132132
child: Column(
133133
children: [
134-
//TODO: "this post has been deleted by the author"
134+
//TODO(sacha): "this post has been deleted by the author"
135135
if (activity != null) ...[
136136
StreamBuilder(
137137
stream: FeedProvider.of(context)
@@ -147,8 +147,8 @@ class CommentView extends StatelessWidget {
147147
);
148148
},
149149
)
150-
//TODO: analytics
151-
//TODO: "in response to" activity.to
150+
//TODO(sacha): analytics
151+
//TODO(sacha): "in response to" activity.to
152152
],
153153
//TODO: builder for using it elsewhere than in actions
154154
if (enableReactions && activity != null)

packages/stream_feed_flutter/lib/src/widgets/pages/compose_view.dart

+8-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import 'package:flutter/foundation.dart';
22
import 'package:flutter/material.dart';
33
import 'package:image_picker/image_picker.dart';
4+
import 'package:stream_feed_flutter/src/default/error_state.dart';
5+
import 'package:stream_feed_flutter/src/default/loading_state.dart';
46
import 'package:stream_feed_flutter/src/widgets/activity/activity.dart';
57
import 'package:stream_feed_flutter/src/widgets/buttons/reactive_elevated_button.dart';
68
import 'package:stream_feed_flutter/stream_feed_flutter.dart';
@@ -32,7 +34,9 @@ class ComposeView extends StatefulWidget {
3234
..add(DiagnosticsProperty<EnrichedActivity?>(
3335
'parentActivity', parentActivity))
3436
..add(StringProperty('feedGroup', feedGroup))
35-
..add(StringProperty('nameJsonKey', nameJsonKey));
37+
..add(StringProperty('nameJsonKey', nameJsonKey))
38+
..add(DiagnosticsProperty<TextEditingController>(
39+
'textEditingController', textEditingController));
3640
}
3741

3842
@override
@@ -170,6 +174,8 @@ class _ComposeViewState extends State<ComposeView> {
170174
children: [
171175
Expanded(
172176
child: UploadListCore(
177+
uploadsErrorBuilder: (error) => const ErrorStateWidget(),
178+
loadingBuilder: (context) => const LoadingStateWidget(),
173179
uploadController:
174180
FeedProvider.of(context).bloc.uploadController,
175181
uploadsBuilder: (context, uploads) {
@@ -205,7 +211,7 @@ class _ComposeViewState extends State<ComposeView> {
205211
.uploadController
206212
.uploadImage(AttachmentFile(path: image.path));
207213
} else {
208-
// User canceled the picker
214+
// TODO: User canceled the picker
209215
}
210216
},
211217
),

packages/stream_feed_flutter/lib/src/widgets/pages/flat_feed_list_view.dart

+50-40
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import 'package:animations/animations.dart';
22
import 'package:flutter/cupertino.dart';
33
import 'package:flutter/foundation.dart';
44
import 'package:flutter/material.dart';
5+
import 'package:stream_feed_flutter/src/default/default.dart';
56
import 'package:stream_feed_flutter/src/utils/typedefs.dart';
67
import 'package:stream_feed_flutter/src/widgets/activity/activity.dart';
78
import 'package:stream_feed_flutter/src/widgets/dialogs/comment.dart';
@@ -47,7 +48,7 @@ class FlatFeedListView extends StatelessWidget {
4748
this.ranking,
4849
this.handleJsonKey = 'handle',
4950
this.nameJsonKey = 'name',
50-
this.onProgressWidget = const ProgressStateWidget(),
51+
this.onProgressWidget = const LoadingStateWidget(),
5152
this.onErrorWidget = const ErrorStateWidget(),
5253
this.onEmptyWidget =
5354
const EmptyStateWidget(message: 'No activities to display'),
@@ -133,52 +134,61 @@ class FlatFeedListView extends StatelessWidget {
133134
session: session,
134135
filter: filter,
135136
ranking: ranking,
136-
onProgressWidget: onProgressWidget,
137-
onErrorWidget: onErrorWidget,
137+
loadingBuilder: (context) => onProgressWidget,
138+
errorBuilder: (context, error) => onErrorWidget,
139+
emptyBuilder: (context) => onEmptyWidget,
138140
//TODO: activity type Flat?
139-
feedBuilder: (context, activities, idx) {
140-
return ActivityWidget(
141-
activity: activities[idx],
142-
feedGroup: feedGroup,
143-
onHashtagTap: onHashtagTap,
144-
onMentionTap: onMentionTap,
145-
onUserTap: onUserTap,
146-
nameJsonKey: nameJsonKey,
147-
handleJsonKey: handleJsonKey,
148-
activityHeaderBuilder: activityHeaderBuilder,
149-
activityFooterBuilder: activityFooterBuilder,
150-
activityContentBuilder: activityContentBuilder,
151-
onActivityTap: (context, activity) {
152-
// onActivityTap != null
153-
// ? onActivityTap?.call(context, activity)
154-
// TODO: provide a way to load via url / ModalRoute.of(context).settings with ActivityCore (todo)
155-
_pageRouteBuilder(
156-
activity: activity,
157-
context: context,
158-
transitionType: transitionType,
159-
currentNavigator: Navigator.of(context),
160-
page: Scaffold(
161-
appBar: AppBar(
162-
// TODO: Parameterize me
163-
title: const Text('Post'),
164-
),
165-
body: CommentView(
166-
feedGroup: feedGroup,
167-
nameJsonKey: nameJsonKey,
168-
handleJsonKey: handleJsonKey,
141+
feedBuilder: (
142+
context,
143+
activities,
144+
) {
145+
return ListView.builder(
146+
itemCount: activities.length,
147+
physics: scrollPhysics,
148+
itemBuilder: (context, index) {
149+
return ActivityWidget(
150+
activity: activities[index],
151+
feedGroup: feedGroup,
152+
onHashtagTap: onHashtagTap,
153+
onMentionTap: onMentionTap,
154+
onUserTap: onUserTap,
155+
nameJsonKey: nameJsonKey,
156+
handleJsonKey: handleJsonKey,
157+
activityHeaderBuilder: activityHeaderBuilder,
158+
activityFooterBuilder: activityFooterBuilder,
159+
activityContentBuilder: activityContentBuilder,
160+
onActivityTap: (context, activity) {
161+
// onActivityTap != null
162+
// ? onActivityTap?.call(context, activity)
163+
// TODO: provide a way to load via url / ModalRoute.of(context).settings with ActivityCore (todo)
164+
_pageRouteBuilder(
169165
activity: activity,
170-
enableCommentFieldButton: true,
171-
enableReactions: true,
172-
textEditingController:
173-
TextEditingController(), //TODO: move this into props for customisation like buildSpans
174-
),
175-
),
166+
context: context,
167+
transitionType: transitionType,
168+
currentNavigator: Navigator.of(context),
169+
page: Scaffold(
170+
appBar: AppBar(
171+
// TODO: Parameterize me
172+
title: const Text('Post'),
173+
),
174+
body: CommentView(
175+
feedGroup: feedGroup,
176+
nameJsonKey: nameJsonKey,
177+
handleJsonKey: handleJsonKey,
178+
activity: activity,
179+
enableCommentFieldButton: true,
180+
enableReactions: true,
181+
textEditingController:
182+
TextEditingController(), //TODO: move this into props for customisation like buildSpans
183+
),
184+
),
185+
);
186+
},
176187
);
177188
},
178189
);
179190
},
180191
feedGroup: feedGroup,
181-
scrollPhysics: scrollPhysics,
182192
);
183193
}
184194

0 commit comments

Comments
 (0)