feat: like live room

Closes #963

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
This commit is contained in:
bggRGjQaUbCoE
2025-08-09 11:14:33 +08:00
parent bd490b87ca
commit 2df6c91a3d
6 changed files with 151 additions and 2 deletions

View File

@@ -945,4 +945,7 @@ class Api {
static const String expLog = '/x/member/web/exp/log'; static const String expLog = '/x/member/web/exp/log';
static const String moralLog = '/x/member/web/moral/log'; static const String moralLog = '/x/member/web/moral/log';
static const String liveLikeReport =
'${HttpString.liveBaseUrl}/xlive/app-ucenter/v1/like_info_v3/like/likeReportV3';
} }

View File

@@ -619,4 +619,29 @@ class LiveHttp {
return {'status': false, 'msg': res.data['message']}; return {'status': false, 'msg': res.data['message']};
} }
} }
static Future liveLikeReport({
required int clickTime,
required dynamic roomId,
required dynamic uid,
required dynamic anchorId,
}) async {
var res = await Request().post(
Api.liveLikeReport,
data: await WbiSign.makSign({
'click_time': clickTime,
'room_id': roomId,
'uid': uid,
'anchor_id': anchorId,
'web_location': 444.8,
'csrf': Accounts.heartbeat.csrf,
}),
options: Options(contentType: Headers.formUrlEncodedContentType),
);
if (res.data['code'] == 0) {
return {'status': true};
} else {
return {'status': false, 'msg': res.data['message']};
}
}
} }

View File

@@ -24,6 +24,7 @@ import 'package:canvas_danmaku/canvas_danmaku.dart';
import 'package:connectivity_plus/connectivity_plus.dart'; import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart'; import 'package:flutter/rendering.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
class LiveRoomController extends GetxController { class LiveRoomController extends GetxController {
@@ -256,6 +257,7 @@ class LiveRoomController extends GetxController {
@override @override
void onClose() { void onClose() {
cancelLikeTimer();
cancelLiveTimer(); cancelLiveTimer();
savedDanmaku?.clear(); savedDanmaku?.clear();
savedDanmaku = null; savedDanmaku = null;
@@ -330,4 +332,43 @@ class LiveRoomController extends GetxController {
}) })
..init(); ..init();
} }
final RxInt likeClickTime = 0.obs;
Timer? likeClickTimer;
void cancelLikeTimer() {
likeClickTimer?.cancel();
likeClickTimer = null;
}
void onLikeTapDown([_]) {
cancelLikeTimer();
likeClickTime.value++;
}
void onLikeTapUp([_]) {
likeClickTimer ??= Timer(
const Duration(milliseconds: 800),
onLike,
);
}
Future<void> onLike() async {
if (!Accounts.heartbeat.isLogin) {
likeClickTime.value = 0;
return;
}
var res = await LiveHttp.liveLikeReport(
clickTime: likeClickTime.value,
roomId: roomId,
uid: accountService.mid,
anchorId: roomInfoH5.value?.roomInfo?.uid,
);
if (res['status']) {
SmartDialog.showToast('点赞成功');
} else {
SmartDialog.showToast(res['msg']);
}
likeClickTime.value = 0;
}
} }

View File

@@ -578,6 +578,60 @@ class _LiveRoomPageState extends State<LiveRoomPage>
style: TextStyle(color: _color), style: TextStyle(color: _color),
), ),
), ),
Builder(
builder: (context) {
final theme = Theme.of(context).colorScheme;
return Material(
type: MaterialType.transparency,
child: Stack(
clipBehavior: Clip.none,
children: [
InkWell(
overlayColor: overlayColor(theme),
customBorder: const CircleBorder(),
onTapDown: _liveRoomController.onLikeTapDown,
onTapUp: _liveRoomController.onLikeTapUp,
onTapCancel: _liveRoomController.onLikeTapUp,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Icon(Icons.thumb_up_off_alt, color: _color),
),
),
Positioned(
right: -12,
top: -12,
child: Obx(() {
final likeClickTime =
_liveRoomController.likeClickTime.value;
if (likeClickTime == 0) {
return const SizedBox.shrink();
}
return AnimatedSwitcher(
duration: const Duration(milliseconds: 160),
transitionBuilder: (child, animation) {
return ScaleTransition(
scale: animation,
child: child,
);
},
child: Text(
key: ValueKey(likeClickTime),
'x$likeClickTime',
style: TextStyle(
fontSize: 16,
color: theme.brightness.isDark
? theme.primary
: theme.inversePrimary,
),
),
);
}),
),
],
),
);
},
),
IconButton( IconButton(
onPressed: () => onSendDanmaku(true), onPressed: () => onSendDanmaku(true),
icon: Icon(Icons.emoji_emotions_outlined, color: _color), icon: Icon(Icons.emoji_emotions_outlined, color: _color),
@@ -588,8 +642,33 @@ class _LiveRoomPageState extends State<LiveRoomPage>
), ),
); );
WidgetStateProperty<Color?>? overlayColor(ColorScheme theme) =>
WidgetStateProperty.resolveWith((Set<WidgetState> states) {
if (states.contains(WidgetState.selected)) {
if (states.contains(WidgetState.pressed)) {
return theme.primary.withValues(alpha: 0.1);
}
if (states.contains(WidgetState.hovered)) {
return theme.primary.withValues(alpha: 0.08);
}
if (states.contains(WidgetState.focused)) {
return theme.primary.withValues(alpha: 0.1);
}
}
if (states.contains(WidgetState.pressed)) {
return theme.onSurfaceVariant.withValues(alpha: 0.1);
}
if (states.contains(WidgetState.hovered)) {
return theme.onSurfaceVariant.withValues(alpha: 0.08);
}
if (states.contains(WidgetState.focused)) {
return theme.onSurfaceVariant.withValues(alpha: 0.1);
}
return Colors.transparent;
});
void onSendDanmaku([bool fromEmote = false]) { void onSendDanmaku([bool fromEmote = false]) {
if (!_liveRoomController.accountService.isLogin.value) { if (!_liveRoomController.isLogin) {
SmartDialog.showToast('账号未登录'); SmartDialog.showToast('账号未登录');
return; return;
} }

View File

@@ -54,7 +54,7 @@ class BottomControl extends StatelessWidget {
padding: WidgetStateProperty.all(EdgeInsets.zero), padding: WidgetStateProperty.all(EdgeInsets.zero),
), ),
onPressed: () { onPressed: () {
if (liveRoomCtr.accountService.isLogin.value) { if (liveRoomCtr.isLogin) {
Get.toNamed( Get.toNamed(
'/liveDmBlockPage', '/liveDmBlockPage',
parameters: { parameters: {

View File

@@ -29,6 +29,7 @@ class AccountManager extends Interceptor {
Api.heartBeat, Api.heartBeat,
Api.historyReport, Api.historyReport,
Api.roomEntryAction, Api.roomEntryAction,
Api.liveLikeReport,
// Api.historyList, // Api.historyList,
// Api.pauseHistory, // Api.pauseHistory,
// Api.clearHistory, // Api.clearHistory,