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

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();
}
}