优化批阅升级流程
This commit is contained in:
parent
57ec197956
commit
ef2684ca5d
|
|
@ -19,6 +19,7 @@ import 'package:get/get.dart';
|
||||||
import 'package:making_school_asignment_app/common/utils/app_upgrade/DownloadApk.dart';
|
import 'package:making_school_asignment_app/common/utils/app_upgrade/DownloadApk.dart';
|
||||||
import 'package:making_school_asignment_app/common/utils/app_upgrade/UpgradePermission.dart';
|
import 'package:making_school_asignment_app/common/utils/app_upgrade/UpgradePermission.dart';
|
||||||
import 'package:making_school_asignment_app/common/utils/app_upgrade/upgradeLogic.dart';
|
import 'package:making_school_asignment_app/common/utils/app_upgrade/upgradeLogic.dart';
|
||||||
|
import 'package:making_school_asignment_app/common/utils/utils.dart';
|
||||||
import 'package:making_school_asignment_app/page/global_widget/my_text.dart';
|
import 'package:making_school_asignment_app/page/global_widget/my_text.dart';
|
||||||
import 'package:percent_indicator/linear_percent_indicator.dart';
|
import 'package:percent_indicator/linear_percent_indicator.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
@ -33,14 +34,13 @@ class UpdateDialog extends Dialog {
|
||||||
final UpdateAppEvent updateAppEvent;
|
final UpdateAppEvent updateAppEvent;
|
||||||
final String deviceInfo;
|
final String deviceInfo;
|
||||||
|
|
||||||
const UpdateDialog(
|
const UpdateDialog({super.key, required this.updateAppEvent, required this.deviceInfo});
|
||||||
{super.key, required this.updateAppEvent, required this.deviceInfo});
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Center(
|
return Center(
|
||||||
child: Container(
|
child: Container(
|
||||||
width: 319.w,
|
width: isPad() ? ScreenUtil().screenWidth * 0.62 : 319.w,
|
||||||
height: 440.h,
|
height: 440.h,
|
||||||
padding: EdgeInsets.symmetric(vertical: 8.h),
|
padding: EdgeInsets.symmetric(vertical: 8.h),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
|
|
@ -61,7 +61,7 @@ class UpdateDialog extends Dialog {
|
||||||
children: [
|
children: [
|
||||||
Container(
|
Container(
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
margin: EdgeInsets.only(top: 128.h, bottom: 10.h),
|
margin: EdgeInsets.only(top:isPad()? 136.h : 128.h, bottom: 10.h),
|
||||||
child: quickText(
|
child: quickText(
|
||||||
updateAppEvent.title,
|
updateAppEvent.title,
|
||||||
size: 18.sp,
|
size: 18.sp,
|
||||||
|
|
@ -72,17 +72,11 @@ class UpdateDialog extends Dialog {
|
||||||
HtmlWidget(
|
HtmlWidget(
|
||||||
updateAppEvent.description,
|
updateAppEvent.description,
|
||||||
customStylesBuilder: (element) {
|
customStylesBuilder: (element) {
|
||||||
return {
|
return {'color': '#666666', 'font-weight': 'normal', 'text-decoration': 'none'};
|
||||||
'color': '#666666',
|
|
||||||
'font-weight': 'normal',
|
|
||||||
'text-decoration': 'none'
|
|
||||||
};
|
|
||||||
},
|
},
|
||||||
onLoadingBuilder: (context, element, loadingProgress) =>
|
onLoadingBuilder: (context, element, loadingProgress) => const CircularProgressIndicator(),
|
||||||
const CircularProgressIndicator(),
|
|
||||||
renderMode: RenderMode.column,
|
renderMode: RenderMode.column,
|
||||||
textStyle:
|
textStyle: TextStyle(fontSize: 14.sp, color: Colors.black87),
|
||||||
TextStyle(fontSize: 14.sp, color: Colors.black87),
|
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
@ -103,9 +97,7 @@ class UpdateDialog extends Dialog {
|
||||||
builder: (BuildContext context) {
|
builder: (BuildContext context) {
|
||||||
return WillPopScope(
|
return WillPopScope(
|
||||||
onWillPop: () async => false,
|
onWillPop: () async => false,
|
||||||
child: UpdateDialog(
|
child: UpdateDialog(updateAppEvent: updateAppEvent, deviceInfo: updateAppEvent.deviceInfo),
|
||||||
updateAppEvent: updateAppEvent,
|
|
||||||
deviceInfo: updateAppEvent.deviceInfo),
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
@ -128,7 +120,7 @@ class DownloadProgress extends StatelessWidget {
|
||||||
children: [
|
children: [
|
||||||
LinearPercentIndicator(
|
LinearPercentIndicator(
|
||||||
alignment: MainAxisAlignment.center,
|
alignment: MainAxisAlignment.center,
|
||||||
width: 245.w,
|
width: isPad() ? ScreenUtil().screenWidth * 0.55 : 245.w,
|
||||||
animation: false,
|
animation: false,
|
||||||
lineHeight: 20.0.h,
|
lineHeight: 20.0.h,
|
||||||
percent: logic.downloadRatio.value,
|
percent: logic.downloadRatio.value,
|
||||||
|
|
@ -144,10 +136,7 @@ class DownloadProgress extends StatelessWidget {
|
||||||
Container(
|
Container(
|
||||||
height: 6.h,
|
height: 6.h,
|
||||||
),
|
),
|
||||||
quickText('更新中...',
|
quickText('更新中...', size: 12.sp, fontWeight: FontWeight.w500, color: const Color.fromRGBO(90, 90, 90, 1))
|
||||||
size: 12.sp,
|
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
color: const Color.fromRGBO(90, 90, 90, 1))
|
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
@ -189,23 +178,18 @@ class DownloadButton extends StatelessWidget {
|
||||||
|
|
||||||
return Container(
|
return Container(
|
||||||
height: 38.h,
|
height: 38.h,
|
||||||
width: 245.w,
|
width: isPad() ? ScreenUtil().screenWidth * 0.45 : 245.w,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
borderRadius: BorderRadius.all(Radius.circular(42.h)),
|
borderRadius: BorderRadius.all(Radius.circular(42.h)),
|
||||||
gradient: LinearGradient(
|
gradient: LinearGradient(colors: [primaryColor, primaryColor.withOpacity(0.7)]),
|
||||||
colors: [primaryColor, primaryColor.withOpacity(0.7)]),
|
|
||||||
),
|
),
|
||||||
child: MaterialButton(
|
child: MaterialButton(
|
||||||
onPressed: () => easyThrottle('DownloadButton_App_Upgrade',
|
onPressed: () => easyThrottle('DownloadButton_App_Upgrade', duration: const Duration(milliseconds: 1000), () async {
|
||||||
duration: const Duration(milliseconds: 1000), () async {
|
if (deviceInfo == "android" && updateAppEvent.equipment == Equipment.android) {
|
||||||
if (deviceInfo == "android" &&
|
|
||||||
updateAppEvent.equipment == Equipment.android) {
|
|
||||||
// 权限检查 判断是否有读写内存的权限
|
// 权限检查 判断是否有读写内存的权限
|
||||||
bool flag = await UpgradePermission(updateAppEvent.deviceInfo)
|
bool flag = await UpgradePermission(updateAppEvent.deviceInfo).checkPermission(context, updateAppEvent);
|
||||||
.checkPermission(context, updateAppEvent);
|
|
||||||
if (flag) {
|
if (flag) {
|
||||||
flag = await DownloadApk.installApk(
|
flag = await DownloadApk.installApk(context, updateAppEvent, logic);
|
||||||
context, updateAppEvent, logic);
|
|
||||||
if (!flag) {
|
if (!flag) {
|
||||||
print('执行到了重置更新按钮的地方....');
|
print('执行到了重置更新按钮的地方....');
|
||||||
logic.downloadRatio.value = 0.0; // 更新失败重置 更新按钮
|
logic.downloadRatio.value = 0.0; // 更新失败重置 更新按钮
|
||||||
|
|
@ -217,8 +201,7 @@ class DownloadButton extends StatelessWidget {
|
||||||
}
|
}
|
||||||
// await FlutterClipboard.copy(updateAppEvent.link);
|
// await FlutterClipboard.copy(updateAppEvent.link);
|
||||||
// setTimeOut(1000, () => ToastUtils.showInfo('下载链接已经复制到设备,可前往浏览器下载安装'));
|
// setTimeOut(1000, () => ToastUtils.showInfo('下载链接已经复制到设备,可前往浏览器下载安装'));
|
||||||
} else if (deviceInfo == "ios" &&
|
} else if (deviceInfo == "ios" && updateAppEvent.equipment == Equipment.ios) {
|
||||||
updateAppEvent.equipment == Equipment.ios) {
|
|
||||||
try {
|
try {
|
||||||
print(updateAppEvent.link);
|
print(updateAppEvent.link);
|
||||||
await launchUrlString(updateAppEvent.link);
|
await launchUrlString(updateAppEvent.link);
|
||||||
|
|
@ -232,8 +215,7 @@ class DownloadButton extends StatelessWidget {
|
||||||
// await autoUpdater.setScheduledCheckInterval(0);
|
// await autoUpdater.setScheduledCheckInterval(0);
|
||||||
// }
|
// }
|
||||||
}),
|
}),
|
||||||
child: quickText(!logic.loadingApk.value ? '立即体验' : '正在下载...',
|
child: quickText(!logic.loadingApk.value ? '立即体验' : '正在下载...', size: 16.sp, color: Colors.white, fontWeight: FontWeight.w500),
|
||||||
size: 16.sp, color: Colors.white, fontWeight: FontWeight.w500),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ class UpgradeLogic extends GetxController with RequestToolMixin {
|
||||||
// super.onInit();
|
// super.onInit();
|
||||||
// }
|
// }
|
||||||
|
|
||||||
void getAppUpgrade(BuildContext context) async {
|
Future<void> getAppUpgrade(BuildContext context) async {
|
||||||
if (!const bool.fromEnvironment('dart.vm.product')) return;
|
if (!const bool.fromEnvironment('dart.vm.product')) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
|
||||||
|
|
@ -39,12 +39,13 @@ class _StartPageState extends State<StartPage> with RequestToolMixin {
|
||||||
|
|
||||||
Future.delayed(const Duration(seconds: 3), () => FlutterNativeSplash.remove());
|
Future.delayed(const Duration(seconds: 3), () => FlutterNativeSplash.remove());
|
||||||
|
|
||||||
Get.put(HomeLogic());
|
|
||||||
Get.put(WorkLogic());
|
|
||||||
// const WorkPage(),
|
// const WorkPage(),
|
||||||
_bodyList = [const HomePage(), const MyInfo()];
|
_bodyList = [const HomePage(), const MyInfo()];
|
||||||
// APP 启动后就直接更新
|
// APP 启动后就直接更新
|
||||||
if (!_upgradeLogic.showUpgrade.value) _upgradeLogic.getAppUpgrade(context);
|
WidgetsBinding.instance.addPostFrameCallback((e) {
|
||||||
|
/// 首页用户未更新不进行更新判断,没有登陆的用户在登陆页面和40秒回调中检查弹出更新APP
|
||||||
|
if (!_upgradeLogic.showUpgrade.value && UserStore.to.userDetailInfo.value != null) _upgradeLogic.getAppUpgrade(context);
|
||||||
|
});
|
||||||
_timer?.cancel();
|
_timer?.cancel();
|
||||||
_timer = Timer.periodic(const Duration(seconds: 40), (e) {
|
_timer = Timer.periodic(const Duration(seconds: 40), (e) {
|
||||||
if (Get.currentRoute == Routes.login) return; // 在登录页面不更新APP
|
if (Get.currentRoute == Routes.login) return; // 在登录页面不更新APP
|
||||||
|
|
|
||||||
|
|
@ -143,7 +143,14 @@ class _MyInfoState extends State<MyInfo> with AutomaticKeepAliveClientMixin {
|
||||||
onTap: () {
|
onTap: () {
|
||||||
_showAlertDialog(context);
|
_showAlertDialog(context);
|
||||||
},
|
},
|
||||||
child: Image.asset('assets/images/out_icon.png'),
|
child: Container(
|
||||||
|
padding: EdgeInsets.all(2.r),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: const Color.fromRGBO(255, 255, 255, 1),
|
||||||
|
borderRadius: BorderRadius.circular(50.r),
|
||||||
|
),
|
||||||
|
child: Image.asset('assets/images/out_icon.png',fit: BoxFit.cover),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import 'package:flutter_easyloading/flutter_easyloading.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:making_school_asignment_app/common/mixins/request_tool_mixin.dart';
|
import 'package:making_school_asignment_app/common/mixins/request_tool_mixin.dart';
|
||||||
import 'package:making_school_asignment_app/common/store/user_store.dart';
|
import 'package:making_school_asignment_app/common/store/user_store.dart';
|
||||||
|
import 'package:making_school_asignment_app/common/utils/app_upgrade/upgradeLogic.dart';
|
||||||
import 'package:making_school_asignment_app/common/utils/storage.dart';
|
import 'package:making_school_asignment_app/common/utils/storage.dart';
|
||||||
import 'package:making_school_asignment_app/common/utils/toast_utils.dart';
|
import 'package:making_school_asignment_app/common/utils/toast_utils.dart';
|
||||||
import 'package:making_school_asignment_app/common/utils/utils.dart';
|
import 'package:making_school_asignment_app/common/utils/utils.dart';
|
||||||
|
|
@ -55,13 +56,17 @@ class LoginLogic extends GetxController with RequestToolMixin {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 前往登录
|
/// 前往登录
|
||||||
void toLogin(BuildContext context) async {
|
void toLogin(BuildContext context, UpgradeLogic upgradeLogic) async {
|
||||||
if (!state.canLogin.value) return;
|
if (!state.canLogin.value) return;
|
||||||
Utils.hideKeyboard();
|
Utils.hideKeyboard();
|
||||||
state.canLogin.value = false;
|
state.canLogin.value = false;
|
||||||
|
|
||||||
void toMsg(msg) {
|
void toMsg(msg, [bool error = true]) {
|
||||||
|
if (error) {
|
||||||
ToastUtils.showError(msg);
|
ToastUtils.showError(msg);
|
||||||
|
} else {
|
||||||
|
ToastUtils.showInfo(msg);
|
||||||
|
}
|
||||||
state.canLogin.value = true;
|
state.canLogin.value = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -78,6 +83,11 @@ class LoginLogic extends GetxController with RequestToolMixin {
|
||||||
EasyLoading.show(status: 'loading...');
|
EasyLoading.show(status: 'loading...');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
/// 检查升级APP
|
||||||
|
if (userName != 'AppleTester' && const bool.fromEnvironment('dart.vm.product')) {
|
||||||
|
upgradeLogic.getAppUpgrade(Get.context ?? context);
|
||||||
|
return toMsg('请在升级APP后再次登陆', false);
|
||||||
|
}
|
||||||
await getClient().toLogin(userName, userPwd);
|
await getClient().toLogin(userName, userPwd);
|
||||||
String? nameidentifier = UserStore.to.userInfo.value?.nameidentifier;
|
String? nameidentifier = UserStore.to.userInfo.value?.nameidentifier;
|
||||||
if (nameidentifier == null) {
|
if (nameidentifier == null) {
|
||||||
|
|
@ -88,7 +98,6 @@ class LoginLogic extends GetxController with RequestToolMixin {
|
||||||
throw Exception('用户信息获取失败');
|
throw Exception('用户信息获取失败');
|
||||||
}
|
}
|
||||||
UserStore.to.setUserDetailInfo(data);
|
UserStore.to.setUserDetailInfo(data);
|
||||||
EasyLoading.dismiss();
|
|
||||||
|
|
||||||
if (state.keepPwd.value) {
|
if (state.keepPwd.value) {
|
||||||
StorageService.to.write(AppStorageKey.account.value, userName);
|
StorageService.to.write(AppStorageKey.account.value, userName);
|
||||||
|
|
@ -96,39 +105,8 @@ class LoginLogic extends GetxController with RequestToolMixin {
|
||||||
}
|
}
|
||||||
|
|
||||||
Get.offAllNamed(Routes.startPage);
|
Get.offAllNamed(Routes.startPage);
|
||||||
// if (resultData.code != 200 || userData?.accessToken == null || userData?.accessToken == '') {
|
|
||||||
// return toMsg(resultData.message ?? '登录失败,请重试');
|
|
||||||
// }
|
|
||||||
|
|
||||||
// UserStore.to.setToken(userData!.accessToken);
|
|
||||||
// state.canLogin.value = true;
|
|
||||||
|
|
||||||
// BaseStructureResult<UserInfo> userRes = await UserApi.getUserInfo();
|
|
||||||
// print('99999999999');
|
|
||||||
// print(userRes.data!.userName);
|
|
||||||
// if (userRes.code != 200 || userRes.data == null) {
|
|
||||||
// throw Exception('登录失败,请重试');
|
|
||||||
// } else {
|
|
||||||
// UserStore.to.setUserInfo(userRes.data!);
|
|
||||||
// Get.offAllNamed(Routes.home);
|
|
||||||
// }
|
|
||||||
|
|
||||||
/* fastData.setUser(userRes.data!).then((value) {
|
|
||||||
// 记住密码
|
|
||||||
if (keepPwd) {
|
|
||||||
fastData.setUserPwdAndAccount({'pwd': userPwd, 'account': userName});
|
|
||||||
}
|
|
||||||
// 更新
|
|
||||||
ref.read(userProvider.notifier).initUserInfo();
|
|
||||||
ref.read(userTokenProvider.notifier).initToken();
|
|
||||||
|
|
||||||
// 跳转登录页
|
|
||||||
Get.toNamed(Routes.login);
|
|
||||||
// RouterManager.router.navigateTo(context, RouterManager.root, clearStack: true, transition: getTransition());
|
|
||||||
});*/
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print('进来异常');
|
print('进来异常');
|
||||||
EasyLoading.dismiss();
|
|
||||||
UserStore.to.erase();
|
UserStore.to.erase();
|
||||||
StorageService.to.erase();
|
StorageService.to.erase();
|
||||||
if (e is Exception) {
|
if (e is Exception) {
|
||||||
|
|
@ -147,6 +125,7 @@ class LoginLogic extends GetxController with RequestToolMixin {
|
||||||
toMsg('登录失败,请重试');
|
toMsg('登录失败,请重试');
|
||||||
} finally {
|
} finally {
|
||||||
state.canLogin.value = true;
|
state.canLogin.value = true;
|
||||||
|
EasyLoading.dismiss();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,8 @@ class _LoginPageState extends State<LoginPage> {
|
||||||
|
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
||||||
await sysProtocol(context);
|
await sysProtocol(context);
|
||||||
await Future.delayed(Duration.zero, () => upgradeLogic.getAppUpgrade(context));
|
/// 为了发布各平台方便审核 不能再登陆页面直接弹出审核
|
||||||
|
// await Future.delayed(Duration.zero, () => upgradeLogic.getAppUpgrade(context));
|
||||||
});
|
});
|
||||||
super.initState();
|
super.initState();
|
||||||
}
|
}
|
||||||
|
|
@ -171,7 +172,7 @@ class _LoginPageState extends State<LoginPage> {
|
||||||
onSubmitted: (_) => easyThrottle('LOGIN_EASYTHROTTLE', () async{
|
onSubmitted: (_) => easyThrottle('LOGIN_EASYTHROTTLE', () async{
|
||||||
Utils.hideKeyboard();
|
Utils.hideKeyboard();
|
||||||
await Future.delayed(const Duration(milliseconds: 300));
|
await Future.delayed(const Duration(milliseconds: 300));
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) => logic.toLogin(context));
|
WidgetsBinding.instance.addPostFrameCallback((_) => logic.toLogin(context,upgradeLogic));
|
||||||
}),
|
}),
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: const Color(0xFF434343),
|
color: const Color(0xFF434343),
|
||||||
|
|
@ -290,7 +291,7 @@ class _LoginPageState extends State<LoginPage> {
|
||||||
onTap: () => easyThrottle('LOGIN_EASYTHROTTLE', () async{
|
onTap: () => easyThrottle('LOGIN_EASYTHROTTLE', () async{
|
||||||
Utils.hideKeyboard();
|
Utils.hideKeyboard();
|
||||||
await Future.delayed(Duration.zero);
|
await Future.delayed(Duration.zero);
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) => logic.toLogin(context));
|
WidgetsBinding.instance.addPostFrameCallback((_) => logic.toLogin(context,upgradeLogic));
|
||||||
}),
|
}),
|
||||||
child: Obx(() {
|
child: Obx(() {
|
||||||
return Container(
|
return Container(
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,16 @@ abstract class AppPages {
|
||||||
GetPage(name: Routes.register, page: () => const Register(), transition: getTransition()),
|
GetPage(name: Routes.register, page: () => const Register(), transition: getTransition()),
|
||||||
GetPage(name: Routes.agreementPage, page: () => const AgreementPage(), binding: LoginBinding(), transition: getTransition()),
|
GetPage(name: Routes.agreementPage, page: () => const AgreementPage(), binding: LoginBinding(), transition: getTransition()),
|
||||||
GetPage(name: Routes.home, page: () => const HomePage(), binding: HomeBinding(), transition: getTransition()),
|
GetPage(name: Routes.home, page: () => const HomePage(), binding: HomeBinding(), transition: getTransition()),
|
||||||
GetPage(name: Routes.startPage, page: () => const StartPage(), binding: StartPageIndexBinding(), transition: getTransition()),
|
GetPage(
|
||||||
|
name: Routes.startPage,
|
||||||
|
page: () => const StartPage(),
|
||||||
|
bindings: [
|
||||||
|
HomeBinding(),
|
||||||
|
WorkBinding(),
|
||||||
|
StartPageIndexBinding(),
|
||||||
|
],
|
||||||
|
transition: getTransition(),
|
||||||
|
),
|
||||||
GetPage(name: Routes.myInfo, page: () => const MyInfo(), transition: getTransition()),
|
GetPage(name: Routes.myInfo, page: () => const MyInfo(), transition: getTransition()),
|
||||||
GetPage(name: Routes.work, page: () => const WorkPage(), binding: WorkBinding(), transition: getTransition()),
|
GetPage(name: Routes.work, page: () => const WorkPage(), binding: WorkBinding(), transition: getTransition()),
|
||||||
GetPage(name: Routes.otherPage, page: () => const OhterPage(), transition: getTransition()),
|
GetPage(name: Routes.otherPage, page: () => const OhterPage(), transition: getTransition()),
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue