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:get_storage/get_storage.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? avatarUrlSet; UserInfo({ required this.userId, required this.userName, this.nickName, this.avatarUrlSet, }); factory UserInfo.fromJson(Map json) => _$UserInfoFromJson(json); Map 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({}); 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 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 storage = GetStorage(); var userInfoJson = storage.read("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 = storage.read("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 storage = GetStorage(); 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()); storage.write("userInfo", userInfoJson); if (loginRequestToken.isNotEmpty) { storage.write("loginRequestToken", loginRequestToken.value); } else { storage.remove("loginRequestToken"); } } /// 开始登录流程 /// 从服务器获取loginRequestToken Future 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 attemptFinishAuth() async { if (loginRequestToken.isEmpty) { authProcessing.value = false; return; } MWResponse 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 logout({bool logoutRemote = true}) async { if (logoutRemote) { // 登出网站 authProcessing.value = true; try { await MWApiUser.logout(); } catch (err, stack) { authProcessing.value = false; alert(Get.overlayContext!, ErrorUtils.getErrorMessage(err), title: "错误"); if (kDebugMode) { print("Exception in logout: $err"); stack.printError(); } } } // 清除Cookie await BaseApi.clearCookie(); // 清除所有用户信息 userId.value = 0; userName.value = ""; nickName.value = ""; avatarUrlSet.value = {}; authProcessing.value = false; saveToStorage(); } }