Compare commits
28 Commits
v1.0.19.01
...
feature-ho
| Author | SHA1 | Date | |
|---|---|---|---|
| 41c40dfbc4 | |||
| 5d9ecab1b0 | |||
| 3de009ac43 | |||
| b29256f598 | |||
| e7cf472a0f | |||
| 03c59d23b8 | |||
| b6f805f0e4 | |||
| e23c2469ed | |||
| 387c799de1 | |||
| 230dd81342 | |||
| 47bdfec8c2 | |||
| 6a844da259 | |||
| 18bb58d293 | |||
| 045186b3c8 | |||
| b531599893 | |||
| 1da84508d8 | |||
| 4c44fab217 | |||
| 5c3d438a7e | |||
| 92a8efdee1 | |||
| eb1e2ca5f4 | |||
| 5b1022628c | |||
| 33f61ac0fa | |||
| 0b349e102e | |||
| 81371c5a31 | |||
| 85a59e11b9 | |||
| e24ccc16fa | |||
| e603942b5f | |||
| 0c4bad406e |
@ -224,10 +224,11 @@ class VideoHttp {
|
|||||||
// 获取投币状态
|
// 获取投币状态
|
||||||
static Future hasCoinVideo({required String bvid}) async {
|
static Future hasCoinVideo({required String bvid}) async {
|
||||||
var res = await Request().get(Api.hasCoinVideo, data: {'bvid': bvid});
|
var res = await Request().get(Api.hasCoinVideo, data: {'bvid': bvid});
|
||||||
|
print('res: $res');
|
||||||
if (res.data['code'] == 0) {
|
if (res.data['code'] == 0) {
|
||||||
return {'status': true, 'data': res.data['data']};
|
return {'status': true, 'data': res.data['data']};
|
||||||
} else {
|
} else {
|
||||||
return {'status': true, 'data': []};
|
return {'status': false, 'data': []};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -361,7 +362,7 @@ class VideoHttp {
|
|||||||
if (res.data['code'] == 0) {
|
if (res.data['code'] == 0) {
|
||||||
return {'status': true, 'data': res.data['data']};
|
return {'status': true, 'data': res.data['data']};
|
||||||
} else {
|
} else {
|
||||||
return {'status': true, 'data': []};
|
return {'status': false, 'data': []};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -377,7 +378,7 @@ class VideoHttp {
|
|||||||
if (res.data['code'] == 0) {
|
if (res.data['code'] == 0) {
|
||||||
return {'status': true, 'data': res.data['data']};
|
return {'status': true, 'data': res.data['data']};
|
||||||
} else {
|
} else {
|
||||||
return {'status': true, 'data': []};
|
return {'status': false, 'data': []};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -433,6 +434,8 @@ class VideoHttp {
|
|||||||
});
|
});
|
||||||
if (res.data['code'] == 0) {
|
if (res.data['code'] == 0) {
|
||||||
return {'status': true, 'data': res.data['data']};
|
return {'status': true, 'data': res.data['data']};
|
||||||
|
} else {
|
||||||
|
return {'status': false, 'data': null, 'msg': res.data['message']};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -453,10 +456,7 @@ class VideoHttp {
|
|||||||
'data': AiConclusionModel.fromJson(res.data['data']),
|
'data': AiConclusionModel.fromJson(res.data['data']),
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
return {
|
return {'status': false, 'data': []};
|
||||||
'status': false,
|
|
||||||
'data': []
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
9
lib/models/common/dynamic_badge_mode.dart
Normal file
9
lib/models/common/dynamic_badge_mode.dart
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
enum DynamicBadgeMode { hidden, point, number }
|
||||||
|
|
||||||
|
extension DynamicBadgeModeDesc on DynamicBadgeMode {
|
||||||
|
String get description => ['隐藏', '红点', '数字'][index];
|
||||||
|
}
|
||||||
|
|
||||||
|
extension DynamicBadgeModeCode on DynamicBadgeMode {
|
||||||
|
int get code => [0, 1, 2][index];
|
||||||
|
}
|
||||||
@ -17,8 +17,9 @@ class LatestDataModel {
|
|||||||
url = json['url'];
|
url = json['url'];
|
||||||
tagName = json['tag_name'];
|
tagName = json['tag_name'];
|
||||||
createdAt = json['created_at'];
|
createdAt = json['created_at'];
|
||||||
assets =
|
assets = json['assets'] != null
|
||||||
json['assets'].map<AssetItem>((e) => AssetItem.fromJson(e)).toList();
|
? json['assets'].map<AssetItem>((e) => AssetItem.fromJson(e)).toList()
|
||||||
|
: [];
|
||||||
body = json['body'];
|
body = json['body'];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,14 +1,8 @@
|
|||||||
import 'package:hive/hive.dart';
|
|
||||||
|
|
||||||
part 'hot.g.dart';
|
|
||||||
|
|
||||||
@HiveType(typeId: 6)
|
|
||||||
class HotSearchModel {
|
class HotSearchModel {
|
||||||
HotSearchModel({
|
HotSearchModel({
|
||||||
this.list,
|
this.list,
|
||||||
});
|
});
|
||||||
|
|
||||||
@HiveField(0)
|
|
||||||
List<HotSearchItem>? list;
|
List<HotSearchItem>? list;
|
||||||
|
|
||||||
HotSearchModel.fromJson(Map<String, dynamic> json) {
|
HotSearchModel.fromJson(Map<String, dynamic> json) {
|
||||||
@ -18,7 +12,6 @@ class HotSearchModel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@HiveType(typeId: 7)
|
|
||||||
class HotSearchItem {
|
class HotSearchItem {
|
||||||
HotSearchItem({
|
HotSearchItem({
|
||||||
this.keyword,
|
this.keyword,
|
||||||
@ -27,20 +20,19 @@ class HotSearchItem {
|
|||||||
this.icon,
|
this.icon,
|
||||||
});
|
});
|
||||||
|
|
||||||
@HiveField(0)
|
|
||||||
String? keyword;
|
String? keyword;
|
||||||
@HiveField(1)
|
|
||||||
String? showName;
|
String? showName;
|
||||||
// 4/5热 11话题 8普通 7直播
|
// 4/5热 11话题 8普通 7直播
|
||||||
@HiveField(2)
|
|
||||||
int? wordType;
|
int? wordType;
|
||||||
@HiveField(3)
|
|
||||||
String? icon;
|
String? icon;
|
||||||
|
List? liveId;
|
||||||
|
|
||||||
HotSearchItem.fromJson(Map<String, dynamic> json) {
|
HotSearchItem.fromJson(Map<String, dynamic> json) {
|
||||||
keyword = json['keyword'];
|
keyword = json['keyword'];
|
||||||
showName = json['show_name'];
|
showName = json['show_name'];
|
||||||
wordType = json['word_type'];
|
wordType = json['word_type'];
|
||||||
icon = json['icon'];
|
icon = json['icon'];
|
||||||
|
liveId = json['live_id'];
|
||||||
|
liveId = json['live_id'];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,84 +0,0 @@
|
|||||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
|
||||||
|
|
||||||
part of 'hot.dart';
|
|
||||||
|
|
||||||
// **************************************************************************
|
|
||||||
// TypeAdapterGenerator
|
|
||||||
// **************************************************************************
|
|
||||||
|
|
||||||
class HotSearchModelAdapter extends TypeAdapter<HotSearchModel> {
|
|
||||||
@override
|
|
||||||
final int typeId = 6;
|
|
||||||
|
|
||||||
@override
|
|
||||||
HotSearchModel read(BinaryReader reader) {
|
|
||||||
final numOfFields = reader.readByte();
|
|
||||||
final fields = <int, dynamic>{
|
|
||||||
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
|
|
||||||
};
|
|
||||||
return HotSearchModel(
|
|
||||||
list: (fields[0] as List?)?.cast<HotSearchItem>(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void write(BinaryWriter writer, HotSearchModel obj) {
|
|
||||||
writer
|
|
||||||
..writeByte(1)
|
|
||||||
..writeByte(0)
|
|
||||||
..write(obj.list);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
int get hashCode => typeId.hashCode;
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) =>
|
|
||||||
identical(this, other) ||
|
|
||||||
other is HotSearchModelAdapter &&
|
|
||||||
runtimeType == other.runtimeType &&
|
|
||||||
typeId == other.typeId;
|
|
||||||
}
|
|
||||||
|
|
||||||
class HotSearchItemAdapter extends TypeAdapter<HotSearchItem> {
|
|
||||||
@override
|
|
||||||
final int typeId = 7;
|
|
||||||
|
|
||||||
@override
|
|
||||||
HotSearchItem read(BinaryReader reader) {
|
|
||||||
final numOfFields = reader.readByte();
|
|
||||||
final fields = <int, dynamic>{
|
|
||||||
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
|
|
||||||
};
|
|
||||||
return HotSearchItem(
|
|
||||||
keyword: fields[0] as String?,
|
|
||||||
showName: fields[1] as String?,
|
|
||||||
wordType: fields[2] as int?,
|
|
||||||
icon: fields[3] as String?,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void write(BinaryWriter writer, HotSearchItem obj) {
|
|
||||||
writer
|
|
||||||
..writeByte(4)
|
|
||||||
..writeByte(0)
|
|
||||||
..write(obj.keyword)
|
|
||||||
..writeByte(1)
|
|
||||||
..write(obj.showName)
|
|
||||||
..writeByte(2)
|
|
||||||
..write(obj.wordType)
|
|
||||||
..writeByte(3)
|
|
||||||
..write(obj.icon);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
int get hashCode => typeId.hashCode;
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) =>
|
|
||||||
identical(this, other) ||
|
|
||||||
other is HotSearchItemAdapter &&
|
|
||||||
runtimeType == other.runtimeType &&
|
|
||||||
typeId == other.typeId;
|
|
||||||
}
|
|
||||||
@ -11,6 +11,7 @@ import 'package:pilipala/pages/home/view.dart';
|
|||||||
import 'package:pilipala/pages/media/index.dart';
|
import 'package:pilipala/pages/media/index.dart';
|
||||||
import 'package:pilipala/utils/storage.dart';
|
import 'package:pilipala/utils/storage.dart';
|
||||||
import 'package:pilipala/utils/utils.dart';
|
import 'package:pilipala/utils/utils.dart';
|
||||||
|
import '../../models/common/dynamic_badge_mode.dart';
|
||||||
|
|
||||||
class MainController extends GetxController {
|
class MainController extends GetxController {
|
||||||
List<Widget> pages = <Widget>[
|
List<Widget> pages = <Widget>[
|
||||||
@ -65,6 +66,7 @@ class MainController extends GetxController {
|
|||||||
int selectedIndex = 0;
|
int selectedIndex = 0;
|
||||||
Box userInfoCache = GStrorage.userInfo;
|
Box userInfoCache = GStrorage.userInfo;
|
||||||
RxBool userLogin = false.obs;
|
RxBool userLogin = false.obs;
|
||||||
|
late Rx<DynamicBadgeMode> dynamicBadgeType = DynamicBadgeMode.number.obs;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onInit() {
|
void onInit() {
|
||||||
@ -75,7 +77,12 @@ class MainController extends GetxController {
|
|||||||
hideTabBar = setting.get(SettingBoxKey.hideTabBar, defaultValue: true);
|
hideTabBar = setting.get(SettingBoxKey.hideTabBar, defaultValue: true);
|
||||||
var userInfo = userInfoCache.get('userInfoCache');
|
var userInfo = userInfoCache.get('userInfoCache');
|
||||||
userLogin.value = userInfo != null;
|
userLogin.value = userInfo != null;
|
||||||
getUnreadDynamic();
|
dynamicBadgeType.value = DynamicBadgeMode.values[setting.get(
|
||||||
|
SettingBoxKey.dynamicBadgeMode,
|
||||||
|
defaultValue: DynamicBadgeMode.number.code)];
|
||||||
|
if (dynamicBadgeType.value != DynamicBadgeMode.hidden) {
|
||||||
|
getUnreadDynamic();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void onBackPressed(BuildContext context) {
|
void onBackPressed(BuildContext context) {
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import 'dart:async';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
|
import 'package:pilipala/models/common/dynamic_badge_mode.dart';
|
||||||
import 'package:pilipala/pages/dynamics/index.dart';
|
import 'package:pilipala/pages/dynamics/index.dart';
|
||||||
import 'package:pilipala/pages/home/index.dart';
|
import 'package:pilipala/pages/home/index.dart';
|
||||||
import 'package:pilipala/pages/media/index.dart';
|
import 'package:pilipala/pages/media/index.dart';
|
||||||
@ -127,11 +128,21 @@ class _MainAppState extends State<MainApp> with SingleTickerProviderStateMixin {
|
|||||||
destinations: <Widget>[
|
destinations: <Widget>[
|
||||||
..._mainController.navigationBars.map((e) {
|
..._mainController.navigationBars.map((e) {
|
||||||
return NavigationDestination(
|
return NavigationDestination(
|
||||||
icon: Badge(
|
icon: Obx(
|
||||||
label: Text(e['count'].toString()),
|
() => Badge(
|
||||||
padding: const EdgeInsets.fromLTRB(6, 0, 6, 0),
|
label:
|
||||||
isLabelVisible: e['count'] > 0,
|
_mainController.dynamicBadgeType.value ==
|
||||||
child: e['icon'],
|
DynamicBadgeMode.number
|
||||||
|
? Text(e['count'].toString())
|
||||||
|
: null,
|
||||||
|
padding:
|
||||||
|
const EdgeInsets.fromLTRB(6, 0, 6, 0),
|
||||||
|
isLabelVisible:
|
||||||
|
_mainController.dynamicBadgeType.value !=
|
||||||
|
DynamicBadgeMode.hidden &&
|
||||||
|
e['count'] > 0,
|
||||||
|
child: e['icon'],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
selectedIcon: e['selectIcon'],
|
selectedIcon: e['selectIcon'],
|
||||||
label: e['label'],
|
label: e['label'],
|
||||||
@ -148,11 +159,21 @@ class _MainAppState extends State<MainApp> with SingleTickerProviderStateMixin {
|
|||||||
items: [
|
items: [
|
||||||
..._mainController.navigationBars.map((e) {
|
..._mainController.navigationBars.map((e) {
|
||||||
return BottomNavigationBarItem(
|
return BottomNavigationBarItem(
|
||||||
icon: Badge(
|
icon: Obx(
|
||||||
label: Text(e['count'].toString()),
|
() => Badge(
|
||||||
padding: const EdgeInsets.fromLTRB(6, 0, 6, 0),
|
label:
|
||||||
isLabelVisible: e['count'] > 0,
|
_mainController.dynamicBadgeType.value ==
|
||||||
child: e['icon'],
|
DynamicBadgeMode.number
|
||||||
|
? Text(e['count'].toString())
|
||||||
|
: null,
|
||||||
|
padding:
|
||||||
|
const EdgeInsets.fromLTRB(6, 0, 6, 0),
|
||||||
|
isLabelVisible:
|
||||||
|
_mainController.dynamicBadgeType.value !=
|
||||||
|
DynamicBadgeMode.hidden &&
|
||||||
|
e['count'] > 0,
|
||||||
|
child: e['icon'],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
activeIcon: e['selectIcon'],
|
activeIcon: e['selectIcon'],
|
||||||
label: e['label'],
|
label: e['label'],
|
||||||
|
|||||||
@ -64,7 +64,7 @@ class _MinePageState extends State<MinePage> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
IconButton(
|
IconButton(
|
||||||
onPressed: () => Get.toNamed('/setting'),
|
onPressed: () => Get.toNamed('/setting', preventDuplicates: false),
|
||||||
icon: const Icon(
|
icon: const Icon(
|
||||||
CupertinoIcons.slider_horizontal_3,
|
CupertinoIcons.slider_horizontal_3,
|
||||||
),
|
),
|
||||||
|
|||||||
@ -29,7 +29,7 @@ class SSearchController extends GetxController {
|
|||||||
// 其他页面跳转过来
|
// 其他页面跳转过来
|
||||||
if (Get.parameters.keys.isNotEmpty) {
|
if (Get.parameters.keys.isNotEmpty) {
|
||||||
if (Get.parameters['keyword'] != null) {
|
if (Get.parameters['keyword'] != null) {
|
||||||
onClickKeyword(Get.parameters['keyword']!);
|
onClickKeyword(Get.parameters['keyword']!, null);
|
||||||
}
|
}
|
||||||
if (Get.parameters['hintText'] != null) {
|
if (Get.parameters['hintText'] != null) {
|
||||||
hintText = Get.parameters['hintText']!;
|
hintText = Get.parameters['hintText']!;
|
||||||
@ -88,7 +88,12 @@ class SSearchController extends GetxController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 点击热搜关键词
|
// 点击热搜关键词
|
||||||
void onClickKeyword(String keyword) {
|
void onClickKeyword(String keyword, item) {
|
||||||
|
if (item != null && item.wordType == 7) {
|
||||||
|
Get.toNamed('/liveRoom?roomid=${item.liveId.first}',
|
||||||
|
arguments: {'liveItem': null, 'heroTag': '${item.liveId.first}'});
|
||||||
|
return;
|
||||||
|
}
|
||||||
searchKeyWord.value = keyword;
|
searchKeyWord.value = keyword;
|
||||||
controller.value.text = keyword;
|
controller.value.text = keyword;
|
||||||
// 移动光标
|
// 移动光标
|
||||||
|
|||||||
@ -115,8 +115,8 @@ class _SearchPageState extends State<SearchPage> with RouteAware {
|
|||||||
customBorder: RoundedRectangleBorder(
|
customBorder: RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.circular(4),
|
borderRadius: BorderRadius.circular(4),
|
||||||
),
|
),
|
||||||
onTap: () => ssCtr
|
onTap: () => ssCtr.onClickKeyword(
|
||||||
.onClickKeyword(ssCtr.searchSuggestList[index].term!),
|
ssCtr.searchSuggestList[index].term!, null),
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.only(left: 20, top: 9, bottom: 9),
|
padding: const EdgeInsets.only(left: 20, top: 9, bottom: 9),
|
||||||
child: ssCtr.searchSuggestList[index].textRich,
|
child: ssCtr.searchSuggestList[index].textRich,
|
||||||
@ -178,11 +178,11 @@ class _SearchPageState extends State<SearchPage> with RouteAware {
|
|||||||
width: width,
|
width: width,
|
||||||
// ignore: invalid_use_of_protected_member
|
// ignore: invalid_use_of_protected_member
|
||||||
hotSearchList: _searchController.hotSearchList.value,
|
hotSearchList: _searchController.hotSearchList.value,
|
||||||
onClick: (keyword) async {
|
onClick: (keyword, item) async {
|
||||||
_searchController.searchFocusNode.unfocus();
|
_searchController.searchFocusNode.unfocus();
|
||||||
await Future.delayed(
|
await Future.delayed(
|
||||||
const Duration(milliseconds: 150));
|
const Duration(milliseconds: 150));
|
||||||
_searchController.onClickKeyword(keyword);
|
_searchController.onClickKeyword(keyword, item);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@ -193,15 +193,7 @@ class _SearchPageState extends State<SearchPage> with RouteAware {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// 缓存数据
|
return const SizedBox();
|
||||||
if (_searchController.hotSearchList.isNotEmpty) {
|
|
||||||
return HotKeyword(
|
|
||||||
width: width,
|
|
||||||
hotSearchList: _searchController.hotSearchList,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return const SizedBox();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|||||||
@ -26,7 +26,7 @@ class HotKeyword extends StatelessWidget {
|
|||||||
borderRadius: BorderRadius.circular(3),
|
borderRadius: BorderRadius.circular(3),
|
||||||
clipBehavior: Clip.hardEdge,
|
clipBehavior: Clip.hardEdge,
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
onTap: () => onClick!(i.keyword),
|
onTap: () => onClick!(i.keyword, i),
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: EdgeInsets.only(
|
padding: EdgeInsets.only(
|
||||||
left: 2,
|
left: 2,
|
||||||
|
|||||||
@ -7,6 +7,9 @@ import 'package:pilipala/models/common/theme_type.dart';
|
|||||||
import 'package:pilipala/utils/feed_back.dart';
|
import 'package:pilipala/utils/feed_back.dart';
|
||||||
import 'package:pilipala/utils/login.dart';
|
import 'package:pilipala/utils/login.dart';
|
||||||
import 'package:pilipala/utils/storage.dart';
|
import 'package:pilipala/utils/storage.dart';
|
||||||
|
import '../../models/common/dynamic_badge_mode.dart';
|
||||||
|
import '../main/index.dart';
|
||||||
|
import 'widgets/select_dialog.dart';
|
||||||
|
|
||||||
class SettingController extends GetxController {
|
class SettingController extends GetxController {
|
||||||
Box userInfoCache = GStrorage.userInfo;
|
Box userInfoCache = GStrorage.userInfo;
|
||||||
@ -19,6 +22,7 @@ class SettingController extends GetxController {
|
|||||||
RxInt picQuality = 10.obs;
|
RxInt picQuality = 10.obs;
|
||||||
Rx<ThemeType> themeType = ThemeType.system.obs;
|
Rx<ThemeType> themeType = ThemeType.system.obs;
|
||||||
var userInfo;
|
var userInfo;
|
||||||
|
Rx<DynamicBadgeMode> dynamicBadgeType = DynamicBadgeMode.number.obs;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onInit() {
|
void onInit() {
|
||||||
@ -33,6 +37,9 @@ class SettingController extends GetxController {
|
|||||||
setting.get(SettingBoxKey.defaultPicQa, defaultValue: 10);
|
setting.get(SettingBoxKey.defaultPicQa, defaultValue: 10);
|
||||||
themeType.value = ThemeType.values[setting.get(SettingBoxKey.themeMode,
|
themeType.value = ThemeType.values[setting.get(SettingBoxKey.themeMode,
|
||||||
defaultValue: ThemeType.system.code)];
|
defaultValue: ThemeType.system.code)];
|
||||||
|
dynamicBadgeType.value = DynamicBadgeMode.values[setting.get(
|
||||||
|
SettingBoxKey.dynamicBadgeMode,
|
||||||
|
defaultValue: DynamicBadgeMode.number.code)];
|
||||||
}
|
}
|
||||||
|
|
||||||
loginOut() async {
|
loginOut() async {
|
||||||
@ -76,4 +83,31 @@ class SettingController extends GetxController {
|
|||||||
feedBackEnable.value = !feedBackEnable.value;
|
feedBackEnable.value = !feedBackEnable.value;
|
||||||
setting.put(SettingBoxKey.feedBackEnable, feedBackEnable.value);
|
setting.put(SettingBoxKey.feedBackEnable, feedBackEnable.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 设置动态未读标记
|
||||||
|
setDynamicBadgeMode(BuildContext context) async {
|
||||||
|
DynamicBadgeMode? result = await showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (context) {
|
||||||
|
return SelectDialog<DynamicBadgeMode>(
|
||||||
|
title: '动态未读标记',
|
||||||
|
value: dynamicBadgeType.value,
|
||||||
|
values: DynamicBadgeMode.values.map((e) {
|
||||||
|
return {'title': e.description, 'value': e};
|
||||||
|
}).toList(),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
if (result != null) {
|
||||||
|
dynamicBadgeType.value = result;
|
||||||
|
setting.put(SettingBoxKey.dynamicBadgeMode, result.code);
|
||||||
|
MainController mainController = Get.put(MainController());
|
||||||
|
mainController.dynamicBadgeType.value =
|
||||||
|
DynamicBadgeMode.values[result.code];
|
||||||
|
if (mainController.dynamicBadgeType.value != DynamicBadgeMode.hidden) {
|
||||||
|
mainController.getUnreadDynamic();
|
||||||
|
}
|
||||||
|
SmartDialog.showToast('设置成功');
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,6 +10,7 @@ import 'package:pilipala/pages/setting/widgets/select_dialog.dart';
|
|||||||
import 'package:pilipala/pages/setting/widgets/slide_dialog.dart';
|
import 'package:pilipala/pages/setting/widgets/slide_dialog.dart';
|
||||||
import 'package:pilipala/utils/storage.dart';
|
import 'package:pilipala/utils/storage.dart';
|
||||||
|
|
||||||
|
import '../../models/common/dynamic_badge_mode.dart';
|
||||||
import 'controller.dart';
|
import 'controller.dart';
|
||||||
import 'widgets/switch_item.dart';
|
import 'widgets/switch_item.dart';
|
||||||
|
|
||||||
@ -241,6 +242,14 @@ class _StyleSettingState extends State<StyleSetting> {
|
|||||||
'当前模式:${settingController.themeType.value.description}',
|
'当前模式:${settingController.themeType.value.description}',
|
||||||
style: subTitleStyle)),
|
style: subTitleStyle)),
|
||||||
),
|
),
|
||||||
|
ListTile(
|
||||||
|
dense: false,
|
||||||
|
onTap: () => settingController.setDynamicBadgeMode(context),
|
||||||
|
title: Text('动态未读标记', style: titleStyle),
|
||||||
|
subtitle: Obx(() => Text(
|
||||||
|
'当前标记样式:${settingController.dynamicBadgeType.value.description}',
|
||||||
|
style: subTitleStyle)),
|
||||||
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
dense: false,
|
dense: false,
|
||||||
onTap: () => Get.toNamed('/colorSetting'),
|
onTap: () => Get.toNamed('/colorSetting'),
|
||||||
|
|||||||
@ -148,7 +148,9 @@ class VideoIntroController extends GetxController {
|
|||||||
// 获取投币状态
|
// 获取投币状态
|
||||||
Future queryHasCoinVideo() async {
|
Future queryHasCoinVideo() async {
|
||||||
var result = await VideoHttp.hasCoinVideo(bvid: bvid);
|
var result = await VideoHttp.hasCoinVideo(bvid: bvid);
|
||||||
hasCoin.value = result["data"]['multiply'] == 0 ? false : true;
|
if (result['status']) {
|
||||||
|
hasCoin.value = result["data"]['multiply'] == 0 ? false : true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取收藏状态
|
// 获取收藏状态
|
||||||
@ -208,6 +210,10 @@ class VideoIntroController extends GetxController {
|
|||||||
|
|
||||||
// (取消)点赞
|
// (取消)点赞
|
||||||
Future actionLikeVideo() async {
|
Future actionLikeVideo() async {
|
||||||
|
if (userInfo == null) {
|
||||||
|
SmartDialog.showToast('账号未登录');
|
||||||
|
return;
|
||||||
|
}
|
||||||
var result = await VideoHttp.likeVideo(bvid: bvid, type: !hasLike.value);
|
var result = await VideoHttp.likeVideo(bvid: bvid, type: !hasLike.value);
|
||||||
if (result['status']) {
|
if (result['status']) {
|
||||||
// hasLike.value = result["data"] == 1 ? true : false;
|
// hasLike.value = result["data"] == 1 ? true : false;
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import 'package:flutter/gestures.dart';
|
import 'package:flutter/gestures.dart';
|
||||||
import 'package:flutter/material.dart';
|
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:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
@ -539,18 +540,6 @@ InlineSpan buildContent(
|
|||||||
// replyReply 查看二楼回复(回复详情)回调
|
// replyReply 查看二楼回复(回复详情)回调
|
||||||
// fReplyItem 父级回复内容,用作二楼回复(回复详情)展示
|
// fReplyItem 父级回复内容,用作二楼回复(回复详情)展示
|
||||||
final content = replyItem.content;
|
final content = replyItem.content;
|
||||||
if (content.emote.isEmpty &&
|
|
||||||
content.atNameToMid.isEmpty &&
|
|
||||||
content.jumpUrl.isEmpty &&
|
|
||||||
content.vote.isEmpty &&
|
|
||||||
content.pictures.isEmpty) {
|
|
||||||
return TextSpan(
|
|
||||||
text: content.message,
|
|
||||||
recognizer: TapGestureRecognizer()
|
|
||||||
..onTap =
|
|
||||||
() => replyReply(replyItem.root == 0 ? replyItem : fReplyItem),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
final List<InlineSpan> spanChilds = <InlineSpan>[];
|
final List<InlineSpan> spanChilds = <InlineSpan>[];
|
||||||
bool hasMatchMember = false;
|
bool hasMatchMember = false;
|
||||||
|
|
||||||
@ -582,258 +571,171 @@ InlineSpan buildContent(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
// content.message = content.message.replaceAll(RegExp(r"\{vote:.*?\}"), ' ');
|
// content.message = content.message.replaceAll(RegExp(r"\{vote:.*?\}"), ' ');
|
||||||
if (content.message.contains('&')) {
|
content.message = content.message.replaceAll('&', '&')
|
||||||
content.message = content.message.replaceAll('&', '&');
|
.replaceAll('<', '<')
|
||||||
|
.replaceAll('>', '>')
|
||||||
|
.replaceAll('"', '"')
|
||||||
|
.replaceAll(''', "'")
|
||||||
|
.replaceAll(' ', ' ');
|
||||||
|
// print("content.jumpUrl.keys:" + content.jumpUrl.keys.toString());
|
||||||
|
// 构建正则表达式
|
||||||
|
final List<String> specialTokens = [
|
||||||
|
...content.emote.keys,
|
||||||
|
...content.atNameToMid.keys.map((e) => '@$e'),
|
||||||
|
...content.jumpUrl.keys.map((e) =>
|
||||||
|
e.replaceAll('?', '\\?').replaceAll('+', '\\+').replaceAll('*', '\\*')),
|
||||||
|
];
|
||||||
|
|
||||||
|
String patternStr =
|
||||||
|
specialTokens.map(RegExp.escape).join('|');
|
||||||
|
if (patternStr.isNotEmpty) {
|
||||||
|
patternStr += "|";
|
||||||
}
|
}
|
||||||
// 匹配表情
|
patternStr += r'(\b\d{1,2}[::]\d{2}\b)';
|
||||||
|
final RegExp pattern = RegExp(patternStr);
|
||||||
|
List<String> matchedStrs = [];
|
||||||
|
void addPlainTextSpan(str){
|
||||||
|
spanChilds.add(TextSpan(
|
||||||
|
text: str,
|
||||||
|
recognizer: TapGestureRecognizer()
|
||||||
|
..onTap = () =>
|
||||||
|
replyReply(replyItem.root == 0 ? replyItem : fReplyItem)));
|
||||||
|
}
|
||||||
|
// 分割文本并处理每个部分
|
||||||
content.message.splitMapJoin(
|
content.message.splitMapJoin(
|
||||||
RegExp(r"\[.*?\]"),
|
pattern,
|
||||||
onMatch: (Match match) {
|
onMatch: (Match match) {
|
||||||
final String matchStr = match[0]!;
|
String matchStr = match[0]!;
|
||||||
if (content.emote.isNotEmpty &&
|
if (content.emote.containsKey(matchStr)) {
|
||||||
matchStr.indexOf('[') == matchStr.lastIndexOf('[') &&
|
// 处理表情
|
||||||
matchStr.indexOf(']') == matchStr.lastIndexOf(']')) {
|
|
||||||
final int size = content.emote[matchStr]['meta']['size'];
|
final int size = content.emote[matchStr]['meta']['size'];
|
||||||
if (content.emote.keys.contains(matchStr)) {
|
spanChilds.add(WidgetSpan(
|
||||||
spanChilds.add(
|
child: NetworkImgLayer(
|
||||||
WidgetSpan(
|
src: content.emote[matchStr]['url'],
|
||||||
child: NetworkImgLayer(
|
type: 'emote',
|
||||||
src: content.emote[matchStr]['url'],
|
width: size * 20,
|
||||||
type: 'emote',
|
height: size * 20,
|
||||||
width: size * 20,
|
),
|
||||||
height: size * 20,
|
));
|
||||||
),
|
} else if (matchStr.startsWith("@") &&
|
||||||
),
|
content.atNameToMid.containsKey(matchStr.substring(1))) {
|
||||||
);
|
// 处理@用户
|
||||||
} else {
|
final String userName = matchStr.substring(1);
|
||||||
spanChilds.add(TextSpan(
|
final int userId = content.atNameToMid[userName];
|
||||||
text: matchStr,
|
spanChilds.add(
|
||||||
recognizer: TapGestureRecognizer()
|
TextSpan(
|
||||||
..onTap = () =>
|
|
||||||
replyReply(replyItem.root == 0 ? replyItem : fReplyItem)));
|
|
||||||
return matchStr;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
spanChilds.add(TextSpan(
|
|
||||||
text: matchStr,
|
text: matchStr,
|
||||||
|
style: TextStyle(
|
||||||
|
color: Theme.of(context).colorScheme.primary,
|
||||||
|
),
|
||||||
recognizer: TapGestureRecognizer()
|
recognizer: TapGestureRecognizer()
|
||||||
..onTap = () =>
|
..onTap = () {
|
||||||
replyReply(replyItem.root == 0 ? replyItem : fReplyItem)));
|
final String heroTag = Utils.makeHeroTag(userId);
|
||||||
return matchStr;
|
Get.toNamed(
|
||||||
}
|
'/member?mid=$userId',
|
||||||
return '';
|
arguments: {'face': '', 'heroTag': heroTag},
|
||||||
},
|
|
||||||
onNonMatch: (String str) {
|
|
||||||
// 匹配@用户
|
|
||||||
String matchMember = str;
|
|
||||||
if (content.atNameToMid.isNotEmpty) {
|
|
||||||
final List atNameToMidKeys = content.atNameToMid.keys.toList();
|
|
||||||
RegExp reg = RegExp(atNameToMidKeys.map((key) => key).join('|'));
|
|
||||||
// if (!content.message.contains(':')) {
|
|
||||||
// reg = RegExp(r"@.*( |:)");
|
|
||||||
// }
|
|
||||||
|
|
||||||
// 只@用户没有内容
|
|
||||||
if (!content.message.contains(':') ||
|
|
||||||
(content.atNameToMid.length == 1 &&
|
|
||||||
content.message == '@${content.members.first.uname}')) {
|
|
||||||
reg = RegExp(r"@.*( |:|$)");
|
|
||||||
}
|
|
||||||
matchMember = str.splitMapJoin(
|
|
||||||
reg,
|
|
||||||
onMatch: (Match match) {
|
|
||||||
if (match[0] != null) {
|
|
||||||
hasMatchMember = true;
|
|
||||||
content.atNameToMid.forEach((key, value) {
|
|
||||||
if (str.contains('回复')) {
|
|
||||||
spanChilds.add(
|
|
||||||
TextSpan(
|
|
||||||
text: '回复 ',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize:
|
|
||||||
Theme.of(context).textTheme.titleSmall!.fontSize,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
spanChilds.add(
|
|
||||||
TextSpan(
|
|
||||||
text: '@$key',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize:
|
|
||||||
Theme.of(context).textTheme.titleSmall!.fontSize,
|
|
||||||
color: Theme.of(context).colorScheme.primary,
|
|
||||||
),
|
|
||||||
recognizer: TapGestureRecognizer()
|
|
||||||
..onTap = () {
|
|
||||||
final String heroTag = Utils.makeHeroTag(value);
|
|
||||||
Get.toNamed(
|
|
||||||
'/member?mid=$value',
|
|
||||||
arguments: {'face': '', 'heroTag': heroTag},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
});
|
},
|
||||||
}
|
),
|
||||||
return '';
|
);
|
||||||
},
|
} else if (RegExp(r'^\b[0-9]{1,2}[::][0-9]{2}\b$').hasMatch(matchStr)) {
|
||||||
onNonMatch: (String str) {
|
spanChilds.add(
|
||||||
if (!str.contains('@')) {
|
TextSpan(
|
||||||
spanChilds.add(TextSpan(text: str));
|
text: ' $matchStr ',
|
||||||
}
|
style: TextStyle(
|
||||||
print(str);
|
color: Theme.of(context).colorScheme.primary,
|
||||||
return str;
|
),
|
||||||
},
|
recognizer: TapGestureRecognizer()
|
||||||
|
..onTap = () {
|
||||||
|
// 跳转到指定位置
|
||||||
|
try {
|
||||||
|
matchStr = matchStr.replaceAll(':', ':');
|
||||||
|
SmartDialog.showToast('跳转至:$matchStr');
|
||||||
|
Get.find<VideoDetailController>(tag: Get.arguments['heroTag'])
|
||||||
|
.plPlayerController
|
||||||
|
.seekTo(
|
||||||
|
Duration(seconds: Utils.duration(matchStr)),
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
SmartDialog.showToast('跳转失败: $e');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
matchMember = str;
|
// print("matchStr=$matchStr");
|
||||||
}
|
String appUrlSchema = '';
|
||||||
|
final bool enableWordRe = setting.get(SettingBoxKey.enableWordRe,
|
||||||
// 匹配 jumpUrl
|
defaultValue: false) as bool;
|
||||||
String matchUrl = matchMember;
|
if (content.jumpUrl[matchStr] != null &&
|
||||||
if (content.jumpUrl.isNotEmpty) {
|
!matchedStrs.contains(matchStr)) {
|
||||||
final List urlKeys = content.jumpUrl.keys.toList().reversed.toList();
|
appUrlSchema = content.jumpUrl[matchStr]['app_url_schema'];
|
||||||
for (int index = 0; index < urlKeys.length; index++) {
|
if (appUrlSchema.startsWith('bilibili://search') && !enableWordRe) {
|
||||||
var i = urlKeys[index];
|
addPlainTextSpan(matchStr);
|
||||||
if (i.contains('?')) {
|
return "";
|
||||||
urlKeys[index] = i.replaceAll('?', '\\?');
|
|
||||||
}
|
}
|
||||||
if (i.contains('+')) {
|
|
||||||
urlKeys[index] = i.replaceAll('+', '\\+');
|
|
||||||
}
|
|
||||||
if (i.contains('*')) {
|
|
||||||
urlKeys[index] = i.replaceAll('*', '\\*');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (hasMatchMember) {
|
|
||||||
matchMember = matchMember.split('回复 @ :').length > 1
|
|
||||||
? matchMember.split('回复 @ :')[1]
|
|
||||||
: matchMember;
|
|
||||||
}
|
|
||||||
matchUrl = matchMember.splitMapJoin(
|
|
||||||
/// RegExp.escape() 转义特殊字符
|
|
||||||
RegExp(urlKeys.map((key) => key).join("|")),
|
|
||||||
// RegExp('What does the fox say\\?'),
|
|
||||||
onMatch: (Match match) {
|
|
||||||
final String matchStr = match[0]!;
|
|
||||||
String appUrlSchema = '';
|
|
||||||
if (content.jumpUrl[matchStr] != null) {
|
|
||||||
appUrlSchema = content.jumpUrl[matchStr]['app_url_schema'];
|
|
||||||
}
|
|
||||||
// 默认不显示关键词
|
|
||||||
final bool enableWordRe = setting.get(SettingBoxKey.enableWordRe,
|
|
||||||
defaultValue: false) as bool;
|
|
||||||
if (content.jumpUrl[matchStr] != null) {
|
|
||||||
spanChilds.add(
|
|
||||||
TextSpan(
|
|
||||||
text: content.jumpUrl[matchStr]['title'],
|
|
||||||
style: TextStyle(
|
|
||||||
color: enableWordRe
|
|
||||||
? Theme.of(context).colorScheme.primary
|
|
||||||
: null,
|
|
||||||
),
|
|
||||||
recognizer: TapGestureRecognizer()
|
|
||||||
..onTap = () {
|
|
||||||
if (appUrlSchema == '') {
|
|
||||||
final String str = Uri.parse(matchStr).pathSegments[0];
|
|
||||||
final Map matchRes = IdUtils.matchAvorBv(input: str);
|
|
||||||
final List matchKeys = matchRes.keys.toList();
|
|
||||||
if (matchKeys.isNotEmpty) {
|
|
||||||
if (matchKeys.first == 'BV') {
|
|
||||||
Get.toNamed(
|
|
||||||
'/searchResult',
|
|
||||||
parameters: {'keyword': matchRes['BV']},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Get.toNamed(
|
|
||||||
'/webview',
|
|
||||||
parameters: {
|
|
||||||
'url': matchStr,
|
|
||||||
'type': 'url',
|
|
||||||
'pageTitle': ''
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (appUrlSchema.startsWith('bilibili://search') &&
|
|
||||||
enableWordRe) {
|
|
||||||
Get.toNamed('/searchResult', parameters: {
|
|
||||||
'keyword': content.jumpUrl[matchStr]['title']
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (appUrlSchema.startsWith('bilibili://search') && enableWordRe) {
|
|
||||||
spanChilds.add(
|
|
||||||
WidgetSpan(
|
|
||||||
child: Icon(
|
|
||||||
FontAwesomeIcons.magnifyingGlass,
|
|
||||||
size: 9,
|
|
||||||
color: Theme.of(context).colorScheme.primary,
|
|
||||||
),
|
|
||||||
alignment: PlaceholderAlignment.top,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return '';
|
|
||||||
},
|
|
||||||
onNonMatch: (String str) {
|
|
||||||
spanChilds.add(TextSpan(
|
|
||||||
text: str,
|
|
||||||
recognizer: TapGestureRecognizer()
|
|
||||||
..onTap = () => replyReply(
|
|
||||||
replyItem.root == 0 ? replyItem : fReplyItem)));
|
|
||||||
return str;
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
str = matchUrl.splitMapJoin(
|
|
||||||
RegExp(r'\b\d{2}:\d{2}\b'),
|
|
||||||
onMatch: (Match match) {
|
|
||||||
String matchStr = match[0]!;
|
|
||||||
spanChilds.add(
|
spanChilds.add(
|
||||||
TextSpan(
|
TextSpan(
|
||||||
text: ' $matchStr ',
|
text: content.jumpUrl[matchStr]['title'],
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Theme.of(context).colorScheme.primary,
|
color: Theme.of(context).colorScheme.primary,
|
||||||
),
|
),
|
||||||
recognizer: TapGestureRecognizer()
|
recognizer: TapGestureRecognizer()
|
||||||
..onTap = () {
|
..onTap = () {
|
||||||
// 跳转到指定位置
|
if (appUrlSchema == '') {
|
||||||
try {
|
final String str = Uri.parse(matchStr).pathSegments[0];
|
||||||
Get.find<VideoDetailController>(
|
final Map matchRes = IdUtils.matchAvorBv(input: str);
|
||||||
tag: Get.arguments['heroTag'])
|
final List matchKeys = matchRes.keys.toList();
|
||||||
.plPlayerController
|
if (matchKeys.isNotEmpty) {
|
||||||
.seekTo(
|
if (matchKeys.first == 'BV') {
|
||||||
Duration(seconds: Utils.duration(matchStr)),
|
Get.toNamed(
|
||||||
|
'/searchResult',
|
||||||
|
parameters: {'keyword': matchRes['BV']},
|
||||||
);
|
);
|
||||||
} catch (_) {}
|
}
|
||||||
|
} else {
|
||||||
|
Get.toNamed(
|
||||||
|
'/webview',
|
||||||
|
parameters: {
|
||||||
|
'url': matchStr,
|
||||||
|
'type': 'url',
|
||||||
|
'pageTitle': ''
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (appUrlSchema.startsWith('bilibili://search')) {
|
||||||
|
Get.toNamed('/searchResult', parameters: {
|
||||||
|
'keyword': content.jumpUrl[matchStr]['title']
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
return '';
|
if (appUrlSchema.startsWith('bilibili://search')) {
|
||||||
},
|
spanChilds.add(
|
||||||
onNonMatch: (str) {
|
WidgetSpan(
|
||||||
return str;
|
child: Icon(
|
||||||
},
|
FontAwesomeIcons.magnifyingGlass,
|
||||||
);
|
size: 9,
|
||||||
|
color: Theme.of(context).colorScheme.primary,
|
||||||
if (content.atNameToMid.isEmpty && content.jumpUrl.isEmpty) {
|
),
|
||||||
if (str != '') {
|
alignment: PlaceholderAlignment.top,
|
||||||
spanChilds.add(TextSpan(
|
),
|
||||||
text: str,
|
);
|
||||||
recognizer: TapGestureRecognizer()
|
}
|
||||||
..onTap = () =>
|
// 只显示一次
|
||||||
replyReply(replyItem.root == 0 ? replyItem : fReplyItem)));
|
matchedStrs.add(matchStr);
|
||||||
|
} else {
|
||||||
|
addPlainTextSpan(matchStr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return str;
|
return '';
|
||||||
|
},
|
||||||
|
onNonMatch: (String nonMatchStr) {
|
||||||
|
addPlainTextSpan(nonMatchStr);
|
||||||
|
return nonMatchStr;
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -841,10 +743,10 @@ InlineSpan buildContent(
|
|||||||
if (content.pictures.isNotEmpty) {
|
if (content.pictures.isNotEmpty) {
|
||||||
final List<String> picList = <String>[];
|
final List<String> picList = <String>[];
|
||||||
final int len = content.pictures.length;
|
final int len = content.pictures.length;
|
||||||
|
spanChilds.add(const TextSpan(text: '\n'));
|
||||||
if (len == 1) {
|
if (len == 1) {
|
||||||
Map pictureItem = content.pictures.first;
|
Map pictureItem = content.pictures.first;
|
||||||
picList.add(pictureItem['img_src']);
|
picList.add(pictureItem['img_src']);
|
||||||
spanChilds.add(const TextSpan(text: '\n'));
|
|
||||||
spanChilds.add(
|
spanChilds.add(
|
||||||
WidgetSpan(
|
WidgetSpan(
|
||||||
child: LayoutBuilder(
|
child: LayoutBuilder(
|
||||||
|
|||||||
@ -27,6 +27,7 @@ enum MsgType {
|
|||||||
article_card(value: 12, label: "专栏卡片"),
|
article_card(value: 12, label: "专栏卡片"),
|
||||||
pic_card(value: 13, label: "图片卡片"),
|
pic_card(value: 13, label: "图片卡片"),
|
||||||
common_share(value: 14, label: "异形卡片"),
|
common_share(value: 14, label: "异形卡片"),
|
||||||
|
auto_reply_push(value: 16, label: "自动回复推送"),
|
||||||
notify_text(value: 18, label: "文本提示");
|
notify_text(value: 18, label: "文本提示");
|
||||||
|
|
||||||
final int value;
|
final int value;
|
||||||
@ -59,8 +60,10 @@ class ChatItem extends StatelessWidget {
|
|||||||
// bool isArticle = item.msgType == 12; // 专栏
|
// bool isArticle = item.msgType == 12; // 专栏
|
||||||
bool isRevoke = item.msgType == MsgType.revoke.value; // 撤回消息
|
bool isRevoke = item.msgType == MsgType.revoke.value; // 撤回消息
|
||||||
bool isShareV2 = item.msgType == MsgType.share_v2.value;
|
bool isShareV2 = item.msgType == MsgType.share_v2.value;
|
||||||
bool isSystem =
|
bool isSystem = item.msgType == MsgType.notify_text.value ||
|
||||||
item.msgType == 18 || item.msgType == 10 || item.msgType == 13;
|
item.msgType == MsgType.notify_msg.value ||
|
||||||
|
item.msgType == MsgType.pic_card.value ||
|
||||||
|
item.msgType == MsgType.auto_reply_push.value;
|
||||||
dynamic content = item.content ?? '';
|
dynamic content = item.content ?? '';
|
||||||
Color textColor(BuildContext context) {
|
Color textColor(BuildContext context) {
|
||||||
return isOwner
|
return isOwner
|
||||||
@ -190,6 +193,163 @@ class ChatItem extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
case MsgType.archive_card:
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
GestureDetector(
|
||||||
|
onTap: () async {
|
||||||
|
SmartDialog.showLoading();
|
||||||
|
var bvid = content["bvid"];
|
||||||
|
final int cid = await SearchHttp.ab2c(bvid: bvid);
|
||||||
|
final String heroTag = Utils.makeHeroTag(bvid);
|
||||||
|
SmartDialog.dismiss<dynamic>().then(
|
||||||
|
(e) => Get.toNamed<dynamic>('/video?bvid=$bvid&cid=$cid',
|
||||||
|
arguments: <String, String?>{
|
||||||
|
'pic': content['thumb'],
|
||||||
|
'heroTag': heroTag,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: NetworkImgLayer(
|
||||||
|
width: 220,
|
||||||
|
height: 220 * 9 / 16,
|
||||||
|
src: content['cover'],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 6),
|
||||||
|
Text(
|
||||||
|
content['title'],
|
||||||
|
style: TextStyle(
|
||||||
|
letterSpacing: 0.6,
|
||||||
|
height: 1.5,
|
||||||
|
color: textColor(context),
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 1),
|
||||||
|
Text(
|
||||||
|
Utils.timeFormat(content['times']),
|
||||||
|
style: TextStyle(
|
||||||
|
letterSpacing: 0.6,
|
||||||
|
height: 1.5,
|
||||||
|
color: textColor(context).withOpacity(0.6),
|
||||||
|
fontSize: 12,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
case MsgType.auto_reply_push:
|
||||||
|
return Container(
|
||||||
|
constraints: const BoxConstraints(
|
||||||
|
maxWidth: 300.0, // 设置最大宽度为200.0
|
||||||
|
),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Theme.of(context)
|
||||||
|
.colorScheme
|
||||||
|
.secondaryContainer
|
||||||
|
.withOpacity(0.4),
|
||||||
|
borderRadius: const BorderRadius.only(
|
||||||
|
topLeft: Radius.circular(16),
|
||||||
|
topRight: Radius.circular(16),
|
||||||
|
bottomLeft: Radius.circular(6),
|
||||||
|
bottomRight: Radius.circular(16),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
margin: const EdgeInsets.all(12),
|
||||||
|
padding: const EdgeInsets.all(12),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
content['main_title'],
|
||||||
|
style: TextStyle(
|
||||||
|
letterSpacing: 0.6,
|
||||||
|
height: 1.5,
|
||||||
|
color: textColor(context),
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
for (var i in content['sub_cards']) ...<Widget>[
|
||||||
|
const SizedBox(height: 6),
|
||||||
|
GestureDetector(
|
||||||
|
onTap: () async {
|
||||||
|
RegExp bvRegex = RegExp(r'BV[0-9A-Za-z]{10}',
|
||||||
|
caseSensitive: false);
|
||||||
|
Iterable<Match> matches =
|
||||||
|
bvRegex.allMatches(i['jump_url']);
|
||||||
|
if (matches.isNotEmpty) {
|
||||||
|
Match match = matches.first;
|
||||||
|
String bvid = match.group(0)!;
|
||||||
|
try {
|
||||||
|
SmartDialog.showLoading();
|
||||||
|
final int cid = await SearchHttp.ab2c(bvid: bvid);
|
||||||
|
final String heroTag = Utils.makeHeroTag(bvid);
|
||||||
|
SmartDialog.dismiss<dynamic>().then(
|
||||||
|
(e) => Get.toNamed<dynamic>(
|
||||||
|
'/video?bvid=$bvid&cid=$cid',
|
||||||
|
arguments: <String, String?>{
|
||||||
|
'pic': i['cover_url'],
|
||||||
|
'heroTag': heroTag,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
} catch (err) {
|
||||||
|
SmartDialog.dismiss();
|
||||||
|
SmartDialog.showToast(err.toString());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
SmartDialog.showToast('未匹配到 BV 号');
|
||||||
|
Get.toNamed('/webview',
|
||||||
|
arguments: {'url': i['jump_url']});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
NetworkImgLayer(
|
||||||
|
width: 130,
|
||||||
|
height: 130 * 9 / 16,
|
||||||
|
src: i['cover_url'],
|
||||||
|
),
|
||||||
|
const SizedBox(width: 6),
|
||||||
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
i['field1'],
|
||||||
|
maxLines: 2,
|
||||||
|
style: TextStyle(
|
||||||
|
letterSpacing: 0.6,
|
||||||
|
height: 1.5,
|
||||||
|
color: textColor(context),
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
i['field2'],
|
||||||
|
style: TextStyle(
|
||||||
|
letterSpacing: 0.6,
|
||||||
|
height: 1.5,
|
||||||
|
color: textColor(context).withOpacity(0.6),
|
||||||
|
fontSize: 12,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
Utils.timeFormat(int.parse(i['field3'])),
|
||||||
|
style: TextStyle(
|
||||||
|
letterSpacing: 0.6,
|
||||||
|
height: 1.5,
|
||||||
|
color: textColor(context).withOpacity(0.6),
|
||||||
|
fontSize: 12,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)),
|
||||||
|
],
|
||||||
|
)),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
));
|
||||||
default:
|
default:
|
||||||
return Text(
|
return Text(
|
||||||
content['content'] ?? content.toString(),
|
content['content'] ?? content.toString(),
|
||||||
|
|||||||
@ -147,8 +147,8 @@ class VideoPlayerServiceHandler extends BaseAudioHandler with SeekHandler {
|
|||||||
processingState: AudioProcessingState.idle,
|
processingState: AudioProcessingState.idle,
|
||||||
playing: false,
|
playing: false,
|
||||||
));
|
));
|
||||||
_item.removeLast();
|
|
||||||
if (_item.isNotEmpty) {
|
if (_item.isNotEmpty) {
|
||||||
|
_item.removeLast();
|
||||||
setMediaItem(_item.last);
|
setMediaItem(_item.last);
|
||||||
}
|
}
|
||||||
if (_item.isEmpty) {
|
if (_item.isEmpty) {
|
||||||
|
|||||||
@ -13,7 +13,20 @@ class IdUtils {
|
|||||||
|
|
||||||
/// av转bv
|
/// av转bv
|
||||||
static String av2bv(int aid) {
|
static String av2bv(int aid) {
|
||||||
List<String> bytes = List.filled(12, '0', growable: false);
|
List<String> bytes = [
|
||||||
|
'B',
|
||||||
|
'V',
|
||||||
|
'1',
|
||||||
|
'0',
|
||||||
|
'0',
|
||||||
|
'0',
|
||||||
|
'0',
|
||||||
|
'0',
|
||||||
|
'0',
|
||||||
|
'0',
|
||||||
|
'0',
|
||||||
|
'0'
|
||||||
|
];
|
||||||
int bvIndex = bytes.length - 1;
|
int bvIndex = bytes.length - 1;
|
||||||
BigInt tmp = (MAX_AID | BigInt.from(aid)) ^ XOR_CODE;
|
BigInt tmp = (MAX_AID | BigInt.from(aid)) ^ XOR_CODE;
|
||||||
while (tmp > BigInt.zero) {
|
while (tmp > BigInt.zero) {
|
||||||
@ -21,13 +34,13 @@ class IdUtils {
|
|||||||
tmp = tmp ~/ BASE;
|
tmp = tmp ~/ BASE;
|
||||||
bvIndex -= 1;
|
bvIndex -= 1;
|
||||||
}
|
}
|
||||||
final tmpValue = bytes[3];
|
String tmpSwap = bytes[3];
|
||||||
bytes[3] = bytes[9];
|
bytes[3] = bytes[9];
|
||||||
bytes[9] = tmpValue;
|
bytes[9] = tmpSwap;
|
||||||
|
|
||||||
final tmpValue2 = bytes[4];
|
tmpSwap = bytes[4];
|
||||||
bytes[4] = bytes[7];
|
bytes[4] = bytes[7];
|
||||||
bytes[7] = tmpValue2;
|
bytes[7] = tmpSwap;
|
||||||
|
|
||||||
return bytes.join();
|
return bytes.join();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,7 +4,6 @@ import 'dart:io';
|
|||||||
import 'package:hive_flutter/hive_flutter.dart';
|
import 'package:hive_flutter/hive_flutter.dart';
|
||||||
import 'package:path_provider/path_provider.dart';
|
import 'package:path_provider/path_provider.dart';
|
||||||
import 'package:pilipala/models/model_owner.dart';
|
import 'package:pilipala/models/model_owner.dart';
|
||||||
import 'package:pilipala/models/search/hot.dart';
|
|
||||||
import 'package:pilipala/models/user/info.dart';
|
import 'package:pilipala/models/user/info.dart';
|
||||||
|
|
||||||
class GStrorage {
|
class GStrorage {
|
||||||
@ -48,8 +47,6 @@ class GStrorage {
|
|||||||
Hive.registerAdapter(OwnerAdapter());
|
Hive.registerAdapter(OwnerAdapter());
|
||||||
Hive.registerAdapter(UserInfoDataAdapter());
|
Hive.registerAdapter(UserInfoDataAdapter());
|
||||||
Hive.registerAdapter(LevelInfoAdapter());
|
Hive.registerAdapter(LevelInfoAdapter());
|
||||||
Hive.registerAdapter(HotSearchModelAdapter());
|
|
||||||
Hive.registerAdapter(HotSearchItemAdapter());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<void> lazyInit() async {
|
static Future<void> lazyInit() async {
|
||||||
@ -130,7 +127,8 @@ class SettingBoxKey {
|
|||||||
enableMYBar = 'enableMYBar',
|
enableMYBar = 'enableMYBar',
|
||||||
hideSearchBar = 'hideSearchBar', // 收起顶栏
|
hideSearchBar = 'hideSearchBar', // 收起顶栏
|
||||||
hideTabBar = 'hideTabBar', // 收起底栏
|
hideTabBar = 'hideTabBar', // 收起底栏
|
||||||
tabbarSort = 'tabbarSort'; // 首页tabbar
|
tabbarSort = 'tabbarSort', // 首页tabbar
|
||||||
|
dynamicBadgeMode = 'dynamicBadgeMode';
|
||||||
}
|
}
|
||||||
|
|
||||||
class LocalCacheKey {
|
class LocalCacheKey {
|
||||||
|
|||||||
@ -276,16 +276,18 @@ class Utils {
|
|||||||
// [arm64-v8a]
|
// [arm64-v8a]
|
||||||
String abi = androidInfo.supportedAbis.first;
|
String abi = androidInfo.supportedAbis.first;
|
||||||
late String downloadUrl;
|
late String downloadUrl;
|
||||||
for (var i in data.assets) {
|
if (data.assets.isNotEmpty) {
|
||||||
if (i.downloadUrl.contains(abi)) {
|
for (var i in data.assets) {
|
||||||
downloadUrl = i.downloadUrl;
|
if (i.downloadUrl.contains(abi)) {
|
||||||
|
downloadUrl = i.downloadUrl;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
// 应用外下载
|
||||||
|
launchUrl(
|
||||||
|
Uri.parse(downloadUrl),
|
||||||
|
mode: LaunchMode.externalApplication,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
// 应用外下载
|
|
||||||
launchUrl(
|
|
||||||
Uri.parse(downloadUrl),
|
|
||||||
mode: LaunchMode.externalApplication,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -500,10 +500,11 @@ packages:
|
|||||||
floating:
|
floating:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: floating
|
path: "."
|
||||||
sha256: d9d563089e34fbd714ffdcdd2df447ec41b40c9226dacae6b4f78847aef8b991
|
ref: main
|
||||||
url: "https://pub.flutter-io.cn"
|
resolved-ref: d2d8421c4d80f6113f832404109853684721e11a
|
||||||
source: hosted
|
url: "https://github.com/guozhigq/floating.git"
|
||||||
|
source: git
|
||||||
version: "2.0.1"
|
version: "2.0.1"
|
||||||
flutter:
|
flutter:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
|
|||||||
@ -124,7 +124,10 @@ dependencies:
|
|||||||
# 代理
|
# 代理
|
||||||
system_proxy: ^0.1.0
|
system_proxy: ^0.1.0
|
||||||
# pip
|
# pip
|
||||||
floating: ^2.0.1
|
floating:
|
||||||
|
git:
|
||||||
|
url: https://github.com/guozhigq/floating.git
|
||||||
|
ref: main
|
||||||
# html解析
|
# html解析
|
||||||
html: ^0.15.4
|
html: ^0.15.4
|
||||||
# html渲染
|
# html渲染
|
||||||
|
|||||||
Reference in New Issue
Block a user