Skip to content

Commit 90f9502

Browse files
authored
feat(llc, samples): add polls in sample app (#38)
1 parent 5cea126 commit 90f9502

37 files changed

+5023
-14
lines changed

packages/stream_feeds/CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,5 @@
1+
## NEXT RELEASE
2+
- Fix for updating poll votes from web socket events.
3+
14
## 0.1.0
25
- Initial release of Feeds V3 SDK for Dart and Flutter.

packages/stream_feeds/lib/src/models.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ export 'models/feed_member_request_data.dart';
88
export 'models/feeds_config.dart';
99
export 'models/follow_data.dart';
1010
export 'models/poll_data.dart';
11+
export 'models/poll_option_data.dart';
12+
export 'models/poll_vote_data.dart';
1113
export 'models/push_notifications_config.dart';
1214
export 'models/request/activity_add_comment_request.dart'
1315
show ActivityAddCommentRequest;

packages/stream_feeds/lib/src/resolvers/poll/poll_answer_casted.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import '../../generated/api/models.dart';
77
core.WsEvent? pollAnswerCastedFeedEventResolver(core.WsEvent event) {
88
if (event is PollVoteCastedFeedEvent) {
99
final pollVote = event.pollVote;
10-
if (pollVote.isAnswer ?? false) return null;
10+
if (!(pollVote.isAnswer ?? false)) return null;
1111

1212
// If the event is a PollVoteCastedFeedEvent and the pollVote indicates
1313
// an answer was casted, we can resolve it to a PollAnswerCastedFeedEvent.
@@ -24,7 +24,7 @@ core.WsEvent? pollAnswerCastedFeedEventResolver(core.WsEvent event) {
2424

2525
if (event is PollVoteChangedFeedEvent) {
2626
final pollVote = event.pollVote;
27-
if (pollVote.isAnswer ?? false) return null;
27+
if (!(pollVote.isAnswer ?? false)) return null;
2828

2929
// If the event is a PollVoteChangedFeedEvent and the pollVote indicates
3030
// an answer was casted, we can resolve it to a PollAnswerCastedFeedEvent.

packages/stream_feeds/lib/src/resolvers/poll/poll_answer_removed.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import '../../generated/api/models.dart';
77
core.WsEvent? pollAnswerRemovedFeedEventResolver(core.WsEvent event) {
88
if (event is PollVoteRemovedFeedEvent) {
99
final pollVote = event.pollVote;
10-
if (pollVote.isAnswer ?? false) return null;
10+
if (!(pollVote.isAnswer ?? false)) return null;
1111

1212
// If the event is a PollVoteRemovedFeedEvent and the poll vote indicates an
1313
// answer was removed, we can resolve it to a PollAnswerRemovedFeedEvent.
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
// ignore_for_file: avoid_redundant_argument_values
2+
3+
import 'package:stream_feeds/src/resolvers/resolvers.dart';
4+
import 'package:stream_feeds/stream_feeds.dart';
5+
import 'package:test/test.dart';
6+
7+
void main() {
8+
group('pollAnswerCastedFeedEventResolver PollVoteCastedFeedEvent', () {
9+
test('resolves Answer when answer is true', () {
10+
final event = createPollVoteCastedFeedEvent(isAnswer: true);
11+
final resolvedEvent = pollAnswerCastedFeedEventResolver(event);
12+
expect(resolvedEvent, isA<PollAnswerCastedFeedEvent>());
13+
});
14+
15+
test('does not resolve Answer when answer is false', () {
16+
final event = createPollVoteCastedFeedEvent(isAnswer: false);
17+
final resolvedEvent = pollAnswerCastedFeedEventResolver(event);
18+
expect(resolvedEvent, isNull);
19+
});
20+
test('does not resolve Answer when answer is null', () {
21+
final event = createPollVoteCastedFeedEvent(isAnswer: null);
22+
final resolvedEvent = pollAnswerCastedFeedEventResolver(event);
23+
expect(resolvedEvent, isNull);
24+
});
25+
});
26+
group('pollAnswerCastedFeedEventResolver PollVoteChangedFeedEvent', () {
27+
test('resolves Answer when answer is true', () {
28+
final event = createPollVoteChangedFeedEvent(isAnswer: true);
29+
final resolvedEvent = pollAnswerCastedFeedEventResolver(event);
30+
expect(resolvedEvent, isA<PollAnswerCastedFeedEvent>());
31+
});
32+
test('does not resolve Answer when answer is false', () {
33+
final event = createPollVoteChangedFeedEvent(isAnswer: false);
34+
final resolvedEvent = pollAnswerCastedFeedEventResolver(event);
35+
expect(resolvedEvent, isNull);
36+
});
37+
test('does not resolve Answer when answer is null', () {
38+
final event = createPollVoteChangedFeedEvent(isAnswer: null);
39+
final resolvedEvent = pollAnswerCastedFeedEventResolver(event);
40+
expect(resolvedEvent, isNull);
41+
});
42+
});
43+
}
44+
45+
PollVoteCastedFeedEvent createPollVoteCastedFeedEvent({bool? isAnswer}) {
46+
return PollVoteCastedFeedEvent(
47+
createdAt: DateTime.now(),
48+
custom: const {},
49+
fid: '1',
50+
poll: PollResponseData(
51+
createdAt: DateTime.now(),
52+
updatedAt: DateTime.now(),
53+
id: '1',
54+
allowAnswers: true,
55+
allowUserSuggestedOptions: true,
56+
answersCount: 1,
57+
createdById: '1',
58+
custom: const {},
59+
description: '1',
60+
enforceUniqueVote: true,
61+
latestAnswers: const [],
62+
latestVotesByOption: const {},
63+
maxVotesAllowed: 1,
64+
name: '1',
65+
options: const [],
66+
ownVotes: const [],
67+
voteCount: 1,
68+
voteCountsByOption: const {},
69+
votingVisibility: '1',
70+
),
71+
type: 'poll.vote.casted',
72+
pollVote: PollVoteResponseData(
73+
createdAt: DateTime.now(),
74+
updatedAt: DateTime.now(),
75+
id: '1',
76+
optionId: '1',
77+
pollId: '1',
78+
isAnswer: isAnswer,
79+
),
80+
);
81+
}
82+
83+
PollVoteChangedFeedEvent createPollVoteChangedFeedEvent({bool? isAnswer}) {
84+
return PollVoteChangedFeedEvent(
85+
createdAt: DateTime.now(),
86+
custom: const {},
87+
fid: '1',
88+
poll: PollResponseData(
89+
createdAt: DateTime.now(),
90+
updatedAt: DateTime.now(),
91+
id: '1',
92+
allowAnswers: true,
93+
allowUserSuggestedOptions: true,
94+
answersCount: 1,
95+
createdById: '1',
96+
custom: const {},
97+
description: '1',
98+
enforceUniqueVote: true,
99+
latestAnswers: const [],
100+
latestVotesByOption: const {},
101+
maxVotesAllowed: 1,
102+
name: '1',
103+
options: const [],
104+
ownVotes: const [],
105+
voteCount: 1,
106+
voteCountsByOption: const {},
107+
votingVisibility: '1',
108+
),
109+
pollVote: PollVoteResponseData(
110+
createdAt: DateTime.now(),
111+
updatedAt: DateTime.now(),
112+
id: '1',
113+
optionId: '1',
114+
pollId: '1',
115+
isAnswer: isAnswer,
116+
),
117+
type: 'poll.vote.changed',
118+
);
119+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
// ignore_for_file: avoid_redundant_argument_values
2+
3+
import 'package:stream_feeds/src/resolvers/resolvers.dart';
4+
import 'package:stream_feeds/stream_feeds.dart';
5+
import 'package:test/test.dart';
6+
7+
void main() {
8+
group('pollAnswerRemovedFeedEventResolver', () {
9+
test('resolves Answer when answer is true', () {
10+
final event = createPollVoteRemovedFeedEvent(isAnswer: true);
11+
final resolvedEvent = pollAnswerRemovedFeedEventResolver(event);
12+
expect(resolvedEvent, isA<PollAnswerRemovedFeedEvent>());
13+
});
14+
});
15+
16+
test('does not resolve Answer when answer is false', () {
17+
final event = createPollVoteRemovedFeedEvent(isAnswer: false);
18+
final resolvedEvent = pollAnswerRemovedFeedEventResolver(event);
19+
expect(resolvedEvent, isNull);
20+
});
21+
test('does not resolve Answer when answer is null', () {
22+
final event = createPollVoteRemovedFeedEvent(isAnswer: null);
23+
final resolvedEvent = pollAnswerRemovedFeedEventResolver(event);
24+
expect(resolvedEvent, isNull);
25+
});
26+
}
27+
28+
PollVoteRemovedFeedEvent createPollVoteRemovedFeedEvent({bool? isAnswer}) {
29+
return PollVoteRemovedFeedEvent(
30+
createdAt: DateTime.now(),
31+
custom: const {},
32+
fid: '1',
33+
poll: PollResponseData(
34+
createdAt: DateTime.now(),
35+
updatedAt: DateTime.now(),
36+
id: '1',
37+
allowAnswers: true,
38+
allowUserSuggestedOptions: true,
39+
answersCount: 1,
40+
createdById: '1',
41+
custom: const {},
42+
description: '1',
43+
enforceUniqueVote: true,
44+
latestAnswers: const [],
45+
latestVotesByOption: const {},
46+
maxVotesAllowed: 1,
47+
name: '1',
48+
options: const [],
49+
ownVotes: const [],
50+
voteCount: 1,
51+
voteCountsByOption: const {},
52+
votingVisibility: '1',
53+
),
54+
pollVote: PollVoteResponseData(
55+
createdAt: DateTime.now(),
56+
updatedAt: DateTime.now(),
57+
id: '1',
58+
optionId: '1',
59+
pollId: '1',
60+
isAnswer: isAnswer,
61+
),
62+
type: 'poll.vote.removed',
63+
);
64+
}

sample_app/lib/screens/user_feed/feed/user_feed.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ class UserFeed extends StatelessWidget {
7070
),
7171
],
7272
UserFeedItem(
73+
feed: userFeed,
7374
data: activity,
7475
user: baseActivity.user,
7576
text: baseActivity.text ?? '',

sample_app/lib/screens/user_feed/feed/user_feed_item.dart

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,12 @@ import '../../../widgets/action_button.dart';
99
import '../../../widgets/attachment_gallery/attachment_metadata.dart';
1010
import '../../../widgets/attachments/attachments.dart';
1111
import '../../../widgets/user_avatar.dart';
12+
import '../polls/show_poll/show_poll_widget.dart';
1213

1314
class UserFeedItem extends StatelessWidget {
1415
const UserFeedItem({
1516
super.key,
17+
required this.feed,
1618
required this.user,
1719
required this.text,
1820
required this.attachments,
@@ -37,6 +39,7 @@ class UserFeedItem extends StatelessWidget {
3739
final VoidCallback? onBookmarkClick;
3840
final VoidCallback? onDeleteClick;
3941
final ValueChanged<String>? onEditSave;
42+
final Feed feed;
4043

4144
@override
4245
Widget build(BuildContext context) {
@@ -48,6 +51,7 @@ class UserFeedItem extends StatelessWidget {
4851
data: data,
4952
text: text,
5053
attachments: attachments,
54+
feed: feed,
5155
),
5256
const SizedBox(height: 8),
5357
Center(
@@ -72,12 +76,14 @@ class _UserContent extends StatelessWidget {
7276
required this.data,
7377
required this.text,
7478
required this.attachments,
79+
required this.feed,
7580
});
7681

7782
final UserData user;
7883
final ActivityData data;
7984
final String text;
8085
final List<Attachment> attachments;
86+
final Feed feed;
8187

8288
@override
8389
Widget build(BuildContext context) {
@@ -102,6 +108,8 @@ class _UserContent extends StatelessWidget {
102108
),
103109
const SizedBox(height: 8),
104110
_ActivityBody(
111+
feed: feed,
112+
activity: data,
105113
user: user,
106114
text: text,
107115
attachments: attachments,
@@ -117,12 +125,16 @@ class _UserContent extends StatelessWidget {
117125

118126
class _ActivityBody extends StatelessWidget {
119127
const _ActivityBody({
128+
required this.activity,
120129
required this.user,
121130
required this.text,
122131
required this.attachments,
123132
required this.data,
133+
required this.feed,
124134
});
125135

136+
final Feed feed;
137+
final ActivityData activity;
126138
final UserData user;
127139
final String text;
128140
final List<Attachment> attachments;
@@ -136,6 +148,8 @@ class _ActivityBody extends StatelessWidget {
136148
crossAxisAlignment: CrossAxisAlignment.start,
137149
children: [
138150
if (text.isNotEmpty) Text(text),
151+
if (data.poll case final poll?)
152+
ShowPollWidget(poll: poll, activity: activity, feed: feed),
139153
if (attachments.isNotEmpty) ...[
140154
AttachmentGrid(
141155
attachments: attachments,

0 commit comments

Comments
 (0)