mirror of
https://github.com/HChaZZY/PiliPlus.git
synced 2025-12-25 11:36:45 +08:00
feat: super chat
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
This commit is contained in:
149
lib/pages/live_room/superchat/superchat_card.dart
Normal file
149
lib/pages/live_room/superchat/superchat_card.dart
Normal file
@@ -0,0 +1,149 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
|
||||
import 'package:PiliPlus/models/common/image_type.dart';
|
||||
import 'package:PiliPlus/models_new/live/live_superchat/item.dart';
|
||||
import 'package:PiliPlus/utils/utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
class SuperChatCard extends StatefulWidget {
|
||||
const SuperChatCard({
|
||||
super.key,
|
||||
required this.item,
|
||||
required this.onRemove,
|
||||
});
|
||||
|
||||
final SuperChatItem item;
|
||||
final VoidCallback onRemove;
|
||||
|
||||
@override
|
||||
State<SuperChatCard> createState() => _SuperChatCardState();
|
||||
}
|
||||
|
||||
class _SuperChatCardState extends State<SuperChatCard> {
|
||||
Timer? _timer;
|
||||
RxInt _remains = 0.obs;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
if (widget.item.expired) {
|
||||
_onRemove();
|
||||
return;
|
||||
}
|
||||
final now = DateTime.now().millisecondsSinceEpoch ~/ 1000;
|
||||
final offset = widget.item.endTime - now;
|
||||
if (offset > 0) {
|
||||
_remains = offset.obs;
|
||||
_timer = Timer.periodic(const Duration(seconds: 1), _callback);
|
||||
} else {
|
||||
_onRemove();
|
||||
}
|
||||
}
|
||||
|
||||
void _onRemove() {
|
||||
widget
|
||||
..item.expired = true
|
||||
..onRemove();
|
||||
}
|
||||
|
||||
void _callback(_) {
|
||||
final remains = _remains.value;
|
||||
if (remains > 0) {
|
||||
_remains.value = remains - 1;
|
||||
} else {
|
||||
_cancelTimer();
|
||||
_onRemove();
|
||||
}
|
||||
}
|
||||
|
||||
void _cancelTimer() {
|
||||
_timer?.cancel();
|
||||
_timer = null;
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_cancelTimer();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final item = widget.item;
|
||||
final bottomColor = Utils.parseColor(item.backgroundBottomColor);
|
||||
final border = BorderSide(color: bottomColor);
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: const BorderRadius.only(
|
||||
topLeft: Radius.circular(8),
|
||||
topRight: Radius.circular(8),
|
||||
),
|
||||
color: Utils.parseColor(item.backgroundColor),
|
||||
border: Border(top: border, left: border, right: border),
|
||||
),
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: Row(
|
||||
spacing: 12,
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: () => Get.toNamed('/member?mid=${item.uid}'),
|
||||
child: NetworkImgLayer(
|
||||
src: item.userInfo.face,
|
||||
width: 45,
|
||||
height: 45,
|
||||
type: ImageType.avatar,
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
item.userInfo.uname,
|
||||
style: TextStyle(
|
||||
color: Utils.parseColor(item.userInfo.nameColor),
|
||||
),
|
||||
),
|
||||
Text(
|
||||
"¥${item.price}",
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: Utils.parseColor(item.backgroundPriceColor),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Obx(
|
||||
() => Text(
|
||||
_remains.toString(),
|
||||
style: const TextStyle(fontSize: 14, color: Colors.grey),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: const BorderRadius.only(
|
||||
bottomLeft: Radius.circular(8),
|
||||
bottomRight: Radius.circular(8),
|
||||
),
|
||||
color: bottomColor,
|
||||
),
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: SelectableText(
|
||||
item.message,
|
||||
style: TextStyle(color: Utils.parseColor(item.messageFontColor)),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
45
lib/pages/live_room/superchat/superchat_panel.dart
Normal file
45
lib/pages/live_room/superchat/superchat_panel.dart
Normal file
@@ -0,0 +1,45 @@
|
||||
import 'package:PiliPlus/pages/live_room/controller.dart';
|
||||
import 'package:PiliPlus/pages/live_room/superchat/superchat_card.dart';
|
||||
import 'package:PiliPlus/pages/search/controller.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get_state_manager/get_state_manager.dart';
|
||||
|
||||
class SuperChatPanel extends StatefulWidget {
|
||||
const SuperChatPanel({
|
||||
super.key,
|
||||
required this.controller,
|
||||
});
|
||||
|
||||
final LiveRoomController controller;
|
||||
|
||||
@override
|
||||
State<SuperChatPanel> createState() => _SuperChatPanelState();
|
||||
}
|
||||
|
||||
class _SuperChatPanelState extends DebounceStreamState<SuperChatPanel, bool> {
|
||||
@override
|
||||
Duration get duration => const Duration(milliseconds: 300);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Obx(
|
||||
() => ListView.separated(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12),
|
||||
physics: const ClampingScrollPhysics(),
|
||||
itemCount: widget.controller.superChatMsg.length,
|
||||
itemBuilder: (context, index) {
|
||||
final item = widget.controller.superChatMsg[index];
|
||||
return SuperChatCard(
|
||||
key: Key(item.id.toString()),
|
||||
item: item,
|
||||
onRemove: () => ctr?.add(true),
|
||||
);
|
||||
},
|
||||
separatorBuilder: (_, _) => const SizedBox(height: 12),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void onValueChanged(value) => widget.controller.clearSC();
|
||||
}
|
||||
Reference in New Issue
Block a user