Skip to content

Commit

Permalink
Update message sender
Browse files Browse the repository at this point in the history
  • Loading branch information
PlugFox committed Aug 12, 2023
1 parent 7d27e8c commit 09d33ab
Show file tree
Hide file tree
Showing 12 changed files with 708 additions and 54 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@ class AuthenticationRepositoryImpl implements IAuthenticationRepository {
},
channels: <String>[
switch (secret) {
null || '' => channel,
String secret => encodeChannel(secret),
null => channel,
}
],
);
Expand All @@ -63,17 +63,27 @@ class AuthenticationRepositoryImpl implements IAuthenticationRepository {
Stream<User> userChanges() => _userController.stream;

@override
Future<void> signIn(SignInData data) => Future<void>.sync(
() => _userController.add(
_user = User.authenticated(
Future<void> signIn(SignInData data) {
String encryptedChannel(String channel, String secret) => '${data.channel}'
'#'
'${hex.encode(utf8.encoder.fuse(sha256).convert(secret).bytes)}';
return Future<void>.sync(
() => _userController.add(
_user = User.authenticated(
username: data.username,
endpoint: data.endpoint,
token: data.token,
channel: data.channel,
secret: data.secret,
),
),
);
channel: switch (data.secret) {
null || '' => data.channel,
String secret => encryptedChannel(data.channel, secret),
},
secret: switch (data.secret) {
null || '' => null,
String secret => secret,
}),
),
);
}

@override
Future<void> signOut() => Future<void>.sync(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ final class SignInData {
final String? secret;

static final RegExp _urlValidator = RegExp(
r'^(https?:\/\/)?(localhost|((([a-z\d]([a-z\d-]*[a-z\d])*)\.)+[a-z]{2,}|((\d{1,3}\.){3}\d{1,3})))?(:\d+)?(\/[-a-z\d%_.~+]*)*(\?[;&a-z\d%_.~+=-]*)?(\#[-a-z\d_]*)?$',
r'^(https?|ws|wss):\/\/(localhost|((([a-z\d]([a-z\d-]*[a-z\d])*)\.)+[a-z]{2,}|((\d{1,3}\.){3}\d{1,3})))?(:\d+)?(\/[-a-z\d%_.~+]*)*(\?[;&a-z\d%_.~+=-]*)?(\#[-a-z\d_]*)?$',
caseSensitive: false,
multiLine: false,
);
Expand Down
122 changes: 118 additions & 4 deletions example/lib/src/feature/chat/controller/chat_connection_state.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,119 @@
enum ChatConnectionState {
disconnected,
connecting,
connected,
import 'package:meta/meta.dart';

/// {@template chat_connection_state}
/// ChatConnectionState.
/// {@endtemplate}
sealed class ChatConnectionState extends _$ChatConnectionStateBase {
/// Disconnected
/// {@macro chat_connection_state}
const factory ChatConnectionState.disconnected({
String message,
}) = ChatConnectionState$Disconnected;

/// Connecting
/// {@macro chat_connection_state}
const factory ChatConnectionState.connecting({
String message,
}) = ChatConnectionState$Connecting;

/// Connected
/// {@macro chat_connection_state}
const factory ChatConnectionState.connected({
String message,
}) = ChatConnectionState$Connected;

/// {@macro chat_connection_state}
const ChatConnectionState({required super.message});
}

/// Disconnected
/// {@nodoc}
final class ChatConnectionState$Disconnected extends ChatConnectionState {
/// {@nodoc}
const ChatConnectionState$Disconnected({super.message = 'Disconnected'});
}

/// Connecting
/// {@nodoc}
final class ChatConnectionState$Connecting extends ChatConnectionState {
/// {@nodoc}
const ChatConnectionState$Connecting({super.message = 'Connecting'});
}

/// Connected
/// {@nodoc}
final class ChatConnectionState$Connected extends ChatConnectionState {
/// {@nodoc}
const ChatConnectionState$Connected({super.message = 'Connected'});
}

/// Pattern matching for [ChatConnectionState].
typedef ChatConnectionStateMatch<R, S extends ChatConnectionState> = R Function(
S state);

/// {@nodoc}
@immutable
abstract base class _$ChatConnectionStateBase {
/// {@nodoc}
const _$ChatConnectionStateBase({required this.message});

/// Message or state description.
@nonVirtual
final String message;

/// Is connecting?
bool get isConnecting =>
maybeMap<bool>(orElse: () => false, connecting: (_) => true);

/// Is connected?
bool get isConnected =>
maybeMap<bool>(orElse: () => false, connected: (_) => true);

/// Is disconnected?
bool get isDisconnected =>
maybeMap<bool>(orElse: () => false, disconnected: (_) => true);

/// Pattern matching for [ChatConnectionState].
R map<R>({
required ChatConnectionStateMatch<R, ChatConnectionState$Disconnected>
disconnected,
required ChatConnectionStateMatch<R, ChatConnectionState$Connecting>
connecting,
required ChatConnectionStateMatch<R, ChatConnectionState$Connected>
connected,
}) =>
switch (this) {
ChatConnectionState$Disconnected s => disconnected(s),
ChatConnectionState$Connecting s => connecting(s),
ChatConnectionState$Connected s => connected(s),
_ => throw AssertionError(),
};

/// Pattern matching for [ChatConnectionState].
R maybeMap<R>({
ChatConnectionStateMatch<R, ChatConnectionState$Disconnected>? disconnected,
ChatConnectionStateMatch<R, ChatConnectionState$Connecting>? connecting,
ChatConnectionStateMatch<R, ChatConnectionState$Connected>? connected,
required R Function() orElse,
}) =>
map<R>(
disconnected: disconnected ?? (_) => orElse(),
connecting: connecting ?? (_) => orElse(),
connected: connected ?? (_) => orElse(),
);

/// Pattern matching for [ChatConnectionState].
R? mapOrNull<R>({
ChatConnectionStateMatch<R, ChatConnectionState$Disconnected>? disconnected,
ChatConnectionStateMatch<R, ChatConnectionState$Connecting>? connecting,
ChatConnectionStateMatch<R, ChatConnectionState$Connected>? connected,
}) =>
map<R?>(
disconnected: disconnected ?? (_) => null,
connecting: connecting ?? (_) => null,
connected: connected ?? (_) => null,
);

@override
String toString() => message;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import 'package:l/l.dart';
import 'package:spinifyapp/src/common/controller/droppable_controller_concurrency.dart';
import 'package:spinifyapp/src/common/controller/state_controller.dart';
import 'package:spinifyapp/src/feature/authentication/model/user.dart';
import 'package:spinifyapp/src/feature/chat/controller/chat_messages_state.dart';
import 'package:spinifyapp/src/feature/chat/data/chat_repository.dart';

final class ChatMessagesController extends StateController<ChatMessagesState>
with DroppableControllerConcurrency {
ChatMessagesController({required IChatRepository repository})
: _repository = repository,
super(initialState: ChatMessagesState.initial);

final IChatRepository _repository;

void sendMessage(AuthenticatedUser user, String message) => handle(
() async {
l.v6('Sending message');
await _repository.sendMessage(user, message);
setState(ChatMessagesState.successful(
data: state.data, message: 'Message sent'));
l.v6('Message sent');
},
(error, stackTrace) {
l.w('Error sending message: $error', stackTrace);
setState(
ChatMessagesState.error(
data: state.data, message: 'Error sending message'),
);
},
() => setState(ChatMessagesState.idle(data: state.data)),
);

void disconnect() => handle(_repository.disconnect);

@override
void dispose() {
_repository.disconnect();
super.dispose();
}
}
161 changes: 161 additions & 0 deletions example/lib/src/feature/chat/controller/chat_messages_state.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
import 'package:meta/meta.dart';

/// {@template chat_messages_state_placeholder}
/// Entity placeholder for ChatMessagesState
/// {@endtemplate}
typedef ChatMessagesEntity = Object;

/// {@template chat_messages_state}
/// ChatMessagesState.
/// {@endtemplate}
sealed class ChatMessagesState extends _$ChatMessagesStateBase {
/// Idling state
/// {@macro chat_messages_state}
const factory ChatMessagesState.idle({
required ChatMessagesEntity? data,
String message,
}) = ChatMessagesState$Idle;

/// Processing
/// {@macro chat_messages_state}
const factory ChatMessagesState.processing({
required ChatMessagesEntity? data,
String message,
}) = ChatMessagesState$Processing;

/// Successful
/// {@macro chat_messages_state}
const factory ChatMessagesState.successful({
required ChatMessagesEntity? data,
String message,
}) = ChatMessagesState$Successful;

/// An error has occurred
/// {@macro chat_messages_state}
const factory ChatMessagesState.error({
required ChatMessagesEntity? data,
String message,
}) = ChatMessagesState$Error;

/// {@macro chat_messages_state}
const ChatMessagesState({required super.data, required super.message});

static ChatMessagesState get initial =>
const ChatMessagesState.idle(data: null);
}

/// Idling state
/// {@nodoc}
final class ChatMessagesState$Idle extends ChatMessagesState {
/// {@nodoc}
const ChatMessagesState$Idle({required super.data, super.message = 'Idling'});
}

/// Processing
/// {@nodoc}
final class ChatMessagesState$Processing extends ChatMessagesState {
/// {@nodoc}
const ChatMessagesState$Processing(
{required super.data, super.message = 'Processing'});
}

/// Successful
/// {@nodoc}
final class ChatMessagesState$Successful extends ChatMessagesState {
/// {@nodoc}
const ChatMessagesState$Successful(
{required super.data, super.message = 'Successful'});
}

/// Error
/// {@nodoc}
final class ChatMessagesState$Error extends ChatMessagesState {
/// {@nodoc}
const ChatMessagesState$Error(
{required super.data, super.message = 'An error has occurred.'});
}

/// Pattern matching for [ChatMessagesState].
typedef ChatMessagesStateMatch<R, S extends ChatMessagesState> = R Function(
S state);

/// {@nodoc}
@immutable
abstract base class _$ChatMessagesStateBase {
/// {@nodoc}
const _$ChatMessagesStateBase({required this.data, required this.message});

/// Data entity payload.
@nonVirtual
final ChatMessagesEntity? data;

/// Message or state description.
@nonVirtual
final String message;

/// Has data?
bool get hasData => data != null;

/// If an error has occurred?
bool get hasError => maybeMap<bool>(orElse: () => false, error: (_) => true);

/// Is in progress state?
bool get isProcessing =>
maybeMap<bool>(orElse: () => false, processing: (_) => true);

/// Is in idle state?
bool get isIdling => !isProcessing;

/// Pattern matching for [ChatMessagesState].
R map<R>({
required ChatMessagesStateMatch<R, ChatMessagesState$Idle> idle,
required ChatMessagesStateMatch<R, ChatMessagesState$Processing> processing,
required ChatMessagesStateMatch<R, ChatMessagesState$Successful> successful,
required ChatMessagesStateMatch<R, ChatMessagesState$Error> error,
}) =>
switch (this) {
ChatMessagesState$Idle s => idle(s),
ChatMessagesState$Processing s => processing(s),
ChatMessagesState$Successful s => successful(s),
ChatMessagesState$Error s => error(s),
_ => throw AssertionError(),
};

/// Pattern matching for [ChatMessagesState].
R maybeMap<R>({
ChatMessagesStateMatch<R, ChatMessagesState$Idle>? idle,
ChatMessagesStateMatch<R, ChatMessagesState$Processing>? processing,
ChatMessagesStateMatch<R, ChatMessagesState$Successful>? successful,
ChatMessagesStateMatch<R, ChatMessagesState$Error>? error,
required R Function() orElse,
}) =>
map<R>(
idle: idle ?? (_) => orElse(),
processing: processing ?? (_) => orElse(),
successful: successful ?? (_) => orElse(),
error: error ?? (_) => orElse(),
);

/// Pattern matching for [ChatMessagesState].
R? mapOrNull<R>({
ChatMessagesStateMatch<R, ChatMessagesState$Idle>? idle,
ChatMessagesStateMatch<R, ChatMessagesState$Processing>? processing,
ChatMessagesStateMatch<R, ChatMessagesState$Successful>? successful,
ChatMessagesStateMatch<R, ChatMessagesState$Error>? error,
}) =>
map<R?>(
idle: idle ?? (_) => null,
processing: processing ?? (_) => null,
successful: successful ?? (_) => null,
error: error ?? (_) => null,
);

@override
int get hashCode => data.hashCode;

@override
bool operator ==(Object other) => identical(this, other);

@override
String toString() => 'ChatMessagesState(data: $data, message: $message)';
}
Loading

0 comments on commit 09d33ab

Please sign in to comment.