From 31bdbe6b7f91bfebd081a0da1afb2f1821c4a3f6 Mon Sep 17 00:00:00 2001 From: Plague Fox Date: Sat, 12 Aug 2023 13:32:37 +0400 Subject: [PATCH] Update example --- .../feature/authentication/model/user.dart | 10 ++- .../chat_connection_controller.dart | 20 ++++++ .../controller/chat_connection_state.dart | 5 ++ .../feature/chat/data/chat_repository.dart | 66 +++++++++++++++++++ .../src/feature/chat/widget/chat_room.dart | 56 ++++++++++------ .../src/feature/chat/widget/chat_screen.dart | 18 +---- 6 files changed, 135 insertions(+), 40 deletions(-) create mode 100644 example/lib/src/feature/chat/controller/chat_connection_controller.dart create mode 100644 example/lib/src/feature/chat/controller/chat_connection_state.dart create mode 100644 example/lib/src/feature/chat/data/chat_repository.dart diff --git a/example/lib/src/feature/authentication/model/user.dart b/example/lib/src/feature/authentication/model/user.dart index 09d2c29..7c2d0f4 100644 --- a/example/lib/src/feature/authentication/model/user.dart +++ b/example/lib/src/feature/authentication/model/user.dart @@ -53,8 +53,7 @@ class UnauthenticatedUser extends User { @override bool operator ==(Object other) => - identical(this, other) || - other is UnauthenticatedUser && username == other.username; + identical(this, other) || other is UnauthenticatedUser; @override String toString() => 'UnauthenticatedUser()'; @@ -127,7 +126,12 @@ final class AuthenticatedUser extends User { @override bool operator ==(Object other) => identical(this, other) || - other is AuthenticatedUser && username == other.username; + other is AuthenticatedUser && + username == other.username && + endpoint == other.endpoint && + token == other.token && + channel == other.channel && + secret == other.secret; @override String toString() => 'AuthenticatedUser(username: $username)'; diff --git a/example/lib/src/feature/chat/controller/chat_connection_controller.dart b/example/lib/src/feature/chat/controller/chat_connection_controller.dart new file mode 100644 index 0000000..38b222b --- /dev/null +++ b/example/lib/src/feature/chat/controller/chat_connection_controller.dart @@ -0,0 +1,20 @@ +import 'package:spinifyapp/src/common/controller/droppable_controller_concurrency.dart'; +import 'package:spinifyapp/src/common/controller/state_controller.dart'; +import 'package:spinifyapp/src/feature/chat/controller/chat_connection_state.dart'; +import 'package:spinifyapp/src/feature/chat/data/chat_repository.dart'; + +final class ChatConnectionController + extends StateController + with DroppableControllerConcurrency { + ChatConnectionController({required IChatRepository repository}) + : _repository = repository, + super(initialState: repository.connectionState) { + _repository.connectionStates.distinct().listen(setState); + } + + final IChatRepository _repository; + + void connect(String url) => handle(() => _repository.connect(url)); + + void disconnect() => handle(_repository.disconnect); +} diff --git a/example/lib/src/feature/chat/controller/chat_connection_state.dart b/example/lib/src/feature/chat/controller/chat_connection_state.dart new file mode 100644 index 0000000..75f56d6 --- /dev/null +++ b/example/lib/src/feature/chat/controller/chat_connection_state.dart @@ -0,0 +1,5 @@ +enum ChatConnectionState { + disconnected, + connecting, + connected, +} diff --git a/example/lib/src/feature/chat/data/chat_repository.dart b/example/lib/src/feature/chat/data/chat_repository.dart new file mode 100644 index 0000000..2430fb9 --- /dev/null +++ b/example/lib/src/feature/chat/data/chat_repository.dart @@ -0,0 +1,66 @@ +import 'dart:async'; + +import 'package:spinify/spinify.dart'; +import 'package:spinifyapp/src/feature/chat/controller/chat_connection_state.dart'; + +/// Chat repository +abstract interface class IChatRepository { + Stream get messages; + + /// Connection state + ChatConnectionState get connectionState; + + /// Connection states stream + Stream get connectionStates; + + /// Connect to chat server + Future connect(String url); + + /// Disconnect from chat server + Future disconnect(); + + /// Dispose + Future dispose(); +} + +final class ChatRepositorySpinifyImpl implements IChatRepository { + ChatRepositorySpinifyImpl({required FutureOr Function()? getToken}) + : _spinify = Spinify( + SpinifyConfig( + getToken: getToken, + ), + ); + + /// Centrifugo client + final Spinify _spinify; + + @override + ChatConnectionState get connectionState => + _spinifyStateToConnectionState(_spinify.state); + + @override + late final Stream connectionStates = + _spinify.states.map(_spinifyStateToConnectionState); + + ChatConnectionState _spinifyStateToConnectionState(SpinifyState state) => + switch (state) { + SpinifyState$Connected _ => ChatConnectionState.connected, + SpinifyState$Connecting _ => ChatConnectionState.connecting, + SpinifyState$Disconnected _ => ChatConnectionState.disconnected, + SpinifyState$Closed _ => ChatConnectionState.disconnected, + }; + + @override + Stream get messages => throw UnimplementedError(); + + @override + Future connect(String url) => _spinify.connect(url); + + @override + Future disconnect() => _spinify.disconnect(); + + @override + Future dispose() async { + await _spinify.close(); + } +} diff --git a/example/lib/src/feature/chat/widget/chat_room.dart b/example/lib/src/feature/chat/widget/chat_room.dart index ed0363b..a07c891 100644 --- a/example/lib/src/feature/chat/widget/chat_room.dart +++ b/example/lib/src/feature/chat/widget/chat_room.dart @@ -1,18 +1,19 @@ import 'package:flutter/material.dart'; -import 'package:meta/meta.dart'; +import 'package:spinifyapp/src/common/controller/state_consumer.dart'; +import 'package:spinifyapp/src/feature/authentication/model/user.dart'; +import 'package:spinifyapp/src/feature/chat/controller/chat_connection_controller.dart'; +import 'package:spinifyapp/src/feature/chat/controller/chat_connection_state.dart'; +import 'package:spinifyapp/src/feature/chat/data/chat_repository.dart'; /// {@template chat_screen} /// ChatRoom widget. /// {@endtemplate} class ChatRoom extends StatefulWidget { /// {@macro chat_screen} - const ChatRoom({super.key}); + const ChatRoom({required this.user, super.key}); - /// The state from the closest instance of this class - /// that encloses the given context, if any. - @internal - static _ChatRoomState? maybeOf(BuildContext context) => - context.findAncestorStateOfType<_ChatRoomState>(); + /// The user that is currently logged in + final AuthenticatedUser user; @override State createState() => _ChatRoomState(); @@ -20,34 +21,47 @@ class ChatRoom extends StatefulWidget { /// State for widget ChatRoom. class _ChatRoomState extends State { - /* #region Lifecycle */ + late final IChatRepository _repository; + late final ChatConnectionController _chatConnectionController; + @override void initState() { super.initState(); - // Initial state initialization + _repository = ChatRepositorySpinifyImpl(getToken: () => widget.user.token); + _chatConnectionController = + ChatConnectionController(repository: _repository); + _chatConnectionController.connect(widget.user.endpoint); } @override - void didUpdateWidget(ChatRoom oldWidget) { + void didUpdateWidget(covariant ChatRoom oldWidget) { super.didUpdateWidget(oldWidget); - // Widget configuration changed - } - - @override - void didChangeDependencies() { - super.didChangeDependencies(); - // The configuration of InheritedWidgets has changed - // Also called after initState but before build + if (oldWidget.user != widget.user) { + _chatConnectionController.disconnect(); + _chatConnectionController.connect(widget.user.endpoint); + } } @override void dispose() { - // Permanent removal of a tree stent + _chatConnectionController.dispose(); + _repository.dispose(); super.dispose(); } - /* #endregion */ @override + Widget build(BuildContext context) => Center( + child: StateConsumer( + controller: _chatConnectionController, + builder: (context, state, child) => switch (state) { + ChatConnectionState.connecting => const CircularProgressIndicator(), + ChatConnectionState.connected => const Text('Connected'), + ChatConnectionState.disconnected => const Text('Disconnected'), + }, + ), + ); + + /* @override Widget build(BuildContext context) => ListView.builder( scrollDirection: Axis.vertical, reverse: true, @@ -55,5 +69,5 @@ class _ChatRoomState extends State { itemBuilder: (context, index) => ListTile( title: Text('Item $index'), ), - ); + ); */ } diff --git a/example/lib/src/feature/chat/widget/chat_screen.dart b/example/lib/src/feature/chat/widget/chat_screen.dart index 397192a..e8860ff 100644 --- a/example/lib/src/feature/chat/widget/chat_screen.dart +++ b/example/lib/src/feature/chat/widget/chat_screen.dart @@ -1,30 +1,16 @@ import 'package:flutter/material.dart'; import 'package:spinifyapp/src/common/controller/state_consumer.dart'; import 'package:spinifyapp/src/common/localization/localization.dart'; -import 'package:spinifyapp/src/feature/authentication/controller/authentication_controller.dart'; import 'package:spinifyapp/src/feature/authentication/widget/authentication_scope.dart'; import 'package:spinifyapp/src/feature/chat/widget/chat_room.dart'; /// {@template chat_screen} /// ChatScreen widget. /// {@endtemplate} -class ChatScreen extends StatefulWidget { +class ChatScreen extends StatelessWidget { /// {@macro chat_screen} const ChatScreen({super.key}); - @override - State createState() => _ChatScreenState(); -} - -class _ChatScreenState extends State { - late final AuthenticationController authController; - - @override - void initState() { - super.initState(); - authController = AuthenticationScope.controllerOf(context); - } - @override Widget build(BuildContext context) { final authController = AuthenticationScope.controllerOf(context); @@ -49,7 +35,7 @@ class _ChatScreenState extends State { body: AnimatedSwitcher( duration: const Duration(milliseconds: 250), child: state.user.map( - authenticated: (user) => const ChatRoom(), + authenticated: (user) => ChatRoom(user: user), unauthenticated: (_) => const SizedBox.expand(), ), ),