diff --git a/lib/constants/api.dart b/lib/constants/api.dart index 0677c55..502c322 100644 --- a/lib/constants/api.dart +++ b/lib/constants/api.dart @@ -481,4 +481,6 @@ class LiveAPI { ///直播场次数据列表 static const String liveDataList = '/v1/live/live/data/list'; + + static const String exitLive = '/v1/live/live/stop'; } diff --git a/lib/pages/live/live_stream/live_page.dart b/lib/pages/live/live_stream/live_page.dart index ac4d06c..03363b6 100644 --- a/lib/pages/live/live_stream/live_page.dart +++ b/lib/pages/live/live_stream/live_page.dart @@ -27,6 +27,7 @@ class _LivePageState extends State { TopicListModel _topicModel; TextEditingController _editingController = TextEditingController(); List pickedIds = []; + int liveItemId = 0; @override void initState() { @@ -39,6 +40,9 @@ class _LivePageState extends State { _livePusher?.stopPush(); _livePusher?.stopPreview(); _editingController?.dispose(); + HttpManager.post(LiveAPI.exitLive, { + 'liveItemId': liveItemId, + }); super.dispose(); } @@ -168,6 +172,7 @@ class _LivePageState extends State { 'goodsIds': pickedIds, }).then((resultData) { GSDialog.of(context).dismiss(context); + liveItemId = resultData.data['data']['liveItemId']; _livePusher .startPush(resultData.data['data']['pushUrl']); }); diff --git a/lib/pages/live/live_stream/live_stream_view_page.dart b/lib/pages/live/live_stream/live_stream_view_page.dart index 6cb2f8a..14b6e96 100644 --- a/lib/pages/live/live_stream/live_stream_view_page.dart +++ b/lib/pages/live/live_stream/live_stream_view_page.dart @@ -1,14 +1,20 @@ +import 'dart:math'; + import 'package:flutter/material.dart'; import 'package:many_like/many_like.dart'; import 'package:recook/constants/api.dart'; import 'package:recook/constants/header.dart'; import 'package:recook/manager/http_manager.dart'; +import 'package:recook/manager/user_manager.dart'; import 'package:recook/pages/live/models/live_stream_info_model.dart'; import 'package:recook/pages/live/tencent_im/tencent_im_tool.dart'; import 'package:recook/pages/live/widget/live_user_bar.dart'; import 'package:recook/pages/live/widget/more_people.dart'; import 'package:recook/widgets/bottom_sheet/action_sheet.dart'; import 'package:recook/widgets/custom_image_button.dart'; +import 'package:tencent_im_plugin/entity/message_entity.dart'; +import 'package:tencent_im_plugin/entity/session_entity.dart'; +import 'package:tencent_im_plugin/message_node/text_message_node.dart'; import 'package:tencent_im_plugin/tencent_im_plugin.dart'; import 'package:tencent_live_fluttify/tencent_live_fluttify.dart'; @@ -25,6 +31,10 @@ class _LiveStreamViewPageState extends State { bool _showTools = true; LivePlayer _livePlayer; LiveStreamInfoModel _streamInfoModel; + TencentGroupTool group; + List chatObjects = []; + ScrollController _scrollController = ScrollController(); + TextEditingController _editingController = TextEditingController(); @override void initState() { @@ -36,16 +46,19 @@ class _LiveStreamViewPageState extends State { //腾讯IM登陆 TencentIMTool.login().then((_) { DPrint.printLongJson('用户登陆'); - TencentImPlugin.addListener(parseMessage); - }); - - getLiveStreamModel().then((model) { - if (model == null) - Navigator.pop(context); - else - setState(() { - _streamInfoModel = model; - }); + getLiveStreamModel().then((model) { + if (model == null) + Navigator.pop(context); + else { + setState(() { + _streamInfoModel = model; + }); + TencentImPlugin.applyJoinGroup( + groupId: model.groupId, reason: 'enterLive'); + TencentImPlugin.addListener(parseMessage); + group = TencentGroupTool.fromId(model.groupId); + } + }); }); } @@ -60,15 +73,58 @@ class _LiveStreamViewPageState extends State { return LiveStreamInfoModel.fromJson(resultData.data['data']); } - parseMessage(ListenerTypeEnum type, dynamic params) { - print(type.toString()); + parseMessage(ListenerTypeEnum type, params) { + print('ListenerTypeEnum$type'); + switch (type) { + case ListenerTypeEnum.ForceOffline: + break; + case ListenerTypeEnum.UserSigExpired: + break; + case ListenerTypeEnum.Connected: + break; + case ListenerTypeEnum.Disconnected: + break; + case ListenerTypeEnum.WifiNeedAuth: + break; + case ListenerTypeEnum.Refresh: + break; + case ListenerTypeEnum.RefreshConversation: + break; + case ListenerTypeEnum.MessageRevoked: + break; + case ListenerTypeEnum.NewMessages: + chatObjects.insertAll(0, params); + _scrollController.animateTo( + -50, + duration: Duration(milliseconds: 300), + curve: Curves.easeInOutCubic, + ); + setState(() {}); + break; + case ListenerTypeEnum.GroupTips: + break; + case ListenerTypeEnum.RecvReceipt: + break; + case ListenerTypeEnum.ReConnFailed: + break; + case ListenerTypeEnum.ConnFailed: + break; + case ListenerTypeEnum.Connecting: + break; + case ListenerTypeEnum.UploadProgress: + break; + case ListenerTypeEnum.DownloadProgress: + break; + } } @override void dispose() { _livePlayer?.stopPlay(); + TencentImPlugin.quitGroup(groupId: _streamInfoModel.groupId); TencentImPlugin.removeListener(parseMessage); TencentImPlugin.logout(); + _scrollController?.dispose(); DPrint.printLongJson('用户退出'); super.dispose(); } @@ -157,7 +213,10 @@ class _LiveStreamViewPageState extends State { AnimatedPositioned( duration: Duration(milliseconds: 300), curve: Curves.easeInOutCubic, - bottom: _showTools ? 0 : -rSize(15 + 44.0), + bottom: _showTools + ? 0 + : -(rSize(15 + 44.0) + + MediaQuery.of(context).size.height / 3), left: 0, right: 0, child: Padding( @@ -168,6 +227,20 @@ class _LiveStreamViewPageState extends State { ), child: Column( children: [ + Container( + height: MediaQuery.of(context).size.height / 3, + child: ListView.builder( + reverse: true, + controller: _scrollController, + physics: BouncingScrollPhysics( + parent: AlwaysScrollableScrollPhysics()), + itemBuilder: (context, index) { + return _buildChatBox(chatObjects[index].sender, + chatObjects[index].note); + }, + itemCount: chatObjects.length, + ), + ), Row( crossAxisAlignment: CrossAxisAlignment.end, children: [ @@ -181,6 +254,30 @@ class _LiveStreamViewPageState extends State { BorderRadius.circular(rSize(16)), ), child: TextField( + controller: _editingController, + onEditingComplete: () { + TencentImPlugin.sendMessage( + sessionId: _streamInfoModel.groupId, + sessionType: SessionType.Group, + node: TextMessageNode( + content: _editingController.text), + ); + chatObjects.insert( + 0, + MessageEntity( + note: _editingController.text, + sender: UserManager + .instance.user.info.nickname, + ), + ); + _scrollController.animateTo( + -50, + duration: Duration(milliseconds: 300), + curve: Curves.easeInOutCubic, + ); + setState(() {}); + _editingController.clear(); + }, decoration: InputDecoration( isDense: true, border: InputBorder.none, @@ -198,6 +295,7 @@ class _LiveStreamViewPageState extends State { ), SizedBox(width: rSize(24)), CustomImageButton( + padding: EdgeInsets.zero, onPressed: () { ActionSheet.show( context, @@ -219,12 +317,12 @@ class _LiveStreamViewPageState extends State { height: rSize(32), ), ), - SizedBox(width: rSize(10)), - Image.asset( - R.ASSETS_LIVE_LIVE_SHARE_PNG, - width: rSize(32), - height: rSize(32), - ), + // SizedBox(width: rSize(10)), + // Image.asset( + // R.ASSETS_LIVE_LIVE_SHARE_PNG, + // width: rSize(32), + // height: rSize(32), + // ), SizedBox(width: rSize(10)), ManyLikeButton( child: Image.asset( @@ -254,4 +352,55 @@ class _LiveStreamViewPageState extends State { ), ); } + + _buildChatBox(String sender, String note) { + final Color color = Color.fromRGBO( + 180 + Random().nextInt(55), + 180 + Random().nextInt(55), + 180 + Random().nextInt(55), + 1, + ); + return Align( + alignment: Alignment.centerLeft, + child: Container( + child: Text.rich( + TextSpan(children: [ + TextSpan( + text: '$sender:', + style: TextStyle( + color: color, + fontSize: rSP(13), + ), + ), + TextSpan( + text: note, + style: TextStyle( + color: Colors.white, + fontSize: rSP(13), + ), + ), + ]), + maxLines: 5, + ), + margin: EdgeInsets.symmetric(vertical: rSize(5 / 2)), + padding: EdgeInsets.symmetric( + horizontal: rSize(10), + vertical: rSize(4), + ), + constraints: BoxConstraints( + maxWidth: rSize(200), + ), + decoration: BoxDecoration( + color: Colors.black.withOpacity(0.1), + borderRadius: BorderRadius.circular(rSize(16)), + ), + ), + ); + } +} + +class ChatObj { + String name; + String message; + ChatObj(this.name, this.message); } diff --git a/lib/pages/live/models/live_stream_info_model.dart b/lib/pages/live/models/live_stream_info_model.dart index 971c7a6..96df0dc 100644 --- a/lib/pages/live/models/live_stream_info_model.dart +++ b/lib/pages/live/models/live_stream_info_model.dart @@ -7,6 +7,7 @@ class LiveStreamInfoModel { int isFollow; int isPraise; int praise; + String groupId; List goodsLists; LiveStreamInfoModel( @@ -18,6 +19,7 @@ class LiveStreamInfoModel { this.isFollow, this.isPraise, this.praise, + this.groupId, this.goodsLists}); LiveStreamInfoModel.fromJson(Map json) { @@ -29,9 +31,10 @@ class LiveStreamInfoModel { isFollow = json['isFollow']; isPraise = json['isPraise']; praise = json['praise']; - if (json['GoodsLists'] != null) { + groupId = json['groupId']; + if (json['goodsLists'] != null) { goodsLists = new List(); - json['GoodsLists'].forEach((v) { + json['goodsLists'].forEach((v) { goodsLists.add(new GoodsLists.fromJson(v)); }); } @@ -47,8 +50,9 @@ class LiveStreamInfoModel { data['isFollow'] = this.isFollow; data['isPraise'] = this.isPraise; data['praise'] = this.praise; + data['groupId'] = this.groupId; if (this.goodsLists != null) { - data['GoodsLists'] = this.goodsLists.map((v) => v.toJson()).toList(); + data['goodsLists'] = this.goodsLists.map((v) => v.toJson()).toList(); } return data; } diff --git a/lib/pages/live/tencent_im/tencent_im_tool.dart b/lib/pages/live/tencent_im/tencent_im_tool.dart index be6ba65..faee975 100644 --- a/lib/pages/live/tencent_im/tencent_im_tool.dart +++ b/lib/pages/live/tencent_im/tencent_im_tool.dart @@ -3,6 +3,8 @@ import 'package:recook/constants/header.dart'; import 'package:recook/manager/http_manager.dart'; import 'package:recook/manager/user_manager.dart'; import 'package:recook/pages/live/models/tencent_im_user_model.dart'; +import 'package:tencent_im_plugin/entity/group_info_entity.dart'; +import 'package:tencent_im_plugin/entity/group_member_entity.dart'; import 'package:tencent_im_plugin/tencent_im_plugin.dart'; class TencentIMTool { @@ -10,7 +12,7 @@ class TencentIMTool { static int loginCount = 0; static Future login() async { if (model == null) model = await getTencentImUser(); - await TencentImPlugin.initStorage(identifier: model.identifier); + DPrint.printLongJson('${model.sign}'); await TencentImPlugin.login( identifier: model.identifier, userSig: model.sign, @@ -33,3 +35,32 @@ class TencentIMTool { return TencentIMUserModel.fromJson(resultData?.data['data']); } } + +class TencentGroupTool { + ///Group Id + String id; + + ///用户数量 + int memberLength = 0; + TencentGroupTool.fromId(this.id); + + ///申请加入直播见 + Future joinGroup() async { + await TencentImPlugin.applyJoinGroup( + groupId: this.id, + reason: 'join group', + ); + } + + ///获取直播间人数 + Future> groupMembers() async { + List result = + await TencentImPlugin.getGroupMembers(groupId: id); + memberLength = result.length; + return result; + } + + Future getGroupInfo() async { + return await TencentImPlugin.getGroupInfo(id: id); + } +} diff --git a/pubspec.lock b/pubspec.lock index a2316b3..c74c202 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -459,6 +459,13 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_txugcupload: + dependency: "direct main" + description: + name: flutter_txugcupload + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.0.1" flutter_web_plugins: dependency: transitive description: flutter diff --git a/pubspec.yaml b/pubspec.yaml index 24afdd1..b5c91d2 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -176,7 +176,8 @@ dependencies: # ref: f07d8ed25b2776e28baddae105a64664cee2922c flip_card: ^0.4.4 - + #腾讯云点播上传SDK + flutter_txugcupload: ^0.0.1 #腾讯直播 tencent_live_fluttify: ^0.2.0+1 #腾讯IM