2023-02-02 21:39:25 +09:00
import ' dart:async ' ;
2022-08-04 17:24:02 +08:00
import ' package:dash_chat_2/dash_chat_2.dart ' ;
2022-08-11 10:19:12 +08:00
import ' package:draggable_float_widget/draggable_float_widget.dart ' ;
2022-02-28 21:26:44 +08:00
import ' package:flutter/material.dart ' ;
2022-08-08 17:53:51 +08:00
import ' package:flutter_hbb/models/platform_model.dart ' ;
2023-02-06 09:54:21 +09:00
import ' package:get/get_rx/src/rx_types/rx_types.dart ' ;
2023-02-06 20:10:39 +08:00
import ' package:get/get.dart ' ;
2022-08-22 20:12:58 +08:00
import ' package:window_manager/window_manager.dart ' ;
2022-03-03 14:58:57 +08:00
2022-11-08 11:07:20 +08:00
import ' ../consts.dart ' ;
2022-08-11 10:19:12 +08:00
import ' ../common.dart ' ;
2022-09-13 09:03:34 +08:00
import ' ../common/widgets/overlay.dart ' ;
2022-03-03 14:58:57 +08:00
import ' model.dart ' ;
2022-02-28 21:26:44 +08:00
2022-05-16 00:01:27 +08:00
class MessageBody {
ChatUser chatUser ;
List < ChatMessage > chatMessages ;
MessageBody ( this . chatUser , this . chatMessages ) ;
2022-08-04 17:24:02 +08:00
void insert ( ChatMessage cm ) {
2022-10-26 23:50:36 +09:00
chatMessages . insert ( 0 , cm ) ;
2022-05-16 00:01:27 +08:00
}
void clear ( ) {
2022-10-26 23:50:36 +09:00
chatMessages . clear ( ) ;
2022-05-16 00:01:27 +08:00
}
}
2022-02-28 21:26:44 +08:00
class ChatModel with ChangeNotifier {
2022-03-22 16:40:23 +08:00
static final clientModeID = - 1 ;
2022-08-11 10:19:12 +08:00
OverlayEntry ? chatIconOverlayEntry ;
OverlayEntry ? chatWindowOverlayEntry ;
2023-02-06 20:10:39 +08:00
2022-11-04 21:43:51 +08:00
bool isConnManager = false ;
2022-08-11 10:19:12 +08:00
2023-02-06 09:54:21 +09:00
RxBool isWindowFocus = true . obs ;
2023-02-08 22:01:15 +09:00
BlockableOverlayState ? _blockableOverlayState ;
2023-02-06 20:10:39 +08:00
final Rx < VoiceCallStatus > _voiceCallStatus = Rx ( VoiceCallStatus . notStarted ) ;
Rx < VoiceCallStatus > get voiceCallStatus = > _voiceCallStatus ;
2023-02-06 09:54:21 +09:00
2022-03-03 14:58:57 +08:00
final ChatUser me = ChatUser (
2022-08-04 17:24:02 +08:00
id: " " ,
2023-03-13 20:43:03 +01:00
firstName: translate ( " Me " ) ,
2022-03-03 14:58:57 +08:00
) ;
2022-11-03 21:58:25 +08:00
late final Map < int , MessageBody > _messages = { } . . [ clientModeID ] =
MessageBody ( me , [ ] ) ;
2022-05-16 00:01:27 +08:00
2022-03-22 16:40:23 +08:00
var _currentID = clientModeID ;
2022-11-04 21:43:51 +08:00
late bool _isShowCMChatPage = false ;
2022-03-22 16:40:23 +08:00
2022-05-16 00:01:27 +08:00
Map < int , MessageBody > get messages = > _messages ;
2022-03-25 16:34:27 +08:00
int get currentID = > _currentID ;
2022-03-03 14:58:57 +08:00
2022-11-04 21:43:51 +08:00
bool get isShowCMChatPage = > _isShowCMChatPage ;
2022-08-22 20:12:58 +08:00
2023-02-08 22:01:15 +09:00
void setOverlayState ( BlockableOverlayState blockableOverlayState ) {
_blockableOverlayState = blockableOverlayState ;
2023-02-07 00:11:48 +09:00
2023-02-08 22:01:15 +09:00
_blockableOverlayState ! . addMiddleBlockedListener ( ( v ) {
2023-02-07 00:11:48 +09:00
if ( ! v ) {
isWindowFocus . value = false ;
if ( isWindowFocus . value ) {
isWindowFocus . toggle ( ) ;
}
}
} ) ;
}
2022-09-27 20:35:02 +08:00
final WeakReference < FFI > parent ;
2022-06-13 21:07:26 +08:00
2022-09-27 20:35:02 +08:00
ChatModel ( this . parent ) ;
2022-06-13 21:07:26 +08:00
2022-10-26 23:50:36 +09:00
FocusNode inputNode = FocusNode ( ) ;
2022-05-16 00:01:27 +08:00
ChatUser get currentUser {
final user = messages [ currentID ] ? . chatUser ;
if ( user = = null ) {
_currentID = clientModeID ;
return me ;
} else {
return user ;
}
}
2022-04-04 14:54:00 +08:00
2022-08-11 10:19:12 +08:00
showChatIconOverlay ( { Offset offset = const Offset ( 200 , 50 ) } ) {
if ( chatIconOverlayEntry ! = null ) {
chatIconOverlayEntry ! . remove ( ) ;
}
// mobile check navigationBar
final bar = navigationBarKey . currentWidget ;
if ( bar ! = null ) {
if ( ( bar as BottomNavigationBar ) . currentIndex = = 1 ) {
return ;
}
}
2023-02-08 22:01:15 +09:00
final overlayState = _blockableOverlayState ? . state ;
2022-08-11 10:19:12 +08:00
if ( overlayState = = null ) return ;
final overlay = OverlayEntry ( builder: ( context ) {
return DraggableFloatWidget (
config: DraggableFloatWidgetBaseConfig (
initPositionYInTop: false ,
initPositionYMarginBorder: 100 ,
borderTopContainTopBar: true ,
) ,
child: FloatingActionButton (
onPressed: ( ) {
if ( chatWindowOverlayEntry = = null ) {
showChatWindowOverlay ( ) ;
} else {
hideChatWindowOverlay ( ) ;
}
} ,
2022-10-25 21:36:01 +09:00
backgroundColor: Theme . of ( context ) . colorScheme . primary ,
2022-08-11 10:19:12 +08:00
child: Icon ( Icons . message ) ) ) ;
} ) ;
overlayState . insert ( overlay ) ;
chatIconOverlayEntry = overlay ;
}
hideChatIconOverlay ( ) {
if ( chatIconOverlayEntry ! = null ) {
chatIconOverlayEntry ! . remove ( ) ;
chatIconOverlayEntry = null ;
}
}
2023-02-07 00:11:48 +09:00
showChatWindowOverlay ( { Offset ? chatInitPos } ) {
2022-08-11 10:19:12 +08:00
if ( chatWindowOverlayEntry ! = null ) return ;
2023-02-07 00:11:48 +09:00
isWindowFocus . value = true ;
2023-02-08 22:01:15 +09:00
_blockableOverlayState ? . setMiddleBlocked ( true ) ;
2023-02-07 00:11:48 +09:00
2023-02-08 22:01:15 +09:00
final overlayState = _blockableOverlayState ? . state ;
2022-08-11 10:19:12 +08:00
if ( overlayState = = null ) return ;
final overlay = OverlayEntry ( builder: ( context ) {
2023-02-06 09:54:21 +09:00
return Listener (
onPointerDown: ( _ ) {
2023-02-07 00:11:48 +09:00
if ( ! isWindowFocus . value ) {
isWindowFocus . value = true ;
2023-02-08 22:01:15 +09:00
_blockableOverlayState ? . setMiddleBlocked ( true ) ;
2023-02-06 09:54:21 +09:00
}
} ,
2023-02-07 00:11:48 +09:00
child: DraggableChatWindow (
position: chatInitPos ? ? Offset ( 20 , 80 ) ,
width: 250 ,
height: 350 ,
chatModel: this ) ) ;
2022-08-11 10:19:12 +08:00
} ) ;
overlayState . insert ( overlay ) ;
chatWindowOverlayEntry = overlay ;
2023-02-02 21:39:25 +09:00
requestChatInputFocus ( ) ;
2022-08-11 10:19:12 +08:00
}
hideChatWindowOverlay ( ) {
if ( chatWindowOverlayEntry ! = null ) {
2023-02-08 22:01:15 +09:00
_blockableOverlayState ? . setMiddleBlocked ( false ) ;
2022-08-11 10:19:12 +08:00
chatWindowOverlayEntry ! . remove ( ) ;
chatWindowOverlayEntry = null ;
return ;
}
}
2022-11-04 21:43:51 +08:00
_isChatOverlayHide ( ) = > ( ( ! isDesktop & & chatIconOverlayEntry = = null ) | |
chatWindowOverlayEntry = = null ) ;
2023-02-07 00:11:48 +09:00
toggleChatOverlay ( { Offset ? chatInitPos } ) {
2022-11-04 21:43:51 +08:00
if ( _isChatOverlayHide ( ) ) {
2022-08-11 10:19:12 +08:00
gFFI . invokeMethod ( " enable_soft_keyboard " , true ) ;
2022-09-08 22:18:02 +08:00
if ( ! isDesktop ) {
showChatIconOverlay ( ) ;
}
2023-02-07 00:11:48 +09:00
showChatWindowOverlay ( chatInitPos: chatInitPos ) ;
2022-08-11 10:19:12 +08:00
} else {
hideChatIconOverlay ( ) ;
hideChatWindowOverlay ( ) ;
}
}
2022-11-04 21:43:51 +08:00
showChatPage ( int id ) async {
if ( isConnManager ) {
if ( ! _isShowCMChatPage ) {
await toggleCMChatPage ( id ) ;
}
} else {
if ( _isChatOverlayHide ( ) ) {
await toggleChatOverlay ( ) ;
}
}
}
2022-08-22 20:12:58 +08:00
toggleCMChatPage ( int id ) async {
if ( gFFI . chatModel . currentID ! = id ) {
gFFI . chatModel . changeCurrentID ( id ) ;
}
2022-11-04 21:43:51 +08:00
if ( _isShowCMChatPage ) {
_isShowCMChatPage = ! _isShowCMChatPage ;
2022-08-22 20:12:58 +08:00
notifyListeners ( ) ;
2022-11-08 11:07:20 +08:00
await windowManager . show ( ) ;
await windowManager . setSizeAlignment (
kConnectionManagerWindowSize , Alignment . topRight ) ;
2022-08-22 20:12:58 +08:00
} else {
2023-02-02 21:39:25 +09:00
requestChatInputFocus ( ) ;
2022-11-08 11:07:20 +08:00
await windowManager . show ( ) ;
2022-11-05 10:35:56 +08:00
await windowManager . setSizeAlignment ( Size ( 600 , 400 ) , Alignment . topRight ) ;
2022-11-04 21:43:51 +08:00
_isShowCMChatPage = ! _isShowCMChatPage ;
2022-08-22 20:12:58 +08:00
notifyListeners ( ) ;
}
}
2022-04-05 00:51:47 +08:00
changeCurrentID ( int id ) {
if ( _messages . containsKey ( id ) ) {
2022-03-25 16:34:27 +08:00
_currentID = id ;
notifyListeners ( ) ;
2022-04-04 14:54:00 +08:00
} else {
2022-09-27 20:35:02 +08:00
final client = parent . target ? . serverModel . clients
2022-08-22 20:12:58 +08:00
. firstWhere ( ( client ) = > client . id = = id ) ;
2022-05-16 00:01:27 +08:00
if ( client = = null ) {
2022-04-05 00:51:47 +08:00
return debugPrint (
" Failed to changeCurrentID,remote user doesn't exist " ) ;
2022-04-04 14:54:00 +08:00
}
2022-05-16 00:01:27 +08:00
final chatUser = ChatUser (
2022-08-04 17:24:02 +08:00
id: client . peerId ,
firstName: client . name ,
2022-05-16 00:01:27 +08:00
) ;
_messages [ id ] = MessageBody ( chatUser , [ ] ) ;
2022-04-04 14:54:00 +08:00
_currentID = id ;
2022-05-16 00:01:27 +08:00
notifyListeners ( ) ;
2022-03-25 16:34:27 +08:00
}
}
2022-03-22 16:40:23 +08:00
2022-08-08 17:53:51 +08:00
receive ( int id , String text ) async {
2022-10-26 23:50:36 +09:00
final session = parent . target ;
if ( session = = null ) {
debugPrint ( " Failed to receive msg, session state is null " ) ;
return ;
}
2022-03-03 14:58:57 +08:00
if ( text . isEmpty ) return ;
2022-08-22 20:12:58 +08:00
// mobile: first message show overlay icon
2022-11-04 21:43:51 +08:00
if ( ! isDesktop & & chatIconOverlayEntry = = null ) {
showChatIconOverlay ( ) ;
2022-08-22 20:12:58 +08:00
}
2022-11-04 21:43:51 +08:00
// show chat page
await showChatPage ( id ) ;
2022-08-22 20:12:58 +08:00
2022-10-26 23:50:36 +09:00
int toId = currentID ;
late final ChatUser chatUser ;
2022-04-05 00:51:47 +08:00
if ( id = = clientModeID ) {
2022-03-25 16:34:27 +08:00
chatUser = ChatUser (
2022-10-26 23:50:36 +09:00
firstName: session . ffiModel . pi . username ,
id: session . id ,
2022-03-25 16:34:27 +08:00
) ;
2022-10-26 23:50:36 +09:00
toId = id ;
2022-04-05 00:51:47 +08:00
} else {
2022-10-26 23:50:36 +09:00
final client =
session . serverModel . clients . firstWhere ( ( client ) = > client . id = = id ) ;
2022-09-01 21:18:53 +08:00
if ( isDesktop ) {
window_on_top ( null ) ;
2022-10-26 23:50:36 +09:00
// disable auto jumpTo other tab when hasFocus, and mark unread message
final currentSelectedTab =
session . serverModel . tabController . state . value . selectedTabInfo ;
if ( currentSelectedTab . key ! = id . toString ( ) & & inputNode . hasFocus ) {
client . hasUnreadChatMessage . value = true ;
} else {
parent . target ? . serverModel . jumpTo ( id ) ;
toId = id ;
2022-09-01 21:18:53 +08:00
}
2022-10-26 23:50:36 +09:00
} else {
toId = id ;
2022-09-01 21:18:53 +08:00
}
2022-08-04 17:24:02 +08:00
chatUser = ChatUser ( id: client . peerId , firstName: client . name ) ;
2022-03-25 16:34:27 +08:00
}
2022-05-16 00:01:27 +08:00
2022-04-05 00:51:47 +08:00
if ( ! _messages . containsKey ( id ) ) {
2022-05-16 00:01:27 +08:00
_messages [ id ] = MessageBody ( chatUser , [ ] ) ;
2022-03-22 16:40:23 +08:00
}
2022-08-04 17:24:02 +08:00
_messages [ id ] ! . insert (
ChatMessage ( text: text , user: chatUser , createdAt: DateTime . now ( ) ) ) ;
2022-10-26 23:50:36 +09:00
_currentID = toId ;
2022-03-03 14:58:57 +08:00
notifyListeners ( ) ;
}
2022-03-22 16:40:23 +08:00
send ( ChatMessage message ) {
2022-08-04 17:24:02 +08:00
if ( message . text . isNotEmpty ) {
_messages [ _currentID ] ? . insert ( message ) ;
2022-03-22 16:40:23 +08:00
if ( _currentID = = clientModeID ) {
2022-09-27 20:35:02 +08:00
if ( parent . target ! = null ) {
bind . sessionSendChat ( id: parent . target ! . id , text: message . text ) ;
2022-08-08 22:27:27 +08:00
}
2022-03-22 16:40:23 +08:00
} else {
2022-08-17 17:23:55 +08:00
bind . cmSendChat ( connId: _currentID , msg: message . text ) ;
2022-03-22 16:40:23 +08:00
}
2022-03-03 14:58:57 +08:00
}
notifyListeners ( ) ;
}
2022-02-28 21:26:44 +08:00
2022-03-25 16:34:27 +08:00
close ( ) {
2022-03-03 14:58:57 +08:00
hideChatIconOverlay ( ) ;
hideChatWindowOverlay ( ) ;
notifyListeners ( ) ;
}
2022-05-16 00:01:27 +08:00
resetClientMode ( ) {
_messages [ clientModeID ] ? . clear ( ) ;
}
2023-02-02 21:39:25 +09:00
void requestChatInputFocus ( ) {
Timer ( Duration ( milliseconds: 100 ) , ( ) {
if ( inputNode . hasListeners & & inputNode . canRequestFocus ) {
inputNode . requestFocus ( ) ;
}
} ) ;
}
2023-02-08 22:29:51 +09:00
2023-02-06 20:10:39 +08:00
void onVoiceCallWaiting ( ) {
_voiceCallStatus . value = VoiceCallStatus . waitingForResponse ;
}
void onVoiceCallStarted ( ) {
_voiceCallStatus . value = VoiceCallStatus . connected ;
}
void onVoiceCallClosed ( String reason ) {
_voiceCallStatus . value = VoiceCallStatus . notStarted ;
}
void onVoiceCallIncoming ( ) {
if ( isConnManager ) {
_voiceCallStatus . value = VoiceCallStatus . incoming ;
}
}
2023-02-07 16:11:55 +08:00
void closeVoiceCall ( String id ) {
bind . sessionCloseVoiceCall ( id: id ) ;
}
2022-03-22 16:40:23 +08:00
}
2023-02-06 20:10:39 +08:00
enum VoiceCallStatus {
notStarted ,
waitingForResponse ,
connected ,
// Connection manager only.
incoming
2022-03-22 16:40:23 +08:00
}