You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
265 lines
6.8 KiB
Dart
265 lines
6.8 KiB
Dart
import 'dart:convert';
|
|
|
|
import 'package:flutter/foundation.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter_web_browser/flutter_web_browser.dart';
|
|
import 'package:get/get.dart';
|
|
import 'package:isekai_wiki/api/base_api.dart';
|
|
import 'package:isekai_wiki/api/mw/mw_api.dart';
|
|
import 'package:isekai_wiki/api/mw/user.dart';
|
|
import 'package:isekai_wiki/api/response/mugenapp.dart';
|
|
import 'package:isekai_wiki/global.dart';
|
|
import 'package:isekai_wiki/styles.dart';
|
|
import 'package:isekai_wiki/utils/dialog.dart';
|
|
import 'package:isekai_wiki/utils/error.dart';
|
|
import 'package:json_annotation/json_annotation.dart';
|
|
|
|
part 'user.g.dart';
|
|
|
|
@JsonSerializable()
|
|
class UserInfo {
|
|
int userId;
|
|
String userName;
|
|
String? nickName;
|
|
Map<int, String>? avatarUrlSet;
|
|
|
|
UserInfo({
|
|
required this.userId,
|
|
required this.userName,
|
|
this.nickName,
|
|
this.avatarUrlSet,
|
|
});
|
|
|
|
factory UserInfo.fromJson(Map<String, dynamic> json) => _$UserInfoFromJson(json);
|
|
|
|
Map<String, dynamic> toJson() => _$UserInfoToJson(this);
|
|
}
|
|
|
|
class UserController extends GetxController {
|
|
bool _ignoreSave = false;
|
|
|
|
var authProcessing = false.obs;
|
|
|
|
var loginRequestToken = "".obs;
|
|
|
|
var userId = 0.obs;
|
|
|
|
var userName = "".obs;
|
|
var nickName = "".obs;
|
|
var avatarUrlSet = RxMap<int, String>({});
|
|
|
|
bool get isLoggedIn {
|
|
return userId.value > 0;
|
|
}
|
|
|
|
String get displayName {
|
|
return nickName.isNotEmpty ? nickName.string : userName.string;
|
|
}
|
|
|
|
@override
|
|
void onInit() async {
|
|
super.onInit();
|
|
|
|
loadFromStorage();
|
|
updateProfile();
|
|
|
|
ever(loginRequestToken, (String token) {
|
|
saveToStorage();
|
|
});
|
|
}
|
|
|
|
/// 更新用户资料,并检测登录状态
|
|
Future<void> updateProfile() async {
|
|
try {
|
|
var userInfoMWRes = await MWApiUser.getCurrentUserInfo();
|
|
|
|
var userInfoRes = userInfoMWRes.data;
|
|
|
|
if (userInfoRes.userinfo.id == 0) {
|
|
// 登录状态已失效,提示用户
|
|
// 由于刷新用户信息可能出现在首页加载完成之前,这里应该需要一个消息列表
|
|
// 把加载中的错误放在消息列表中,首页加载完成后弹出
|
|
logout(logoutRemote: false);
|
|
}
|
|
|
|
userId.value = userInfoRes.userinfo.id;
|
|
userName.value = userInfoRes.userinfo.name;
|
|
nickName.value = userInfoRes.userinfo.realname ?? "";
|
|
if (userInfoRes.useravatar != null) {
|
|
avatarUrlSet.value = userInfoRes.useravatar!;
|
|
}
|
|
} catch (err, stack) {
|
|
if (kDebugMode) {
|
|
printError(info: "Cannot update profile of current user: $err");
|
|
stack.printError();
|
|
}
|
|
}
|
|
}
|
|
|
|
String? getAvatar(int size) {
|
|
if (avatarUrlSet.isEmpty) {
|
|
return null;
|
|
}
|
|
|
|
for (var imgSize in avatarUrlSet.keys) {
|
|
if (size < imgSize) {
|
|
return avatarUrlSet[size];
|
|
}
|
|
}
|
|
|
|
return avatarUrlSet.values.last;
|
|
}
|
|
|
|
/// 从本地存储读取
|
|
void loadFromStorage() {
|
|
try {
|
|
final prefs = Global.sharedPreferences!;
|
|
var userInfoJson = prefs.getString("userInfo");
|
|
if (userInfoJson == null) return;
|
|
|
|
var userInfoObject = jsonDecode(userInfoJson);
|
|
if (userInfoObject == null) return;
|
|
|
|
var userInfo = UserInfo.fromJson(userInfoObject);
|
|
|
|
_ignoreSave = true;
|
|
userId.value = userInfo.userId;
|
|
userName.value = userInfo.userName;
|
|
nickName.value = userInfo.nickName ?? "";
|
|
avatarUrlSet.value = userInfo.avatarUrlSet ?? {};
|
|
_ignoreSave = false;
|
|
|
|
var savedLoginRequestToken = prefs.getString("loginRequestToken");
|
|
if (savedLoginRequestToken != null) {
|
|
_ignoreSave = true;
|
|
loginRequestToken.value = savedLoginRequestToken;
|
|
_ignoreSave = false;
|
|
}
|
|
} catch (ex) {
|
|
if (kDebugMode) {
|
|
print(ex);
|
|
}
|
|
} finally {
|
|
_ignoreSave = false;
|
|
}
|
|
}
|
|
|
|
/// 保存到本地存储
|
|
void saveToStorage() {
|
|
if (_ignoreSave) return;
|
|
|
|
final prefs = Global.sharedPreferences!;
|
|
|
|
var userInfo = UserInfo(
|
|
userId: userId.value,
|
|
userName: userName.value,
|
|
nickName: nickName.isNotEmpty ? nickName.value : null,
|
|
avatarUrlSet: avatarUrlSet.isNotEmpty ? avatarUrlSet : null,
|
|
);
|
|
|
|
var userInfoJson = jsonEncode(userInfo.toJson());
|
|
|
|
prefs.setString("userInfo", userInfoJson);
|
|
|
|
if (loginRequestToken.isNotEmpty) {
|
|
prefs.setString("loginRequestToken", loginRequestToken.value);
|
|
} else if (prefs.containsKey("loginRequestToken")) {
|
|
prefs.remove("loginRequestToken");
|
|
}
|
|
}
|
|
|
|
/// 开始登录流程
|
|
/// 从服务器获取loginRequestToken
|
|
Future<void> startAuthFlow() async {
|
|
authProcessing.value = true;
|
|
|
|
try {
|
|
var startAuthRes = await MWApiUser.startAuth();
|
|
|
|
var startAuthInfo = startAuthRes.data;
|
|
loginRequestToken.value = startAuthInfo.loginRequestKey;
|
|
|
|
await openUrl(startAuthInfo.loginUrl, inApp: true);
|
|
} catch (err, stack) {
|
|
authProcessing.value = false;
|
|
loginRequestToken.value = "";
|
|
|
|
alert(Get.overlayContext!, ErrorUtils.getErrorMessage(err), title: "错误");
|
|
if (kDebugMode) {
|
|
print("Exception in startAuthFlow: $err");
|
|
stack.printError();
|
|
}
|
|
}
|
|
}
|
|
|
|
// 尝试通过登录请求Token完成登录
|
|
Future<void> attemptFinishAuth() async {
|
|
if (loginRequestToken.isEmpty) {
|
|
authProcessing.value = false;
|
|
return;
|
|
}
|
|
|
|
MWResponse<MugenAppAttemptAuthInfo> attemptAuthRes;
|
|
|
|
try {
|
|
attemptAuthRes = await MWApiUser.attemptAuth(loginRequestToken.value);
|
|
} catch (err, stack) {
|
|
authProcessing.value = false;
|
|
alert(Get.overlayContext!, ErrorUtils.getErrorMessage(err), title: "错误");
|
|
if (kDebugMode) {
|
|
print("Exception in startAuthFlow: $err");
|
|
stack.printError();
|
|
}
|
|
return;
|
|
}
|
|
|
|
var attemptAuthInfo = attemptAuthRes.data;
|
|
if (attemptAuthInfo.status == "pending") {
|
|
authProcessing.value = false;
|
|
loginRequestToken.value = "";
|
|
|
|
alert(Get.overlayContext!, "已取消登录", title: "提示");
|
|
|
|
return;
|
|
} else if (attemptAuthInfo.status == "success") {
|
|
loginRequestToken.value = "";
|
|
|
|
userId.value = attemptAuthInfo.userid!;
|
|
userName.value = attemptAuthInfo.username!;
|
|
|
|
await updateProfile();
|
|
|
|
authProcessing.value = false;
|
|
saveToStorage();
|
|
}
|
|
}
|
|
|
|
Future<void> logout({bool logoutRemote = true}) async {
|
|
authProcessing.value = true;
|
|
|
|
try {
|
|
await MWApiUser.logout();
|
|
} catch (err, stack) {
|
|
alert(Get.overlayContext!, ErrorUtils.getErrorMessage(err), title: "错误");
|
|
if (kDebugMode) {
|
|
print("Exception in logout: $err");
|
|
stack.printError();
|
|
}
|
|
return;
|
|
}
|
|
|
|
// 清除Cookie
|
|
await BaseApi.clearCookie();
|
|
|
|
// 清除所有用户信息
|
|
userId.value = 0;
|
|
userName.value = "";
|
|
nickName.value = "";
|
|
avatarUrlSet.clear();
|
|
|
|
authProcessing.value = true;
|
|
|
|
saveToStorage();
|
|
}
|
|
}
|