Merge branch 'main' into fix
This commit is contained in:
@ -47,13 +47,14 @@
|
||||
<activity
|
||||
android:name="com.guozhigq.pilipala.MainActivity"
|
||||
android:exported="true"
|
||||
android:launchMode="singleTop"
|
||||
android:launchMode="singleTask"
|
||||
android:theme="@style/LaunchTheme"
|
||||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
||||
android:hardwareAccelerated="true"
|
||||
android:windowSoftInputMode="adjustResize"
|
||||
android:supportsPictureInPicture="true"
|
||||
android:resizeableActivity="true"
|
||||
android:autoVerify="true"
|
||||
>
|
||||
<!-- Specifies an Android theme to apply to this Activity as soon as
|
||||
the Android process has started. This theme is visible to the user
|
||||
@ -63,10 +64,21 @@
|
||||
android:name="io.flutter.embedding.android.NormalTheme"
|
||||
android:resource="@style/NormalTheme"
|
||||
/>
|
||||
<meta-data android:name="flutter_deeplinking_enabled" android:value="false" />
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
<category android:name="android.intent.category.LAUNCHER"/>
|
||||
</intent-filter>
|
||||
<!-- Deep Link -->
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<action android:name="android.intent.action.SEARCH" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<data android:scheme="pili"/>
|
||||
<data android:scheme="pilipala"/>
|
||||
</intent-filter>
|
||||
<!-- App Link -->
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<action android:name="android.intent.action.SEARCH" />
|
||||
|
||||
@ -1,4 +1,6 @@
|
||||
PODS:
|
||||
- app_links (0.0.2):
|
||||
- Flutter
|
||||
- appscheme (1.0.4):
|
||||
- Flutter
|
||||
- audio_service (0.0.1):
|
||||
@ -66,6 +68,7 @@ PODS:
|
||||
- Flutter
|
||||
|
||||
DEPENDENCIES:
|
||||
- app_links (from `.symlinks/plugins/app_links/ios`)
|
||||
- appscheme (from `.symlinks/plugins/appscheme/ios`)
|
||||
- audio_service (from `.symlinks/plugins/audio_service/ios`)
|
||||
- audio_session (from `.symlinks/plugins/audio_session/ios`)
|
||||
@ -102,6 +105,8 @@ SPEC REPOS:
|
||||
- Toast
|
||||
|
||||
EXTERNAL SOURCES:
|
||||
app_links:
|
||||
:path: ".symlinks/plugins/app_links/ios"
|
||||
appscheme:
|
||||
:path: ".symlinks/plugins/appscheme/ios"
|
||||
audio_service:
|
||||
@ -160,6 +165,7 @@ EXTERNAL SOURCES:
|
||||
:path: ".symlinks/plugins/webview_flutter_wkwebview/ios"
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
app_links: e7a6750a915a9e161c58d91bc610e8cd1d4d0ad0
|
||||
appscheme: b1c3f8862331cb20430cf9e0e4af85dbc1572ad8
|
||||
audio_service: f509d65da41b9521a61f1c404dd58651f265a567
|
||||
audio_session: 4f3e461722055d21515cf3261b64c973c062f345
|
||||
|
||||
@ -65,44 +65,29 @@
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleURLName</key>
|
||||
<string></string>
|
||||
<string>bilibili</string>
|
||||
<key>CFBundleURLSchemes</key>
|
||||
<array>
|
||||
<string>http</string>
|
||||
<string>https</string>
|
||||
</array>
|
||||
<key>CFBundleURLTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleURLName</key>
|
||||
<string></string>
|
||||
<key>CFBundleURLSchemes</key>
|
||||
<array>
|
||||
<string>m.bilibili.com</string>
|
||||
<string>bilibili.com</string>
|
||||
<string>www.bilibili.com</string>
|
||||
<string>bangumi.bilibili.com</string>
|
||||
<string>bilibili.cn</string>
|
||||
<string>www.bilibili.cn</string>
|
||||
<string>bangumi.bilibili.cn</string>
|
||||
<string>bilibili.tv</string>
|
||||
<string>www.bilibili.tv</string>
|
||||
<string>bangumi.bilibili.tv</string>
|
||||
<string>miniapp.bilibili.com</string>
|
||||
<string>live.bilibili.com</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
|
||||
<!-- 当其他应用程序或系统通过 bilibili -->
|
||||
<dict>
|
||||
<key>CFBundleURLName</key>
|
||||
<string>bilibili</string>
|
||||
<key>CFBundleURLSchemes</key>
|
||||
<array>
|
||||
<string>bilibili</string>
|
||||
<string>m.bilibili.com</string>
|
||||
<string>bilibili.com</string>
|
||||
<string>www.bilibili.com</string>
|
||||
<string>bangumi.bilibili.com</string>
|
||||
<string>bilibili.cn</string>
|
||||
<string>www.bilibili.cn</string>
|
||||
<string>bangumi.bilibili.cn</string>
|
||||
<string>bilibili.tv</string>
|
||||
<string>www.bilibili.tv</string>
|
||||
<string>bangumi.bilibili.tv</string>
|
||||
<string>miniapp.bilibili.com</string>
|
||||
<string>live.bilibili.com</string>
|
||||
<string>pili</string>
|
||||
<string>pilipala</string>
|
||||
</array>
|
||||
<key>FlutterDeepLinkingEnabled</key>
|
||||
<false/>
|
||||
</dict>
|
||||
</array>
|
||||
<key>UIBackgroundModes</key>
|
||||
|
||||
@ -217,12 +217,13 @@ class Request {
|
||||
/*
|
||||
* get请求
|
||||
*/
|
||||
get(url, {data, options, cancelToken, extra}) async {
|
||||
get(url, {data, Options? options, cancelToken, extra}) async {
|
||||
Response response;
|
||||
final Options options = Options();
|
||||
options ??= Options(); // 如果 options 为 null,则初始化一个新的 Options 对象
|
||||
ResponseType resType = ResponseType.json;
|
||||
|
||||
if (extra != null) {
|
||||
resType = extra!['resType'] ?? ResponseType.json;
|
||||
resType = extra['resType'] ?? ResponseType.json;
|
||||
if (extra['ua'] != null) {
|
||||
options.headers = {'user-agent': headerUa(type: extra['ua'])};
|
||||
}
|
||||
@ -238,14 +239,11 @@ class Request {
|
||||
);
|
||||
return response;
|
||||
} on DioException catch (e) {
|
||||
Response errResponse = Response(
|
||||
data: {
|
||||
'message': await ApiInterceptor.dioError(e)
|
||||
}, // 将自定义 Map 数据赋值给 Response 的 data 属性
|
||||
return Response(
|
||||
data: {'message': await ApiInterceptor.dioError(e)},
|
||||
statusCode: 200,
|
||||
requestOptions: RequestOptions(),
|
||||
);
|
||||
return errResponse;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -4,6 +4,7 @@ import 'package:hive/hive.dart';
|
||||
import 'package:html/parser.dart';
|
||||
import 'package:pilipala/models/member/article.dart';
|
||||
import 'package:pilipala/models/member/like.dart';
|
||||
import 'package:pilipala/utils/global_data_cache.dart';
|
||||
import '../common/constants.dart';
|
||||
import '../models/dynamics/result.dart';
|
||||
import '../models/follow/result.dart';
|
||||
@ -19,14 +20,20 @@ import 'index.dart';
|
||||
|
||||
class MemberHttp {
|
||||
static Future memberInfo({
|
||||
int? mid,
|
||||
required int mid,
|
||||
String token = '',
|
||||
}) async {
|
||||
String? wWebid;
|
||||
if ((await getWWebid(mid: mid))['status']) {
|
||||
wWebid = GlobalDataCache().wWebid;
|
||||
}
|
||||
|
||||
Map params = await WbiSign().makSign({
|
||||
'mid': mid,
|
||||
'token': token,
|
||||
'platform': 'web',
|
||||
'web_location': 1550101,
|
||||
...wWebid != null ? {'w_webid': wWebid} : {},
|
||||
});
|
||||
var res = await Request().get(
|
||||
Api.memberInfo,
|
||||
@ -566,6 +573,10 @@ class MemberHttp {
|
||||
}
|
||||
|
||||
static Future getWWebid({required int mid}) async {
|
||||
String? wWebid = GlobalDataCache().wWebid;
|
||||
if (wWebid != null) {
|
||||
return {'status': true, 'data': wWebid};
|
||||
}
|
||||
var res = await Request().get('https://space.bilibili.com/$mid/article');
|
||||
String? headContent = parse(res.data).head?.outerHtml;
|
||||
final regex = RegExp(
|
||||
@ -576,6 +587,7 @@ class MemberHttp {
|
||||
final content = match.group(1);
|
||||
String decodedString = Uri.decodeComponent(content!);
|
||||
Map<String, dynamic> map = jsonDecode(decodedString);
|
||||
GlobalDataCache().wWebid = map['access_id'];
|
||||
return {'status': true, 'data': map['access_id']};
|
||||
} else {
|
||||
return {'status': false, 'data': '请检查登录状态'};
|
||||
@ -588,25 +600,20 @@ class MemberHttp {
|
||||
static Future getMemberArticle({
|
||||
required int mid,
|
||||
required int pn,
|
||||
required String wWebid,
|
||||
String? offset,
|
||||
}) async {
|
||||
String? wWebid;
|
||||
if ((await getWWebid(mid: mid))['status']) {
|
||||
wWebid = GlobalDataCache().wWebid;
|
||||
}
|
||||
Map params = await WbiSign().makSign({
|
||||
'host_mid': mid,
|
||||
'page': pn,
|
||||
'offset': offset,
|
||||
'web_location': 333.999,
|
||||
'w_webid': wWebid,
|
||||
});
|
||||
var res = await Request().get(Api.opusList, data: {
|
||||
'host_mid': mid,
|
||||
'page': pn,
|
||||
'offset': offset,
|
||||
'web_location': 333.999,
|
||||
'w_webid': wWebid,
|
||||
'w_rid': params['w_rid'],
|
||||
'wts': params['wts'],
|
||||
...wWebid != null ? {'w_webid': wWebid} : {},
|
||||
});
|
||||
var res = await Request().get(Api.opusList, data: params);
|
||||
if (res.data['code'] == 0) {
|
||||
return {
|
||||
'status': true,
|
||||
|
||||
@ -4,9 +4,12 @@ enum FullScreenGestureMode {
|
||||
|
||||
/// 从下滑到上
|
||||
fromBottomtoTop,
|
||||
|
||||
/// 关闭手势
|
||||
none,
|
||||
}
|
||||
|
||||
extension FullScreenGestureModeExtension on FullScreenGestureMode {
|
||||
String get values => ['fromToptoBottom', 'fromBottomtoTop'][index];
|
||||
String get labels => ['从上往下滑进入全屏', '从下往上滑进入全屏'][index];
|
||||
String get values => ['fromToptoBottom', 'fromBottomtoTop', 'none'][index];
|
||||
String get labels => ['从上往下滑进入全屏', '从下往上滑进入全屏', '关闭手势'][index];
|
||||
}
|
||||
|
||||
@ -295,7 +295,7 @@ class AboutController extends GetxController {
|
||||
displayTime: const Duration(milliseconds: 500),
|
||||
).then(
|
||||
(value) => launchUrl(
|
||||
Uri.parse('https://www.123pan.com/s/9sVqVv-flu0A.html'),
|
||||
Uri.parse('https://www.123684.com/s/9sVqVv-DEZ0A'),
|
||||
mode: LaunchMode.externalApplication,
|
||||
),
|
||||
);
|
||||
@ -349,7 +349,7 @@ class AboutController extends GetxController {
|
||||
// 官网
|
||||
webSiteUrl() {
|
||||
launchUrl(
|
||||
Uri.parse('https://pilipalanet.mysxl.cn'),
|
||||
Uri.parse('https://pilipala.life'),
|
||||
mode: LaunchMode.externalApplication,
|
||||
);
|
||||
}
|
||||
|
||||
@ -49,6 +49,8 @@ class MemberController extends GetxController {
|
||||
if (res['status']) {
|
||||
memberInfo.value = res['data'];
|
||||
face.value = res['data'].face;
|
||||
} else {
|
||||
SmartDialog.showToast('用户信息请求异常:${res['msg']}');
|
||||
}
|
||||
return res;
|
||||
}
|
||||
@ -78,42 +80,10 @@ class MemberController extends GetxController {
|
||||
return;
|
||||
}
|
||||
if (attribute.value == 128) {
|
||||
blockUser();
|
||||
return;
|
||||
modifyRelation('block');
|
||||
} else {
|
||||
modifyRelation('follow');
|
||||
}
|
||||
SmartDialog.show(
|
||||
useSystem: true,
|
||||
animationType: SmartAnimationType.centerFade_otherSlide,
|
||||
builder: (BuildContext context) {
|
||||
return AlertDialog(
|
||||
title: const Text('提示'),
|
||||
content: Text(memberInfo.value.isFollowed! ? '取消关注UP主?' : '关注UP主?'),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => SmartDialog.dismiss(),
|
||||
child: Text(
|
||||
'点错了',
|
||||
style: TextStyle(color: Theme.of(context).colorScheme.outline),
|
||||
),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () async {
|
||||
await VideoHttp.relationMod(
|
||||
mid: mid,
|
||||
act: memberInfo.value.isFollowed! ? 2 : 1,
|
||||
reSrc: 11,
|
||||
);
|
||||
memberInfo.value.isFollowed = !memberInfo.value.isFollowed!;
|
||||
relationSearch();
|
||||
SmartDialog.dismiss();
|
||||
memberInfo.update((val) {});
|
||||
},
|
||||
child: const Text('确认'),
|
||||
)
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
// 关系查询
|
||||
@ -123,24 +93,15 @@ class MemberController extends GetxController {
|
||||
var res = await UserHttp.hasFollow(mid);
|
||||
if (res['status']) {
|
||||
attribute.value = res['data']['attribute'];
|
||||
switch (attribute.value) {
|
||||
case 1:
|
||||
attributeText.value = '悄悄关注';
|
||||
break;
|
||||
case 2:
|
||||
attributeText.value = '已关注';
|
||||
break;
|
||||
case 6:
|
||||
attributeText.value = '已互关';
|
||||
break;
|
||||
case 128:
|
||||
attributeText.value = '已拉黑';
|
||||
break;
|
||||
default:
|
||||
attributeText.value = '关注';
|
||||
}
|
||||
final Map<int, String> attributeTextMap = {
|
||||
1: '悄悄关注',
|
||||
2: '已关注',
|
||||
6: '已互关',
|
||||
128: '已拉黑',
|
||||
};
|
||||
attributeText.value = attributeTextMap[attribute.value] ?? '关注';
|
||||
if (res['data']['special'] == 1) {
|
||||
attributeText.value += 'SP';
|
||||
attributeText.value = '特别关注';
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -151,16 +112,37 @@ class MemberController extends GetxController {
|
||||
SmartDialog.showToast('账号未登录');
|
||||
return;
|
||||
}
|
||||
SmartDialog.show(
|
||||
useSystem: true,
|
||||
animationType: SmartAnimationType.centerFade_otherSlide,
|
||||
modifyRelation('block');
|
||||
}
|
||||
|
||||
// 合并关注/取关和拉黑逻辑
|
||||
Future modifyRelation(String actionType) async {
|
||||
if (userInfo == null) {
|
||||
SmartDialog.showToast('账号未登录');
|
||||
return;
|
||||
}
|
||||
|
||||
String contentText;
|
||||
int act;
|
||||
if (actionType == 'follow') {
|
||||
contentText = memberInfo.value.isFollowed! ? '确定取消关注UP主?' : '确定关注UP主?';
|
||||
act = memberInfo.value.isFollowed! ? 2 : 1;
|
||||
} else if (actionType == 'block') {
|
||||
contentText = attribute.value != 128 ? '确定拉黑UP主?' : '确定从黑名单移除UP主?';
|
||||
act = attribute.value != 128 ? 5 : 6;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
showDialog(
|
||||
context: Get.context!,
|
||||
builder: (BuildContext context) {
|
||||
return AlertDialog(
|
||||
title: const Text('提示'),
|
||||
content: Text(attribute.value != 128 ? '确定拉黑UP主?' : '从黑名单移除UP主'),
|
||||
content: Text(contentText),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => SmartDialog.dismiss(),
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
child: Text(
|
||||
'点错了',
|
||||
style: TextStyle(color: Theme.of(context).colorScheme.outline),
|
||||
@ -170,19 +152,26 @@ class MemberController extends GetxController {
|
||||
onPressed: () async {
|
||||
var res = await VideoHttp.relationMod(
|
||||
mid: mid,
|
||||
act: attribute.value != 128 ? 5 : 6,
|
||||
act: act,
|
||||
reSrc: 11,
|
||||
);
|
||||
SmartDialog.dismiss();
|
||||
if (res['status']) {
|
||||
attribute.value = attribute.value != 128 ? 128 : 0;
|
||||
attributeText.value = attribute.value == 128 ? '已拉黑' : '关注';
|
||||
memberInfo.value.isFollowed = false;
|
||||
if (actionType == 'follow') {
|
||||
memberInfo.value.isFollowed = !memberInfo.value.isFollowed!;
|
||||
} else if (actionType == 'block') {
|
||||
attribute.value = attribute.value != 128 ? 128 : 0;
|
||||
attributeText.value = attribute.value == 128 ? '已拉黑' : '关注';
|
||||
memberInfo.value.isFollowed = false;
|
||||
}
|
||||
relationSearch();
|
||||
if (context.mounted) {
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
memberInfo.update((val) {});
|
||||
}
|
||||
},
|
||||
child: const Text('确认'),
|
||||
child: const Text('确定'),
|
||||
)
|
||||
],
|
||||
);
|
||||
@ -228,17 +217,14 @@ class MemberController extends GetxController {
|
||||
|
||||
// 跳转查看动态
|
||||
void pushDynamicsPage() => Get.toNamed('/memberDynamics?mid=$mid');
|
||||
|
||||
// 跳转查看投稿
|
||||
void pushArchivesPage() => Get.toNamed('/memberArchive?mid=$mid');
|
||||
|
||||
// 跳转查看专栏
|
||||
void pushSeasonsPage() {}
|
||||
// 跳转查看最近投币
|
||||
void pushRecentCoinsPage() async {
|
||||
if (recentCoinsList.isNotEmpty) {}
|
||||
}
|
||||
|
||||
// 跳转查看收藏夹
|
||||
void pushfavPage() => Get.toNamed('/fav?mid=$mid');
|
||||
// 跳转图文专栏
|
||||
void pushArticlePage() => Get.toNamed('/memberArticle?mid=$mid');
|
||||
|
||||
@ -1,13 +1,13 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:pilipala/common/constants.dart';
|
||||
import 'package:pilipala/common/widgets/network_img_layer.dart';
|
||||
import 'package:pilipala/models/member/info.dart';
|
||||
import 'package:pilipala/pages/member/index.dart';
|
||||
import 'package:pilipala/utils/utils.dart';
|
||||
|
||||
import 'widgets/commen_widget.dart';
|
||||
import 'widgets/conis.dart';
|
||||
import 'widgets/like.dart';
|
||||
import 'widgets/profile.dart';
|
||||
@ -65,259 +65,233 @@ class _MemberPageState extends State<MemberPage>
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
primary: true,
|
||||
body: Column(
|
||||
children: [
|
||||
AppBar(
|
||||
title: StreamBuilder(
|
||||
stream: appbarStream.stream.distinct(),
|
||||
initialData: false,
|
||||
builder: (BuildContext context, AsyncSnapshot snapshot) {
|
||||
return AnimatedOpacity(
|
||||
opacity: snapshot.data ? 1 : 0,
|
||||
curve: Curves.easeOut,
|
||||
duration: const Duration(milliseconds: 500),
|
||||
child: Row(
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Obx(
|
||||
() => NetworkImgLayer(
|
||||
width: 35,
|
||||
height: 35,
|
||||
type: 'avatar',
|
||||
src: _memberController.face.value,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
Obx(
|
||||
() => Text(
|
||||
_memberController.memberInfo.value.name ?? '',
|
||||
style: TextStyle(
|
||||
color:
|
||||
Theme.of(context).colorScheme.onSurface,
|
||||
fontSize: 14),
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
],
|
||||
appBar: AppBar(
|
||||
title: StreamBuilder(
|
||||
stream: appbarStream.stream.distinct(),
|
||||
initialData: false,
|
||||
builder: (BuildContext context, AsyncSnapshot snapshot) {
|
||||
return AnimatedOpacity(
|
||||
opacity: snapshot.data ? 1 : 0,
|
||||
curve: Curves.easeOut,
|
||||
duration: const Duration(milliseconds: 500),
|
||||
child: Row(
|
||||
children: [
|
||||
Obx(
|
||||
() => NetworkImgLayer(
|
||||
width: 35,
|
||||
height: 35,
|
||||
type: 'avatar',
|
||||
src: _memberController.face.value,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
actions: [
|
||||
IconButton(
|
||||
onPressed: () => Get.toNamed(
|
||||
'/memberSearch?mid=$mid&uname=${_memberController.memberInfo.value.name!}'),
|
||||
icon: const Icon(Icons.search_outlined),
|
||||
),
|
||||
PopupMenuButton(
|
||||
icon: const Icon(Icons.more_vert),
|
||||
itemBuilder: (BuildContext context) => <PopupMenuEntry>[
|
||||
if (_memberController.ownerMid != _memberController.mid) ...[
|
||||
PopupMenuItem(
|
||||
onTap: () => _memberController.blockUser(),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const Icon(Icons.block, size: 19),
|
||||
const SizedBox(width: 10),
|
||||
Text(_memberController.attribute.value != 128
|
||||
? '加入黑名单'
|
||||
: '移除黑名单'),
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
PopupMenuItem(
|
||||
onTap: () => _memberController.shareUser(),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const Icon(Icons.share_outlined, size: 19),
|
||||
const SizedBox(width: 10),
|
||||
Text(_memberController.ownerMid != _memberController.mid
|
||||
? '分享UP主'
|
||||
: '分享我的主页'),
|
||||
],
|
||||
const SizedBox(width: 10),
|
||||
Obx(
|
||||
() => Text(
|
||||
_memberController.memberInfo.value.name ?? '',
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
fontSize: 14),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
actions: [
|
||||
IconButton(
|
||||
onPressed: () => Get.toNamed(
|
||||
'/memberSearch?mid=$mid&uname=${_memberController.memberInfo.value.name!}'),
|
||||
icon: const Icon(Icons.search_outlined),
|
||||
),
|
||||
Expanded(
|
||||
child: SingleChildScrollView(
|
||||
controller: _extendNestCtr,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(
|
||||
bottom: MediaQuery.of(context).padding.bottom + 20,
|
||||
),
|
||||
child: Column(
|
||||
PopupMenuButton(
|
||||
icon: const Icon(Icons.more_vert),
|
||||
itemBuilder: (BuildContext context) => <PopupMenuEntry>[
|
||||
if (_memberController.ownerMid != _memberController.mid) ...[
|
||||
PopupMenuItem(
|
||||
onTap: () => _memberController.blockUser(),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const Icon(Icons.block, size: 19),
|
||||
const SizedBox(width: 10),
|
||||
Text(_memberController.attribute.value != 128
|
||||
? '加入黑名单'
|
||||
: '移除黑名单'),
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
PopupMenuItem(
|
||||
onTap: () => _memberController.shareUser(),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
profileWidget(),
|
||||
|
||||
/// 动态链接
|
||||
Obx(
|
||||
() => ListTile(
|
||||
onTap: _memberController.pushDynamicsPage,
|
||||
title: Text(
|
||||
'${_memberController.isOwner.value ? '我' : 'Ta'}的动态'),
|
||||
trailing:
|
||||
const Icon(Icons.arrow_forward_outlined, size: 19),
|
||||
),
|
||||
),
|
||||
|
||||
/// 视频
|
||||
Obx(
|
||||
() => ListTile(
|
||||
onTap: _memberController.pushArchivesPage,
|
||||
title: Text(
|
||||
'${_memberController.isOwner.value ? '我' : 'Ta'}的投稿'),
|
||||
trailing:
|
||||
const Icon(Icons.arrow_forward_outlined, size: 19),
|
||||
),
|
||||
),
|
||||
|
||||
/// 他的收藏夹
|
||||
Obx(
|
||||
() => ListTile(
|
||||
onTap: _memberController.pushfavPage,
|
||||
title: Text(
|
||||
'${_memberController.isOwner.value ? '我' : 'Ta'}的收藏'),
|
||||
trailing:
|
||||
const Icon(Icons.arrow_forward_outlined, size: 19),
|
||||
),
|
||||
),
|
||||
|
||||
/// 专栏
|
||||
Obx(
|
||||
() => ListTile(
|
||||
onTap: _memberController.pushArticlePage,
|
||||
title: Text(
|
||||
'${_memberController.isOwner.value ? '我' : 'Ta'}的专栏'),
|
||||
trailing:
|
||||
const Icon(Icons.arrow_forward_outlined, size: 19),
|
||||
),
|
||||
),
|
||||
|
||||
/// 合集
|
||||
Obx(
|
||||
() => ListTile(
|
||||
title: Text(
|
||||
'${_memberController.isOwner.value ? '我' : 'Ta'}的合集')),
|
||||
),
|
||||
MediaQuery.removePadding(
|
||||
removeTop: true,
|
||||
removeBottom: true,
|
||||
context: context,
|
||||
child: FutureBuilder(
|
||||
future: _memberSeasonsFuture,
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.connectionState ==
|
||||
ConnectionState.done) {
|
||||
if (snapshot.data == null) {
|
||||
return const SizedBox();
|
||||
}
|
||||
if (snapshot.data['status']) {
|
||||
Map data = snapshot.data as Map;
|
||||
if (data['data'].seasonsList.isEmpty) {
|
||||
return commenWidget('用户没有设置合集');
|
||||
} else {
|
||||
return MemberSeasonsPanel(data: data['data']);
|
||||
}
|
||||
} else {
|
||||
// 请求错误
|
||||
return const SizedBox();
|
||||
}
|
||||
} else {
|
||||
return const SizedBox();
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
|
||||
/// 追番
|
||||
/// 最近投币
|
||||
Obx(
|
||||
() => _memberController.recentCoinsList.isNotEmpty
|
||||
? const ListTile(title: Text('最近投币的视频'))
|
||||
: const SizedBox(),
|
||||
),
|
||||
MediaQuery.removePadding(
|
||||
removeTop: true,
|
||||
removeBottom: true,
|
||||
context: context,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: StyleString.safeSpace,
|
||||
right: StyleString.safeSpace,
|
||||
),
|
||||
child: FutureBuilder(
|
||||
future: _memberCoinsFuture,
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.connectionState ==
|
||||
ConnectionState.done) {
|
||||
if (snapshot.data == null) {
|
||||
return const SizedBox();
|
||||
}
|
||||
if (snapshot.data['status']) {
|
||||
Map data = snapshot.data as Map;
|
||||
return MemberCoinsPanel(data: data['data']);
|
||||
} else {
|
||||
// 请求错误
|
||||
return const SizedBox();
|
||||
}
|
||||
} else {
|
||||
return const SizedBox();
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
/// 最近点赞
|
||||
Obx(
|
||||
() => _memberController.recentLikeList.isNotEmpty
|
||||
? const ListTile(title: Text('最近点赞的视频'))
|
||||
: const SizedBox(),
|
||||
),
|
||||
MediaQuery.removePadding(
|
||||
removeTop: true,
|
||||
removeBottom: true,
|
||||
context: context,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: StyleString.safeSpace,
|
||||
right: StyleString.safeSpace,
|
||||
),
|
||||
child: FutureBuilder(
|
||||
future: _memberLikeFuture,
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.connectionState ==
|
||||
ConnectionState.done) {
|
||||
if (snapshot.data == null) {
|
||||
return const SizedBox();
|
||||
}
|
||||
if (snapshot.data['status']) {
|
||||
Map data = snapshot.data as Map;
|
||||
return MemberLikePanel(data: data['data']);
|
||||
} else {
|
||||
// 请求错误
|
||||
return const SizedBox();
|
||||
}
|
||||
} else {
|
||||
return const SizedBox();
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
const Icon(Icons.share_outlined, size: 19),
|
||||
const SizedBox(width: 10),
|
||||
Text(_memberController.ownerMid != _memberController.mid
|
||||
? '分享UP主'
|
||||
: '分享我的主页'),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
],
|
||||
),
|
||||
primary: true,
|
||||
body: ListView(
|
||||
controller: _extendNestCtr,
|
||||
padding: EdgeInsets.only(
|
||||
bottom: MediaQuery.of(context).padding.bottom + 20,
|
||||
),
|
||||
children: [
|
||||
profileWidget(),
|
||||
|
||||
/// 动态链接
|
||||
Obx(
|
||||
() => ListTile(
|
||||
onTap: _memberController.pushDynamicsPage,
|
||||
title: Text('${_memberController.isOwner.value ? '我' : 'Ta'}的动态'),
|
||||
trailing: const Icon(Icons.arrow_forward_outlined, size: 19),
|
||||
),
|
||||
),
|
||||
|
||||
/// 视频
|
||||
Obx(
|
||||
() => ListTile(
|
||||
onTap: _memberController.pushArchivesPage,
|
||||
title: Text('${_memberController.isOwner.value ? '我' : 'Ta'}的投稿'),
|
||||
trailing: const Icon(Icons.arrow_forward_outlined, size: 19),
|
||||
),
|
||||
),
|
||||
|
||||
/// 他的收藏夹
|
||||
Obx(
|
||||
() => ListTile(
|
||||
onTap: _memberController.pushfavPage,
|
||||
title: Text('${_memberController.isOwner.value ? '我' : 'Ta'}的收藏'),
|
||||
trailing: const Icon(Icons.arrow_forward_outlined, size: 19),
|
||||
),
|
||||
),
|
||||
|
||||
/// 专栏
|
||||
Obx(
|
||||
() => ListTile(
|
||||
onTap: _memberController.pushArticlePage,
|
||||
title: Text('${_memberController.isOwner.value ? '我' : 'Ta'}的专栏'),
|
||||
trailing: const Icon(Icons.arrow_forward_outlined, size: 19),
|
||||
),
|
||||
),
|
||||
|
||||
/// 合集
|
||||
Obx(
|
||||
() => ListTile(
|
||||
title: Text('${_memberController.isOwner.value ? '我' : 'Ta'}的合集'),
|
||||
),
|
||||
),
|
||||
MediaQuery.removePadding(
|
||||
removeTop: true,
|
||||
removeBottom: true,
|
||||
context: context,
|
||||
child: FutureBuilder(
|
||||
future: _memberSeasonsFuture,
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.connectionState == ConnectionState.done) {
|
||||
if (snapshot.data == null) {
|
||||
return const SizedBox();
|
||||
}
|
||||
if (snapshot.data['status']) {
|
||||
Map data = snapshot.data as Map;
|
||||
if (data['data'].seasonsList.isEmpty) {
|
||||
return const CommenWidget(msg: '用户没有设置合集');
|
||||
} else {
|
||||
return MemberSeasonsPanel(data: data['data']);
|
||||
}
|
||||
} else {
|
||||
// 请求错误
|
||||
return const SizedBox();
|
||||
}
|
||||
} else {
|
||||
return const SizedBox();
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
|
||||
/// 追番
|
||||
/// 最近投币
|
||||
Obx(
|
||||
() => _memberController.recentCoinsList.isNotEmpty
|
||||
? const ListTile(title: Text('最近投币的视频'))
|
||||
: const SizedBox(),
|
||||
),
|
||||
MediaQuery.removePadding(
|
||||
removeTop: true,
|
||||
removeBottom: true,
|
||||
context: context,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: StyleString.safeSpace,
|
||||
right: StyleString.safeSpace,
|
||||
),
|
||||
child: FutureBuilder(
|
||||
future: _memberCoinsFuture,
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.connectionState == ConnectionState.done) {
|
||||
if (snapshot.data == null) {
|
||||
return const SizedBox();
|
||||
}
|
||||
if (snapshot.data['status']) {
|
||||
Map data = snapshot.data as Map;
|
||||
return MemberCoinsPanel(data: data['data']);
|
||||
} else {
|
||||
// 请求错误
|
||||
return const SizedBox();
|
||||
}
|
||||
} else {
|
||||
return const SizedBox();
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
/// 最近点赞
|
||||
Obx(
|
||||
() => _memberController.recentLikeList.isNotEmpty
|
||||
? const ListTile(title: Text('最近点赞的视频'))
|
||||
: const SizedBox(),
|
||||
),
|
||||
MediaQuery.removePadding(
|
||||
removeTop: true,
|
||||
removeBottom: true,
|
||||
context: context,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: StyleString.safeSpace,
|
||||
right: StyleString.safeSpace,
|
||||
),
|
||||
child: FutureBuilder(
|
||||
future: _memberLikeFuture,
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.connectionState == ConnectionState.done) {
|
||||
if (snapshot.data == null) {
|
||||
return const SizedBox();
|
||||
}
|
||||
if (snapshot.data['status']) {
|
||||
Map data = snapshot.data as Map;
|
||||
return MemberLikePanel(data: data['data']);
|
||||
} else {
|
||||
// 请求错误
|
||||
return const SizedBox();
|
||||
}
|
||||
} else {
|
||||
return const SizedBox();
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
@ -334,115 +308,90 @@ class _MemberPageState extends State<MemberPage>
|
||||
if (snapshot.connectionState == ConnectionState.done) {
|
||||
Map? data = snapshot.data;
|
||||
if (data != null && data['status']) {
|
||||
Rx<MemberInfoModel> memberInfo = _memberController.memberInfo;
|
||||
return Obx(
|
||||
() => Stack(
|
||||
alignment: AlignmentDirectional.center,
|
||||
() => Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
ProfilePanel(ctr: _memberController),
|
||||
const SizedBox(height: 20),
|
||||
Row(
|
||||
children: [
|
||||
ProfilePanel(ctr: _memberController),
|
||||
const SizedBox(height: 20),
|
||||
Row(
|
||||
children: [
|
||||
Flexible(
|
||||
child: Text(
|
||||
_memberController.memberInfo.value.name!,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.titleMedium!
|
||||
.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: _memberController.memberInfo.value
|
||||
.vip!.nicknameColor !=
|
||||
null
|
||||
? Color(_memberController.memberInfo
|
||||
.value.vip!.nicknameColor!)
|
||||
: null),
|
||||
)),
|
||||
const SizedBox(width: 2),
|
||||
if (_memberController.memberInfo.value.sex == '女')
|
||||
const Icon(
|
||||
FontAwesomeIcons.venus,
|
||||
size: 14,
|
||||
color: Colors.pink,
|
||||
),
|
||||
if (_memberController.memberInfo.value.sex == '男')
|
||||
const Icon(
|
||||
FontAwesomeIcons.mars,
|
||||
size: 14,
|
||||
color: Colors.blue,
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
Image.asset(
|
||||
'assets/images/lv/lv${_memberController.memberInfo.value.level}.png',
|
||||
height: 11,
|
||||
),
|
||||
const SizedBox(width: 6),
|
||||
if (_memberController
|
||||
.memberInfo.value.vip!.status ==
|
||||
1 &&
|
||||
_memberController.memberInfo.value.vip!
|
||||
.label!['img_label_uri_hans'] !=
|
||||
'') ...[
|
||||
Image.network(
|
||||
_memberController.memberInfo.value.vip!
|
||||
.label!['img_label_uri_hans'],
|
||||
height: 20,
|
||||
),
|
||||
] else if (_memberController
|
||||
.memberInfo.value.vip!.status ==
|
||||
1 &&
|
||||
_memberController.memberInfo.value.vip!
|
||||
.label!['img_label_uri_hans_static'] !=
|
||||
'') ...[
|
||||
Image.network(
|
||||
_memberController.memberInfo.value.vip!
|
||||
.label!['img_label_uri_hans_static'],
|
||||
height: 20,
|
||||
),
|
||||
]
|
||||
],
|
||||
Flexible(
|
||||
child: Text(
|
||||
memberInfo.value.name!,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.titleMedium!
|
||||
.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: memberInfo.value.vip!.nicknameColor !=
|
||||
null
|
||||
? Color(_memberController
|
||||
.memberInfo.value.vip!.nicknameColor!)
|
||||
: null),
|
||||
)),
|
||||
const SizedBox(width: 2),
|
||||
if (memberInfo.value.sex == '女')
|
||||
const Icon(
|
||||
FontAwesomeIcons.venus,
|
||||
size: 14,
|
||||
color: Colors.pink,
|
||||
),
|
||||
if (memberInfo.value.sex == '男')
|
||||
const Icon(
|
||||
FontAwesomeIcons.mars,
|
||||
size: 14,
|
||||
color: Colors.blue,
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
Image.asset(
|
||||
'assets/images/lv/lv${memberInfo.value.level}.png',
|
||||
height: 11,
|
||||
),
|
||||
if (_memberController
|
||||
.memberInfo.value.official!['title'] !=
|
||||
'') ...[
|
||||
const SizedBox(height: 6),
|
||||
Text.rich(
|
||||
maxLines: 2,
|
||||
TextSpan(
|
||||
text: _memberController
|
||||
.memberInfo.value.official!['role'] ==
|
||||
1
|
||||
? '个人认证:'
|
||||
: '企业认证:',
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
children: [
|
||||
TextSpan(
|
||||
text: _memberController
|
||||
.memberInfo.value.official!['title'],
|
||||
),
|
||||
],
|
||||
),
|
||||
softWrap: true,
|
||||
const SizedBox(width: 6),
|
||||
if (memberInfo.value.vip!.status == 1 &&
|
||||
memberInfo
|
||||
.value.vip!.label!['img_label_uri_hans'] !=
|
||||
'') ...[
|
||||
Image.network(
|
||||
memberInfo.value.vip!.label!['img_label_uri_hans'],
|
||||
height: 20,
|
||||
),
|
||||
],
|
||||
const SizedBox(height: 6),
|
||||
if (_memberController.memberInfo.value.sign != '')
|
||||
SelectableText(
|
||||
_memberController.memberInfo.value.sign!,
|
||||
] else if (memberInfo.value.vip!.status == 1 &&
|
||||
memberInfo.value.vip!
|
||||
.label!['img_label_uri_hans_static'] !=
|
||||
'') ...[
|
||||
Image.network(
|
||||
memberInfo
|
||||
.value.vip!.label!['img_label_uri_hans_static'],
|
||||
height: 20,
|
||||
),
|
||||
]
|
||||
],
|
||||
),
|
||||
if (memberInfo.value.official!['title'] != '') ...[
|
||||
const SizedBox(height: 6),
|
||||
Text(
|
||||
memberInfo.value.official!['role'] == 1
|
||||
? '个人认证:${memberInfo.value.official!['title']}'
|
||||
: '企业认证:${memberInfo.value.official!['title']}',
|
||||
maxLines: 2,
|
||||
softWrap: true,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
),
|
||||
],
|
||||
const SizedBox(height: 6),
|
||||
SelectableText(memberInfo.value.sign ?? ''),
|
||||
],
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return const SizedBox();
|
||||
return ProfilePanel(ctr: _memberController, loadingStatus: true);
|
||||
}
|
||||
} else {
|
||||
// 骨架屏
|
||||
@ -452,22 +401,4 @@ class _MemberPageState extends State<MemberPage>
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget commenWidget(msg) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
top: 20,
|
||||
bottom: 30,
|
||||
),
|
||||
child: Center(
|
||||
child: Text(
|
||||
msg,
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.labelMedium!
|
||||
.copyWith(color: Theme.of(context).colorScheme.outline),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
24
lib/pages/member/widgets/commen_widget.dart
Normal file
24
lib/pages/member/widgets/commen_widget.dart
Normal file
@ -0,0 +1,24 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class CommenWidget extends StatelessWidget {
|
||||
final String msg;
|
||||
|
||||
const CommenWidget({required this.msg, Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 20.0, horizontal: 10.0),
|
||||
child: Center(
|
||||
child: Text(
|
||||
msg,
|
||||
textAlign: TextAlign.center,
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.labelMedium!
|
||||
.copyWith(color: Theme.of(context).colorScheme.outline),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -18,253 +18,245 @@ class ProfilePanel extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
MemberInfoModel memberInfo = ctr.memberInfo.value;
|
||||
return Builder(
|
||||
builder: ((context) {
|
||||
return Padding(
|
||||
padding:
|
||||
EdgeInsets.only(top: MediaQuery.of(context).padding.top - 20),
|
||||
child: Row(
|
||||
children: [
|
||||
Hero(
|
||||
tag: ctr.heroTag!,
|
||||
child: Stack(
|
||||
children: [
|
||||
NetworkImgLayer(
|
||||
width: 90,
|
||||
height: 90,
|
||||
type: 'avatar',
|
||||
src: !loadingStatus ? memberInfo.face : ctr.face.value,
|
||||
),
|
||||
if (!loadingStatus &&
|
||||
memberInfo.liveRoom != null &&
|
||||
memberInfo.liveRoom!.liveStatus == 1)
|
||||
Positioned(
|
||||
bottom: 0,
|
||||
left: 14,
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
LiveItemModel liveItem = LiveItemModel.fromJson({
|
||||
'title': memberInfo.liveRoom!.title,
|
||||
'uname': memberInfo.name,
|
||||
'face': memberInfo.face,
|
||||
'roomid': memberInfo.liveRoom!.roomId,
|
||||
'watched_show': memberInfo.liveRoom!.watchedShow,
|
||||
});
|
||||
Get.toNamed(
|
||||
'/liveRoom?roomid=${memberInfo.liveRoom!.roomId}',
|
||||
arguments: {'liveItem': liveItem},
|
||||
);
|
||||
},
|
||||
child: Container(
|
||||
padding: const EdgeInsets.fromLTRB(6, 2, 6, 2),
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
borderRadius:
|
||||
const BorderRadius.all(Radius.circular(10)),
|
||||
),
|
||||
child: Row(children: [
|
||||
Image.asset(
|
||||
'assets/images/live.gif',
|
||||
height: 10,
|
||||
),
|
||||
Text(
|
||||
' 直播中',
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: Theme.of(context)
|
||||
.textTheme
|
||||
.labelSmall!
|
||||
.fontSize),
|
||||
)
|
||||
]),
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
final int? mid = memberInfo.mid;
|
||||
final String? name = memberInfo.name;
|
||||
|
||||
Map<String, dynamic> buildStatItem({
|
||||
required String label,
|
||||
required String value,
|
||||
required VoidCallback onTap,
|
||||
}) {
|
||||
return {
|
||||
'label': label,
|
||||
'value': value,
|
||||
'fn': onTap,
|
||||
};
|
||||
}
|
||||
|
||||
final List<Map<String, dynamic>> statList = [
|
||||
buildStatItem(
|
||||
label: '关注',
|
||||
value: !loadingStatus ? "${ctr.userStat!['following']}" : '-',
|
||||
onTap: () {
|
||||
Get.toNamed('/follow?mid=$mid&name=$name');
|
||||
},
|
||||
),
|
||||
buildStatItem(
|
||||
label: '粉丝',
|
||||
value: !loadingStatus
|
||||
? ctr.userStat!['follower'] != null
|
||||
? Utils.numFormat(ctr.userStat!['follower'])
|
||||
: '-'
|
||||
: '-',
|
||||
onTap: () {
|
||||
Get.toNamed('/fan?mid=$mid&name=$name');
|
||||
},
|
||||
),
|
||||
buildStatItem(
|
||||
label: '获赞',
|
||||
value: !loadingStatus
|
||||
? ctr.userStat!['likes'] != null
|
||||
? Utils.numFormat(ctr.userStat!['likes'])
|
||||
: '-'
|
||||
: '-',
|
||||
onTap: () {},
|
||||
),
|
||||
];
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(top: 30, left: 4),
|
||||
child: Row(
|
||||
children: [
|
||||
Hero(
|
||||
tag: ctr.heroTag!,
|
||||
child: Stack(
|
||||
children: [
|
||||
NetworkImgLayer(
|
||||
width: 90,
|
||||
height: 90,
|
||||
type: 'avatar',
|
||||
src: !loadingStatus ? memberInfo.face : ctr.face.value,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Padding(
|
||||
padding:
|
||||
const EdgeInsets.only(top: 10, left: 10, right: 10),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
children: [
|
||||
InkWell(
|
||||
onTap: () {
|
||||
Get.toNamed(
|
||||
'/follow?mid=${memberInfo.mid}&name=${memberInfo.name}');
|
||||
},
|
||||
child: Column(
|
||||
children: [
|
||||
Text(
|
||||
!loadingStatus
|
||||
? ctr.userStat!['following'].toString()
|
||||
: '-',
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.bold),
|
||||
),
|
||||
Text(
|
||||
'关注',
|
||||
style: TextStyle(
|
||||
fontSize: Theme.of(context)
|
||||
.textTheme
|
||||
.labelMedium!
|
||||
.fontSize),
|
||||
)
|
||||
],
|
||||
),
|
||||
if (!loadingStatus &&
|
||||
memberInfo.liveRoom != null &&
|
||||
memberInfo.liveRoom!.liveStatus == 1)
|
||||
Positioned(
|
||||
bottom: 0,
|
||||
left: 14,
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
LiveItemModel liveItem = LiveItemModel(
|
||||
title: memberInfo.liveRoom!.title,
|
||||
uname: memberInfo.name,
|
||||
face: memberInfo.face,
|
||||
roomId: memberInfo.liveRoom!.roomId,
|
||||
watchedShow: memberInfo.liveRoom!.watchedShow,
|
||||
);
|
||||
Get.toNamed(
|
||||
'/liveRoom?roomid=${memberInfo.liveRoom!.roomId}',
|
||||
arguments: {'liveItem': liveItem},
|
||||
);
|
||||
},
|
||||
child: Container(
|
||||
padding: const EdgeInsets.fromLTRB(6, 2, 6, 2),
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
borderRadius:
|
||||
const BorderRadius.all(Radius.circular(10)),
|
||||
),
|
||||
child: Row(children: [
|
||||
Image.asset(
|
||||
'assets/images/live.gif',
|
||||
height: 10,
|
||||
),
|
||||
InkWell(
|
||||
onTap: () {
|
||||
Get.toNamed(
|
||||
'/fan?mid=${memberInfo.mid}&name=${memberInfo.name}');
|
||||
},
|
||||
child: Column(
|
||||
children: [
|
||||
Text(
|
||||
!loadingStatus
|
||||
? ctr.userStat!['follower'] != null
|
||||
? Utils.numFormat(
|
||||
ctr.userStat!['follower'],
|
||||
)
|
||||
: '-'
|
||||
: '-',
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.bold)),
|
||||
Text(
|
||||
'粉丝',
|
||||
style: TextStyle(
|
||||
fontSize: Theme.of(context)
|
||||
.textTheme
|
||||
.labelMedium!
|
||||
.fontSize),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
Column(
|
||||
children: [
|
||||
Text(
|
||||
!loadingStatus
|
||||
? ctr.userStat!['likes'] != null
|
||||
? Utils.numFormat(
|
||||
ctr.userStat!['likes'],
|
||||
)
|
||||
: '-'
|
||||
: '-',
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.bold)),
|
||||
Text(
|
||||
'获赞',
|
||||
style: TextStyle(
|
||||
fontSize: Theme.of(context)
|
||||
.textTheme
|
||||
.labelMedium!
|
||||
.fontSize),
|
||||
)
|
||||
],
|
||||
),
|
||||
],
|
||||
Text(
|
||||
' 直播中',
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: Theme.of(context)
|
||||
.textTheme
|
||||
.labelSmall!
|
||||
.fontSize),
|
||||
)
|
||||
]),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
if (ctr.ownerMid != ctr.mid && ctr.ownerMid != -1) ...[
|
||||
Row(
|
||||
children: [
|
||||
Obx(
|
||||
() => Expanded(
|
||||
child: TextButton(
|
||||
onPressed: () => loadingStatus
|
||||
? null
|
||||
: ctr.actionRelationMod(),
|
||||
style: TextButton.styleFrom(
|
||||
foregroundColor: ctr.attribute.value == -1
|
||||
? Colors.transparent
|
||||
: ctr.attribute.value != 0
|
||||
? Theme.of(context)
|
||||
.colorScheme
|
||||
.outline
|
||||
: Theme.of(context)
|
||||
.colorScheme
|
||||
.onPrimary,
|
||||
backgroundColor: ctr.attribute.value != 0
|
||||
? Theme.of(context)
|
||||
.colorScheme
|
||||
.onInverseSurface
|
||||
: Theme.of(context)
|
||||
.colorScheme
|
||||
.primary, // 设置按钮背景色
|
||||
),
|
||||
child: Obx(() => Text(ctr.attributeText.value)),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
child: TextButton(
|
||||
onPressed: () {
|
||||
Get.toNamed(
|
||||
'/whisperDetail',
|
||||
parameters: {
|
||||
'name': memberInfo.name!,
|
||||
'face': memberInfo.face!,
|
||||
'mid': memberInfo.mid.toString(),
|
||||
'heroTag': ctr.heroTag!,
|
||||
},
|
||||
);
|
||||
},
|
||||
style: TextButton.styleFrom(
|
||||
backgroundColor: Theme.of(context)
|
||||
.colorScheme
|
||||
.onInverseSurface,
|
||||
),
|
||||
child: const Text('发消息'),
|
||||
),
|
||||
)
|
||||
],
|
||||
)
|
||||
],
|
||||
if (ctr.ownerMid == ctr.mid && ctr.ownerMid != -1) ...[
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
SmartDialog.showToast('功能开发中 💪');
|
||||
},
|
||||
style: TextButton.styleFrom(
|
||||
padding: const EdgeInsets.only(left: 80, right: 80),
|
||||
foregroundColor:
|
||||
Theme.of(context).colorScheme.onPrimary,
|
||||
backgroundColor:
|
||||
Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
child: const Text('编辑资料'),
|
||||
)
|
||||
],
|
||||
if (ctr.ownerMid == -1) ...[
|
||||
TextButton(
|
||||
onPressed: () {},
|
||||
style: TextButton.styleFrom(
|
||||
padding: const EdgeInsets.only(left: 80, right: 80),
|
||||
foregroundColor:
|
||||
Theme.of(context).colorScheme.outline,
|
||||
backgroundColor:
|
||||
Theme.of(context).colorScheme.onInverseSurface,
|
||||
),
|
||||
child: const Text('未登录'),
|
||||
)
|
||||
]
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
children: statList.map((item) {
|
||||
return buildStatColumn(
|
||||
context,
|
||||
item['label'],
|
||||
item['value'],
|
||||
item['fn'],
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
if (ctr.ownerMid != ctr.mid && ctr.ownerMid != -1)
|
||||
buildActionButtons(context, ctr, memberInfo),
|
||||
if (ctr.ownerMid == ctr.mid && ctr.ownerMid != -1)
|
||||
buildEditProfileButton(context),
|
||||
if (ctr.ownerMid == -1) buildNotLoggedInButton(context),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildStatColumn(
|
||||
BuildContext context,
|
||||
String label,
|
||||
String value,
|
||||
VoidCallback? onTap,
|
||||
) {
|
||||
return InkWell(
|
||||
onTap: onTap,
|
||||
child: Column(
|
||||
children: [
|
||||
Text(
|
||||
value,
|
||||
style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 16),
|
||||
),
|
||||
Text(
|
||||
label,
|
||||
style: TextStyle(
|
||||
fontSize: Theme.of(context).textTheme.labelMedium!.fontSize,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildActionButtons(
|
||||
BuildContext context,
|
||||
dynamic ctr,
|
||||
MemberInfoModel memberInfo,
|
||||
) {
|
||||
ColorScheme colorScheme = Theme.of(context).colorScheme;
|
||||
return Row(
|
||||
children: [
|
||||
const SizedBox(width: 20),
|
||||
Obx(
|
||||
() => Expanded(
|
||||
child: TextButton(
|
||||
onPressed: () => loadingStatus ? null : ctr.actionRelationMod(),
|
||||
style: TextButton.styleFrom(
|
||||
foregroundColor: ctr.attribute.value == -1
|
||||
? Colors.transparent
|
||||
: ctr.attribute.value != 0
|
||||
? colorScheme.outline
|
||||
: colorScheme.onPrimary,
|
||||
backgroundColor: ctr.attribute.value != 0
|
||||
? colorScheme.onInverseSurface
|
||||
: colorScheme.primary,
|
||||
),
|
||||
child: Obx(() => Text(ctr.attributeText.value)),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
child: TextButton(
|
||||
onPressed: () {
|
||||
Get.toNamed(
|
||||
'/whisperDetail',
|
||||
parameters: {
|
||||
'name': memberInfo.name!,
|
||||
'face': memberInfo.face!,
|
||||
'mid': memberInfo.mid.toString(),
|
||||
'heroTag': ctr.heroTag!,
|
||||
},
|
||||
);
|
||||
},
|
||||
style: TextButton.styleFrom(
|
||||
backgroundColor: colorScheme.onInverseSurface,
|
||||
),
|
||||
child: const Text('发消息'),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildEditProfileButton(BuildContext context) {
|
||||
return TextButton(
|
||||
onPressed: () {
|
||||
SmartDialog.showToast('功能开发中 💪');
|
||||
},
|
||||
style: TextButton.styleFrom(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 80),
|
||||
foregroundColor: Theme.of(context).colorScheme.onPrimary,
|
||||
backgroundColor: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
child: const Text('编辑资料'),
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildNotLoggedInButton(BuildContext context) {
|
||||
return TextButton(
|
||||
onPressed: () {},
|
||||
style: TextButton.styleFrom(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 80),
|
||||
foregroundColor: Theme.of(context).colorScheme.outline,
|
||||
backgroundColor: Theme.of(context).colorScheme.onInverseSurface,
|
||||
),
|
||||
child: const Text('未登录'),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -10,7 +10,6 @@ class MemberArticleController extends GetxController {
|
||||
int pn = 1;
|
||||
String? offset;
|
||||
bool hasMore = true;
|
||||
String? wWebid;
|
||||
RxBool isLoading = false.obs;
|
||||
RxList<MemberArticleItemModel> articleList = <MemberArticleItemModel>[].obs;
|
||||
|
||||
@ -20,25 +19,11 @@ class MemberArticleController extends GetxController {
|
||||
mid = int.parse(Get.parameters['mid']!);
|
||||
}
|
||||
|
||||
// 获取wWebid
|
||||
Future getWWebid() async {
|
||||
var res = await MemberHttp.getWWebid(mid: mid);
|
||||
if (res['status']) {
|
||||
wWebid = res['data'];
|
||||
} else {
|
||||
wWebid = '-1';
|
||||
SmartDialog.showToast(res['msg']);
|
||||
}
|
||||
}
|
||||
|
||||
Future getMemberArticle(type) async {
|
||||
if (isLoading.value) {
|
||||
return;
|
||||
}
|
||||
isLoading.value = true;
|
||||
if (wWebid == null) {
|
||||
await getWWebid();
|
||||
}
|
||||
if (type == 'init') {
|
||||
pn = 1;
|
||||
articleList.clear();
|
||||
@ -47,7 +32,6 @@ class MemberArticleController extends GetxController {
|
||||
mid: mid,
|
||||
pn: pn,
|
||||
offset: offset,
|
||||
wWebid: wWebid!,
|
||||
);
|
||||
if (res['status']) {
|
||||
offset = res['data'].offset;
|
||||
|
||||
@ -125,21 +125,29 @@ class LikeItem extends StatelessWidget {
|
||||
Color outline = Theme.of(context).colorScheme.outline;
|
||||
final nickNameList = item.users!.map((e) => e.nickname).take(2).toList();
|
||||
int usersLen = item.users!.length > 3 ? 3 : item.users!.length;
|
||||
final String bvid = item.item!.uri!.split('/').last;
|
||||
final Uri uri = Uri.parse(item.item!.uri!);
|
||||
final String path = uri.path;
|
||||
final String bvid = path.split('/').last;
|
||||
|
||||
/// bilibili://
|
||||
final Uri nativeUri = Uri.parse(item.item!.nativeUri!);
|
||||
final Map<String, String> queryParameters = nativeUri.queryParameters;
|
||||
final String type = item.item!.type!;
|
||||
// cid
|
||||
final String? argCid = queryParameters['cid'];
|
||||
// 页码
|
||||
final String page =
|
||||
item.item!.nativeUri!.split('page=').last.split('&').first;
|
||||
final String? page = queryParameters['page'];
|
||||
// 根评论id
|
||||
final String commentRootId =
|
||||
item.item!.nativeUri!.split('comment_root_id=').last.split('&').first;
|
||||
final String? commentRootId = queryParameters['comment_root_id'];
|
||||
// 二级评论id
|
||||
final String commentSecondaryId =
|
||||
item.item!.nativeUri!.split('comment_secondary_id=').last;
|
||||
final String? commentSecondaryId = queryParameters['comment_secondary_id'];
|
||||
|
||||
return InkWell(
|
||||
onTap: () async {
|
||||
try {
|
||||
final int cid = await SearchHttp.ab2c(bvid: bvid);
|
||||
final int cid = argCid != null
|
||||
? int.parse(argCid)
|
||||
: await SearchHttp.ab2c(bvid: bvid);
|
||||
final String heroTag = Utils.makeHeroTag(bvid);
|
||||
Get.toNamed<dynamic>(
|
||||
'/video?bvid=$bvid&cid=$cid',
|
||||
@ -148,8 +156,8 @@ class LikeItem extends StatelessWidget {
|
||||
'heroTag': heroTag,
|
||||
},
|
||||
);
|
||||
} catch (_) {
|
||||
SmartDialog.showToast('视频可能失效了');
|
||||
} catch (e) {
|
||||
SmartDialog.showToast('视频可能失效了$e');
|
||||
}
|
||||
},
|
||||
child: Stack(
|
||||
@ -222,7 +230,7 @@ class LikeItem extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 25),
|
||||
if (item.item!.type! == 'reply')
|
||||
if (type == 'reply' || type == 'danmu')
|
||||
Container(
|
||||
width: 60,
|
||||
height: 60,
|
||||
@ -234,7 +242,7 @@ class LikeItem extends StatelessWidget {
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
if (item.item!.type! == 'video')
|
||||
if (type == 'video')
|
||||
NetworkImgLayer(
|
||||
width: 60,
|
||||
height: 60,
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:pilipala/utils/global_data_cache.dart';
|
||||
|
||||
@ -22,7 +23,7 @@ class _PlayGesturePageState extends State<PlayGesturePage> {
|
||||
void initState() {
|
||||
super.initState();
|
||||
fullScreenGestureMode = setting.get(SettingBoxKey.fullScreenGestureMode,
|
||||
defaultValue: FullScreenGestureMode.values.last.index);
|
||||
defaultValue: FullScreenGestureMode.fromBottomtoTop.index);
|
||||
}
|
||||
|
||||
@override
|
||||
@ -71,6 +72,7 @@ class _PlayGesturePageState extends State<PlayGesturePage> {
|
||||
GlobalDataCache().fullScreenGestureMode.index;
|
||||
setting.put(
|
||||
SettingBoxKey.fullScreenGestureMode, fullScreenGestureMode);
|
||||
SmartDialog.showToast('设置成功');
|
||||
setState(() {});
|
||||
}
|
||||
},
|
||||
|
||||
@ -730,7 +730,7 @@ InlineSpan buildContent(
|
||||
source: '',
|
||||
dataString: matchStr,
|
||||
);
|
||||
PiliSchame.fullPathPush(scheme);
|
||||
PiliSchame.httpsScheme(scheme);
|
||||
}
|
||||
} else {
|
||||
if (appUrlSchema.startsWith('bilibili://search')) {
|
||||
|
||||
@ -204,6 +204,11 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
||||
widget.controller.brightness.value = value;
|
||||
}
|
||||
|
||||
bool isUsingFullScreenGestures(double tapPosition, double sectionWidth) {
|
||||
return fullScreenGestureMode != FullScreenGestureMode.none &&
|
||||
tapPosition < sectionWidth * 2;
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
animationController.dispose();
|
||||
@ -638,7 +643,10 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
||||
onVerticalDragUpdate: (DragUpdateDetails details) async {
|
||||
final double totalWidth = MediaQuery.sizeOf(context).width;
|
||||
final double tapPosition = details.localPosition.dx;
|
||||
final double sectionWidth = totalWidth / 3;
|
||||
final double sectionWidth =
|
||||
fullScreenGestureMode == FullScreenGestureMode.none
|
||||
? totalWidth / 2
|
||||
: totalWidth / 3;
|
||||
final double delta = details.delta.dy;
|
||||
|
||||
/// 锁定时禁用
|
||||
@ -660,12 +668,12 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
||||
_brightnessValue.value - delta / level;
|
||||
final double result = brightness.clamp(0.0, 1.0);
|
||||
setBrightness(result);
|
||||
} else if (tapPosition < sectionWidth * 2) {
|
||||
} else if (isUsingFullScreenGestures(tapPosition, sectionWidth)) {
|
||||
// 全屏
|
||||
final double dy = details.delta.dy;
|
||||
const double threshold = 7.0; // 滑动阈值
|
||||
final bool flag =
|
||||
fullScreenGestureMode != FullScreenGestureMode.values.last;
|
||||
final bool flag = fullScreenGestureMode !=
|
||||
FullScreenGestureMode.fromBottomtoTop;
|
||||
if (dy > _distance.value &&
|
||||
dy > threshold &&
|
||||
!_.controlsLock.value) {
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import 'package:app_links/app_links.dart';
|
||||
import 'package:appscheme/appscheme.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
@ -10,22 +11,29 @@ import 'url_utils.dart';
|
||||
import 'utils.dart';
|
||||
|
||||
class PiliSchame {
|
||||
static late AppLinks appLinks;
|
||||
static AppScheme appScheme = AppSchemeImpl.getInstance()!;
|
||||
static Future<void> init() async {
|
||||
///
|
||||
final SchemeEntity? value = await appScheme.getInitScheme();
|
||||
if (value != null) {
|
||||
_routePush(value);
|
||||
}
|
||||
appLinks = AppLinks();
|
||||
appLinks.uriLinkStream.listen((Uri uri) {
|
||||
final String scheme = uri.scheme;
|
||||
if (RegExp(r'^pili', caseSensitive: false).hasMatch(scheme)) {
|
||||
piliScheme(uri);
|
||||
}
|
||||
});
|
||||
|
||||
appScheme.getInitScheme().then((SchemeEntity? value) {
|
||||
if (value != null) {
|
||||
_routePush(value);
|
||||
}
|
||||
});
|
||||
|
||||
/// 完整链接进入 b23.无效
|
||||
appScheme.getLatestScheme().then((SchemeEntity? value) {
|
||||
if (value != null) {
|
||||
_routePush(value);
|
||||
}
|
||||
});
|
||||
|
||||
/// 注册从外部打开的Scheme监听信息 #
|
||||
appScheme.registerSchemeListener().listen((SchemeEntity? event) {
|
||||
if (event != null) {
|
||||
_routePush(event);
|
||||
@ -36,88 +44,11 @@ class PiliSchame {
|
||||
/// 路由跳转
|
||||
static void _routePush(value) async {
|
||||
final String scheme = value.scheme;
|
||||
final String host = value.host;
|
||||
final String path = value.path;
|
||||
if (scheme == 'bilibili') {
|
||||
switch (host) {
|
||||
case 'root':
|
||||
Navigator.popUntil(
|
||||
Get.context!, (Route<dynamic> route) => route.isFirst);
|
||||
break;
|
||||
case 'space':
|
||||
final String mid = path.split('/').last;
|
||||
Get.toNamed<dynamic>(
|
||||
'/member?mid=$mid',
|
||||
arguments: <String, dynamic>{'face': null},
|
||||
);
|
||||
break;
|
||||
case 'video':
|
||||
String pathQuery = path.split('/').last;
|
||||
final numericRegex = RegExp(r'^[0-9]+$');
|
||||
if (numericRegex.hasMatch(pathQuery)) {
|
||||
pathQuery = 'AV$pathQuery';
|
||||
}
|
||||
Map map = IdUtils.matchAvorBv(input: pathQuery);
|
||||
if (map.containsKey('AV')) {
|
||||
_videoPush(map['AV'], null);
|
||||
} else if (map.containsKey('BV')) {
|
||||
_videoPush(null, map['BV']);
|
||||
} else {
|
||||
SmartDialog.showToast('投稿匹配失败');
|
||||
}
|
||||
break;
|
||||
case 'live':
|
||||
final String roomId = path.split('/').last;
|
||||
Get.toNamed<dynamic>(
|
||||
'/liveRoom?roomid=$roomId',
|
||||
arguments: <String, String?>{'liveItem': null, 'heroTag': roomId},
|
||||
);
|
||||
break;
|
||||
case 'bangumi':
|
||||
if (path.startsWith('/season')) {
|
||||
final String seasonId = path.split('/').last;
|
||||
RoutePush.bangumiPush(int.parse(seasonId), null);
|
||||
}
|
||||
break;
|
||||
case 'opus':
|
||||
if (path.startsWith('/detail')) {
|
||||
var opusId = path.split('/').last;
|
||||
Get.toNamed('/opus', parameters: {
|
||||
'title': '',
|
||||
'id': opusId,
|
||||
'articleType': 'opus',
|
||||
});
|
||||
}
|
||||
break;
|
||||
case 'search':
|
||||
Get.toNamed('/searchResult', parameters: {'keyword': ''});
|
||||
break;
|
||||
case 'article':
|
||||
final String id = path.split('/').last.split('?').first;
|
||||
Get.toNamed(
|
||||
'/read',
|
||||
parameters: {
|
||||
'title': 'cv$id',
|
||||
'id': id,
|
||||
'dynamicType': 'read',
|
||||
},
|
||||
);
|
||||
break;
|
||||
case 'pgc':
|
||||
if (path.contains('ep')) {
|
||||
final String lastPathSegment = path.split('/').last;
|
||||
RoutePush.bangumiPush(
|
||||
null, int.parse(lastPathSegment.split('?').first));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
SmartDialog.showToast('未匹配地址,请联系开发者');
|
||||
Clipboard.setData(ClipboardData(text: value.toJson().toString()));
|
||||
break;
|
||||
}
|
||||
biliScheme(value);
|
||||
}
|
||||
if (scheme == 'https') {
|
||||
fullPathPush(value);
|
||||
httpsScheme(value);
|
||||
}
|
||||
}
|
||||
|
||||
@ -148,7 +79,7 @@ class PiliSchame {
|
||||
}
|
||||
}
|
||||
|
||||
static Future<void> fullPathPush(SchemeEntity value) async {
|
||||
static Future<void> httpsScheme(SchemeEntity value) async {
|
||||
// https://m.bilibili.com/bangumi/play/ss39708
|
||||
// https | m.bilibili.com | /bangumi/play/ss39708
|
||||
// final String scheme = value.scheme!;
|
||||
@ -281,6 +212,140 @@ class PiliSchame {
|
||||
}
|
||||
}
|
||||
|
||||
static Future<void> biliScheme(SchemeEntity value) async {
|
||||
final String host = value.host!;
|
||||
final String path = value.path!;
|
||||
switch (host) {
|
||||
case 'root':
|
||||
Navigator.popUntil(
|
||||
Get.context!, (Route<dynamic> route) => route.isFirst);
|
||||
break;
|
||||
case 'space':
|
||||
final String mid = path.split('/').last;
|
||||
Get.toNamed<dynamic>(
|
||||
'/member?mid=$mid',
|
||||
arguments: <String, dynamic>{'face': null},
|
||||
);
|
||||
break;
|
||||
case 'video':
|
||||
String pathQuery = path.split('/').last;
|
||||
final numericRegex = RegExp(r'^[0-9]+$');
|
||||
if (numericRegex.hasMatch(pathQuery)) {
|
||||
pathQuery = 'AV$pathQuery';
|
||||
}
|
||||
Map map = IdUtils.matchAvorBv(input: pathQuery);
|
||||
if (map.containsKey('AV')) {
|
||||
_videoPush(map['AV'], null);
|
||||
} else if (map.containsKey('BV')) {
|
||||
_videoPush(null, map['BV']);
|
||||
} else {
|
||||
SmartDialog.showToast('投稿匹配失败');
|
||||
}
|
||||
break;
|
||||
case 'live':
|
||||
final String roomId = path.split('/').last;
|
||||
Get.toNamed<dynamic>(
|
||||
'/liveRoom?roomid=$roomId',
|
||||
arguments: <String, String?>{'liveItem': null, 'heroTag': roomId},
|
||||
);
|
||||
break;
|
||||
case 'bangumi':
|
||||
if (path.startsWith('/season')) {
|
||||
final String seasonId = path.split('/').last;
|
||||
RoutePush.bangumiPush(int.parse(seasonId), null);
|
||||
}
|
||||
break;
|
||||
case 'opus':
|
||||
if (path.startsWith('/detail')) {
|
||||
var opusId = path.split('/').last;
|
||||
Get.toNamed('/opus', parameters: {
|
||||
'title': '',
|
||||
'id': opusId,
|
||||
'articleType': 'opus',
|
||||
});
|
||||
}
|
||||
break;
|
||||
case 'search':
|
||||
Get.toNamed('/searchResult', parameters: {'keyword': ''});
|
||||
break;
|
||||
case 'article':
|
||||
final String id = path.split('/').last.split('?').first;
|
||||
Get.toNamed(
|
||||
'/read',
|
||||
parameters: {
|
||||
'title': 'cv$id',
|
||||
'id': id,
|
||||
'dynamicType': 'read',
|
||||
},
|
||||
);
|
||||
break;
|
||||
case 'pgc':
|
||||
if (path.contains('ep')) {
|
||||
final String lastPathSegment = path.split('/').last;
|
||||
RoutePush.bangumiPush(
|
||||
null, int.parse(lastPathSegment.split('?').first));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
SmartDialog.showToast('未匹配地址,请联系开发者');
|
||||
Clipboard.setData(ClipboardData(text: value.toJson().toString()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static Future<void> piliScheme(Uri value) async {
|
||||
final String host = value.host;
|
||||
final String path = value.path;
|
||||
final String arg = path.split('/').last;
|
||||
switch (host) {
|
||||
case 'home':
|
||||
case 'root':
|
||||
Get.toNamed('/');
|
||||
break;
|
||||
case 'member':
|
||||
if (arg != '') {
|
||||
final int? mid = int.tryParse(arg);
|
||||
if (mid == null) {
|
||||
SmartDialog.showToast('用户id有误');
|
||||
return;
|
||||
}
|
||||
Get.toNamed<dynamic>(
|
||||
'/member?mid=$mid',
|
||||
arguments: <String, dynamic>{'face': null},
|
||||
);
|
||||
} else {
|
||||
Get.toNamed('/mine');
|
||||
}
|
||||
break;
|
||||
case 'search':
|
||||
if (arg != '') {
|
||||
final String encodedArg = Uri.decodeComponent(arg);
|
||||
Get.toNamed('/searchResult', parameters: {'keyword': encodedArg});
|
||||
} else {
|
||||
Get.toNamed('/search');
|
||||
}
|
||||
break;
|
||||
case 'setting':
|
||||
Get.toNamed('/setting');
|
||||
break;
|
||||
case 'fav':
|
||||
Get.toNamed('/fav');
|
||||
break;
|
||||
case 'history':
|
||||
Get.toNamed('/history');
|
||||
break;
|
||||
case 'later':
|
||||
Get.toNamed('/later');
|
||||
break;
|
||||
case 'msg':
|
||||
Get.toNamed('/whisper');
|
||||
break;
|
||||
default:
|
||||
Get.toNamed('/');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void _handleEpisodePath(String lastPathSegment, String redirectUrl) {
|
||||
final String seasonId = _extractIdFromPath(lastPathSegment);
|
||||
RoutePush.bangumiPush(null, Utils.matchNum(seasonId).first);
|
||||
|
||||
@ -15,6 +15,7 @@ class GlobalDataCache {
|
||||
late FullScreenGestureMode fullScreenGestureMode;
|
||||
late bool enablePlayerControlAnimation;
|
||||
late List<String> actionTypeSort;
|
||||
String? wWebid;
|
||||
|
||||
/// 播放器相关
|
||||
// 弹幕开关
|
||||
@ -59,7 +60,7 @@ class GlobalDataCache {
|
||||
defaultValue: 10); // 设置全局变量
|
||||
fullScreenGestureMode = FullScreenGestureMode.values[setting.get(
|
||||
SettingBoxKey.fullScreenGestureMode,
|
||||
defaultValue: FullScreenGestureMode.values.last.index) as int];
|
||||
defaultValue: FullScreenGestureMode.fromBottomtoTop.index)];
|
||||
enablePlayerControlAnimation = setting
|
||||
.get(SettingBoxKey.enablePlayerControlAnimation, defaultValue: true);
|
||||
actionTypeSort = await setting.get(SettingBoxKey.actionTypeSort,
|
||||
|
||||
@ -306,7 +306,7 @@ class Utils {
|
||||
onPressed: () async {
|
||||
await SmartDialog.dismiss();
|
||||
launchUrl(
|
||||
Uri.parse('https://www.123pan.com/s/9sVqVv-flu0A.html'),
|
||||
Uri.parse('https://www.123684.com/s/9sVqVv-DEZ0A'),
|
||||
mode: LaunchMode.externalApplication,
|
||||
);
|
||||
},
|
||||
|
||||
@ -8,6 +8,7 @@
|
||||
|
||||
#include <dynamic_color/dynamic_color_plugin.h>
|
||||
#include <flutter_volume_controller/flutter_volume_controller_plugin.h>
|
||||
#include <gtk/gtk_plugin.h>
|
||||
#include <media_kit_libs_linux/media_kit_libs_linux_plugin.h>
|
||||
#include <media_kit_video/media_kit_video_plugin.h>
|
||||
#include <url_launcher_linux/url_launcher_plugin.h>
|
||||
@ -19,6 +20,9 @@ void fl_register_plugins(FlPluginRegistry* registry) {
|
||||
g_autoptr(FlPluginRegistrar) flutter_volume_controller_registrar =
|
||||
fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterVolumeControllerPlugin");
|
||||
flutter_volume_controller_plugin_register_with_registrar(flutter_volume_controller_registrar);
|
||||
g_autoptr(FlPluginRegistrar) gtk_registrar =
|
||||
fl_plugin_registry_get_registrar_for_plugin(registry, "GtkPlugin");
|
||||
gtk_plugin_register_with_registrar(gtk_registrar);
|
||||
g_autoptr(FlPluginRegistrar) media_kit_libs_linux_registrar =
|
||||
fl_plugin_registry_get_registrar_for_plugin(registry, "MediaKitLibsLinuxPlugin");
|
||||
media_kit_libs_linux_plugin_register_with_registrar(media_kit_libs_linux_registrar);
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
list(APPEND FLUTTER_PLUGIN_LIST
|
||||
dynamic_color
|
||||
flutter_volume_controller
|
||||
gtk
|
||||
media_kit_libs_linux
|
||||
media_kit_video
|
||||
url_launcher_linux
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
import FlutterMacOS
|
||||
import Foundation
|
||||
|
||||
import app_links
|
||||
import audio_service
|
||||
import audio_session
|
||||
import connectivity_plus
|
||||
@ -22,6 +23,7 @@ import url_launcher_macos
|
||||
import wakelock_plus
|
||||
|
||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||
AppLinksMacosPlugin.register(with: registry.registrar(forPlugin: "AppLinksMacosPlugin"))
|
||||
AudioServicePlugin.register(with: registry.registrar(forPlugin: "AudioServicePlugin"))
|
||||
AudioSessionPlugin.register(with: registry.registrar(forPlugin: "AudioSessionPlugin"))
|
||||
ConnectivityPlusPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlusPlugin"))
|
||||
|
||||
40
pubspec.lock
40
pubspec.lock
@ -17,6 +17,38 @@ packages:
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "6.2.0"
|
||||
app_links:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: app_links
|
||||
sha256: ad1a6d598e7e39b46a34f746f9a8b011ee147e4c275d407fa457e7a62f84dd99
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "6.3.2"
|
||||
app_links_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: app_links_linux
|
||||
sha256: f5f7173a78609f3dfd4c2ff2c95bd559ab43c80a87dc6a095921d96c05688c81
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.0.3"
|
||||
app_links_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: app_links_platform_interface
|
||||
sha256: "05f5379577c513b534a29ddea68176a4d4802c46180ee8e2e966257158772a3f"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.0.2"
|
||||
app_links_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: app_links_web
|
||||
sha256: af060ed76183f9e2b87510a9480e56a5352b6c249778d07bd2c95fc35632a555
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.0.4"
|
||||
appscheme:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@ -686,6 +718,14 @@ packages:
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "0.1.0"
|
||||
gtk:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: gtk
|
||||
sha256: e8ce9ca4b1df106e4d72dad201d345ea1a036cc12c360f1a7d5a758f78ffa42c
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.1.0"
|
||||
hive:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
||||
@ -112,6 +112,7 @@ dependencies:
|
||||
flutter_displaymode: ^0.6.0
|
||||
# scheme跳转
|
||||
appscheme: ^1.0.8
|
||||
app_links: ^6.3.2
|
||||
# 弹幕
|
||||
ns_danmaku:
|
||||
git:
|
||||
|
||||
@ -6,6 +6,7 @@
|
||||
|
||||
#include "generated_plugin_registrant.h"
|
||||
|
||||
#include <app_links/app_links_plugin_c_api.h>
|
||||
#include <connectivity_plus/connectivity_plus_windows_plugin.h>
|
||||
#include <dynamic_color/dynamic_color_plugin_c_api.h>
|
||||
#include <flutter_volume_controller/flutter_volume_controller_plugin_c_api.h>
|
||||
@ -17,6 +18,8 @@
|
||||
#include <url_launcher_windows/url_launcher_windows.h>
|
||||
|
||||
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
||||
AppLinksPluginCApiRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("AppLinksPluginCApi"));
|
||||
ConnectivityPlusWindowsPluginRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("ConnectivityPlusWindowsPlugin"));
|
||||
DynamicColorPluginCApiRegisterWithRegistrar(
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
#
|
||||
|
||||
list(APPEND FLUTTER_PLUGIN_LIST
|
||||
app_links
|
||||
connectivity_plus
|
||||
dynamic_color
|
||||
flutter_volume_controller
|
||||
|
||||
Reference in New Issue
Block a user