master
jack ning 3 years ago
parent 4adda0eceb
commit 8c7df6205d

BIN
.DS_Store vendored

Binary file not shown.

@ -44,6 +44,10 @@ class MyApp extends StatefulWidget {
} }
class _MyAppState extends State<MyApp> with WidgetsBindingObserver { class _MyAppState extends State<MyApp> with WidgetsBindingObserver {
// // appkey->->Flutter->->appkey
// String _appKey = '81f427ea-4467-4c7c-b0cd-5c0e4b51456f';
// // subDomain->->->
// String _subDomain = "vip";
// //
String _title = '萝卜丝客服Demo(连接中...)'; String _title = '萝卜丝客服Demo(连接中...)';
AudioCache audioCache = AudioCache(); AudioCache audioCache = AudioCache();
@ -74,7 +78,7 @@ class _MyAppState extends State<MyApp> with WidgetsBindingObserver {
// //
Navigator.of(context) Navigator.of(context)
.push(MaterialPageRoute(builder: (context) { .push(MaterialPageRoute(builder: (context) {
return ChatTypePage(); return const ChatTypePage();
})); }));
}, },
), ),
@ -85,7 +89,7 @@ class _MyAppState extends State<MyApp> with WidgetsBindingObserver {
// anonymousLogin // anonymousLogin
Navigator.of(context) Navigator.of(context)
.push(MaterialPageRoute(builder: (context) { .push(MaterialPageRoute(builder: (context) {
return UserInfoPage(); return const UserInfoPage();
})); }));
}, },
), ),
@ -95,7 +99,7 @@ class _MyAppState extends State<MyApp> with WidgetsBindingObserver {
onTap: () { onTap: () {
Navigator.of(context) Navigator.of(context)
.push(MaterialPageRoute(builder: (context) { .push(MaterialPageRoute(builder: (context) {
return OnlineStatusPage(); return const OnlineStatusPage();
})); }));
}, },
), ),
@ -105,7 +109,7 @@ class _MyAppState extends State<MyApp> with WidgetsBindingObserver {
onTap: () { onTap: () {
Navigator.of(context) Navigator.of(context)
.push(MaterialPageRoute(builder: (context) { .push(MaterialPageRoute(builder: (context) {
return HistoryThreadPage(); return const HistoryThreadPage();
})); }));
}, },
), ),
@ -131,10 +135,27 @@ class _MyAppState extends State<MyApp> with WidgetsBindingObserver {
onTap: () { onTap: () {
Navigator.of(context) Navigator.of(context)
.push(MaterialPageRoute(builder: (context) { .push(MaterialPageRoute(builder: (context) {
return SettingPage(); return const SettingPage();
})); }));
}, },
), ),
// ListTile(
// title: Text('退出登录'),
// onTap: () {
// BytedeskKefu.logout();
// },
// ),
// ListTile(
// title: Text('重新初始化'),
// onTap: () {
// BytedeskKefu.initWithUsernameAndNicknameAndAvatar(
// 'myflutterusername2',
// '我是帅哥',
// 'https://bytedesk.oss-cn-shenzhen.aliyuncs.com/avatars/boy.png',
// _appKey,
// _subDomain);
// },
// ),
const ListTile( const ListTile(
title: Text('技术支持: QQ-3群: 825257535'), title: Text('技术支持: QQ-3群: 825257535'),
) )

@ -15,10 +15,10 @@ class ChatTypePage extends StatefulWidget {
class _ChatTypePageState extends State<ChatTypePage> { class _ChatTypePageState extends State<ChatTypePage> {
// ->- IDwId, wid // ->- IDwId, wid
// 访 // 访
String _workGroupWid = "201807171659201"; // final String _workGroupWid = "201807171659201"; //
String _workGroupWidRobot = "201809061716221"; // , final String _workGroupWidRobot = "201809061716221"; // ,
// //
String _agentUid = "201808221551193"; final String _agentUid = "201808221551193";
// //
String _unreadMessageCount = "0"; String _unreadMessageCount = "0";
// //
@ -53,6 +53,7 @@ class _ChatTypePageState extends State<ChatTypePage> {
), ),
ListTile( ListTile(
title: const Text('技能组客服'), title: const Text('技能组客服'),
subtitle: const Text('默认人工'),
trailing: const Icon(Icons.keyboard_arrow_right), trailing: const Icon(Icons.keyboard_arrow_right),
onTap: () { onTap: () {
BytedeskKefu.startWorkGroupChat( BytedeskKefu.startWorkGroupChat(
@ -61,14 +62,25 @@ class _ChatTypePageState extends State<ChatTypePage> {
), ),
ListTile( ListTile(
title: const Text('技能组客服-机器人'), title: const Text('技能组客服-机器人'),
subtitle: const Text('默认热门问题'),
trailing: const Icon(Icons.keyboard_arrow_right), trailing: const Icon(Icons.keyboard_arrow_right),
onTap: () { onTap: () {
BytedeskKefu.startWorkGroupChat( BytedeskKefu.startWorkGroupChat(
context, _workGroupWidRobot, "技能组客服-默认机器人"); context, _workGroupWidRobot, "技能组客服-默认机器人问题");
},
),
ListTile(
title: const Text('技能组客服-机器人2'),
subtitle: const Text('默认问题分类'),
trailing: const Icon(Icons.keyboard_arrow_right),
onTap: () {
BytedeskKefu.startWorkGroupChatV2Robot(
context, _workGroupWidRobot, "技能组客服-默认机器人分类");
}, },
), ),
ListTile( ListTile(
title: const Text('技能组客服-电商'), title: const Text('技能组客服-电商'),
subtitle: const Text('携带商品参数'),
trailing: const Icon(Icons.keyboard_arrow_right), trailing: const Icon(Icons.keyboard_arrow_right),
onTap: () { onTap: () {
// type/title/content/price/url/imageUrl/id/categoryCode // type/title/content/price/url/imageUrl/id/categoryCode
@ -92,6 +104,7 @@ class _ChatTypePageState extends State<ChatTypePage> {
), ),
ListTile( ListTile(
title: const Text('技能组客服-电商-回调'), title: const Text('技能组客服-电商-回调'),
subtitle: const Text('携带商品参数,点击商品支持回调'),
trailing: const Icon(Icons.keyboard_arrow_right), trailing: const Icon(Icons.keyboard_arrow_right),
onTap: () { onTap: () {
// type/title/content/price/url/imageUrl/id/categoryCode // type/title/content/price/url/imageUrl/id/categoryCode
@ -122,6 +135,7 @@ class _ChatTypePageState extends State<ChatTypePage> {
), ),
ListTile( ListTile(
title: const Text('技能组客服-附言'), title: const Text('技能组客服-附言'),
subtitle: const Text('自动发送一条消息'),
trailing: const Icon(Icons.keyboard_arrow_right), trailing: const Icon(Icons.keyboard_arrow_right),
onTap: () { onTap: () {
BytedeskKefu.startWorkGroupChatPostscript( BytedeskKefu.startWorkGroupChatPostscript(
@ -133,6 +147,7 @@ class _ChatTypePageState extends State<ChatTypePage> {
), ),
ListTile( ListTile(
title: const Text('指定一对一客服'), title: const Text('指定一对一客服'),
subtitle: const Text('默认人工'),
trailing: const Icon(Icons.keyboard_arrow_right), trailing: const Icon(Icons.keyboard_arrow_right),
onTap: () { onTap: () {
BytedeskKefu.startAppointedChat(context, _agentUid, "指定一对一客服"); BytedeskKefu.startAppointedChat(context, _agentUid, "指定一对一客服");
@ -140,6 +155,7 @@ class _ChatTypePageState extends State<ChatTypePage> {
), ),
ListTile( ListTile(
title: const Text('指定一对一客服-电商'), title: const Text('指定一对一客服-电商'),
subtitle: const Text('携带商品参数'),
trailing: const Icon(Icons.keyboard_arrow_right), trailing: const Icon(Icons.keyboard_arrow_right),
onTap: () { onTap: () {
// type/title/content/price/url/imageUrl/id/categoryCode // type/title/content/price/url/imageUrl/id/categoryCode
@ -162,6 +178,7 @@ class _ChatTypePageState extends State<ChatTypePage> {
), ),
ListTile( ListTile(
title: const Text('指定一对一客服-电商-回调'), title: const Text('指定一对一客服-电商-回调'),
subtitle: const Text('携带商品参数,点击商品支持回调'),
trailing: const Icon(Icons.keyboard_arrow_right), trailing: const Icon(Icons.keyboard_arrow_right),
onTap: () { onTap: () {
// type/title/content/price/url/imageUrl/id/categoryCode // type/title/content/price/url/imageUrl/id/categoryCode
@ -191,6 +208,7 @@ class _ChatTypePageState extends State<ChatTypePage> {
), ),
ListTile( ListTile(
title: const Text('指定一对一客服-附言'), title: const Text('指定一对一客服-附言'),
subtitle: const Text('自动发送一条消息'),
trailing: const Icon(Icons.keyboard_arrow_right), trailing: const Icon(Icons.keyboard_arrow_right),
onTap: () { onTap: () {
BytedeskKefu.startAppointedChatPostscript( BytedeskKefu.startAppointedChatPostscript(
@ -204,7 +222,7 @@ class _ChatTypePageState extends State<ChatTypePage> {
title: const Text('H5网页会话'), title: const Text('H5网页会话'),
trailing: const Icon(Icons.keyboard_arrow_right), trailing: const Icon(Icons.keyboard_arrow_right),
onTap: () { onTap: () {
// print('h5 chat'); print('h5 chat');
// : ->->()-> URL // : ->->()-> URL
String url = String url =
"https://h2.kefux.cn/chat/h5/index.html?sub=vip&uid=201808221551193&wid=201807171659201&type=workGroup&aid=&hidenav=1&ph=ph"; "https://h2.kefux.cn/chat/h5/index.html?sub=vip&uid=201808221551193&wid=201807171659201&type=workGroup&aid=&hidenav=1&ph=ph";

@ -46,7 +46,7 @@ dependencies:
# 请在ios/Podfile中添加use_frameworks! # 请在ios/Podfile中添加use_frameworks!
vibration: ^1.7.3 vibration: ^1.7.3
# 在线客服 https://pub.dev/packages/bytedesk_kefu # 在线客服 https://pub.dev/packages/bytedesk_kefu
bytedesk_kefu: ^1.2.5 bytedesk_kefu: ^1.2.8
# The following adds the Cupertino Icons font to your application. # The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons. # Use with the CupertinoIcons class for iOS style icons.

@ -1,5 +1,13 @@
# Upgrade Log # Upgrade Log
## 1.2.8
* optimize user experience
## 1.2.7
* optimize user experience
## 1.2.6 ## 1.2.6
* optimize user experience * optimize user experience

@ -65,7 +65,7 @@ BytedeskKefu.startWorkGroupChat(context, workGroupWid, "title");
```dart ```dart
bytedesk_kefu: bytedesk_kefu:
path: ./vendors/bytedesk path: ./vendors/bytedesk_kefu
``` ```
### Support ### Support

@ -43,10 +43,10 @@ class MyApp extends StatefulWidget {
} }
class _MyAppState extends State<MyApp> with WidgetsBindingObserver { class _MyAppState extends State<MyApp> with WidgetsBindingObserver {
// appkey->->Flutter->->appkey // // appkey->->Flutter->->appkey
String _appKey = '81f427ea-4467-4c7c-b0cd-5c0e4b51456f'; // String _appKey = '81f427ea-4467-4c7c-b0cd-5c0e4b51456f';
// subDomain->->-> // // subDomain->->->
String _subDomain = "vip"; // String _subDomain = "vip";
// //
String _title = '萝卜丝客服Demo(连接中...)'; String _title = '萝卜丝客服Demo(连接中...)';
// AudioCache audioCache = AudioCache(); // AudioCache audioCache = AudioCache();

@ -53,6 +53,7 @@ class _ChatTypePageState extends State<ChatTypePage> {
), ),
ListTile( ListTile(
title: Text('技能组客服'), title: Text('技能组客服'),
subtitle: Text('默认人工'),
trailing: Icon(Icons.keyboard_arrow_right), trailing: Icon(Icons.keyboard_arrow_right),
onTap: () { onTap: () {
BytedeskKefu.startWorkGroupChat( BytedeskKefu.startWorkGroupChat(
@ -61,14 +62,25 @@ class _ChatTypePageState extends State<ChatTypePage> {
), ),
ListTile( ListTile(
title: Text('技能组客服-机器人'), title: Text('技能组客服-机器人'),
subtitle: Text('默认热门问题'),
trailing: Icon(Icons.keyboard_arrow_right), trailing: Icon(Icons.keyboard_arrow_right),
onTap: () { onTap: () {
BytedeskKefu.startWorkGroupChat( BytedeskKefu.startWorkGroupChat(
context, _workGroupWidRobot, "技能组客服-默认机器人"); context, _workGroupWidRobot, "技能组客服-默认机器人问题");
},
),
ListTile(
title: Text('技能组客服-机器人2'),
subtitle: Text('默认问题分类'),
trailing: Icon(Icons.keyboard_arrow_right),
onTap: () {
BytedeskKefu.startWorkGroupChatV2Robot(
context, _workGroupWidRobot, "技能组客服-默认机器人分类");
}, },
), ),
ListTile( ListTile(
title: Text('技能组客服-电商'), title: Text('技能组客服-电商'),
subtitle: Text('携带商品参数'),
trailing: Icon(Icons.keyboard_arrow_right), trailing: Icon(Icons.keyboard_arrow_right),
onTap: () { onTap: () {
// type/title/content/price/url/imageUrl/id/categoryCode // type/title/content/price/url/imageUrl/id/categoryCode
@ -92,6 +104,7 @@ class _ChatTypePageState extends State<ChatTypePage> {
), ),
ListTile( ListTile(
title: Text('技能组客服-电商-回调'), title: Text('技能组客服-电商-回调'),
subtitle: Text('携带商品参数,点击商品支持回调'),
trailing: Icon(Icons.keyboard_arrow_right), trailing: Icon(Icons.keyboard_arrow_right),
onTap: () { onTap: () {
// type/title/content/price/url/imageUrl/id/categoryCode // type/title/content/price/url/imageUrl/id/categoryCode
@ -122,6 +135,7 @@ class _ChatTypePageState extends State<ChatTypePage> {
), ),
ListTile( ListTile(
title: Text('技能组客服-附言'), title: Text('技能组客服-附言'),
subtitle: Text('自动发送一条消息'),
trailing: Icon(Icons.keyboard_arrow_right), trailing: Icon(Icons.keyboard_arrow_right),
onTap: () { onTap: () {
BytedeskKefu.startWorkGroupChatPostscript( BytedeskKefu.startWorkGroupChatPostscript(
@ -133,6 +147,7 @@ class _ChatTypePageState extends State<ChatTypePage> {
), ),
ListTile( ListTile(
title: Text('指定一对一客服'), title: Text('指定一对一客服'),
subtitle: Text('默认人工'),
trailing: Icon(Icons.keyboard_arrow_right), trailing: Icon(Icons.keyboard_arrow_right),
onTap: () { onTap: () {
BytedeskKefu.startAppointedChat(context, _agentUid, "指定一对一客服"); BytedeskKefu.startAppointedChat(context, _agentUid, "指定一对一客服");
@ -140,6 +155,7 @@ class _ChatTypePageState extends State<ChatTypePage> {
), ),
ListTile( ListTile(
title: Text('指定一对一客服-电商'), title: Text('指定一对一客服-电商'),
subtitle: Text('携带商品参数'),
trailing: Icon(Icons.keyboard_arrow_right), trailing: Icon(Icons.keyboard_arrow_right),
onTap: () { onTap: () {
// type/title/content/price/url/imageUrl/id/categoryCode // type/title/content/price/url/imageUrl/id/categoryCode
@ -162,6 +178,7 @@ class _ChatTypePageState extends State<ChatTypePage> {
), ),
ListTile( ListTile(
title: Text('指定一对一客服-电商-回调'), title: Text('指定一对一客服-电商-回调'),
subtitle: Text('携带商品参数,点击商品支持回调'),
trailing: Icon(Icons.keyboard_arrow_right), trailing: Icon(Icons.keyboard_arrow_right),
onTap: () { onTap: () {
// type/title/content/price/url/imageUrl/id/categoryCode // type/title/content/price/url/imageUrl/id/categoryCode
@ -191,6 +208,7 @@ class _ChatTypePageState extends State<ChatTypePage> {
), ),
ListTile( ListTile(
title: Text('指定一对一客服-附言'), title: Text('指定一对一客服-附言'),
subtitle: Text('自动发送一条消息'),
trailing: Icon(Icons.keyboard_arrow_right), trailing: Icon(Icons.keyboard_arrow_right),
onTap: () { onTap: () {
BytedeskKefu.startAppointedChatPostscript( BytedeskKefu.startAppointedChatPostscript(

@ -3,10 +3,7 @@ import 'package:bloc/bloc.dart';
import './bloc.dart'; import './bloc.dart';
class GroupBloc extends Bloc<GroupEvent, GroupState> { class GroupBloc extends Bloc<GroupEvent, GroupState> {
GroupBloc() : super(InitialGroupState());
GroupBloc() : super(InitialGroupState()) {
}
// @override // @override
// Stream<GroupState> mapEventToState( // Stream<GroupState> mapEventToState(

@ -1,4 +1,4 @@
import 'dart:async'; // import 'dart:async';
import 'package:bytedesk_kefu/blocs/help_bloc/bloc.dart'; import 'package:bytedesk_kefu/blocs/help_bloc/bloc.dart';
import 'package:bytedesk_kefu/model/helpArticle.dart'; import 'package:bytedesk_kefu/model/helpArticle.dart';
import 'package:bytedesk_kefu/model/helpCategory.dart'; import 'package:bytedesk_kefu/model/helpCategory.dart';

@ -1,4 +1,4 @@
import 'dart:async'; // import 'dart:async';
import 'package:bytedesk_kefu/blocs/leavemsg_bloc/bloc.dart'; import 'package:bytedesk_kefu/blocs/leavemsg_bloc/bloc.dart';
import 'package:bloc/bloc.dart'; import 'package:bloc/bloc.dart';
import 'package:bytedesk_kefu/repositories/leavemsg_repository.dart'; import 'package:bytedesk_kefu/repositories/leavemsg_repository.dart';

@ -2,6 +2,7 @@
import 'package:bytedesk_kefu/model/jsonResult.dart'; import 'package:bytedesk_kefu/model/jsonResult.dart';
import 'package:bytedesk_kefu/model/message.dart'; import 'package:bytedesk_kefu/model/message.dart';
import 'package:bytedesk_kefu/model/requestAnswer.dart'; import 'package:bytedesk_kefu/model/requestAnswer.dart';
import 'package:bytedesk_kefu/model/requestCategory.dart';
import 'package:bytedesk_kefu/model/uploadJsonResult.dart'; import 'package:bytedesk_kefu/model/uploadJsonResult.dart';
import 'package:bytedesk_kefu/repositories/message_repository.dart'; import 'package:bytedesk_kefu/repositories/message_repository.dart';
import 'package:bloc/bloc.dart'; import 'package:bloc/bloc.dart';
@ -21,6 +22,7 @@ class MessageBloc extends Bloc<MessageEvent, MessageState> {
on<LoadChannelMessageEvent>(_mapLoadChannelMessageToState); on<LoadChannelMessageEvent>(_mapLoadChannelMessageToState);
on<QueryAnswerEvent>(_mapQueryAnswerToState); on<QueryAnswerEvent>(_mapQueryAnswerToState);
on<QueryCategoryEvent>(_mapQueryCategoryToState);
on<MessageAnswerEvent>(_mapMessageAnswerToState); on<MessageAnswerEvent>(_mapMessageAnswerToState);
on<RateAnswerEvent>(_mapRateAnswerToState); on<RateAnswerEvent>(_mapRateAnswerToState);
} }
@ -134,7 +136,7 @@ class MessageBloc extends Bloc<MessageEvent, MessageState> {
emit(MessageLoading()); emit(MessageLoading());
try { try {
final RequestAnswerResult requestAnswerResult = final RequestAnswerResult requestAnswerResult =
await messageRepository.queryAnswer(event.tid, event.aid); await messageRepository.queryAnswer(event.tid, event.aid, event.mid);
emit(QueryAnswerSuccess( emit(QueryAnswerSuccess(
query: requestAnswerResult.query, answer: requestAnswerResult.anwser)); query: requestAnswerResult.query, answer: requestAnswerResult.anwser));
} catch (error) { } catch (error) {
@ -143,11 +145,26 @@ class MessageBloc extends Bloc<MessageEvent, MessageState> {
} }
} }
void _mapQueryCategoryToState(
QueryCategoryEvent event, Emitter<MessageState> emit) async {
emit(MessageLoading());
try {
final RequestCategoryResult requestAnswerResult =
await messageRepository.queryCategory(event.tid, event.cid);
emit(QueryCategorySuccess(
query: requestAnswerResult.query,
answer: requestAnswerResult.anwser));
} catch (error) {
print(error);
emit(UpLoadImageError());
}
}
void _mapMessageAnswerToState(MessageAnswerEvent event, Emitter<MessageState> emit) async { void _mapMessageAnswerToState(MessageAnswerEvent event, Emitter<MessageState> emit) async {
emit(MessageLoading()); emit(MessageLoading());
try { try {
final RequestAnswerResult requestAnswerResult = await messageRepository final RequestAnswerResult requestAnswerResult = await messageRepository
.messageAnswer(event.type, event.wid, event.aid, event.content); .messageAnswer(event.wid, event.content);
emit(MessageAnswerSuccess( emit(MessageAnswerSuccess(
query: requestAnswerResult.query, answer: requestAnswerResult.anwser)); query: requestAnswerResult.query, answer: requestAnswerResult.anwser));
} catch (error) { } catch (error) {

@ -67,20 +67,30 @@ class LoadChannelMessageEvent extends MessageEvent {
class QueryAnswerEvent extends MessageEvent { class QueryAnswerEvent extends MessageEvent {
final String? tid; final String? tid;
final String? aid; final String? aid;
final String? mid;
QueryAnswerEvent({@required this.tid, @required this.aid, @required this.mid}) : super();
}
class QueryCategoryEvent extends MessageEvent {
final String? tid;
final String? cid;
QueryAnswerEvent({@required this.tid, @required this.aid}) : super(); QueryCategoryEvent({@required this.tid, @required this.cid})
: super();
} }
class MessageAnswerEvent extends MessageEvent { class MessageAnswerEvent extends MessageEvent {
final String? type; // final String? type;
final String? wid; final String? wid;
final String? aid; // final String? aid;
final String? content; final String? content;
MessageAnswerEvent( MessageAnswerEvent(
{@required this.type, {
// @required this.type,
@required this.wid, @required this.wid,
@required this.aid, // @required this.aid,
@required this.content}) @required this.content})
: super(); : super();
} }

@ -126,6 +126,13 @@ class QueryAnswerSuccess extends MessageState {
QueryAnswerSuccess({@required this.query, @required this.answer}) : super(); QueryAnswerSuccess({@required this.query, @required this.answer}) : super();
} }
class QueryCategorySuccess extends MessageState {
final Message? query;
final Message? answer;
QueryCategorySuccess({@required this.query, @required this.answer}) : super();
}
class MessageAnswerSuccess extends MessageState { class MessageAnswerSuccess extends MessageState {
final Message? query; final Message? query;
final Message? answer; final Message? answer;

@ -1,4 +1,4 @@
import 'dart:async'; // import 'dart:async';
import 'package:bytedesk_kefu/model/jsonResult.dart'; import 'package:bytedesk_kefu/model/jsonResult.dart';
import 'package:bytedesk_kefu/model/user.dart'; import 'package:bytedesk_kefu/model/user.dart';
import 'package:bytedesk_kefu/repositories/user_repository.dart'; import 'package:bytedesk_kefu/repositories/user_repository.dart';

@ -17,8 +17,8 @@ class ThreadBloc extends Bloc<ThreadEvent, ThreadState> {
on<RefreshHistoryThreadEvent>(_mapRefreshHistoryThreadToState); on<RefreshHistoryThreadEvent>(_mapRefreshHistoryThreadToState);
on<RefreshVisitorThreadEvent>(_mapRefreshVisitorThreadToState); on<RefreshVisitorThreadEvent>(_mapRefreshVisitorThreadToState);
on<RefreshVisitorThreadAllEvent>(_mapRefreshVisitorThreadAllToState); on<RefreshVisitorThreadAllEvent>(_mapRefreshVisitorThreadAllToState);
on<RequestThreadEvent>(_mapRequestThreadToState);
on<RequestThreadEvent>(_mapRequestThreadToState);
on<RequestAgentEvent>(_mapRequestAgentToState); on<RequestAgentEvent>(_mapRequestAgentToState);
on<RequestContactThreadEvent>(_mapRequestContactThreadToState); on<RequestContactThreadEvent>(_mapRequestContactThreadToState);
on<RequestGroupThreadEvent>(_mapRequestGroupThreadToState); on<RequestGroupThreadEvent>(_mapRequestGroupThreadToState);
@ -85,11 +85,10 @@ class ThreadBloc extends Bloc<ThreadEvent, ThreadState> {
void _mapRequestThreadToState( void _mapRequestThreadToState(
RequestThreadEvent event, Emitter<ThreadState> emit) async { RequestThreadEvent event, Emitter<ThreadState> emit) async {
print('RequestThreadEvent');
emit(RequestThreading()); emit(RequestThreading());
try { try {
final RequestThreadResult thread = await threadRepository.requestThread( final RequestThreadResult thread = await threadRepository.requestThread(
event.wid, event.type, event.aid); event.wid, event.type, event.aid, event.isV2Robot);
emit(RequestThreadSuccess(thread)); emit(RequestThreadSuccess(thread));
} catch (error) { } catch (error) {
print(error); print(error);

@ -55,9 +55,10 @@ class RequestThreadEvent extends ThreadEvent {
final String? wid; final String? wid;
final String? type; final String? type;
final String? aid; final String? aid;
final bool? isV2Robot;
RequestThreadEvent( RequestThreadEvent(
{@required this.wid, @required this.type, @required this.aid}) {@required this.wid, @required this.type, @required this.aid, @required this.isV2Robot})
: super(); : super();
} }

@ -173,14 +173,20 @@ class BytedeskKefu {
static void startWorkGroupChat( static void startWorkGroupChat(
BuildContext context, String wid, String title) { BuildContext context, String wid, String title) {
startChatDefault( startChatDefault(
context, wid, BytedeskConstants.CHAT_TYPE_WORKGROUP, title); context, wid, BytedeskConstants.CHAT_TYPE_WORKGROUP, title, false);
}
static void startWorkGroupChatV2Robot(
BuildContext context, String wid, String title) {
startChatDefault(
context, wid, BytedeskConstants.CHAT_TYPE_WORKGROUP, title, true);
} }
// //
static void startWorkGroupChatPostscript( static void startWorkGroupChatPostscript(
BuildContext context, String wid, String title, String postScript) { BuildContext context, String wid, String title, String postScript) {
startChat(context, wid, BytedeskConstants.CHAT_TYPE_WORKGROUP, title, '', startChat(context, wid, BytedeskConstants.CHAT_TYPE_WORKGROUP, title, '',
postScript, null); postScript, false, null);
} }
// //
@ -201,14 +207,14 @@ class BytedeskKefu {
static void startAppointedChat( static void startAppointedChat(
BuildContext context, String uid, String title) { BuildContext context, String uid, String title) {
startChatDefault( startChatDefault(
context, uid, BytedeskConstants.CHAT_TYPE_APPOINTED, title); context, uid, BytedeskConstants.CHAT_TYPE_APPOINTED, title, false);
} }
// //
static void startAppointedChatPostscript( static void startAppointedChatPostscript(
BuildContext context, String uid, String title, String postScript) { BuildContext context, String uid, String title, String postScript) {
startChat(context, uid, BytedeskConstants.CHAT_TYPE_APPOINTED, title, '', startChat(context, uid, BytedeskConstants.CHAT_TYPE_APPOINTED, title, '',
postScript, null); postScript, false, null);
} }
// //
@ -226,20 +232,20 @@ class BytedeskKefu {
// //
static void startChatDefault( static void startChatDefault(
BuildContext context, String uuid, String type, String title) { BuildContext context, String uuid, String type, String title, bool isV2Robot) {
startChat(context, uuid, type, title, '', '', null); startChat(context, uuid, type, title, '', '', isV2Robot, null);
} }
// -() // -()
static void startChatShop(BuildContext context, String uuid, String type, static void startChatShop(BuildContext context, String uuid, String type,
String title, String commodity, ValueSetter<String>? customCallback) { String title, String commodity, ValueSetter<String>? customCallback) {
startChat(context, uuid, type, title, commodity, '', customCallback); startChat(context, uuid, type, title, commodity, '', false, customCallback);
} }
// -() // -()
static void startChatPostscript(BuildContext context, String uuid, static void startChatPostscript(BuildContext context, String uuid,
String type, String title, String postScript) { String type, String title, String postScript) {
startChat(context, uuid, type, title, '', postScript, null); startChat(context, uuid, type, title, '', postScript, false, null);
} }
// -() // -()
@ -250,6 +256,7 @@ class BytedeskKefu {
String title, String title,
String commodity, String commodity,
String postScript, String postScript,
bool isV2Robot,
ValueSetter<String>? customCallback) { ValueSetter<String>? customCallback) {
Navigator.of(context).push(new MaterialPageRoute(builder: (context) { Navigator.of(context).push(new MaterialPageRoute(builder: (context) {
return new ChatKFProvider( return new ChatKFProvider(
@ -259,6 +266,7 @@ class BytedeskKefu {
title: title, title: title,
custom: commodity, custom: commodity,
postscript: postScript, postscript: postScript,
isV2Robot: isV2Robot,
customCallback: customCallback, customCallback: customCallback,
); );
})); }));

@ -5,6 +5,7 @@ import 'package:bytedesk_kefu/http/bytedesk_base_api.dart';
import 'package:bytedesk_kefu/model/jsonResult.dart'; import 'package:bytedesk_kefu/model/jsonResult.dart';
import 'package:bytedesk_kefu/model/message.dart'; import 'package:bytedesk_kefu/model/message.dart';
import 'package:bytedesk_kefu/model/requestAnswer.dart'; import 'package:bytedesk_kefu/model/requestAnswer.dart';
import 'package:bytedesk_kefu/model/requestCategory.dart';
import 'package:bytedesk_kefu/model/uploadJsonResult.dart'; import 'package:bytedesk_kefu/model/uploadJsonResult.dart';
import 'package:bytedesk_kefu/util/bytedesk_constants.dart'; import 'package:bytedesk_kefu/util/bytedesk_constants.dart';
import 'package:bytedesk_kefu/util/bytedesk_events.dart'; import 'package:bytedesk_kefu/util/bytedesk_events.dart';
@ -143,8 +144,6 @@ class BytedeskMessageHttpApi extends BytedeskBaseHttpApi {
// //
Future<RequestAnswerResult> queryAnswer(String? tid, String? aid) async { Future<RequestAnswerResult> queryAnswer(String? tid, String? aid) async {
// //
// final queryAnswerUrl =
// '$baseUrl/api/answer/query?tid=$tid&aid=$aid&client=$client';
final queryAnswerUrl = Uri.http(BytedeskConstants.host, '/api/answer/query', final queryAnswerUrl = Uri.http(BytedeskConstants.host, '/api/answer/query',
{'tid': tid, 'aid': aid, 'client': client}); {'tid': tid, 'aid': aid, 'client': client});
print("query Url $queryAnswerUrl"); print("query Url $queryAnswerUrl");
@ -165,16 +164,64 @@ class BytedeskMessageHttpApi extends BytedeskBaseHttpApi {
} }
// //
Future<RequestAnswerResult> messageAnswer( Future<RequestAnswerResult> queryAnswer2(
String? type, String? wid, String? aid, String? content) async { String? tid, String? aid, String? mid) async {
//
final queryAnswerUrl = Uri.http(
BytedeskConstants.host,
'/api/v2/answer/query',
{'tid': tid, 'aid': aid, 'mid': mid, 'client': client});
print("query Url $queryAnswerUrl");
final initResponse =
await this.httpClient.get(queryAnswerUrl, headers: getHeaders());
//
//json
Utf8Decoder utf8decoder = Utf8Decoder(); // fix
//string json
final responseJson =
json.decode(utf8decoder.convert(initResponse.bodyBytes));
// token
if (responseJson.toString().contains('invalid_token')) {
bytedeskEventBus.fire(InvalidTokenEventBus());
}
//
return RequestAnswerResult.fromJson(responseJson);
}
//
Future<RequestCategoryResult> queryCategory(
String? tid, String? cid) async {
//
final queryCategoryUrl = Uri.http(
BytedeskConstants.host,
'/api/v2/answer/category',
{'tid': tid, 'cid': cid, 'client': client});
print("query Url $queryCategoryUrl");
final initResponse =
await this.httpClient.get(queryCategoryUrl, headers: getHeaders());
//
//json
Utf8Decoder utf8decoder = Utf8Decoder(); // fix
//string json
final responseJson =
json.decode(utf8decoder.convert(initResponse.bodyBytes));
// token
if (responseJson.toString().contains('invalid_token')) {
bytedeskEventBus.fire(InvalidTokenEventBus());
}
// //
// final messageAnswerUrl = return RequestCategoryResult.fromJson(responseJson);
// '$baseUrl/api/v2/answer/message?type=$type&wid=$wid&aid=$aid&content=$content&client=$client'; }
final messageAnswerUrl = Uri.http(
BytedeskConstants.host, '/api/v2/answer/message', { //
'type': type, Future<RequestAnswerResult> messageAnswer(
String? wid, String? content) async {
// String? aid, String? type,
final messageAnswerUrl =
Uri.http(BytedeskConstants.host, '/api/v2/answer/message', {
// 'type': type,
'wid': wid, 'wid': wid,
'aid': aid, // 'aid': aid,
'content': content, 'content': content,
'client': client 'client': client
}); });
@ -187,7 +234,8 @@ class BytedeskMessageHttpApi extends BytedeskBaseHttpApi {
//string json //string json
final responseJson = final responseJson =
json.decode(utf8decoder.convert(initResponse.bodyBytes)); json.decode(utf8decoder.convert(initResponse.bodyBytes));
print("messageAnswer responseJson $responseJson"); print("messageAnswer:");
print(responseJson);
// token // token
if (responseJson.toString().contains('invalid_token')) { if (responseJson.toString().contains('invalid_token')) {
bytedeskEventBus.fire(InvalidTokenEventBus()); bytedeskEventBus.fire(InvalidTokenEventBus());

@ -149,7 +149,7 @@ class BytedeskThreadHttpApi extends BytedeskBaseHttpApi {
// //
final threadUrl = Uri.http(BytedeskConstants.host, '/api/thread/request', final threadUrl = Uri.http(BytedeskConstants.host, '/api/thread/request',
{'wId': wid, 'type': type, 'aId': aid, 'client': client}); {'wId': wid, 'type': type, 'aId': aid, 'client': client});
// print("request thread Url $threadUrl"); print(threadUrl);
final initResponse = final initResponse =
await this.httpClient.get(threadUrl, headers: getHeaders()); await this.httpClient.get(threadUrl, headers: getHeaders());
@ -158,7 +158,8 @@ class BytedeskThreadHttpApi extends BytedeskBaseHttpApi {
//string json //string json
final responseJson = final responseJson =
json.decode(utf8decoder.convert(initResponse.bodyBytes)); json.decode(utf8decoder.convert(initResponse.bodyBytes));
// print("responseJson $responseJson"); // print("requestThread:");
// print(responseJson);
// token // token
if (responseJson.toString().contains('invalid_token')) { if (responseJson.toString().contains('invalid_token')) {
bytedeskEventBus.fire(InvalidTokenEventBus()); bytedeskEventBus.fire(InvalidTokenEventBus());
@ -167,6 +168,30 @@ class BytedeskThreadHttpApi extends BytedeskBaseHttpApi {
return RequestThreadResult.fromJson(responseJson); return RequestThreadResult.fromJson(responseJson);
} }
//
Future<RequestThreadResult> requestWorkGroupThreadV2(String? wid) async {
//
final threadUrl = Uri.http(BytedeskConstants.host,
'/api/v2/thread/workGroup', {'wId': wid, 'client': client});
print(threadUrl);
final initResponse =
await this.httpClient.get(threadUrl, headers: getHeaders());
//json
Utf8Decoder utf8decoder = Utf8Decoder(); // fix
//string json
final responseJson =
json.decode(utf8decoder.convert(initResponse.bodyBytes));
print("requestWorkGroupThreadV2:");
print(responseJson);
// token
if (responseJson.toString().contains('invalid_token')) {
bytedeskEventBus.fire(InvalidTokenEventBus());
}
return RequestThreadResult.fromJsonV2(responseJson);
}
// 线 // 线
Future<RequestThreadResult> requestAgent( Future<RequestThreadResult> requestAgent(
String? wid, String? type, String? aid) async { String? wid, String? type, String? aid) async {

@ -33,11 +33,11 @@ class BytedeskUserHttpApi extends BytedeskBaseHttpApi {
// print('oauth result: $oauthResponse'); // print('oauth result: $oauthResponse');
// check the status code for the result // check the status code for the result
int statusCode = oauthResponse.statusCode; int statusCode = oauthResponse.statusCode;
print("statusCode $statusCode"); // print("statusCode $statusCode");
// 200: // 200:
final oauthJson = jsonDecode(oauthResponse.body); final oauthJson = jsonDecode(oauthResponse.body);
print('oauth:'); // print('oauth:');
print(oauthJson); // print(oauthJson);
SpUtil.putBool(BytedeskConstants.isLogin, true); SpUtil.putBool(BytedeskConstants.isLogin, true);
SpUtil.putString(BytedeskConstants.accessToken, oauthJson['access_token']); SpUtil.putString(BytedeskConstants.accessToken, oauthJson['access_token']);
// //

@ -0,0 +1,18 @@
import 'package:equatable/equatable.dart';
class Category extends Equatable {
//
final String? cid;
final String? name;
//
Category({this.cid, this.name}) : super();
//
static Category fromJson(dynamic json) {
return Category(
cid: json['cid'], name: json['name']);
}
//
@override
List<Object> get props => [cid!];
}

@ -5,6 +5,8 @@ import 'package:bytedesk_kefu/model/thread.dart';
import 'package:bytedesk_kefu/model/user.dart'; import 'package:bytedesk_kefu/model/user.dart';
import 'package:bytedesk_kefu/util/bytedesk_constants.dart'; import 'package:bytedesk_kefu/util/bytedesk_constants.dart';
import 'package:sp_util/sp_util.dart'; import 'package:sp_util/sp_util.dart';
import 'category.dart';
// import 'package:equatable/equatable.dart'; // import 'package:equatable/equatable.dart';
class Message { class Message {
@ -30,6 +32,9 @@ class Message {
// //
List<Answer>? answers; List<Answer>? answers;
String? answersJson; String? answersJson;
//
List<Category>? categories;
String? categoriesJson;
Message( Message(
{this.mid, {this.mid,
@ -50,7 +55,9 @@ class Message {
this.currentUid, this.currentUid,
this.client, this.client,
this.answers, this.answers,
this.answersJson}) this.answersJson,
this.categories,
this.categoriesJson})
: super(); : super();
// //
@ -91,6 +98,77 @@ class Message {
answersJson: json['answers'].toString()); answersJson: json['answers'].toString());
} }
//
static Message fromJsonThreadWorkGroupV2(dynamic json) {
//
List<Category> categoriesList = [];
if (json['type'] == BytedeskConstants.MESSAGE_TYPE_ROBOT_V2) {
categoriesList = json['categories'] == null
? []
: (json['categories'] as List<dynamic>)
.map((item) => Category.fromJson(item))
.toList();
}
//
return Message(
mid: json['mid'],
content: json['content'],
imageUrl: json['imageUrl'],
voiceUrl: json['voiceUrl'],
fileUrl: json['fileUrl'],
videoUrl: json['videoOrShortUrl'],
nickname: json['user']['nickname'],
avatar: json['user']['avatar'],
type: json['type'],
timestamp: json['createdAt'],
status: 'stored',
isSend: 0,
currentUid: SpUtil.getString(BytedeskConstants.uid),
client: json['client'],
thread: Thread.fromVisitorJson(json['thread']),
user: User.fromJson(json['user']),
categories: categoriesList,
categoriesJson: json['categories'].toString());
}
//
static Message fromJsonRobotQuery(dynamic json) {
//
// String? content = json['content'];
List<Answer> robotQaList = [];
if (json['type'] == BytedeskConstants.MESSAGE_TYPE_ROBOT) {
robotQaList = json['answers'] == null
? []
: (json['answers'] as List<dynamic>)
.map((item) => Answer.fromJson(item))
.toList();
// for (var i = 0; i < robotQaList.length; i++) {
// Answer answer = robotQaList[i];
// content += '\n\n' + answer.aid + ':' + answer.question;
// }
}
//
return Message(
mid: json['mid'],
content: json['content'],
imageUrl: json['imageUrl'],
voiceUrl: json['voiceUrl'],
fileUrl: json['fileUrl'],
videoUrl: json['videoOrShortUrl'],
nickname: json['user']['nickname'],
avatar: json['user']['avatar'],
type: json['type'],
timestamp: json['createdAt'],
status: 'stored',
isSend: 0,
currentUid: SpUtil.getString(BytedeskConstants.uid),
client: json['client'],
// thread: Thread.fromVisitorJson(json['thread']),
user: User.fromJson(json['user']),
answers: robotQaList,
answersJson: json['answers'].toString());
}
static Message fromJson(dynamic json) { static Message fromJson(dynamic json) {
List<Answer> robotQaList = []; List<Answer> robotQaList = [];
if (json['type'] == BytedeskConstants.MESSAGE_TYPE_ROBOT) { if (json['type'] == BytedeskConstants.MESSAGE_TYPE_ROBOT) {
@ -139,7 +217,8 @@ class Message {
'isSend': isSend, 'isSend': isSend,
'currentUid': currentUid, 'currentUid': currentUid,
'client': client, 'client': client,
'answers': answersJson 'answers': answersJson,
'categories': categoriesJson
}; };
} }

@ -1,6 +1,8 @@
import 'dart:async'; import 'dart:async';
import 'dart:convert';
import 'package:bytedesk_kefu/model/answer.dart'; import 'package:bytedesk_kefu/model/answer.dart';
import 'package:bytedesk_kefu/model/category.dart';
import 'package:bytedesk_kefu/util/bytedesk_constants.dart'; import 'package:bytedesk_kefu/util/bytedesk_constants.dart';
import 'package:bytedesk_kefu/util/bytedesk_utils.dart'; import 'package:bytedesk_kefu/util/bytedesk_utils.dart';
import 'package:path/path.dart'; import 'package:path/path.dart';
@ -43,6 +45,7 @@ class MessageProvider {
final String? columnCurrentUid = 'currentUid'; final String? columnCurrentUid = 'currentUid';
final String? columnClient = 'client'; final String? columnClient = 'client';
final String? columnAnswers = 'answers'; final String? columnAnswers = 'answers';
final String? columnCategories = 'categories';
// //
Database? database; Database? database;
@ -52,19 +55,19 @@ class MessageProvider {
// Set the path to the database. Note: Using the `join` function from the // Set the path to the database. Note: Using the `join` function from the
// `path` package is best practice to ensure the path is correctly // `path` package is best practice to ensure the path is correctly
// constructed for each platform. // constructed for each platform.
join(await getDatabasesPath(), 'bytedesk-message-v9.db'), join(await getDatabasesPath(), 'bytedesk-message-v10.db'),
// When the database is first created, create a table to store dogs. // When the database is first created, create a table to store dogs.
onCreate: (db, version) { onCreate: (db, version) {
// Run the CREATE TABLE statement on the database. autoincrement // Run the CREATE TABLE statement on the database. autoincrement
return db.execute( return db.execute(
"CREATE TABLE $tableMessage($columnId INTEGER PRIMARY KEY AUTOINCREMENT, " + "CREATE TABLE $tableMessage($columnId INTEGER PRIMARY KEY AUTOINCREMENT, " +
"$columnMid TEXT, $columnType TEXT, $columnTopic TEXT, $columnContent TEXT, $columnImageUrl TEXT, $columnVoiceUrl TEXT,$columnVideoUrl TEXT, $columnFileUrl TEXT, $columnNickname TEXT, $columnAvatar TEXT, $columnStatus TEXT, " + "$columnMid TEXT, $columnType TEXT, $columnTopic TEXT, $columnContent TEXT, $columnImageUrl TEXT, $columnVoiceUrl TEXT,$columnVideoUrl TEXT, $columnFileUrl TEXT, $columnNickname TEXT, $columnAvatar TEXT, $columnStatus TEXT, " +
"$columnIsSend INTEGER, $columnTimestamp TEXT, $columnCurrentUid TEXT, $columnClient TEXT, $columnAnswers TEXT)", "$columnIsSend INTEGER, $columnTimestamp TEXT, $columnCurrentUid TEXT, $columnClient TEXT, $columnAnswers TEXT, $columnCategories TEXT)",
); );
}, },
// Set the version. This executes the onCreate function and provides a // Set the version. This executes the onCreate function and provides a
// path to perform database upgrades and downgrades. // path to perform database upgrades and downgrades.
version: 9, version: 10,
); );
// database path:/Users/ningjinpeng/Library/Developer/CoreSimulator/Devices/715CBA02-A602-4DE1-8C57-75A64B53BF03/data/Containers/Data/Application/8F46273D-9492-4C42-A618-4DF3815562BA/Documents/bytedesk-message-v9.db // database path:/Users/ningjinpeng/Library/Developer/CoreSimulator/Devices/715CBA02-A602-4DE1-8C57-75A64B53BF03/data/Containers/Data/Application/8F46273D-9492-4C42-A618-4DF3815562BA/Documents/bytedesk-message-v9.db
// print('database path:' + database!.path); // print('database path:' + database!.path);
@ -104,7 +107,9 @@ class MessageProvider {
columnNickname!, columnNickname!,
columnAvatar!, columnAvatar!,
columnIsSend!, columnIsSend!,
columnClient! columnClient!,
columnAnswers!,
columnCategories!
], ],
where: '$columnTopic = ? and $columnCurrentUid = ?', where: '$columnTopic = ? and $columnCurrentUid = ?',
whereArgs: [topic, currentUid], whereArgs: [topic, currentUid],
@ -118,13 +123,23 @@ class MessageProvider {
return List.generate(maps.length, (i) { return List.generate(maps.length, (i) {
// //
List<Answer> robotQaList = []; List<Answer> robotQaList = [];
if (maps[i]['type'] == BytedeskConstants.MESSAGE_TYPE_ROBOT) { // FIXME:
robotQaList = maps[i]['answers'] == null // if (maps[i]['type'] == BytedeskConstants.MESSAGE_TYPE_ROBOT) {
? [] // robotQaList = maps[i][columnAnswers] == null
: (maps[i]['answers'] as List<dynamic>) // ? []
.map((item) => Answer.fromJson(item)) // : (json.decode(maps[i][columnAnswers]) as List<dynamic>)
.toList(); // .map((item) => Answer.fromJson(item))
} // .toList();
// }
List<Category> categoriesList = [];
// FIXME:
// if (maps[i]['type'] == BytedeskConstants.MESSAGE_TYPE_ROBOT_V2) {
// categoriesList = maps[i][columnCategories] == null
// ? []
// : (json.decode(maps[i][columnCategories]) as List<dynamic>)
// .map((item) => Category.fromJson(item))
// .toList();
// }
// print('4'); // print('4');
return Message( return Message(
mid: maps[i]['mid'], mid: maps[i]['mid'],
@ -141,7 +156,8 @@ class MessageProvider {
avatar: maps[i]['avatar'], avatar: maps[i]['avatar'],
isSend: maps[i]['isSend'], isSend: maps[i]['isSend'],
client: maps[i]['client'], client: maps[i]['client'],
answers: robotQaList); answers: robotQaList,
categories: categoriesList);
}); });
} }

@ -15,15 +15,15 @@ class RequestAnswerResult extends Equatable {
return RequestAnswerResult( return RequestAnswerResult(
message: json["message"], message: json["message"],
statusCode: json["status_code"], statusCode: json["status_code"],
query: Message.fromJsonThread(json["data"]["query"]), query: Message.fromJsonRobotQuery(json["data"]["query"]),
anwser: Message.fromJsonThread(json["data"]["reply"])); anwser: Message.fromJsonRobotQuery(json["data"]["reply"]));
} }
static RequestAnswerResult fromRateJson(dynamic json) { static RequestAnswerResult fromRateJson(dynamic json) {
return RequestAnswerResult( return RequestAnswerResult(
message: json["message"], message: json["message"],
statusCode: json["status_code"], statusCode: json["status_code"],
anwser: Message.fromJsonThread(json["data"])); anwser: Message.fromJsonRobotQuery(json["data"]));
} }
@override @override

@ -0,0 +1,24 @@
import 'package:equatable/equatable.dart';
import 'message.dart';
class RequestCategoryResult extends Equatable {
final String? message;
final int? statusCode;
//
final Message? query;
final Message? anwser;
RequestCategoryResult({this.message, this.statusCode, this.query, this.anwser})
: super();
static RequestCategoryResult fromJson(dynamic json) {
return RequestCategoryResult(
message: json["message"],
statusCode: json["status_code"],
query: Message.fromJsonRobotQuery(json["data"]["query"]),
anwser: Message.fromJsonRobotQuery(json["data"]["reply"]));
}
@override
List<Object> get props => [this.statusCode!];
}

@ -16,6 +16,13 @@ class RequestThreadResult extends Equatable {
msg: Message.fromJsonThread(json["data"])); msg: Message.fromJsonThread(json["data"]));
} }
static RequestThreadResult fromJsonV2(dynamic json) {
return RequestThreadResult(
message: json["message"],
statusCode: json["status_code"],
msg: Message.fromJsonThreadWorkGroupV2(json["data"]));
}
@override @override
List<Object> get props => [this.msg!.mid!]; List<Object> get props => [this.msg!.mid!];
} }

@ -47,7 +47,8 @@ class Thread extends Equatable {
this.nodisturbVisitor, this.nodisturbVisitor,
this.unread, this.unread,
this.unreadVisitor, this.unreadVisitor,
this.client}); this.client,
this.currentUid});
static Thread fromWorkGroupJson(dynamic json) { static Thread fromWorkGroupJson(dynamic json) {
return Thread( return Thread(
@ -61,6 +62,7 @@ class Thread extends Equatable {
unreadCount: json['unreadCount'], unreadCount: json['unreadCount'],
type: json['type'], type: json['type'],
current: json['current'], current: json['current'],
client: json['client'],
top: json['top'], top: json['top'],
topVisitor: json['topVisitor'], topVisitor: json['topVisitor'],
nodisturb: json['nodisturb'], nodisturb: json['nodisturb'],
@ -82,6 +84,7 @@ class Thread extends Equatable {
unreadCount: json['unreadCount'], unreadCount: json['unreadCount'],
type: json['type'], type: json['type'],
current: json['current'], current: json['current'],
client: json['client'],
top: json['top'], top: json['top'],
topVisitor: json['topVisitor'], topVisitor: json['topVisitor'],
nodisturb: json['nodisturb'], nodisturb: json['nodisturb'],
@ -100,6 +103,7 @@ class Thread extends Equatable {
unreadCount: json['unreadCount'], unreadCount: json['unreadCount'],
type: json['type'], type: json['type'],
current: json['current'], current: json['current'],
client: json['client'],
top: json['top'], top: json['top'],
topVisitor: json['topVisitor'], topVisitor: json['topVisitor'],
nodisturb: json['nodisturb'], nodisturb: json['nodisturb'],
@ -118,6 +122,7 @@ class Thread extends Equatable {
unreadCount: json['unreadCount'], unreadCount: json['unreadCount'],
type: json['type'], type: json['type'],
current: json['current'], current: json['current'],
client: json['client'],
top: json['top'], top: json['top'],
topVisitor: json['topVisitor'], topVisitor: json['topVisitor'],
nodisturb: json['nodisturb'], nodisturb: json['nodisturb'],
@ -137,6 +142,7 @@ class Thread extends Equatable {
unreadCount: json['unreadCount'], unreadCount: json['unreadCount'],
type: json['type'], type: json['type'],
current: json['current'], current: json['current'],
client: json['client'],
top: json['top'], top: json['top'],
topVisitor: json['topVisitor'], topVisitor: json['topVisitor'],
nodisturb: json['nodisturb'], nodisturb: json['nodisturb'],
@ -157,6 +163,7 @@ class Thread extends Equatable {
unreadCount: json['unreadCount'], unreadCount: json['unreadCount'],
type: json['type'], type: json['type'],
current: json['current'], current: json['current'],
client: json['client'],
top: json['top'], top: json['top'],
topVisitor: json['topVisitor'], topVisitor: json['topVisitor'],
nodisturb: json['nodisturb'], nodisturb: json['nodisturb'],
@ -177,6 +184,7 @@ class Thread extends Equatable {
unreadCount: json['unreadCount'], unreadCount: json['unreadCount'],
type: json['type'], type: json['type'],
current: json['current'], current: json['current'],
client: json['client'],
top: json['top'], top: json['top'],
topVisitor: json['topVisitor'], topVisitor: json['topVisitor'],
nodisturb: json['nodisturb'], nodisturb: json['nodisturb'],
@ -196,6 +204,7 @@ class Thread extends Equatable {
unreadCount: json['unreadCount'], unreadCount: json['unreadCount'],
type: json['type'], type: json['type'],
current: json['current'], current: json['current'],
client: json['client'],
top: json['top'], top: json['top'],
topVisitor: json['topVisitor'], topVisitor: json['topVisitor'],
nodisturb: json['nodisturb'], nodisturb: json['nodisturb'],
@ -215,6 +224,7 @@ class Thread extends Equatable {
unreadCount: json['unreadCount'], unreadCount: json['unreadCount'],
type: json['type'], type: json['type'],
current: json['current'], current: json['current'],
client: json['client'],
top: json['top'], top: json['top'],
topVisitor: json['topVisitor'], topVisitor: json['topVisitor'],
nodisturb: json['nodisturb'], nodisturb: json['nodisturb'],
@ -264,6 +274,7 @@ class Thread extends Equatable {
unreadCount = map['unreadCount']; unreadCount = map['unreadCount'];
type = map['type']; type = map['type'];
current = map['current']; current = map['current'];
client = map['client'];
top = map['top']; top = map['top'];
topVisitor = map['topVisitor']; topVisitor = map['topVisitor'];
nodisturb = map['nodisturb']; nodisturb = map['nodisturb'];

@ -45,7 +45,7 @@ class BytedeskMqtt {
factory BytedeskMqtt() { factory BytedeskMqtt() {
return _singleton; return _singleton;
} }
BytedeskMqtt._internal() {} BytedeskMqtt._internal();
void connect() async { void connect() async {
// eventbus广... // eventbus广...
@ -670,7 +670,8 @@ class BytedeskMqtt {
currentThread, extraParam); currentThread, extraParam);
} }
void publish(String content, String type, Thread currentThread, ExtraParam? extraParam) { void publish(String content, String type, Thread currentThread,
ExtraParam? extraParam) {
// if (currentThread == null) { // if (currentThread == null) {
// print('连接客服失败,请退出页面重新进入。注意: 请在App启动的时候调用init接口'); // print('连接客服失败,请退出页面重新进入。注意: 请在App启动的时候调用init接口');
// Fluttertoast.showToast(msg: '连接客服失败,请退出页面重新进入'); // Fluttertoast.showToast(msg: '连接客服失败,请退出页面重新进入');
@ -689,6 +690,7 @@ class BytedeskMqtt {
thread.topic = currentThread.topic!; thread.topic = currentThread.topic!;
thread.nickname = currentThread.nickname!; thread.nickname = currentThread.nickname!;
thread.avatar = currentThread.avatar!; thread.avatar = currentThread.avatar!;
thread.client = currentThread.client!;
thread.timestamp = BytedeskUtils.formatedDateNow(); // thread.timestamp = BytedeskUtils.formatedDateNow(); //
thread.unreadCount = 0; thread.unreadCount = 0;
var extra = {'top': false, 'undisturb': false}; var extra = {'top': false, 'undisturb': false};

@ -2,6 +2,7 @@ import 'package:bytedesk_kefu/http/bytedesk_message_api.dart';
import 'package:bytedesk_kefu/model/jsonResult.dart'; import 'package:bytedesk_kefu/model/jsonResult.dart';
import 'package:bytedesk_kefu/model/message.dart'; import 'package:bytedesk_kefu/model/message.dart';
import 'package:bytedesk_kefu/model/requestAnswer.dart'; import 'package:bytedesk_kefu/model/requestAnswer.dart';
import 'package:bytedesk_kefu/model/requestCategory.dart';
import 'package:bytedesk_kefu/model/uploadJsonResult.dart'; import 'package:bytedesk_kefu/model/uploadJsonResult.dart';
class MessageRepository { class MessageRepository {
@ -28,13 +29,17 @@ class MessageRepository {
return await bytedeskHttpApi.loadChannelMessages(cid, page, size); return await bytedeskHttpApi.loadChannelMessages(cid, page, size);
} }
Future<RequestAnswerResult> queryAnswer(String? tid, String? aid) async { Future<RequestAnswerResult> queryAnswer(String? tid, String? aid, String? mid) async {
return await bytedeskHttpApi.queryAnswer(tid, aid); return await bytedeskHttpApi.queryAnswer2(tid, aid, mid);
} }
Future<RequestAnswerResult> messageAnswer( Future<RequestCategoryResult> queryCategory(
String? type, String? wid, String? aid, String? content) async { String? tid, String? cid) async {
return await bytedeskHttpApi.messageAnswer(type, wid, aid, content); return await bytedeskHttpApi.queryCategory(tid, cid);
}
Future<RequestAnswerResult> messageAnswer(String? wid, String? content) async {
return await bytedeskHttpApi.messageAnswer(wid, content);
} }
Future<RequestAnswerResult> rateAnswer( Future<RequestAnswerResult> rateAnswer(

@ -28,7 +28,10 @@ class ThreadRepository {
} }
Future<RequestThreadResult> requestThread( Future<RequestThreadResult> requestThread(
String? wid, String? type, String? aid) async { String? wid, String? type, String? aid, bool? isV2Robot) async {
if (isV2Robot!) {
return await bytedeskHttpApi.requestWorkGroupThreadV2(wid);
}
return await bytedeskHttpApi.requestThread(wid, type, aid); return await bytedeskHttpApi.requestThread(wid, type, aid);
} }

@ -500,9 +500,9 @@ class _ChatIMPageState extends State<ChatIMPage>
// //
BlocProvider.of<MessageBloc>(context) BlocProvider.of<MessageBloc>(context)
..add(MessageAnswerEvent( ..add(MessageAnswerEvent(
type: widget.type, // type: widget.type,
wid: widget.wid, wid: widget.wid,
aid: widget.aid, // aid: widget.aid,
content: text)); content: text));
} else if (_bdMqtt.isConnected()) { } else if (_bdMqtt.isConnected()) {
if (_currentThread == null) { if (_currentThread == null) {

@ -40,6 +40,7 @@ class ChatKFPage extends StatefulWidget {
final String? title; final String? title;
final String? custom; final String? custom;
final String? postscript; final String? postscript;
final bool? isV2Robot;
// //
final bool? isThread; final bool? isThread;
final Thread? thread; final Thread? thread;
@ -53,6 +54,7 @@ class ChatKFPage extends StatefulWidget {
this.title, this.title,
this.custom, this.custom,
this.postscript, this.postscript,
this.isV2Robot,
this.isThread, this.isThread,
this.thread, this.thread,
this.customCallback}) this.customCallback})
@ -89,6 +91,7 @@ class _ChatKFPageState extends State<ChatKFPage>
String? _currentAvatar = SpUtil.getString(BytedeskConstants.avatar); String? _currentAvatar = SpUtil.getString(BytedeskConstants.avatar);
// //
Thread? _currentThread; Thread? _currentThread;
User? _robotUser;
// //
bool _isRobot = false; bool _isRobot = false;
// //
@ -189,11 +192,8 @@ class _ChatKFPageState extends State<ChatKFPage>
_currentThread = state.threadResult.msg!.thread; _currentThread = state.threadResult.msg!.thread;
_isRequestingThread = false; _isRequestingThread = false;
}); });
//
// _messageProvider.insert(state.threadResult.msg!);
// TODO: // TODO:
_getMessages(_page, _size); _getMessages(_page, _size);
// _appendMessage(state.threadResult.msg!);
// //
if (state.threadResult.statusCode == 200 || if (state.threadResult.statusCode == 200 ||
state.threadResult.statusCode == 201) { state.threadResult.statusCode == 201) {
@ -286,6 +286,7 @@ class _ChatKFPageState extends State<ChatKFPage>
// TODO: // TODO:
setState(() { setState(() {
_isRobot = true; _isRobot = true;
_robotUser = state.threadResult.msg!.user;
_currentThread = state.threadResult.msg!.thread; _currentThread = state.threadResult.msg!.thread;
}); });
// //
@ -347,20 +348,25 @@ class _ChatKFPageState extends State<ChatKFPage>
} else if (state is UploadVideoSuccess) { } else if (state is UploadVideoSuccess) {
_bdMqtt.sendVideoMessage( _bdMqtt.sendVideoMessage(
state.uploadJsonResult.url!, _currentThread!); state.uploadJsonResult.url!, _currentThread!);
// TODO: http rest sendImageRest
} else if (state is QueryAnswerSuccess) { } else if (state is QueryAnswerSuccess) {
Message queryMessage = state.query!; //
queryMessage.isSend = 1; // Message queryMessage = state.query!;
_messageProvider.insert(queryMessage); // queryMessage.isSend = 1;
_appendMessage(queryMessage); // _messageProvider.insert(queryMessage);
// // _appendMessage(queryMessage);
// //
// _messageProvider.insert(state.answer!);
// _appendMessage(state.answer!);
} else if (state is QueryCategorySuccess) {
_messageProvider.insert(state.answer!); _messageProvider.insert(state.answer!);
_appendMessage(state.answer!); _appendMessage(state.answer!);
} else if (state is MessageAnswerSuccess) { } else if (state is MessageAnswerSuccess) {
Message queryMessage = state.query!; // Message queryMessage = state.query!;
queryMessage.isSend = 1; // queryMessage.isSend = 1;
_messageProvider.insert(queryMessage); // _messageProvider.insert(queryMessage);
_appendMessage(queryMessage); // _appendMessage(queryMessage);
// //
if (state.query!.content!.contains('人工')) { if (state.query!.content!.contains('人工')) {
BlocProvider.of<ThreadBloc>(context) BlocProvider.of<ThreadBloc>(context)
@ -375,7 +381,7 @@ class _ChatKFPageState extends State<ChatKFPage>
} else if (state is RateAnswerSuccess) { } else if (state is RateAnswerSuccess) {
// TODO: // TODO:
} else if (state is LoadHistoryMessageSuccess) { } else if (state is LoadHistoryMessageSuccess) {
print('LoadHistoryMessageSuccess'); // print('LoadHistoryMessageSuccess');
// //
for (var i = 0; i < state.messageList!.length; i++) { for (var i = 0; i < state.messageList!.length; i++) {
Message message = state.messageList![i]; Message message = state.messageList![i];
@ -490,7 +496,7 @@ class _ChatKFPageState extends State<ChatKFPage>
if (_debounce?.isActive ?? false) _debounce!.cancel(); if (_debounce?.isActive ?? false) _debounce!.cancel();
// 500 // 500
_debounce = Timer(const Duration(milliseconds: 500), () { _debounce = Timer(const Duration(milliseconds: 500), () {
print('send preview $value'); // print('send preview $value');
// //
if (value.trim().length > 0) { if (value.trim().length > 0) {
_bdMqtt.sendPreviewMessage(value, _currentThread!); _bdMqtt.sendPreviewMessage(value, _currentThread!);
@ -530,12 +536,14 @@ class _ChatKFPageState extends State<ChatKFPage>
} }
// //
if (_isRobot) { if (_isRobot) {
//
appendQueryMessage(text);
// //
BlocProvider.of<MessageBloc>(context) BlocProvider.of<MessageBloc>(context)
..add(MessageAnswerEvent( ..add(MessageAnswerEvent(
type: widget.type, // type: widget.type,
wid: widget.wid, wid: widget.wid,
aid: widget.aid, // aid: widget.aid,
content: text)); content: text));
} else if (_bdMqtt.isConnected()) { } else if (_bdMqtt.isConnected()) {
if (_currentThread == null) { if (_currentThread == null) {
@ -545,7 +553,7 @@ class _ChatKFPageState extends State<ChatKFPage>
// //
_bdMqtt.sendTextMessage(text, _currentThread!); _bdMqtt.sendTextMessage(text, _currentThread!);
} else { } else {
print('长连接断开的情况下调用rest接口'); // print('长连接断开的情况下调用rest接口');
sendTextMessageRest(text); sendTextMessageRest(text);
} }
} }
@ -553,7 +561,7 @@ class _ChatKFPageState extends State<ChatKFPage>
// http rest // http rest
void sendTextMessageRest(String text) { void sendTextMessageRest(String text) {
// //
String? mid = BytedeskUuid.generateV4(); String? mid = BytedeskUuid.uuid();
String? timestamp = BytedeskUtils.formatedDateNow(); String? timestamp = BytedeskUtils.formatedDateNow();
String? client = BytedeskUtils.getClient(); String? client = BytedeskUtils.getClient();
String? type = BytedeskConstants.MESSAGE_TYPE_TEXT; String? type = BytedeskConstants.MESSAGE_TYPE_TEXT;
@ -564,6 +572,7 @@ class _ChatKFPageState extends State<ChatKFPage>
"client": client, "client": client,
"version": "1", "version": "1",
"type": type, "type": type,
"status": "sending",
"user": { "user": {
"uid": this._currentUid, "uid": this._currentUid,
"nickname": this._currentNickname, "nickname": this._currentNickname,
@ -606,12 +615,14 @@ class _ChatKFPageState extends State<ChatKFPage>
message.content = text; message.content = text;
// //
_messageProvider.insert(message); _messageProvider.insert(message);
//
pushToMessageArray(message, true);
} }
// http rest // http rest
void sendImageMessageRest(String imageUrl) { void sendImageMessageRest(String imageUrl) {
// //
String? mid = BytedeskUuid.generateV4(); String? mid = BytedeskUuid.uuid();
String? timestamp = BytedeskUtils.formatedDateNow(); String? timestamp = BytedeskUtils.formatedDateNow();
String? client = BytedeskUtils.getClient(); String? client = BytedeskUtils.getClient();
String? type = BytedeskConstants.MESSAGE_TYPE_IMAGE; String? type = BytedeskConstants.MESSAGE_TYPE_IMAGE;
@ -622,6 +633,7 @@ class _ChatKFPageState extends State<ChatKFPage>
"client": client, "client": client,
"version": "1", "version": "1",
"type": type, "type": type,
"status": "sending",
"user": { "user": {
"uid": this._currentUid, "uid": this._currentUid,
"nickname": this._currentNickname, "nickname": this._currentNickname,
@ -664,6 +676,73 @@ class _ChatKFPageState extends State<ChatKFPage>
message.imageUrl = imageUrl; message.imageUrl = imageUrl;
// //
_messageProvider.insert(message); _messageProvider.insert(message);
//
pushToMessageArray(message, true);
}
void appendQueryMessage(String content) {
//
String? mid = BytedeskUuid.uuid();
String? timestamp = BytedeskUtils.formatedDateNow();
String? client = BytedeskUtils.getClient();
String? type = BytedeskConstants.MESSAGE_TYPE_ROBOT;
//
//
Message message = new Message();
message.mid = mid;
message.type = type;
message.timestamp = timestamp;
message.client = client;
message.nickname = _currentNickname;
message.avatar = _currentAvatar;
message.topic = this._currentThread!.topic;
message.status = BytedeskConstants.MESSAGE_STATUS_STORED;
message.isSend = 1;
message.currentUid = this._currentUid;
message.answersJson = '';
message.thread = this._currentThread;
message.user = User(
uid: this._currentUid,
avatar: this._currentAvatar,
nickname: this._currentNickname);
//
message.content = content;
//
_messageProvider.insert(message);
//
pushToMessageArray(message, true);
}
void appendReplyMessage(String aid, String mid, String content) {
//
String? timestamp = BytedeskUtils.formatedDateNow();
String? client = BytedeskUtils.getClient();
String? type = BytedeskConstants.MESSAGE_TYPE_ROBOT_RESULT;
//
// R
Message message = new Message();
message.mid = mid;
message.type = type;
message.timestamp = timestamp;
message.client = client;
message.nickname = _robotUser!.nickname;
message.avatar = _robotUser!.avatar;
message.topic = this._currentThread!.topic;
message.status = BytedeskConstants.MESSAGE_STATUS_STORED;
message.isSend = 0;
message.currentUid = this._currentUid;
message.answersJson = '';
message.thread = this._currentThread;
message.user = User(
uid: this._robotUser!.uid,
avatar: this._robotUser!.avatar,
nickname: this._robotUser!.nickname);
//
message.content = content;
//
_messageProvider.insert(message);
//
pushToMessageArray(message, true);
} }
// //
@ -731,18 +810,19 @@ class _ChatKFPageState extends State<ChatKFPage>
event.message.mid!, event.message.thread!); event.message.mid!, event.message.thread!);
} }
// //
if (this.mounted) { pushToMessageArray(event.message, true);
// // if (this.mounted) {
MessageWidget messageWidget = new MessageWidget( // //
message: event.message, // MessageWidget messageWidget = new MessageWidget(
customCallback: widget.customCallback, // message: event.message,
animationController: new AnimationController( // customCallback: widget.customCallback,
vsync: this, duration: Duration(milliseconds: 500))); // animationController: new AnimationController(
setState(() { // vsync: this, duration: Duration(milliseconds: 500)));
_messages.insert(0, messageWidget); // setState(() {
// _messages.add(messageWidget); // _messages.insert(0, messageWidget);
}); // // _messages.add(messageWidget);
} // });
// }
}); });
// //
bytedeskEventBus.on<DeleteMessageEventBus>().listen((event) { bytedeskEventBus.on<DeleteMessageEventBus>().listen((event) {
@ -761,11 +841,26 @@ class _ChatKFPageState extends State<ChatKFPage>
if (this.mounted) { if (this.mounted) {
// print('aid ${event.aid}, question ${event.question}, answer ${event.answer}'); // print('aid ${event.aid}, question ${event.question}, answer ${event.answer}');
// //
appendQueryMessage(event.question);
//
String? mid = BytedeskUuid.uuid();
appendReplyMessage(event.aid, mid, event.answer);
//
BlocProvider.of<MessageBloc>(context) BlocProvider.of<MessageBloc>(context)
..add(QueryAnswerEvent( ..add(QueryAnswerEvent(
tid: _currentThread!.tid, tid: _currentThread!.tid, aid: event.aid, mid: mid));
aid: event.aid, }
)); });
//
bytedeskEventBus.on<QueryCategoryEventBus>().listen((event) {
if (this.mounted) {
print('cid ${event.cid}, name ${event.name}');
//
appendQueryMessage(event.name);
//
BlocProvider.of<MessageBloc>(context)
..add(QueryCategoryEvent(
tid: _currentThread!.tid, cid: event.cid));
} }
}); });
// //
@ -971,30 +1066,45 @@ class _ChatKFPageState extends State<ChatKFPage>
BytedeskConstants.MESSAGE_TYPE_NOTIFICATION_THREAD_REENTRY) { BytedeskConstants.MESSAGE_TYPE_NOTIFICATION_THREAD_REENTRY) {
continue; continue;
} else { } else {
pushToMessageArray(message); pushToMessageArray(message, false);
} }
} }
} else { } else {
pushToMessageArray(message); pushToMessageArray(message, false);
} }
} }
// //
_page += 1; _page += 1;
} }
void pushToMessageArray(Message message) { void pushToMessageArray(Message message, bool append) {
if (this.mounted) { if (this.mounted) {
MessageWidget messageWidget = new MessageWidget( bool contains = false;
message: message, for (var i = 0; i < _messages.length; i++) {
customCallback: widget.customCallback, Message? element = _messages[i].message;
animationController: new AnimationController( if (element!.mid == message.mid) {
vsync: this, duration: Duration(milliseconds: 500))); contains = true;
setState(() { //
_messages.add(messageWidget); _messageProvider.update(element.mid, message.status);
_messages.sort((a, b) { }
return b.message!.timestamp!.compareTo(a.message!.timestamp!); }
if (!contains) {
MessageWidget messageWidget = new MessageWidget(
message: message,
customCallback: widget.customCallback,
animationController: new AnimationController(
vsync: this, duration: Duration(milliseconds: 500)));
setState(() {
if (append) {
_messages.insert(0, messageWidget);
} else {
_messages.add(messageWidget);
_messages.sort((a, b) {
return b.message!.timestamp!.compareTo(a.message!.timestamp!);
});
}
}); });
}); }
} }
if (message.status != BytedeskConstants.MESSAGE_STATUS_READ) { if (message.status != BytedeskConstants.MESSAGE_STATUS_READ) {
// //
@ -1005,29 +1115,8 @@ class _ChatKFPageState extends State<ChatKFPage>
} }
Future<Null> _appendMessage(Message message) async { Future<Null> _appendMessage(Message message) async {
// print('append:' + message.mid! + 'content:' + message.content!); print('append:' + message.mid! + 'content:' + message.content!);
bool contains = false; pushToMessageArray(message, true);
for (var i = 0; i < _messages.length; i++) {
Message? element = _messages[i].message;
if (element!.mid == message.mid) {
contains = true;
//
_messageProvider.update(element.mid, message.status);
}
}
if (!contains) {
// _messageProvider.insert(message); //
MessageWidget messageWidget = new MessageWidget(
message: message,
customCallback: widget.customCallback,
animationController: new AnimationController(
vsync: this, duration: Duration(milliseconds: 500)));
if (this.mounted) {
setState(() {
_messages.insert(0, messageWidget);
});
}
}
} }
void scrollToBottom() { void scrollToBottom() {

@ -469,9 +469,9 @@ class _ChatLSPageState extends State<ChatLSPage>
if (_isRobot) { if (_isRobot) {
BlocProvider.of<MessageBloc>(context) BlocProvider.of<MessageBloc>(context)
..add(MessageAnswerEvent( ..add(MessageAnswerEvent(
type: widget.type, // type: widget.type,
wid: widget.wid, wid: widget.wid,
aid: widget.aid, // aid: widget.aid,
content: text)); content: text));
} else { } else {
if (_currentThread == null) { if (_currentThread == null) {

@ -12,6 +12,7 @@ class ChatKFProvider extends StatelessWidget {
final String? title; final String? title;
final String? custom; final String? custom;
final String? postscript; final String? postscript;
final bool? isV2Robot;
final ValueSetter<String>? customCallback; final ValueSetter<String>? customCallback;
// //
const ChatKFProvider( const ChatKFProvider(
@ -22,6 +23,7 @@ class ChatKFProvider extends StatelessWidget {
this.title, this.title,
this.custom, this.custom,
this.postscript, this.postscript,
this.isV2Robot,
this.customCallback}) this.customCallback})
: super(key: key); : super(key: key);
// //
@ -32,7 +34,7 @@ class ChatKFProvider extends StatelessWidget {
providers: [ providers: [
BlocProvider<ThreadBloc>( BlocProvider<ThreadBloc>(
create: (BuildContext context) => ThreadBloc() create: (BuildContext context) => ThreadBloc()
..add(RequestThreadEvent(wid: wid, aid: aid, type: type)), ..add(RequestThreadEvent(wid: wid, aid: aid, type: type, isV2Robot: isV2Robot)),
), ),
BlocProvider<MessageBloc>( BlocProvider<MessageBloc>(
create: (BuildContext context) => MessageBloc(), create: (BuildContext context) => MessageBloc(),
@ -45,6 +47,7 @@ class ChatKFProvider extends StatelessWidget {
title: title, title: title,
custom: custom, custom: custom,
postscript: postscript, postscript: postscript,
isV2Robot: isV2Robot,
isThread: false, isThread: false,
customCallback: customCallback), customCallback: customCallback),
); );

@ -41,12 +41,16 @@ class MessageWidget extends StatelessWidget {
double tWidth = MediaQuery.of(context).size.width - 160; double tWidth = MediaQuery.of(context).size.width - 160;
// FIXME: // FIXME:
String status = ''; String status = '';
if (message!.status == BytedeskConstants.MESSAGE_STATUS_STORED) { if (message!.status == BytedeskConstants.MESSAGE_STATUS_SENDING) {
// status = '发送成功'; status = '发送中';
} else if (message!.status == BytedeskConstants.MESSAGE_STATUS_STORED) {
status = ''; //
} else if (message!.status == BytedeskConstants.MESSAGE_STATUS_RECEIVED) { } else if (message!.status == BytedeskConstants.MESSAGE_STATUS_RECEIVED) {
status = '送达'; status = '送达';
} else if (message!.status == BytedeskConstants.MESSAGE_STATUS_READ) { } else if (message!.status == BytedeskConstants.MESSAGE_STATUS_READ) {
status = '已读'; status = '已读';
} else if (message!.status == BytedeskConstants.MESSAGE_STATUS_ERROR) {
status = '失败';
} }
return Container( return Container(
margin: EdgeInsets.only(top: 8.0, left: 8.0), margin: EdgeInsets.only(top: 8.0, left: 8.0),
@ -450,43 +454,46 @@ class MessageWidget extends StatelessWidget {
// softWrap: true, // softWrap: true,
// style: TextStyle(color: Colors.black, fontSize: 16.0), // style: TextStyle(color: Colors.black, fontSize: 16.0),
// ), // ),
Html( Visibility(
data: message.content ?? '', visible: message.content != null && message.content!.length > 0,
onLinkTap: (url, _, __, ___) { child: Html(
// url data: message.content ?? '',
BytedeskKefu.openWebView(context, url!, '网页'); onLinkTap: (url, _, __, ___) {
}, // url
onImageTap: (src, _, __, ___) { BytedeskKefu.openWebView(context, url!, '网页');
// },
// print("open image $src"); onImageTap: (src, _, __, ___) {
Navigator.push( //
context, // print("open image $src");
MaterialPageRoute( Navigator.push(
builder: (context) => PhotoViewWrapper( context,
imageUrl: message.imageUrl!, MaterialPageRoute(
imageProvider: NetworkImage( builder: (context) => PhotoViewWrapper(
src!, imageUrl: message.imageUrl!,
), imageProvider: NetworkImage(
loadingBuilder: (context, event) { src!,
if (event == null) { ),
return const Center( loadingBuilder: (context, event) {
child: Text("Loading"), if (event == null) {
return const Center(
child: Text("Loading"),
);
}
final value = event.cumulativeBytesLoaded /
event.expectedTotalBytes!;
final percentage = (100 * value).floor();
return Center(
child: Text("$percentage%"),
); );
} },
final value = event.cumulativeBytesLoaded / ),
event.expectedTotalBytes!;
final percentage = (100 * value).floor();
return Center(
child: Text("$percentage%"),
);
},
), ),
), );
); },
}, onImageError: (exception, stackTrace) {
onImageError: (exception, stackTrace) { print(exception);
print(exception); },
}, ),
), ),
Visibility( Visibility(
visible: message.answers != null && message.answers!.length > 0, visible: message.answers != null && message.answers!.length > 0,
@ -549,6 +556,88 @@ class MessageWidget extends StatelessWidget {
) )
], ],
); );
} else if (message.type == BytedeskConstants.MESSAGE_TYPE_ROBOT_V2) {
return Column(
children: <Widget>[
Text(
message.content ?? '',
textAlign: TextAlign.left,
softWrap: true,
style: TextStyle(
color: Colors.black,
fontSize: 16.0,
),
),
Visibility(
visible: message.categories != null && message.categories!.length > 0,
// visible: false,
child: Container(
// color: Colors.black,
child: ListView.builder(
// shrinkWraptrue
shrinkWrap: true,
// ListView使ScrollView
physics: const NeverScrollableScrollPhysics(),
padding: EdgeInsets.all(0),
itemCount:
message.categories == null ? 0 : message.categories!.length,
itemBuilder: (_, index) {
//
var category = message.categories![index];
// return Text(answer.question!);
return DecoratedBox(
decoration: BoxDecoration(
border: Border(
bottom: Divider.createBorderSide(context, width: 0.8),
)),
child: Container(
margin: EdgeInsets.only(top: 6, left: 8, bottom: 8),
// color: Colors.pink,
child: InkWell(
child: Text(
category.name!,
style: TextStyle(color: Colors.blue),
),
onTap: () => {
// print(category.name),
bytedeskEventBus.fire(QueryCategoryEventBus(
category.cid!,
category.name!))
}),
));
},
),
)),
Container(
margin: EdgeInsets.only(left: 10, top: 10),
child: Row(
children: [
Text('没有找到答案?'),
GestureDetector(
child: Text(
'人工客服',
style: TextStyle(color: Theme.of(context).primaryColor),
),
onTap: () {
print('请求人工客服');
bytedeskEventBus.fire(RequestAgentThreadEventBus());
},
)
],
),
)
],
);
} else if (message.type == BytedeskConstants.MESSAGE_TYPE_ROBOT_RESULT) {
return Text(
message.content ?? '',
textAlign: TextAlign.left,
softWrap: true,
style: TextStyle(
color: Colors.black,
fontSize: 16.0,
),
);
} else if (message.type == BytedeskConstants.MESSAGE_TYPE_COMMODITY) { } else if (message.type == BytedeskConstants.MESSAGE_TYPE_COMMODITY) {
// , TODO: add send button // , TODO: add send button
final commodityJson = json.decode(message.content!); final commodityJson = json.decode(message.content!);

@ -6,7 +6,7 @@ import 'package:bytedesk_kefu/ui/widget/loading_widget.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_easyrefresh/easy_refresh.dart'; import 'package:flutter_easyrefresh/easy_refresh.dart';
import 'package:flutter_easyrefresh/phoenix_header.dart'; // import 'package:flutter_easyrefresh/phoenix_header.dart';
class FeedbackPage extends StatefulWidget { class FeedbackPage extends StatefulWidget {
final String? uid; final String? uid;

@ -45,7 +45,7 @@ class BytedeskConstants {
// static const String httpBaseUrliOS = 'http://' + mqttHost + ':8000'; // static const String httpBaseUrliOS = 'http://' + mqttHost + ':8000';
// static const String httpUploadUrl = 'http://' + mqttHost + ':8000'; // static const String httpUploadUrl = 'http://' + mqttHost + ':8000';
// static const String host = mqttHost + ':8000'; // static const String host = mqttHost + ':8000';
// static const String mqttHost = '192.168.0.102'; // static const String mqttHost = '192.168.0.104';
// 线 // 线
static const bool isDebug = false; // false; static const bool isDebug = false; // false;
@ -149,6 +149,10 @@ class BytedeskConstants {
static const String MESSAGE_TYPE_EVENT = 'event'; static const String MESSAGE_TYPE_EVENT = 'event';
// //
static const String MESSAGE_TYPE_ROBOT = 'robot'; static const String MESSAGE_TYPE_ROBOT = 'robot';
//
static const String MESSAGE_TYPE_ROBOT_V2 = 'robotv2';
//
static const String MESSAGE_TYPE_ROBOT_RESULT = 'robot_result';
// //
static const String MESSAGE_TYPE_QUESTIONNAIRE = 'questionnaire'; static const String MESSAGE_TYPE_QUESTIONNAIRE = 'questionnaire';
// 便 // 便

@ -46,6 +46,12 @@ class QueryAnswerEventBus {
QueryAnswerEventBus(this.aid, this.question, this.answer); QueryAnswerEventBus(this.aid, this.question, this.answer);
} }
class QueryCategoryEventBus {
String cid;
String name;
QueryCategoryEventBus(this.cid, this.name);
}
class RequestAgentThreadEventBus { class RequestAgentThreadEventBus {
RequestAgentThreadEventBus(); RequestAgentThreadEventBus();
} }

@ -1,3 +1,5 @@
// ignore_for_file: unnecessary_null_comparison
import 'dart:io'; import 'dart:io';
import 'dart:math'; import 'dart:math';
@ -219,7 +221,7 @@ class BytedeskUtils {
} }
static String getDateStr(DateTime date) { static String getDateStr(DateTime date) {
if (date == null || date.toString() == null) { if (date.toString() == null) {
return ""; return "";
} else if (date.toString().length < 10) { } else if (date.toString().length < 10) {
return date.toString(); return date.toString();

@ -1,6 +1,6 @@
name: bytedesk_kefu name: bytedesk_kefu
description: the best app chat sdk in china, you can chat with the agent freely at anytime. the agent can chat with the visitor by web/pc/mac/ios/android client. description: the best app chat sdk in china, you can chat with the agent freely at anytime. the agent can chat with the visitor by web/pc/mac/ios/android client.
version: 1.2.6 version: 1.2.8
homepage: https://www.weikefu.net homepage: https://www.weikefu.net
environment: environment:

Loading…
Cancel
Save