mirror of
https://github.com/bggRGjQaUbCoE/PiliPlus.git
synced 2026-07-09 10:40:12 +08:00
@@ -33,22 +33,19 @@ import 'package:dio/dio.dart';
|
|||||||
abstract final class DynamicsHttp {
|
abstract final class DynamicsHttp {
|
||||||
@pragma('vm:notify-debugger-on-exception')
|
@pragma('vm:notify-debugger-on-exception')
|
||||||
static Future<LoadingState<DynamicsDataModel>> followDynamic({
|
static Future<LoadingState<DynamicsDataModel>> followDynamic({
|
||||||
DynamicsTabType type = .all,
|
int? hostMid,
|
||||||
String? offset,
|
String? offset,
|
||||||
int? mid,
|
|
||||||
Set<int>? tempBannedList,
|
Set<int>? tempBannedList,
|
||||||
|
DynamicsTabType type = .all,
|
||||||
}) async {
|
}) async {
|
||||||
Map<String, dynamic> data = {
|
final res = await Request.get(
|
||||||
if (type == .up)
|
Api.followDynamic,
|
||||||
'host_mid': mid
|
queryParameters: {
|
||||||
else ...{
|
if (type == .up) 'host_mid': hostMid else 'type': type.name,
|
||||||
'type': type.name,
|
'offset': ?offset,
|
||||||
'timezone_offset': '-480',
|
'features': Constants.dynFeatures,
|
||||||
},
|
},
|
||||||
'offset': offset,
|
);
|
||||||
'features': Constants.dynFeatures,
|
|
||||||
};
|
|
||||||
final res = await Request.get(Api.followDynamic, queryParameters: data);
|
|
||||||
final code = res.data['code'];
|
final code = res.data['code'];
|
||||||
if (code == 0) {
|
if (code == 0) {
|
||||||
try {
|
try {
|
||||||
@@ -61,7 +58,7 @@ abstract final class DynamicsHttp {
|
|||||||
return await followDynamic(
|
return await followDynamic(
|
||||||
type: type,
|
type: type,
|
||||||
offset: data.offset,
|
offset: data.offset,
|
||||||
mid: mid,
|
hostMid: hostMid,
|
||||||
tempBannedList: tempBannedList,
|
tempBannedList: tempBannedList,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -89,17 +86,17 @@ abstract final class DynamicsHttp {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<LoadingState<DynUpList>> dynUpList(String? offset) async {
|
static Future<LoadingState<FollowUpModel>> dynUpList(String? offset) async {
|
||||||
final res = await Request.get(
|
final res = await Request.get(
|
||||||
Api.dynUplist,
|
Api.dynUplist,
|
||||||
queryParameters: {
|
queryParameters: {
|
||||||
'offset': offset,
|
'offset': ?offset,
|
||||||
'platform': 'web',
|
'platform': 'web',
|
||||||
'web_location': 333.1365,
|
'web_location': 333.1365,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
if (res.data['code'] == 0) {
|
if (res.data['code'] == 0) {
|
||||||
return Success(DynUpList.fromJson(res.data['data']));
|
return Success(FollowUpModel.fromUpList(res.data['data']));
|
||||||
} else {
|
} else {
|
||||||
return Error(res.data['message']);
|
return Error(res.data['message']);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import 'package:brotli/brotli.dart';
|
|||||||
import 'package:dio/dio.dart';
|
import 'package:dio/dio.dart';
|
||||||
import 'package:dio/io.dart';
|
import 'package:dio/io.dart';
|
||||||
import 'package:dio_http2_adapter/dio_http2_adapter.dart';
|
import 'package:dio_http2_adapter/dio_http2_adapter.dart';
|
||||||
|
import 'package:flutter/foundation.dart' show kDebugMode;
|
||||||
|
|
||||||
abstract final class Request {
|
abstract final class Request {
|
||||||
static const _gzipDecoder = GZipDecoder();
|
static const _gzipDecoder = GZipDecoder();
|
||||||
@@ -163,15 +164,15 @@ abstract final class Request {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 日志拦截器 输出请求、响应内容
|
// 日志拦截器 输出请求、响应内容
|
||||||
// if (kDebugMode) {
|
if (kDebugMode) {
|
||||||
// dio.interceptors.add(
|
dio.interceptors.add(
|
||||||
// LogInterceptor(
|
LogInterceptor(
|
||||||
// request: false,
|
request: false,
|
||||||
// requestHeader: false,
|
requestHeader: false,
|
||||||
// responseHeader: false,
|
responseHeader: false,
|
||||||
// ),
|
),
|
||||||
// );
|
);
|
||||||
// }
|
}
|
||||||
|
|
||||||
dio
|
dio
|
||||||
..transformer = BackgroundTransformer()
|
..transformer = BackgroundTransformer()
|
||||||
|
|||||||
@@ -134,7 +134,7 @@ class Word {
|
|||||||
style = json['style'] == null ? null : Style.fromJson(json['style']);
|
style = json['style'] == null ? null : Style.fromJson(json['style']);
|
||||||
if (json['color'] case final String rawColor
|
if (json['color'] case final String rawColor
|
||||||
when rawColor.startsWith('#')) {
|
when rawColor.startsWith('#')) {
|
||||||
color = ColourUtils.parse2Int(json['color']);
|
color = ColourUtils.parse2Int(rawColor);
|
||||||
}
|
}
|
||||||
fontLevel = json['font_level'];
|
fontLevel = json['font_level'];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,41 +1,28 @@
|
|||||||
import 'package:PiliPlus/utils/parse_int.dart';
|
import 'package:PiliPlus/utils/parse_int.dart';
|
||||||
|
|
||||||
class FollowUpModel {
|
class FollowUpModel {
|
||||||
FollowUpModel({
|
|
||||||
this.liveUsers,
|
|
||||||
required this.upList,
|
|
||||||
});
|
|
||||||
|
|
||||||
LiveUsers? liveUsers;
|
LiveUsers? liveUsers;
|
||||||
late List<UpItem> upList;
|
|
||||||
bool? hasMore;
|
|
||||||
String? offset;
|
|
||||||
|
|
||||||
FollowUpModel.fromJson(Map<String, dynamic> json) {
|
|
||||||
liveUsers = json['live_users'] != null
|
|
||||||
? LiveUsers.fromJson(json['live_users'])
|
|
||||||
: null;
|
|
||||||
upList =
|
|
||||||
(json['up_list']?['items'] as List?)
|
|
||||||
?.map<UpItem>((e) => UpItem.fromJson(e))
|
|
||||||
.toList() ??
|
|
||||||
<UpItem>[];
|
|
||||||
hasMore = json['up_list']?['has_more'];
|
|
||||||
offset = json['up_list']?['offset'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class DynUpList {
|
|
||||||
List<UpItem>? upList;
|
List<UpItem>? upList;
|
||||||
bool? hasMore;
|
bool? hasMore;
|
||||||
String? offset;
|
String? offset;
|
||||||
|
|
||||||
DynUpList.fromJson(Map<String, dynamic> json) {
|
factory FollowUpModel.fromJson(Map<String, dynamic> json) {
|
||||||
upList = (json['items'] as List?)
|
final model = FollowUpModel.fromUpList(json['up_list']);
|
||||||
?.map<UpItem>((e) => UpItem.fromJson(e))
|
final liveUsers = json['live_users'];
|
||||||
.toList();
|
if (liveUsers != null) {
|
||||||
hasMore = json['has_more'];
|
model.liveUsers = LiveUsers.fromJson(liveUsers);
|
||||||
offset = json['offset'];
|
}
|
||||||
|
return model;
|
||||||
|
}
|
||||||
|
|
||||||
|
FollowUpModel.fromUpList(Map<String, dynamic>? json) {
|
||||||
|
if (json != null) {
|
||||||
|
upList = (json['items'] as List?)
|
||||||
|
?.map((e) => UpItem.fromJson(e))
|
||||||
|
.toList();
|
||||||
|
hasMore = json['has_more'];
|
||||||
|
offset = json['offset'];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,151 +1,86 @@
|
|||||||
import 'package:PiliPlus/http/dynamics.dart';
|
import 'package:PiliPlus/http/dynamics.dart';
|
||||||
import 'package:PiliPlus/http/loading_state.dart';
|
import 'package:PiliPlus/http/loading_state.dart';
|
||||||
import 'package:PiliPlus/models/common/dynamic/dynamics_type.dart';
|
|
||||||
import 'package:PiliPlus/models/dynamics/up.dart';
|
import 'package:PiliPlus/models/dynamics/up.dart';
|
||||||
import 'package:PiliPlus/pages/common/common_controller.dart';
|
import 'package:PiliPlus/pages/common/common_data_controller.dart';
|
||||||
import 'package:PiliPlus/pages/dynamics_tab/controller.dart';
|
import 'package:PiliPlus/pages/dynamics_tab/controller.dart';
|
||||||
import 'package:PiliPlus/services/account_service.dart';
|
import 'package:PiliPlus/services/account_service.dart';
|
||||||
import 'package:PiliPlus/utils/extension/scroll_controller_ext.dart';
|
import 'package:PiliPlus/utils/extension/scroll_controller_ext.dart';
|
||||||
import 'package:PiliPlus/utils/extension/string_ext.dart';
|
import 'package:PiliPlus/utils/extension/string_ext.dart';
|
||||||
import 'package:PiliPlus/utils/storage_pref.dart';
|
import 'package:PiliPlus/utils/storage_pref.dart';
|
||||||
import 'package:flutter/material.dart' show ScrollController, TabController;
|
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
|
|
||||||
class DynamicsController extends GetxController
|
class DynamicsController
|
||||||
with GetSingleTickerProviderStateMixin, ScrollOrRefreshMixin, AccountMixin {
|
extends CommonDataController<FollowUpModel, FollowUpModel>
|
||||||
@override
|
with AccountMixin {
|
||||||
final ScrollController scrollController = ScrollController();
|
String? offset;
|
||||||
late final TabController tabController;
|
|
||||||
|
|
||||||
late final RxInt mid = (-1).obs;
|
bool isEnd = false;
|
||||||
late int currentMid = -1;
|
|
||||||
|
|
||||||
Set<int> tempBannedList = <int>{};
|
|
||||||
|
|
||||||
final Rx<LoadingState<FollowUpModel>> upState =
|
|
||||||
LoadingState<FollowUpModel>.loading().obs;
|
|
||||||
late bool _upEnd = false;
|
|
||||||
late bool showLiveUp = false;
|
late bool showLiveUp = false;
|
||||||
|
final Set<int> tempBannedList = <int>{};
|
||||||
final upPanelPosition = Pref.upPanelPosition;
|
final upPanelPosition = Pref.upPanelPosition;
|
||||||
|
|
||||||
|
late final innerController = Get.find<DynamicsTabController>();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
final AccountService accountService = Get.find<AccountService>();
|
final AccountService accountService = Get.find<AccountService>();
|
||||||
|
|
||||||
DynamicsTabController? get controller {
|
|
||||||
try {
|
|
||||||
return Get.find<DynamicsTabController>(
|
|
||||||
tag: DynamicsTabType.values[tabController.index].name,
|
|
||||||
);
|
|
||||||
} catch (_) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onInit() {
|
void onInit() {
|
||||||
super.onInit();
|
super.onInit();
|
||||||
tabController = TabController(
|
queryData();
|
||||||
length: DynamicsTabType.values.length,
|
|
||||||
vsync: this,
|
|
||||||
);
|
|
||||||
queryFollowUp();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void onLoadMoreUp() {
|
@override
|
||||||
queryUpList();
|
bool customHandleResponse(bool isRefresh, Success<FollowUpModel> response) {
|
||||||
|
final res = response.response;
|
||||||
|
offset = res.offset;
|
||||||
|
if (!isRefresh) {
|
||||||
|
final lastData = loadingState.value.data;
|
||||||
|
if (res.upList case final upList?) {
|
||||||
|
lastData.upList!.addAll(upList);
|
||||||
|
}
|
||||||
|
res
|
||||||
|
..liveUsers = lastData.liveUsers
|
||||||
|
..upList = lastData.upList;
|
||||||
|
}
|
||||||
|
if (res.hasMore != true || offset.isNullOrEmpty) {
|
||||||
|
isEnd = true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> queryUpList() async {
|
@override
|
||||||
if (isQuerying || _upEnd) return;
|
Future<LoadingState<FollowUpModel>> customGetData() {
|
||||||
isQuerying = true;
|
if (offset == null) {
|
||||||
|
return DynamicsHttp.followUp();
|
||||||
final res = await DynamicsHttp.dynUpList(upState.value.data.offset);
|
|
||||||
|
|
||||||
if (res case Success(:final response)) {
|
|
||||||
if (response.hasMore == false || response.offset.isNullOrEmpty) {
|
|
||||||
_upEnd = true;
|
|
||||||
}
|
|
||||||
final upData = upState.value.data
|
|
||||||
..hasMore = response.hasMore
|
|
||||||
..offset = response.offset;
|
|
||||||
final list = response.upList;
|
|
||||||
if (list != null && list.isNotEmpty) {
|
|
||||||
upData.upList.addAll(list);
|
|
||||||
upState.refresh();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return DynamicsHttp.dynUpList(offset);
|
||||||
isQuerying = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
late bool isQuerying = false;
|
Future<void> singleRefresh() {
|
||||||
Future<void> queryFollowUp() async {
|
offset = null;
|
||||||
if (isQuerying) return;
|
isEnd = false;
|
||||||
isQuerying = true;
|
return super.onRefresh();
|
||||||
|
|
||||||
if (!accountService.isLogin.value) {
|
|
||||||
upState.value = const Error(null);
|
|
||||||
isQuerying = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// reset
|
|
||||||
_upEnd = false;
|
|
||||||
|
|
||||||
final res = await DynamicsHttp.followUp();
|
|
||||||
|
|
||||||
if (res case final Success<FollowUpModel> i) {
|
|
||||||
final data = i.response;
|
|
||||||
if (data.hasMore == false || data.offset.isNullOrEmpty) {
|
|
||||||
_upEnd = true;
|
|
||||||
}
|
|
||||||
upState.value = Success(data);
|
|
||||||
} else {
|
|
||||||
upState.value = const Error(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
isQuerying = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void onSelectUp(int mid) {
|
|
||||||
if (this.mid.value == mid) {
|
|
||||||
tabController.index = (mid == -1 ? 0 : 4);
|
|
||||||
if (mid == -1) {
|
|
||||||
queryFollowUp();
|
|
||||||
}
|
|
||||||
controller?.onReload();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.mid.value = mid;
|
|
||||||
tabController.index = (mid == -1 ? 0 : 4);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> onRefresh() {
|
Future<void> onRefresh() {
|
||||||
_refreshFollowUp();
|
singleRefresh();
|
||||||
return controller!.onRefresh();
|
return innerController.onRefresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
void _refreshFollowUp() {
|
@override
|
||||||
queryFollowUp();
|
Future<void> queryData([bool isRefresh = true]) {
|
||||||
|
if (!isRefresh && isEnd) return Future.value();
|
||||||
|
return super.queryData(isRefresh);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void animateToTop() {
|
void animateToTop() {
|
||||||
controller?.animateToTop();
|
super.animateToTop();
|
||||||
scrollController.animToTop();
|
innerController.scrollController.animToTop();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onClose() {
|
void onChangeAccount(bool isLogin) => onReload();
|
||||||
mid.close();
|
|
||||||
tabController.dispose();
|
|
||||||
scrollController.dispose();
|
|
||||||
super.onClose();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void onChangeAccount(bool isLogin) => _refreshFollowUp();
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,11 @@
|
|||||||
import 'package:PiliPlus/common/widgets/scroll_physics.dart';
|
|
||||||
import 'package:PiliPlus/http/loading_state.dart';
|
import 'package:PiliPlus/http/loading_state.dart';
|
||||||
import 'package:PiliPlus/models/common/dynamic/dynamics_type.dart';
|
import 'package:PiliPlus/models/common/dynamic/dynamics_type.dart';
|
||||||
import 'package:PiliPlus/models/common/dynamic/up_panel_position.dart';
|
|
||||||
import 'package:PiliPlus/models/dynamics/up.dart';
|
import 'package:PiliPlus/models/dynamics/up.dart';
|
||||||
import 'package:PiliPlus/pages/dynamics/controller.dart';
|
import 'package:PiliPlus/pages/dynamics/controller.dart';
|
||||||
import 'package:PiliPlus/pages/dynamics/widgets/up_panel.dart';
|
import 'package:PiliPlus/pages/dynamics/widgets/up_panel.dart';
|
||||||
import 'package:PiliPlus/pages/dynamics_create/view.dart';
|
import 'package:PiliPlus/pages/dynamics_create/view.dart';
|
||||||
|
import 'package:PiliPlus/pages/dynamics_tab/controller.dart';
|
||||||
import 'package:PiliPlus/pages/dynamics_tab/view.dart';
|
import 'package:PiliPlus/pages/dynamics_tab/view.dart';
|
||||||
import 'package:PiliPlus/utils/extension/get_ext.dart';
|
|
||||||
import 'package:flutter/material.dart' hide DraggableScrollableSheet;
|
import 'package:flutter/material.dart' hide DraggableScrollableSheet;
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
|
|
||||||
@@ -20,52 +18,59 @@ class DynamicsPage extends StatefulWidget {
|
|||||||
|
|
||||||
class _DynamicsPageState extends State<DynamicsPage>
|
class _DynamicsPageState extends State<DynamicsPage>
|
||||||
with AutomaticKeepAliveClientMixin {
|
with AutomaticKeepAliveClientMixin {
|
||||||
final _dynamicsController = Get.putOrFind(DynamicsController.new);
|
late ColorScheme colorScheme;
|
||||||
UpPanelPosition get upPanelPosition => _dynamicsController.upPanelPosition;
|
late final DynamicsController _outerController;
|
||||||
|
late final DynamicsTabController _innerController;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool get wantKeepAlive => true;
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_outerController = Get.put(DynamicsController());
|
||||||
|
_innerController = Get.put(DynamicsTabController());
|
||||||
|
}
|
||||||
|
|
||||||
Widget _createDynamicBtn(ThemeData theme, {bool isRight = true}) => Center(
|
@override
|
||||||
child: Container(
|
void didChangeDependencies() {
|
||||||
width: 34,
|
super.didChangeDependencies();
|
||||||
height: 34,
|
colorScheme = ColorScheme.of(context);
|
||||||
margin: EdgeInsets.only(left: !isRight ? 16 : 0, right: isRight ? 16 : 0),
|
}
|
||||||
child: IconButton(
|
|
||||||
tooltip: '发布动态',
|
Widget _createDynamicBtn() => SizedBox(
|
||||||
style: ButtonStyle(
|
width: 34,
|
||||||
padding: const WidgetStatePropertyAll(EdgeInsets.zero),
|
height: 34,
|
||||||
backgroundColor: WidgetStatePropertyAll(
|
child: IconButton(
|
||||||
theme.colorScheme.secondaryContainer,
|
tooltip: '发布动态',
|
||||||
),
|
style: ButtonStyle(
|
||||||
),
|
padding: const WidgetStatePropertyAll(.zero),
|
||||||
onPressed: () => CreateDynPanel.onCreateDyn(context),
|
backgroundColor: WidgetStatePropertyAll(
|
||||||
icon: Icon(
|
colorScheme.secondaryContainer,
|
||||||
Icons.add,
|
|
||||||
size: 18,
|
|
||||||
color: theme.colorScheme.onSecondaryContainer,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
onPressed: () => CreateDynPanel.onCreateDyn(context),
|
||||||
|
icon: Icon(
|
||||||
|
Icons.add,
|
||||||
|
size: 18,
|
||||||
|
color: colorScheme.onSecondaryContainer,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget upPanelPart(ThemeData theme, {bool isTop = false}) {
|
Widget upPanelPart({bool isTop = false}) {
|
||||||
final needBg = upPanelPosition.index > 2;
|
|
||||||
return Material(
|
return Material(
|
||||||
type: needBg ? .canvas : .transparency,
|
type: .transparency,
|
||||||
color: needBg ? theme.colorScheme.surface : null,
|
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
width: isTop ? null : 64,
|
width: isTop ? null : 64,
|
||||||
height: isTop ? 76 : null,
|
height: isTop ? 76 : null,
|
||||||
child: NotificationListener<ScrollEndNotification>(
|
child: NotificationListener<ScrollEndNotification>(
|
||||||
onNotification: (notification) {
|
onNotification: (notification) {
|
||||||
final metrics = notification.metrics;
|
final metrics = notification.metrics;
|
||||||
if (metrics.pixels >= metrics.maxScrollExtent - 300) {
|
if (metrics.pixels >= metrics.maxScrollExtent - 300 &&
|
||||||
_dynamicsController.onLoadMoreUp();
|
!_outerController.isEnd) {
|
||||||
|
_outerController.onLoadMore();
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
child: Obx(() => _buildUpPanel(_dynamicsController.upState.value)),
|
child: Obx(() => _buildUpPanel(_outerController.loadingState.value)),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -74,15 +79,14 @@ class _DynamicsPageState extends State<DynamicsPage>
|
|||||||
Widget _buildUpPanel(LoadingState<FollowUpModel> upState) {
|
Widget _buildUpPanel(LoadingState<FollowUpModel> upState) {
|
||||||
return switch (upState) {
|
return switch (upState) {
|
||||||
Loading() => const SizedBox.shrink(),
|
Loading() => const SizedBox.shrink(),
|
||||||
Success<FollowUpModel>() => UpPanel(
|
Success<FollowUpModel>(:final response) => UpPanel(
|
||||||
dynamicsController: _dynamicsController,
|
controller: _outerController,
|
||||||
|
upData: response,
|
||||||
),
|
),
|
||||||
Error() => Center(
|
Error() => Center(
|
||||||
child: IconButton(
|
child: IconButton(
|
||||||
icon: const Icon(Icons.refresh),
|
icon: const Icon(Icons.refresh),
|
||||||
onPressed: () => _dynamicsController
|
onPressed: _outerController.onReload,
|
||||||
..upState.value = LoadingState<FollowUpModel>.loading()
|
|
||||||
..queryFollowUp(),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
@@ -91,79 +95,107 @@ class _DynamicsPageState extends State<DynamicsPage>
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
super.build(context);
|
super.build(context);
|
||||||
final theme = Theme.of(context);
|
|
||||||
|
|
||||||
Widget? leading;
|
final tab = _buildTab();
|
||||||
List<Widget>? actions;
|
Widget child = DynamicsTabPage(controller: _innerController);
|
||||||
|
|
||||||
Widget child = tabBarView(
|
switch (_outerController.upPanelPosition) {
|
||||||
controller: _dynamicsController.tabController,
|
|
||||||
children: DynamicsTabType.values
|
|
||||||
.map((e) => DynamicsTabPage(dynamicsType: e))
|
|
||||||
.toList(),
|
|
||||||
);
|
|
||||||
|
|
||||||
switch (upPanelPosition) {
|
|
||||||
case .top:
|
case .top:
|
||||||
child = Column(
|
return Column(
|
||||||
children: [
|
children: [
|
||||||
upPanelPart(theme, isTop: true),
|
tab,
|
||||||
|
upPanelPart(isTop: true),
|
||||||
Expanded(child: child),
|
Expanded(child: child),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
actions = [_createDynamicBtn(theme)];
|
|
||||||
case .leftFixed:
|
case .leftFixed:
|
||||||
child = Row(
|
child = Row(
|
||||||
children: [
|
children: [
|
||||||
upPanelPart(theme),
|
upPanelPart(),
|
||||||
Expanded(child: child),
|
Expanded(child: child),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
actions = [_createDynamicBtn(theme)];
|
|
||||||
case .rightFixed:
|
case .rightFixed:
|
||||||
child = Row(
|
child = Row(
|
||||||
children: [
|
children: [
|
||||||
Expanded(child: child),
|
Expanded(child: child),
|
||||||
upPanelPart(theme),
|
upPanelPart(),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
actions = [_createDynamicBtn(theme)];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return Column(
|
return Column(
|
||||||
children: [
|
children: [
|
||||||
AppBar(
|
tab,
|
||||||
primary: false,
|
|
||||||
leading: leading,
|
|
||||||
leadingWidth: 50,
|
|
||||||
toolbarHeight: 50,
|
|
||||||
backgroundColor: Colors.transparent,
|
|
||||||
title: SizedBox(
|
|
||||||
height: 50,
|
|
||||||
child: TabBar(
|
|
||||||
dividerHeight: 0,
|
|
||||||
isScrollable: true,
|
|
||||||
tabAlignment: .center,
|
|
||||||
dividerColor: Colors.transparent,
|
|
||||||
labelColor: theme.colorScheme.primary,
|
|
||||||
indicatorColor: theme.colorScheme.primary,
|
|
||||||
controller: _dynamicsController.tabController,
|
|
||||||
unselectedLabelColor: theme.colorScheme.onSurface,
|
|
||||||
labelStyle: const TextStyle(fontSize: 13),
|
|
||||||
tabs: DynamicsTabType.values
|
|
||||||
.map((e) => Tab(text: e.label))
|
|
||||||
.toList(),
|
|
||||||
onTap: (index) {
|
|
||||||
if (!_dynamicsController.tabController.indexIsChanging) {
|
|
||||||
_dynamicsController.animateToTop();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
actions: actions,
|
|
||||||
),
|
|
||||||
Expanded(child: child),
|
Expanded(child: child),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget _buildTab() {
|
||||||
|
return Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: Obx(
|
||||||
|
() {
|
||||||
|
final dynamicsType = _innerController.dynamicsType.value;
|
||||||
|
return Row(
|
||||||
|
children: DynamicsTabType.values.map((e) {
|
||||||
|
final isCurr = e == dynamicsType;
|
||||||
|
return InkWell(
|
||||||
|
onTap: e == .up && !isCurr
|
||||||
|
? null
|
||||||
|
: () {
|
||||||
|
if (isCurr) {
|
||||||
|
_outerController.animateToTop();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (dynamicsType == .up) {
|
||||||
|
_innerController.hostMid = -1;
|
||||||
|
_outerController.loadingState.refresh();
|
||||||
|
}
|
||||||
|
_innerController
|
||||||
|
..dynamicsType.value = e
|
||||||
|
..onReload();
|
||||||
|
},
|
||||||
|
child: DecoratedBox(
|
||||||
|
decoration: isCurr
|
||||||
|
? BoxDecoration(
|
||||||
|
border: Border(
|
||||||
|
bottom: BorderSide(
|
||||||
|
width: 2.0,
|
||||||
|
color: colorScheme.primary,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: const BoxDecoration(),
|
||||||
|
child: Container(
|
||||||
|
height: 46,
|
||||||
|
alignment: .center,
|
||||||
|
padding: const .symmetric(horizontal: 16),
|
||||||
|
child: Text(
|
||||||
|
e.label,
|
||||||
|
style: isCurr
|
||||||
|
? TextStyle(
|
||||||
|
fontSize: 13,
|
||||||
|
color: colorScheme.primary,
|
||||||
|
)
|
||||||
|
: const TextStyle(fontSize: 13),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}).toList(),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
_createDynamicBtn(),
|
||||||
|
const SizedBox(width: 16),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool get wantKeepAlive => true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,65 +10,181 @@ import 'package:PiliPlus/utils/platform_utils.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
|
|
||||||
class UpPanel extends StatefulWidget {
|
class UpPanel extends StatelessWidget {
|
||||||
const UpPanel({
|
const UpPanel({
|
||||||
required this.dynamicsController,
|
|
||||||
super.key,
|
super.key,
|
||||||
|
required this.upData,
|
||||||
|
required this.controller,
|
||||||
});
|
});
|
||||||
|
|
||||||
final DynamicsController dynamicsController;
|
final FollowUpModel upData;
|
||||||
|
final DynamicsController controller;
|
||||||
@override
|
|
||||||
State<UpPanel> createState() => _UpPanelState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _UpPanelState extends State<UpPanel> {
|
|
||||||
late final controller = widget.dynamicsController;
|
|
||||||
late final isTop = controller.upPanelPosition == .top;
|
|
||||||
|
|
||||||
void toFollowPage() => Get.to(const LiveFollowPage());
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final accountService = controller.accountService;
|
|
||||||
if (!accountService.isLogin.value) {
|
|
||||||
return const SizedBox.shrink();
|
|
||||||
}
|
|
||||||
final theme = Theme.of(context);
|
final theme = Theme.of(context);
|
||||||
final upData = controller.upState.value.data;
|
final upList = upData.upList;
|
||||||
final List<UpItem> upList = upData.upList;
|
final liveList = upData.liveUsers?.items;
|
||||||
final List<LiveUserItem>? liveList = upData.liveUsers?.items;
|
|
||||||
|
final isTop = controller.upPanelPosition == .top;
|
||||||
|
void toFollowPage() => Get.to(const LiveFollowPage());
|
||||||
|
|
||||||
|
Widget buildItem(UpItem item) {
|
||||||
|
final hostMid = controller.innerController.hostMid;
|
||||||
|
final isLive = item is LiveUserItem;
|
||||||
|
final isCurrent = isLive || hostMid == -1 || hostMid == item.mid;
|
||||||
|
|
||||||
|
final isAll = item.mid == -1;
|
||||||
|
void toMemberPage() => Get.toNamed('/member?mid=${item.mid}');
|
||||||
|
|
||||||
|
Widget avatar;
|
||||||
|
if (isAll) {
|
||||||
|
avatar = DecoratedBox(
|
||||||
|
decoration: const BoxDecoration(
|
||||||
|
shape: .circle,
|
||||||
|
color: Color(0xFF5CB67B),
|
||||||
|
),
|
||||||
|
child: Image.asset(
|
||||||
|
width: 38,
|
||||||
|
height: 38,
|
||||||
|
cacheWidth: 38.cacheSize,
|
||||||
|
Assets.logo2,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
avatar = Padding(
|
||||||
|
padding: const .symmetric(horizontal: 4),
|
||||||
|
child: NetworkImgLayer(
|
||||||
|
width: 38,
|
||||||
|
height: 38,
|
||||||
|
src: item.face,
|
||||||
|
type: .avatar,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
if (isLive) {
|
||||||
|
avatar = Stack(
|
||||||
|
clipBehavior: .none,
|
||||||
|
children: [
|
||||||
|
avatar,
|
||||||
|
Positioned(
|
||||||
|
top: isLive && !isTop ? -5 : 0,
|
||||||
|
right: -6,
|
||||||
|
child: Badge(
|
||||||
|
label: const Text(' Live '),
|
||||||
|
textColor: theme.colorScheme.onSecondaryContainer,
|
||||||
|
backgroundColor: theme.colorScheme.secondaryContainer
|
||||||
|
.withValues(alpha: 0.75),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
} else if (item.hasUpdate ?? false) {
|
||||||
|
avatar = Stack(
|
||||||
|
clipBehavior: .none,
|
||||||
|
children: [
|
||||||
|
avatar,
|
||||||
|
Positioned(
|
||||||
|
top: 0,
|
||||||
|
right: 4,
|
||||||
|
child: Badge(
|
||||||
|
smallSize: 8,
|
||||||
|
backgroundColor: theme.colorScheme.primary,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return SizedBox(
|
||||||
|
height: 76,
|
||||||
|
width: isTop ? 70 : null,
|
||||||
|
child: InkWell(
|
||||||
|
onTap: () {
|
||||||
|
if (isLive) {
|
||||||
|
PageUtils.toLiveRoom(item.roomId);
|
||||||
|
} else {
|
||||||
|
if (isAll) {
|
||||||
|
if (hostMid == -1) {
|
||||||
|
controller.singleRefresh();
|
||||||
|
}
|
||||||
|
controller.innerController.dynamicsType.value = .all;
|
||||||
|
} else {
|
||||||
|
item.hasUpdate = false;
|
||||||
|
controller.innerController.dynamicsType.value = .up;
|
||||||
|
}
|
||||||
|
controller.innerController
|
||||||
|
..hostMid = item.mid
|
||||||
|
..onReload();
|
||||||
|
(context as Element).markNeedsBuild();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// onDoubleTap: isLive ? () => _onSelect(data) : null,
|
||||||
|
onLongPress: !isAll ? toMemberPage : null,
|
||||||
|
onSecondaryTap: !isAll && !PlatformUtils.isMobile
|
||||||
|
? toMemberPage
|
||||||
|
: null,
|
||||||
|
child: Opacity(
|
||||||
|
opacity: isCurrent ? 1 : 0.6,
|
||||||
|
child: Column(
|
||||||
|
spacing: 4,
|
||||||
|
mainAxisSize: .min,
|
||||||
|
mainAxisAlignment: .center,
|
||||||
|
children: [
|
||||||
|
avatar,
|
||||||
|
Padding(
|
||||||
|
padding: const .symmetric(horizontal: 4),
|
||||||
|
child: Text(
|
||||||
|
isTop ? '${item.uname}\n' : item.uname!,
|
||||||
|
maxLines: 2,
|
||||||
|
textAlign: .center,
|
||||||
|
style: TextStyle(
|
||||||
|
color: hostMid == item.mid
|
||||||
|
? theme.colorScheme.primary
|
||||||
|
: theme.colorScheme.outline,
|
||||||
|
height: 1.1,
|
||||||
|
fontSize: 12.5,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return CustomScrollView(
|
return CustomScrollView(
|
||||||
scrollDirection: isTop ? Axis.horizontal : Axis.vertical,
|
|
||||||
physics: const AlwaysScrollableScrollPhysics(),
|
|
||||||
controller: controller.scrollController,
|
controller: controller.scrollController,
|
||||||
|
physics: const AlwaysScrollableScrollPhysics(),
|
||||||
|
scrollDirection: isTop ? .horizontal : .vertical,
|
||||||
slivers: [
|
slivers: [
|
||||||
SliverToBoxAdapter(
|
SliverToBoxAdapter(
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
onTap: () => setState(() {
|
onTap: () {
|
||||||
controller.showLiveUp = !controller.showLiveUp;
|
controller.showLiveUp = !controller.showLiveUp;
|
||||||
}),
|
(context as Element).markNeedsBuild();
|
||||||
|
},
|
||||||
onLongPress: toFollowPage,
|
onLongPress: toFollowPage,
|
||||||
onSecondaryTap: PlatformUtils.isMobile ? null : toFollowPage,
|
onSecondaryTap: PlatformUtils.isMobile ? null : toFollowPage,
|
||||||
child: Container(
|
child: Container(
|
||||||
alignment: Alignment.center,
|
alignment: .center,
|
||||||
height: isTop ? 76 : 60,
|
height: isTop ? 76 : 60,
|
||||||
padding: isTop ? const EdgeInsets.only(left: 12, right: 6) : null,
|
padding: isTop ? const .only(left: 12, right: 6) : null,
|
||||||
child: Text.rich(
|
child: Text.rich(
|
||||||
textAlign: TextAlign.center,
|
textAlign: .center,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 13,
|
fontSize: 13,
|
||||||
color: theme.colorScheme.primary,
|
color: theme.colorScheme.primary,
|
||||||
),
|
),
|
||||||
TextSpan(
|
TextSpan(
|
||||||
children: [
|
children: [
|
||||||
TextSpan(
|
TextSpan(text: 'Live(${upData.liveUsers?.count ?? 0})'),
|
||||||
text: 'Live(${upData.liveUsers?.count ?? 0})',
|
|
||||||
),
|
|
||||||
if (!isTop) ...[
|
if (!isTop) ...[
|
||||||
const TextSpan(text: '\n'),
|
const TextSpan(text: '\n'),
|
||||||
WidgetSpan(
|
WidgetSpan(
|
||||||
alignment: PlaceholderAlignment.middle,
|
alignment: .middle,
|
||||||
child: Icon(
|
child: Icon(
|
||||||
controller.showLiveUp
|
controller.showLiveUp
|
||||||
? Icons.expand_less
|
? Icons.expand_less
|
||||||
@@ -79,7 +195,7 @@ class _UpPanelState extends State<UpPanel> {
|
|||||||
),
|
),
|
||||||
] else
|
] else
|
||||||
WidgetSpan(
|
WidgetSpan(
|
||||||
alignment: PlaceholderAlignment.middle,
|
alignment: .middle,
|
||||||
child: Icon(
|
child: Icon(
|
||||||
controller.showLiveUp
|
controller.showLiveUp
|
||||||
? Icons.keyboard_arrow_right
|
? Icons.keyboard_arrow_right
|
||||||
@@ -98,157 +214,32 @@ class _UpPanelState extends State<UpPanel> {
|
|||||||
SliverList.builder(
|
SliverList.builder(
|
||||||
itemCount: liveList.length,
|
itemCount: liveList.length,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
return upItemBuild(theme, liveList[index]);
|
return buildItem(liveList[index]);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
SliverToBoxAdapter(
|
SliverToBoxAdapter(
|
||||||
child: upItemBuild(theme, UpItem(face: '', uname: '全部动态', mid: -1)),
|
child: buildItem(UpItem(face: '', uname: '全部动态', mid: -1)),
|
||||||
),
|
),
|
||||||
SliverToBoxAdapter(
|
SliverToBoxAdapter(
|
||||||
child: Obx(
|
child: Obx(
|
||||||
() => upItemBuild(
|
() => buildItem(
|
||||||
theme,
|
|
||||||
UpItem(
|
UpItem(
|
||||||
uname: '我',
|
uname: '我',
|
||||||
face: accountService.face.value,
|
face: controller.accountService.face.value,
|
||||||
mid: Accounts.main.mid,
|
mid: Accounts.main.mid,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (upList.isNotEmpty)
|
if (upList != null && upList.isNotEmpty)
|
||||||
SliverList.builder(
|
SliverList.builder(
|
||||||
itemCount: upList.length,
|
itemCount: upList.length,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
return upItemBuild(theme, upList[index]);
|
return buildItem(upList[index]);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
if (!isTop) const SliverToBoxAdapter(child: SizedBox(height: 200)),
|
if (!isTop) const SliverToBoxAdapter(child: SizedBox(height: 200)),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onSelect(UpItem data) {
|
|
||||||
controller
|
|
||||||
..currentMid = data.mid
|
|
||||||
..onSelectUp(data.mid);
|
|
||||||
|
|
||||||
data.hasUpdate = false;
|
|
||||||
|
|
||||||
setState(() {});
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget upItemBuild(ThemeData theme, UpItem data) {
|
|
||||||
final currentMid = controller.currentMid;
|
|
||||||
final isLive = data is LiveUserItem;
|
|
||||||
final isCurrent = isLive || currentMid == data.mid || currentMid == -1;
|
|
||||||
|
|
||||||
final isAll = data.mid == -1;
|
|
||||||
void toMemberPage() => Get.toNamed('/member?mid=${data.mid}');
|
|
||||||
|
|
||||||
Widget avatar;
|
|
||||||
if (isAll) {
|
|
||||||
avatar = DecoratedBox(
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
shape: .circle,
|
|
||||||
border: Border.all(
|
|
||||||
width: 5,
|
|
||||||
color: const Color(0xFF5CB67B),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: Image.asset(
|
|
||||||
width: 38,
|
|
||||||
height: 38,
|
|
||||||
cacheWidth: 38.cacheSize,
|
|
||||||
Assets.logo,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
avatar = Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 4),
|
|
||||||
child: NetworkImgLayer(
|
|
||||||
width: 38,
|
|
||||||
height: 38,
|
|
||||||
src: data.face,
|
|
||||||
type: .avatar,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
if (isLive) {
|
|
||||||
avatar = Stack(
|
|
||||||
clipBehavior: .none,
|
|
||||||
children: [
|
|
||||||
avatar,
|
|
||||||
Positioned(
|
|
||||||
top: isLive && !isTop ? -5 : 0,
|
|
||||||
right: -6,
|
|
||||||
child: Badge(
|
|
||||||
label: const Text(' Live '),
|
|
||||||
textColor: theme.colorScheme.onSecondaryContainer,
|
|
||||||
backgroundColor: theme.colorScheme.secondaryContainer
|
|
||||||
.withValues(alpha: 0.75),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
} else if (data.hasUpdate ?? false) {
|
|
||||||
avatar = Stack(
|
|
||||||
clipBehavior: .none,
|
|
||||||
children: [
|
|
||||||
avatar,
|
|
||||||
Positioned(
|
|
||||||
top: 0,
|
|
||||||
right: 4,
|
|
||||||
child: Badge(
|
|
||||||
smallSize: 8,
|
|
||||||
backgroundColor: theme.colorScheme.primary,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return SizedBox(
|
|
||||||
height: 76,
|
|
||||||
width: isTop ? 70 : null,
|
|
||||||
child: InkWell(
|
|
||||||
onTap: () {
|
|
||||||
if (isLive) {
|
|
||||||
PageUtils.toLiveRoom(data.roomId);
|
|
||||||
} else {
|
|
||||||
_onSelect(data);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// onDoubleTap: isLive ? () => _onSelect(data) : null,
|
|
||||||
onLongPress: !isAll ? toMemberPage : null,
|
|
||||||
onSecondaryTap: !isAll && !PlatformUtils.isMobile ? toMemberPage : null,
|
|
||||||
child: Opacity(
|
|
||||||
opacity: isCurrent ? 1 : 0.6,
|
|
||||||
child: Column(
|
|
||||||
spacing: 4,
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
avatar,
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 4),
|
|
||||||
child: Text(
|
|
||||||
isTop ? '${data.uname}\n' : data.uname!,
|
|
||||||
maxLines: 2,
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
style: TextStyle(
|
|
||||||
color: currentMid == data.mid
|
|
||||||
? theme.colorScheme.primary
|
|
||||||
: theme.colorScheme.outline,
|
|
||||||
height: 1.1,
|
|
||||||
fontSize: 12.5,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
import 'dart:async' show StreamSubscription;
|
|
||||||
|
|
||||||
import 'package:PiliPlus/http/dynamics.dart';
|
import 'package:PiliPlus/http/dynamics.dart';
|
||||||
import 'package:PiliPlus/http/loading_state.dart';
|
import 'package:PiliPlus/http/loading_state.dart';
|
||||||
import 'package:PiliPlus/http/msg.dart';
|
import 'package:PiliPlus/http/msg.dart';
|
||||||
@@ -17,53 +15,41 @@ import 'package:get/get.dart';
|
|||||||
class DynamicsTabController
|
class DynamicsTabController
|
||||||
extends CommonListController<DynamicsDataModel, DynamicItemModel>
|
extends CommonListController<DynamicsDataModel, DynamicItemModel>
|
||||||
with AccountMixin, CommonReloadMixin {
|
with AccountMixin, CommonReloadMixin {
|
||||||
DynamicsTabController({required this.dynamicsType});
|
int hostMid = -1;
|
||||||
final DynamicsTabType dynamicsType;
|
String? offset;
|
||||||
String offset = '';
|
final Rx<DynamicsTabType> dynamicsType = Rx(.all);
|
||||||
int? mid;
|
|
||||||
late int flag = 0;
|
|
||||||
|
|
||||||
late final MainController mainController = Get.find<MainController>();
|
late final outerController = Get.find<DynamicsController>();
|
||||||
final dynamicsController = Get.find<DynamicsController>();
|
late final MainController _mainController = Get.find<MainController>();
|
||||||
StreamSubscription? _listener;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onInit() {
|
void onInit() {
|
||||||
super.onInit();
|
super.onInit();
|
||||||
queryData();
|
queryData();
|
||||||
if (dynamicsType == .up) {
|
|
||||||
_listener = dynamicsController.mid.listen((mid) {
|
|
||||||
if (mid != -1) {
|
|
||||||
flag++;
|
|
||||||
this.mid = mid;
|
|
||||||
onReload();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> onRefresh() {
|
Future<void> onRefresh() {
|
||||||
if (dynamicsType == .all) {
|
if (dynamicsType.value == .all) {
|
||||||
mainController.setDynCount();
|
_mainController.setDynCount();
|
||||||
}
|
}
|
||||||
offset = '';
|
offset = null;
|
||||||
return super.onRefresh();
|
return super.onRefresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<DynamicItemModel>? getDataList(DynamicsDataModel response) {
|
List<DynamicItemModel>? getDataList(DynamicsDataModel response) {
|
||||||
offset = response.offset ?? '';
|
offset = response.offset;
|
||||||
return response.items;
|
return response.items;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<LoadingState<DynamicsDataModel>> customGetData() =>
|
Future<LoadingState<DynamicsDataModel>> customGetData() =>
|
||||||
DynamicsHttp.followDynamic(
|
DynamicsHttp.followDynamic(
|
||||||
type: dynamicsType,
|
|
||||||
offset: offset,
|
offset: offset,
|
||||||
mid: mid,
|
hostMid: hostMid,
|
||||||
tempBannedList: dynamicsController.tempBannedList,
|
type: dynamicsType.value,
|
||||||
|
tempBannedList: outerController.tempBannedList,
|
||||||
);
|
);
|
||||||
|
|
||||||
Future<void> onRemove(int index, dynamic dynamicId) async {
|
Future<void> onRemove(int index, dynamic dynamicId) async {
|
||||||
@@ -79,7 +65,7 @@ class DynamicsTabController
|
|||||||
}
|
}
|
||||||
|
|
||||||
void onBlock(int index) {
|
void onBlock(int index) {
|
||||||
if (dynamicsType != .up) {
|
if (dynamicsType.value != .up) {
|
||||||
loadingState
|
loadingState
|
||||||
..value.data!.removeAt(index)
|
..value.data!.removeAt(index)
|
||||||
..refresh();
|
..refresh();
|
||||||
@@ -101,10 +87,4 @@ class DynamicsTabController
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
void onChangeAccount(bool isLogin) => onReload();
|
void onChangeAccount(bool isLogin) => onReload();
|
||||||
|
|
||||||
@override
|
|
||||||
void onClose() {
|
|
||||||
_listener?.cancel();
|
|
||||||
super.onClose();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,12 +2,9 @@ import 'package:PiliPlus/common/widgets/flutter/refresh_indicator.dart';
|
|||||||
import 'package:PiliPlus/common/widgets/loading_widget/http_error.dart';
|
import 'package:PiliPlus/common/widgets/loading_widget/http_error.dart';
|
||||||
import 'package:PiliPlus/common/widgets/scroll_physics.dart';
|
import 'package:PiliPlus/common/widgets/scroll_physics.dart';
|
||||||
import 'package:PiliPlus/http/loading_state.dart';
|
import 'package:PiliPlus/http/loading_state.dart';
|
||||||
import 'package:PiliPlus/models/common/dynamic/dynamics_type.dart';
|
|
||||||
import 'package:PiliPlus/models/dynamics/result.dart';
|
import 'package:PiliPlus/models/dynamics/result.dart';
|
||||||
import 'package:PiliPlus/pages/dynamics/controller.dart';
|
|
||||||
import 'package:PiliPlus/pages/dynamics/widgets/dynamic_panel.dart';
|
import 'package:PiliPlus/pages/dynamics/widgets/dynamic_panel.dart';
|
||||||
import 'package:PiliPlus/pages/dynamics_tab/controller.dart';
|
import 'package:PiliPlus/pages/dynamics_tab/controller.dart';
|
||||||
import 'package:PiliPlus/utils/extension/get_ext.dart';
|
|
||||||
import 'package:PiliPlus/utils/global_data.dart';
|
import 'package:PiliPlus/utils/global_data.dart';
|
||||||
import 'package:PiliPlus/utils/waterfall.dart';
|
import 'package:PiliPlus/utils/waterfall.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
@@ -15,53 +12,24 @@ import 'package:get/get.dart';
|
|||||||
import 'package:waterfall_flow/waterfall_flow.dart'
|
import 'package:waterfall_flow/waterfall_flow.dart'
|
||||||
hide SliverWaterfallFlowDelegateWithMaxCrossAxisExtent;
|
hide SliverWaterfallFlowDelegateWithMaxCrossAxisExtent;
|
||||||
|
|
||||||
class DynamicsTabPage extends StatefulWidget {
|
class DynamicsTabPage extends StatelessWidget with DynMixin {
|
||||||
const DynamicsTabPage({super.key, required this.dynamicsType});
|
DynamicsTabPage({
|
||||||
|
super.key,
|
||||||
|
required this.controller,
|
||||||
|
});
|
||||||
|
|
||||||
final DynamicsTabType dynamicsType;
|
final DynamicsTabController controller;
|
||||||
|
|
||||||
@override
|
|
||||||
State<DynamicsTabPage> createState() => _DynamicsTabPageState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _DynamicsTabPageState extends State<DynamicsTabPage>
|
|
||||||
with AutomaticKeepAliveClientMixin, DynMixin {
|
|
||||||
DynamicsController dynamicsController = Get.putOrFind(DynamicsController.new);
|
|
||||||
late final DynamicsTabController controller;
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool get wantKeepAlive => widget.dynamicsType == .all;
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
controller = Get.putOrFind(
|
|
||||||
() =>
|
|
||||||
DynamicsTabController(dynamicsType: widget.dynamicsType)
|
|
||||||
..mid = dynamicsController.mid.value,
|
|
||||||
tag: widget.dynamicsType.name,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
super.build(context);
|
|
||||||
return refreshIndicator(
|
return refreshIndicator(
|
||||||
onRefresh: () {
|
onRefresh: controller.outerController.onRefresh,
|
||||||
dynamicsController.queryFollowUp();
|
|
||||||
return controller.onRefresh();
|
|
||||||
},
|
|
||||||
child: CustomScrollView(
|
child: CustomScrollView(
|
||||||
key: switch (widget.dynamicsType) {
|
|
||||||
.all => null,
|
|
||||||
.up => PageStorageKey('${widget.dynamicsType}${controller.flag}'),
|
|
||||||
_ => PageStorageKey(widget.dynamicsType),
|
|
||||||
},
|
|
||||||
physics: ReloadScrollPhysics(controller: controller),
|
physics: ReloadScrollPhysics(controller: controller),
|
||||||
controller: controller.scrollController,
|
controller: controller.scrollController,
|
||||||
slivers: [
|
slivers: [
|
||||||
SliverPadding(
|
SliverPadding(
|
||||||
padding: const EdgeInsets.only(bottom: 100),
|
padding: const .only(bottom: 100),
|
||||||
sliver: buildPage(
|
sliver: buildPage(
|
||||||
Obx(() => _buildBody(controller.loadingState.value)),
|
Obx(() => _buildBody(controller.loadingState.value)),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -311,13 +311,15 @@ abstract final class RequestUtils {
|
|||||||
await Future.delayed(const Duration(milliseconds: 450));
|
await Future.delayed(const Duration(milliseconds: 450));
|
||||||
final res = await DynamicsHttp.dynamicDetail(id: id);
|
final res = await DynamicsHttp.dynamicDetail(id: id);
|
||||||
if (res case final Success<DynamicItemModel> e) {
|
if (res case final Success<DynamicItemModel> e) {
|
||||||
final ctr = Get.find<DynamicsTabController>(tag: 'all');
|
final ctr = Get.find<DynamicsTabController>();
|
||||||
if (ctr.loadingState.value case Success(:final response?)) {
|
if (ctr.dynamicsType.value == .all) {
|
||||||
response.insert(0, e.response);
|
if (ctr.loadingState.value case Success(:final response?)) {
|
||||||
ctr.loadingState.refresh();
|
response.insert(0, e.response);
|
||||||
return;
|
ctr.loadingState.refresh();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ctr.loadingState.value = Success([e.response]);
|
||||||
}
|
}
|
||||||
ctr.loadingState.value = Success([e.response]);
|
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (kDebugMode) debugPrint('create dyn $e');
|
if (kDebugMode) debugPrint('create dyn $e');
|
||||||
|
|||||||
Reference in New Issue
Block a user