Skip to content

Commit

Permalink
feat(core, ui, localization): Add Poll attachment composer (#2048)
Browse files Browse the repository at this point in the history
Co-authored-by: Deven Joshi <[email protected]>
  • Loading branch information
xsahil03x and deven98 authored Dec 11, 2024
1 parent d2d5ac9 commit bd684f2
Show file tree
Hide file tree
Showing 123 changed files with 5,382 additions and 227 deletions.
30 changes: 30 additions & 0 deletions packages/stream_chat/lib/src/client/channel.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1035,6 +1035,36 @@ class Channel {
return _client.sendEvent(id!, type, event);
}

/// Send a message with a poll to this channel.
///
/// Optionally provide a [messageText] to send a message along with the poll.
Future<SendMessageResponse> sendPoll(
Poll poll, {
String messageText = '',
}) async {
_checkInitialized();
final res = await _client.createPoll(poll);
return sendMessage(
Message(
text: messageText,
poll: res.poll,
pollId: res.poll.id,
),
);
}

/// Updates the [poll] in this channel.
Future<UpdatePollResponse> updatePoll(Poll poll) {
_checkInitialized();
return _client.updatePoll(poll);
}

/// Deletes the poll with the given [pollId] from this channel.
Future<EmptyResponse> deletePoll(String pollId) {
_checkInitialized();
return _client.deletePoll(pollId);
}

/// Send a reaction to this channel.
///
/// Set [enforceUnique] to true to remove the existing user reaction.
Expand Down
14 changes: 11 additions & 3 deletions packages/stream_chat/lib/src/core/models/poll.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ import 'package:uuid/uuid.dart';

part 'poll.g.dart';

class _NullConst {
const _NullConst();
}

const _nullConst = _NullConst();

/// {@template streamVotingVisibility}
/// Represents the visibility of the voting process.
/// {@endtemplate}
Expand All @@ -32,7 +38,7 @@ class Poll extends Equatable {
this.description,
required this.options,
this.votingVisibility = VotingVisibility.public,
this.enforceUniqueVote = false,
this.enforceUniqueVote = true,
this.maxVotesAllowed,
this.allowAnswers = false,
this.answers = const [],
Expand Down Expand Up @@ -159,7 +165,7 @@ class Poll extends Equatable {
List<PollOption>? options,
VotingVisibility? votingVisibility,
bool? enforceUniqueVote,
int? maxVotesAllowed,
Object? maxVotesAllowed = _nullConst,
bool? allowUserSuggestedOptions,
bool? allowAnswers,
bool? isClosed,
Expand All @@ -182,7 +188,9 @@ class Poll extends Equatable {
options: options ?? this.options,
votingVisibility: votingVisibility ?? this.votingVisibility,
enforceUniqueVote: enforceUniqueVote ?? this.enforceUniqueVote,
maxVotesAllowed: maxVotesAllowed ?? this.maxVotesAllowed,
maxVotesAllowed: maxVotesAllowed == _nullConst
? this.maxVotesAllowed
: maxVotesAllowed as int?,
allowUserSuggestedOptions:
allowUserSuggestedOptions ?? this.allowUserSuggestedOptions,
allowAnswers: allowAnswers ?? this.allowAnswers,
Expand Down
2 changes: 1 addition & 1 deletion packages/stream_chat/lib/src/core/models/poll.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

20 changes: 19 additions & 1 deletion packages/stream_chat/lib/src/core/models/poll_option.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ import 'package:stream_chat/src/core/util/serializer.dart';

part 'poll_option.g.dart';

class _NullConst {
const _NullConst();
}

const _nullConst = _NullConst();

/// {@template streamPollOption}
/// A model class representing a poll option.
/// {@endtemplate}
Expand All @@ -23,7 +29,7 @@ class PollOption extends Equatable {
);

/// The unique identifier of the poll option.
@JsonKey(includeToJson: false)
@JsonKey(includeIfNull: false)
final String? id;

/// The text describing the poll option.
Expand All @@ -36,6 +42,18 @@ class PollOption extends Equatable {
Map<String, dynamic> toJson() =>
Serializer.moveFromExtraDataToRoot(_$PollOptionToJson(this));

/// Creates a copy of [PollOption] with specified attributes overridden.
PollOption copyWith({
Object? id = _nullConst,
String? text,
Map<String, Object?>? extraData,
}) =>
PollOption(
id: id == _nullConst ? this.id : id as String?,
text: text ?? this.text,
extraData: extraData ?? this.extraData,
);

/// Known top level fields.
///
/// Useful for [Serializer] methods.
Expand Down
19 changes: 14 additions & 5 deletions packages/stream_chat/lib/src/core/models/poll_option.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion packages/stream_chat/lib/src/core/models/poll_vote.dart
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ class PollVote extends Equatable {
_$PollVoteFromJson(json);

/// The unique identifier of the poll vote.
@JsonKey(includeToJson: false)
@JsonKey(includeIfNull: false)
final String? id;

/// The unique identifier of the option selected in the poll.
Expand Down
1 change: 1 addition & 0 deletions packages/stream_chat/lib/src/core/models/poll_vote.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 5 additions & 4 deletions packages/stream_chat/lib/src/db/chat_persistence_client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -255,10 +255,11 @@ abstract class ChatPersistenceClient {
final members = state.members;
final Iterable<Message>? messages;
if (CurrentPlatform.isWeb) {
messages = state.messages?.where((it) => !it.attachments.any(
(attachment) =>
attachment.uploadState != const UploadState.success(),
));
messages = state.messages?.where(
(it) => !it.attachments.any(
(it) => it.uploadState != const UploadState.success(),
),
);
} else {
messages = state.messages;
}
Expand Down
3 changes: 3 additions & 0 deletions packages/stream_chat/lib/src/permission_type.dart
Original file line number Diff line number Diff line change
Expand Up @@ -93,4 +93,7 @@ class PermissionType {
/// Capability required to update/edit channel members
/// Channel is not distinct and user has UpdateChannelMembers permission
static const String updateChannelMembers = 'update-channel-members';

/// Capability required to send a poll in a channel.
static const String sendPoll = 'send-poll';
}
7 changes: 5 additions & 2 deletions packages/stream_chat/test/src/core/models/poll_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,13 @@ void main() {
expect(json['name'], 'test');
expect(json['description'], isNull);
expect(json['options'], [
{'text': 'option1 text'}
{
'id': 'option1',
'text': 'option1 text',
}
]);
expect(json['voting_visibility'], 'public');
expect(json['enforce_unique_vote'], false);
expect(json['enforce_unique_vote'], true);
expect(json['max_votes_allowed'], isNull);
expect(json['allow_user_suggested_options'], false);
expect(json['allow_answers'], false);
Expand Down
6 changes: 6 additions & 0 deletions packages/stream_chat_flutter/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
## Upcoming

✅ Added

- Added a new `StreamPollCreator` widget to facilitate poll creation within the chat interface.

## 8.3.0

✅ Added
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ class StreamUnreadIndicator extends StatelessWidget {

@override
Widget build(BuildContext context) {
final theme = StreamChatTheme.of(context);
final client = StreamChat.of(context).client;
return IgnorePointer(
child: BetterStreamBuilder<int>(
Expand All @@ -25,31 +26,18 @@ class StreamUnreadIndicator extends StatelessWidget {
initialData: cid != null
? client.state.channels[cid]?.state?.unreadCount
: client.state.totalUnreadCount,
builder: (context, data) {
if (data == 0) {
return const Offstage();
}
return Material(
borderRadius: BorderRadius.circular(8),
color: StreamChatTheme.of(context)
.channelPreviewTheme
.unreadCounterColor,
child: Padding(
padding: const EdgeInsets.only(
left: 5,
right: 5,
top: 2,
bottom: 1,
),
child: Center(
child: Text(
'${data > 99 ? '99+' : data}',
style: const TextStyle(
fontSize: 11,
color: Colors.white,
),
),
),
builder: (context, unreadCount) {
if (unreadCount == 0) return const SizedBox.shrink();

return Badge(
textColor: Colors.white,
textStyle: theme.textTheme.footnoteBold,
backgroundColor: theme.channelPreviewTheme.unreadCounterColor,
label: Text(
switch (unreadCount) {
> 99 => '99+',
_ => '$unreadCount',
},
),
);
},
Expand Down
Loading

0 comments on commit bd684f2

Please sign in to comment.