diff --git a/.idea/libraries/Dart_Packages.xml b/.idea/libraries/Dart_Packages.xml index 307aa47..df7af86 100644 --- a/.idea/libraries/Dart_Packages.xml +++ b/.idea/libraries/Dart_Packages.xml @@ -5,14 +5,14 @@ - - @@ -89,7 +89,7 @@ - @@ -194,7 +194,7 @@ - @@ -222,7 +222,7 @@ - @@ -383,7 +383,7 @@ - @@ -653,20 +653,6 @@ - - - - - - - - - - - - @@ -695,13 +681,6 @@ - - - - - - @@ -740,7 +719,7 @@ - @@ -824,7 +803,7 @@ - @@ -845,7 +824,7 @@ - @@ -859,8 +838,8 @@ - - + + @@ -871,7 +850,7 @@ - + @@ -886,11 +865,11 @@ - + - + @@ -910,7 +889,7 @@ - + @@ -948,19 +927,16 @@ - - - - + @@ -972,10 +948,10 @@ - + - + diff --git a/.idea/libraries/Flutter_Plugins.xml b/.idea/libraries/Flutter_Plugins.xml index 0ed35db..75b4719 100644 --- a/.idea/libraries/Flutter_Plugins.xml +++ b/.idea/libraries/Flutter_Plugins.xml @@ -11,7 +11,6 @@ - diff --git a/assets/background/chat.png b/assets/background/chat.png new file mode 100644 index 0000000..d41c9fc Binary files /dev/null and b/assets/background/chat.png differ diff --git a/flutter_01.log b/flutter_01.log new file mode 100644 index 0000000..7b6fdc5 --- /dev/null +++ b/flutter_01.log @@ -0,0 +1,59 @@ +Flutter crash report. +Please report a bug at https://github.com/flutter/flutter/issues. + +## command + +flutter --no-color run --machine --device-id=5200de3d4dd02295 --profile --dart-define=flutter.inspector.structuredErrors=true lib/main.dart + +## exception + +String: failed to connect to http://127.0.0.1:45275/X0V7tlGyJXA=/ + +``` +``` + +## flutter doctor + +``` +[!] Flutter (Channel stable, 3.7.3, on Linux Mint 20.3 5.15.0-60-generic, locale de_DE.UTF-8) + • Flutter version 3.7.3 on channel stable at /opt/flutter + ! The flutter binary is not on your path. Consider adding /opt/flutter/bin to your path. + ! The dart binary is not on your path. Consider adding /opt/flutter/bin to your path. + • Upstream repository https://github.com/flutter/flutter.git + • Framework revision 9944297138 (vor 8 Tagen), 2023-02-08 15:46:04 -0800 + • Engine revision 248290d6d5 + • Dart version 2.19.2 + • DevTools version 2.20.1 + • If those were intentional, you can disregard the above warnings; however it is recommended to use "git" directly to perform update checks and upgrades. + +[✓] Android toolchain - develop for Android devices (Android SDK version 33.0.0) + • Android SDK at /home/elias/Android/Sdk + • Platform android-33, build-tools 33.0.0 + • Java binary at: /home/elias/.local/share/JetBrains/Toolbox/apps/AndroidStudio/ch-0/213.7172.25.2113.9123335/jre/bin/java + • Java version OpenJDK Runtime Environment (build 11.0.13+0-b1751.21-8125866) + • All Android licenses accepted. + +[✗] Chrome - develop for the web (Cannot find Chrome executable at google-chrome) + ! Cannot find Chrome. Try setting CHROME_EXECUTABLE to a Chrome executable. + +[✓] Linux toolchain - develop for Linux desktop + • clang version 10.0.0-4ubuntu1 + • cmake version 3.16.3 + • ninja version 1.10.0 + • pkg-config version 0.29.1 + +[✓] Android Studio (version 2021.3) + • Android Studio at /home/elias/.local/share/JetBrains/Toolbox/apps/AndroidStudio/ch-0/213.7172.25.2113.9123335 + • Flutter plugin version 71.0.3 + • Dart plugin version 213.7433 + • Java version OpenJDK Runtime Environment (build 11.0.13+0-b1751.21-8125866) + +[✓] Connected device (2 available) + • SM G800F (mobile) • 5200de3d4dd02295 • android-arm • Android 6.0.1 (API 23) + • Linux (desktop) • linux • linux-x64 • Linux Mint 20.3 5.15.0-60-generic + +[✓] HTTP Host Availability + • All required HTTP hosts are available + +! Doctor found issues in 2 categories. +``` diff --git a/lib/api/webuntis/apiParams.dart b/lib/api/apiParams.dart similarity index 100% rename from lib/api/webuntis/apiParams.dart rename to lib/api/apiParams.dart diff --git a/lib/api/apiRequest.dart b/lib/api/apiRequest.dart index c17ad0b..dec2cdb 100644 --- a/lib/api/apiRequest.dart +++ b/lib/api/apiRequest.dart @@ -4,36 +4,5 @@ import 'package:http/http.dart' as http; import 'package:marianum_mobile/api/apiError.dart'; class ApiRequest { - Uri endpoint; - ApiRequest(this.endpoint); - - Future get(Map? headers) async { - return await http.get(endpoint, headers: headers); - } - - Future post(String data, Map? headers) async { - log("Fetching: ${data}"); - try { - http.Response response = await http - .post(endpoint, body: data, headers: headers) - .timeout( - const Duration(seconds: 10), - onTimeout: () { - log("timeout!"); - throw ApiError("Network timeout"); - } - ); - - if(response.statusCode != 200) { - log("Got ${response.statusCode}"); - throw ApiError("Service response invalid, got status ${response.statusCode}"); - } - return response; - - } on Exception catch(e) { - throw ApiError("Http: ${e.toString()}"); - } - - } } \ No newline at end of file diff --git a/lib/api/webuntis/apiResponse.dart b/lib/api/apiResponse.dart similarity index 100% rename from lib/api/webuntis/apiResponse.dart rename to lib/api/apiResponse.dart diff --git a/lib/api/marianumcloud/talk/chat/getChat.dart b/lib/api/marianumcloud/talk/chat/getChat.dart new file mode 100644 index 0000000..5c23004 --- /dev/null +++ b/lib/api/marianumcloud/talk/chat/getChat.dart @@ -0,0 +1,26 @@ +import 'dart:convert'; + +import 'package:http/http.dart' as http; +import 'package:http/src/response.dart'; +import 'package:marianum_mobile/api/marianumcloud/talk/chat/getChatResponse.dart'; +import 'package:marianum_mobile/api/marianumcloud/talk/talkApi.dart'; + +import 'getChatParams.dart'; + +class GetChat extends TalkApi { + String chatToken; + + GetChatParams params; + GetChat(this.chatToken, this.params) : super("v1/chat/$chatToken", null, getParameters: params.toJson()); + + @override + assemble(String raw) { + return GetChatResponse.fromJson(jsonDecode(raw)['ocs']); // TODO move "ocs" to superclass + } + + @override + Future request(Uri uri, Object? body, Map? headers) { + return http.get(uri, headers: headers); + } + +} \ No newline at end of file diff --git a/lib/api/marianumcloud/talk/chat/getChatCache.dart b/lib/api/marianumcloud/talk/chat/getChatCache.dart new file mode 100644 index 0000000..a645faf --- /dev/null +++ b/lib/api/marianumcloud/talk/chat/getChatCache.dart @@ -0,0 +1,30 @@ +import 'dart:convert'; + +import 'package:marianum_mobile/api/marianumcloud/talk/chat/getChat.dart'; +import 'package:marianum_mobile/api/marianumcloud/talk/chat/getChatParams.dart'; +import 'package:marianum_mobile/api/marianumcloud/talk/chat/getChatResponse.dart'; +import 'package:marianum_mobile/api/requestCache.dart'; + +class GetChatCache extends RequestCache { + String chatToken; + + GetChatCache({required onUpdate, required this.chatToken}) : super(RequestCache.cacheNothing, onUpdate) { + start("MarianumMobile", "nc-chat-$chatToken"); + } + + @override + Future onLoad() { + return GetChat( + chatToken, + GetChatParams( + lookIntoFuture: GetChatParamsSwitch.off + ) + ).run(); + } + + @override + GetChatResponse onLocalData(String json) { + return GetChatResponse.fromJson(jsonDecode(json)); + } + +} \ No newline at end of file diff --git a/lib/api/marianumcloud/talk/chat/getChatParams.dart b/lib/api/marianumcloud/talk/chat/getChatParams.dart new file mode 100644 index 0000000..5644e18 --- /dev/null +++ b/lib/api/marianumcloud/talk/chat/getChatParams.dart @@ -0,0 +1,33 @@ +import 'package:json_annotation/json_annotation.dart'; +import 'package:marianum_mobile/api/apiParams.dart'; + +part 'getChatParams.g.dart'; + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class GetChatParams extends ApiParams { + GetChatParamsSwitch lookIntoFuture; + int? limit; + int? lastKnownMessageId; + int? lastCommonReadId; + int? timeout; + GetChatParamsSwitch? setReadMarker; + GetChatParamsSwitch? includeLastKnown; + + GetChatParams({ + required this.lookIntoFuture, + this.limit, + this.lastKnownMessageId, + this.lastCommonReadId, + this.timeout, + this.setReadMarker, + this.includeLastKnown + }); + + factory GetChatParams.fromJson(Map json) => _$GetChatParamsFromJson(json); + Map toJson() => _$GetChatParamsToJson(this); +} + +enum GetChatParamsSwitch { + @JsonValue(1) on, + @JsonValue(0) off, +} \ No newline at end of file diff --git a/lib/api/marianumcloud/talk/chat/getChatParams.g.dart b/lib/api/marianumcloud/talk/chat/getChatParams.g.dart new file mode 100644 index 0000000..743e219 --- /dev/null +++ b/lib/api/marianumcloud/talk/chat/getChatParams.g.dart @@ -0,0 +1,48 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'getChatParams.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +GetChatParams _$GetChatParamsFromJson(Map json) => + GetChatParams( + lookIntoFuture: + $enumDecode(_$GetChatParamsSwitchEnumMap, json['lookIntoFuture']), + limit: json['limit'] as int?, + lastKnownMessageId: json['lastKnownMessageId'] as int?, + lastCommonReadId: json['lastCommonReadId'] as int?, + timeout: json['timeout'] as int?, + setReadMarker: $enumDecodeNullable( + _$GetChatParamsSwitchEnumMap, json['setReadMarker']), + includeLastKnown: $enumDecodeNullable( + _$GetChatParamsSwitchEnumMap, json['includeLastKnown']), + ); + +Map _$GetChatParamsToJson(GetChatParams instance) { + final val = { + 'lookIntoFuture': _$GetChatParamsSwitchEnumMap[instance.lookIntoFuture]!, + }; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('limit', instance.limit); + writeNotNull('lastKnownMessageId', instance.lastKnownMessageId); + writeNotNull('lastCommonReadId', instance.lastCommonReadId); + writeNotNull('timeout', instance.timeout); + writeNotNull( + 'setReadMarker', _$GetChatParamsSwitchEnumMap[instance.setReadMarker]); + writeNotNull('includeLastKnown', + _$GetChatParamsSwitchEnumMap[instance.includeLastKnown]); + return val; +} + +const _$GetChatParamsSwitchEnumMap = { + GetChatParamsSwitch.on: 1, + GetChatParamsSwitch.off: 0, +}; diff --git a/lib/api/marianumcloud/talk/chat/getChatResponse.dart b/lib/api/marianumcloud/talk/chat/getChatResponse.dart new file mode 100644 index 0000000..6d9c3f4 --- /dev/null +++ b/lib/api/marianumcloud/talk/chat/getChatResponse.dart @@ -0,0 +1,53 @@ +import 'package:json_annotation/json_annotation.dart'; +import 'package:marianum_mobile/api/apiResponse.dart'; +import 'package:marianum_mobile/api/marianumcloud/talk/room/getRoomResponse.dart'; + +part 'getChatResponse.g.dart'; + +@JsonSerializable(explicitToJson: true) +class GetChatResponse extends ApiResponse { + Set data; + + GetChatResponse(this.data); + + factory GetChatResponse.fromJson(Map json) => _$GetChatResponseFromJson(json); + Map toJson() => _$GetChatResponseToJson(this); + + List sortByTimestamp() { + List sorted = data.toList(); + sorted.sort((a, b) => a.timestamp.compareTo(b.timestamp)); + return sorted; + } +} + +@JsonSerializable(explicitToJson: true) +class GetChatResponseObject { + int id; + String token; + GetRoomResponseObjectMessageActorType actorType; + String actorId; + String actorDisplayName; + int timestamp; + String systemMessage; + GetRoomResponseObjectMessageType messageType; + bool isReplyable; + String referenceId; + String message; + + GetChatResponseObject( + this.id, + this.token, + this.actorType, + this.actorId, + this.actorDisplayName, + this.timestamp, + this.systemMessage, + this.messageType, + this.isReplyable, + this.referenceId, + this.message); + + factory GetChatResponseObject.fromJson(Map json) => _$GetChatResponseObjectFromJson(json); + Map toJson() => _$GetChatResponseObjectToJson(this); +} + diff --git a/lib/api/marianumcloud/talk/chat/getChatResponse.g.dart b/lib/api/marianumcloud/talk/chat/getChatResponse.g.dart new file mode 100644 index 0000000..7e70ddc --- /dev/null +++ b/lib/api/marianumcloud/talk/chat/getChatResponse.g.dart @@ -0,0 +1,69 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'getChatResponse.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +GetChatResponse _$GetChatResponseFromJson(Map json) => + GetChatResponse( + (json['data'] as List) + .map((e) => GetChatResponseObject.fromJson(e as Map)) + .toSet(), + ); + +Map _$GetChatResponseToJson(GetChatResponse instance) => + { + 'data': instance.data.map((e) => e.toJson()).toList(), + }; + +GetChatResponseObject _$GetChatResponseObjectFromJson( + Map json) => + GetChatResponseObject( + json['id'] as int, + json['token'] as String, + $enumDecode( + _$GetRoomResponseObjectMessageActorTypeEnumMap, json['actorType']), + json['actorId'] as String, + json['actorDisplayName'] as String, + json['timestamp'] as int, + json['systemMessage'] as String, + $enumDecode( + _$GetRoomResponseObjectMessageTypeEnumMap, json['messageType']), + json['isReplyable'] as bool, + json['referenceId'] as String, + json['message'] as String, + ); + +Map _$GetChatResponseObjectToJson( + GetChatResponseObject instance) => + { + 'id': instance.id, + 'token': instance.token, + 'actorType': + _$GetRoomResponseObjectMessageActorTypeEnumMap[instance.actorType]!, + 'actorId': instance.actorId, + 'actorDisplayName': instance.actorDisplayName, + 'timestamp': instance.timestamp, + 'systemMessage': instance.systemMessage, + 'messageType': + _$GetRoomResponseObjectMessageTypeEnumMap[instance.messageType]!, + 'isReplyable': instance.isReplyable, + 'referenceId': instance.referenceId, + 'message': instance.message, + }; + +const _$GetRoomResponseObjectMessageActorTypeEnumMap = { + GetRoomResponseObjectMessageActorType.user: 'users', + GetRoomResponseObjectMessageActorType.guest: 'guests', + GetRoomResponseObjectMessageActorType.bot: 'bots', + GetRoomResponseObjectMessageActorType.bridge: 'bridged', +}; + +const _$GetRoomResponseObjectMessageTypeEnumMap = { + GetRoomResponseObjectMessageType.comment: 'comment', + GetRoomResponseObjectMessageType.deletedComment: 'comment_deleted', + GetRoomResponseObjectMessageType.system: 'system', + GetRoomResponseObjectMessageType.command: 'command', +}; diff --git a/lib/api/marianumcloud/talk/room/getRoom.dart b/lib/api/marianumcloud/talk/room/getRoom.dart new file mode 100644 index 0000000..f677c0d --- /dev/null +++ b/lib/api/marianumcloud/talk/room/getRoom.dart @@ -0,0 +1,32 @@ +import 'dart:convert'; +import 'dart:developer'; + +import 'package:http/http.dart' as http; +import 'package:marianum_mobile/api/marianumcloud/talk/room/getRoomResponse.dart'; +import 'package:marianum_mobile/api/marianumcloud/talk/talkApi.dart'; + +import 'getRoomParams.dart'; + + +class GetRoom extends TalkApi { + GetRoomParams params; + GetRoom(this.params) : super("v4/room", null, getParameters: params.toJson()); + + + + @override + GetRoomResponse assemble(String raw) { + log("ASSEMBLING"); + log(raw); + return GetRoomResponse.fromJson(jsonDecode(raw)['ocs']); + } + + @override + Future request(Uri uri, Object? body, Map? headers) { + log("REQUSTING..."); + log(uri.toString()); + log(headers.toString()); + return http.get(uri, headers: headers); + } + +} \ No newline at end of file diff --git a/lib/api/marianumcloud/talk/room/getRoomCache.dart b/lib/api/marianumcloud/talk/room/getRoomCache.dart new file mode 100644 index 0000000..0bcc387 --- /dev/null +++ b/lib/api/marianumcloud/talk/room/getRoomCache.dart @@ -0,0 +1,30 @@ +import 'dart:convert'; +import 'dart:developer'; + +import 'package:marianum_mobile/api/marianumcloud/talk/room/getRoomParams.dart'; +import 'package:marianum_mobile/api/marianumcloud/talk/room/getRoomResponse.dart'; +import 'package:marianum_mobile/api/requestCache.dart'; + +import 'getRoom.dart'; + +class GetRoomCache extends RequestCache { + GetRoomCache({onUpdate}) : super(RequestCache.cacheMinute, onUpdate) { + start("MarianumMobile", "nc-rooms"); + } + + @override + GetRoomResponse onLocalData(String json) { + log("LOCAL DATA FOUND"); + return GetRoomResponse.fromJson(jsonDecode(json)); + } + + @override + Future onLoad() { + log("FETCHING DATA"); + return GetRoom( + GetRoomParams( + includeStatus: true, + ) + ).run(); + } +} \ No newline at end of file diff --git a/lib/api/marianumcloud/talk/room/getRoomParams.dart b/lib/api/marianumcloud/talk/room/getRoomParams.dart new file mode 100644 index 0000000..cdf8e89 --- /dev/null +++ b/lib/api/marianumcloud/talk/room/getRoomParams.dart @@ -0,0 +1,26 @@ +import 'dart:convert'; + +import 'package:json_annotation/json_annotation.dart'; + +import '../../../apiParams.dart'; + +part 'getRoomParams.g.dart'; + +@JsonSerializable(explicitToJson: true) +class GetRoomParams extends ApiParams { + GetRoomParamsStatusUpdate? noStatusUpdate; + @JsonKey(toJson: _format) bool? includeStatus; + int? modifiedSince; + + GetRoomParams({this.noStatusUpdate, this.includeStatus, this.modifiedSince}); + + factory GetRoomParams.fromJson(Map json) => _$GetRoomParamsFromJson(json); + Map toJson() => _$GetRoomParamsToJson(this); + + static String _format(bool? v) => v.toString(); +} + +enum GetRoomParamsStatusUpdate { + @JsonValue(0) defaults, + @JsonValue(1) keepAlive, +} \ No newline at end of file diff --git a/lib/api/marianumcloud/talk/room/getRoomParams.g.dart b/lib/api/marianumcloud/talk/room/getRoomParams.g.dart new file mode 100644 index 0000000..616c591 --- /dev/null +++ b/lib/api/marianumcloud/talk/room/getRoomParams.g.dart @@ -0,0 +1,28 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'getRoomParams.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +GetRoomParams _$GetRoomParamsFromJson(Map json) => + GetRoomParams( + noStatusUpdate: $enumDecodeNullable( + _$GetRoomParamsStatusUpdateEnumMap, json['noStatusUpdate']), + includeStatus: json['includeStatus'] as bool?, + modifiedSince: json['modifiedSince'] as int?, + ); + +Map _$GetRoomParamsToJson(GetRoomParams instance) => + { + 'noStatusUpdate': + _$GetRoomParamsStatusUpdateEnumMap[instance.noStatusUpdate], + 'includeStatus': GetRoomParams._format(instance.includeStatus), + 'modifiedSince': instance.modifiedSince, + }; + +const _$GetRoomParamsStatusUpdateEnumMap = { + GetRoomParamsStatusUpdate.defaults: 0, + GetRoomParamsStatusUpdate.keepAlive: 1, +}; diff --git a/lib/api/marianumcloud/talk/room/getRoomResponse.dart b/lib/api/marianumcloud/talk/room/getRoomResponse.dart new file mode 100644 index 0000000..ee95fb9 --- /dev/null +++ b/lib/api/marianumcloud/talk/room/getRoomResponse.dart @@ -0,0 +1,151 @@ +import 'package:json_annotation/json_annotation.dart'; +import 'package:marianum_mobile/api/apiResponse.dart'; + +part 'getRoomResponse.g.dart'; + +@JsonSerializable(explicitToJson: true) +class GetRoomResponse extends ApiResponse { + Set data; + + GetRoomResponse(this.data); + + factory GetRoomResponse.fromJson(Map json) => _$GetRoomResponseFromJson(json); + Map toJson() => _$GetRoomResponseToJson(this); + + List sortByLastActivity() { + List sorted = data.toList(); + sorted.sort((a, b) => -a.lastActivity.compareTo(b.lastActivity)); + return sorted; + } + +} + +@JsonSerializable(explicitToJson: true) +class GetRoomResponseObject { + int id; + String token; + GetRoomResponseObjectConversationType type; + String name; + String displayName; + String description; + int participantType; + int participantFlags; + int readOnly; + int listable; + int lastPing; + String sessionId; + bool hasPassword; + bool hasCall; + int callFlag; + bool canStartCall; + bool canDeleteConversation; + bool canLeaveConversation; + int lastActivity; + bool isFavorite; + GetRoomResponseObjectParticipantNotificationLevel notificationLevel; + int unreadMessages; + bool unreadMention; + bool unreadMentionDirect; + int lastReadMessage; + int lastCommonReadMessage; + GetRoomResponseObjectMessage lastMessage; + String? status; + String? statusIcon; + String? statusMessage; + + GetRoomResponseObject( + this.id, + this.token, + this.type, + this.name, + this.displayName, + this.description, + this.participantType, + this.participantFlags, + this.readOnly, + this.listable, + this.lastPing, + this.sessionId, + this.hasPassword, + this.hasCall, + this.callFlag, + this.canStartCall, + this.canDeleteConversation, + this.canLeaveConversation, + this.lastActivity, + this.isFavorite, + this.notificationLevel, + this.unreadMessages, + this.unreadMention, + this.unreadMentionDirect, + this.lastReadMessage, + this.lastCommonReadMessage, + this.lastMessage, + this.status, + this.statusIcon, + this.statusMessage); + + factory GetRoomResponseObject.fromJson(Map json) => _$GetRoomResponseObjectFromJson(json); + Map toJson() => _$GetRoomResponseObjectToJson(this); +} + +enum GetRoomResponseObjectConversationType { + @JsonValue(1) oneToOne, + @JsonValue(2) group, + @JsonValue(3) public, + @JsonValue(4) changelog, + @JsonValue(5) deleted, +} + +enum GetRoomResponseObjectParticipantNotificationLevel { + @JsonValue(0) defaultLevel, + @JsonValue(1) alwaysNotify, + @JsonValue(2) notifyOnMention, + @JsonValue(3) neverNotify, +} + +@JsonSerializable(explicitToJson: true) +class GetRoomResponseObjectMessage { + int id; + String token; + GetRoomResponseObjectMessageActorType actorType; + String actorId; + String actorDisplayName; + int timestamp; + String message; + String systemMessage; + GetRoomResponseObjectMessageType messageType; + bool isReplyable; + String referenceId; + + + GetRoomResponseObjectMessage( + this.id, + this.token, + this.actorType, + this.actorId, + this.actorDisplayName, + this.timestamp, + this.message, + this.systemMessage, + this.messageType, + this.isReplyable, + this.referenceId); + + factory GetRoomResponseObjectMessage.fromJson(Map json) => _$GetRoomResponseObjectMessageFromJson(json); + Map toJson() => _$GetRoomResponseObjectMessageToJson(this); +} + +enum GetRoomResponseObjectMessageActorType { + @JsonValue("users") user, + @JsonValue("guests") guest, + @JsonValue("bots") bot, + @JsonValue("bridged") bridge, +} + +enum GetRoomResponseObjectMessageType { + @JsonValue("comment") comment, + @JsonValue("comment_deleted") deletedComment, + @JsonValue("system") system, + @JsonValue("command") command, +} \ No newline at end of file diff --git a/lib/api/marianumcloud/talk/room/getRoomResponse.g.dart b/lib/api/marianumcloud/talk/room/getRoomResponse.g.dart new file mode 100644 index 0000000..5400980 --- /dev/null +++ b/lib/api/marianumcloud/talk/room/getRoomResponse.g.dart @@ -0,0 +1,158 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'getRoomResponse.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +GetRoomResponse _$GetRoomResponseFromJson(Map json) => + GetRoomResponse( + (json['data'] as List) + .map((e) => GetRoomResponseObject.fromJson(e as Map)) + .toSet(), + ); + +Map _$GetRoomResponseToJson(GetRoomResponse instance) => + { + 'data': instance.data.map((e) => e.toJson()).toList(), + }; + +GetRoomResponseObject _$GetRoomResponseObjectFromJson( + Map json) => + GetRoomResponseObject( + json['id'] as int, + json['token'] as String, + $enumDecode(_$GetRoomResponseObjectConversationTypeEnumMap, json['type']), + json['name'] as String, + json['displayName'] as String, + json['description'] as String, + json['participantType'] as int, + json['participantFlags'] as int, + json['readOnly'] as int, + json['listable'] as int, + json['lastPing'] as int, + json['sessionId'] as String, + json['hasPassword'] as bool, + json['hasCall'] as bool, + json['callFlag'] as int, + json['canStartCall'] as bool, + json['canDeleteConversation'] as bool, + json['canLeaveConversation'] as bool, + json['lastActivity'] as int, + json['isFavorite'] as bool, + $enumDecode(_$GetRoomResponseObjectParticipantNotificationLevelEnumMap, + json['notificationLevel']), + json['unreadMessages'] as int, + json['unreadMention'] as bool, + json['unreadMentionDirect'] as bool, + json['lastReadMessage'] as int, + json['lastCommonReadMessage'] as int, + GetRoomResponseObjectMessage.fromJson( + json['lastMessage'] as Map), + json['status'] as String?, + json['statusIcon'] as String?, + json['statusMessage'] as String?, + ); + +Map _$GetRoomResponseObjectToJson( + GetRoomResponseObject instance) => + { + 'id': instance.id, + 'token': instance.token, + 'type': _$GetRoomResponseObjectConversationTypeEnumMap[instance.type]!, + 'name': instance.name, + 'displayName': instance.displayName, + 'description': instance.description, + 'participantType': instance.participantType, + 'participantFlags': instance.participantFlags, + 'readOnly': instance.readOnly, + 'listable': instance.listable, + 'lastPing': instance.lastPing, + 'sessionId': instance.sessionId, + 'hasPassword': instance.hasPassword, + 'hasCall': instance.hasCall, + 'callFlag': instance.callFlag, + 'canStartCall': instance.canStartCall, + 'canDeleteConversation': instance.canDeleteConversation, + 'canLeaveConversation': instance.canLeaveConversation, + 'lastActivity': instance.lastActivity, + 'isFavorite': instance.isFavorite, + 'notificationLevel': + _$GetRoomResponseObjectParticipantNotificationLevelEnumMap[ + instance.notificationLevel]!, + 'unreadMessages': instance.unreadMessages, + 'unreadMention': instance.unreadMention, + 'unreadMentionDirect': instance.unreadMentionDirect, + 'lastReadMessage': instance.lastReadMessage, + 'lastCommonReadMessage': instance.lastCommonReadMessage, + 'lastMessage': instance.lastMessage.toJson(), + 'status': instance.status, + 'statusIcon': instance.statusIcon, + 'statusMessage': instance.statusMessage, + }; + +const _$GetRoomResponseObjectConversationTypeEnumMap = { + GetRoomResponseObjectConversationType.oneToOne: 1, + GetRoomResponseObjectConversationType.group: 2, + GetRoomResponseObjectConversationType.public: 3, + GetRoomResponseObjectConversationType.changelog: 4, + GetRoomResponseObjectConversationType.deleted: 5, +}; + +const _$GetRoomResponseObjectParticipantNotificationLevelEnumMap = { + GetRoomResponseObjectParticipantNotificationLevel.defaultLevel: 0, + GetRoomResponseObjectParticipantNotificationLevel.alwaysNotify: 1, + GetRoomResponseObjectParticipantNotificationLevel.notifyOnMention: 2, + GetRoomResponseObjectParticipantNotificationLevel.neverNotify: 3, +}; + +GetRoomResponseObjectMessage _$GetRoomResponseObjectMessageFromJson( + Map json) => + GetRoomResponseObjectMessage( + json['id'] as int, + json['token'] as String, + $enumDecode( + _$GetRoomResponseObjectMessageActorTypeEnumMap, json['actorType']), + json['actorId'] as String, + json['actorDisplayName'] as String, + json['timestamp'] as int, + json['message'] as String, + json['systemMessage'] as String, + $enumDecode( + _$GetRoomResponseObjectMessageTypeEnumMap, json['messageType']), + json['isReplyable'] as bool, + json['referenceId'] as String, + ); + +Map _$GetRoomResponseObjectMessageToJson( + GetRoomResponseObjectMessage instance) => + { + 'id': instance.id, + 'token': instance.token, + 'actorType': + _$GetRoomResponseObjectMessageActorTypeEnumMap[instance.actorType]!, + 'actorId': instance.actorId, + 'actorDisplayName': instance.actorDisplayName, + 'timestamp': instance.timestamp, + 'message': instance.message, + 'systemMessage': instance.systemMessage, + 'messageType': + _$GetRoomResponseObjectMessageTypeEnumMap[instance.messageType]!, + 'isReplyable': instance.isReplyable, + 'referenceId': instance.referenceId, + }; + +const _$GetRoomResponseObjectMessageActorTypeEnumMap = { + GetRoomResponseObjectMessageActorType.user: 'users', + GetRoomResponseObjectMessageActorType.guest: 'guests', + GetRoomResponseObjectMessageActorType.bot: 'bots', + GetRoomResponseObjectMessageActorType.bridge: 'bridged', +}; + +const _$GetRoomResponseObjectMessageTypeEnumMap = { + GetRoomResponseObjectMessageType.comment: 'comment', + GetRoomResponseObjectMessageType.deletedComment: 'comment_deleted', + GetRoomResponseObjectMessageType.system: 'system', + GetRoomResponseObjectMessageType.command: 'command', +}; diff --git a/lib/api/marianumcloud/talk/talkApi.dart b/lib/api/marianumcloud/talk/talkApi.dart new file mode 100644 index 0000000..4c8afce --- /dev/null +++ b/lib/api/marianumcloud/talk/talkApi.dart @@ -0,0 +1,51 @@ +import 'dart:convert'; +import 'dart:developer'; + +import 'package:http/http.dart' as http; +import 'package:marianum_mobile/api/apiRequest.dart'; +import 'package:marianum_mobile/api/apiResponse.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +import '../../apiParams.dart'; + +enum TalkApiMethod { + get, + post, + put, + delete, +} + +abstract class TalkApi extends ApiRequest { + String path; + ApiParams? body; + Map? headers = {}; + Map? getParameters; + + http.Response? response; + + TalkApi(this.path, this.body, {this.headers, this.getParameters}); + + Future request(Uri uri, Object? body, Map? headers); + T assemble(String raw); + + Future run() async { + getParameters?.forEach((key, value) { + getParameters?.update(key, (value) => value.toString()); + }); + + SharedPreferences preferences = await SharedPreferences.getInstance(); + + Uri endpoint = Uri.https("${preferences.getString("username")!}:${preferences.getString("password")!}@cloud.marianum-fulda.de", "/ocs/v2.php/apps/spreed/api/$path", getParameters); + + headers ??= {}; + headers?.putIfAbsent("Accept", () => "application/json"); + headers?.putIfAbsent("OCS-APIRequest", () => "true"); + + http.Response data = await request(endpoint, body, headers); + dynamic jsonData = jsonDecode(data.body); + + + return assemble(data.body); + } + +} \ No newline at end of file diff --git a/lib/api/marianumcloud/talk/talkError.dart b/lib/api/marianumcloud/talk/talkError.dart new file mode 100644 index 0000000..f4ac002 --- /dev/null +++ b/lib/api/marianumcloud/talk/talkError.dart @@ -0,0 +1,12 @@ +class TalkError { + String status; + int code; + String message; + + TalkError(this.status, this.code, this.message); + + @override + String toString() { + return "Talk - $status - ($code): $message"; + } +} \ No newline at end of file diff --git a/lib/api/marianumcloud/webdav/webdavApi.dart b/lib/api/marianumcloud/webdav/webdavApi.dart new file mode 100644 index 0000000..3ab0855 --- /dev/null +++ b/lib/api/marianumcloud/webdav/webdavApi.dart @@ -0,0 +1,9 @@ +import 'package:marianum_mobile/api/apiRequest.dart'; + +class WebdavApi extends ApiRequest { + String basePath; + + WebdavApi(this.basePath); + + +} \ No newline at end of file diff --git a/lib/api/requestCache.dart b/lib/api/requestCache.dart index 5e716bb..9b9182e 100644 --- a/lib/api/requestCache.dart +++ b/lib/api/requestCache.dart @@ -3,6 +3,11 @@ import 'dart:convert'; import 'package:localstore/localstore.dart'; abstract class RequestCache { + static const int cacheNothing = 0; + static const int cacheMinute = 60; + static const int cacheHour = 60 * 60; + static const int cacheDay = 60 * 60 * 24; + int maxCacheTime; Function(T) onUpdate; @@ -29,4 +34,5 @@ abstract class RequestCache { T onLocalData(String json); Future onLoad(); + } \ No newline at end of file diff --git a/lib/api/talk/requestLoginTest.dart b/lib/api/talk/requestLoginTest.dart deleted file mode 100644 index 724e63c..0000000 --- a/lib/api/talk/requestLoginTest.dart +++ /dev/null @@ -1,6 +0,0 @@ -import 'package:marianum_mobile/api/apiRequest.dart'; - -class RequestLoginTest extends ApiRequest { - RequestLoginTest(super.endpoint); - -} \ No newline at end of file diff --git a/lib/api/webuntis/queries/authenticate/authenticateParams.dart b/lib/api/webuntis/queries/authenticate/authenticateParams.dart index 30ce8e7..cab8111 100644 --- a/lib/api/webuntis/queries/authenticate/authenticateParams.dart +++ b/lib/api/webuntis/queries/authenticate/authenticateParams.dart @@ -1,5 +1,5 @@ import 'package:json_annotation/json_annotation.dart'; -import 'package:marianum_mobile/api/webuntis/apiParams.dart'; +import 'package:marianum_mobile/api/apiParams.dart'; part 'authenticateParams.g.dart'; diff --git a/lib/api/webuntis/queries/authenticate/authenticateResponse.dart b/lib/api/webuntis/queries/authenticate/authenticateResponse.dart index 2ff4e0f..d0b7d56 100644 --- a/lib/api/webuntis/queries/authenticate/authenticateResponse.dart +++ b/lib/api/webuntis/queries/authenticate/authenticateResponse.dart @@ -1,5 +1,5 @@ import 'package:json_annotation/json_annotation.dart'; -import 'package:marianum_mobile/api/webuntis/apiResponse.dart'; +import 'package:marianum_mobile/api/apiResponse.dart'; part 'authenticateResponse.g.dart'; diff --git a/lib/api/webuntis/queries/getHolidays/getHolidays.dart b/lib/api/webuntis/queries/getHolidays/getHolidays.dart index 37dabc6..d900ec0 100644 --- a/lib/api/webuntis/queries/getHolidays/getHolidays.dart +++ b/lib/api/webuntis/queries/getHolidays/getHolidays.dart @@ -1,6 +1,8 @@ import 'dart:convert'; +import 'dart:developer'; -import 'package:marianum_mobile/api/webuntis/apiResponse.dart'; +import 'package:intl/intl.dart'; +import 'package:marianum_mobile/api/apiResponse.dart'; import 'package:marianum_mobile/api/webuntis/webuntisApi.dart'; import 'getHolidaysResponse.dart'; @@ -14,4 +16,17 @@ class GetHolidays extends WebuntisApi { return finalize(GetHolidaysResponse.fromJson(jsonDecode(rawAnswer))); } + static GetHolidaysResponseObject? find(GetHolidaysResponse holidaysResponse, {DateTime? time}) { + time ??= DateTime.now(); + time = DateTime(time.year, time.month, time.day, 0, 0, 0, 0, 0); + + for (var element in holidaysResponse.result) { + DateTime start = DateTime.parse(element.startDate.toString()); + DateTime end = DateTime.parse(element.endDate.toString()); + + if(!start.isAfter(time) && !end.isBefore(time)) return element; + } + return null; + } + } \ No newline at end of file diff --git a/lib/api/webuntis/queries/getHolidays/getHolidaysCache.dart b/lib/api/webuntis/queries/getHolidays/getHolidaysCache.dart index 88c20a1..2accf6b 100644 --- a/lib/api/webuntis/queries/getHolidays/getHolidaysCache.dart +++ b/lib/api/webuntis/queries/getHolidays/getHolidaysCache.dart @@ -5,8 +5,8 @@ import 'package:marianum_mobile/api/webuntis/queries/getHolidays/getHolidays.dar import 'package:marianum_mobile/api/webuntis/queries/getHolidays/getHolidaysResponse.dart'; class GetHolidaysCache extends RequestCache { - GetHolidaysCache({onUpdate}) : super(60 * 60, onUpdate) { - start("holidays", "data"); + GetHolidaysCache({onUpdate}) : super(RequestCache.cacheDay, onUpdate) { + start("MarianumMobile", "wu-holidays"); } @override diff --git a/lib/api/webuntis/queries/getHolidays/getHolidaysResponse.dart b/lib/api/webuntis/queries/getHolidays/getHolidaysResponse.dart index a991c8f..46790ec 100644 --- a/lib/api/webuntis/queries/getHolidays/getHolidaysResponse.dart +++ b/lib/api/webuntis/queries/getHolidays/getHolidaysResponse.dart @@ -1,5 +1,5 @@ import 'package:json_annotation/json_annotation.dart'; -import 'package:marianum_mobile/api/webuntis/apiResponse.dart'; +import 'package:marianum_mobile/api/apiResponse.dart'; part 'getHolidaysResponse.g.dart'; diff --git a/lib/api/webuntis/queries/getRooms/getRooms.dart b/lib/api/webuntis/queries/getRooms/getRooms.dart index 58b303c..03a7a87 100644 --- a/lib/api/webuntis/queries/getRooms/getRooms.dart +++ b/lib/api/webuntis/queries/getRooms/getRooms.dart @@ -1,6 +1,6 @@ import 'dart:convert'; -import 'package:marianum_mobile/api/webuntis/apiResponse.dart'; +import 'package:marianum_mobile/api/apiResponse.dart'; import 'package:marianum_mobile/api/webuntis/webuntisApi.dart'; import 'getRoomsResponse.dart'; diff --git a/lib/api/webuntis/queries/getRooms/getRoomsCache.dart b/lib/api/webuntis/queries/getRooms/getRoomsCache.dart index 1104637..6c0687e 100644 --- a/lib/api/webuntis/queries/getRooms/getRoomsCache.dart +++ b/lib/api/webuntis/queries/getRooms/getRoomsCache.dart @@ -6,8 +6,8 @@ import 'package:marianum_mobile/api/webuntis/queries/getRooms/getRoomsResponse.d import 'getRooms.dart'; class GetRoomsCache extends RequestCache { - GetRoomsCache({onUpdate}) : super(60 * 60, onUpdate) { - start("rooms", "data"); + GetRoomsCache({onUpdate}) : super(RequestCache.cacheHour, onUpdate) { + start("MarianumMobile", "wu-rooms"); } @override diff --git a/lib/api/webuntis/queries/getRooms/getRoomsResponse.dart b/lib/api/webuntis/queries/getRooms/getRoomsResponse.dart index 4f1cfe3..9c96cd0 100644 --- a/lib/api/webuntis/queries/getRooms/getRoomsResponse.dart +++ b/lib/api/webuntis/queries/getRooms/getRoomsResponse.dart @@ -1,5 +1,5 @@ import 'package:json_annotation/json_annotation.dart'; -import 'package:marianum_mobile/api/webuntis/apiResponse.dart'; +import 'package:marianum_mobile/api/apiResponse.dart'; part 'getRoomsResponse.g.dart'; @@ -21,7 +21,6 @@ class GetRoomsResponseObject { bool active; String building; - GetRoomsResponseObject(this.id, this.name, this.longName, this.active, this.building); factory GetRoomsResponseObject.fromJson(Map json) => _$GetRoomsResponseObjectFromJson(json); diff --git a/lib/api/webuntis/queries/getSubjects/getSubjects.dart b/lib/api/webuntis/queries/getSubjects/getSubjects.dart index 78a0a0f..127d28c 100644 --- a/lib/api/webuntis/queries/getSubjects/getSubjects.dart +++ b/lib/api/webuntis/queries/getSubjects/getSubjects.dart @@ -2,7 +2,7 @@ import 'dart:convert'; import 'package:marianum_mobile/api/webuntis/webuntisApi.dart'; -import '../../apiResponse.dart'; +import '../../../apiResponse.dart'; import 'getSubjectsResponse.dart'; class GetSubjects extends WebuntisApi { diff --git a/lib/api/webuntis/queries/getSubjects/getSubjectsCache.dart b/lib/api/webuntis/queries/getSubjects/getSubjectsCache.dart index 8c4a6ba..3c2d9d6 100644 --- a/lib/api/webuntis/queries/getSubjects/getSubjectsCache.dart +++ b/lib/api/webuntis/queries/getSubjects/getSubjectsCache.dart @@ -6,8 +6,8 @@ import 'package:marianum_mobile/api/webuntis/queries/getSubjects/getSubjectsResp import 'getSubjects.dart'; class GetSubjectsCache extends RequestCache { - GetSubjectsCache({onUpdate}) : super(60 * 60, onUpdate) { - start("subjects", "data"); + GetSubjectsCache({onUpdate}) : super(RequestCache.cacheHour, onUpdate) { + start("MarianumMobile", "wu-subjects"); } @override diff --git a/lib/api/webuntis/queries/getSubjects/getSubjectsResponse.dart b/lib/api/webuntis/queries/getSubjects/getSubjectsResponse.dart index 897e2ae..4fd6aad 100644 --- a/lib/api/webuntis/queries/getSubjects/getSubjectsResponse.dart +++ b/lib/api/webuntis/queries/getSubjects/getSubjectsResponse.dart @@ -1,5 +1,5 @@ import 'package:json_annotation/json_annotation.dart'; -import 'package:marianum_mobile/api/webuntis/apiResponse.dart'; +import 'package:marianum_mobile/api/apiResponse.dart'; part 'getSubjectsResponse.g.dart'; diff --git a/lib/api/webuntis/queries/getTimetable/getTimetable.dart b/lib/api/webuntis/queries/getTimetable/getTimetable.dart index 6d75057..78d1de4 100644 --- a/lib/api/webuntis/queries/getTimetable/getTimetable.dart +++ b/lib/api/webuntis/queries/getTimetable/getTimetable.dart @@ -1,7 +1,7 @@ import 'dart:convert'; import 'dart:developer'; -import 'package:marianum_mobile/api/webuntis/apiResponse.dart'; +import 'package:marianum_mobile/api/apiResponse.dart'; import 'package:marianum_mobile/api/webuntis/webuntisApi.dart'; import 'getTimetableParams.dart'; diff --git a/lib/api/webuntis/queries/getTimetable/getTimetableCache.dart b/lib/api/webuntis/queries/getTimetable/getTimetableCache.dart index 48290ca..9302fed 100644 --- a/lib/api/webuntis/queries/getTimetable/getTimetableCache.dart +++ b/lib/api/webuntis/queries/getTimetable/getTimetableCache.dart @@ -12,8 +12,8 @@ import 'getTimetableResponse.dart'; class GetTimetableCache extends RequestCache { int day; - GetTimetableCache({required onUpdate, required this.day}) : super(30, onUpdate) { - start("timetable", "$day"); + GetTimetableCache({required onUpdate, required this.day}) : super(RequestCache.cacheMinute, onUpdate) { + start("MarianumMobile", "wu-timetable-$day"); } @override @@ -25,14 +25,18 @@ class GetTimetableCache extends RequestCache { Future onLoad() async { return GetTimetable( GetTimetableParams( - options: GetTimetableParamsOptions( - element: GetTimetableParamsOptionsElement( - id: (await Authenticate.getSession()).personId, - type: 5, - keyType: GetTimetableParamsOptionsElementKeyType.id, - ), - startDate: day, - endDate: day, + options: GetTimetableParamsOptions( + element: GetTimetableParamsOptionsElement( + id: (await Authenticate.getSession()).personId, + type: 5, + keyType: GetTimetableParamsOptionsElementKeyType.id, + ), + startDate: day, + endDate: day, + teacherFields: GetTimetableParamsOptionsFields.all, + subjectFields: GetTimetableParamsOptionsFields.all, + roomFields: GetTimetableParamsOptionsFields.all, + klasseFields: GetTimetableParamsOptionsFields.all, ) ) ).run(); diff --git a/lib/api/webuntis/queries/getTimetable/getTimetableParams.dart b/lib/api/webuntis/queries/getTimetable/getTimetableParams.dart index 3a5ea76..efb3726 100644 --- a/lib/api/webuntis/queries/getTimetable/getTimetableParams.dart +++ b/lib/api/webuntis/queries/getTimetable/getTimetableParams.dart @@ -1,5 +1,5 @@ import 'package:json_annotation/json_annotation.dart'; -import 'package:marianum_mobile/api/webuntis/apiParams.dart'; +import 'package:marianum_mobile/api/apiParams.dart'; part 'getTimetableParams.g.dart'; @@ -36,13 +36,13 @@ class GetTimetableParamsOptions { @JsonKey(includeIfNull: false) bool? showStudentgroup; @JsonKey(includeIfNull: false) - GetTimetableParamsOptionsFields? klasseFields; + List? klasseFields; @JsonKey(includeIfNull: false) - GetTimetableParamsOptionsFields? roomFields; + List? roomFields; @JsonKey(includeIfNull: false) - GetTimetableParamsOptionsFields? subjectFields; + List? subjectFields; @JsonKey(includeIfNull: false) - GetTimetableParamsOptionsFields? teacherFields; + List? teacherFields; GetTimetableParamsOptions({ required this.element, @@ -69,7 +69,9 @@ enum GetTimetableParamsOptionsFields { @JsonValue("id") id, @JsonValue("name") name, @JsonValue("longname") longname, - @JsonValue("externalkey") externalkey, + @JsonValue("externalkey") externalkey; + + static List all = [id, name, longname, externalkey]; } @JsonSerializable() diff --git a/lib/api/webuntis/queries/getTimetable/getTimetableParams.g.dart b/lib/api/webuntis/queries/getTimetable/getTimetableParams.g.dart index 803512c..c5b6d92 100644 --- a/lib/api/webuntis/queries/getTimetable/getTimetableParams.g.dart +++ b/lib/api/webuntis/queries/getTimetable/getTimetableParams.g.dart @@ -31,14 +31,18 @@ GetTimetableParamsOptions _$GetTimetableParamsOptionsFromJson( showLsText: json['showLsText'] as bool?, showLsNumber: json['showLsNumber'] as bool?, showStudentgroup: json['showStudentgroup'] as bool?, - klasseFields: $enumDecodeNullable( - _$GetTimetableParamsOptionsFieldsEnumMap, json['klasseFields']), - roomFields: $enumDecodeNullable( - _$GetTimetableParamsOptionsFieldsEnumMap, json['roomFields']), - subjectFields: $enumDecodeNullable( - _$GetTimetableParamsOptionsFieldsEnumMap, json['subjectFields']), - teacherFields: $enumDecodeNullable( - _$GetTimetableParamsOptionsFieldsEnumMap, json['teacherFields']), + klasseFields: (json['klasseFields'] as List?) + ?.map((e) => $enumDecode(_$GetTimetableParamsOptionsFieldsEnumMap, e)) + .toList(), + roomFields: (json['roomFields'] as List?) + ?.map((e) => $enumDecode(_$GetTimetableParamsOptionsFieldsEnumMap, e)) + .toList(), + subjectFields: (json['subjectFields'] as List?) + ?.map((e) => $enumDecode(_$GetTimetableParamsOptionsFieldsEnumMap, e)) + .toList(), + teacherFields: (json['teacherFields'] as List?) + ?.map((e) => $enumDecode(_$GetTimetableParamsOptionsFieldsEnumMap, e)) + .toList(), ); Map _$GetTimetableParamsOptionsToJson( @@ -62,14 +66,26 @@ Map _$GetTimetableParamsOptionsToJson( writeNotNull('showLsText', instance.showLsText); writeNotNull('showLsNumber', instance.showLsNumber); writeNotNull('showStudentgroup', instance.showStudentgroup); - writeNotNull('klasseFields', - _$GetTimetableParamsOptionsFieldsEnumMap[instance.klasseFields]); - writeNotNull('roomFields', - _$GetTimetableParamsOptionsFieldsEnumMap[instance.roomFields]); - writeNotNull('subjectFields', - _$GetTimetableParamsOptionsFieldsEnumMap[instance.subjectFields]); - writeNotNull('teacherFields', - _$GetTimetableParamsOptionsFieldsEnumMap[instance.teacherFields]); + writeNotNull( + 'klasseFields', + instance.klasseFields + ?.map((e) => _$GetTimetableParamsOptionsFieldsEnumMap[e]!) + .toList()); + writeNotNull( + 'roomFields', + instance.roomFields + ?.map((e) => _$GetTimetableParamsOptionsFieldsEnumMap[e]!) + .toList()); + writeNotNull( + 'subjectFields', + instance.subjectFields + ?.map((e) => _$GetTimetableParamsOptionsFieldsEnumMap[e]!) + .toList()); + writeNotNull( + 'teacherFields', + instance.teacherFields + ?.map((e) => _$GetTimetableParamsOptionsFieldsEnumMap[e]!) + .toList()); return val; } diff --git a/lib/api/webuntis/queries/getTimetable/getTimetableResponse.dart b/lib/api/webuntis/queries/getTimetable/getTimetableResponse.dart index 547af3c..b32c1b9 100644 --- a/lib/api/webuntis/queries/getTimetable/getTimetableResponse.dart +++ b/lib/api/webuntis/queries/getTimetable/getTimetableResponse.dart @@ -1,5 +1,5 @@ import 'package:json_annotation/json_annotation.dart'; -import 'package:marianum_mobile/api/webuntis/apiResponse.dart'; +import 'package:marianum_mobile/api/apiResponse.dart'; part 'getTimetableResponse.g.dart'; diff --git a/lib/api/webuntis/webuntisApi.dart b/lib/api/webuntis/webuntisApi.dart index 1df6327..c5e435c 100644 --- a/lib/api/webuntis/webuntisApi.dart +++ b/lib/api/webuntis/webuntisApi.dart @@ -3,19 +3,20 @@ import 'dart:developer'; import 'package:marianum_mobile/api/apiRequest.dart'; import 'package:http/http.dart' as http; import 'package:marianum_mobile/api/webuntis/webuntisError.dart'; -import 'package:marianum_mobile/api/webuntis/apiResponse.dart'; +import 'package:marianum_mobile/api/apiResponse.dart'; -import 'apiParams.dart'; +import '../apiParams.dart'; import 'queries/authenticate/authenticate.dart'; abstract class WebuntisApi extends ApiRequest { + Uri endpoint = Uri.parse("https://peleus.webuntis.com/WebUntis/jsonrpc.do?school=marianum-fulda"); String method; ApiParams? genericParam; http.Response? response; bool authenticatedResponse; - WebuntisApi(this.method, this.genericParam, {this.authenticatedResponse = true}) : super(Uri.parse("https://peleus.webuntis.com/WebUntis/jsonrpc.do?school=marianum-fulda")); + WebuntisApi(this.method, this.genericParam, {this.authenticatedResponse = true}); Future query(WebuntisApi untis) async { @@ -50,4 +51,14 @@ abstract class WebuntisApi extends ApiRequest { String _body() { return genericParam == null ? "{}" : jsonEncode(genericParam); } + + Future post(String data, Map? headers) async { + log("POST: $endpoint\n$data"); + return await http + .post(endpoint, body: data, headers: headers) + .timeout( + const Duration(seconds: 10), + onTimeout: () => throw WebuntisError("Timeout", 1) + ); + } } \ No newline at end of file diff --git a/lib/api/webuntis/webuntisError.dart b/lib/api/webuntis/webuntisError.dart index edd1843..8963e19 100644 --- a/lib/api/webuntis/webuntisError.dart +++ b/lib/api/webuntis/webuntisError.dart @@ -4,6 +4,7 @@ class WebuntisError { WebuntisError(this.message, this.code); + @override String toString() { return "WebUntis ($code): $message"; } diff --git a/lib/app.dart b/lib/app.dart index b4133b2..14b0362 100644 --- a/lib/app.dart +++ b/lib/app.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; -import 'package:marianum_mobile/screen/pages/timetable/storedTimetable.dart'; +import 'package:marianum_mobile/screen/pages/timetable/timetable.dart'; import 'package:marianum_mobile/screen/pages/timetable/testTimetable.dart'; import 'package:provider/provider.dart'; import 'package:shared_preferences/shared_preferences.dart'; @@ -8,8 +8,8 @@ import 'package:shared_preferences/shared_preferences.dart'; import 'dataOld/incommingPackets/talkNotificationsPacket.dart'; import 'screen/pages/files/files.dart'; import 'screen/pages/more/overhang.dart'; -import 'screen/pages/talk/chatOverview.dart'; -import 'screen/pages/timetable/timetable.dart'; +import 'screen/pages/talk/chatList.dart'; +import 'screen/pages/timetable/timetableOld.dart'; import 'screen/settings/settings.dart'; class App extends StatefulWidget { @@ -26,6 +26,7 @@ class _AppState extends State { Widget build(BuildContext context) { final PageController pageController = PageController(); return Scaffold( + resizeToAvoidBottomInset: false, appBar: AppBar( title: const Text("Marianum Fulda"), actions: [ @@ -38,27 +39,32 @@ class _AppState extends State { ) ], ), - body: Stack( + body: Column( children: [ - PageView( - controller: pageController, - children: const [ - StoredTimetable(), - Talk(), - Files(), - Overhang(), - ], - onPageChanged: (page) { - setState(() { - currentPage = page; - }); - }, + Visibility( + visible: false, + child: LinearProgressIndicator( + backgroundColor: Colors.transparent, + valueColor: AlwaysStoppedAnimation(Theme.of(context).primaryColor), + minHeight: 5, + ), ), - // LinearProgressIndicator( - // backgroundColor: Colors.transparent, - // valueColor: AlwaysStoppedAnimation(Theme.of(context).primaryColor), - // minHeight: 5, - // ), + Flexible( + child: PageView( + controller: pageController, + children: [ + Timetable(), + ChatList(), + Files(), + Overhang(), + ], + onPageChanged: (page) { + setState(() { + currentPage = page; + }); + }, + ), + ) ], ), diff --git a/lib/data/chatList/chatListProps.dart b/lib/data/chatList/chatListProps.dart new file mode 100644 index 0000000..e0dca12 --- /dev/null +++ b/lib/data/chatList/chatListProps.dart @@ -0,0 +1,29 @@ +import 'dart:developer'; + +import 'package:marianum_mobile/api/marianumcloud/talk/room/getRoomCache.dart'; +import 'package:marianum_mobile/api/marianumcloud/talk/room/getRoomResponse.dart'; +import 'package:marianum_mobile/data/dataHolder.dart'; + +import '../../api/apiResponse.dart'; + +class ChatListProps extends DataHolder { + GetRoomResponse? _getRoomResponse; + GetRoomResponse get getRoomsResponse => _getRoomResponse!; + + @override + List properties() { + return [_getRoomResponse]; + } + + @override + void run() { + log("RUN CACHE"); + GetRoomCache( + onUpdate: (GetRoomResponse data) => { + _getRoomResponse = data, + notifyListeners(), + } + ); + } + +} \ No newline at end of file diff --git a/lib/data/chatList/chatProps.dart b/lib/data/chatList/chatProps.dart new file mode 100644 index 0000000..8a8f8c9 --- /dev/null +++ b/lib/data/chatList/chatProps.dart @@ -0,0 +1,35 @@ +import 'package:marianum_mobile/api/apiResponse.dart'; +import 'package:marianum_mobile/api/marianumcloud/talk/chat/getChatCache.dart'; +import 'package:marianum_mobile/api/marianumcloud/talk/chat/getChatResponse.dart'; +import 'package:marianum_mobile/data/dataHolder.dart'; + +class ChatProps extends DataHolder { + String _queryToken = ""; + + GetChatResponse? _getChatResponse; + GetChatResponse get getChatResponse => _getChatResponse!; + + @override + List properties() { + return [_getChatResponse]; + } + + @override + void run() { + notifyListeners(); + GetChatCache( + chatToken: _queryToken, + onUpdate: (GetChatResponse data) => { + _getChatResponse = data, + notifyListeners(), + } + ); + } + + void setQueryToken(String token) { + _queryToken = token; + _getChatResponse = null; + run(); + } + +} \ No newline at end of file diff --git a/lib/data/dataHolder.dart b/lib/data/dataHolder.dart index 08e140e..2c431bd 100644 --- a/lib/data/dataHolder.dart +++ b/lib/data/dataHolder.dart @@ -1,11 +1,18 @@ import 'package:flutter/cupertino.dart'; import 'package:localstore/localstore.dart'; +import '../api/apiResponse.dart'; + abstract class DataHolder extends ChangeNotifier { CollectionRef storage(String path) { return Localstore.instance.collection(path); } - Future run(); + void run(); + List properties(); + + bool primaryLoading() { + return properties().where((element) => element != null).isEmpty; + } } \ No newline at end of file diff --git a/lib/data/timetable/persistantTimetable.dart b/lib/data/timetable/persistantTimetable.dart deleted file mode 100644 index 27af2c7..0000000 --- a/lib/data/timetable/persistantTimetable.dart +++ /dev/null @@ -1,12 +0,0 @@ -import 'dart:async'; - -import 'package:flutter/widgets.dart'; -import 'package:path/path.dart'; -import 'package:sqflite/sqflite.dart'; - -class PersistantTimetable { - final int id; - final String json; - - PersistantTimetable(this.id, this.json); -} \ No newline at end of file diff --git a/lib/data/timetable/timetable.dart b/lib/data/timetable/timetable.dart deleted file mode 100644 index 082a631..0000000 --- a/lib/data/timetable/timetable.dart +++ /dev/null @@ -1,60 +0,0 @@ -import 'dart:collection'; -import 'dart:convert'; -import 'dart:developer'; - -import 'package:intl/intl.dart'; -import 'package:marianum_mobile/api/webuntis/queries/authenticate/authenticate.dart'; -import 'package:marianum_mobile/api/webuntis/queries/authenticate/authenticateResponse.dart'; -import 'package:marianum_mobile/api/webuntis/queries/getRooms/getRoomsCache.dart'; -import 'package:marianum_mobile/api/webuntis/queries/getRooms/getRoomsResponse.dart'; -import 'package:marianum_mobile/api/webuntis/queries/getSubjects/getSubjectsCache.dart'; -import 'package:marianum_mobile/api/webuntis/queries/getSubjects/getSubjectsResponse.dart'; -import 'package:marianum_mobile/data/dataHolder.dart'; - -import '../../api/webuntis/queries/getTimetable/getTimetable.dart'; -import '../../api/webuntis/queries/getTimetable/getTimetableCache.dart'; -import '../../api/webuntis/queries/getTimetable/getTimetableParams.dart'; -import '../../api/webuntis/queries/getTimetable/getTimetableResponse.dart'; - -class Timetable extends DataHolder { - int day = int.parse(DateFormat("yyyyMMdd").format(DateTime.now())); - - Timetable() : super(); - - GetTimetableResponse? _getTimetableResponse; - GetTimetableResponse? get getTimetableResponse => _getTimetableResponse; - - GetRoomsResponse? _getRoomsResponse; - GetRoomsResponse? get getRoomsResponse => _getRoomsResponse; - - GetSubjectsResponse? _getSubjectsResponse; - GetSubjectsResponse? get getSubjectsResponse => _getSubjectsResponse; - - @override - Future run() async { - GetTimetableCache( - day: day, - onUpdate: (data) => - { - _getTimetableResponse = data, - notifyListeners(), - } - ); - - GetRoomsCache( - onUpdate: (data) => - { - _getRoomsResponse = data, - notifyListeners(), - } - ); - - GetSubjectsCache( - onUpdate: (data) => - { - _getSubjectsResponse = data, - notifyListeners(), - } - ); - } -} \ No newline at end of file diff --git a/lib/data/timetable/timetableProps.dart b/lib/data/timetable/timetableProps.dart new file mode 100644 index 0000000..d18bff7 --- /dev/null +++ b/lib/data/timetable/timetableProps.dart @@ -0,0 +1,101 @@ +import 'dart:collection'; +import 'dart:convert'; +import 'dart:developer'; + +import 'package:intl/intl.dart'; +import 'package:marianum_mobile/api/apiResponse.dart'; +import 'package:marianum_mobile/api/webuntis/queries/authenticate/authenticate.dart'; +import 'package:marianum_mobile/api/webuntis/queries/authenticate/authenticateResponse.dart'; +import 'package:marianum_mobile/api/webuntis/queries/getHolidays/getHolidaysCache.dart'; +import 'package:marianum_mobile/api/webuntis/queries/getHolidays/getHolidaysResponse.dart'; +import 'package:marianum_mobile/api/webuntis/queries/getRooms/getRoomsCache.dart'; +import 'package:marianum_mobile/api/webuntis/queries/getRooms/getRoomsResponse.dart'; +import 'package:marianum_mobile/api/webuntis/queries/getSubjects/getSubjectsCache.dart'; +import 'package:marianum_mobile/api/webuntis/queries/getSubjects/getSubjectsResponse.dart'; +import 'package:marianum_mobile/data/dataHolder.dart'; + +import '../../api/webuntis/queries/getTimetable/getTimetable.dart'; +import '../../api/webuntis/queries/getTimetable/getTimetableCache.dart'; +import '../../api/webuntis/queries/getTimetable/getTimetableParams.dart'; +import '../../api/webuntis/queries/getTimetable/getTimetableResponse.dart'; + +class TimetableProps extends DataHolder { + late DateTime queryDate; + + GetTimetableResponse? _getTimetableResponse; + GetTimetableResponse get getTimetableResponse => _getTimetableResponse!; + + GetRoomsResponse? _getRoomsResponse; + GetRoomsResponse get getRoomsResponse => _getRoomsResponse!; + + GetSubjectsResponse? _getSubjectsResponse; + GetSubjectsResponse get getSubjectsResponse => _getSubjectsResponse!; + + GetHolidaysResponse? _getHolidaysResponse; + GetHolidaysResponse get getHolidaysResponse => _getHolidaysResponse!; + + + TimetableProps() { + nearest(); + } + + @override + List properties() { + return [_getTimetableResponse, _getRoomsResponse, _getSubjectsResponse, _getHolidaysResponse]; + } + + @override + void run() { + GetTimetableCache( + day: int.parse(DateFormat("yyyyMMdd").format(queryDate)), + onUpdate: (GetTimetableResponse data) => { + _getTimetableResponse = data, + notifyListeners(), + } + ); + + GetRoomsCache( + onUpdate: (GetRoomsResponse data) => { + _getRoomsResponse = data, + notifyListeners(), + } + ); + + GetSubjectsCache( + onUpdate: (GetSubjectsResponse data) => { + _getSubjectsResponse = data, + notifyListeners(), + } + ); + + GetHolidaysCache( + onUpdate: (GetHolidaysResponse data) => { + _getHolidaysResponse = data, + notifyListeners(), + } + ); + } + + void nearest() { + queryDate = DateTime.now(); + while(isWeekend(queryDate)) { + next(); + } + run(); + } + + void next({previous = false}) { + do { + if(previous) { + queryDate = queryDate.subtract(const Duration(days: 1)); + } else { + queryDate = queryDate.add(const Duration(days: 1)); + } + } while(isWeekend(queryDate)); + run(); + } + + bool isWeekend(DateTime queryDate) { + return queryDate.weekday == DateTime.saturday || queryDate.weekday == DateTime.sunday; + } +} \ No newline at end of file diff --git a/lib/main.dart b/lib/main.dart index 7497568..106a734 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -2,7 +2,7 @@ import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:marianum_mobile/data/timetable/timetable.dart'; +import 'package:marianum_mobile/data/timetable/timetableProps.dart'; import 'package:marianum_mobile/screen/login/login.dart'; import 'package:marianum_mobile/widget/loadingSpinner.dart'; import 'package:provider/provider.dart'; @@ -10,6 +10,8 @@ import 'package:shared_preferences/shared_preferences.dart'; import 'package:web_socket_channel/web_socket_channel.dart'; import 'app.dart'; +import 'data/chatList/chatListProps.dart'; +import 'data/chatList/chatProps.dart'; import 'dataOld/accountModel.dart'; import 'dataOld/incommingPackets/authenticatePacket.dart'; import 'dataOld/incommingPackets/errorPacket.dart'; @@ -39,7 +41,9 @@ Future main() async { // ChangeNotifierProvider(create: (context) => FileListPacket()), // ChangeNotifierProvider(create: (context) => TalkChatPacket()), // ChangeNotifierProvider(create: (context) => TimetablePacket()), - ChangeNotifierProvider(create: (context) => Timetable()), + ChangeNotifierProvider(create: (context) => TimetableProps()), + ChangeNotifierProvider(create: (context) => ChatListProps()), + ChangeNotifierProvider(create: (context) => ChatProps()), ], child: const Main(), ) @@ -54,7 +58,7 @@ class Main extends StatefulWidget { } class _MainState extends State
{ - static const Color red = Color.fromARGB(255, 153, 51, 51); + static const Color marianumRed = Color.fromARGB(255, 153, 51, 51); final Future _storage = SharedPreferences.getInstance(); @@ -79,12 +83,16 @@ class _MainState extends State
{ title: 'Marianum Fulda', theme: ThemeData( brightness: Brightness.light, - primaryColor: red, + primaryColor: marianumRed, + hintColor: marianumRed, + inputDecorationTheme: const InputDecorationTheme( + border: UnderlineInputBorder(borderSide: BorderSide(color: marianumRed)), + ), appBarTheme: const AppBarTheme( - backgroundColor: red, + backgroundColor: marianumRed, ), progressIndicatorTheme: const ProgressIndicatorThemeData( - color: red, + color: marianumRed, ), ), @@ -93,30 +101,16 @@ class _MainState extends State
{ builder: (BuildContext context, AsyncSnapshot snapshot) { if(snapshot.hasData) { - - return Consumer2( - builder: (context, accountModel, errorPacket, child) { - if(errorPacket.errorDismissed) { - return accountModel.isLoggedIn ? const App() : const Login(); - } else { - return AlertDialog(title: const Text("Serverseitige Fehlermeldung"), content: Text(errorPacket.errorText), actions: [ - TextButton(onPressed: () { - Provider.of(context, listen: false).errorDismissed = true; - }, child: const Text("Weiter")) - ]); - } + return Consumer( + builder: (context, accountModel, child) { + return accountModel.isLoggedIn ? const App() : const Login(); }, ); - } else { - return const LoadingSpinner(); - } - }, ) - ); } } diff --git a/lib/screen/pages/talk/chatList.dart b/lib/screen/pages/talk/chatList.dart new file mode 100644 index 0000000..06446c8 --- /dev/null +++ b/lib/screen/pages/talk/chatList.dart @@ -0,0 +1,101 @@ +import 'dart:convert'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:http/http.dart' as http; +import 'package:jiffy/jiffy.dart'; +import 'package:marianum_mobile/api/marianumcloud/talk/room/getRoomResponse.dart'; +import 'package:marianum_mobile/data/chatList/chatListProps.dart'; +import 'package:marianum_mobile/widget/loadingPacket.dart'; +import 'package:provider/provider.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +import 'chatView.dart'; + +class ChatList extends StatefulWidget { + const ChatList({Key? key}) : super(key: key); + + @override + State createState() => _ChatListState(); +} + +class _ChatListState extends State { + late String username; + + @override + void initState() { + super.initState(); + + SharedPreferences.getInstance().then((value) => { + username = value.getString("username")! + }); + + WidgetsBinding.instance.addPostFrameCallback((timeStamp) { + Provider.of(context, listen: false).run(); + }); + } + + @override + Widget build(BuildContext context) { + + return Consumer( + builder: (context, data, child) { + + if(data.primaryLoading()) { + return const Center(child: CircularProgressIndicator()); + } + + List chats = List.empty(growable: true); + + for (var chatRoom in data.getRoomsResponse.sortByLastActivity()) { + + CircleAvatar _circleAvatar = CircleAvatar( + foregroundImage: chatRoom.type == GetRoomResponseObjectConversationType.oneToOne ? Image.network("https://cloud.marianum-fulda.de/avatar/${chatRoom.name}/128").image : null, + backgroundColor: Theme.of(context).primaryColor, + foregroundColor: Colors.white, + child: chatRoom.type == GetRoomResponseObjectConversationType.group ? const Icon(Icons.group) : const Icon(Icons.person), + ); + + chats.add(ListTile( + title: Text(chatRoom.displayName), + subtitle: Text("${Jiffy.unixFromSecondsSinceEpoch(chatRoom.lastMessage.timestamp).fromNow()}: ${chatRoom.lastMessage.message.replaceAll("\n", " ")}", overflow: TextOverflow.ellipsis), + trailing: Visibility( + visible: chatRoom.unreadMessages > 0, + child: Container( + padding: const EdgeInsets.all(1), + decoration: BoxDecoration( + color: Theme.of(context).primaryColor, + borderRadius: BorderRadius.circular(30), + ), + constraints: const BoxConstraints( + minWidth: 20, + minHeight: 20, + ), + child: Text( + "${chatRoom.unreadMessages}", + style: const TextStyle( + color: Colors.white, + fontSize: 15, + ), + textAlign: TextAlign.center, + ), + ), + ), + leading: _circleAvatar, + onTap: () async { + Navigator.of(context).push(MaterialPageRoute(builder: (context) { + return ChatView( + user: chatRoom, + selfId: username, + avatar: _circleAvatar, + ); + })); + }, + )); + } + + return ListView(children: chats); + }, + ); + } +} \ No newline at end of file diff --git a/lib/screen/pages/talk/chatOverview.dart b/lib/screen/pages/talk/chatOverview.dart deleted file mode 100644 index be89182..0000000 --- a/lib/screen/pages/talk/chatOverview.dart +++ /dev/null @@ -1,164 +0,0 @@ -import 'dart:convert'; - -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:http/http.dart' as http; -import 'package:marianum_mobile/widget/loadingPacket.dart'; -import 'package:provider/provider.dart'; - -import '../../../dataOld/incommingPackets/talkContactsPacket.dart'; -import '../../../widget/loadingSpinner.dart'; -import 'chatView.dart'; - -class Talk extends StatefulWidget { - const Talk({Key? key}) : super(key: key); - - @override - State createState() => _TalkState(); -} - -class _TalkState extends State { - // List chats = List.empty(growable: true); - // - // Future> getChats() async { - // var url = Uri.https("***REMOVED***:***REMOVED***@mhsl.eu", "marianum/app/middleware/chat.php"); - // var response = await http.get( - // url, - // headers: ( - // { - // "Accept": "application/json", - // "OCS-APIRequest": "true", - // } - // ), - // ); - // - // return compute(parseChats, response.body); - // } - - - @override - void initState() { - Provider.of(context, listen: false).invoke(); - //TalkContactsAskPacket().send(); - super.initState(); - // Future.delayed(Duration.zero).then((context) => updateChats()); - // Provider.of(context, listen: false).channel.sink.add("chat"); - - } - - void updateChats() { - // var chats = getChats(); - // - // showDialog( - // context: context, - // barrierDismissible: false, - // builder: (BuildContext context) { - // return const LoadingSpinner(); - // } - // ); - // - // chats.then((value) => - // setState(() { - // Navigator.pop(context); - // this.chats.clear(); - // this.chats = value; - // }) - // ); - } - - @override - Widget build(BuildContext context) { - // List chats = List.empty(growable: true); - // - // for (var element in this.chats) { - // chats.add( - // ListTile( - // leading: element.type == 1 ? CircleAvatar( - // backgroundColor: Colors.grey, - // foregroundImage: Image.network(element.avatar).image, - // ) : const Icon(Icons.group), - // title: Text(element.name), - // subtitle: Text( - // "${element.lastMessageAuthor}: ${element.lastMessage.replaceAll("\n", "")}", - // overflow: TextOverflow.ellipsis, - // ), - // onTap: () { - // Navigator.push(context, MaterialPageRoute(builder: (builder) => const ChatView())); - // }, - // trailing: element.unreadMessages > 0 ? const Icon(Icons.mark_chat_unread) : Text(element.lastActivity), - // ) - // ); - // } - // - // return ListView( - // children: chats, - // ); - - return Consumer( - builder: (context, data, child) { - List chats = List.empty(growable: true); - - for (var element in data.contacts) { - chats.add(ListTile( - title: Text(element.name), - subtitle: Text("${element.lastTime}: ${element.lastMessage}".replaceAll("\n", " "), overflow: TextOverflow.ellipsis), - trailing: element.unreadMessages ? const Icon(Icons.new_releases_outlined) : null, - leading: CircleAvatar( - foregroundImage: element.isGroup ? null : Image.network(element.profilePicture).image, - backgroundColor: Theme.of(context).primaryColor, - foregroundColor: Colors.white, - child: element.isGroup ? const Icon(Icons.group) : const Icon(Icons.person), - ), - onTap: () { - Navigator.of(context).push(MaterialPageRoute(builder: (context) { - return ChatView( - userToken: element.userToken, - ); - })); - }, - )); - } - - return LoadingPacket(packet: data, child: ListView(children: chats)); - }, - ); - } -} - -// -// List parseChats(String json) { -// final parsed = jsonDecode(json).cast>(); -// return parsed.map((a) => ChatData.fromJson(a)).toList(); -// } -// -// class ChatData { -// final String name; -// final String lastMessage; -// final String lastMessageAuthor; -// final String avatar; -// final int type; -// final String lastActivity; -// final int unreadMessages; -// -// const ChatData({ -// required this.name, -// required this.lastMessage, -// required this.lastMessageAuthor, -// required this.avatar, -// required this.type, -// required this.lastActivity, -// required this.unreadMessages, -// }); -// -// factory ChatData.fromJson(Map json) { -// return ChatData( -// name: json['name'] as String, -// lastMessage: json['last_message'] as String, -// lastMessageAuthor: json['last_message_author'] as String, -// avatar: json['avatar'] as String, -// type: json['type'] as int, -// lastActivity: json['lastActivity'] as String, -// unreadMessages: json['unreadMessages'] as int, -// ); -// } -// } \ No newline at end of file diff --git a/lib/screen/pages/talk/chatView.dart b/lib/screen/pages/talk/chatView.dart index 0da55cf..af82c33 100644 --- a/lib/screen/pages/talk/chatView.dart +++ b/lib/screen/pages/talk/chatView.dart @@ -1,14 +1,18 @@ +import 'dart:developer'; import 'package:bubble/bubble.dart'; import 'package:flutter/material.dart'; -import 'package:marianum_mobile/widget/loadingPacket.dart'; +import 'package:jiffy/jiffy.dart'; +import 'package:marianum_mobile/api/marianumcloud/talk/room/getRoomResponse.dart'; +import 'package:marianum_mobile/data/chatList/chatProps.dart'; import 'package:provider/provider.dart'; -import '../../../dataOld/incommingPackets/talkChatPacket.dart'; - class ChatView extends StatefulWidget { - final String userToken; - const ChatView({Key? key, required this.userToken}) : super(key: key); + final GetRoomResponseObject user; + final String selfId; + final CircleAvatar avatar; + + const ChatView({Key? key, required this.user, required this.selfId, required this.avatar}) : super(key: key); @override State createState() => _ChatViewState(); @@ -16,7 +20,7 @@ class ChatView extends StatefulWidget { class _ChatViewState extends State { static const styleSystem = BubbleStyle( - color: Color.fromRGBO(212, 234, 244, 1.0), + color: Color(0xffd4eaf4), borderWidth: 1, elevation: 2, margin: BubbleEdges.only(top: 15), @@ -24,79 +28,151 @@ class _ChatViewState extends State { ); static const styleOther = BubbleStyle( - nip: BubbleNip.leftBottom, + nip: BubbleNip.leftTop, color: Colors.white, borderWidth: 1, - elevation: 2, - margin: BubbleEdges.only(top: 15, left: 10), + elevation: 1, + margin: BubbleEdges.only(top: 15, left: 10, right: 50), alignment: Alignment.topLeft, ); static const styleSelf = BubbleStyle( nip: BubbleNip.rightBottom, - color: Color.fromRGBO(225, 255, 199, 1.0), + color: Color(0xffd9fdd3), borderWidth: 1, - elevation: 2, - margin: BubbleEdges.only(top: 15, right: 10), + elevation: 1, + margin: BubbleEdges.only(top: 15, right: 10, left: 50), alignment: Alignment.topRight, ); + final ScrollController _listController = ScrollController(); @override void initState() { super.initState(); - Provider.of(context, listen: false).invoke( - data: { - "token": widget.userToken - }, - indicateLoading: true, - allowNotifyListeners: false, - ); + WidgetsBinding.instance.addPostFrameCallback((timeStamp) { + Provider.of(context, listen: false).setQueryToken(widget.user.token); + }); } @override Widget build(BuildContext context) { - return Scaffold( - backgroundColor: Colors.grey, - appBar: AppBar( - title: const Text("Chat mit jemandem"), - ), - body: Consumer( - builder: (context, data, child) { - List messages = List.empty(growable: true); + return Consumer( + builder: (context, data, child) { + List messages = List.empty(growable: true); + + if(!data.primaryLoading()) { + String lastActor = ""; + bool showMetadata = true; + + data.getChatResponse.sortByTimestamp().forEach((element) { + + showMetadata = element.messageType == GetRoomResponseObjectMessageType.comment; + + BubbleStyle currentStyle; + if(element.messageType == GetRoomResponseObjectMessageType.comment) { + if(element.actorId == widget.selfId) { + currentStyle = styleSelf; + } else { + currentStyle = styleOther; + } + } else { + currentStyle = styleSystem; + } + - data.messages.forEach((element) { messages.add(Bubble( - style: styleSelf, - child: Text(element.content), + margin: BubbleEdges.only(bottom: element == data.getChatResponse.sortByTimestamp().last ? 20 : 0), + + style: currentStyle, + child: Stack( + children: [ + Visibility( + visible: showMetadata, + child: Positioned( + top: 0, + left: 0, + child: Text("${element.actorDisplayName}", style: TextStyle(fontWeight: FontWeight.bold, color: Theme.of(context).primaryColor)), + ), + ), + Padding( + padding: EdgeInsets.symmetric(vertical: showMetadata ? 18 : 0), + child: Text(element.message), + ), + Visibility( + visible: showMetadata, + child: Positioned( + bottom: 0, + right: 0, + child: Text( + "${Jiffy.unixFromSecondsSinceEpoch(element.timestamp).yMMMMd} - ${Jiffy.unixFromSecondsSinceEpoch(element.timestamp).format("HH:mm")}", + style: TextStyle(color: Theme.of(context).disabledColor), + ), + ), + ), + ], + ), )); + + lastActor = element.actorId; }); + } - return LoadingPacket(packet: data, child: ListView( - children: [], - )); - }, - ), - - - - // ListView( - // children: [ - // Bubble( - // style: styleSystem, - // child: const Text("Chat gestartet"), - // ), - // Bubble( - // style: styleOther, - // child: const Text("Hi, das ist ein Testtext"), - // ), - // Bubble( - // style: styleSelf, - // child: Text(widget.userToken), - // ) - // ], - // ), + return Scaffold( + backgroundColor: const Color(0xffefeae2), + appBar: AppBar( + title: Row( + children: [ + widget.avatar, + const SizedBox(width: 10), + Text(widget.user.displayName, overflow: TextOverflow.ellipsis, maxLines: 1), + ], + ), + ), + body: Container( + decoration: const BoxDecoration( + image: DecorationImage( + image: AssetImage("assets/background/chat.png"), + scale: 1.5, + opacity: 0.5, + repeat: ImageRepeat.repeat, + colorFilter: ColorFilter.linearToSrgbGamma() + ) + ), + child: data.primaryLoading() ? const Center(child: CircularProgressIndicator()) : Column( + children: [ + Expanded( + child: ListView( + reverse: true, + controller: _listController, + children: messages.reversed.toList(), + ), + ), + Container( + color: Theme.of(context).dividerColor, + padding: const EdgeInsets.all(10), + child: Row( + children: [ + const Expanded( + child: TextField( + maxLines: null, + decoration: InputDecoration( + hintText: "Nachricht", + border: OutlineInputBorder(), + labelText: "", + ), + ), + ), + IconButton(onPressed: () {}, icon: const Icon(Icons.send)) + ], + ), + ) + ], + ) + ), + ); + }, ); } } diff --git a/lib/screen/pages/timetable/dayListView.dart b/lib/screen/pages/timetable/dayListView.dart new file mode 100644 index 0000000..bafd9c4 --- /dev/null +++ b/lib/screen/pages/timetable/dayListView.dart @@ -0,0 +1,139 @@ +import 'dart:developer'; + +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; +import 'package:jiffy/jiffy.dart'; +import 'package:marianum_mobile/api/webuntis/queries/getHolidays/getHolidaysResponse.dart'; +import 'package:timetable_view/timetable_view.dart'; + +import '../../../api/webuntis/queries/getHolidays/getHolidays.dart'; +import '../../../api/webuntis/queries/getRooms/getRoomsResponse.dart'; +import '../../../api/webuntis/queries/getSubjects/getSubjectsResponse.dart'; +import '../../../api/webuntis/queries/getTimetable/getTimetableResponse.dart'; +import '../../../data/timetable/timetableProps.dart'; + +class DayListView extends StatefulWidget { + final TimetableProps value; + const DayListView(this.value, {Key? key}) : super(key: key); + + @override + State createState() => _DayListViewState(); +} + +class _DayListViewState extends State { + @override + Widget build(BuildContext context) { + return TimetableView( + laneEventsList: _buildLaneEvents(widget.value), + onEventTap: (TableEvent event) {}, + timetableStyle: CustomTableStyle(context), + onEmptySlotTap: (int laneIndex, TableEventTime start, TableEventTime end) => {}, + ); + } + + List _buildLaneEvents(TimetableProps data) { + List laneEvents = List.empty(growable: true); + Jiffy.locale("de"); // todo move outwards + + GetTimetableResponse timetable = data.getTimetableResponse; + GetRoomsResponse rooms = data.getRoomsResponse; + GetSubjectsResponse subjects = data.getSubjectsResponse; + GetHolidaysResponse holidays = data.getHolidaysResponse; + + GetHolidaysResponseObject? holidayInfo = GetHolidays.find(holidays, time: data.queryDate); + if(holidayInfo != null) { + laneEvents.add( + LaneEvents( + lane: Lane( + laneIndex: data.queryDate.millisecondsSinceEpoch, + name: "${Jiffy(data.queryDate.toString()).format("dd.MM.yy")}\n${Jiffy(data.queryDate.toString()).format("EEEE")}", + textStyle: TextStyle( + color: Theme.of(context).primaryColor, + fontWeight: FontWeight.bold, + fontSize: 14 + ) + ), + events: List.of([ + TableEvent( + title: holidayInfo.name, + eventId: holidayInfo.id, + laneIndex: data.queryDate.millisecondsSinceEpoch, + startTime: parseTime(0800), + endTime: parseTime(1500), + padding: const EdgeInsets.all(5), + backgroundColor: Theme.of(context).disabledColor, + location: "\n${holidayInfo.longName}", + ) + ]), + ) + ); + } + + List dayList = timetable.result.map((e) => e.date).toSet().toList(); + dayList.sort((a, b) => a-b); + dayList.forEach((day) { + //Every Day + laneEvents.add( + LaneEvents( + lane: Lane( + laneIndex: day, + name: "${Jiffy(day.toString()).format("dd.MM.yy")}\n${Jiffy(day.toString()).format("EEEE")}", + textStyle: TextStyle( + color: Theme.of(context).primaryColor, + fontWeight: FontWeight.bold, + fontSize: 14 + ) + ), + events: List.generate( + timetable.result.where((element) => element.date == day).length, + (index) { + GetTimetableResponseObject tableEvent = timetable.result.where((element) => element.date == day).elementAt(index); + + GetSubjectsResponseObject subject = subjects.result.firstWhere((subject) => subject.id == tableEvent.su[0]['id']); + + return TableEvent( + title: "${subject.alternateName} (${subject.longName})", + eventId: tableEvent.id, + laneIndex: day, + startTime: parseTime(tableEvent.startTime), + endTime: parseTime(tableEvent.endTime), + padding: const EdgeInsets.all(5), + backgroundColor: Theme.of(context).primaryColor, + location: "\n${rooms.result.firstWhere((room) => room.id == tableEvent.ro[0]['id']).name} - ${tableEvent.te[0]['longname']} (${tableEvent.te[0]['name']})", + ); + } + ) + ) + ); + }); + + return laneEvents; + } + + TableEventTime parseTime(int input) { + String time = input.toString().length < 4 ? "0$input" : input.toString(); + return TableEventTime(hour: int.parse(time.substring(0, 2)), minute: int.parse(time.substring(2, 4))); + } +} + +class CustomTableStyle extends TimetableStyle { + dynamic context; + CustomTableStyle(this.context); + + @override + int get startHour => 07; + @override + int get endHour => 17; + @override + Color get cornerColor => Theme.of(context).primaryColor; + @override + Color get timeItemTextColor => Theme.of(context).primaryColor; + @override + double get timeItemHeight => 70; + @override + double get timeItemWidth => 50; + @override + double get laneWidth => MediaQuery.of(context).size.width - timeItemWidth; + +} \ No newline at end of file diff --git a/lib/screen/pages/timetable/storedTimetable.dart b/lib/screen/pages/timetable/storedTimetable.dart deleted file mode 100644 index 0a63e79..0000000 --- a/lib/screen/pages/timetable/storedTimetable.dart +++ /dev/null @@ -1,104 +0,0 @@ -import 'dart:developer'; - -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; -import 'package:jiffy/jiffy.dart'; -import 'package:marianum_mobile/api/webuntis/queries/getRooms/getRoomsResponse.dart'; -import 'package:marianum_mobile/api/webuntis/queries/getSubjects/getSubjectsResponse.dart'; -import 'package:marianum_mobile/data/timetable/timetable.dart'; -import 'package:marianum_mobile/screen/pages/timetable/testTimetable.dart'; -import 'package:marianum_mobile/widget/loadingSpinner.dart'; -import 'package:provider/provider.dart'; -import 'package:timetable_view/timetable_view.dart'; - -import '../../../api/webuntis/queries/getTimetable/getTimetableResponse.dart'; - -class StoredTimetable extends StatefulWidget { - const StoredTimetable({Key? key}) : super(key: key); - - @override - State createState() => _StoredTimetableState(); -} - -class _StoredTimetableState extends State { - @override - void initState() { - super.initState(); - - WidgetsBinding.instance.addPostFrameCallback((timeStamp) { - Provider.of(context, listen: false).run(); - }); - } - - @override - Widget build(BuildContext context) { - return Consumer( - builder: (context, value, child) { - if(value.getTimetableResponse == null) { - return const LoadingSpinner(); - } - - return TimetableView( - laneEventsList: _buildLaneEvents(value), - onEventTap: (TableEvent event) {}, - timetableStyle: CustomTableStyle(context), - onEmptySlotTap: (int laneIndex, TableEventTime start, TableEventTime end) => {}, - ); - }, - ); - } - - - List _buildLaneEvents(Timetable data) { - List laneEvents = List.empty(growable: true); - Jiffy.locale("de"); // todo move outwards - - GetTimetableResponse timetable = data.getTimetableResponse!; - GetRoomsResponse rooms = data.getRoomsResponse!; - GetSubjectsResponse subjects = data.getSubjectsResponse!; - - List dayList = timetable.result.map((e) => e.date).toSet().toList(); - dayList.sort((a, b) => a-b); - dayList.forEach((day) { - //Every Day - - laneEvents.add( - LaneEvents( - lane: Lane( - laneIndex: day, - name: "${Jiffy(day.toString()).format("dd.MM.yy")}\n${Jiffy(day.toString()).format("EEEE")}", - textStyle: TextStyle( - color: Theme.of(context).primaryColor, - fontWeight: FontWeight.bold, - fontSize: 14 - ) - ), - events: List.generate( - timetable.result.where((element) => element.date == day).length, - (index) { - GetTimetableResponseObject tableEvent = timetable.result.where((element) => element.date == day).elementAt(index); - return TableEvent( - title: subjects.result.firstWhere((subject) => subject.id == tableEvent.su[0]['id']).longName, - eventId: tableEvent.id, - laneIndex: day, - startTime: parseTime(tableEvent.startTime), - endTime: parseTime(tableEvent.endTime), - padding: const EdgeInsets.all(5), - backgroundColor: Theme.of(context).primaryColor, - location: "\n${rooms.result.firstWhere((room) => room.id == tableEvent.ro[0]['id']).name}", - ); - } - ) - ) - ); - - }); - - return laneEvents; - } - - TableEventTime parseTime(int input) { - String time = input.toString().length < 4 ? "0$input" : input.toString(); - return TableEventTime(hour: int.parse(time.substring(0, 2)), minute: int.parse(time.substring(2, 4))); - } -} diff --git a/lib/screen/pages/timetable/testTimetable.dart b/lib/screen/pages/timetable/testTimetable.dart index 3023a8c..13aff8f 100644 --- a/lib/screen/pages/timetable/testTimetable.dart +++ b/lib/screen/pages/timetable/testTimetable.dart @@ -4,6 +4,7 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:jiffy/jiffy.dart'; import 'package:marianum_mobile/api/webuntis/queries/getRooms/getRooms.dart'; +import 'package:marianum_mobile/data/timetable/timetableProps.dart'; import 'package:marianum_mobile/widget/loadingSpinner.dart'; import 'package:marianum_mobile/widget/offlineError.dart'; import 'package:timetable_view/timetable_view.dart'; @@ -131,14 +132,14 @@ class CustomTableStyle extends TimetableStyle { @override int get endHour => 17; @override - double get laneWidth => 300; - @override Color get cornerColor => Theme.of(context).primaryColor; @override Color get timeItemTextColor => Theme.of(context).primaryColor; @override - double get timeItemHeight => 80; + double get timeItemHeight => 70; @override - double get timeItemWidth => 70; + double get timeItemWidth => 50; + @override + double get laneWidth => MediaQuery.of(context).size.width - timeItemWidth; } \ No newline at end of file diff --git a/lib/screen/pages/timetable/timetable.dart b/lib/screen/pages/timetable/timetable.dart index 9beb944..36f184e 100644 --- a/lib/screen/pages/timetable/timetable.dart +++ b/lib/screen/pages/timetable/timetable.dart @@ -1,13 +1,18 @@ - import 'dart:developer'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; -import 'package:marianum_mobile/widget/loadingPacket.dart'; +import 'package:jiffy/jiffy.dart'; +import 'package:marianum_mobile/api/webuntis/queries/getRooms/getRoomsResponse.dart'; +import 'package:marianum_mobile/api/webuntis/queries/getSubjects/getSubjectsResponse.dart'; +import 'package:marianum_mobile/data/timetable/timetableProps.dart'; +import 'package:marianum_mobile/screen/pages/timetable/dayListView.dart'; +import 'package:marianum_mobile/screen/pages/timetable/testTimetable.dart'; +import 'package:marianum_mobile/widget/loadingSpinner.dart'; import 'package:provider/provider.dart'; import 'package:timetable_view/timetable_view.dart'; -import '../../../dataOld/incommingPackets/timetablePacket.dart'; +import '../../../api/webuntis/queries/getTimetable/getTimetableResponse.dart'; class Timetable extends StatefulWidget { const Timetable({Key? key}) : super(key: key); @@ -17,90 +22,69 @@ class Timetable extends StatefulWidget { } class _TimetableState extends State { - @override void initState() { - Provider.of(context, listen: false).invoke(); super.initState(); - } + WidgetsBinding.instance.addPostFrameCallback((timeStamp) { + Provider.of(context, listen: false).run(); + }); + } + @override Widget build(BuildContext context) { - return Consumer( - builder: (context, data, child) { + return Consumer( + builder: (context, value, child) { + if(value.primaryLoading()) { + return const Center(child: CircularProgressIndicator()); + } - return LoadingPacket(packet: data, child: TimetableView( - laneEventsList: _buildLaneEvents(context, data), - onEventTap: (TableEvent event) {}, - timetableStyle: CustomTableStyle(context), - onEmptySlotTap: (int laneIndex, TableEventTime start, TableEventTime end) => {}, - )); + TimetableProps timetable = Provider.of(context, listen: false); + return Column( + children: [ + Flexible( + child: DayListView(value), + ), + + Container( + padding: const EdgeInsets.only(top: 5, bottom: 5), + decoration: BoxDecoration( + border: Border( + top: BorderSide(width: 2, color: Theme.of(context).disabledColor) + ) + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + + children: [ + IconButton( + onPressed: () => timetable.next(previous: true), + icon: const Icon(Icons.navigate_before_sharp), + color: Theme.of(context).primaryColor, + iconSize: 30, + ), + Row( + children: [ + IconButton( + onPressed: () => timetable.nearest(), + icon: const Icon(Icons.home), + color: Theme.of(context).primaryColor, + iconSize: 30, + ), + ], + ), + IconButton( + onPressed: () => timetable.next(), + icon: const Icon(Icons.navigate_next_sharp), + color: Theme.of(context).primaryColor, + iconSize: 30, + ) + ], + ), + ) + ], + ); }, ); } - - List _buildLaneEvents(context, TimetablePacket data) { - List laneEvents = List.empty(growable: true); - data.timeTable.days.forEach((day) { - List tableEvents = List.empty(growable: true); - - day.entries.forEach((element) { - tableEvents.add( - TableEvent( - backgroundColor: Theme.of(context).primaryColor, - padding: const EdgeInsets.all(5), - title: element.subject, - location: "\n${element.room}", - eventId: tableEvents.length, - laneIndex: tableEvents.length, - startTime: TableEventTime(hour: element.start.hour, minute: element.start.minute), - endTime: TableEventTime(hour: element.end.hour, minute: element.end.minute) - ) - ); - }); - - laneEvents.add( - LaneEvents( - lane: Lane(laneIndex: laneEvents.length, name: day.name, textStyle: TextStyle(color: Theme.of(context).primaryColor, fontWeight: FontWeight.bold)), - events: tableEvents - ) - ); - }); - - - return laneEvents; - } - - void onEventTapCallBack(TableEvent event) { - print( - "Event Clicked!! LaneIndex ${event.laneIndex} Title: ${event.title} StartHour: ${event.startTime.hour} EndHour: ${event.endTime.hour}"); - } - - void onTimeSlotTappedCallBack( - int laneIndex, TableEventTime start, TableEventTime end) { - print( - "Empty Slot Clicked !! LaneIndex: $laneIndex StartHour: ${start.hour} EndHour: ${end.hour}"); - } } - -class CustomTableStyle extends TimetableStyle { - dynamic context; - CustomTableStyle(context) { - this.context = context; - } - - - @override - int get startHour => 07; - @override - int get endHour => 17; - @override - double get laneWidth => 200; - @override - Color get cornerColor => Theme.of(this.context).primaryColor; - @override - Color get timeItemTextColor => Theme.of(this.context).primaryColor; - @override - // TODO: implement timeItemHeight - double get timeItemHeight => 60; -} \ No newline at end of file diff --git a/lib/screen/pages/timetable/timetableOld.dart b/lib/screen/pages/timetable/timetableOld.dart new file mode 100644 index 0000000..ecdc56f --- /dev/null +++ b/lib/screen/pages/timetable/timetableOld.dart @@ -0,0 +1,106 @@ + +import 'dart:developer'; + +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:marianum_mobile/widget/loadingPacket.dart'; +import 'package:provider/provider.dart'; +import 'package:timetable_view/timetable_view.dart'; + +import '../../../dataOld/incommingPackets/timetablePacket.dart'; + +class TimetableOld extends StatefulWidget { + const TimetableOld({Key? key}) : super(key: key); + + @override + State createState() => _TimetableOldState(); +} + +class _TimetableOldState extends State { + + @override + void initState() { + Provider.of(context, listen: false).invoke(); + super.initState(); + } + + @override + Widget build(BuildContext context) { + return Consumer( + builder: (context, data, child) { + + return LoadingPacket(packet: data, child: TimetableView( + laneEventsList: _buildLaneEvents(context, data), + onEventTap: (TableEvent event) {}, + timetableStyle: CustomTableStyle(context), + onEmptySlotTap: (int laneIndex, TableEventTime start, TableEventTime end) => {}, + )); + }, + ); + } + + List _buildLaneEvents(context, TimetablePacket data) { + List laneEvents = List.empty(growable: true); + data.timeTable.days.forEach((day) { + List tableEvents = List.empty(growable: true); + + day.entries.forEach((element) { + tableEvents.add( + TableEvent( + backgroundColor: Theme.of(context).primaryColor, + padding: const EdgeInsets.all(5), + title: element.subject, + location: "\n${element.room}", + eventId: tableEvents.length, + laneIndex: tableEvents.length, + startTime: TableEventTime(hour: element.start.hour, minute: element.start.minute), + endTime: TableEventTime(hour: element.end.hour, minute: element.end.minute) + ) + ); + }); + + laneEvents.add( + LaneEvents( + lane: Lane(laneIndex: laneEvents.length, name: day.name, textStyle: TextStyle(color: Theme.of(context).primaryColor, fontWeight: FontWeight.bold)), + events: tableEvents + ) + ); + }); + + + return laneEvents; + } + + void onEventTapCallBack(TableEvent event) { + print( + "Event Clicked!! LaneIndex ${event.laneIndex} Title: ${event.title} StartHour: ${event.startTime.hour} EndHour: ${event.endTime.hour}"); + } + + void onTimeSlotTappedCallBack( + int laneIndex, TableEventTime start, TableEventTime end) { + print( + "Empty Slot Clicked !! LaneIndex: $laneIndex StartHour: ${start.hour} EndHour: ${end.hour}"); + } +} + +class CustomTableStyle extends TimetableStyle { + dynamic context; + CustomTableStyle(context) { + this.context = context; + } + + + @override + int get startHour => 07; + @override + int get endHour => 17; + @override + double get laneWidth => 200; + @override + Color get cornerColor => Theme.of(this.context).primaryColor; + @override + Color get timeItemTextColor => Theme.of(this.context).primaryColor; + @override + // TODO: implement timeItemHeight + double get timeItemHeight => 60; +} \ No newline at end of file diff --git a/lib/screen/settings/debug/debugOverview.dart b/lib/screen/settings/debug/debugOverview.dart new file mode 100644 index 0000000..23ecb94 --- /dev/null +++ b/lib/screen/settings/debug/debugOverview.dart @@ -0,0 +1,90 @@ +import 'dart:convert'; +import 'dart:developer'; + +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:jiffy/jiffy.dart'; +import 'package:localstore/localstore.dart'; +import 'package:marianum_mobile/screen/settings/debug/jsonViewer.dart'; + +class DebugOverview extends StatefulWidget { + const DebugOverview({Key? key}) : super(key: key); + + @override + State createState() => _DebugOverviewState(); +} + +class _DebugOverviewState extends State { + + final Localstore storage = Localstore.instance; + Future?> files = Localstore.instance.collection("MarianumMobile").get(); + dynamic data; + + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text("Lokaler cache"), + ), + body: Column( + children: [ + Expanded( + flex: 1, + child: ListView( + children: [ + ListTile( + leading: const Icon(Icons.delete_forever), + title: const Text("Cache löschen"), + onTap: () { + storage.collection("MarianumMobile").delete().then((value) => { + Navigator.pop(context) + }); + }, + ) + ], + ), + ), + const Divider(), + FutureBuilder( + future: files, + builder: (context, snapshot) { + if(snapshot.hasData) { + List files = snapshot.data?.keys.map((e) => e.toString()).toList() ?? List.empty(); + + Map getValue(int index) { + return snapshot.data?[files[index]]; + } + + return Expanded( + flex: 5, + child: ListView.builder( + itemCount: files.length, + itemBuilder: (context, index) { + String filename = files[index].split("/").last; + //String data = getValue(index).toString().replaceAll("{", "{\n ").replaceAll("[", "[\n ").replaceAll(",", ",\n "); + String data = getValue(index).toString(); + + return ListTile( + leading: const Icon(Icons.text_snippet_outlined), + title: Text("(${data.length} z) [$filename] ${Jiffy.unixFromMillisecondsSinceEpoch(getValue(index)['lastupdate']).fromNow()}"), + textColor: Colors.black, + onTap: () { + Navigator.push(context, MaterialPageRoute(builder: (context) { + return JsonViewer(title: filename, data: data); + },)); + }, + ); + }, + ), + ); + } else { + return snapshot.data == null ? const Text("No data") : const Center(child: CircularProgressIndicator()); + } + }, + ), + ], + ), + ); + } +} diff --git a/lib/screen/settings/debug/jsonViewer.dart b/lib/screen/settings/debug/jsonViewer.dart new file mode 100644 index 0000000..7bd1211 --- /dev/null +++ b/lib/screen/settings/debug/jsonViewer.dart @@ -0,0 +1,21 @@ +import 'package:flutter/material.dart'; + +class JsonViewer extends StatelessWidget { + String title; + String data; + + JsonViewer({Key? key, required this.title, required this.data}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text(title), + ), + body: SingleChildScrollView( + scrollDirection: Axis.vertical, + child: Text(data.replaceAllMapped(RegExp(r'[{,}]'), (match) => "${match.group(0)}\n ")), + ), + ); + } +} diff --git a/lib/screen/settings/settings.dart b/lib/screen/settings/settings.dart index f269c99..a9595d5 100644 --- a/lib/screen/settings/settings.dart +++ b/lib/screen/settings/settings.dart @@ -6,6 +6,7 @@ import 'package:shared_preferences/shared_preferences.dart'; import '../../dataOld/accountModel.dart'; import '../../dataOld/incommingPackets/serverInfoPacket.dart'; import '../../widget/ListItem.dart'; +import 'debug/debugOverview.dart'; class Settings extends StatefulWidget { const Settings({Key? key}) : super(key: key); @@ -19,7 +20,6 @@ class _SettingsState extends State { @override void initState() { super.initState(); - Provider.of(context, listen: false).invoke(); } @override @@ -31,56 +31,64 @@ class _SettingsState extends State { body: ListView( children: [ - const ListItemNavigator(icon: Icons.info, text: "Über diese App", target: AboutDialog( - applicationIcon: Icon(Icons.send_time_extension_outlined), - applicationLegalese: "Released under MIT-License", - applicationName: "Marianum Fulda", - applicationVersion: "ALPHA 0.1", - )), - ListTile( leading: const Icon(Icons.logout), - title: const Text("Account abmelden"), + title: const Text("Konto abmelden"), onTap: () { - Navigator.push(context, MaterialPageRoute(builder: (builder) => AlertDialog( - title: const Text("Abmelden?"), - content: const Text("Möchtest du dich wirklich abmelden?"), - actions: [ - TextButton( - child: const Text("Abmelden"), - onPressed: () { - SharedPreferences.getInstance().then((value) => { - value.clear(), - }).then((value) => { - Provider.of(context, listen: false).logout(), - Navigator.popUntil(context, (route) => !Navigator.canPop(context)), - }); - } - ), + showDialog( + context: context, + builder: (context) { + return AlertDialog( + title: const Text("Abmelden?"), + content: const Text("Möchtest du dich wirklich abmelden?"), + actions: [ + TextButton( + child: const Text("Abmelden"), + onPressed: () { + SharedPreferences.getInstance().then((value) => { + value.clear(), + }).then((value) => { + Provider.of(context, listen: false).logout(), + Navigator.popUntil(context, (route) => !Navigator.canPop(context)), + }); + } + ), - TextButton( - child: const Text("Abbrechen"), - onPressed: () { - Navigator.pop(context); - }, - ), - ], - ))); + TextButton( + child: const Text("Abbrechen"), + onPressed: () { + Navigator.pop(context); + }, + ), + ], + ); + }, + ); }, ), - Consumer( - builder: (context, serverInfo, child) { - return ListTile( - leading: const Icon(Icons.home_work_outlined), - title: Text("Server: ${serverInfo.serverName}"), - subtitle: Text( - "Betreiber: ${serverInfo.serverOwner}\n" - "Serverversion: ${serverInfo.serverVersion}\n" - "Rechtliche hinweise: ${serverInfo.legal}\n" - ), + ListTile( + leading: const Icon(Icons.info), + title: const Text("Informationen und Lizenzen"), + onTap: () { + showAboutDialog( + context: context, + applicationIcon: const Icon(Icons.send_time_extension_outlined), + applicationName: "MarianumMobile", + applicationVersion: "Development Build", + applicationLegalese: "Marianum Fulda 2023 Elias Müller", ); }, + ), + + ListTile( + leading: const Icon(Icons.bug_report_outlined), + title: const Text("Speicheransicht"), + onTap: () { + Navigator.push(context, MaterialPageRoute(builder: (context) { + return const DebugOverview(); + })); + }, ) ], diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 63ad0d1..b19945c 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -7,12 +7,10 @@ import Foundation import path_provider_foundation import shared_preferences_foundation -import sqflite import url_launcher_macos func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) - SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin")) UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) } diff --git a/pubspec.yaml b/pubspec.yaml index 8da3b6d..af72320 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -47,7 +47,6 @@ dependencies: jiffy: ^5.0.0 timetable_view: ^0.3.0 json_annotation: ^4.8.0 - sqflite: ^2.2.4+1 localstore: ^1.2.3 intl: ^0.17.0 @@ -82,6 +81,7 @@ flutter: assets: - assets/ca/ + - assets/background/ # An image asset can refer to one or more resolution-specific "variants", see # https://flutter.dev/assets-and-images/#resolution-aware