diff --git a/lib/api/marianumcloud/webdav/queries/listFiles/cacheableFile.g.dart b/lib/api/marianumcloud/webdav/queries/listFiles/cacheableFile.g.dart index 99ba008..dc95eb7 100644 --- a/lib/api/marianumcloud/webdav/queries/listFiles/cacheableFile.g.dart +++ b/lib/api/marianumcloud/webdav/queries/listFiles/cacheableFile.g.dart @@ -20,7 +20,7 @@ CacheableFile _$CacheableFileFromJson(Map json) => modifiedAt: json['modifiedAt'] == null ? null : DateTime.parse(json['modifiedAt'] as String), - ); + )..sort = json['sort'] as String?; Map _$CacheableFileToJson(CacheableFile instance) => { @@ -32,4 +32,5 @@ Map _$CacheableFileToJson(CacheableFile instance) => 'eTag': instance.eTag, 'createdAt': instance.createdAt?.toIso8601String(), 'modifiedAt': instance.modifiedAt?.toIso8601String(), + 'sort': instance.sort, }; diff --git a/lib/api/mhsl/breaker/getBreakers/getBreakers.dart b/lib/api/mhsl/breaker/getBreakers/getBreakers.dart new file mode 100644 index 0000000..1067d54 --- /dev/null +++ b/lib/api/mhsl/breaker/getBreakers/getBreakers.dart @@ -0,0 +1,21 @@ +import 'dart:convert'; +import 'package:http/http.dart' as http; +import 'package:http/http.dart'; + +import '../../mhslApi.dart'; +import 'getBreakersResponse.dart'; + +class GetBreakers extends MhslApi { + GetBreakers() : super("breaker/"); + + @override + GetBreakersResponse assemble(String raw) { + return GetBreakersResponse.fromJson(jsonDecode(raw)); + } + + @override + Future? request(Uri uri) { + return http.get(uri); + } + +} \ No newline at end of file diff --git a/lib/api/mhsl/breaker/getBreakers/getBreakersCache.dart b/lib/api/mhsl/breaker/getBreakers/getBreakersCache.dart new file mode 100644 index 0000000..bf2f859 --- /dev/null +++ b/lib/api/mhsl/breaker/getBreakers/getBreakersCache.dart @@ -0,0 +1,21 @@ +import 'dart:convert'; +import '../../../requestCache.dart'; +import 'getBreakers.dart'; +import 'getBreakersResponse.dart'; + + +class GetBreakersCache extends RequestCache { + GetBreakersCache({onUpdate, renew}) : super(RequestCache.cacheMinute, onUpdate, renew: renew) { + start("MarianumMobile", "breakers"); + } + + @override + GetBreakersResponse onLocalData(String json) { + return GetBreakersResponse.fromJson(jsonDecode(json)); + } + + @override + Future onLoad() { + return GetBreakers().run(); + } +} \ No newline at end of file diff --git a/lib/api/mhsl/breaker/getBreakers/getBreakersResponse.dart b/lib/api/mhsl/breaker/getBreakers/getBreakersResponse.dart new file mode 100644 index 0000000..7de4d4a --- /dev/null +++ b/lib/api/mhsl/breaker/getBreakers/getBreakersResponse.dart @@ -0,0 +1,36 @@ + +import 'package:json_annotation/json_annotation.dart'; + +import '../../../apiResponse.dart'; + +part 'getBreakersResponse.g.dart'; + +@JsonSerializable(explicitToJson: true) +class GetBreakersResponse extends ApiResponse { + GetBreakersReponseObject global; + Map regional; + + GetBreakersResponse(this.global, this.regional); + + factory GetBreakersResponse.fromJson(Map json) => _$GetBreakersResponseFromJson(json); + Map toJson() => _$GetBreakersResponseToJson(this); +} + +@JsonSerializable() +class GetBreakersReponseObject { + List areas; + String message; + + GetBreakersReponseObject(this.areas, this.message); + + factory GetBreakersReponseObject.fromJson(Map json) => _$GetBreakersReponseObjectFromJson(json); + Map toJson() => _$GetBreakersReponseObjectToJson(this); +} + +enum BreakerArea { + @JsonValue("GLOBAL") global, + @JsonValue("TIMETABLE") timetable, + @JsonValue("TALK") talk, + @JsonValue("FILES") files, + @JsonValue("MORE") more, +} \ No newline at end of file diff --git a/lib/api/mhsl/breaker/getBreakers/getBreakersResponse.g.dart b/lib/api/mhsl/breaker/getBreakers/getBreakersResponse.g.dart new file mode 100644 index 0000000..58c0986 --- /dev/null +++ b/lib/api/mhsl/breaker/getBreakers/getBreakersResponse.g.dart @@ -0,0 +1,47 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'getBreakersResponse.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +GetBreakersResponse _$GetBreakersResponseFromJson(Map json) => + GetBreakersResponse( + GetBreakersReponseObject.fromJson(json['global'] as Map), + (json['regional'] as Map).map( + (k, e) => MapEntry( + k, GetBreakersReponseObject.fromJson(e as Map)), + ), + ); + +Map _$GetBreakersResponseToJson( + GetBreakersResponse instance) => + { + 'global': instance.global.toJson(), + 'regional': instance.regional.map((k, e) => MapEntry(k, e.toJson())), + }; + +GetBreakersReponseObject _$GetBreakersReponseObjectFromJson( + Map json) => + GetBreakersReponseObject( + (json['areas'] as List) + .map((e) => $enumDecode(_$BreakerAreaEnumMap, e)) + .toList(), + json['message'] as String, + ); + +Map _$GetBreakersReponseObjectToJson( + GetBreakersReponseObject instance) => + { + 'areas': instance.areas.map((e) => _$BreakerAreaEnumMap[e]!).toList(), + 'message': instance.message, + }; + +const _$BreakerAreaEnumMap = { + BreakerArea.global: 'GLOBAL', + BreakerArea.timetable: 'TIMETABLE', + BreakerArea.talk: 'TALK', + BreakerArea.files: 'FILES', + BreakerArea.more: 'MORE', +}; diff --git a/lib/api/mhsl/message/getMessages/getMessages.dart b/lib/api/mhsl/message/getMessages/getMessages.dart index 99338ff..62704df 100644 --- a/lib/api/mhsl/message/getMessages/getMessages.dart +++ b/lib/api/mhsl/message/getMessages/getMessages.dart @@ -2,10 +2,12 @@ import 'dart:convert'; import 'package:http/http.dart' as http; -import '../messageApi.dart'; +import '../../mhslApi.dart'; import 'getMessagesResponse.dart'; -class GetMessages extends MessageApi { +class GetMessages extends MhslApi { + GetMessages() : super("message/messages.json"); + @override GetMessagesResponse assemble(String raw) { diff --git a/lib/api/mhsl/message/messageApi.dart b/lib/api/mhsl/mhslApi.dart similarity index 62% rename from lib/api/mhsl/message/messageApi.dart rename to lib/api/mhsl/mhslApi.dart index d0b15e0..fa9ec02 100644 --- a/lib/api/mhsl/message/messageApi.dart +++ b/lib/api/mhsl/mhslApi.dart @@ -1,18 +1,20 @@ import 'dart:convert'; import 'package:http/http.dart' as http; -import '../../apiError.dart'; -import '../../apiRequest.dart'; +import '../apiError.dart'; +import '../apiRequest.dart'; + +abstract class MhslApi extends ApiRequest { + String subpath; + MhslApi(this.subpath); -abstract class MessageApi extends ApiRequest { - String path = "https://mhsl.eu/marianum/marianummobile/message/messages.json"; http.Response? response; Future? request(Uri uri); T assemble(String raw); Future run() async { - Uri endpoint = Uri.parse(path); + Uri endpoint = Uri.parse("https://mhsl.eu/marianum/marianummobile/$subpath"); http.Response? data = await request(endpoint); if(data == null) { diff --git a/lib/app.dart b/lib/app.dart index 75eb558..a84648e 100644 --- a/lib/app.dart +++ b/lib/app.dart @@ -6,6 +6,9 @@ import 'package:persistent_bottom_nav_bar/persistent_tab_view.dart'; import 'package:provider/provider.dart'; import 'package:badges/badges.dart' as badges; +import 'api/mhsl/breaker/getBreakers/getBreakersResponse.dart'; +import 'model/breakers/Breaker.dart'; +import 'model/breakers/BreakerProps.dart'; import 'model/chatList/chatListProps.dart'; import 'view/pages/files/files.dart'; import 'view/pages/more/overhang.dart'; @@ -27,14 +30,16 @@ class _AppState extends State { @override void initState() { WidgetsBinding.instance.addPostFrameCallback((timeStamp) { + Provider.of(context, listen: false).run(); Provider.of(context, listen: false).run(); }); updateTimings = Timer.periodic(const Duration(seconds: 30), (Timer t) => setState((){})); - refetchChats = Timer.periodic(const Duration(minutes: 1), (timer) { - if(!context.mounted) return; - Provider.of(context, listen: false).run(); + refetchChats = Timer.periodic(const Duration(seconds: 60), (timer) { + WidgetsBinding.instance.addPostFrameCallback((timeStamp) { + Provider.of(context, listen: false).run(); + }); }); super.initState(); @@ -53,11 +58,11 @@ class _AppState extends State { border: Border.symmetric(vertical: BorderSide.none, horizontal: BorderSide(color: Colors.grey, width: 1)) ), screenTransitionAnimation: const ScreenTransitionAnimation(animateTabTransition: false, curve: Curves.ease, duration: Duration(milliseconds: 200)), - screens: const [ - Timetable(), - ChatList(), - Files([]), - Overhang(), + screens: [ + Breaker(breaker: BreakerArea.timetable, child: Timetable()), + Breaker(breaker: BreakerArea.talk, child: ChatList()), + Breaker(breaker: BreakerArea.files, child: Files([])), + Breaker(breaker: BreakerArea.more, child: Overhang()), ], items: [ PersistentBottomNavBarItem( diff --git a/lib/main.dart b/lib/main.dart index 7e35a68..d0af28b 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,3 +1,4 @@ +import 'dart:async'; import 'dart:io'; import 'package:flutter/material.dart'; @@ -6,9 +7,12 @@ import 'package:jiffy/jiffy.dart'; import 'package:provider/provider.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; +import 'api/mhsl/breaker/getBreakers/getBreakersResponse.dart'; import 'app.dart'; import 'model/accountData.dart'; import 'model/accountModel.dart'; +import 'model/breakers/Breaker.dart'; +import 'model/breakers/BreakerProps.dart'; import 'model/chatList/chatListProps.dart'; import 'model/chatList/chatProps.dart'; import 'model/files/filesProps.dart'; @@ -34,6 +38,8 @@ Future main() async { runApp( MultiProvider( providers: [ + ChangeNotifierProvider(create: (context) => BreakerProps()), + ChangeNotifierProvider(create: (context) => SettingsProvider()), ChangeNotifierProvider(create: (context) => AccountModel()), @@ -58,6 +64,8 @@ class Main extends StatefulWidget { } class _MainState extends State
{ + late Timer refetchProps; + @override void initState() { Jiffy.setLocale("de"); @@ -67,6 +75,10 @@ class _MainState extends State
{ .setState(value ? AccountModelState.loggedIn : AccountModelState.loggedOut); }); + refetchProps = Timer.periodic(const Duration(seconds: 60), (timer) { + Provider.of(context, listen: false).run(); + }); + super.initState(); } @@ -93,18 +105,27 @@ class _MainState extends State
{ themeMode: settings.val().appTheme, theme: LightAppTheme.theme, darkTheme: DarkAppTheme.theme, - home: Consumer( - builder: (context, accountModel, child) { - switch(accountModel.state) { - case AccountModelState.loggedIn: return const App(); - case AccountModelState.loggedOut: return const Login(); - case AccountModelState.undefined: return const PlaceholderView(icon: Icons.timer, text: "Daten werden geladen"); - } - }, - ), + home: Breaker( + breaker: BreakerArea.global, + child: Consumer( + builder: (context, accountModel, child) { + switch(accountModel.state) { + case AccountModelState.loggedIn: return const App(); + case AccountModelState.loggedOut: return const Login(); + case AccountModelState.undefined: return const PlaceholderView(icon: Icons.timer, text: "Daten werden geladen"); + } + }, + ) + ) ); }, ), ); } + + @override + void dispose() { + refetchProps.cancel(); + super.dispose(); + } } diff --git a/lib/model/breakers/Breaker.dart b/lib/model/breakers/Breaker.dart new file mode 100644 index 0000000..7fb4b24 --- /dev/null +++ b/lib/model/breakers/Breaker.dart @@ -0,0 +1,34 @@ + +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 createState() => _BreakerState(); +} + +class _BreakerState extends State { + @override + Widget build(BuildContext context) { + return Consumer( + builder: (context, value, child) { + String? blocked = value.isBlocked(widget.breaker); + if(blocked != null) { + return PlaceholderView(icon: Icons.security_outlined, text: "Die App/ Dieser Bereich wurde als Schutzmaßnahme deaktiviert!\n\n${blocked.isEmpty ? "Es wurde vom Server kein Grund übermittelt." : blocked}"); + } + + return widget.child; + }, + ); + } +} diff --git a/lib/model/breakers/BreakerProps.dart b/lib/model/breakers/BreakerProps.dart new file mode 100644 index 0000000..86a28ae --- /dev/null +++ b/lib/model/breakers/BreakerProps.dart @@ -0,0 +1,51 @@ +import 'package:package_info/package_info.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(packageInfo == null) { + PackageInfo.fromPlatform().then((value) => packageInfo = value); + return null; + } + + if(primaryLoading()) return null; + GetBreakersResponse breakers = _getBreakersResponse!; + + if(breakers.global.areas.contains(type)) return breakers.global.message; + + int selfVersion = int.parse(packageInfo!.buildNumber); + for(var key in breakers.regional.keys) { + GetBreakersReponseObject value = breakers.regional[key]!; + + if(int.parse(key.split("b")[1]) >= selfVersion) { + if(value.areas.contains(type)) return value.message; + } + } + + return null; + } + + @override + List properties() { + return [_getBreakersResponse]; + } + + @override + void run() { + GetBreakersCache( + onUpdate: (GetBreakersResponse getBreakersResponse) { + _getBreakersResponse = getBreakersResponse; + notifyListeners(); + } + ); + } +} \ No newline at end of file diff --git a/lib/view/login/login.dart b/lib/view/login/login.dart index 158f8ce..81806d7 100644 --- a/lib/view/login/login.dart +++ b/lib/view/login/login.dart @@ -3,10 +3,10 @@ import 'dart:developer'; import 'package:flutter/material.dart'; import 'package:flutter_login/flutter_login.dart'; -import 'package:marianum_mobile/api/marianumcloud/talk/room/getRoom.dart'; -import 'package:marianum_mobile/api/marianumcloud/talk/room/getRoomParams.dart'; import 'package:provider/provider.dart'; +import '../../api/marianumcloud/talk/room/getRoom.dart'; +import '../../api/marianumcloud/talk/room/getRoomParams.dart'; import '../../model/accountData.dart'; import '../../model/accountModel.dart'; diff --git a/lib/view/pages/talk/chatMessage.dart b/lib/view/pages/talk/chatMessage.dart index 894c5be..b4aee4b 100644 --- a/lib/view/pages/talk/chatMessage.dart +++ b/lib/view/pages/talk/chatMessage.dart @@ -2,12 +2,12 @@ import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:flutter_linkify/flutter_linkify.dart'; -import 'package:marianum_mobile/model/endpointData.dart'; import 'package:url_launcher/url_launcher_string.dart'; import '../../../api/marianumcloud/talk/chat/getChatResponse.dart'; import '../../../api/marianumcloud/talk/chat/richObjectStringProcessor.dart'; import '../../../model/accountData.dart'; +import '../../../model/endpointData.dart'; class ChatMessage { String originalMessage; diff --git a/lib/view/pages/talk/chatTile.dart b/lib/view/pages/talk/chatTile.dart index 8646fca..fe2088c 100644 --- a/lib/view/pages/talk/chatTile.dart +++ b/lib/view/pages/talk/chatTile.dart @@ -1,6 +1,5 @@ import 'package:flutter/material.dart'; import 'package:jiffy/jiffy.dart'; -import 'package:marianum_mobile/model/endpointData.dart'; import 'package:persistent_bottom_nav_bar/persistent_tab_view.dart'; import 'package:shared_preferences/shared_preferences.dart'; @@ -10,6 +9,7 @@ import '../../../api/marianumcloud/talk/room/getRoomResponse.dart'; import '../../../api/marianumcloud/talk/setFavorite/setFavorite.dart'; import '../../../api/marianumcloud/talk/setReadMarker/setReadMarker.dart'; import '../../../api/marianumcloud/talk/setReadMarker/setReadMarkerParams.dart'; +import '../../../model/endpointData.dart'; import '../../../widget/confirmDialog.dart'; import '../../../widget/debug/debugTile.dart'; import 'chatView.dart'; diff --git a/lib/view/pages/talk/joinChat.dart b/lib/view/pages/talk/joinChat.dart index f2bc239..a121889 100644 --- a/lib/view/pages/talk/joinChat.dart +++ b/lib/view/pages/talk/joinChat.dart @@ -1,10 +1,10 @@ import 'package:async/async.dart'; import 'package:flutter/material.dart'; -import 'package:marianum_mobile/model/endpointData.dart'; import '../../../api/marianumcloud/autocomplete/autocompleteApi.dart'; import '../../../api/marianumcloud/autocomplete/autocompleteResponse.dart'; +import '../../../model/endpointData.dart'; import '../../../widget/placeholderView.dart'; class JoinChat extends SearchDelegate {