import 'dart:convert'; import 'dart:io'; import 'dart:typed_data'; import 'package:dio/dio.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter_easyloading/flutter_easyloading.dart'; import 'package:flutter_styled_toast/flutter_styled_toast.dart'; import 'package:path_provider/path_provider.dart'; import 'package:permission_handler/permission_handler.dart'; import 'package:prime_chat/basic/Api.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:flutter_chat_types/flutter_chat_types.dart' as types; import 'Entities.dart'; import 'Global.dart'; class Handler { Handler(this.name) : value = prefs.getString(name) ?? ""; final String name; late String value; Future set(String value) async { this.value = value; return prefs.setString(name, value); } } final method = Method._(); class Method { Method._(); Future init() async { _token = Handler("token"); _name = Handler("name"); _password = Handler("password"); _uid = Handler("uid"); return await preLogin(); } late Handler _token; late Handler _name; late Handler _password; late Handler _uid; get dio => token.isEmpty ? Dio() : Dio(BaseOptions( headers: {HttpHeaders.authorizationHeader: "Bearer $token"})); get token => _token.value; set token(value) => _token.set(value); get name => _name.value; set name(value) => _name.set(value); get password => _password.value; set password(value) => _password.set(value); get uid => _uid.value; set uid(value) => _uid.set(value); get user => types.User(id: uid); Future _handleDioError(DioError e, {int retry = 0}) async { final res = e.response; final req = e.requestOptions; final data = jsonDecode(res.toString()); final code = res == null ? -1 : res.statusCode; debugPrint("api error, path=${req.path} code=$code error=${data['error']}"); switch (code) { case HttpStatus.unauthorized: if (await login()) { final res = await request(req.path, data: req.data, queryParameters: req.queryParameters, options: Options(method: req.method), retry: retry); return res; } break; } } Future request(String path, {data, Map? queryParameters, Options? options, int retry = 0}) async { if (retry < 0) return null; try { final res = await dio.request( path, data: data, queryParameters: queryParameters, options: options, ); return res; } on DioError catch (e) { final ret = await _handleDioError(e, retry: retry); if (ret is Response) { return ret; } } return null; } Future get authorized async { final res = await request("$server/home", options: Options(method: "get")); if (res?.statusCode == HttpStatus.ok) { debugPrint("token=$token authorized"); final data = jsonDecode(res.toString()); debugPrint("authorized: ${data.toString()}"); uid = data["uid"].toString(); return true; } return false; } Future login() async { final name = _name.value, password = _password.value; debugPrint('login: name=$name password=$password'); if (name.isEmpty || password.isEmpty) return false; final res = await request("$server/token", data: {"name": name, "password": password}, options: Options(method: "post")); if (res?.statusCode == HttpStatus.ok) { final data = jsonDecode(res.toString()); token = data['token']; debugPrint('login ok, token=$token'); // Todo: 修改uid的更新方式为:login时获取profile return authorized; } return false; } logout() { token = ""; password = ""; } Future preLogin() async { if (await authorized) return true; return login(); } Future?> getFriends() async { final res = await request('$server/friends', options: Options(method: "get")); if (res?.statusCode == HttpStatus.ok) { var data = jsonDecode(res.toString()); List friends = []; data["friends"].forEach((v) => friends.add(Profile.fromJson(v))); // debugPrint(friends.toString()); return friends; } return null; } Future getProfile({String? name}) async { name = name ?? _name.value; debugPrint("get profile of $name"); final res = await request("$server/profile/$name", options: Options(method: "get")); if (res?.statusCode == HttpStatus.ok) { final data = jsonDecode(res.toString()); final profile = Profile.fromJson(data["profile"]); debugPrint("profile get, name=${profile.name}"); return profile; } return null; } Future download( String url, String savePath, { Map? queryParams, CancelToken? cancelToken, dynamic data, Options? options, void Function(int, int)? onReceiveProgress, }) async { try { return await dio.download( url, savePath, queryParameters: queryParams, cancelToken: cancelToken, onReceiveProgress: onReceiveProgress, ); } on DioError catch (e) { print(e); if (CancelToken.isCancel(e)) { EasyLoading.showInfo('下载已取消!'); } else { if (e.response != null) { // _handleErrorResponse(e.response); } else { EasyLoading.showError(e.message); } } } on Exception catch (e) { // EasyLoading.showError(e.toString()); } } Future downloadImage(String path, String fileServer) async { Directory dir = await getApplicationDocumentsDirectory(); String dirPath = dir.path; final savePath = "$dirPath/$path"; File file = File(savePath); debugPrint("savePath=$savePath"); if (file.existsSync()) return savePath; try { final res = await dio.download('$fileServer/$path', savePath); debugPrint(res.toString()); } on Exception catch (e) { debugPrint("文件下载失败"); return null; } return savePath; } Future updateAvatar(Uint8List buff, String suf) async { final file = MultipartFile.fromBytes(buff, filename: 'avatar$suf'); final res = await request("$server/avatar", data: FormData.fromMap({"file": file})); return res?.statusCode == HttpStatus.ok; } Future?> getMessages(String roomId) async { final res = await request("$server/messages/$roomId", options: Options(method: "get")); if (res?.statusCode != HttpStatus.ok) return null; final data = jsonDecode(res.toString()); List messages = []; debugPrint(data.toString()); for (var msg in data["messages"]) { messages.add(types.TextMessage( author: types.User(id: msg["authorId"].toString()), id: msg["id"], text: msg["content"], createdAt: msg["createdAt"]*1000 ?? 0 )); } messages.sort((a, b) => b.createdAt!.compareTo(a.createdAt!)); debugPrint(messages.toString()); return messages; } Future sendMessage( String roomId, types.MessageType type, String content, {int receiverId = 0}) async { final res = await request("$server/message", data: { "roomId": roomId, "type": type.name, "content": content, "receiverId": receiverId }, options: Options(method: "post")); return res?.statusCode == HttpStatus.ok; } String directRoomId(String id) { String a = uid, b = id; final res = a.compareTo(b); switch (res) { case 0: throw Exception("id equals to current user's id"); case 1: return "direct:$b,$a"; case -1: return "direct:$a,$b"; } return ""; } }