diff --git a/packages/mottai_flutter_app/lib/chat/chat_room.dart b/packages/mottai_flutter_app/lib/chat/chat_room.dart index 7851a7e6..38e5bda5 100644 --- a/packages/mottai_flutter_app/lib/chat/chat_room.dart +++ b/packages/mottai_flutter_app/lib/chat/chat_room.dart @@ -45,6 +45,7 @@ final chatRoomStateNotifierProvider = StateNotifierProvider.family .autoDispose( (ref, chatRoomId) => ChatRoomStateNotifier( chatRoomId: chatRoomId, + chatRoomRepository: ref.watch(chatRoomRepositoryProvider), chatMessageRepository: ref.watch(chatMessageRepositoryProvider), ), ); @@ -52,8 +53,10 @@ final chatRoomStateNotifierProvider = StateNotifierProvider.family class ChatRoomStateNotifier extends StateNotifier { ChatRoomStateNotifier({ required String chatRoomId, + required ChatRoomRepository chatRoomRepository, required ChatMessageRepository chatMessageRepository, }) : _chatRoomId = chatRoomId, + _chatRoomRepository = chatRoomRepository, _chatMessageRepository = chatMessageRepository, super(const ChatRoomState()) { _newReadChatMessagesSubscription = _chatMessageRepository @@ -64,6 +67,7 @@ class ChatRoomStateNotifier extends StateNotifier { .listen(_updateNewReadChatMessages); Future(() async { await Future.wait([ + _fetchChatRoom(), loadMore(), // ChatPage に遷移直後のメッセージアイコンを意図的に見せるために最低でも 500 ms 待つ。 Future.delayed(const Duration(milliseconds: 500)), @@ -78,10 +82,13 @@ class ChatRoomStateNotifier extends StateNotifier { super.dispose(); } + /// [ChatRoomRepository] のインスタンス。 + final ChatRoomRepository _chatRoomRepository; + /// [ChatMessageRepository] のインスタンス。 - late final ChatMessageRepository _chatMessageRepository; + final ChatMessageRepository _chatMessageRepository; - /// チャットルームの ID。 + /// チャットルームの ID. final String _chatRoomId; /// 無限スクロールで取得するメッセージ件数の limit 値。 @@ -95,6 +102,14 @@ class ChatRoomStateNotifier extends StateNotifier { late final StreamSubscription> _newReadChatMessagesSubscription; + /// チャットルームを取得する。 + Future _fetchChatRoom() async { + final readChatRoom = await _chatRoomRepository.fetchChatRoom( + chatRoomId: _chatRoomId, + ); + state = state.copyWith(readChatRoom: readChatRoom); + } + /// 過去のメッセージを、最後に取得した queryDocumentSnapshot 以降の _limit 件だけ取得する。 Future loadMore() async { if (!state.hasMore) { diff --git a/packages/mottai_flutter_app/lib/chat/chat_room_state.dart b/packages/mottai_flutter_app/lib/chat/chat_room_state.dart index 5748ca31..7c09b5ad 100644 --- a/packages/mottai_flutter_app/lib/chat/chat_room_state.dart +++ b/packages/mottai_flutter_app/lib/chat/chat_room_state.dart @@ -9,6 +9,9 @@ class ChatRoomState with _$ChatRoomState { /// チャットページに入ったときの初回ローディング中かどうか。 @Default(true) bool loading, + /// チャットルーム。初回ローディングで取得することを期待する。 + ReadChatRoom? readChatRoom, + /// メッセージを送信中かどうか。 @Default(false) bool sending, diff --git a/packages/mottai_flutter_app/lib/chat/chat_room_state.freezed.dart b/packages/mottai_flutter_app/lib/chat/chat_room_state.freezed.dart index 48f190b1..0ea8c021 100644 --- a/packages/mottai_flutter_app/lib/chat/chat_room_state.freezed.dart +++ b/packages/mottai_flutter_app/lib/chat/chat_room_state.freezed.dart @@ -19,6 +19,9 @@ mixin _$ChatRoomState { /// チャットページに入ったときの初回ローディング中かどうか。 bool get loading => throw _privateConstructorUsedError; + /// チャットルーム。初回ローディングで取得することを期待する。 + ReadChatRoom? get readChatRoom => throw _privateConstructorUsedError; + /// メッセージを送信中かどうか。 bool get sending => throw _privateConstructorUsedError; @@ -56,6 +59,7 @@ abstract class $ChatRoomStateCopyWith<$Res> { @useResult $Res call( {bool loading, + ReadChatRoom? readChatRoom, bool sending, List readChatMessages, List newReadChatMessages, @@ -79,6 +83,7 @@ class _$ChatRoomStateCopyWithImpl<$Res, $Val extends ChatRoomState> @override $Res call({ Object? loading = null, + Object? readChatRoom = freezed, Object? sending = null, Object? readChatMessages = null, Object? newReadChatMessages = null, @@ -92,6 +97,10 @@ class _$ChatRoomStateCopyWithImpl<$Res, $Val extends ChatRoomState> ? _value.loading : loading // ignore: cast_nullable_to_non_nullable as bool, + readChatRoom: freezed == readChatRoom + ? _value.readChatRoom + : readChatRoom // ignore: cast_nullable_to_non_nullable + as ReadChatRoom?, sending: null == sending ? _value.sending : sending // ignore: cast_nullable_to_non_nullable @@ -134,6 +143,7 @@ abstract class _$$_ChatRoomStateCopyWith<$Res> @useResult $Res call( {bool loading, + ReadChatRoom? readChatRoom, bool sending, List readChatMessages, List newReadChatMessages, @@ -155,6 +165,7 @@ class __$$_ChatRoomStateCopyWithImpl<$Res> @override $Res call({ Object? loading = null, + Object? readChatRoom = freezed, Object? sending = null, Object? readChatMessages = null, Object? newReadChatMessages = null, @@ -168,6 +179,10 @@ class __$$_ChatRoomStateCopyWithImpl<$Res> ? _value.loading : loading // ignore: cast_nullable_to_non_nullable as bool, + readChatRoom: freezed == readChatRoom + ? _value.readChatRoom + : readChatRoom // ignore: cast_nullable_to_non_nullable + as ReadChatRoom?, sending: null == sending ? _value.sending : sending // ignore: cast_nullable_to_non_nullable @@ -205,6 +220,7 @@ class __$$_ChatRoomStateCopyWithImpl<$Res> class _$_ChatRoomState implements _ChatRoomState { const _$_ChatRoomState( {this.loading = true, + this.readChatRoom, this.sending = false, final List readChatMessages = const [], final List newReadChatMessages = @@ -223,6 +239,10 @@ class _$_ChatRoomState implements _ChatRoomState { @JsonKey() final bool loading; + /// チャットルーム。初回ローディングで取得することを期待する。 + @override + final ReadChatRoom? readChatRoom; + /// メッセージを送信中かどうか。 @override @JsonKey() @@ -283,7 +303,7 @@ class _$_ChatRoomState implements _ChatRoomState { @override String toString() { - return 'ChatRoomState(loading: $loading, sending: $sending, readChatMessages: $readChatMessages, newReadChatMessages: $newReadChatMessages, pastReadChatMessages: $pastReadChatMessages, fetching: $fetching, hasMore: $hasMore, lastReadChatMessageId: $lastReadChatMessageId)'; + return 'ChatRoomState(loading: $loading, readChatRoom: $readChatRoom, sending: $sending, readChatMessages: $readChatMessages, newReadChatMessages: $newReadChatMessages, pastReadChatMessages: $pastReadChatMessages, fetching: $fetching, hasMore: $hasMore, lastReadChatMessageId: $lastReadChatMessageId)'; } @override @@ -292,6 +312,8 @@ class _$_ChatRoomState implements _ChatRoomState { (other.runtimeType == runtimeType && other is _$_ChatRoomState && (identical(other.loading, loading) || other.loading == loading) && + (identical(other.readChatRoom, readChatRoom) || + other.readChatRoom == readChatRoom) && (identical(other.sending, sending) || other.sending == sending) && const DeepCollectionEquality() .equals(other._readChatMessages, _readChatMessages) && @@ -310,6 +332,7 @@ class _$_ChatRoomState implements _ChatRoomState { int get hashCode => Object.hash( runtimeType, loading, + readChatRoom, sending, const DeepCollectionEquality().hash(_readChatMessages), const DeepCollectionEquality().hash(_newReadChatMessages), @@ -328,6 +351,7 @@ class _$_ChatRoomState implements _ChatRoomState { abstract class _ChatRoomState implements ChatRoomState { const factory _ChatRoomState( {final bool loading, + final ReadChatRoom? readChatRoom, final bool sending, final List readChatMessages, final List newReadChatMessages, @@ -342,6 +366,10 @@ abstract class _ChatRoomState implements ChatRoomState { bool get loading; @override + /// チャットルーム。初回ローディングで取得することを期待する。 + ReadChatRoom? get readChatRoom; + @override + /// メッセージを送信中かどうか。 bool get sending; @override diff --git a/packages/mottai_flutter_app/lib/chat/ui/chat_room.dart b/packages/mottai_flutter_app/lib/chat/ui/chat_room.dart index 2f1e3f71..70662868 100644 --- a/packages/mottai_flutter_app/lib/chat/ui/chat_room.dart +++ b/packages/mottai_flutter_app/lib/chat/ui/chat_room.dart @@ -71,13 +71,9 @@ class _ChatRoomPageState extends ConsumerState { @override Widget build(BuildContext context) { - final readChatRoom = - ref.watch(chatRoomFutureProvider(widget.chatRoomId)).value; - if (readChatRoom == null) { - // TODO: この実装だと、loading 中に UnavailablePage がちらっと見えそうなので改善したい。 - return const UnavailablePage('チャットルームの情報の取得に失敗しました。'); - } final state = ref.watch(chatRoomStateNotifierProvider(widget.chatRoomId)); + final loading = state.loading; + final readChatRoom = state.readChatRoom; return Scaffold( appBar: AppBar( // TODO: chatPartnerImageUrlProvider を真似して、chatPartnerNameProvider @@ -88,50 +84,55 @@ class _ChatRoomPageState extends ConsumerState { body: Padding( padding: const EdgeInsets.symmetric(horizontal: 8), child: AuthDependentBuilder( - onAuthenticated: (userId) => state.loading - ? const Center( - child: FaIcon( - FontAwesomeIcons.solidComment, - size: 72, - color: Colors.black12, - ), - ) - : Stack( + onAuthenticated: (userId) { + if (loading) { + return const Center( + child: FaIcon( + FontAwesomeIcons.solidComment, + size: 72, + color: Colors.black12, + ), + ); + } + if (readChatRoom == null) { + return const UnavailablePage('チャットルームの情報の取得に失敗しました。'); + } + return Stack( + children: [ + Column( children: [ - Column( - children: [ - Expanded( - child: ListView.builder( - itemCount: state.readChatMessages.length, - reverse: true, - controller: _scrollController, - itemBuilder: (context, index) { - final readChatMessage = - state.readChatMessages[index]; - return _ChatMessageItem( - readChatRoom: readChatRoom, - readChatMessage: readChatMessage, - isMyMessage: readChatMessage.senderId == userId, - chatRoomId: widget.chatRoomId, - ); - }, - ), - ), - _MessageTextField( - chatRoomId: widget.chatRoomId, - userId: userId, - ), - const Gap(24), - ], - ), - Positioned( - child: Align( - alignment: Alignment.topCenter, - child: _DebugIndicator(chatRoomId: widget.chatRoomId), + Expanded( + child: ListView.builder( + itemCount: state.readChatMessages.length, + reverse: true, + controller: _scrollController, + itemBuilder: (context, index) { + final readChatMessage = state.readChatMessages[index]; + return _ChatMessageItem( + readChatRoom: readChatRoom, + readChatMessage: readChatMessage, + isMyMessage: readChatMessage.senderId == userId, + chatRoomId: widget.chatRoomId, + ); + }, ), ), + _MessageTextField( + chatRoomId: widget.chatRoomId, + userId: userId, + ), + const Gap(24), ], ), + Positioned( + child: Align( + alignment: Alignment.topCenter, + child: _DebugIndicator(chatRoomId: widget.chatRoomId), + ), + ), + ], + ); + }, ), ), );