feat: 动态转发

This commit is contained in:
guozhigq
2024-04-20 22:40:54 +08:00
parent 718dbdefff
commit c9bcece7b4
3 changed files with 319 additions and 7 deletions

View File

@ -511,4 +511,10 @@ class Api {
/// 取消订阅
static const String cancelSub = '/x/v3/fav/season/unfav';
/// 动态转发
static const String dynamicForwardUrl = '/x/dynamic/feed/create/submit_check';
/// 创建动态
static const String dynamicCreate = '/x/dynamic/feed/create/dyn';
}

View File

@ -1,3 +1,4 @@
import 'dart:math';
import '../models/dynamics/result.dart';
import '../models/dynamics/up.dart';
import 'index.dart';
@ -117,4 +118,82 @@ class DynamicsHttp {
};
}
}
static Future dynamicForward() async {
var res = await Request().post(
Api.dynamicForwardUrl,
queryParameters: {
'csrf': await Request.getCsrf(),
'x-bili-device-req-json': {'platform': 'web', 'device': 'pc'},
'x-bili-web-req-json': {'spm_id': '333.999'},
},
data: {
'attach_card': null,
'scene': 4,
'content': {
'conetents': [
{'raw_text': "2", 'type': 1, 'biz_id': ""}
]
}
},
);
if (res.data['code'] == 0) {
return {
'status': true,
'data': res.data['data'],
};
} else {
return {
'status': false,
'data': [],
'msg': res.data['message'],
};
}
}
static Future dynamicCreate({
required String dynIdStr,
required int mid,
String? rawText,
}) async {
DateTime now = DateTime.now();
int timestamp = now.millisecondsSinceEpoch ~/ 1000;
Random random = Random();
int randomNumber = random.nextInt(9000) + 1000;
String uploadId =
mid.toString() + timestamp.toString() + randomNumber.toString();
var res = await Request().post(Api.dynamicCreate, queryParameters: {
'platform': 'web',
'csrf': await Request.getCsrf(),
'x-bili-device-req-json': {'platform': 'web', 'device': 'pc'},
'x-bili-web-req-json': {'spm_id': '333.999'},
}, data: {
'dyn_req': {
'content': {
'contents': [
{'raw_text': rawText ?? '', 'type': 1, 'biz_id': ''}
]
},
'scene': 4,
'attach_card': null,
'upload_id': uploadId,
'meta': {
'app_meta': {'from': 'create.dynamic.web', 'mobi_app': 'web'}
}
},
'web_repost_src': {'dyn_id_str': dynIdStr}
});
if (res.data['code'] == 0) {
return {
'status': true,
'data': res.data['data'],
};
} else {
return {
'status': false,
'data': [],
'msg': res.data['message'],
};
}
}
}

View File

@ -3,10 +3,13 @@ import 'package:flutter/material.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:get/get.dart';
import 'package:pilipala/common/widgets/network_img_layer.dart';
import 'package:pilipala/http/dynamics.dart';
import 'package:pilipala/models/dynamics/result.dart';
import 'package:pilipala/pages/dynamics/index.dart';
import 'package:pilipala/utils/feed_back.dart';
import 'package:status_bar_control/status_bar_control.dart';
import 'rich_node_panel.dart';
class ActionPanel extends StatefulWidget {
const ActionPanel({
@ -20,21 +23,37 @@ class ActionPanel extends StatefulWidget {
State<ActionPanel> createState() => _ActionPanelState();
}
class _ActionPanelState extends State<ActionPanel> {
class _ActionPanelState extends State<ActionPanel>
with TickerProviderStateMixin {
final DynamicsController _dynamicsController = Get.put(DynamicsController());
late ModuleStatModel stat;
bool isProcessing = false;
RxDouble height = (310.0).obs;
RxBool isExpand = false.obs;
late double statusHeight;
TextEditingController _inputController = TextEditingController();
FocusNode myFocusNode = FocusNode();
String _inputText = '';
void Function()? handleState(Future Function() action) {
return isProcessing ? null : () async {
setState(() => isProcessing = true);
await action();
setState(() => isProcessing = false);
};
return isProcessing
? null
: () async {
isProcessing = true;
await action();
isProcessing = false;
};
}
@override
void initState() {
super.initState();
stat = widget.item!.modules.moduleStat;
onInit();
}
onInit() async {
statusHeight = await StatusBarControl.getHeight;
}
// 动态点赞
@ -67,6 +86,214 @@ class _ActionPanelState extends State<ActionPanel> {
}
}
// 转发动态预览
Widget dynamicPreview() {
return Padding(
padding: const EdgeInsets.fromLTRB(12, 0, 14, 12),
child: Container(
width: double.infinity,
decoration: BoxDecoration(
color:
Theme.of(context).colorScheme.secondaryContainer.withOpacity(0.4),
borderRadius: BorderRadius.circular(12),
),
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 14),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'@${widget.item.modules.moduleAuthor.name}',
style: TextStyle(
color: Theme.of(context).colorScheme.primary,
),
),
const SizedBox(height: 6),
Row(
children: [
NetworkImgLayer(
src: widget.item.modules.moduleAuthor.face,
width: 34,
height: 34,
type: 'emote',
),
const SizedBox(width: 10),
Expanded(
child: Text.rich(
style: const TextStyle(height: 0),
richNode(widget.item, context),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
),
// Text(data)
],
)
],
)),
),
);
}
// 动态转发
void forwardHandler() async {
showModalBottomSheet(
context: context,
enableDrag: false,
useRootNavigator: true,
isScrollControlled: true,
builder: (context) {
return Obx(
() => AnimatedContainer(
duration: Durations.medium1,
onEnd: () async {
if (isExpand.value) {
await Future.delayed(const Duration(milliseconds: 120));
myFocusNode.requestFocus();
}
},
height: height.value,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
AnimatedContainer(
duration: Durations.medium1,
height: isExpand.value ? statusHeight : 0,
),
Padding(
padding: const EdgeInsets.fromLTRB(16, 10, 12, 0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
AnimatedSwitcher(
duration: Durations.medium1,
transitionBuilder:
(Widget child, Animation<double> animation) {
return ScaleTransition(
scale: animation, child: child);
},
child: isExpand.value
? IconButton(
onPressed: () => togglePanelState(false),
icon: const Icon(Icons.close))
: const Text(
'转发动态',
style: TextStyle(fontWeight: FontWeight.bold),
),
),
AnimatedSwitcher(
duration: Durations.medium1,
transitionBuilder:
(Widget child, Animation<double> animation) {
return ScaleTransition(
scale: animation, child: child);
},
child: isExpand.value
? FilledButton(
onPressed: () => dynamicForward('forward'),
child: const Text('转发'),
)
: TextButton(
onPressed: () {},
child: const Text('立即转发'),
),
),
],
),
),
if (!isExpand.value) ...[
GestureDetector(
onTap: () => togglePanelState(true),
behavior: HitTestBehavior.translucent,
child: Container(
width: double.infinity,
alignment: Alignment.centerLeft,
padding: const EdgeInsets.fromLTRB(16, 0, 10, 15),
child: Text(
'说点什么吧',
textAlign: TextAlign.start,
style: TextStyle(
color: Theme.of(context).colorScheme.outline),
),
),
),
] else ...[
Padding(
padding: const EdgeInsets.fromLTRB(16, 10, 16, 0),
child: TextField(
maxLines: 5,
focusNode: myFocusNode,
controller: _inputController,
onChanged: (value) {
setState(() {
_inputText = value;
});
},
decoration: const InputDecoration(
border: InputBorder.none,
hintText: '说点什么吧',
),
),
),
],
dynamicPreview(),
if (!isExpand.value) ...[
const Divider(thickness: 0.1, height: 1),
ListTile(
onTap: () => Get.back(),
minLeadingWidth: 0,
dense: true,
title: Text(
'取消',
style: TextStyle(
color: Theme.of(context).colorScheme.outline),
textAlign: TextAlign.center,
),
),
SizedBox(
height: MediaQuery.of(context).padding.bottom + 20,
)
]
],
),
),
);
},
);
}
togglePanelState(status) {
if (!status) {
Get.back();
height.value = 310;
_inputText = '';
_inputController.clear();
} else {
height.value = Get.size.height;
}
isExpand.value = !(isExpand.value);
}
dynamicForward(String type) async {
String dynamicId = widget.item.idStr!;
var res = await DynamicsHttp.dynamicCreate(
dynIdStr: dynamicId,
mid: _dynamicsController.userInfo.mid,
rawText: _inputText,
);
if (res['status']) {
SmartDialog.showToast(type == 'forward' ? '转发成功' : '发布成功');
togglePanelState(false);
}
}
@override
void dispose() {
myFocusNode.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
var color = Theme.of(context).colorScheme.outline;
@ -77,7 +304,7 @@ class _ActionPanelState extends State<ActionPanel> {
Expanded(
flex: 1,
child: TextButton.icon(
onPressed: () {},
onPressed: forwardHandler,
icon: const Icon(
FontAwesomeIcons.shareFromSquare,
size: 16,