claude refactor
This commit is contained in:
+51
-28
@@ -4,68 +4,91 @@ import 'dart:convert';
|
||||
import 'package:crypto/crypto.dart';
|
||||
import 'package:firebase_messaging/firebase_messaging.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
import 'accountModel.dart';
|
||||
import '../state/app/modules/account/bloc/account_bloc.dart';
|
||||
import '../state/app/modules/account/bloc/account_state.dart';
|
||||
|
||||
class AccountData {
|
||||
static const _usernameField = 'username';
|
||||
static const _passwordField = 'password';
|
||||
|
||||
|
||||
static const FlutterSecureStorage _secureStorage = FlutterSecureStorage(
|
||||
aOptions: AndroidOptions(encryptedSharedPreferences: true),
|
||||
);
|
||||
|
||||
static final AccountData _instance = AccountData._construct();
|
||||
final Future<SharedPreferences> _storage = SharedPreferences.getInstance();
|
||||
Completer<void> _populated = Completer();
|
||||
|
||||
factory AccountData() => _instance;
|
||||
|
||||
AccountData._construct() {
|
||||
_updateFromStorage();
|
||||
_migrateAndLoad();
|
||||
}
|
||||
|
||||
String? _username;
|
||||
String? _password;
|
||||
|
||||
String getUsername() {
|
||||
if(_username == null) throw Exception('Username not initialized');
|
||||
if (_username == null) throw Exception('Username not initialized');
|
||||
return _username!;
|
||||
}
|
||||
|
||||
String getPassword() {
|
||||
if(_password == null) throw Exception('Password not initialized');
|
||||
if (_password == null) throw Exception('Password not initialized');
|
||||
return _password!;
|
||||
}
|
||||
|
||||
String getUserSecret() => sha512.convert(utf8.encode('${AccountData().getUsername()}:${AccountData().getPassword()}')).toString();
|
||||
String getUserSecret() =>
|
||||
sha512.convert(utf8.encode('${getUsername()}:${getPassword()}')).toString();
|
||||
|
||||
Future<String> getDeviceId() async => sha512.convert(utf8.encode('${getUserSecret()}@${await FirebaseMessaging.instance.getToken()}')).toString();
|
||||
Future<String> getDeviceId() async => sha512
|
||||
.convert(utf8.encode('${getUserSecret()}@${await FirebaseMessaging.instance.getToken()}'))
|
||||
.toString();
|
||||
|
||||
Future<void> setData(String username, String password) async {
|
||||
var storage = await _storage;
|
||||
|
||||
storage.setString(_usernameField, username);
|
||||
storage.setString(_passwordField, password);
|
||||
await _updateFromStorage();
|
||||
await _secureStorage.write(key: _usernameField, value: username);
|
||||
await _secureStorage.write(key: _passwordField, value: password);
|
||||
_username = username;
|
||||
_password = password;
|
||||
if (!_populated.isCompleted) _populated.complete();
|
||||
}
|
||||
|
||||
Future<void> removeData({BuildContext? context}) async {
|
||||
_populated = Completer();
|
||||
|
||||
if(context != null) Provider.of<AccountModel>(context, listen: false).setState(AccountModelState.loggedOut);
|
||||
|
||||
var storage = await _storage;
|
||||
await storage.remove(_usernameField);
|
||||
await storage.remove(_passwordField);
|
||||
if (context != null) {
|
||||
context.read<AccountBloc>().setStatus(AccountStatus.loggedOut);
|
||||
}
|
||||
_username = null;
|
||||
_password = null;
|
||||
await _secureStorage.delete(key: _usernameField);
|
||||
await _secureStorage.delete(key: _passwordField);
|
||||
}
|
||||
|
||||
Future<void> _updateFromStorage() async {
|
||||
var storage = await _storage;
|
||||
//await storage.reload(); // This line was the cause of the first rejected google play upload :(
|
||||
if(storage.containsKey(_usernameField) && storage.containsKey(_passwordField)) {
|
||||
_username = storage.getString(_usernameField);
|
||||
_password = storage.getString(_passwordField);
|
||||
Future<void> _migrateAndLoad() async {
|
||||
await _migrateFromLegacyStorage();
|
||||
_username = await _secureStorage.read(key: _usernameField);
|
||||
_password = await _secureStorage.read(key: _passwordField);
|
||||
if (!_populated.isCompleted) _populated.complete();
|
||||
}
|
||||
|
||||
// Move credentials from the old SharedPreferences plain-text storage into the
|
||||
// platform's secure keystore. Run once per install and clear the legacy keys.
|
||||
Future<void> _migrateFromLegacyStorage() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
final legacyUsername = prefs.getString(_usernameField);
|
||||
final legacyPassword = prefs.getString(_passwordField);
|
||||
if (legacyUsername == null || legacyPassword == null) return;
|
||||
|
||||
final hasSecure = (await _secureStorage.read(key: _usernameField)) != null;
|
||||
if (!hasSecure) {
|
||||
await _secureStorage.write(key: _usernameField, value: legacyUsername);
|
||||
await _secureStorage.write(key: _passwordField, value: legacyPassword);
|
||||
}
|
||||
if(!_populated.isCompleted) _populated.complete();
|
||||
await prefs.remove(_usernameField);
|
||||
await prefs.remove(_passwordField);
|
||||
}
|
||||
|
||||
Future<bool> waitForPopulation() async {
|
||||
@@ -76,7 +99,7 @@ class AccountData {
|
||||
bool isPopulated() => _username != null && _password != null;
|
||||
|
||||
String buildHttpAuthString() {
|
||||
if(!isPopulated()) throw Exception('AccountData (e.g. username or password) is not initialized!');
|
||||
if (!isPopulated()) throw Exception('AccountData (e.g. username or password) is not initialized!');
|
||||
return '$_username:$_password';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
|
||||
class AccountModel extends ChangeNotifier {
|
||||
AccountModelState _accountState = AccountModelState.undefined;
|
||||
AccountModelState get state => _accountState;
|
||||
|
||||
void setState(AccountModelState state) {
|
||||
_accountState = state;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
enum AccountModelState {
|
||||
undefined,
|
||||
loggedIn,
|
||||
loggedOut,
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
import '../../api/mhsl/breaker/getBreakers/getBreakersResponse.dart';
|
||||
import '../../widget/placeholderView.dart';
|
||||
import 'BreakerProps.dart';
|
||||
|
||||
|
||||
class Breaker extends StatefulWidget {
|
||||
final BreakerArea breaker;
|
||||
final Widget child;
|
||||
|
||||
const Breaker({required this.breaker, required this.child, super.key});
|
||||
|
||||
@override
|
||||
State<Breaker> createState() => _BreakerState();
|
||||
}
|
||||
|
||||
class _BreakerState extends State<Breaker> {
|
||||
@override
|
||||
Widget build(BuildContext context) => Consumer<BreakerProps>(
|
||||
builder: (context, value, child) {
|
||||
var blocked = value.isBlocked(widget.breaker);
|
||||
if(blocked != null) {
|
||||
return PlaceholderView(
|
||||
icon: Icons.app_blocking_outlined,
|
||||
text: 'Die App / Dieser Bereich ist zurzeit nicht verfügbar!\n\n'
|
||||
"${blocked.isEmpty ? "Es wurde vom Server kein Grund übermittelt.\nAktualisiere die App und versuche es später erneut" : blocked}"
|
||||
);
|
||||
}
|
||||
|
||||
return widget.child;
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:package_info_plus/package_info_plus.dart';
|
||||
|
||||
import '../../api/apiResponse.dart';
|
||||
import '../../api/mhsl/breaker/getBreakers/getBreakersCache.dart';
|
||||
import '../../api/mhsl/breaker/getBreakers/getBreakersResponse.dart';
|
||||
import '../dataHolder.dart';
|
||||
|
||||
class BreakerProps extends DataHolder {
|
||||
GetBreakersResponse? _getBreakersResponse;
|
||||
GetBreakersResponse get getBreakersResponse => _getBreakersResponse!;
|
||||
|
||||
PackageInfo? packageInfo;
|
||||
|
||||
String? isBlocked(BreakerArea? type) {
|
||||
if(kDebugMode) return null;
|
||||
|
||||
if(packageInfo == null) {
|
||||
PackageInfo.fromPlatform().then((value) => packageInfo = value);
|
||||
return null;
|
||||
}
|
||||
|
||||
if(primaryLoading()) return null;
|
||||
var breakers = _getBreakersResponse!;
|
||||
|
||||
if(breakers.global.areas.contains(type)) return breakers.global.message;
|
||||
|
||||
var selfVersion = int.parse(packageInfo!.buildNumber);
|
||||
for(var key in breakers.regional.keys) {
|
||||
var value = breakers.regional[key]!;
|
||||
|
||||
if(int.parse(key.split('b')[1]) >= selfVersion) {
|
||||
if(value.areas.contains(type)) return value.message;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@override
|
||||
List<ApiResponse?> properties() => [_getBreakersResponse];
|
||||
|
||||
@override
|
||||
void run() {
|
||||
GetBreakersCache(
|
||||
onUpdate: (GetBreakersResponse getBreakersResponse) {
|
||||
_getBreakersResponse = getBreakersResponse;
|
||||
notifyListeners();
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
|
||||
import 'package:flutter_app_badge/flutter_app_badge.dart';
|
||||
|
||||
import '../../api/apiResponse.dart';
|
||||
import '../../api/marianumcloud/talk/room/getRoomCache.dart';
|
||||
import '../../api/marianumcloud/talk/room/getRoomResponse.dart';
|
||||
import '../dataHolder.dart';
|
||||
|
||||
class ChatListProps extends DataHolder {
|
||||
GetRoomResponse? _getRoomResponse;
|
||||
GetRoomResponse get getRoomsResponse => _getRoomResponse!;
|
||||
|
||||
@override
|
||||
List<ApiResponse?> properties() => [_getRoomResponse];
|
||||
|
||||
@override
|
||||
void run({renew}) {
|
||||
GetRoomCache(
|
||||
renew: renew,
|
||||
onUpdate: (GetRoomResponse data) => {
|
||||
_getRoomResponse = data,
|
||||
notifyListeners(),
|
||||
FlutterAppBadge.count(data.data.map((e) => e.unreadMessages).reduce((a, b) => a+b))
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
import '../../api/apiResponse.dart';
|
||||
import '../../api/marianumcloud/talk/chat/getChatCache.dart';
|
||||
import '../../api/marianumcloud/talk/chat/getChatResponse.dart';
|
||||
import '../../storage/base/settingsProvider.dart';
|
||||
import '../dataHolder.dart';
|
||||
|
||||
class ChatProps extends DataHolder {
|
||||
String _queryToken = '';
|
||||
DateTime _lastTokenSet = DateTime.now();
|
||||
int? _referenceMessageId;
|
||||
|
||||
GetChatResponse? _getChatResponse;
|
||||
GetChatResponse get getChatResponse => _getChatResponse!;
|
||||
|
||||
int? get getReferenceMessageId => _referenceMessageId;
|
||||
set unsafeInternalSetReferenceMessageId(int? reference) => _referenceMessageId = reference;
|
||||
|
||||
@override
|
||||
List<ApiResponse?> properties() => [_getChatResponse];
|
||||
|
||||
@override
|
||||
void run() {
|
||||
notifyListeners();
|
||||
if(_queryToken.isEmpty) return;
|
||||
var requestStart = DateTime.now();
|
||||
|
||||
GetChatCache(
|
||||
chatToken: _queryToken,
|
||||
onUpdate: (GetChatResponse data) {
|
||||
if(_lastTokenSet.isAfter(requestStart)) return; // Another request was faster
|
||||
|
||||
_getChatResponse = data;
|
||||
notifyListeners();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void setReferenceMessageId(int? messageId, BuildContext context, String sendToToken) {
|
||||
Future.microtask(() {
|
||||
_referenceMessageId = messageId;
|
||||
notifyListeners();
|
||||
|
||||
var settings = Provider.of<SettingsProvider>(context, listen: false);
|
||||
if(messageId != null) {
|
||||
settings.val(write: true).talkSettings.draftReplies[sendToToken] = messageId;
|
||||
} else {
|
||||
settings.val(write: true).talkSettings.draftReplies.removeWhere((key, value) => key == sendToToken);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void setQueryToken(String token) {
|
||||
_queryToken = token;
|
||||
_getChatResponse = null;
|
||||
_lastTokenSet = DateTime.now();
|
||||
run();
|
||||
}
|
||||
|
||||
String currentToken() => _queryToken;
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:localstore/localstore.dart';
|
||||
|
||||
import '../api/apiResponse.dart';
|
||||
|
||||
abstract class DataHolder extends ChangeNotifier {
|
||||
|
||||
CollectionRef storage(String path) => Localstore.instance.collection(path);
|
||||
|
||||
void run();
|
||||
List<ApiResponse?> properties();
|
||||
|
||||
bool primaryLoading() {
|
||||
// log("${toString()} ${properties().map((e) => e != null ? "1" : "0").join(", ")}");
|
||||
for(var element in properties()) {
|
||||
if(element == null) return true;
|
||||
}
|
||||
return false;
|
||||
//return properties().where((element) => element != null).isEmpty;
|
||||
}
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
|
||||
import '../../api/apiResponse.dart';
|
||||
import '../../api/marianumcloud/webdav/queries/listFiles/listFilesCache.dart';
|
||||
import '../../api/marianumcloud/webdav/queries/listFiles/listFilesResponse.dart';
|
||||
import '../dataHolder.dart';
|
||||
|
||||
extension ExtendedList on List {
|
||||
T indexOrNull<T>(int index) => index +1 <= length ? this[index] : null;
|
||||
T firstOrNull<T>() => isEmpty ? null : first;
|
||||
T lastOrNull<T>() => isEmpty ? null : last;
|
||||
}
|
||||
|
||||
class FilesProps extends DataHolder {
|
||||
List<String> folderPath = List<String>.empty(growable: true);
|
||||
String currentFolderName = 'Home';
|
||||
|
||||
ListFilesResponse? _listFilesResponse;
|
||||
ListFilesResponse get listFilesResponse => _listFilesResponse!;
|
||||
|
||||
void runPath(List<String> path) {
|
||||
folderPath = path;
|
||||
run();
|
||||
}
|
||||
|
||||
@override
|
||||
List<ApiResponse?> properties() => [_listFilesResponse];
|
||||
|
||||
@override
|
||||
void run() {
|
||||
_listFilesResponse = null;
|
||||
notifyListeners();
|
||||
ListFilesCache(
|
||||
path: folderPath.isEmpty ? '/' : folderPath.join('/'),
|
||||
onUpdate: (ListFilesResponse data) => {
|
||||
_listFilesResponse = data,
|
||||
notifyListeners(),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void enterFolder(String name) {
|
||||
folderPath.add(name);
|
||||
currentFolderName = name;
|
||||
run();
|
||||
}
|
||||
|
||||
void popFolder() {
|
||||
folderPath.removeLast();
|
||||
if(folderPath.isEmpty) currentFolderName = 'Home';
|
||||
run();
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
|
||||
import '../../api/apiResponse.dart';
|
||||
import '../../api/holidays/getHolidaysCache.dart';
|
||||
import '../../api/holidays/getHolidaysResponse.dart';
|
||||
import '../dataHolder.dart';
|
||||
|
||||
|
||||
class HolidaysProps extends DataHolder {
|
||||
GetHolidaysResponse? _getHolidaysResponse;
|
||||
GetHolidaysResponse get getHolidaysResponse => _getHolidaysResponse!;
|
||||
|
||||
@override
|
||||
List<ApiResponse?> properties() => [_getHolidaysResponse];
|
||||
|
||||
@override
|
||||
void run() {
|
||||
GetHolidaysCache(
|
||||
onUpdate: (GetHolidaysResponse data) => {
|
||||
_getHolidaysResponse = data,
|
||||
notifyListeners(),
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,130 +0,0 @@
|
||||
import 'package:intl/intl.dart';
|
||||
|
||||
import '../../api/apiResponse.dart';
|
||||
import '../../api/mhsl/customTimetableEvent/get/getCustomTimetableEventCache.dart';
|
||||
import '../../api/mhsl/customTimetableEvent/get/getCustomTimetableEventParams.dart';
|
||||
import '../../api/mhsl/customTimetableEvent/get/getCustomTimetableEventResponse.dart';
|
||||
import '../../api/webuntis/queries/getHolidays/getHolidaysCache.dart';
|
||||
import '../../api/webuntis/queries/getHolidays/getHolidaysResponse.dart';
|
||||
import '../../api/webuntis/queries/getRooms/getRoomsCache.dart';
|
||||
import '../../api/webuntis/queries/getRooms/getRoomsResponse.dart';
|
||||
import '../../api/webuntis/queries/getSubjects/getSubjectsCache.dart';
|
||||
import '../../api/webuntis/queries/getSubjects/getSubjectsResponse.dart';
|
||||
import '../../api/webuntis/queries/getTimetable/getTimetableCache.dart';
|
||||
import '../../api/webuntis/queries/getTimetable/getTimetableResponse.dart';
|
||||
import '../../api/webuntis/webuntisError.dart';
|
||||
import '../accountData.dart';
|
||||
import '../dataHolder.dart';
|
||||
|
||||
class TimetableProps extends DataHolder {
|
||||
final _queryWeek = DateTime.now().add(const Duration(days: 2));
|
||||
|
||||
late DateTime startDate = getDate(_queryWeek.subtract(Duration(days: _queryWeek.weekday - 1)));
|
||||
late DateTime endDate = getDate(_queryWeek.add(Duration(days: DateTime.daysPerWeek - _queryWeek.weekday - 2)));
|
||||
|
||||
GetTimetableResponse? _getTimetableResponse;
|
||||
GetTimetableResponse get getTimetableResponse => _getTimetableResponse!;
|
||||
|
||||
GetRoomsResponse? _getRoomsResponse;
|
||||
GetRoomsResponse get getRoomsResponse => _getRoomsResponse!;
|
||||
|
||||
GetSubjectsResponse? _getSubjectsResponse;
|
||||
GetSubjectsResponse get getSubjectsResponse => _getSubjectsResponse!;
|
||||
|
||||
GetHolidaysResponse? _getHolidaysResponse;
|
||||
GetHolidaysResponse get getHolidaysResponse => _getHolidaysResponse!;
|
||||
|
||||
GetCustomTimetableEventResponse? _getCustomTimetableEventResponse;
|
||||
GetCustomTimetableEventResponse get getCustomTimetableEventResponse => _getCustomTimetableEventResponse!;
|
||||
|
||||
WebuntisError? error;
|
||||
WebuntisError? get getError => error;
|
||||
bool get hasError => error != null;
|
||||
|
||||
@override
|
||||
List<ApiResponse?> properties() => [_getTimetableResponse, _getRoomsResponse, _getSubjectsResponse, _getHolidaysResponse, _getCustomTimetableEventResponse];
|
||||
|
||||
@override
|
||||
void run({renew}) {
|
||||
GetTimetableCache(
|
||||
startdate: int.parse(DateFormat('yyyyMMdd').format(startDate)),
|
||||
enddate: int.parse(DateFormat('yyyyMMdd').format(endDate)),
|
||||
onUpdate: (GetTimetableResponse data) => {
|
||||
_getTimetableResponse = data,
|
||||
notifyListeners(),
|
||||
},
|
||||
onError: (Exception e) => {
|
||||
error = e as WebuntisError?,
|
||||
notifyListeners(),
|
||||
}
|
||||
);
|
||||
|
||||
GetRoomsCache(
|
||||
onUpdate: (GetRoomsResponse data) => {
|
||||
_getRoomsResponse = data,
|
||||
notifyListeners(),
|
||||
}
|
||||
);
|
||||
|
||||
GetSubjectsCache(
|
||||
onUpdate: (GetSubjectsResponse data) => {
|
||||
_getSubjectsResponse = data,
|
||||
notifyListeners(),
|
||||
}
|
||||
);
|
||||
|
||||
GetHolidaysCache( // This broke in the past. Below here is backup simulation for an empty holiday block
|
||||
onUpdate: (GetHolidaysResponse data) => {
|
||||
_getHolidaysResponse = data,
|
||||
notifyListeners(),
|
||||
}
|
||||
);
|
||||
// _getHolidaysResponse = GetHolidaysResponse.fromJson(jsonDecode("""
|
||||
// {"jsonrpc":"2.0","id":"ID","result":[]}
|
||||
// """));
|
||||
|
||||
GetCustomTimetableEventCache(
|
||||
renew: renew,
|
||||
GetCustomTimetableEventParams(
|
||||
AccountData().getUserSecret()
|
||||
),
|
||||
onUpdate: (GetCustomTimetableEventResponse data) => {
|
||||
_getCustomTimetableEventResponse = data,
|
||||
notifyListeners(),
|
||||
}
|
||||
);
|
||||
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
DateTime getDate(DateTime d) => DateTime(d.year, d.month, d.day);
|
||||
|
||||
bool isWeekend(DateTime queryDate) => queryDate.weekday == DateTime.saturday || queryDate.weekday == DateTime.sunday;
|
||||
|
||||
void updateWeek(DateTime start, DateTime end) {
|
||||
properties().forEach((element) => element = null);
|
||||
error = null;
|
||||
notifyListeners();
|
||||
startDate = start;
|
||||
endDate = end;
|
||||
try {
|
||||
run();
|
||||
} on WebuntisError catch(e) {
|
||||
error = e;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
void resetWeek() {
|
||||
error = null;
|
||||
notifyListeners();
|
||||
|
||||
var queryWeek = DateTime.now().add(const Duration(days: 2));
|
||||
|
||||
startDate = getDate(queryWeek.subtract(Duration(days: queryWeek.weekday - 1)));
|
||||
endDate = getDate(queryWeek.add(Duration(days: DateTime.daysPerWeek - queryWeek.weekday)));
|
||||
|
||||
run();
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user