Merge branch 'main' into fix
This commit is contained in:
1
assets/loading.json
Normal file
1
assets/loading.json
Normal file
File diff suppressed because one or more lines are too long
1
assets/trail_loading.json
Normal file
1
assets/trail_loading.json
Normal file
File diff suppressed because one or more lines are too long
@ -124,7 +124,7 @@ class EpisodeBottomSheet {
|
||||
});
|
||||
return Container(
|
||||
height: sheetHeight,
|
||||
color: Theme.of(context).colorScheme.background,
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
child: Column(
|
||||
children: [
|
||||
buildTitle(),
|
||||
|
@ -13,8 +13,8 @@ class Skeleton extends StatelessWidget {
|
||||
var shimmerGradient = LinearGradient(
|
||||
colors: [
|
||||
Colors.transparent,
|
||||
Theme.of(context).colorScheme.background.withAlpha(10),
|
||||
Theme.of(context).colorScheme.background.withAlpha(10),
|
||||
Theme.of(context).colorScheme.surface.withAlpha(10),
|
||||
Theme.of(context).colorScheme.surface.withAlpha(10),
|
||||
Colors.transparent,
|
||||
],
|
||||
stops: const [
|
||||
|
@ -14,7 +14,7 @@ class StatDanMu extends StatelessWidget {
|
||||
Map<String, Color> colorObject = {
|
||||
'white': Colors.white,
|
||||
'gray': Theme.of(context).colorScheme.outline,
|
||||
'black': Theme.of(context).colorScheme.onBackground.withOpacity(0.8),
|
||||
'black': Theme.of(context).colorScheme.onSurface.withOpacity(0.8),
|
||||
};
|
||||
Color color = colorObject[theme]!;
|
||||
return Row(
|
||||
|
@ -14,7 +14,7 @@ class StatView extends StatelessWidget {
|
||||
Map<String, Color> colorObject = {
|
||||
'white': Colors.white,
|
||||
'gray': Theme.of(context).colorScheme.outline,
|
||||
'black': Theme.of(context).colorScheme.onBackground.withOpacity(0.8),
|
||||
'black': Theme.of(context).colorScheme.onSurface.withOpacity(0.8),
|
||||
};
|
||||
Color color = colorObject[theme]!;
|
||||
return Row(
|
||||
|
@ -400,12 +400,24 @@ class Api {
|
||||
'${HttpString.passBaseUrl}/x/passport-login/captcha?source=main_web';
|
||||
|
||||
// web端短信验证码
|
||||
static const String smsCode =
|
||||
static const String webSmsCode =
|
||||
'${HttpString.passBaseUrl}/x/passport-login/web/sms/send';
|
||||
|
||||
// web端验证码登录
|
||||
static const String webSmsLogin =
|
||||
'${HttpString.passBaseUrl}/x/passport-login/web/login/sms';
|
||||
|
||||
// web端密码登录
|
||||
static const String loginInByWebPwd =
|
||||
'${HttpString.passBaseUrl}/x/passport-login/web/login';
|
||||
|
||||
// web端二维码
|
||||
static const String qrCodeApi =
|
||||
'${HttpString.passBaseUrl}/x/passport-login/web/qrcode/generate';
|
||||
|
||||
// 扫码登录
|
||||
static const String loginInByQrcode =
|
||||
'${HttpString.passBaseUrl}/x/passport-login/web/qrcode/poll';
|
||||
|
||||
// app端短信验证码
|
||||
static const String appSmsCode =
|
||||
|
@ -3,6 +3,7 @@ import 'dart:math';
|
||||
import 'package:crypto/crypto.dart';
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:encrypt/encrypt.dart';
|
||||
import 'package:pilipala/http/constants.dart';
|
||||
import 'package:uuid/uuid.dart';
|
||||
import '../models/login/index.dart';
|
||||
import '../utils/login.dart';
|
||||
@ -21,32 +22,32 @@ class LoginHttp {
|
||||
}
|
||||
}
|
||||
|
||||
static Future sendSmsCode({
|
||||
int? cid,
|
||||
required int tel,
|
||||
required String token,
|
||||
required String challenge,
|
||||
required String validate,
|
||||
required String seccode,
|
||||
}) async {
|
||||
var res = await Request().post(
|
||||
Api.appSmsCode,
|
||||
data: {
|
||||
'cid': cid,
|
||||
'tel': tel,
|
||||
"source": "main_web",
|
||||
'token': token,
|
||||
'challenge': challenge,
|
||||
'validate': validate,
|
||||
'seccode': seccode,
|
||||
},
|
||||
options: Options(
|
||||
contentType: Headers.formUrlEncodedContentType,
|
||||
// headers: {'user-agent': ApiConstants.userAgent}
|
||||
),
|
||||
);
|
||||
print(res);
|
||||
}
|
||||
// static Future sendSmsCode({
|
||||
// int? cid,
|
||||
// required int tel,
|
||||
// required String token,
|
||||
// required String challenge,
|
||||
// required String validate,
|
||||
// required String seccode,
|
||||
// }) async {
|
||||
// var res = await Request().post(
|
||||
// Api.appSmsCode,
|
||||
// data: {
|
||||
// 'cid': cid,
|
||||
// 'tel': tel,
|
||||
// "source": "main_web",
|
||||
// 'token': token,
|
||||
// 'challenge': challenge,
|
||||
// 'validate': validate,
|
||||
// 'seccode': seccode,
|
||||
// },
|
||||
// options: Options(
|
||||
// contentType: Headers.formUrlEncodedContentType,
|
||||
// // headers: {'user-agent': ApiConstants.userAgent}
|
||||
// ),
|
||||
// );
|
||||
// print(res);
|
||||
// }
|
||||
|
||||
// web端验证码
|
||||
static Future sendWebSmsCode({
|
||||
@ -60,6 +61,7 @@ class LoginHttp {
|
||||
Map data = {
|
||||
'cid': cid,
|
||||
'tel': tel,
|
||||
"source": "main_web",
|
||||
'token': token,
|
||||
'challenge': challenge,
|
||||
'validate': validate,
|
||||
@ -67,17 +69,56 @@ class LoginHttp {
|
||||
};
|
||||
FormData formData = FormData.fromMap({...data});
|
||||
var res = await Request().post(
|
||||
Api.smsCode,
|
||||
Api.webSmsCode,
|
||||
data: formData,
|
||||
options: Options(
|
||||
contentType: Headers.formUrlEncodedContentType,
|
||||
),
|
||||
);
|
||||
print(res);
|
||||
if (res.data['code'] == 0) {
|
||||
return {
|
||||
'status': true,
|
||||
'data': res.data['data'],
|
||||
};
|
||||
} else {
|
||||
return {'status': false, 'data': [], 'msg': res.data['message']};
|
||||
}
|
||||
}
|
||||
|
||||
// web端验证码登录
|
||||
static Future loginInByWebSmsCode() async {}
|
||||
static Future loginInByWebSmsCode({
|
||||
int? cid,
|
||||
required int tel,
|
||||
required int code,
|
||||
required String captchaKey,
|
||||
}) async {
|
||||
// webSmsLogin
|
||||
Map data = {
|
||||
"cid": cid,
|
||||
"tel": tel,
|
||||
"code": code,
|
||||
"source": "main_mini",
|
||||
"keep": 0,
|
||||
"captcha_key": captchaKey,
|
||||
"go_url": HttpString.baseUrl
|
||||
};
|
||||
FormData formData = FormData.fromMap({...data});
|
||||
var res = await Request().post(
|
||||
Api.webSmsLogin,
|
||||
data: formData,
|
||||
options: Options(
|
||||
contentType: Headers.formUrlEncodedContentType,
|
||||
),
|
||||
);
|
||||
if (res.data['code'] == 0) {
|
||||
return {
|
||||
'status': true,
|
||||
'data': res.data['data'],
|
||||
};
|
||||
} else {
|
||||
return {'status': false, 'data': [], 'msg': res.data['message']};
|
||||
}
|
||||
}
|
||||
|
||||
// web端密码登录
|
||||
static Future liginInByWebPwd() async {}
|
||||
@ -173,4 +214,69 @@ class LoginHttp {
|
||||
);
|
||||
print(res);
|
||||
}
|
||||
|
||||
// web端密码登录
|
||||
static Future loginInByWebPwd({
|
||||
required int username,
|
||||
required String password,
|
||||
required String token,
|
||||
required String challenge,
|
||||
required String validate,
|
||||
required String seccode,
|
||||
}) async {
|
||||
Map data = {
|
||||
'username': username,
|
||||
'password': password,
|
||||
'keep': 0,
|
||||
'token': token,
|
||||
'challenge': challenge,
|
||||
'validate': validate,
|
||||
'seccode': seccode,
|
||||
'source': 'main-fe-header',
|
||||
"go_url": HttpString.baseUrl
|
||||
};
|
||||
FormData formData = FormData.fromMap({...data});
|
||||
var res = await Request().post(
|
||||
Api.loginInByWebPwd,
|
||||
data: formData,
|
||||
options: Options(
|
||||
contentType: Headers.formUrlEncodedContentType,
|
||||
),
|
||||
);
|
||||
if (res.data['code'] == 0) {
|
||||
return {
|
||||
'status': true,
|
||||
'data': res.data['data'],
|
||||
};
|
||||
} else {
|
||||
return {'status': false, 'data': [], 'msg': res.data['message']};
|
||||
}
|
||||
}
|
||||
|
||||
// web端登录二维码
|
||||
static Future getWebQrcode() async {
|
||||
var res = await Request().get(Api.qrCodeApi);
|
||||
if (res.data['code'] == 0) {
|
||||
return {
|
||||
'status': true,
|
||||
'data': res.data['data'],
|
||||
};
|
||||
} else {
|
||||
return {'status': false, 'data': [], 'msg': res.data['message']};
|
||||
}
|
||||
}
|
||||
|
||||
// web端二维码轮询登录状态
|
||||
static Future queryWebQrcodeStatus(String qrcodeKey) async {
|
||||
var res = await Request()
|
||||
.get(Api.loginInByQrcode, data: {'qrcode_key': qrcodeKey});
|
||||
if (res.data['data']['code'] == 0) {
|
||||
return {
|
||||
'status': true,
|
||||
'data': res.data['data'],
|
||||
};
|
||||
} else {
|
||||
return {'status': false, 'data': [], 'msg': res.data['message']};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -20,10 +20,10 @@ class IntroDetail extends StatelessWidget {
|
||||
sheetHeight = localCache.get('sheetHeight');
|
||||
TextStyle smallTitle = TextStyle(
|
||||
fontSize: 12,
|
||||
color: Theme.of(context).colorScheme.onBackground,
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
);
|
||||
return Container(
|
||||
color: Theme.of(context).colorScheme.background,
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
padding: const EdgeInsets.only(left: 14, right: 14),
|
||||
height: sheetHeight,
|
||||
child: Column(
|
||||
|
@ -162,13 +162,12 @@ class _DynamicsPageState extends State<DynamicsPage>
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.surfaceVariant
|
||||
.surfaceContainerHighest
|
||||
.withOpacity(0.7),
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
thumbDecoration: BoxDecoration(
|
||||
color:
|
||||
Theme.of(context).colorScheme.background,
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
duration: const Duration(milliseconds: 300),
|
||||
|
@ -19,7 +19,7 @@ Widget addWidget(item, context, type, {floor = 1}) {
|
||||
};
|
||||
Color bgColor = floor == 1
|
||||
? Theme.of(context).dividerColor.withOpacity(0.08)
|
||||
: Theme.of(context).colorScheme.background;
|
||||
: Theme.of(context).colorScheme.surface;
|
||||
switch (type) {
|
||||
case 'ADDITIONAL_TYPE_UGC':
|
||||
// 转发的投稿
|
||||
|
@ -52,7 +52,7 @@ class AuthorPanel extends StatelessWidget {
|
||||
color: item.modules.moduleAuthor!.vip != null &&
|
||||
item.modules.moduleAuthor!.vip['status'] > 0
|
||||
? const Color.fromARGB(255, 251, 100, 163)
|
||||
: Theme.of(context).colorScheme.onBackground,
|
||||
: Theme.of(context).colorScheme.onSurface,
|
||||
fontSize: Theme.of(context).textTheme.titleSmall!.fontSize,
|
||||
),
|
||||
),
|
||||
|
@ -69,7 +69,7 @@ class _UpPanelState extends State<UpPanel> {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Container(
|
||||
color: Theme.of(context).colorScheme.background,
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
padding: const EdgeInsets.only(left: 16, right: 16),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
@ -95,7 +95,7 @@ class _UpPanelState extends State<UpPanel> {
|
||||
),
|
||||
Container(
|
||||
height: 90,
|
||||
color: Theme.of(context).colorScheme.background,
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
child: Row(
|
||||
children: [
|
||||
Flexible(
|
||||
|
@ -1,11 +1,14 @@
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:encrypt/encrypt.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:pilipala/http/login.dart';
|
||||
import 'package:gt3_flutter_plugin/gt3_flutter_plugin.dart';
|
||||
import 'package:pilipala/models/login/index.dart';
|
||||
import 'package:pilipala/utils/login.dart';
|
||||
|
||||
class LoginPageController extends GetxController {
|
||||
final GlobalKey mobFormKey = GlobalKey<FormState>();
|
||||
@ -26,9 +29,23 @@ class LoginPageController extends GetxController {
|
||||
|
||||
final Gt3FlutterPlugin captcha = Gt3FlutterPlugin();
|
||||
|
||||
// 倒计时60s
|
||||
RxInt seconds = 60.obs;
|
||||
late Timer timer;
|
||||
RxBool smsCodeSendStatus = false.obs;
|
||||
|
||||
// 默认密码登录
|
||||
RxInt loginType = 0.obs;
|
||||
|
||||
late String captchaKey;
|
||||
|
||||
late int tel;
|
||||
late int webSmsCode;
|
||||
|
||||
RxInt validSeconds = 180.obs;
|
||||
late Timer validTimer;
|
||||
late String qrcodeKey;
|
||||
|
||||
// 监听pageView切换
|
||||
void onPageChange(int index) {
|
||||
currentIndex.value = index;
|
||||
@ -43,6 +60,7 @@ class LoginPageController extends GetxController {
|
||||
curve: Curves.easeInOut,
|
||||
);
|
||||
passwordTextFieldNode.requestFocus();
|
||||
(mobFormKey.currentState as FormState).save();
|
||||
}
|
||||
}
|
||||
|
||||
@ -86,18 +104,64 @@ class LoginPageController extends GetxController {
|
||||
}
|
||||
}
|
||||
|
||||
// 验证码登录
|
||||
void loginInByCode() {
|
||||
if ((msgCodeFormKey.currentState as FormState).validate()) {}
|
||||
// web端密码登录
|
||||
void loginInByWebPassword() async {
|
||||
if ((passwordFormKey.currentState as FormState).validate()) {
|
||||
getCaptcha((data) async {
|
||||
CaptchaDataModel captchaData = data;
|
||||
var webKeyRes = await LoginHttp.getWebKey();
|
||||
if (webKeyRes['status']) {
|
||||
String rhash = webKeyRes['data']['hash'];
|
||||
String key = webKeyRes['data']['key'];
|
||||
dynamic publicKey = RSAKeyParser().parse(key);
|
||||
String passwordEncryptyed = Encrypter(RSA(publicKey: publicKey))
|
||||
.encrypt(rhash + passwordTextController.text)
|
||||
.base64;
|
||||
var res = await LoginHttp.loginInByWebPwd(
|
||||
username: tel,
|
||||
password: passwordEncryptyed,
|
||||
token: captchaData.token!,
|
||||
challenge: captchaData.geetest!.challenge!,
|
||||
validate: captchaData.validate!,
|
||||
seccode: captchaData.seccode!,
|
||||
);
|
||||
if (res['status']) {
|
||||
await LoginUtils.confirmLogin('', null);
|
||||
} else {
|
||||
SmartDialog.showToast(res['msg']);
|
||||
}
|
||||
} else {
|
||||
SmartDialog.showToast(webKeyRes['msg']);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// app端验证码
|
||||
void getMsgCode() async {
|
||||
// web端验证码登录
|
||||
void loginInByCode() async {
|
||||
if ((msgCodeFormKey.currentState as FormState).validate()) {
|
||||
(msgCodeFormKey.currentState as FormState).save();
|
||||
var res = await LoginHttp.loginInByWebSmsCode(
|
||||
cid: 86,
|
||||
tel: tel,
|
||||
code: webSmsCode,
|
||||
captchaKey: captchaKey,
|
||||
);
|
||||
if (res['status']) {
|
||||
await LoginUtils.confirmLogin('', null);
|
||||
} else {
|
||||
SmartDialog.showToast(res['msg']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 获取app端验证码
|
||||
void getAppMsgCode() async {
|
||||
getCaptcha((data) async {
|
||||
CaptchaDataModel captchaData = data;
|
||||
var res = await LoginHttp.sendAppSmsCode(
|
||||
cid: 86,
|
||||
tel: 13734077064,
|
||||
tel: tel,
|
||||
token: captchaData.token!,
|
||||
challenge: captchaData.geetest!.challenge!,
|
||||
validate: captchaData.validate!,
|
||||
@ -121,7 +185,7 @@ class LoginPageController extends GetxController {
|
||||
captcha.addEventHandler(onShow: (Map<String, dynamic> message) async {
|
||||
SmartDialog.dismiss();
|
||||
}, onClose: (Map<String, dynamic> message) async {
|
||||
SmartDialog.showToast('关闭验证');
|
||||
SmartDialog.showToast('取消验证');
|
||||
}, onResult: (Map<String, dynamic> message) async {
|
||||
debugPrint("Captcha result: $message");
|
||||
String code = message["code"];
|
||||
@ -201,4 +265,72 @@ class LoginPageController extends GetxController {
|
||||
captcha.startCaptcha(registerData);
|
||||
} else {}
|
||||
}
|
||||
|
||||
// 获取web端验证码
|
||||
void getWebMsgCode() async {
|
||||
getCaptcha((data) async {
|
||||
CaptchaDataModel captchaData = data;
|
||||
var res = await LoginHttp.sendWebSmsCode(
|
||||
cid: 86,
|
||||
tel: tel,
|
||||
token: captchaData.token!,
|
||||
challenge: captchaData.geetest!.challenge!,
|
||||
validate: captchaData.validate!,
|
||||
seccode: captchaData.seccode!,
|
||||
);
|
||||
if (res['status']) {
|
||||
captchaKey = res['data']['captcha_key'];
|
||||
SmartDialog.showToast('验证码已发送');
|
||||
// 倒计时60s
|
||||
smsCodeSendStatus.value = true;
|
||||
startTimer();
|
||||
} else {
|
||||
SmartDialog.showToast(res['msg']);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 验证码倒计时
|
||||
void startTimer() {
|
||||
timer = Timer.periodic(const Duration(seconds: 1), (timer) {
|
||||
if (seconds.value > 0) {
|
||||
seconds.value--;
|
||||
} else {
|
||||
seconds.value = 60;
|
||||
smsCodeSendStatus.value = false;
|
||||
timer.cancel();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 获取登录二维码
|
||||
Future getWebQrcode() async {
|
||||
var res = await LoginHttp.getWebQrcode();
|
||||
validSeconds.value = 180;
|
||||
if (res['status']) {
|
||||
qrcodeKey = res['data']['qrcode_key'];
|
||||
validTimer = Timer.periodic(const Duration(seconds: 1), (validTimer) {
|
||||
if (validSeconds.value > 0) {
|
||||
validSeconds.value--;
|
||||
queryWebQrcodeStatus();
|
||||
} else {
|
||||
getWebQrcode();
|
||||
validTimer.cancel();
|
||||
}
|
||||
});
|
||||
return res;
|
||||
} else {
|
||||
SmartDialog.showToast(res['msg']);
|
||||
}
|
||||
}
|
||||
|
||||
// 轮询二维码登录状态
|
||||
Future queryWebQrcodeStatus() async {
|
||||
var res = await LoginHttp.queryWebQrcodeStatus(qrcodeKey);
|
||||
if (res['status']) {
|
||||
await LoginUtils.confirmLogin('', null);
|
||||
validTimer.cancel();
|
||||
Get.back();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:qr_flutter/qr_flutter.dart';
|
||||
|
||||
import 'controller.dart';
|
||||
|
||||
@ -37,6 +38,105 @@ class _LoginPageState extends State<LoginPage> {
|
||||
icon: const Icon(Icons.arrow_back),
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
IconButton(
|
||||
tooltip: '浏览器打开',
|
||||
onPressed: () {
|
||||
Get.offNamed(
|
||||
'/webview',
|
||||
parameters: {
|
||||
'url': 'https://passport.bilibili.com/h5-app/passport/login',
|
||||
'type': 'login',
|
||||
'pageTitle': '登录bilibili',
|
||||
},
|
||||
);
|
||||
},
|
||||
icon: const Icon(Icons.language),
|
||||
),
|
||||
IconButton(
|
||||
tooltip: '二维码登录',
|
||||
onPressed: () {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return StatefulBuilder(
|
||||
builder: (context, StateSetter setState) {
|
||||
return AlertDialog(
|
||||
title: Row(
|
||||
children: [
|
||||
const Text('扫码登录'),
|
||||
IconButton(
|
||||
onPressed: () {
|
||||
setState(() {});
|
||||
},
|
||||
icon: const Icon(Icons.refresh),
|
||||
),
|
||||
],
|
||||
),
|
||||
contentPadding: const EdgeInsets.fromLTRB(0, 0, 0, 4),
|
||||
content: AspectRatio(
|
||||
aspectRatio: 1,
|
||||
child: Container(
|
||||
width: 200,
|
||||
padding: const EdgeInsets.all(12),
|
||||
child: FutureBuilder(
|
||||
future: _loginPageCtr.getWebQrcode(),
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.connectionState ==
|
||||
ConnectionState.done) {
|
||||
if (snapshot.data == null) {
|
||||
return const SizedBox();
|
||||
}
|
||||
Map data = snapshot.data as Map;
|
||||
return QrImageView(
|
||||
data: data['data']['url'],
|
||||
backgroundColor: Colors.transparent,
|
||||
);
|
||||
} else {
|
||||
return const Center(
|
||||
child: SizedBox(
|
||||
width: 40,
|
||||
height: 40,
|
||||
child: CircularProgressIndicator(),
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () {},
|
||||
child: Obx(() {
|
||||
return Text(
|
||||
'有效期: ${_loginPageCtr.validSeconds.value}s',
|
||||
style: Theme.of(context).textTheme.titleMedium,
|
||||
);
|
||||
}),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () {},
|
||||
child: Text(
|
||||
'检查登录状态',
|
||||
style: TextStyle(
|
||||
fontSize: Theme.of(context)
|
||||
.textTheme
|
||||
.titleMedium!
|
||||
.fontSize,
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
);
|
||||
});
|
||||
},
|
||||
);
|
||||
},
|
||||
icon: const Icon(Icons.qr_code),
|
||||
),
|
||||
const SizedBox(width: 22),
|
||||
],
|
||||
),
|
||||
body: PageView(
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
@ -93,35 +193,12 @@ class _LoginPageState extends State<LoginPage> {
|
||||
validator: (v) {
|
||||
return v!.trim().isNotEmpty ? null : "手机号码不能为空";
|
||||
},
|
||||
onSaved: (val) {
|
||||
print(val);
|
||||
},
|
||||
onSaved: (val) => _loginPageCtr.tel = int.parse(val!),
|
||||
onEditingComplete: () {
|
||||
_loginPageCtr.nextStep();
|
||||
},
|
||||
),
|
||||
),
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
Get.offNamed(
|
||||
'/webview',
|
||||
parameters: {
|
||||
'url':
|
||||
'https://passport.bilibili.com/h5-app/passport/login',
|
||||
'type': 'login',
|
||||
'pageTitle': '登录bilibili',
|
||||
},
|
||||
);
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(left: 2),
|
||||
child: Text(
|
||||
'使用网页端登录',
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.primary),
|
||||
),
|
||||
),
|
||||
),
|
||||
const Spacer(),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
@ -236,7 +313,7 @@ class _LoginPageState extends State<LoginPage> {
|
||||
.primary, // 设置按钮背景色
|
||||
),
|
||||
onPressed: () =>
|
||||
_loginPageCtr.loginInByAppPassword(),
|
||||
_loginPageCtr.loginInByWebPassword(),
|
||||
child: const Text('确认登录'),
|
||||
)
|
||||
],
|
||||
@ -308,21 +385,28 @@ class _LoginPageState extends State<LoginPage> {
|
||||
? null
|
||||
: "验证码不能为空";
|
||||
},
|
||||
onSaved: (val) {
|
||||
print(val);
|
||||
},
|
||||
onSaved: (val) => _loginPageCtr.webSmsCode =
|
||||
int.parse(val!),
|
||||
),
|
||||
Positioned(
|
||||
right: 8,
|
||||
top: 4,
|
||||
child: Center(
|
||||
child: TextButton(
|
||||
onPressed: () =>
|
||||
_loginPageCtr.getMsgCode(),
|
||||
child: const Text('获取验证码'),
|
||||
Obx(() {
|
||||
return Positioned(
|
||||
right: 8,
|
||||
top: 0,
|
||||
child: Center(
|
||||
child: TextButton(
|
||||
onPressed: _loginPageCtr
|
||||
.smsCodeSendStatus.value
|
||||
? null
|
||||
: () =>
|
||||
_loginPageCtr.getWebMsgCode(),
|
||||
child: _loginPageCtr
|
||||
.smsCodeSendStatus.value
|
||||
? Text(
|
||||
'重新获取(${_loginPageCtr.seconds.value}s)')
|
||||
: const Text('获取验证码')),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
})
|
||||
],
|
||||
),
|
||||
),
|
||||
|
@ -6,7 +6,6 @@ import 'package:pilipala/http/user.dart';
|
||||
import 'package:pilipala/models/common/theme_type.dart';
|
||||
import 'package:pilipala/models/user/info.dart';
|
||||
import 'package:pilipala/models/user/stat.dart';
|
||||
import 'package:pilipala/utils/route_push.dart';
|
||||
import 'package:pilipala/utils/storage.dart';
|
||||
|
||||
class MineController extends GetxController {
|
||||
@ -34,8 +33,8 @@ class MineController extends GetxController {
|
||||
|
||||
onLogin() async {
|
||||
if (!userLogin.value) {
|
||||
RoutePush.loginPush();
|
||||
// Get.toNamed('/loginPage');
|
||||
// RoutePush.loginPush();
|
||||
Get.toNamed('/loginPage');
|
||||
} else {
|
||||
int mid = userInfo.value.mid!;
|
||||
String face = userInfo.value.face!;
|
||||
|
@ -16,7 +16,10 @@ class SearchText extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Material(
|
||||
color: Theme.of(context).colorScheme.surfaceVariant.withOpacity(0.5),
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.surfaceContainerHighest
|
||||
.withOpacity(0.5),
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
child: Padding(
|
||||
padding: EdgeInsets.zero,
|
||||
|
@ -66,7 +66,7 @@ class _FontSizeSelectPageState extends State<FontSizeSelectPage> {
|
||||
.colorScheme
|
||||
.primary
|
||||
.withOpacity(0.3))),
|
||||
color: Theme.of(context).colorScheme.background,
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
|
@ -41,7 +41,8 @@ class _LogsPageState extends State<LogsPage> {
|
||||
.replaceAll('DEVICE INFO', '设备信息')
|
||||
.replaceAll('APP INFO', '应用信息')
|
||||
.replaceAll('ERROR', '错误信息')
|
||||
.replaceAll('STACK TRACE', '错误堆栈');
|
||||
.replaceAll('STACK TRACE', '错误堆栈')
|
||||
.replaceAll('#', 'Line');
|
||||
}).toList();
|
||||
List<Map<String, dynamic>> result = [];
|
||||
for (String i in contentList) {
|
||||
@ -50,7 +51,7 @@ class _LogsPageState extends State<LogsPage> {
|
||||
.split("\n")
|
||||
.map((l) {
|
||||
if (l.startsWith("Crash occurred on")) {
|
||||
date = DateTime.parse(
|
||||
date = DateTime.tryParse(
|
||||
l.split("Crash occurred on")[1].trim().split('.')[0],
|
||||
);
|
||||
return "";
|
||||
|
@ -5,6 +5,7 @@ import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:lottie/lottie.dart';
|
||||
import 'package:pilipala/common/constants.dart';
|
||||
import 'package:pilipala/common/widgets/http_error.dart';
|
||||
import 'package:pilipala/pages/video/detail/index.dart';
|
||||
@ -97,11 +98,14 @@ class _VideoIntroPanelState extends State<VideoIntroPanel>
|
||||
);
|
||||
}
|
||||
} else {
|
||||
return const SliverToBoxAdapter(
|
||||
return SliverToBoxAdapter(
|
||||
child: SizedBox(
|
||||
height: 100,
|
||||
child: Center(
|
||||
child: CircularProgressIndicator(),
|
||||
child: Lottie.asset(
|
||||
'assets/loading.json',
|
||||
width: 200,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
@ -595,8 +599,4 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// Widget StaffPanel(BuildContext context, videoIntroController) {
|
||||
// return
|
||||
// }
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ class _FavPanelState extends State<FavPanel> {
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
height: sheetHeight,
|
||||
color: Theme.of(context).colorScheme.background,
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
child: Column(
|
||||
children: [
|
||||
AppBar(
|
||||
|
@ -57,7 +57,7 @@ class _GroupPanelState extends State<GroupPanel> {
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
height: sheetHeight,
|
||||
color: Theme.of(context).colorScheme.background,
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
AppBar(
|
||||
|
@ -12,7 +12,7 @@ class MenuRow extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
width: double.infinity,
|
||||
color: Theme.of(context).colorScheme.background,
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
padding: const EdgeInsets.only(top: 9, bottom: 9, left: 12),
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
@ -84,7 +84,7 @@ class MenuRow extends StatelessWidget {
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
color: selectStatus
|
||||
? Theme.of(context).colorScheme.onBackground
|
||||
? Theme.of(context).colorScheme.onSurface
|
||||
: Theme.of(context).colorScheme.outline),
|
||||
),
|
||||
),
|
||||
|
@ -170,7 +170,7 @@ class _VideoReplyNewDialogState extends State<VideoReplyNewDialog>
|
||||
topLeft: Radius.circular(12),
|
||||
topRight: Radius.circular(12),
|
||||
),
|
||||
color: Theme.of(context).colorScheme.background,
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
|
@ -78,7 +78,7 @@ class _VideoReplyReplyPanelState extends State<VideoReplyReplyPanel> {
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
height: widget.source == 'videoDetail' ? widget.sheetHeight : null,
|
||||
color: Theme.of(context).colorScheme.background,
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
child: Column(
|
||||
children: [
|
||||
if (widget.source == 'videoDetail')
|
||||
|
@ -23,7 +23,7 @@ class AiDetail extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
sheetHeight = localCache.get('sheetHeight');
|
||||
return Container(
|
||||
color: Theme.of(context).colorScheme.background,
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
padding: const EdgeInsets.only(left: 14, right: 14),
|
||||
height: sheetHeight,
|
||||
child: Column(
|
||||
|
@ -29,7 +29,7 @@ class ScrollAppBar extends StatelessWidget {
|
||||
opacity: scrollDistance / (videoHeight - kToolbarHeight),
|
||||
child: Container(
|
||||
height: statusBarHeight + kToolbarHeight,
|
||||
color: Theme.of(context).colorScheme.background,
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
padding: EdgeInsets.only(top: statusBarHeight),
|
||||
child: AppBar(
|
||||
primary: false,
|
||||
|
@ -93,7 +93,7 @@ class _HeaderControlState extends State<HeaderControl> {
|
||||
height: 460,
|
||||
clipBehavior: Clip.hardEdge,
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.background,
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
borderRadius: const BorderRadius.all(Radius.circular(12)),
|
||||
),
|
||||
margin: const EdgeInsets.all(12),
|
||||
@ -317,7 +317,7 @@ class _HeaderControlState extends State<HeaderControl> {
|
||||
height: 500,
|
||||
clipBehavior: Clip.hardEdge,
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.background,
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
borderRadius: const BorderRadius.all(Radius.circular(12)),
|
||||
),
|
||||
margin: const EdgeInsets.all(12),
|
||||
@ -377,7 +377,7 @@ class _HeaderControlState extends State<HeaderControl> {
|
||||
inactiveThumbColor:
|
||||
Theme.of(context).colorScheme.primaryContainer,
|
||||
inactiveTrackColor:
|
||||
Theme.of(context).colorScheme.background,
|
||||
Theme.of(context).colorScheme.surface,
|
||||
splashRadius: 10.0,
|
||||
// boolean variable value
|
||||
value: shutdownTimerService.waitForPlayingCompleted,
|
||||
@ -570,7 +570,7 @@ class _HeaderControlState extends State<HeaderControl> {
|
||||
height: 310,
|
||||
clipBehavior: Clip.hardEdge,
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.background,
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
borderRadius: const BorderRadius.all(Radius.circular(12)),
|
||||
),
|
||||
margin: const EdgeInsets.all(12),
|
||||
@ -660,7 +660,7 @@ class _HeaderControlState extends State<HeaderControl> {
|
||||
height: 250,
|
||||
clipBehavior: Clip.hardEdge,
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.background,
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
borderRadius: const BorderRadius.all(Radius.circular(12)),
|
||||
),
|
||||
margin: const EdgeInsets.all(12),
|
||||
@ -734,7 +734,7 @@ class _HeaderControlState extends State<HeaderControl> {
|
||||
height: 250,
|
||||
clipBehavior: Clip.hardEdge,
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.background,
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
borderRadius: const BorderRadius.all(Radius.circular(12)),
|
||||
),
|
||||
margin: const EdgeInsets.all(12),
|
||||
@ -828,7 +828,7 @@ class _HeaderControlState extends State<HeaderControl> {
|
||||
height: 580,
|
||||
clipBehavior: Clip.hardEdge,
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.background,
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
borderRadius: const BorderRadius.all(Radius.circular(12)),
|
||||
),
|
||||
margin: const EdgeInsets.all(12),
|
||||
@ -1084,7 +1084,7 @@ class _HeaderControlState extends State<HeaderControl> {
|
||||
height: 250,
|
||||
clipBehavior: Clip.hardEdge,
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.background,
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
borderRadius: const BorderRadius.all(Radius.circular(12)),
|
||||
),
|
||||
margin: const EdgeInsets.all(12),
|
||||
|
@ -76,7 +76,7 @@ class WebviewController extends GetxController {
|
||||
(url.startsWith(
|
||||
'https://passport.bilibili.com/web/sso/exchange_cookie') ||
|
||||
url.startsWith('https://m.bilibili.com/'))) {
|
||||
confirmLogin(url);
|
||||
LoginUtils.confirmLogin(url, controller);
|
||||
}
|
||||
},
|
||||
onWebResourceError: (WebResourceError error) {},
|
||||
@ -97,54 +97,4 @@ class WebviewController extends GetxController {
|
||||
)
|
||||
..loadRequest(Uri.parse(url));
|
||||
}
|
||||
|
||||
confirmLogin(url) async {
|
||||
var content = '';
|
||||
if (url != null) {
|
||||
content = '${content + url}; \n';
|
||||
}
|
||||
try {
|
||||
await SetCookie.onSet();
|
||||
final result = await UserHttp.userInfo();
|
||||
if (result['status'] && result['data'].isLogin) {
|
||||
SmartDialog.showToast('登录成功');
|
||||
try {
|
||||
Box userInfoCache = GStrorage.userInfo;
|
||||
if (!userInfoCache.isOpen) {
|
||||
userInfoCache = await Hive.openBox('userInfo');
|
||||
}
|
||||
await userInfoCache.put('userInfoCache', result['data']);
|
||||
|
||||
final HomeController homeCtr = Get.find<HomeController>();
|
||||
homeCtr.updateLoginStatus(true);
|
||||
homeCtr.userFace.value = result['data'].face;
|
||||
final MediaController mediaCtr = Get.find<MediaController>();
|
||||
mediaCtr.mid = result['data'].mid;
|
||||
await LoginUtils.refreshLoginStatus(true);
|
||||
} catch (err) {
|
||||
SmartDialog.show(builder: (BuildContext context) {
|
||||
return AlertDialog(
|
||||
title: const Text('登录遇到问题'),
|
||||
content: Text(err.toString()),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => controller.reload(),
|
||||
child: const Text('确认'),
|
||||
)
|
||||
],
|
||||
);
|
||||
});
|
||||
}
|
||||
Get.back();
|
||||
} else {
|
||||
// 获取用户信息失败
|
||||
SmartDialog.showToast(result['msg']);
|
||||
Clipboard.setData(ClipboardData(text: result['msg']));
|
||||
}
|
||||
} catch (e) {
|
||||
SmartDialog.showNotify(msg: e.toString(), notifyType: NotifyType.warning);
|
||||
content = content + e.toString();
|
||||
Clipboard.setData(ClipboardData(text: content));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:pilipala/utils/login.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
import 'controller.dart';
|
||||
import 'package:webview_flutter/webview_flutter.dart';
|
||||
@ -43,7 +44,8 @@ class _WebviewPageState extends State<WebviewPage> {
|
||||
Obx(
|
||||
() => _webviewController.type.value == 'login'
|
||||
? TextButton(
|
||||
onPressed: () => _webviewController.confirmLogin(null),
|
||||
onPressed: () =>
|
||||
LoginUtils.confirmLogin(null, _webviewController),
|
||||
child: const Text('刷新登录状态'),
|
||||
)
|
||||
: const SizedBox(),
|
||||
|
@ -1,6 +1,7 @@
|
||||
import 'package:easy_debounce/easy_throttle.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:pilipala/common/skeleton/skeleton.dart';
|
||||
import 'package:pilipala/common/widgets/network_img_layer.dart';
|
||||
import 'package:pilipala/utils/utils.dart';
|
||||
|
||||
@ -102,134 +103,83 @@ class _WhisperPageState extends State<WhisperPage> {
|
||||
},
|
||||
child: SingleChildScrollView(
|
||||
controller: _scrollController,
|
||||
child: Column(
|
||||
children: [
|
||||
FutureBuilder(
|
||||
future: _futureBuilderFuture,
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.connectionState == ConnectionState.done) {
|
||||
Map? data = snapshot.data;
|
||||
if (data != null && data['status']) {
|
||||
RxList sessionList = _whisperController.sessionList;
|
||||
return Obx(
|
||||
() => sessionList.isEmpty
|
||||
? const SizedBox()
|
||||
: ListView.separated(
|
||||
itemCount: sessionList.length,
|
||||
shrinkWrap: true,
|
||||
physics:
|
||||
const NeverScrollableScrollPhysics(),
|
||||
itemBuilder: (_, int i) {
|
||||
return ListTile(
|
||||
onTap: () {
|
||||
sessionList[i].unreadCount = 0;
|
||||
sessionList.refresh();
|
||||
Get.toNamed(
|
||||
'/whisperDetail',
|
||||
parameters: {
|
||||
'talkerId': sessionList[i]
|
||||
.talkerId
|
||||
.toString(),
|
||||
'name': sessionList[i]
|
||||
.accountInfo
|
||||
.name,
|
||||
'face': sessionList[i]
|
||||
.accountInfo
|
||||
.face,
|
||||
'mid': sessionList[i]
|
||||
.accountInfo
|
||||
.mid
|
||||
.toString(),
|
||||
},
|
||||
);
|
||||
},
|
||||
leading: Badge(
|
||||
isLabelVisible:
|
||||
sessionList[i].unreadCount > 0,
|
||||
label: Text(sessionList[i]
|
||||
.unreadCount
|
||||
.toString()),
|
||||
alignment: Alignment.topRight,
|
||||
child: NetworkImgLayer(
|
||||
width: 45,
|
||||
height: 45,
|
||||
type: 'avatar',
|
||||
src: sessionList[i]
|
||||
.accountInfo
|
||||
.face,
|
||||
),
|
||||
),
|
||||
title: Text(
|
||||
sessionList[i].accountInfo.name),
|
||||
subtitle: Text(
|
||||
sessionList[i].lastMsg.content !=
|
||||
null &&
|
||||
sessionList[i]
|
||||
.lastMsg
|
||||
.content !=
|
||||
''
|
||||
? (sessionList[i]
|
||||
.lastMsg
|
||||
.content['text'] ??
|
||||
sessionList[i]
|
||||
.lastMsg
|
||||
.content['content'] ??
|
||||
sessionList[i]
|
||||
.lastMsg
|
||||
.content['title'] ??
|
||||
sessionList[i]
|
||||
.lastMsg
|
||||
.content[
|
||||
'reply_content'] ??
|
||||
'不支持的消息类型')
|
||||
: '不支持的消息类型',
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.labelMedium!
|
||||
.copyWith(
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.outline)),
|
||||
trailing: Text(
|
||||
Utils.dateFormat(sessionList[i]
|
||||
.lastMsg
|
||||
.timestamp),
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.labelSmall!
|
||||
.copyWith(
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.outline),
|
||||
),
|
||||
);
|
||||
},
|
||||
separatorBuilder:
|
||||
(BuildContext context, int index) {
|
||||
return Divider(
|
||||
indent: 72,
|
||||
endIndent: 20,
|
||||
height: 6,
|
||||
color: Colors.grey.withOpacity(0.1),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
} else {
|
||||
// 请求错误
|
||||
return Center(
|
||||
child: Text(data?['msg'] ?? '请求异常'),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// 骨架屏
|
||||
return const SizedBox();
|
||||
}
|
||||
},
|
||||
)
|
||||
],
|
||||
child: FutureBuilder(
|
||||
future: _futureBuilderFuture,
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.connectionState == ConnectionState.done) {
|
||||
Map? data = snapshot.data;
|
||||
if (data != null && data['status']) {
|
||||
RxList sessionList = _whisperController.sessionList;
|
||||
return Obx(
|
||||
() => sessionList.isEmpty
|
||||
? const SizedBox()
|
||||
: ListView.separated(
|
||||
itemCount: sessionList.length,
|
||||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
itemBuilder: (_, int i) {
|
||||
return SessionItem(
|
||||
sessionItem: sessionList[i],
|
||||
changeFucCall: () =>
|
||||
sessionList.refresh(),
|
||||
);
|
||||
},
|
||||
separatorBuilder:
|
||||
(BuildContext context, int index) {
|
||||
return Divider(
|
||||
indent: 72,
|
||||
endIndent: 20,
|
||||
height: 6,
|
||||
color: Colors.grey.withOpacity(0.1),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
} else {
|
||||
// 请求错误
|
||||
return Center(
|
||||
child: Text(data?['msg'] ?? '请求异常'),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// 骨架屏
|
||||
return ListView.builder(
|
||||
itemCount: 15,
|
||||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
itemBuilder: (context, int i) {
|
||||
return Skeleton(
|
||||
child: ListTile(
|
||||
leading: Container(
|
||||
width: 45,
|
||||
height: 45,
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.onInverseSurface,
|
||||
borderRadius: BorderRadius.circular(25),
|
||||
),
|
||||
),
|
||||
title: Container(
|
||||
width: 100,
|
||||
height: 14,
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.onInverseSurface,
|
||||
),
|
||||
subtitle: Container(
|
||||
width: 80,
|
||||
height: 14,
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.onInverseSurface,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
@ -239,3 +189,67 @@ class _WhisperPageState extends State<WhisperPage> {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class SessionItem extends StatelessWidget {
|
||||
final dynamic sessionItem;
|
||||
final Function changeFucCall;
|
||||
|
||||
const SessionItem({
|
||||
super.key,
|
||||
required this.sessionItem,
|
||||
required this.changeFucCall,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final content = sessionItem.lastMsg.content;
|
||||
return ListTile(
|
||||
onTap: () {
|
||||
sessionItem.unreadCount = 0;
|
||||
changeFucCall.call();
|
||||
Get.toNamed(
|
||||
'/whisperDetail',
|
||||
parameters: {
|
||||
'talkerId': sessionItem.talkerId.toString(),
|
||||
'name': sessionItem.accountInfo.name,
|
||||
'face': sessionItem.accountInfo.face,
|
||||
'mid': sessionItem.accountInfo.mid.toString(),
|
||||
},
|
||||
);
|
||||
},
|
||||
leading: Badge(
|
||||
isLabelVisible: sessionItem.unreadCount > 0,
|
||||
label: Text(sessionItem.unreadCount.toString()),
|
||||
alignment: Alignment.topRight,
|
||||
child: NetworkImgLayer(
|
||||
width: 45,
|
||||
height: 45,
|
||||
type: 'avatar',
|
||||
src: sessionItem.accountInfo.face,
|
||||
),
|
||||
),
|
||||
title: Text(sessionItem.accountInfo.name),
|
||||
subtitle: Text(
|
||||
content != null && content != ''
|
||||
? (content['text'] ??
|
||||
content['content'] ??
|
||||
content['title'] ??
|
||||
content['reply_content'] ??
|
||||
'不支持的消息类型')
|
||||
: '不支持的消息类型',
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.labelMedium!
|
||||
.copyWith(color: Theme.of(context).colorScheme.outline)),
|
||||
trailing: Text(
|
||||
Utils.dateFormat(sessionItem.lastMsg.timestamp),
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.labelSmall!
|
||||
.copyWith(color: Theme.of(context).colorScheme.outline),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ import 'package:flutter_volume_controller/flutter_volume_controller.dart';
|
||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:lottie/lottie.dart';
|
||||
import 'package:media_kit/media_kit.dart';
|
||||
import 'package:media_kit_video/media_kit_video.dart';
|
||||
import 'package:pilipala/models/common/gesture_mode.dart';
|
||||
@ -913,9 +914,9 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
||||
colors: [Colors.black26, Colors.transparent],
|
||||
),
|
||||
),
|
||||
child: Image.asset(
|
||||
'assets/images/loading.gif',
|
||||
height: 25,
|
||||
child: Lottie.asset(
|
||||
'assets/loading.json',
|
||||
width: 200,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
@ -12,7 +12,7 @@ Future imageSaveDialog(context, videoItem, closeFn) {
|
||||
builder: (context) => Container(
|
||||
margin: const EdgeInsets.symmetric(horizontal: StyleString.safeSpace),
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.background,
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
borderRadius: BorderRadius.circular(10.0),
|
||||
),
|
||||
child: Column(
|
||||
|
@ -2,12 +2,18 @@ import 'dart:convert';
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:crypto/crypto.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:pilipala/http/user.dart';
|
||||
import 'package:pilipala/pages/dynamics/index.dart';
|
||||
import 'package:pilipala/pages/home/index.dart';
|
||||
import 'package:pilipala/pages/media/index.dart';
|
||||
import 'package:pilipala/pages/mine/index.dart';
|
||||
import 'package:pilipala/utils/cookie.dart';
|
||||
import 'package:pilipala/utils/storage.dart';
|
||||
import 'package:uuid/uuid.dart';
|
||||
|
||||
class LoginUtils {
|
||||
@ -57,4 +63,56 @@ class LoginUtils {
|
||||
String uuid = getUUID() + getUUID();
|
||||
return 'XY${uuid.substring(0, 35).toUpperCase()}';
|
||||
}
|
||||
|
||||
static confirmLogin(url, controller) async {
|
||||
var content = '';
|
||||
if (url != null) {
|
||||
content = '${content + url}; \n';
|
||||
}
|
||||
try {
|
||||
await SetCookie.onSet();
|
||||
final result = await UserHttp.userInfo();
|
||||
if (result['status'] && result['data'].isLogin) {
|
||||
SmartDialog.showToast('登录成功');
|
||||
try {
|
||||
Box userInfoCache = GStrorage.userInfo;
|
||||
if (!userInfoCache.isOpen) {
|
||||
userInfoCache = await Hive.openBox('userInfo');
|
||||
}
|
||||
await userInfoCache.put('userInfoCache', result['data']);
|
||||
|
||||
final HomeController homeCtr = Get.find<HomeController>();
|
||||
homeCtr.updateLoginStatus(true);
|
||||
homeCtr.userFace.value = result['data'].face;
|
||||
final MediaController mediaCtr = Get.find<MediaController>();
|
||||
mediaCtr.mid = result['data'].mid;
|
||||
await LoginUtils.refreshLoginStatus(true);
|
||||
} catch (err) {
|
||||
SmartDialog.show(builder: (BuildContext context) {
|
||||
return AlertDialog(
|
||||
title: const Text('登录遇到问题'),
|
||||
content: Text(err.toString()),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: controller != null
|
||||
? () => controller.reload()
|
||||
: SmartDialog.dismiss,
|
||||
child: const Text('确认'),
|
||||
)
|
||||
],
|
||||
);
|
||||
});
|
||||
}
|
||||
Get.back();
|
||||
} else {
|
||||
// 获取用户信息失败
|
||||
SmartDialog.showToast(result['msg']);
|
||||
Clipboard.setData(ClipboardData(text: result['msg']));
|
||||
}
|
||||
} catch (e) {
|
||||
SmartDialog.showNotify(msg: e.toString(), notifyType: NotifyType.warning);
|
||||
content = content + e.toString();
|
||||
Clipboard.setData(ClipboardData(text: content));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
44
pubspec.lock
44
pubspec.lock
@ -674,10 +674,10 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: gt3_flutter_plugin
|
||||
sha256: f12bff2bfbcf27467833f8d564dcc24ee2f1b3254a7c7cf5eb2c4590baf11cc1
|
||||
sha256: "08f35692e937770ad6b3e2017eb8ef81839a82b8a63f5acf3abab14b688fc36c"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "0.0.8"
|
||||
version: "0.1.0"
|
||||
hive:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@ -862,6 +862,14 @@ packages:
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.2.0"
|
||||
lottie:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: lottie
|
||||
sha256: "6a24ade5d3d918c306bb1c21a6b9a04aab0489d51a2582522eea820b4093b62b"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "3.1.2"
|
||||
mailer:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -973,10 +981,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: mime
|
||||
sha256: e4ff8e8564c03f255408decd16e7899da1733852a9110a58fe6d1b817684a63e
|
||||
sha256: "2e123074287cc9fd6c09de8336dae606d1ddb88d9ac47358826db698c176a1f2"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.0.4"
|
||||
version: "1.0.5"
|
||||
nm:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -1046,10 +1054,10 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: path_provider
|
||||
sha256: a1aa8aaa2542a6bc57e381f132af822420216c80d4781f7aa085ca3229208aaa
|
||||
sha256: c9e7d3a4cd1410877472158bee69963a4579f78b68c65a2b7d40d1a7a88bb161
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.1.1"
|
||||
version: "2.1.3"
|
||||
path_provider_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -1062,10 +1070,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_foundation
|
||||
sha256: "19314d595120f82aca0ba62787d58dde2cc6b5df7d2f0daf72489e38d1b57f2d"
|
||||
sha256: f234384a3fdd67f989b4d54a5d73ca2a6c422fa55ae694381ae0f4375cd1ea16
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.3.1"
|
||||
version: "2.4.0"
|
||||
path_provider_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -1210,6 +1218,22 @@ packages:
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "3.1.0"
|
||||
qr:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: qr
|
||||
sha256: "64957a3930367bf97cc211a5af99551d630f2f4625e38af10edd6b19131b64b3"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "3.0.1"
|
||||
qr_flutter:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: qr_flutter
|
||||
sha256: "5095f0fc6e3f71d08adef8feccc8cea4f12eec18a2e31c2e8d82cb6019f4b097"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "4.1.0"
|
||||
rxdart:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -1230,10 +1254,10 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: saver_gallery
|
||||
sha256: "2657953427ebe5a3b2d08157d41587c01923ccce3f1a616d55082be7470f8530"
|
||||
sha256: "0f740608072053a0da3b19cc5812a87e36f5c3c0b959d2475c4eb3d697f4a782"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "3.0.1"
|
||||
version: "3.0.3"
|
||||
screen_brightness:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -132,7 +132,7 @@ dependencies:
|
||||
# html渲染
|
||||
flutter_html: ^3.0.0-beta.2
|
||||
# 极验
|
||||
gt3_flutter_plugin: ^0.0.8
|
||||
gt3_flutter_plugin: ^0.1.0
|
||||
uuid: ^3.0.7
|
||||
scrollable_positioned_list: ^0.3.8
|
||||
catcher_2: ^1.2.6
|
||||
@ -144,6 +144,9 @@ dependencies:
|
||||
expandable: ^5.0.1
|
||||
# 投屏
|
||||
dlna_dart: ^0.0.8
|
||||
lottie: ^3.1.2
|
||||
# 二维码
|
||||
qr_flutter: ^4.1.0
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
@ -203,6 +206,7 @@ flutter:
|
||||
|
||||
# To add assets to your application, add an assets section, like this:
|
||||
assets:
|
||||
- assets/
|
||||
- assets/images/
|
||||
- assets/images/lv/
|
||||
- assets/images/logo/
|
||||
|
Reference in New Issue
Block a user