Merge branch 'develop' into feature-highEduGraduationCalculator
# Conflicts: # lib/view/pages/overhang.dart
This commit is contained in:
commit
5b34afd6cb
1
.gitignore
vendored
1
.gitignore
vendored
@ -349,3 +349,4 @@ hs_err_pid*
|
|||||||
# End of https://www.toptal.com/developers/gitignore/api/flutter,intellij,androidstudio,xcode,dart
|
# End of https://www.toptal.com/developers/gitignore/api/flutter,intellij,androidstudio,xcode,dart
|
||||||
|
|
||||||
*.idea*
|
*.idea*
|
||||||
|
**/.DS_store
|
||||||
|
@ -24,6 +24,10 @@ linter:
|
|||||||
rules:
|
rules:
|
||||||
# avoid_print: false # Uncomment to disable the `avoid_print` rule
|
# avoid_print: false # Uncomment to disable the `avoid_print` rule
|
||||||
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
|
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
|
||||||
|
file_names: false
|
||||||
|
prefer_relative_imports: true
|
||||||
|
unnecessary_lambdas: true
|
||||||
|
prefer_single_quotes: true
|
||||||
|
|
||||||
# Additional information about this file can be found at
|
# Additional information about this file can be found at
|
||||||
# https://dart.dev/guides/language/analysis-options
|
# https://dart.dev/guides/language/analysis-options
|
||||||
|
@ -5,6 +5,6 @@ class ApiError {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return "ApiError: $message";
|
return 'ApiError: $message';
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -6,11 +6,11 @@ import 'getHolidaysResponse.dart';
|
|||||||
|
|
||||||
class GetHolidays {
|
class GetHolidays {
|
||||||
Future<GetHolidaysResponse> query() async {
|
Future<GetHolidaysResponse> query() async {
|
||||||
String response = (await http.get(Uri.parse("https://ferien-api.de/api/v1/holidays/HE"))).body;
|
String response = (await http.get(Uri.parse('https://ferien-api.de/api/v1/holidays/HE'))).body;
|
||||||
return GetHolidaysResponse(
|
return GetHolidaysResponse(
|
||||||
List<GetHolidaysResponseObject>.from(
|
List<GetHolidaysResponseObject>.from(
|
||||||
jsonDecode(response).map<GetHolidaysResponseObject>(
|
jsonDecode(response).map<GetHolidaysResponseObject>(
|
||||||
(dynamic i) => GetHolidaysResponseObject.fromJson(i)
|
GetHolidaysResponseObject.fromJson
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
@ -6,7 +6,7 @@ import 'getHolidaysResponse.dart';
|
|||||||
|
|
||||||
class GetHolidaysCache extends RequestCache<GetHolidaysResponse> {
|
class GetHolidaysCache extends RequestCache<GetHolidaysResponse> {
|
||||||
GetHolidaysCache({onUpdate, renew}) : super(RequestCache.cacheDay, onUpdate, renew: renew) {
|
GetHolidaysCache({onUpdate, renew}) : super(RequestCache.cacheDay, onUpdate, renew: renew) {
|
||||||
start("MarianumMobile", "state-holidays");
|
start('MarianumMobile', 'state-holidays');
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -15,6 +15,7 @@ class GetHolidaysCache extends RequestCache<GetHolidaysResponse> {
|
|||||||
return GetHolidaysResponse(
|
return GetHolidaysResponse(
|
||||||
List<GetHolidaysResponseObject>.from(
|
List<GetHolidaysResponseObject>.from(
|
||||||
parsedListJson.map<GetHolidaysResponseObject>(
|
parsedListJson.map<GetHolidaysResponseObject>(
|
||||||
|
// ignore: unnecessary_lambdas
|
||||||
(dynamic i) => GetHolidaysResponseObject.fromJson(i)
|
(dynamic i) => GetHolidaysResponseObject.fromJson(i)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -11,21 +11,21 @@ import 'autocompleteResponse.dart';
|
|||||||
class AutocompleteApi {
|
class AutocompleteApi {
|
||||||
Future<AutocompleteResponse> find(String query) async {
|
Future<AutocompleteResponse> find(String query) async {
|
||||||
Map<String, dynamic> getParameters = {
|
Map<String, dynamic> getParameters = {
|
||||||
"search": query,
|
'search': query,
|
||||||
"itemType": " ",
|
'itemType': ' ',
|
||||||
"itemId": " ",
|
'itemId': ' ',
|
||||||
"shareTypes[]": ["0"],
|
'shareTypes[]': ['0'],
|
||||||
"limit": "10",
|
'limit': '10',
|
||||||
};
|
};
|
||||||
|
|
||||||
Map<String, String> headers = {};
|
Map<String, String> headers = {};
|
||||||
headers.putIfAbsent("Accept", () => "application/json");
|
headers.putIfAbsent('Accept', () => 'application/json');
|
||||||
headers.putIfAbsent("OCS-APIRequest", () => "true");
|
headers.putIfAbsent('OCS-APIRequest', () => 'true');
|
||||||
|
|
||||||
Uri endpoint = Uri.https("${AccountData().buildHttpAuthString()}@${EndpointData().nextcloud().domain}", "${EndpointData().nextcloud().path}/ocs/v2.php/core/autocomplete/get", getParameters);
|
Uri endpoint = Uri.https('${AccountData().buildHttpAuthString()}@${EndpointData().nextcloud().domain}', '${EndpointData().nextcloud().path}/ocs/v2.php/core/autocomplete/get', getParameters);
|
||||||
|
|
||||||
Response response = await http.get(endpoint, headers: headers);
|
Response response = await http.get(endpoint, headers: headers);
|
||||||
if(response.statusCode != HttpStatus.ok) throw Exception("Api call failed with ${response.statusCode}: ${response.body}");
|
if(response.statusCode != HttpStatus.ok) throw Exception('Api call failed with ${response.statusCode}: ${response.body}');
|
||||||
String result = response.body;
|
String result = response.body;
|
||||||
return AutocompleteResponse.fromJson(jsonDecode(result)['ocs']);
|
return AutocompleteResponse.fromJson(jsonDecode(result)['ocs']);
|
||||||
}
|
}
|
||||||
|
@ -10,14 +10,14 @@ import 'fileSharingApiParams.dart';
|
|||||||
class FileSharingApi {
|
class FileSharingApi {
|
||||||
Future<void> share(FileSharingApiParams query) async {
|
Future<void> share(FileSharingApiParams query) async {
|
||||||
Map<String, String> headers = {};
|
Map<String, String> headers = {};
|
||||||
headers.putIfAbsent("Accept", () => "application/json");
|
headers.putIfAbsent('Accept', () => 'application/json');
|
||||||
headers.putIfAbsent("OCS-APIRequest", () => "true");
|
headers.putIfAbsent('OCS-APIRequest', () => 'true');
|
||||||
|
|
||||||
Uri endpoint = Uri.https("${AccountData().buildHttpAuthString()}@${EndpointData().nextcloud().domain}", "${EndpointData().nextcloud().path}/ocs/v2.php/apps/files_sharing/api/v1/shares", query.toJson().map((key, value) => MapEntry(key, value.toString())));
|
Uri endpoint = Uri.https('${AccountData().buildHttpAuthString()}@${EndpointData().nextcloud().domain}', '${EndpointData().nextcloud().path}/ocs/v2.php/apps/files_sharing/api/v1/shares', query.toJson().map((key, value) => MapEntry(key, value.toString())));
|
||||||
Response response = await http.post(endpoint, headers: headers);
|
Response response = await http.post(endpoint, headers: headers);
|
||||||
|
|
||||||
if(response.statusCode != HttpStatus.ok) {
|
if(response.statusCode != HttpStatus.ok) {
|
||||||
throw Exception("Api call failed with ${response.statusCode}: ${response.body}");
|
throw Exception('Api call failed with ${response.statusCode}: ${response.body}');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -11,7 +11,7 @@ class GetChat extends TalkApi<GetChatResponse> {
|
|||||||
String chatToken;
|
String chatToken;
|
||||||
|
|
||||||
GetChatParams params;
|
GetChatParams params;
|
||||||
GetChat(this.chatToken, this.params) : super("v1/chat/$chatToken", null, getParameters: params.toJson());
|
GetChat(this.chatToken, this.params) : super('v1/chat/$chatToken', null, getParameters: params.toJson());
|
||||||
|
|
||||||
@override
|
@override
|
||||||
assemble(String raw) {
|
assemble(String raw) {
|
||||||
|
@ -9,7 +9,7 @@ class GetChatCache extends RequestCache<GetChatResponse> {
|
|||||||
String chatToken;
|
String chatToken;
|
||||||
|
|
||||||
GetChatCache({required onUpdate, required this.chatToken}) : super(RequestCache.cacheNothing, onUpdate) {
|
GetChatCache({required onUpdate, required this.chatToken}) : super(RequestCache.cacheNothing, onUpdate) {
|
||||||
start("MarianumMobile", "nc-chat-$chatToken");
|
start('MarianumMobile', 'nc-chat-$chatToken');
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -61,21 +61,21 @@ class GetChatResponseObject {
|
|||||||
|
|
||||||
static GetChatResponseObject getDateDummy(int timestamp) {
|
static GetChatResponseObject getDateDummy(int timestamp) {
|
||||||
DateTime elementDate = DateTime.fromMillisecondsSinceEpoch(timestamp * 1000);
|
DateTime elementDate = DateTime.fromMillisecondsSinceEpoch(timestamp * 1000);
|
||||||
return getTextDummy(Jiffy.parseFromDateTime(elementDate).format(pattern: "dd.MM.yyyy"));
|
return getTextDummy(Jiffy.parseFromDateTime(elementDate).format(pattern: 'dd.MM.yyyy'));
|
||||||
}
|
}
|
||||||
|
|
||||||
static GetChatResponseObject getTextDummy(String text) {
|
static GetChatResponseObject getTextDummy(String text) {
|
||||||
return GetChatResponseObject(
|
return GetChatResponseObject(
|
||||||
0,
|
0,
|
||||||
"",
|
'',
|
||||||
GetRoomResponseObjectMessageActorType.user,
|
GetRoomResponseObjectMessageActorType.user,
|
||||||
"",
|
'',
|
||||||
"",
|
'',
|
||||||
0,
|
0,
|
||||||
"",
|
'',
|
||||||
GetRoomResponseObjectMessageType.system,
|
GetRoomResponseObjectMessageType.system,
|
||||||
false,
|
false,
|
||||||
"",
|
'',
|
||||||
text,
|
text,
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
@ -113,10 +113,10 @@ class RichObjectString {
|
|||||||
}
|
}
|
||||||
|
|
||||||
enum RichObjectStringObjectType {
|
enum RichObjectStringObjectType {
|
||||||
@JsonValue("user") user,
|
@JsonValue('user') user,
|
||||||
@JsonValue("group") group,
|
@JsonValue('group') group,
|
||||||
@JsonValue("file") file,
|
@JsonValue('file') file,
|
||||||
@JsonValue("guest") guest,
|
@JsonValue('guest') guest,
|
||||||
@JsonValue("highlight") highlight,
|
@JsonValue('highlight') highlight,
|
||||||
@JsonValue("talk-poll") talkPoll,
|
@JsonValue('talk-poll') talkPoll,
|
||||||
}
|
}
|
@ -6,7 +6,7 @@ class RichObjectStringProcessor {
|
|||||||
if(data == null) return message;
|
if(data == null) return message;
|
||||||
|
|
||||||
data.forEach((key, value) {
|
data.forEach((key, value) {
|
||||||
message = message.replaceAll(RegExp("{$key}"), value.name);
|
message = message.replaceAll(RegExp('{$key}'), value.name);
|
||||||
});
|
});
|
||||||
|
|
||||||
return message;
|
return message;
|
||||||
|
@ -7,7 +7,7 @@ import 'createRoomParams.dart';
|
|||||||
class CreateRoom extends TalkApi {
|
class CreateRoom extends TalkApi {
|
||||||
CreateRoomParams params;
|
CreateRoomParams params;
|
||||||
|
|
||||||
CreateRoom(this.params) : super("v4/room", params);
|
CreateRoom(this.params) : super('v4/room', params);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
assemble(String raw) {
|
assemble(String raw) {
|
||||||
|
@ -7,7 +7,7 @@ import '../talkApi.dart';
|
|||||||
class DeleteMessage extends TalkApi {
|
class DeleteMessage extends TalkApi {
|
||||||
String chatToken;
|
String chatToken;
|
||||||
int messageId;
|
int messageId;
|
||||||
DeleteMessage(this.chatToken, this.messageId) : super("v1/chat/$chatToken/$messageId", null);
|
DeleteMessage(this.chatToken, this.messageId) : super('v1/chat/$chatToken/$messageId', null);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
assemble(String raw) {
|
assemble(String raw) {
|
||||||
|
@ -8,7 +8,7 @@ import 'deleteReactMessageParams.dart';
|
|||||||
class DeleteReactMessage extends TalkApi {
|
class DeleteReactMessage extends TalkApi {
|
||||||
String chatToken;
|
String chatToken;
|
||||||
int messageId;
|
int messageId;
|
||||||
DeleteReactMessage({required this.chatToken, required this.messageId, required DeleteReactMessageParams params}) : super("v1/reaction/$chatToken/$messageId", params);
|
DeleteReactMessage({required this.chatToken, required this.messageId, required DeleteReactMessageParams params}) : super('v1/reaction/$chatToken/$messageId', params);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
assemble(String raw) {
|
assemble(String raw) {
|
||||||
|
@ -7,7 +7,7 @@ import 'getParticipantsResponse.dart';
|
|||||||
|
|
||||||
class GetParticipants extends TalkApi<GetParticipantsResponse> {
|
class GetParticipants extends TalkApi<GetParticipantsResponse> {
|
||||||
String token;
|
String token;
|
||||||
GetParticipants(this.token) : super("v4/room/$token/participants", null);
|
GetParticipants(this.token) : super('v4/room/$token/participants', null);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
GetParticipantsResponse assemble(String raw) {
|
GetParticipantsResponse assemble(String raw) {
|
||||||
|
@ -8,7 +8,7 @@ class GetParticipantsCache extends RequestCache<GetParticipantsResponse> {
|
|||||||
String chatToken;
|
String chatToken;
|
||||||
|
|
||||||
GetParticipantsCache({required onUpdate, required this.chatToken}) : super(RequestCache.cacheNothing, onUpdate) {
|
GetParticipantsCache({required onUpdate, required this.chatToken}) : super(RequestCache.cacheNothing, onUpdate) {
|
||||||
start("MarianumMobile", "nc-chat-participants-$chatToken");
|
start('MarianumMobile', 'nc-chat-participants-$chatToken');
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -10,7 +10,7 @@ import 'getReactionsResponse.dart';
|
|||||||
class GetReactions extends TalkApi<GetReactionsResponse> {
|
class GetReactions extends TalkApi<GetReactionsResponse> {
|
||||||
String chatToken;
|
String chatToken;
|
||||||
int messageId;
|
int messageId;
|
||||||
GetReactions({required this.chatToken, required this.messageId}) : super("v1/reaction/$chatToken/$messageId", null);
|
GetReactions({required this.chatToken, required this.messageId}) : super('v1/reaction/$chatToken/$messageId', null);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
assemble(String raw) {
|
assemble(String raw) {
|
||||||
|
@ -28,6 +28,6 @@ class GetReactionsResponseObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
enum GetReactionsResponseObjectActorType {
|
enum GetReactionsResponseObjectActorType {
|
||||||
@JsonValue("guests") guests,
|
@JsonValue('guests') guests,
|
||||||
@JsonValue("users") users,
|
@JsonValue('users') users,
|
||||||
}
|
}
|
@ -6,7 +6,7 @@ import '../talkApi.dart';
|
|||||||
class LeaveRoom extends TalkApi {
|
class LeaveRoom extends TalkApi {
|
||||||
String chatToken;
|
String chatToken;
|
||||||
|
|
||||||
LeaveRoom(this.chatToken) : super("v4/room/$chatToken/participants/self", null);
|
LeaveRoom(this.chatToken) : super('v4/room/$chatToken/participants/self', null);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
assemble(String raw) {
|
assemble(String raw) {
|
||||||
|
@ -8,7 +8,7 @@ import 'reactMessageParams.dart';
|
|||||||
class ReactMessage extends TalkApi {
|
class ReactMessage extends TalkApi {
|
||||||
String chatToken;
|
String chatToken;
|
||||||
int messageId;
|
int messageId;
|
||||||
ReactMessage({required this.chatToken, required this.messageId, required ReactMessageParams params}) : super("v1/reaction/$chatToken/$messageId", params);
|
ReactMessage({required this.chatToken, required this.messageId, required ReactMessageParams params}) : super('v1/reaction/$chatToken/$messageId', params);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
assemble(String raw) {
|
assemble(String raw) {
|
||||||
|
@ -9,7 +9,7 @@ import 'getRoomResponse.dart';
|
|||||||
|
|
||||||
class GetRoom extends TalkApi<GetRoomResponse> {
|
class GetRoom extends TalkApi<GetRoomResponse> {
|
||||||
GetRoomParams params;
|
GetRoomParams params;
|
||||||
GetRoom(this.params) : super("v4/room", null, getParameters: params.toJson());
|
GetRoom(this.params) : super('v4/room', null, getParameters: params.toJson());
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ import 'getRoomResponse.dart';
|
|||||||
|
|
||||||
class GetRoomCache extends RequestCache<GetRoomResponse> {
|
class GetRoomCache extends RequestCache<GetRoomResponse> {
|
||||||
GetRoomCache({onUpdate, renew}) : super(RequestCache.cacheMinute, onUpdate, renew: renew) {
|
GetRoomCache({onUpdate, renew}) : super(RequestCache.cacheMinute, onUpdate, renew: renew) {
|
||||||
start("MarianumMobile", "nc-rooms");
|
start('MarianumMobile', 'nc-rooms');
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -19,10 +19,10 @@ class GetRoomResponse extends ApiResponse {
|
|||||||
final buffer = StringBuffer();
|
final buffer = StringBuffer();
|
||||||
|
|
||||||
if(favoritesToTop) {
|
if(favoritesToTop) {
|
||||||
buffer.write(chat.isFavorite ? "b" : "a");
|
buffer.write(chat.isFavorite ? 'b' : 'a');
|
||||||
}
|
}
|
||||||
if(unreadToTop) {
|
if(unreadToTop) {
|
||||||
buffer.write(chat.unreadMessages > 0 ? "b" : "a");
|
buffer.write(chat.unreadMessages > 0 ? 'b' : 'a');
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer.write(chat.lastActivity);
|
buffer.write(chat.lastActivity);
|
||||||
@ -152,16 +152,16 @@ enum GetRoomResponseObjectParticipantNotificationLevel {
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
enum GetRoomResponseObjectMessageActorType {
|
enum GetRoomResponseObjectMessageActorType {
|
||||||
@JsonValue("deleted_users") deletedUsers,
|
@JsonValue('deleted_users') deletedUsers,
|
||||||
@JsonValue("users") user,
|
@JsonValue('users') user,
|
||||||
@JsonValue("guests") guest,
|
@JsonValue('guests') guest,
|
||||||
@JsonValue("bots") bot,
|
@JsonValue('bots') bot,
|
||||||
@JsonValue("bridged") bridge,
|
@JsonValue('bridged') bridge,
|
||||||
}
|
}
|
||||||
|
|
||||||
enum GetRoomResponseObjectMessageType {
|
enum GetRoomResponseObjectMessageType {
|
||||||
@JsonValue("comment") comment,
|
@JsonValue('comment') comment,
|
||||||
@JsonValue("comment_deleted") deletedComment,
|
@JsonValue('comment_deleted') deletedComment,
|
||||||
@JsonValue("system") system,
|
@JsonValue('system') system,
|
||||||
@JsonValue("command") command,
|
@JsonValue('command') command,
|
||||||
}
|
}
|
@ -7,7 +7,7 @@ import 'sendMessageParams.dart';
|
|||||||
|
|
||||||
class SendMessage extends TalkApi {
|
class SendMessage extends TalkApi {
|
||||||
String chatToken;
|
String chatToken;
|
||||||
SendMessage(this.chatToken, SendMessageParams params) : super("v1/chat/$chatToken", params);
|
SendMessage(this.chatToken, SendMessageParams params) : super('v1/chat/$chatToken', params);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
assemble(String raw) {
|
assemble(String raw) {
|
||||||
|
@ -8,7 +8,7 @@ class SetFavorite extends TalkApi {
|
|||||||
String chatToken;
|
String chatToken;
|
||||||
bool favoriteState;
|
bool favoriteState;
|
||||||
|
|
||||||
SetFavorite(this.chatToken, this.favoriteState) : super("v4/room/$chatToken/favorite", null);
|
SetFavorite(this.chatToken, this.favoriteState) : super('v4/room/$chatToken/favorite', null);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
assemble(String raw) {
|
assemble(String raw) {
|
||||||
|
@ -10,7 +10,7 @@ class SetReadMarker extends TalkApi {
|
|||||||
bool readState;
|
bool readState;
|
||||||
SetReadMarkerParams? setReadMarkerParams;
|
SetReadMarkerParams? setReadMarkerParams;
|
||||||
|
|
||||||
SetReadMarker(this.chatToken, this.readState, {this.setReadMarkerParams}) : super("v1/chat/$chatToken/read", null, getParameters: setReadMarkerParams?.toJson()) {
|
SetReadMarker(this.chatToken, this.readState, {this.setReadMarkerParams}) : super('v1/chat/$chatToken/read', null, getParameters: setReadMarkerParams?.toJson()) {
|
||||||
if(readState) assert(setReadMarkerParams?.lastReadMessage != null);
|
if(readState) assert(setReadMarkerParams?.lastReadMessage != null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,21 +34,21 @@ abstract class TalkApi<T extends ApiResponse?> extends ApiRequest {
|
|||||||
getParameters?.update(key, (value) => value.toString());
|
getParameters?.update(key, (value) => value.toString());
|
||||||
});
|
});
|
||||||
|
|
||||||
Uri endpoint = Uri.https("${AccountData().buildHttpAuthString()}@${EndpointData().nextcloud().domain}", "${EndpointData().nextcloud().path}/ocs/v2.php/apps/spreed/api/$path", getParameters);
|
Uri endpoint = Uri.https('${AccountData().buildHttpAuthString()}@${EndpointData().nextcloud().domain}', '${EndpointData().nextcloud().path}/ocs/v2.php/apps/spreed/api/$path', getParameters);
|
||||||
|
|
||||||
headers ??= {};
|
headers ??= {};
|
||||||
headers?.putIfAbsent("Accept", () => "application/json");
|
headers?.putIfAbsent('Accept', () => 'application/json');
|
||||||
headers?.putIfAbsent("OCS-APIRequest", () => "true");
|
headers?.putIfAbsent('OCS-APIRequest', () => 'true');
|
||||||
|
|
||||||
http.Response? data;
|
http.Response? data;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
data = await request(endpoint, body, headers);
|
data = await request(endpoint, body, headers);
|
||||||
if(data == null) throw Exception("No response Data");
|
if(data == null) throw Exception('No response Data');
|
||||||
if(data.statusCode >= 400 || data.statusCode < 200) throw Exception("Response status code '${data.statusCode}' might indicate an error");
|
if(data.statusCode >= 400 || data.statusCode < 200) throw Exception("Response status code '${data.statusCode}' might indicate an error");
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
log(e.toString());
|
log(e.toString());
|
||||||
throw ApiError("Request $endpoint could not be dispatched: ${e.toString()}");
|
throw ApiError('Request $endpoint could not be dispatched: ${e.toString()}');
|
||||||
}
|
}
|
||||||
//dynamic jsonData = jsonDecode(data.body);
|
//dynamic jsonData = jsonDecode(data.body);
|
||||||
|
|
||||||
@ -59,10 +59,10 @@ abstract class TalkApi<T extends ApiResponse?> extends ApiRequest {
|
|||||||
return assembled;
|
return assembled;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// TODO report error
|
// TODO report error
|
||||||
log("Error assembling Talk API ${T.toString()} message: ${e.toString()} response on ${endpoint.path} with request body: $body and request headers: ${headers.toString()}");
|
log('Error assembling Talk API ${T.toString()} message: ${e.toString()} response on ${endpoint.path} with request body: $body and request headers: ${headers.toString()}');
|
||||||
}
|
}
|
||||||
|
|
||||||
throw Exception("Error assembling Talk API response");
|
throw Exception('Error assembling Talk API response');
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -7,6 +7,6 @@ class TalkError {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return "Talk - $status - ($code): $message";
|
return 'Talk - $status - ($code): $message';
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -24,7 +24,7 @@ class CacheableFile {
|
|||||||
CacheableFile.fromDavFile(WebDavFile file) {
|
CacheableFile.fromDavFile(WebDavFile file) {
|
||||||
path = file.path.path;
|
path = file.path.path;
|
||||||
isDirectory = file.isDirectory;
|
isDirectory = file.isDirectory;
|
||||||
name = file.isDirectory ? file.name : file.path.path.split("/").last;
|
name = file.isDirectory ? file.name : file.path.path.split('/').last;
|
||||||
mimeType = file.mimeType;
|
mimeType = file.mimeType;
|
||||||
size = file.size;
|
size = file.size;
|
||||||
eTag = file.etag;
|
eTag = file.etag;
|
||||||
|
@ -14,7 +14,7 @@ class ListFiles extends WebdavApi<ListFilesParams> {
|
|||||||
@override
|
@override
|
||||||
Future<ListFilesResponse> run() async {
|
Future<ListFilesResponse> run() async {
|
||||||
List<WebDavFile> davFiles = (await (await WebdavApi.webdav).propfind(PathUri.parse(params.path))).toWebDavFiles();
|
List<WebDavFile> davFiles = (await (await WebdavApi.webdav).propfind(PathUri.parse(params.path))).toWebDavFiles();
|
||||||
Set<CacheableFile> files = davFiles.map((e) => CacheableFile.fromDavFile(e)).toSet();
|
Set<CacheableFile> files = davFiles.map(CacheableFile.fromDavFile).toSet();
|
||||||
|
|
||||||
// webdav handles subdirectories wrong, this is a fix
|
// webdav handles subdirectories wrong, this is a fix
|
||||||
// currently this fix is not needed anymore
|
// currently this fix is not needed anymore
|
||||||
@ -26,7 +26,7 @@ class ListFiles extends WebdavApi<ListFilesParams> {
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
// somehow the current working folder is also listed, it is filtered here.
|
// somehow the current working folder is also listed, it is filtered here.
|
||||||
files.removeWhere((element) => element.path == "/${params.path}/" || element.path == "/");
|
files.removeWhere((element) => element.path == '/${params.path}/' || element.path == '/');
|
||||||
|
|
||||||
return ListFilesResponse(files);
|
return ListFilesResponse(files);
|
||||||
}
|
}
|
||||||
|
@ -10,9 +10,9 @@ class ListFilesCache extends RequestCache<ListFilesResponse> {
|
|||||||
String path;
|
String path;
|
||||||
|
|
||||||
ListFilesCache({required onUpdate, required this.path}) : super(RequestCache.cacheNothing, onUpdate) {
|
ListFilesCache({required onUpdate, required this.path}) : super(RequestCache.cacheNothing, onUpdate) {
|
||||||
var bytes = utf8.encode("MarianumMobile-$path");
|
var bytes = utf8.encode('MarianumMobile-$path');
|
||||||
String cacheName = md5.convert(bytes).toString();
|
String cacheName = md5.convert(bytes).toString();
|
||||||
start("MarianumMobile", "wd-folder-$cacheName");
|
start('MarianumMobile', 'wd-folder-$cacheName');
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -35,7 +35,7 @@ class ListFilesResponse extends ApiResponse {
|
|||||||
|
|
||||||
switch(sortOption) {
|
switch(sortOption) {
|
||||||
case SortOption.date:
|
case SortOption.date:
|
||||||
buffer.write(Jiffy.parseFromMillisecondsSinceEpoch(file.modifiedAt?.millisecondsSinceEpoch ?? 0).format(pattern: "yyyyMMddhhmmss"));
|
buffer.write(Jiffy.parseFromMillisecondsSinceEpoch(file.modifiedAt?.millisecondsSinceEpoch ?? 0).format(pattern: 'yyyyMMddhhmmss'));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SortOption.name:
|
case SortOption.name:
|
||||||
|
@ -18,10 +18,10 @@ abstract class WebdavApi<T> extends ApiRequest {
|
|||||||
static Future<String> webdavConnectString = buildWebdavConnectString();
|
static Future<String> webdavConnectString = buildWebdavConnectString();
|
||||||
|
|
||||||
static Future<WebDavClient> establishWebdavConnection() async {
|
static Future<WebDavClient> establishWebdavConnection() async {
|
||||||
return NextcloudClient(Uri.parse("https://${EndpointData().nextcloud().full()}"), password: AccountData().getPassword(), loginName: AccountData().getUsername()).webdav;
|
return NextcloudClient(Uri.parse('https://${EndpointData().nextcloud().full()}'), password: AccountData().getPassword(), loginName: AccountData().getUsername()).webdav;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<String> buildWebdavConnectString() async {
|
static Future<String> buildWebdavConnectString() async {
|
||||||
return "https://${AccountData().buildHttpAuthString()}@${EndpointData().nextcloud().full()}/remote.php/dav/files/${AccountData().getUsername()}/";
|
return 'https://${AccountData().buildHttpAuthString()}@${EndpointData().nextcloud().full()}/remote.php/dav/files/${AccountData().getUsername()}/';
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -6,7 +6,7 @@ import '../../mhslApi.dart';
|
|||||||
import 'getBreakersResponse.dart';
|
import 'getBreakersResponse.dart';
|
||||||
|
|
||||||
class GetBreakers extends MhslApi<GetBreakersResponse> {
|
class GetBreakers extends MhslApi<GetBreakersResponse> {
|
||||||
GetBreakers() : super("breaker/");
|
GetBreakers() : super('breaker/');
|
||||||
|
|
||||||
@override
|
@override
|
||||||
GetBreakersResponse assemble(String raw) {
|
GetBreakersResponse assemble(String raw) {
|
||||||
|
@ -6,7 +6,7 @@ import 'getBreakersResponse.dart';
|
|||||||
|
|
||||||
class GetBreakersCache extends RequestCache<GetBreakersResponse> {
|
class GetBreakersCache extends RequestCache<GetBreakersResponse> {
|
||||||
GetBreakersCache({onUpdate, renew}) : super(RequestCache.cacheMinute, onUpdate, renew: renew) {
|
GetBreakersCache({onUpdate, renew}) : super(RequestCache.cacheMinute, onUpdate, renew: renew) {
|
||||||
start("MarianumMobile", "breakers");
|
start('MarianumMobile', 'breakers');
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -28,9 +28,9 @@ class GetBreakersReponseObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
enum BreakerArea {
|
enum BreakerArea {
|
||||||
@JsonValue("GLOBAL") global,
|
@JsonValue('GLOBAL') global,
|
||||||
@JsonValue("TIMETABLE") timetable,
|
@JsonValue('TIMETABLE') timetable,
|
||||||
@JsonValue("TALK") talk,
|
@JsonValue('TALK') talk,
|
||||||
@JsonValue("FILES") files,
|
@JsonValue('FILES') files,
|
||||||
@JsonValue("MORE") more,
|
@JsonValue('MORE') more,
|
||||||
}
|
}
|
@ -9,11 +9,11 @@ import 'getCustomTimetableEventResponse.dart';
|
|||||||
|
|
||||||
class GetCustomTimetableEvent extends MhslApi<GetCustomTimetableEventResponse> {
|
class GetCustomTimetableEvent extends MhslApi<GetCustomTimetableEventResponse> {
|
||||||
GetCustomTimetableEventParams params;
|
GetCustomTimetableEventParams params;
|
||||||
GetCustomTimetableEvent(this.params) : super("server/timetable/customEvents?user=${params.user}");
|
GetCustomTimetableEvent(this.params) : super('server/timetable/customEvents?user=${params.user}');
|
||||||
|
|
||||||
@override
|
@override
|
||||||
GetCustomTimetableEventResponse assemble(String raw) {
|
GetCustomTimetableEventResponse assemble(String raw) {
|
||||||
return GetCustomTimetableEventResponse.fromJson({"events": jsonDecode(raw)});
|
return GetCustomTimetableEventResponse.fromJson({'events': jsonDecode(raw)});
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -9,7 +9,7 @@ class GetCustomTimetableEventCache extends RequestCache<GetCustomTimetableEventR
|
|||||||
GetCustomTimetableEventParams params;
|
GetCustomTimetableEventParams params;
|
||||||
|
|
||||||
GetCustomTimetableEventCache(this.params, {onUpdate, renew}) : super(RequestCache.cacheMinute, onUpdate, renew: renew) {
|
GetCustomTimetableEventCache(this.params, {onUpdate, renew}) : super(RequestCache.cacheMinute, onUpdate, renew: renew) {
|
||||||
start("MarianumMobile", "customTimetableEvents");
|
start('MarianumMobile', 'customTimetableEvents');
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -6,7 +6,7 @@ import '../../mhslApi.dart';
|
|||||||
import 'getMessagesResponse.dart';
|
import 'getMessagesResponse.dart';
|
||||||
|
|
||||||
class GetMessages extends MhslApi<GetMessagesResponse> {
|
class GetMessages extends MhslApi<GetMessagesResponse> {
|
||||||
GetMessages() : super("message/messages.json");
|
GetMessages() : super('message/messages.json');
|
||||||
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -6,7 +6,7 @@ import 'getMessagesResponse.dart';
|
|||||||
|
|
||||||
class GetMessagesCache extends RequestCache<GetMessagesResponse> {
|
class GetMessagesCache extends RequestCache<GetMessagesResponse> {
|
||||||
GetMessagesCache({onUpdate, renew}) : super(RequestCache.cacheMinute, onUpdate, renew: renew) {
|
GetMessagesCache({onUpdate, renew}) : super(RequestCache.cacheMinute, onUpdate, renew: renew) {
|
||||||
start("MarianumMobile", "message");
|
start('MarianumMobile', 'message');
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -15,21 +15,21 @@ abstract class MhslApi<T> extends ApiRequest {
|
|||||||
T assemble(String raw);
|
T assemble(String raw);
|
||||||
|
|
||||||
Future<T> run() async {
|
Future<T> run() async {
|
||||||
Uri endpoint = Uri.parse("https://mhsl.eu/marianum/marianummobile/$subpath");
|
Uri endpoint = Uri.parse('https://mhsl.eu/marianum/marianummobile/$subpath');
|
||||||
|
|
||||||
http.Response? data = await request(endpoint);
|
http.Response? data = await request(endpoint);
|
||||||
if(data == null) {
|
if(data == null) {
|
||||||
throw ApiError("Request could not be dispatched!");
|
throw ApiError('Request could not be dispatched!');
|
||||||
}
|
}
|
||||||
|
|
||||||
if(data.statusCode > 299) {
|
if(data.statusCode > 299) {
|
||||||
throw ApiError("Non 200 Status code from mhsl services: $subpath: ${data.statusCode}");
|
throw ApiError('Non 200 Status code from mhsl services: $subpath: ${data.statusCode}');
|
||||||
}
|
}
|
||||||
|
|
||||||
return assemble(utf8.decode(data.bodyBytes));
|
return assemble(utf8.decode(data.bodyBytes));
|
||||||
}
|
}
|
||||||
|
|
||||||
static String dateTimeToJson(DateTime time) => Jiffy.parseFromDateTime(time).format(pattern: "yyyy-MM-dd HH:mm:ss");
|
static String dateTimeToJson(DateTime time) => Jiffy.parseFromDateTime(time).format(pattern: 'yyyy-MM-dd HH:mm:ss');
|
||||||
static DateTime dateTimeFromJson(String time) => DateTime.parse(time);
|
static DateTime dateTimeFromJson(String time) => DateTime.parse(time);
|
||||||
|
|
||||||
}
|
}
|
@ -9,7 +9,7 @@ import 'notifyRegisterParams.dart';
|
|||||||
|
|
||||||
class NotifyRegister extends MhslApi<void> {
|
class NotifyRegister extends MhslApi<void> {
|
||||||
NotifyRegisterParams params;
|
NotifyRegisterParams params;
|
||||||
NotifyRegister(this.params) : super("notify/register/");
|
NotifyRegister(this.params) : super('notify/register/');
|
||||||
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -12,7 +12,7 @@ import 'updateUserIndexParams.dart';
|
|||||||
|
|
||||||
class UpdateUserIndex extends MhslApi<void> {
|
class UpdateUserIndex extends MhslApi<void> {
|
||||||
UpdateUserIndexParams params;
|
UpdateUserIndexParams params;
|
||||||
UpdateUserIndex(this.params) : super("server/userIndex/update");
|
UpdateUserIndex(this.params) : super('server/userIndex/update');
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void assemble(String raw) {}
|
void assemble(String raw) {}
|
||||||
@ -20,7 +20,7 @@ class UpdateUserIndex extends MhslApi<void> {
|
|||||||
@override
|
@override
|
||||||
Future<http.Response> request(Uri uri) {
|
Future<http.Response> request(Uri uri) {
|
||||||
String data = jsonEncode(params.toJson());
|
String data = jsonEncode(params.toJson());
|
||||||
log("Updating userindex:\n $data");
|
log('Updating userindex:\n $data');
|
||||||
return http.post(uri, body: data);
|
return http.post(uri, body: data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,8 +36,8 @@ abstract class RequestCache<T extends ApiResponse?> {
|
|||||||
onUpdate(newValue as T);
|
onUpdate(newValue as T);
|
||||||
|
|
||||||
Localstore.instance.collection(file).doc(document).set({
|
Localstore.instance.collection(file).doc(document).set({
|
||||||
"json": jsonEncode(newValue),
|
'json': jsonEncode(newValue),
|
||||||
"lastupdate": DateTime.now().millisecondsSinceEpoch
|
'lastupdate': DateTime.now().millisecondsSinceEpoch
|
||||||
});
|
});
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
log("Error while fetching/ parsing. Raw server response: ${newValue?.rawResponse.body ?? "no response"}");
|
log("Error while fetching/ parsing. Raw server response: ${newValue?.rawResponse.body ?? "no response"}");
|
||||||
|
@ -9,7 +9,7 @@ import 'authenticateResponse.dart';
|
|||||||
class Authenticate extends WebuntisApi {
|
class Authenticate extends WebuntisApi {
|
||||||
AuthenticateParams param;
|
AuthenticateParams param;
|
||||||
|
|
||||||
Authenticate(this.param) : super("authenticate", param, authenticatedResponse: false);
|
Authenticate(this.param) : super('authenticate', param, authenticatedResponse: false);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<AuthenticateResponse> run() async {
|
Future<AuthenticateResponse> run() async {
|
||||||
|
@ -4,7 +4,7 @@ import '../../webuntisApi.dart';
|
|||||||
import 'getHolidaysResponse.dart';
|
import 'getHolidaysResponse.dart';
|
||||||
|
|
||||||
class GetHolidays extends WebuntisApi {
|
class GetHolidays extends WebuntisApi {
|
||||||
GetHolidays() : super("getHolidays", null);
|
GetHolidays() : super('getHolidays', null);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<GetHolidaysResponse> run() async {
|
Future<GetHolidaysResponse> run() async {
|
||||||
|
@ -6,7 +6,7 @@ import 'getHolidaysResponse.dart';
|
|||||||
|
|
||||||
class GetHolidaysCache extends RequestCache<GetHolidaysResponse> {
|
class GetHolidaysCache extends RequestCache<GetHolidaysResponse> {
|
||||||
GetHolidaysCache({onUpdate}) : super(RequestCache.cacheDay, onUpdate) {
|
GetHolidaysCache({onUpdate}) : super(RequestCache.cacheDay, onUpdate) {
|
||||||
start("MarianumMobile", "wu-holidays");
|
start('MarianumMobile', 'wu-holidays');
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -5,7 +5,7 @@ import '../../webuntisApi.dart';
|
|||||||
import 'getRoomsResponse.dart';
|
import 'getRoomsResponse.dart';
|
||||||
|
|
||||||
class GetRooms extends WebuntisApi {
|
class GetRooms extends WebuntisApi {
|
||||||
GetRooms() : super("getRooms", null);
|
GetRooms() : super('getRooms', null);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<GetRoomsResponse> run() async {
|
Future<GetRoomsResponse> run() async {
|
||||||
@ -14,10 +14,10 @@ class GetRooms extends WebuntisApi {
|
|||||||
return finalize(GetRoomsResponse.fromJson(jsonDecode(rawAnswer)));
|
return finalize(GetRoomsResponse.fromJson(jsonDecode(rawAnswer)));
|
||||||
} catch(e, trace) {
|
} catch(e, trace) {
|
||||||
log(trace.toString());
|
log(trace.toString());
|
||||||
log("Failed to parse getRoom data with server response: $rawAnswer");
|
log('Failed to parse getRoom data with server response: $rawAnswer');
|
||||||
}
|
}
|
||||||
|
|
||||||
throw Exception("Failed to parse getRoom server response: $rawAnswer");
|
throw Exception('Failed to parse getRoom server response: $rawAnswer');
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -6,7 +6,7 @@ import 'getRoomsResponse.dart';
|
|||||||
|
|
||||||
class GetRoomsCache extends RequestCache<GetRoomsResponse> {
|
class GetRoomsCache extends RequestCache<GetRoomsResponse> {
|
||||||
GetRoomsCache({onUpdate}) : super(RequestCache.cacheHour, onUpdate) {
|
GetRoomsCache({onUpdate}) : super(RequestCache.cacheHour, onUpdate) {
|
||||||
start("MarianumMobile", "wu-rooms");
|
start('MarianumMobile', 'wu-rooms');
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -4,7 +4,7 @@ import '../../webuntisApi.dart';
|
|||||||
import 'getSubjectsResponse.dart';
|
import 'getSubjectsResponse.dart';
|
||||||
|
|
||||||
class GetSubjects extends WebuntisApi {
|
class GetSubjects extends WebuntisApi {
|
||||||
GetSubjects() : super("getSubjects", null);
|
GetSubjects() : super('getSubjects', null);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<GetSubjectsResponse> run() async {
|
Future<GetSubjectsResponse> run() async {
|
||||||
|
@ -6,7 +6,7 @@ import 'getSubjectsResponse.dart';
|
|||||||
|
|
||||||
class GetSubjectsCache extends RequestCache<GetSubjectsResponse> {
|
class GetSubjectsCache extends RequestCache<GetSubjectsResponse> {
|
||||||
GetSubjectsCache({onUpdate}) : super(RequestCache.cacheHour, onUpdate) {
|
GetSubjectsCache({onUpdate}) : super(RequestCache.cacheHour, onUpdate) {
|
||||||
start("MarianumMobile", "wu-subjects");
|
start('MarianumMobile', 'wu-subjects');
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -7,7 +7,7 @@ import 'getTimetableResponse.dart';
|
|||||||
class GetTimetable extends WebuntisApi {
|
class GetTimetable extends WebuntisApi {
|
||||||
GetTimetableParams params;
|
GetTimetableParams params;
|
||||||
|
|
||||||
GetTimetable(this.params) : super("getTimetable", params);
|
GetTimetable(this.params) : super('getTimetable', params);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<GetTimetableResponse> run() async {
|
Future<GetTimetableResponse> run() async {
|
||||||
|
@ -11,7 +11,7 @@ class GetTimetableCache extends RequestCache<GetTimetableResponse> {
|
|||||||
int enddate;
|
int enddate;
|
||||||
|
|
||||||
GetTimetableCache({required onUpdate, onError, required this.startdate, required this.enddate}) : super(RequestCache.cacheMinute, onUpdate, onError: onError) {
|
GetTimetableCache({required onUpdate, onError, required this.startdate, required this.enddate}) : super(RequestCache.cacheMinute, onUpdate, onError: onError) {
|
||||||
start("MarianumMobile", "wu-timetable-$startdate-$enddate");
|
start('MarianumMobile', 'wu-timetable-$startdate-$enddate');
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -67,10 +67,10 @@ class GetTimetableParamsOptions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
enum GetTimetableParamsOptionsFields {
|
enum GetTimetableParamsOptionsFields {
|
||||||
@JsonValue("id") id,
|
@JsonValue('id') id,
|
||||||
@JsonValue("name") name,
|
@JsonValue('name') name,
|
||||||
@JsonValue("longname") longname,
|
@JsonValue('longname') longname,
|
||||||
@JsonValue("externalkey") externalkey;
|
@JsonValue('externalkey') externalkey;
|
||||||
|
|
||||||
static List<GetTimetableParamsOptionsFields> all = [id, name, longname, externalkey];
|
static List<GetTimetableParamsOptionsFields> all = [id, name, longname, externalkey];
|
||||||
}
|
}
|
||||||
@ -88,7 +88,7 @@ class GetTimetableParamsOptionsElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
enum GetTimetableParamsOptionsElementKeyType {
|
enum GetTimetableParamsOptionsElementKeyType {
|
||||||
@JsonValue("id") id,
|
@JsonValue('id') id,
|
||||||
@JsonValue("name") name,
|
@JsonValue('name') name,
|
||||||
@JsonValue("externalkey") externalkey
|
@JsonValue('externalkey') externalkey
|
||||||
}
|
}
|
@ -9,7 +9,7 @@ import 'queries/authenticate/authenticate.dart';
|
|||||||
import 'webuntisError.dart';
|
import 'webuntisError.dart';
|
||||||
|
|
||||||
abstract class WebuntisApi extends ApiRequest {
|
abstract class WebuntisApi extends ApiRequest {
|
||||||
Uri endpoint = Uri.parse("https://${EndpointData().webuntis().full()}/WebUntis/jsonrpc.do?school=marianum-fulda");
|
Uri endpoint = Uri.parse('https://${EndpointData().webuntis().full()}/WebUntis/jsonrpc.do?school=marianum-fulda');
|
||||||
String method;
|
String method;
|
||||||
ApiParams? genericParam;
|
ApiParams? genericParam;
|
||||||
http.Response? response;
|
http.Response? response;
|
||||||
@ -22,11 +22,11 @@ abstract class WebuntisApi extends ApiRequest {
|
|||||||
Future<String> query(WebuntisApi untis) async {
|
Future<String> query(WebuntisApi untis) async {
|
||||||
String query = '{"id":"ID","method":"$method","params":${untis._body()},"jsonrpc":"2.0"}';
|
String query = '{"id":"ID","method":"$method","params":${untis._body()},"jsonrpc":"2.0"}';
|
||||||
|
|
||||||
String sessionId = "0";
|
String sessionId = '0';
|
||||||
if(authenticatedResponse) {
|
if(authenticatedResponse) {
|
||||||
sessionId = (await Authenticate.getSession()).sessionId;
|
sessionId = (await Authenticate.getSession()).sessionId;
|
||||||
}
|
}
|
||||||
http.Response data = await post(query, {"Cookie": "JSESSIONID=$sessionId"});
|
http.Response data = await post(query, {'Cookie': 'JSESSIONID=$sessionId'});
|
||||||
response = data;
|
response = data;
|
||||||
|
|
||||||
dynamic jsonData = jsonDecode(data.body);
|
dynamic jsonData = jsonDecode(data.body);
|
||||||
@ -49,7 +49,7 @@ abstract class WebuntisApi extends ApiRequest {
|
|||||||
Future<ApiResponse> run();
|
Future<ApiResponse> run();
|
||||||
|
|
||||||
String _body() {
|
String _body() {
|
||||||
return genericParam == null ? "{}" : jsonEncode(genericParam);
|
return genericParam == null ? '{}' : jsonEncode(genericParam);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<http.Response> post(String data, Map<String, String>? headers) async {
|
Future<http.Response> post(String data, Map<String, String>? headers) async {
|
||||||
@ -57,7 +57,7 @@ abstract class WebuntisApi extends ApiRequest {
|
|||||||
.post(endpoint, body: data, headers: headers)
|
.post(endpoint, body: data, headers: headers)
|
||||||
.timeout(
|
.timeout(
|
||||||
const Duration(seconds: 10),
|
const Duration(seconds: 10),
|
||||||
onTimeout: () => throw WebuntisError("Timeout", 1)
|
onTimeout: () => throw WebuntisError('Timeout', 1)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -6,6 +6,6 @@ class WebuntisError implements Exception {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return "WebUntis ($code): $message";
|
return 'WebUntis ($code): $message';
|
||||||
}
|
}
|
||||||
}
|
}
|
16
lib/app.dart
16
lib/app.dart
@ -39,14 +39,14 @@ class _AppState extends State<App> with WidgetsBindingObserver {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
void didChangeAppLifecycleState(AppLifecycleState state) {
|
void didChangeAppLifecycleState(AppLifecycleState state) {
|
||||||
log("AppLifecycle: ${state.toString()}");
|
log('AppLifecycle: ${state.toString()}');
|
||||||
|
|
||||||
if(state == AppLifecycleState.resumed) {
|
if(state == AppLifecycleState.resumed) {
|
||||||
EasyThrottle.throttle(
|
EasyThrottle.throttle(
|
||||||
"appLifecycleState",
|
'appLifecycleState',
|
||||||
const Duration(seconds: 10),
|
const Duration(seconds: 10),
|
||||||
() {
|
() {
|
||||||
log("Refreshing due to LifecycleChange");
|
log('Refreshing due to LifecycleChange');
|
||||||
NotificationTasks.updateProviders(context);
|
NotificationTasks.updateProviders(context);
|
||||||
Provider.of<TimetableProps>(context, listen: false).run();
|
Provider.of<TimetableProps>(context, listen: false).run();
|
||||||
}
|
}
|
||||||
@ -106,7 +106,7 @@ class _AppState extends State<App> with WidgetsBindingObserver {
|
|||||||
activeForegroundColor: Theme.of(context).primaryColor,
|
activeForegroundColor: Theme.of(context).primaryColor,
|
||||||
inactiveForegroundColor: Theme.of(context).colorScheme.secondary,
|
inactiveForegroundColor: Theme.of(context).colorScheme.secondary,
|
||||||
icon: const Icon(Icons.calendar_month),
|
icon: const Icon(Icons.calendar_month),
|
||||||
title: "Vertretung"
|
title: 'Vertretung'
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
PersistentTabConfig(
|
PersistentTabConfig(
|
||||||
@ -127,12 +127,12 @@ class _AppState extends State<App> with WidgetsBindingObserver {
|
|||||||
badgeColor: Theme.of(context).primaryColor,
|
badgeColor: Theme.of(context).primaryColor,
|
||||||
elevation: 1,
|
elevation: 1,
|
||||||
),
|
),
|
||||||
badgeContent: Text("$messages", style: const TextStyle(color: Colors.white, fontSize: 10, fontWeight: FontWeight.bold)),
|
badgeContent: Text('$messages', style: const TextStyle(color: Colors.white, fontSize: 10, fontWeight: FontWeight.bold)),
|
||||||
child: const Icon(Icons.chat),
|
child: const Icon(Icons.chat),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
title: "Talk",
|
title: 'Talk',
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
PersistentTabConfig(
|
PersistentTabConfig(
|
||||||
@ -141,7 +141,7 @@ class _AppState extends State<App> with WidgetsBindingObserver {
|
|||||||
activeForegroundColor: Theme.of(context).primaryColor,
|
activeForegroundColor: Theme.of(context).primaryColor,
|
||||||
inactiveForegroundColor: Theme.of(context).colorScheme.secondary,
|
inactiveForegroundColor: Theme.of(context).colorScheme.secondary,
|
||||||
icon: const Icon(Icons.folder),
|
icon: const Icon(Icons.folder),
|
||||||
title: "Dateien"
|
title: 'Dateien'
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
PersistentTabConfig(
|
PersistentTabConfig(
|
||||||
@ -150,7 +150,7 @@ class _AppState extends State<App> with WidgetsBindingObserver {
|
|||||||
activeForegroundColor: Theme.of(context).primaryColor,
|
activeForegroundColor: Theme.of(context).primaryColor,
|
||||||
inactiveForegroundColor: Theme.of(context).colorScheme.secondary,
|
inactiveForegroundColor: Theme.of(context).colorScheme.secondary,
|
||||||
icon: const Icon(Icons.apps),
|
icon: const Icon(Icons.apps),
|
||||||
title: "Mehr"
|
title: 'Mehr'
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -39,7 +39,7 @@ Future<void> main() async {
|
|||||||
await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
|
await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
|
||||||
log("Firebase token: ${await FirebaseMessaging.instance.getToken() ?? "Error: no Firebase token!"}");
|
log("Firebase token: ${await FirebaseMessaging.instance.getToken() ?? "Error: no Firebase token!"}");
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log("Error initializing Firebase app!");
|
log('Error initializing Firebase app!');
|
||||||
}
|
}
|
||||||
|
|
||||||
ByteData data = await PlatformAssetBundle().load('assets/ca/lets-encrypt-r3.pem');
|
ByteData data = await PlatformAssetBundle().load('assets/ca/lets-encrypt-r3.pem');
|
||||||
@ -89,7 +89,7 @@ class _MainState extends State<Main> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
Jiffy.setLocale("de");
|
Jiffy.setLocale('de');
|
||||||
|
|
||||||
AccountData().waitForPopulation().then((value) {
|
AccountData().waitForPopulation().then((value) {
|
||||||
Provider.of<AccountModel>(context, listen: false)
|
Provider.of<AccountModel>(context, listen: false)
|
||||||
@ -139,7 +139,7 @@ class _MainState extends State<Main> {
|
|||||||
switch(accountModel.state) {
|
switch(accountModel.state) {
|
||||||
case AccountModelState.loggedIn: return const App();
|
case AccountModelState.loggedIn: return const App();
|
||||||
case AccountModelState.loggedOut: return const Login();
|
case AccountModelState.loggedOut: return const Login();
|
||||||
case AccountModelState.undefined: return const PlaceholderView(icon: Icons.timer, text: "Daten werden geladen");
|
case AccountModelState.undefined: return const PlaceholderView(icon: Icons.timer, text: 'Daten werden geladen');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
@ -10,8 +10,8 @@ import 'package:shared_preferences/shared_preferences.dart';
|
|||||||
import 'accountModel.dart';
|
import 'accountModel.dart';
|
||||||
|
|
||||||
class AccountData {
|
class AccountData {
|
||||||
static const _usernameField = "username";
|
static const _usernameField = 'username';
|
||||||
static const _passwordField = "password";
|
static const _passwordField = 'password';
|
||||||
|
|
||||||
static final AccountData _instance = AccountData._construct();
|
static final AccountData _instance = AccountData._construct();
|
||||||
final Future<SharedPreferences> _storage = SharedPreferences.getInstance();
|
final Future<SharedPreferences> _storage = SharedPreferences.getInstance();
|
||||||
@ -29,21 +29,21 @@ class AccountData {
|
|||||||
String? _password;
|
String? _password;
|
||||||
|
|
||||||
String getUsername() {
|
String getUsername() {
|
||||||
if(_username == null) throw Exception("Username not initialized");
|
if(_username == null) throw Exception('Username not initialized');
|
||||||
return _username!;
|
return _username!;
|
||||||
}
|
}
|
||||||
|
|
||||||
String getPassword() {
|
String getPassword() {
|
||||||
if(_password == null) throw Exception("Password not initialized");
|
if(_password == null) throw Exception('Password not initialized');
|
||||||
return _password!;
|
return _password!;
|
||||||
}
|
}
|
||||||
|
|
||||||
String getUserSecret() {
|
String getUserSecret() {
|
||||||
return sha512.convert(utf8.encode("${AccountData().getUsername()}:${AccountData().getPassword()}")).toString();
|
return sha512.convert(utf8.encode('${AccountData().getUsername()}:${AccountData().getPassword()}')).toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<String> getDeviceId() async {
|
Future<String> getDeviceId() async {
|
||||||
return sha512.convert(utf8.encode("${getUserSecret()}@${await FirebaseMessaging.instance.getToken()}")).toString();
|
return sha512.convert(utf8.encode('${getUserSecret()}@${await FirebaseMessaging.instance.getToken()}')).toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> setData(String username, String password) async {
|
Future<void> setData(String username, String password) async {
|
||||||
@ -84,7 +84,7 @@ class AccountData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
String buildHttpAuthString() {
|
String buildHttpAuthString() {
|
||||||
if(!isPopulated()) throw Exception("AccountData (e.g. username or password) is not initialized!");
|
if(!isPopulated()) throw Exception('AccountData (e.g. username or password) is not initialized!');
|
||||||
return "$_username:$_password";
|
return '$_username:$_password';
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -26,7 +26,7 @@ class BreakerProps extends DataHolder {
|
|||||||
for(var key in breakers.regional.keys) {
|
for(var key in breakers.regional.keys) {
|
||||||
GetBreakersReponseObject value = breakers.regional[key]!;
|
GetBreakersReponseObject value = breakers.regional[key]!;
|
||||||
|
|
||||||
if(int.parse(key.split("b")[1]) >= selfVersion) {
|
if(int.parse(key.split('b')[1]) >= selfVersion) {
|
||||||
if(value.areas.contains(type)) return value.message;
|
if(value.areas.contains(type)) return value.message;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@ import '../../api/marianumcloud/talk/chat/getChatResponse.dart';
|
|||||||
import '../dataHolder.dart';
|
import '../dataHolder.dart';
|
||||||
|
|
||||||
class ChatProps extends DataHolder {
|
class ChatProps extends DataHolder {
|
||||||
String _queryToken = "";
|
String _queryToken = '';
|
||||||
DateTime _lastTokenSet = DateTime.now();
|
DateTime _lastTokenSet = DateTime.now();
|
||||||
|
|
||||||
GetChatResponse? _getChatResponse;
|
GetChatResponse? _getChatResponse;
|
||||||
|
@ -21,7 +21,7 @@ class Endpoint {
|
|||||||
String domain;
|
String domain;
|
||||||
String path;
|
String path;
|
||||||
|
|
||||||
Endpoint({required this.domain, this.path = ""});
|
Endpoint({required this.domain, this.path = ''});
|
||||||
|
|
||||||
String full() {
|
String full() {
|
||||||
return domain + path;
|
return domain + path;
|
||||||
@ -40,17 +40,17 @@ class EndpointData {
|
|||||||
EndpointMode getEndpointMode() {
|
EndpointMode getEndpointMode() {
|
||||||
late String existingName;
|
late String existingName;
|
||||||
existingName = AccountData().getUsername();
|
existingName = AccountData().getUsername();
|
||||||
return existingName.startsWith("google") ? EndpointMode.stage : EndpointMode.live;
|
return existingName.startsWith('google') ? EndpointMode.stage : EndpointMode.live;
|
||||||
}
|
}
|
||||||
|
|
||||||
Endpoint webuntis() {
|
Endpoint webuntis() {
|
||||||
return EndpointOptions(
|
return EndpointOptions(
|
||||||
live: Endpoint(
|
live: Endpoint(
|
||||||
domain: "peleus.webuntis.com",
|
domain: 'peleus.webuntis.com',
|
||||||
),
|
),
|
||||||
staged: Endpoint(
|
staged: Endpoint(
|
||||||
domain: "mhsl.eu",
|
domain: 'mhsl.eu',
|
||||||
path: "/marianum/marianummobile/webuntis/public/index.php/api"
|
path: '/marianum/marianummobile/webuntis/public/index.php/api'
|
||||||
),
|
),
|
||||||
).get(getEndpointMode());
|
).get(getEndpointMode());
|
||||||
}
|
}
|
||||||
@ -58,11 +58,11 @@ class EndpointData {
|
|||||||
Endpoint nextcloud() {
|
Endpoint nextcloud() {
|
||||||
return EndpointOptions(
|
return EndpointOptions(
|
||||||
live: Endpoint(
|
live: Endpoint(
|
||||||
domain: "cloud.marianum-fulda.de",
|
domain: 'cloud.marianum-fulda.de',
|
||||||
),
|
),
|
||||||
staged: Endpoint(
|
staged: Endpoint(
|
||||||
domain: "mhsl.eu",
|
domain: 'mhsl.eu',
|
||||||
path: "/marianum/marianummobile/cloud",
|
path: '/marianum/marianummobile/cloud',
|
||||||
)
|
)
|
||||||
).get(getEndpointMode());
|
).get(getEndpointMode());
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@ extension ExtendedList on List {
|
|||||||
|
|
||||||
class FilesProps extends DataHolder {
|
class FilesProps extends DataHolder {
|
||||||
List<String> folderPath = List<String>.empty(growable: true);
|
List<String> folderPath = List<String>.empty(growable: true);
|
||||||
String currentFolderName = "Home";
|
String currentFolderName = 'Home';
|
||||||
|
|
||||||
ListFilesResponse? _listFilesResponse;
|
ListFilesResponse? _listFilesResponse;
|
||||||
ListFilesResponse get listFilesResponse => _listFilesResponse!;
|
ListFilesResponse get listFilesResponse => _listFilesResponse!;
|
||||||
@ -32,7 +32,7 @@ class FilesProps extends DataHolder {
|
|||||||
_listFilesResponse = null;
|
_listFilesResponse = null;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
ListFilesCache(
|
ListFilesCache(
|
||||||
path: folderPath.isEmpty ? "/" : folderPath.join("/"),
|
path: folderPath.isEmpty ? '/' : folderPath.join('/'),
|
||||||
onUpdate: (ListFilesResponse data) => {
|
onUpdate: (ListFilesResponse data) => {
|
||||||
_listFilesResponse = data,
|
_listFilesResponse = data,
|
||||||
notifyListeners(),
|
notifyListeners(),
|
||||||
@ -48,7 +48,7 @@ class FilesProps extends DataHolder {
|
|||||||
|
|
||||||
void popFolder() {
|
void popFolder() {
|
||||||
folderPath.removeLast();
|
folderPath.removeLast();
|
||||||
if(folderPath.isEmpty) currentFolderName = "Home";
|
if(folderPath.isEmpty) currentFolderName = 'Home';
|
||||||
run();
|
run();
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -49,8 +49,8 @@ class TimetableProps extends DataHolder {
|
|||||||
@override
|
@override
|
||||||
void run({renew}) {
|
void run({renew}) {
|
||||||
GetTimetableCache(
|
GetTimetableCache(
|
||||||
startdate: int.parse(DateFormat("yyyyMMdd").format(startDate)),
|
startdate: int.parse(DateFormat('yyyyMMdd').format(startDate)),
|
||||||
enddate: int.parse(DateFormat("yyyyMMdd").format(endDate)),
|
enddate: int.parse(DateFormat('yyyyMMdd').format(endDate)),
|
||||||
onUpdate: (GetTimetableResponse data) => {
|
onUpdate: (GetTimetableResponse data) => {
|
||||||
_getTimetableResponse = data,
|
_getTimetableResponse = data,
|
||||||
notifyListeners(),
|
notifyListeners(),
|
||||||
|
@ -49,11 +49,11 @@ class NotificationController {
|
|||||||
|
|
||||||
DebugTile(context).run(() {
|
DebugTile(context).run(() {
|
||||||
showDialog(context: context, builder: (context) => AlertDialog(
|
showDialog(context: context, builder: (context) => AlertDialog(
|
||||||
title: const Text("Notification report"),
|
title: const Text('Notification report'),
|
||||||
content: Column(
|
content: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
const Text("Dieser Bericht wird angezeigt, da du den Entwicklermodus aktiviert hast und die App über eine Benachrichtigung geöffnet wurde."),
|
const Text('Dieser Bericht wird angezeigt, da du den Entwicklermodus aktiviert hast und die App über eine Benachrichtigung geöffnet wurde.'),
|
||||||
Text(JsonViewer.format(message.data)),
|
Text(JsonViewer.format(message.data)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
@ -10,13 +10,13 @@ import '../widget/confirmDialog.dart';
|
|||||||
class NotifyUpdater {
|
class NotifyUpdater {
|
||||||
static ConfirmDialog enableAfterDisclaimer(SettingsProvider settings) {
|
static ConfirmDialog enableAfterDisclaimer(SettingsProvider settings) {
|
||||||
return ConfirmDialog(
|
return ConfirmDialog(
|
||||||
title: "Warnung",
|
title: 'Warnung',
|
||||||
icon: Icons.warning_amber,
|
icon: Icons.warning_amber,
|
||||||
content: ""
|
content: ''
|
||||||
"Die Push-Benachrichtigungen werden durch mhsl.eu versendet.\n\n"
|
'Die Push-Benachrichtigungen werden durch mhsl.eu versendet.\n\n'
|
||||||
"Durch das aktivieren dieser Funktion wird dein Nutzername, dein Password und eine Geräte-ID von mhsl dauerhaft gespeichert und verarbeitet.\n\n"
|
'Durch das aktivieren dieser Funktion wird dein Nutzername, dein Password und eine Geräte-ID von mhsl dauerhaft gespeichert und verarbeitet.\n\n'
|
||||||
"Für mehr Informationen drücke lange auf die Einstellungsoption!",
|
'Für mehr Informationen drücke lange auf die Einstellungsoption!',
|
||||||
confirmButton: "Aktivieren",
|
confirmButton: 'Aktivieren',
|
||||||
onConfirm: () {
|
onConfirm: () {
|
||||||
FirebaseMessaging.instance.requestPermission(
|
FirebaseMessaging.instance.requestPermission(
|
||||||
provisional: false
|
provisional: false
|
||||||
@ -29,7 +29,7 @@ class NotifyUpdater {
|
|||||||
static void registerToServer() async {
|
static void registerToServer() async {
|
||||||
String? fcmToken = await FirebaseMessaging.instance.getToken();
|
String? fcmToken = await FirebaseMessaging.instance.getToken();
|
||||||
|
|
||||||
if(fcmToken == null) throw Exception("Failed to register push notification because there is no FBC token!");
|
if(fcmToken == null) throw Exception('Failed to register push notification because there is no FBC token!');
|
||||||
|
|
||||||
NotifyRegister(
|
NotifyRegister(
|
||||||
NotifyRegisterParams(
|
NotifyRegisterParams(
|
||||||
|
@ -8,7 +8,7 @@ import '../../view/settings/defaultSettings.dart';
|
|||||||
import 'settings.dart';
|
import 'settings.dart';
|
||||||
|
|
||||||
class SettingsProvider extends ChangeNotifier {
|
class SettingsProvider extends ChangeNotifier {
|
||||||
static const String _fieldName = "settings";
|
static const String _fieldName = 'settings';
|
||||||
|
|
||||||
late SharedPreferences _storage;
|
late SharedPreferences _storage;
|
||||||
late Settings _settings = DefaultSettings.get();
|
late Settings _settings = DefaultSettings.get();
|
||||||
@ -45,13 +45,13 @@ class SettingsProvider extends ChangeNotifier {
|
|||||||
_settings = Settings.fromJson(jsonDecode(_storage.getString(_fieldName)!));
|
_settings = Settings.fromJson(jsonDecode(_storage.getString(_fieldName)!));
|
||||||
} catch(exception) {
|
} catch(exception) {
|
||||||
try {
|
try {
|
||||||
log("Settings were changed, trying to recover from old Settings: ${exception.toString()}");
|
log('Settings were changed, trying to recover from old Settings: ${exception.toString()}');
|
||||||
_settings = Settings.fromJson(_mergeSettings(jsonDecode(_storage.getString(_fieldName)!), DefaultSettings.get().toJson()));
|
_settings = Settings.fromJson(_mergeSettings(jsonDecode(_storage.getString(_fieldName)!), DefaultSettings.get().toJson()));
|
||||||
log("Settings recovered successfully: ${_settings.toJson().toString()}");
|
log('Settings recovered successfully: ${_settings.toJson().toString()}');
|
||||||
} catch(exception) {
|
} catch(exception) {
|
||||||
log("Settings are defective and not recoverable, using defaults: ${exception.toString()}");
|
log('Settings are defective and not recoverable, using defaults: ${exception.toString()}');
|
||||||
_settings = DefaultSettings.get();
|
_settings = DefaultSettings.get();
|
||||||
log("Settings were reset to defaults!");
|
log('Settings were reset to defaults!');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,13 +4,13 @@ class AppTheme {
|
|||||||
static ThemeModeDisplay getDisplayOptions(ThemeMode theme) {
|
static ThemeModeDisplay getDisplayOptions(ThemeMode theme) {
|
||||||
switch(theme) {
|
switch(theme) {
|
||||||
case ThemeMode.system:
|
case ThemeMode.system:
|
||||||
return ThemeModeDisplay(icon: Icons.auto_fix_high_outlined, displayName: "Systemvorgabe");
|
return ThemeModeDisplay(icon: Icons.auto_fix_high_outlined, displayName: 'Systemvorgabe');
|
||||||
|
|
||||||
case ThemeMode.light:
|
case ThemeMode.light:
|
||||||
return ThemeModeDisplay(icon: Icons.wb_sunny_outlined, displayName: "Hell");
|
return ThemeModeDisplay(icon: Icons.wb_sunny_outlined, displayName: 'Hell');
|
||||||
|
|
||||||
case ThemeMode.dark:
|
case ThemeMode.dark:
|
||||||
return ThemeModeDisplay(icon: Icons.dark_mode_outlined, displayName: "Dunkel");
|
return ThemeModeDisplay(icon: Icons.dark_mode_outlined, displayName: 'Dunkel');
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@ class _LoginState extends State<Login> {
|
|||||||
bool displayDisclaimerText = true;
|
bool displayDisclaimerText = true;
|
||||||
|
|
||||||
String? _checkInput(value){
|
String? _checkInput(value){
|
||||||
return (value ?? "").length == 0 ? "Eingabe erforderlich" : null;
|
return (value ?? '').length == 0 ? 'Eingabe erforderlich' : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<String?> _login(LoginData data) async {
|
Future<String?> _login(LoginData data) async {
|
||||||
@ -42,7 +42,7 @@ class _LoginState extends State<Login> {
|
|||||||
} catch(e) {
|
} catch(e) {
|
||||||
await AccountData().removeData();
|
await AccountData().removeData();
|
||||||
log(e.toString());
|
log(e.toString());
|
||||||
return "Benutzername oder Password falsch! (${e.toString()})";
|
return 'Benutzername oder Password falsch! (${e.toString()})';
|
||||||
}
|
}
|
||||||
|
|
||||||
await Future.delayed(const Duration(seconds: 1));
|
await Future.delayed(const Duration(seconds: 1));
|
||||||
@ -51,14 +51,14 @@ class _LoginState extends State<Login> {
|
|||||||
|
|
||||||
Future<String> _resetPassword(String name) {
|
Future<String> _resetPassword(String name) {
|
||||||
return Future.delayed(Duration.zero).then((_) {
|
return Future.delayed(Duration.zero).then((_) {
|
||||||
return "Diese Funktion steht nicht zur Verfügung!";
|
return 'Diese Funktion steht nicht zur Verfügung!';
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return FlutterLogin(
|
return FlutterLogin(
|
||||||
logo: Image.asset("assets/logo/icon.png").image,
|
logo: Image.asset('assets/logo/icon.png').image,
|
||||||
|
|
||||||
userValidator: _checkInput,
|
userValidator: _checkInput,
|
||||||
passwordValidator: _checkInput,
|
passwordValidator: _checkInput,
|
||||||
@ -84,9 +84,9 @@ class _LoginState extends State<Login> {
|
|||||||
),
|
),
|
||||||
|
|
||||||
messages: LoginMessages(
|
messages: LoginMessages(
|
||||||
loginButton: "Anmelden",
|
loginButton: 'Anmelden',
|
||||||
userHint: "Nutzername",
|
userHint: 'Nutzername',
|
||||||
passwordHint: "Passwort",
|
passwordHint: 'Passwort',
|
||||||
),
|
),
|
||||||
|
|
||||||
disableCustomPageTransformer: true,
|
disableCustomPageTransformer: true,
|
||||||
@ -97,15 +97,15 @@ class _LoginState extends State<Login> {
|
|||||||
child: Visibility(
|
child: Visibility(
|
||||||
visible: displayDisclaimerText,
|
visible: displayDisclaimerText,
|
||||||
child: const Text(
|
child: const Text(
|
||||||
"Dies ist ein Inoffizieller Nextcloud & Webuntis Client und wird nicht vom Marianum selbst betrieben.\nKeinerlei Gewähr für Vollständigkeit, Richtigkeit und Aktualität!",
|
'Dies ist ein Inoffizieller Nextcloud & Webuntis Client und wird nicht vom Marianum selbst betrieben.\nKeinerlei Gewähr für Vollständigkeit, Richtigkeit und Aktualität!',
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
footer: "Marianum Fulda - Die persönliche Schule",
|
footer: 'Marianum Fulda - Die persönliche Schule',
|
||||||
title: "Marianum Fulda",
|
title: 'Marianum Fulda',
|
||||||
|
|
||||||
userType: LoginUserType.name,
|
userType: LoginUserType.name,
|
||||||
);
|
);
|
||||||
|
@ -6,7 +6,7 @@ import 'package:flowder/flowder.dart';
|
|||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:jiffy/jiffy.dart';
|
import 'package:jiffy/jiffy.dart';
|
||||||
import 'package:marianum_mobile/widget/infoDialog.dart';
|
import '../../../widget/infoDialog.dart';
|
||||||
import 'package:nextcloud/nextcloud.dart';
|
import 'package:nextcloud/nextcloud.dart';
|
||||||
import 'package:path_provider/path_provider.dart';
|
import 'package:path_provider/path_provider.dart';
|
||||||
|
|
||||||
@ -29,7 +29,7 @@ class FileElement extends StatefulWidget {
|
|||||||
Directory paths = await getTemporaryDirectory();
|
Directory paths = await getTemporaryDirectory();
|
||||||
|
|
||||||
var encodedPath = Uri.encodeComponent(remotePath);
|
var encodedPath = Uri.encodeComponent(remotePath);
|
||||||
encodedPath = encodedPath.replaceAll("%2F", "/");
|
encodedPath = encodedPath.replaceAll('%2F', '/');
|
||||||
|
|
||||||
String local = paths.path + Platform.pathSeparator + name;
|
String local = paths.path + Platform.pathSeparator + name;
|
||||||
|
|
||||||
@ -44,7 +44,7 @@ class FileElement extends StatefulWidget {
|
|||||||
onDone: () {
|
onDone: () {
|
||||||
//Future<OpenResult> result = OpenFile.open(local); // TODO legacy - refactor: remove onDone parameter
|
//Future<OpenResult> result = OpenFile.open(local); // TODO legacy - refactor: remove onDone parameter
|
||||||
Navigator.of(context).push(MaterialPageRoute(builder: (context) => FileViewer(path: local)));
|
Navigator.of(context).push(MaterialPageRoute(builder: (context) => FileViewer(path: local)));
|
||||||
onDone(OpenResult(message: "File viewer opened", type: ResultType.done));
|
onDone(OpenResult(message: 'File viewer opened', type: ResultType.done));
|
||||||
// result.then((value) => {
|
// result.then((value) => {
|
||||||
// onDone(value)
|
// onDone(value)
|
||||||
// });
|
// });
|
||||||
@ -71,21 +71,21 @@ class _FileElementState extends State<FileElement> {
|
|||||||
children: [
|
children: [
|
||||||
Container(
|
Container(
|
||||||
margin: const EdgeInsets.only(right: 10),
|
margin: const EdgeInsets.only(right: 10),
|
||||||
child: const Text("Download:"),
|
child: const Text('Download:'),
|
||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: LinearProgressIndicator(value: percent/100),
|
child: LinearProgressIndicator(value: percent/100),
|
||||||
),
|
),
|
||||||
Container(
|
Container(
|
||||||
margin: const EdgeInsets.only(left: 10),
|
margin: const EdgeInsets.only(left: 10),
|
||||||
child: Text("${percent.round()}%"),
|
child: Text('${percent.round()}%'),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return widget.file.isDirectory
|
return widget.file.isDirectory
|
||||||
? Text("geändert ${Jiffy.parseFromDateTime(widget.file.modifiedAt ?? DateTime.now()).fromNow()}")
|
? Text('geändert ${Jiffy.parseFromDateTime(widget.file.modifiedAt ?? DateTime.now()).fromNow()}')
|
||||||
: Text("${filesize(widget.file.size)}, ${Jiffy.parseFromDateTime(widget.file.modifiedAt ?? DateTime.now()).fromNow()}");
|
: Text('${filesize(widget.file.size)}, ${Jiffy.parseFromDateTime(widget.file.modifiedAt ?? DateTime.now()).fromNow()}');
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -106,17 +106,17 @@ class _FileElementState extends State<FileElement> {
|
|||||||
));
|
));
|
||||||
} else {
|
} else {
|
||||||
if(EndpointData().getEndpointMode() == EndpointMode.stage) {
|
if(EndpointData().getEndpointMode() == EndpointMode.stage) {
|
||||||
InfoDialog.show(context, "Virtuelle Dateien im Staging Prozess können nicht heruntergeladen werden!");
|
InfoDialog.show(context, 'Virtuelle Dateien im Staging Prozess können nicht heruntergeladen werden!');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(widget.file.currentlyDownloading) {
|
if(widget.file.currentlyDownloading) {
|
||||||
showDialog(
|
showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) => ConfirmDialog(
|
builder: (context) => ConfirmDialog(
|
||||||
title: "Download abbrechen?",
|
title: 'Download abbrechen?',
|
||||||
content: "Möchtest du den Download abbrechen?",
|
content: 'Möchtest du den Download abbrechen?',
|
||||||
cancelButton: "Nein",
|
cancelButton: 'Nein',
|
||||||
confirmButton: "Ja, Abbrechen",
|
confirmButton: 'Ja, Abbrechen',
|
||||||
onConfirm: () {
|
onConfirm: () {
|
||||||
downloadCore?.then((value) {
|
downloadCore?.then((value) {
|
||||||
if(!value.isCancelled) value.cancel();
|
if(!value.isCancelled) value.cancel();
|
||||||
@ -143,7 +143,7 @@ class _FileElementState extends State<FileElement> {
|
|||||||
if(result.type != ResultType.done) {
|
if(result.type != ResultType.done) {
|
||||||
showDialog(context: context, builder: (context) {
|
showDialog(context: context, builder: (context) {
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
title: const Text("Download"),
|
title: const Text('Download'),
|
||||||
content: Text(result.message),
|
content: Text(result.message),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@ -163,12 +163,12 @@ class _FileElementState extends State<FileElement> {
|
|||||||
children: [
|
children: [
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: const Icon(Icons.delete_outline),
|
leading: const Icon(Icons.delete_outline),
|
||||||
title: const Text("Löschen"),
|
title: const Text('Löschen'),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
showDialog(context: context, builder: (context) => ConfirmDialog(
|
showDialog(context: context, builder: (context) => ConfirmDialog(
|
||||||
title: "Element löschen?",
|
title: 'Element löschen?',
|
||||||
content: "Das Element wird unwiederruflich gelöscht.",
|
content: 'Das Element wird unwiederruflich gelöscht.',
|
||||||
onConfirm: () {
|
onConfirm: () {
|
||||||
WebdavApi.webdav
|
WebdavApi.webdav
|
||||||
.then((value) => value.delete(PathUri.parse(widget.file.path)))
|
.then((value) => value.delete(PathUri.parse(widget.file.path)))
|
||||||
@ -181,7 +181,7 @@ class _FileElementState extends State<FileElement> {
|
|||||||
visible: !kReleaseMode,
|
visible: !kReleaseMode,
|
||||||
child: ListTile(
|
child: ListTile(
|
||||||
leading: const Icon(Icons.share_outlined),
|
leading: const Icon(Icons.share_outlined),
|
||||||
title: const Text("Teilen"),
|
title: const Text('Teilen'),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
UnimplementedDialog.show(context);
|
UnimplementedDialog.show(context);
|
||||||
|
@ -43,8 +43,8 @@ class _FileUploadDialogState extends State<FileUploadDialog> {
|
|||||||
setState(() {
|
setState(() {
|
||||||
state = FileUploadState.checkConflict;
|
state = FileUploadState.checkConflict;
|
||||||
});
|
});
|
||||||
List<WebDavResponse> result = (await webdavClient.propfind(PathUri.parse(widget.remotePath.join("/")))).responses;
|
List<WebDavResponse> result = (await webdavClient.propfind(PathUri.parse(widget.remotePath.join('/')))).responses;
|
||||||
if(result.any((element) => element.href!.endsWith("/$targetFileName"))) {
|
if(result.any((element) => element.href!.endsWith('/$targetFileName'))) {
|
||||||
setState(() {
|
setState(() {
|
||||||
state = FileUploadState.conflict;
|
state = FileUploadState.conflict;
|
||||||
});
|
});
|
||||||
@ -57,7 +57,7 @@ class _FileUploadDialogState extends State<FileUploadDialog> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<HttpClientResponse> uploadTask = webdavClient.putFile(File(widget.localPath), FileStat.statSync(widget.localPath), PathUri.parse(fullRemotePath)); // TODO use onProgress from putFile
|
Future<HttpClientResponse> uploadTask = webdavClient.putFile(File(widget.localPath), FileStat.statSync(widget.localPath), PathUri.parse(fullRemotePath)); // TODO use onProgress from putFile
|
||||||
uploadTask.then((value) => Future<HttpClientResponse?>.value(value)).catchError((e) {
|
uploadTask.then(Future<HttpClientResponse?>.value).catchError((e) {
|
||||||
setState(() {
|
setState(() {
|
||||||
state = FileUploadState.error;
|
state = FileUploadState.error;
|
||||||
});
|
});
|
||||||
@ -67,7 +67,7 @@ class _FileUploadDialogState extends State<FileUploadDialog> {
|
|||||||
|
|
||||||
cancelableOperation = CancelableOperation<HttpClientResponse>.fromFuture(
|
cancelableOperation = CancelableOperation<HttpClientResponse>.fromFuture(
|
||||||
uploadTask,
|
uploadTask,
|
||||||
onCancel: () => log("Upload cancelled"),
|
onCancel: () => log('Upload cancelled'),
|
||||||
);
|
);
|
||||||
|
|
||||||
cancelableOperation!.then((value) {
|
cancelableOperation!.then((value) {
|
||||||
@ -88,7 +88,7 @@ class _FileUploadDialogState extends State<FileUploadDialog> {
|
|||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
targetFileName = widget.fileName;
|
targetFileName = widget.fileName;
|
||||||
remoteFolderName = widget.remotePath.isNotEmpty ? widget.remotePath.last : "/";
|
remoteFolderName = widget.remotePath.isNotEmpty ? widget.remotePath.last : '/';
|
||||||
fileNameController.text = widget.fileName;
|
fileNameController.text = widget.fileName;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -96,7 +96,7 @@ class _FileUploadDialogState extends State<FileUploadDialog> {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
if(state == FileUploadState.naming) {
|
if(state == FileUploadState.naming) {
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
title: const Text("Datei hochladen"),
|
title: const Text('Datei hochladen'),
|
||||||
content: Column(
|
content: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
@ -107,7 +107,7 @@ class _FileUploadDialogState extends State<FileUploadDialog> {
|
|||||||
},
|
},
|
||||||
autocorrect: false,
|
autocorrect: false,
|
||||||
decoration: const InputDecoration(
|
decoration: const InputDecoration(
|
||||||
labelText: "Dateiname",
|
labelText: 'Dateiname',
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@ -115,10 +115,10 @@ class _FileUploadDialogState extends State<FileUploadDialog> {
|
|||||||
actions: [
|
actions: [
|
||||||
TextButton(onPressed: () {
|
TextButton(onPressed: () {
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
}, child: const Text("Abbrechen")),
|
}, child: const Text('Abbrechen')),
|
||||||
TextButton(onPressed: () async {
|
TextButton(onPressed: () async {
|
||||||
upload();
|
upload();
|
||||||
}, child: const Text("Hochladen")),
|
}, child: const Text('Hochladen')),
|
||||||
],
|
],
|
||||||
|
|
||||||
);
|
);
|
||||||
@ -127,7 +127,7 @@ class _FileUploadDialogState extends State<FileUploadDialog> {
|
|||||||
if(state == FileUploadState.conflict) {
|
if(state == FileUploadState.conflict) {
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
icon: const Icon(Icons.error_outline),
|
icon: const Icon(Icons.error_outline),
|
||||||
title: const Text("Datei konflikt"),
|
title: const Text('Datei konflikt'),
|
||||||
content: Column(
|
content: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
@ -139,10 +139,10 @@ class _FileUploadDialogState extends State<FileUploadDialog> {
|
|||||||
setState(() {
|
setState(() {
|
||||||
state = FileUploadState.naming;
|
state = FileUploadState.naming;
|
||||||
});
|
});
|
||||||
}, child: const Text("Datei umbenennen")),
|
}, child: const Text('Datei umbenennen')),
|
||||||
TextButton(onPressed: () {
|
TextButton(onPressed: () {
|
||||||
upload(override: true);
|
upload(override: true);
|
||||||
}, child: const Text("Datei überschreiben")),
|
}, child: const Text('Datei überschreiben')),
|
||||||
],
|
],
|
||||||
|
|
||||||
);
|
);
|
||||||
@ -151,15 +151,15 @@ class _FileUploadDialogState extends State<FileUploadDialog> {
|
|||||||
if(state == FileUploadState.upload || state == FileUploadState.checkConflict) {
|
if(state == FileUploadState.upload || state == FileUploadState.checkConflict) {
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
icon: const Icon(Icons.upload),
|
icon: const Icon(Icons.upload),
|
||||||
title: const Text("Hochladen"),
|
title: const Text('Hochladen'),
|
||||||
content: Column(
|
content: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
Visibility(
|
Visibility(
|
||||||
visible: state == FileUploadState.upload,
|
visible: state == FileUploadState.upload,
|
||||||
replacement: const Text("Prüfe auf dateikonflikte..."),
|
replacement: const Text('Prüfe auf dateikonflikte...'),
|
||||||
child: const Text("Upload läuft!\nDies kann je nach Dateigröße einige Zeit dauern...", textAlign: TextAlign.center),
|
child: const Text('Upload läuft!\nDies kann je nach Dateigröße einige Zeit dauern...', textAlign: TextAlign.center),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 30),
|
const SizedBox(height: 30),
|
||||||
const CircularProgressIndicator()
|
const CircularProgressIndicator()
|
||||||
@ -183,7 +183,7 @@ class _FileUploadDialogState extends State<FileUploadDialog> {
|
|||||||
}
|
}
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
icon: const Icon(Icons.done),
|
icon: const Icon(Icons.done),
|
||||||
title: const Text("Upload fertig"),
|
title: const Text('Upload fertig'),
|
||||||
content: Column(
|
content: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
@ -193,7 +193,7 @@ class _FileUploadDialogState extends State<FileUploadDialog> {
|
|||||||
actions: [
|
actions: [
|
||||||
TextButton(onPressed: () {
|
TextButton(onPressed: () {
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
}, child: const Text("Fertig")),
|
}, child: const Text('Fertig')),
|
||||||
],
|
],
|
||||||
|
|
||||||
);
|
);
|
||||||
@ -202,23 +202,23 @@ class _FileUploadDialogState extends State<FileUploadDialog> {
|
|||||||
if(state == FileUploadState.error) {
|
if(state == FileUploadState.error) {
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
icon: const Icon(Icons.error_outline),
|
icon: const Icon(Icons.error_outline),
|
||||||
title: const Text("Fehler"),
|
title: const Text('Fehler'),
|
||||||
content: const Column(
|
content: const Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
Text("Es ist ein Fehler aufgetreten!", textAlign: TextAlign.center),
|
Text('Es ist ein Fehler aufgetreten!', textAlign: TextAlign.center),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(onPressed: () {
|
TextButton(onPressed: () {
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
}, child: const Text("Schlißen")),
|
}, child: const Text('Schlißen')),
|
||||||
],
|
],
|
||||||
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw UnimplementedError("Invalid state");
|
throw UnimplementedError('Invalid state');
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -43,17 +43,17 @@ enum SortOption {
|
|||||||
class SortOptions {
|
class SortOptions {
|
||||||
static Map<SortOption, BetterSortOption> options = {
|
static Map<SortOption, BetterSortOption> options = {
|
||||||
SortOption.name: BetterSortOption(
|
SortOption.name: BetterSortOption(
|
||||||
displayName: "Name",
|
displayName: 'Name',
|
||||||
icon: Icons.sort_by_alpha_outlined,
|
icon: Icons.sort_by_alpha_outlined,
|
||||||
compare: (CacheableFile a, CacheableFile b) => a.name.compareTo(b.name)
|
compare: (CacheableFile a, CacheableFile b) => a.name.compareTo(b.name)
|
||||||
),
|
),
|
||||||
SortOption.date: BetterSortOption(
|
SortOption.date: BetterSortOption(
|
||||||
displayName: "Datum",
|
displayName: 'Datum',
|
||||||
icon: Icons.history_outlined,
|
icon: Icons.history_outlined,
|
||||||
compare: (CacheableFile a, CacheableFile b) => a.modifiedAt!.compareTo(b.modifiedAt!)
|
compare: (CacheableFile a, CacheableFile b) => a.modifiedAt!.compareTo(b.modifiedAt!)
|
||||||
),
|
),
|
||||||
SortOption.size: BetterSortOption(
|
SortOption.size: BetterSortOption(
|
||||||
displayName: "Größe",
|
displayName: 'Größe',
|
||||||
icon: Icons.sd_card_outlined,
|
icon: Icons.sd_card_outlined,
|
||||||
compare: (CacheableFile a, CacheableFile b) {
|
compare: (CacheableFile a, CacheableFile b) {
|
||||||
if(a.isDirectory || b.isDirectory) return a.isDirectory ? 1 : 0;
|
if(a.isDirectory || b.isDirectory) return a.isDirectory ? 1 : 0;
|
||||||
@ -88,7 +88,7 @@ class _FilesState extends State<Files> {
|
|||||||
|
|
||||||
void _query() {
|
void _query() {
|
||||||
ListFilesCache(
|
ListFilesCache(
|
||||||
path: widget.path.isEmpty ? "/" : widget.path.join("/"),
|
path: widget.path.isEmpty ? '/' : widget.path.join('/'),
|
||||||
onUpdate: (ListFilesResponse d) {
|
onUpdate: (ListFilesResponse d) {
|
||||||
if(!context.mounted) return; // prevent setState when widget is possibly already disposed
|
if(!context.mounted) return; // prevent setState when widget is possibly already disposed
|
||||||
d.files.removeWhere((element) => element.name.isEmpty || element.name == widget.path.lastOrNull());
|
d.files.removeWhere((element) => element.name.isEmpty || element.name == widget.path.lastOrNull());
|
||||||
@ -109,7 +109,7 @@ class _FilesState extends State<Files> {
|
|||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text(widget.path.isNotEmpty ? widget.path.last : "Dateien"),
|
title: Text(widget.path.isNotEmpty ? widget.path.last : 'Dateien'),
|
||||||
actions: [
|
actions: [
|
||||||
// IconButton(
|
// IconButton(
|
||||||
// icon: const Icon(Icons.search),
|
// icon: const Icon(Icons.search),
|
||||||
@ -127,7 +127,7 @@ class _FilesState extends State<Files> {
|
|||||||
children: [
|
children: [
|
||||||
Icon(e ? Icons.text_rotate_up : Icons.text_rotation_down, color: Theme.of(context).colorScheme.onSurface),
|
Icon(e ? Icons.text_rotate_up : Icons.text_rotation_down, color: Theme.of(context).colorScheme.onSurface),
|
||||||
const SizedBox(width: 15),
|
const SizedBox(width: 15),
|
||||||
Text(e ? "Aufsteigend" : "Absteigend")
|
Text(e ? 'Aufsteigend' : 'Absteigend')
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
)).toList();
|
)).toList();
|
||||||
@ -164,7 +164,7 @@ class _FilesState extends State<Files> {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
floatingActionButton: FloatingActionButton(
|
floatingActionButton: FloatingActionButton(
|
||||||
heroTag: "uploadFile",
|
heroTag: 'uploadFile',
|
||||||
backgroundColor: Theme.of(context).primaryColor,
|
backgroundColor: Theme.of(context).primaryColor,
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
showDialog(context: context, builder: (context) {
|
showDialog(context: context, builder: (context) {
|
||||||
@ -172,29 +172,29 @@ class _FilesState extends State<Files> {
|
|||||||
children: [
|
children: [
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: const Icon(Icons.create_new_folder_outlined),
|
leading: const Icon(Icons.create_new_folder_outlined),
|
||||||
title: const Text("Ordner erstellen"),
|
title: const Text('Ordner erstellen'),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
showDialog(context: context, builder: (context) {
|
showDialog(context: context, builder: (context) {
|
||||||
var inputController = TextEditingController();
|
var inputController = TextEditingController();
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
title: const Text("Neuer Ordner"),
|
title: const Text('Neuer Ordner'),
|
||||||
content: TextField(
|
content: TextField(
|
||||||
controller: inputController,
|
controller: inputController,
|
||||||
decoration: const InputDecoration(
|
decoration: const InputDecoration(
|
||||||
labelText: "Name",
|
labelText: 'Name',
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(onPressed: () {
|
TextButton(onPressed: () {
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
}, child: const Text("Abbrechen")),
|
}, child: const Text('Abbrechen')),
|
||||||
TextButton(onPressed: () {
|
TextButton(onPressed: () {
|
||||||
WebdavApi.webdav.then((webdav) {
|
WebdavApi.webdav.then((webdav) {
|
||||||
webdav.mkcol(PathUri.parse("${widget.path.join("/")}/${inputController.text}")).then((value) => _query());
|
webdav.mkcol(PathUri.parse("${widget.path.join("/")}/${inputController.text}")).then((value) => _query());
|
||||||
});
|
});
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
}, child: const Text("Ordner erstellen")),
|
}, child: const Text('Ordner erstellen')),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@ -202,12 +202,10 @@ class _FilesState extends State<Files> {
|
|||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: const Icon(Icons.upload_file),
|
leading: const Icon(Icons.upload_file),
|
||||||
title: const Text("Aus Dateien hochladen"),
|
title: const Text('Aus Dateien hochladen'),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
context.loaderOverlay.show();
|
context.loaderOverlay.show();
|
||||||
FilePick.documentPick().then((value) {
|
FilePick.documentPick().then(mediaUpload);
|
||||||
mediaUpload(value);
|
|
||||||
});
|
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@ -215,7 +213,7 @@ class _FilesState extends State<Files> {
|
|||||||
visible: !Platform.isIOS,
|
visible: !Platform.isIOS,
|
||||||
child: ListTile(
|
child: ListTile(
|
||||||
leading: const Icon(Icons.add_a_photo_outlined),
|
leading: const Icon(Icons.add_a_photo_outlined),
|
||||||
title: const Text("Aus Gallerie hochladen"),
|
title: const Text('Aus Gallerie hochladen'),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
context.loaderOverlay.show();
|
context.loaderOverlay.show();
|
||||||
FilePick.galleryPick().then((value) {
|
FilePick.galleryPick().then((value) {
|
||||||
@ -231,7 +229,7 @@ class _FilesState extends State<Files> {
|
|||||||
},
|
},
|
||||||
child: const Icon(Icons.add),
|
child: const Icon(Icons.add),
|
||||||
),
|
),
|
||||||
body: data == null ? const LoadingSpinner() : data!.files.isEmpty ? const PlaceholderView(icon: Icons.folder_off_rounded, text: "Der Ordner ist leer") : LoaderOverlay(
|
body: data == null ? const LoadingSpinner() : data!.files.isEmpty ? const PlaceholderView(icon: Icons.folder_off_rounded, text: 'Der Ordner ist leer') : LoaderOverlay(
|
||||||
child: RefreshIndicator(
|
child: RefreshIndicator(
|
||||||
onRefresh: () {
|
onRefresh: () {
|
||||||
_query();
|
_query();
|
||||||
@ -258,6 +256,6 @@ class _FilesState extends State<Files> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var fileName = path.split(Platform.pathSeparator).last;
|
var fileName = path.split(Platform.pathSeparator).last;
|
||||||
showDialog(context: context, builder: (context) => FileUploadDialog(localPath: path, remotePath: widget.path, fileName: fileName, onUploadFinished: () => _query()), barrierDismissible: false);
|
showDialog(context: context, builder: (context) => FileUploadDialog(localPath: path, remotePath: widget.path, fileName: fileName, onUploadFinished: _query), barrierDismissible: false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,20 +22,20 @@ class _FeedbackDialogState extends State<FeedbackDialog> {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
|
|
||||||
title: const Text("Feedback"),
|
title: const Text('Feedback'),
|
||||||
content: Column(
|
content: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
const Text("Feedback, Anregungen, Ideen, Fehler und Verbesserungen"),
|
const Text('Feedback, Anregungen, Ideen, Fehler und Verbesserungen'),
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
const Text("Bitte gib keine geheimen Daten wie z.B. Passwörter weiter.", style: TextStyle(fontSize: 10)),
|
const Text('Bitte gib keine geheimen Daten wie z.B. Passwörter weiter.', style: TextStyle(fontSize: 10)),
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
TextField(
|
TextField(
|
||||||
controller: _feedbackInput,
|
controller: _feedbackInput,
|
||||||
autofocus: true,
|
autofocus: true,
|
||||||
decoration: const InputDecoration(
|
decoration: const InputDecoration(
|
||||||
border: OutlineInputBorder(),
|
border: OutlineInputBorder(),
|
||||||
label: Text("Feedback und Verbesserungen")
|
label: Text('Feedback und Verbesserungen')
|
||||||
),
|
),
|
||||||
// style: TextStyle(),
|
// style: TextStyle(),
|
||||||
// expands: true,
|
// expands: true,
|
||||||
@ -44,12 +44,12 @@ class _FeedbackDialogState extends State<FeedbackDialog> {
|
|||||||
),
|
),
|
||||||
Visibility(
|
Visibility(
|
||||||
visible: _error != null,
|
visible: _error != null,
|
||||||
child: Text("Senden fehlgeschlagen: $_error", style: const TextStyle(color: Colors.red))
|
child: Text('Senden fehlgeschlagen: $_error', style: const TextStyle(color: Colors.red))
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(onPressed: () => Navigator.of(context).pop(), child: const Text("Abbrechen")),
|
TextButton(onPressed: () => Navigator.of(context).pop(), child: const Text('Abbrechen')),
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
AddFeedback(
|
AddFeedback(
|
||||||
@ -62,7 +62,7 @@ class _FeedbackDialogState extends State<FeedbackDialog> {
|
|||||||
.run()
|
.run()
|
||||||
.then((value) {
|
.then((value) {
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
InfoDialog.show(context, "Danke für dein Feedback!");
|
InfoDialog.show(context, 'Danke für dein Feedback!');
|
||||||
})
|
})
|
||||||
.catchError((error, trace) {
|
.catchError((error, trace) {
|
||||||
setState(() {
|
setState(() {
|
||||||
@ -70,7 +70,7 @@ class _FeedbackDialogState extends State<FeedbackDialog> {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
child: const Text("Senden"),
|
child: const Text('Senden'),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
@ -24,7 +24,7 @@ class _GradeAverageState extends State<GradeAverage> {
|
|||||||
|
|
||||||
String getGradeDisplay(int grade) {
|
String getGradeDisplay(int grade) {
|
||||||
if(gradeSystem) {
|
if(gradeSystem) {
|
||||||
return "Note $grade";
|
return 'Note $grade';
|
||||||
} else {
|
} else {
|
||||||
return "$grade Punkt${grade > 1 ? "e" : ""}";
|
return "$grade Punkt${grade > 1 ? "e" : ""}";
|
||||||
}
|
}
|
||||||
@ -48,22 +48,22 @@ class _GradeAverageState extends State<GradeAverage> {
|
|||||||
if(!settings.val().gradeAveragesSettings.askedForPreferredGradeSystem) {
|
if(!settings.val().gradeAveragesSettings.askedForPreferredGradeSystem) {
|
||||||
settings.val(write: true).gradeAveragesSettings.askedForPreferredGradeSystem = true;
|
settings.val(write: true).gradeAveragesSettings.askedForPreferredGradeSystem = true;
|
||||||
showDialog(context: context, builder: (context) => AlertDialog(
|
showDialog(context: context, builder: (context) => AlertDialog(
|
||||||
title: const Text("Notensystem"),
|
title: const Text('Notensystem'),
|
||||||
content: const Text("Wähle dein bevorzugtes Schulnotensystem"),
|
content: const Text('Wähle dein bevorzugtes Schulnotensystem'),
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
switchSystem(true);
|
switchSystem(true);
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
},
|
},
|
||||||
child: const Text("Realschule"),
|
child: const Text('Realschule'),
|
||||||
),
|
),
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
switchSystem(false);
|
switchSystem(false);
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
},
|
},
|
||||||
child: const Text("Oberstufe"),
|
child: const Text('Oberstufe'),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
));
|
));
|
||||||
@ -81,7 +81,7 @@ class _GradeAverageState extends State<GradeAverage> {
|
|||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: const Text("Notendurschnittsrechner"),
|
title: const Text('Notendurschnittsrechner'),
|
||||||
actions: [
|
actions: [
|
||||||
Visibility(
|
Visibility(
|
||||||
visible: grades.isNotEmpty,
|
visible: grades.isNotEmpty,
|
||||||
@ -89,9 +89,9 @@ class _GradeAverageState extends State<GradeAverage> {
|
|||||||
showDialog(
|
showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) => ConfirmDialog(
|
builder: (context) => ConfirmDialog(
|
||||||
title: "Zurücksetzen?",
|
title: 'Zurücksetzen?',
|
||||||
content: "Alle Einträge werden entfernt.",
|
content: 'Alle Einträge werden entfernt.',
|
||||||
confirmButton: "Zurücksetzen",
|
confirmButton: 'Zurücksetzen',
|
||||||
onConfirm: () {
|
onConfirm: () {
|
||||||
grades.clear();
|
grades.clear();
|
||||||
setState(() {});
|
setState(() {});
|
||||||
@ -109,7 +109,7 @@ class _GradeAverageState extends State<GradeAverage> {
|
|||||||
children: [
|
children: [
|
||||||
Icon(e ? Icons.calculate_outlined : Icons.school_outlined, color: Theme.of(context).colorScheme.onSurface),
|
Icon(e ? Icons.calculate_outlined : Icons.school_outlined, color: Theme.of(context).colorScheme.onSurface),
|
||||||
const SizedBox(width: 15),
|
const SizedBox(width: 15),
|
||||||
Text(e ? "Notensystem" : "Punktesystem"),
|
Text(e ? 'Notensystem' : 'Punktesystem'),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
)).toList(),
|
)).toList(),
|
||||||
@ -120,9 +120,9 @@ class _GradeAverageState extends State<GradeAverage> {
|
|||||||
showDialog(
|
showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) => ConfirmDialog(
|
builder: (context) => ConfirmDialog(
|
||||||
title: "Notensystem wechseln",
|
title: 'Notensystem wechseln',
|
||||||
content: "Beim wechsel des Notensystems werden alle Einträge zurückgesetzt.",
|
content: 'Beim wechsel des Notensystems werden alle Einträge zurückgesetzt.',
|
||||||
confirmButton: "Fortfahren",
|
confirmButton: 'Fortfahren',
|
||||||
onConfirm: () => switchSystem(e),
|
onConfirm: () => switchSystem(e),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@ -142,7 +142,7 @@ class _GradeAverageState extends State<GradeAverage> {
|
|||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
const Divider(),
|
const Divider(),
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
Text(gradeSystem ? "Wähle unten die Anzahl deiner jewiligen Noten aus" : "Wähle unten die Anzahl deiner jeweiligen Punkte aus"),
|
Text(gradeSystem ? 'Wähle unten die Anzahl deiner jewiligen Noten aus' : 'Wähle unten die Anzahl deiner jeweiligen Punkte aus'),
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: ListView.builder(
|
child: ListView.builder(
|
||||||
@ -169,7 +169,7 @@ class _GradeAverageState extends State<GradeAverage> {
|
|||||||
icon: const Icon(Icons.remove),
|
icon: const Icon(Icons.remove),
|
||||||
color: Theme.of(context).colorScheme.onSurface,
|
color: Theme.of(context).colorScheme.onSurface,
|
||||||
),
|
),
|
||||||
Text("${grades.where(isThis).length}", style: const TextStyle(fontSize: 15, fontWeight: FontWeight.bold)),
|
Text('${grades.where(isThis).length}', style: const TextStyle(fontSize: 15, fontWeight: FontWeight.bold)),
|
||||||
IconButton(
|
IconButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
setState(() {
|
setState(() {
|
||||||
|
@ -42,23 +42,23 @@ class _HolidaysState extends State<Holidays> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
String parseString(String enDate) {
|
String parseString(String enDate) {
|
||||||
return Jiffy.parse(enDate).format(pattern: "dd.MM.yyyy");
|
return Jiffy.parse(enDate).format(pattern: 'dd.MM.yyyy');
|
||||||
}
|
}
|
||||||
|
|
||||||
void showDisclaimer() {
|
void showDisclaimer() {
|
||||||
showDialog(context: context, builder: (context) {
|
showDialog(context: context, builder: (context) {
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
title: const Text("Richtigkeit und Bereitstellung der Daten"),
|
title: const Text('Richtigkeit und Bereitstellung der Daten'),
|
||||||
content: Column(
|
content: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
const Text(""
|
const Text(''
|
||||||
"Sämtliche Datumsangaben sind ohne Gewähr.\n"
|
'Sämtliche Datumsangaben sind ohne Gewähr.\n'
|
||||||
"Ich übernehme weder Verantwortung für die Richtigkeit der Daten noch hafte ich für wirtschaftliche Schäden die aus der Verwendung dieser Daten entstehen können.\n\n"
|
'Ich übernehme weder Verantwortung für die Richtigkeit der Daten noch hafte ich für wirtschaftliche Schäden die aus der Verwendung dieser Daten entstehen können.\n\n'
|
||||||
"Die Daten stammen von https://ferien-api.de/"),
|
'Die Daten stammen von https://ferien-api.de/'),
|
||||||
const SizedBox(height: 30),
|
const SizedBox(height: 30),
|
||||||
ListTile(
|
ListTile(
|
||||||
title: const Text("Diese Meldung nicht mehr anzeigen"),
|
title: const Text('Diese Meldung nicht mehr anzeigen'),
|
||||||
trailing: Checkbox(
|
trailing: Checkbox(
|
||||||
value: settings.val().holidaysSettings.dismissedDisclaimer,
|
value: settings.val().holidaysSettings.dismissedDisclaimer,
|
||||||
onChanged: (value) => settings.val(write: true).holidaysSettings.dismissedDisclaimer = value!,
|
onChanged: (value) => settings.val(write: true).holidaysSettings.dismissedDisclaimer = value!,
|
||||||
@ -67,8 +67,8 @@ class _HolidaysState extends State<Holidays> {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(child: const Text("ferien-api.de besuchen"), onPressed: () => ConfirmDialog.openBrowser(context, "https://ferien-api.de/")),
|
TextButton(child: const Text('ferien-api.de besuchen'), onPressed: () => ConfirmDialog.openBrowser(context, 'https://ferien-api.de/')),
|
||||||
TextButton(child: const Text("Okay"), onPressed: () => Navigator.of(context).pop()),
|
TextButton(child: const Text('Okay'), onPressed: () => Navigator.of(context).pop()),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@ -78,11 +78,11 @@ class _HolidaysState extends State<Holidays> {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: const Text("Schulferien in Hessen"),
|
title: const Text('Schulferien in Hessen'),
|
||||||
actions: [
|
actions: [
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: const Icon(Icons.warning_amber_outlined),
|
icon: const Icon(Icons.warning_amber_outlined),
|
||||||
onPressed: () => showDisclaimer(),
|
onPressed: showDisclaimer,
|
||||||
),
|
),
|
||||||
PopupMenuButton<bool>(
|
PopupMenuButton<bool>(
|
||||||
initialValue: settings.val().holidaysSettings.showPastEvents,
|
initialValue: settings.val().holidaysSettings.showPastEvents,
|
||||||
@ -95,7 +95,7 @@ class _HolidaysState extends State<Holidays> {
|
|||||||
children: [
|
children: [
|
||||||
Icon(e ? Icons.history_outlined : Icons.history_toggle_off_outlined, color: Theme.of(context).colorScheme.onSurface),
|
Icon(e ? Icons.history_outlined : Icons.history_toggle_off_outlined, color: Theme.of(context).colorScheme.onSurface),
|
||||||
const SizedBox(width: 15),
|
const SizedBox(width: 15),
|
||||||
Text(e ? "Alle anzeigen" : "Nur zukünftige anzeigen")
|
Text(e ? 'Alle anzeigen' : 'Nur zukünftige anzeigen')
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
)).toList();
|
)).toList();
|
||||||
@ -115,19 +115,19 @@ class _HolidaysState extends State<Holidays> {
|
|||||||
List<GetHolidaysResponseObject> holidays = value.getHolidaysResponse.data;
|
List<GetHolidaysResponseObject> holidays = value.getHolidaysResponse.data;
|
||||||
if(!showPastEvents) holidays = holidays.where((element) => DateTime.parse(element.end).isAfter(DateTime.now())).toList();
|
if(!showPastEvents) holidays = holidays.where((element) => DateTime.parse(element.end).isAfter(DateTime.now())).toList();
|
||||||
|
|
||||||
if(holidays.isEmpty) return const PlaceholderView(icon: Icons.search_off, text: "Es wurden keine Ferieneinträge gefunden!");
|
if(holidays.isEmpty) return const PlaceholderView(icon: Icons.search_off, text: 'Es wurden keine Ferieneinträge gefunden!');
|
||||||
|
|
||||||
return ListView.builder(
|
return ListView.builder(
|
||||||
itemCount: holidays.length,
|
itemCount: holidays.length,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
GetHolidaysResponseObject holiday = holidays[index];
|
GetHolidaysResponseObject holiday = holidays[index];
|
||||||
String holidayType = holiday.name.split(" ").first.capitalize();
|
String holidayType = holiday.name.split(' ').first.capitalize();
|
||||||
return ListTile(
|
return ListTile(
|
||||||
leading: const CenteredLeading(Icon(Icons.calendar_month)),
|
leading: const CenteredLeading(Icon(Icons.calendar_month)),
|
||||||
title: Text("$holidayType ab ${parseString(holiday.start)}"),
|
title: Text('$holidayType ab ${parseString(holiday.start)}'),
|
||||||
subtitle: Text("bis ${parseString(holiday.end)}"),
|
subtitle: Text('bis ${parseString(holiday.end)}'),
|
||||||
onTap: () => showDialog(context: context, builder: (context) => SimpleDialog(
|
onTap: () => showDialog(context: context, builder: (context) => SimpleDialog(
|
||||||
title: Text("$holidayType ${holiday.year} in Hessen"),
|
title: Text('$holidayType ${holiday.year} in Hessen'),
|
||||||
children: [
|
children: [
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: const CenteredLeading(Icon(Icons.signpost_outlined)),
|
leading: const CenteredLeading(Icon(Icons.signpost_outlined)),
|
||||||
@ -136,11 +136,11 @@ class _HolidaysState extends State<Holidays> {
|
|||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: const Icon(Icons.arrow_forward),
|
leading: const Icon(Icons.arrow_forward),
|
||||||
title: Text("vom ${parseString(holiday.start)}"),
|
title: Text('vom ${parseString(holiday.start)}'),
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: const Icon(Icons.arrow_back),
|
leading: const Icon(Icons.arrow_back),
|
||||||
title: Text("bis zum ${parseString(holiday.end)}"),
|
title: Text('bis zum ${parseString(holiday.end)}'),
|
||||||
),
|
),
|
||||||
Visibility(
|
Visibility(
|
||||||
visible: !DateTime.parse(holiday.start).difference(DateTime.now()).isNegative,
|
visible: !DateTime.parse(holiday.start).difference(DateTime.now()).isNegative,
|
||||||
|
@ -27,7 +27,7 @@ class _MessageState extends State<Message> {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: const Text("Marianum Message"),
|
title: const Text('Marianum Message'),
|
||||||
),
|
),
|
||||||
body: Consumer<MessageProps>(builder: (context, value, child) {
|
body: Consumer<MessageProps>(builder: (context, value, child) {
|
||||||
if(value.primaryLoading()) return const LoadingSpinner();
|
if(value.primaryLoading()) return const LoadingSpinner();
|
||||||
@ -43,7 +43,7 @@ class _MessageState extends State<Message> {
|
|||||||
children: [Icon(Icons.newspaper)],
|
children: [Icon(Icons.newspaper)],
|
||||||
),
|
),
|
||||||
title: Text(message.name, overflow: TextOverflow.ellipsis),
|
title: Text(message.name, overflow: TextOverflow.ellipsis),
|
||||||
subtitle: Text("vom ${message.date}"),
|
subtitle: Text('vom ${message.date}'),
|
||||||
trailing: const Icon(Icons.arrow_right),
|
trailing: const Icon(Icons.arrow_right),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navigator.push(context, MaterialPageRoute(builder: (context) => MessageView(basePath: value.getMessagesResponse.base, message: message)));
|
Navigator.push(context, MaterialPageRoute(builder: (context) => MessageView(basePath: value.getMessagesResponse.base, message: message)));
|
||||||
|
@ -29,12 +29,12 @@ class _MessageViewState extends State<MessageView> {
|
|||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
showDialog(context: context, builder: (context) {
|
showDialog(context: context, builder: (context) {
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
title: const Text("Fehler beim öffnen"),
|
title: const Text('Fehler beim öffnen'),
|
||||||
content: Text("Dokument '${widget.message.name}' konnte nicht geladen werden:\n${e.description}"),
|
content: Text("Dokument '${widget.message.name}' konnte nicht geladen werden:\n${e.description}"),
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(onPressed: () {
|
TextButton(onPressed: () {
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
}, child: const Text("Ok"))
|
}, child: const Text('Ok'))
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@ -43,9 +43,9 @@ class _MessageViewState extends State<MessageView> {
|
|||||||
showDialog(
|
showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) => ConfirmDialog(
|
builder: (context) => ConfirmDialog(
|
||||||
title: "Link öffnen",
|
title: 'Link öffnen',
|
||||||
content: "Möchtest du den folgenden Link öffnen?\n${e.uri}",
|
content: 'Möchtest du den folgenden Link öffnen?\n${e.uri}',
|
||||||
confirmButton: "Öffnen",
|
confirmButton: 'Öffnen',
|
||||||
onConfirm: () => launchUrl(Uri.parse(e.uri), mode: LaunchMode.externalApplication),
|
onConfirm: () => launchUrl(Uri.parse(e.uri), mode: LaunchMode.externalApplication),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -8,10 +8,10 @@ class Roomplan extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: const Text("Raumplan"),
|
title: const Text('Raumplan'),
|
||||||
),
|
),
|
||||||
body: PhotoView(
|
body: PhotoView(
|
||||||
imageProvider: Image.asset("assets/img/raumplan.jpg").image,
|
imageProvider: Image.asset('assets/img/raumplan.jpg').image,
|
||||||
minScale: 0.5,
|
minScale: 0.5,
|
||||||
maxScale: 2.0,
|
maxScale: 2.0,
|
||||||
backgroundDecoration: BoxDecoration(color: Theme.of(context).colorScheme.background),
|
backgroundDecoration: BoxDecoration(color: Theme.of(context).colorScheme.background),
|
||||||
|
@ -16,18 +16,18 @@ class _QrShareViewState extends State<QrShareView> {
|
|||||||
length: 2,
|
length: 2,
|
||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: const Text("Teile die App"),
|
title: const Text('Teile die App'),
|
||||||
bottom: const TabBar(
|
bottom: const TabBar(
|
||||||
tabs: [
|
tabs: [
|
||||||
Tab(icon: Icon(Icons.android_outlined), text: "Android"),
|
Tab(icon: Icon(Icons.android_outlined), text: 'Android'),
|
||||||
Tab(icon: Icon(Icons.apple_outlined), text: "iOS & iPadOS"),
|
Tab(icon: Icon(Icons.apple_outlined), text: 'iOS & iPadOS'),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
body: const TabBarView(
|
body: const TabBarView(
|
||||||
children: [
|
children: [
|
||||||
AppSharePlatformView("Für Android", "https://play.google.com/store/apps/details?id=eu.mhsl.marianum.mobile.client"),
|
AppSharePlatformView('Für Android', 'https://play.google.com/store/apps/details?id=eu.mhsl.marianum.mobile.client'),
|
||||||
AppSharePlatformView("Für iOS & iPad", "https://apps.apple.com/us/app/marianum-fulda/id6458789560"),
|
AppSharePlatformView('Für iOS & iPad', 'https://apps.apple.com/us/app/marianum-fulda/id6458789560'),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -13,7 +13,7 @@ class SelectShareTypeDialog extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: const Icon(Icons.qr_code_2_outlined),
|
leading: const Icon(Icons.qr_code_2_outlined),
|
||||||
title: const Text("Per QR-Code"),
|
title: const Text('Per QR-Code'),
|
||||||
trailing: const Icon(Icons.arrow_right),
|
trailing: const Icon(Icons.arrow_right),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navigator.of(context).push(MaterialPageRoute(builder: (context) => const QrShareView()));
|
Navigator.of(context).push(MaterialPageRoute(builder: (context) => const QrShareView()));
|
||||||
@ -21,16 +21,16 @@ class SelectShareTypeDialog extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: const Icon(Icons.link_outlined),
|
leading: const Icon(Icons.link_outlined),
|
||||||
title: const Text("Per Link teilen"),
|
title: const Text('Per Link teilen'),
|
||||||
trailing: const Icon(Icons.arrow_right),
|
trailing: const Icon(Icons.arrow_right),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Share.share(
|
Share.share(
|
||||||
sharePositionOrigin: SharePositionOrigin.get(context),
|
sharePositionOrigin: SharePositionOrigin.get(context),
|
||||||
subject: "App Teilen",
|
subject: 'App Teilen',
|
||||||
"Hol dir die für das Marianum maßgeschneiderte App:"
|
'Hol dir die für das Marianum maßgeschneiderte App:'
|
||||||
"\n\nAndroid: https://play.google.com/store/apps/details?id=eu.mhsl.marianum.mobile.client "
|
'\n\nAndroid: https://play.google.com/store/apps/details?id=eu.mhsl.marianum.mobile.client '
|
||||||
"\niOS: https://apps.apple.com/us/app/marianum-fulda/id6458789560 "
|
'\niOS: https://apps.apple.com/us/app/marianum-fulda/id6458789560 '
|
||||||
"\n\nViel Spaß!"
|
'\n\nViel Spaß!'
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
@ -3,7 +3,7 @@ import 'dart:io';
|
|||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:in_app_review/in_app_review.dart';
|
import 'package:in_app_review/in_app_review.dart';
|
||||||
import 'package:marianum_mobile/extensions/renderNotNull.dart';
|
import '../../extensions/renderNotNull.dart';
|
||||||
import 'package:persistent_bottom_nav_bar_v2/persistent_bottom_nav_bar_v2.dart';
|
import 'package:persistent_bottom_nav_bar_v2/persistent_bottom_nav_bar_v2.dart';
|
||||||
|
|
||||||
import '../../widget/ListItem.dart';
|
import '../../widget/ListItem.dart';
|
||||||
@ -26,23 +26,23 @@ class Overhang extends StatelessWidget {
|
|||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: const Text("Mehr"),
|
title: const Text('Mehr'),
|
||||||
actions: [
|
actions: [
|
||||||
IconButton(onPressed: () => pushScreen(context, screen: const Settings(), withNavBar: false), icon: const Icon(Icons.settings))
|
IconButton(onPressed: () => pushScreen(context, screen: const Settings(), withNavBar: false), icon: const Icon(Icons.settings))
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
body: ListView(
|
body: ListView(
|
||||||
children: [
|
children: [
|
||||||
const ListItemNavigator(icon: Icons.newspaper, text: "Marianum Message", target: Message()),
|
const ListItemNavigator(icon: Icons.newspaper, text: 'Marianum Message', target: Message()),
|
||||||
const ListItemNavigator(icon: Icons.room, text: "Raumplan", target: Roomplan()),
|
const ListItemNavigator(icon: Icons.room, text: 'Raumplan', target: Roomplan()),
|
||||||
const ListItemNavigator(icon: Icons.calculate, text: "Notendurschnittsrechner", target: GradeAverage()),
|
const ListItemNavigator(icon: Icons.calculate, text: 'Notendurschnittsrechner', target: GradeAverage()),
|
||||||
const ListItemNavigator(icon: Icons.school_outlined, text: "Abiturrechner", target: AbiturCalculatorView()),
|
const ListItemNavigator(icon: Icons.school_outlined, text: "Abiturrechner", target: AbiturCalculatorView()),
|
||||||
const ListItemNavigator(icon: Icons.calendar_month, text: "Schulferien", target: Holidays()),
|
const ListItemNavigator(icon: Icons.calendar_month, text: 'Schulferien', target: Holidays()),
|
||||||
const Divider(),
|
const Divider(),
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: const Icon(Icons.share_outlined),
|
leading: const Icon(Icons.share_outlined),
|
||||||
title: const Text("Teile die App"),
|
title: const Text('Teile die App'),
|
||||||
subtitle: const Text("Mit Freunden und deiner Klasse teilen"),
|
subtitle: const Text('Mit Freunden und deiner Klasse teilen'),
|
||||||
trailing: const Icon(Icons.arrow_right),
|
trailing: const Icon(Icons.arrow_right),
|
||||||
onTap: () => showDialog(context: context, builder: (context) => const SelectShareTypeDialog())
|
onTap: () => showDialog(context: context, builder: (context) => const SelectShareTypeDialog())
|
||||||
),
|
),
|
||||||
@ -52,19 +52,19 @@ class Overhang extends StatelessWidget {
|
|||||||
if(!snapshot.hasData) return const SizedBox.shrink();
|
if(!snapshot.hasData) return const SizedBox.shrink();
|
||||||
|
|
||||||
String? getPlatformStoreName() {
|
String? getPlatformStoreName() {
|
||||||
if(Platform.isAndroid) return "Play store";
|
if(Platform.isAndroid) return 'Play store';
|
||||||
if(Platform.isIOS) return "App store";
|
if(Platform.isIOS) return 'App store';
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ListTile(
|
return ListTile(
|
||||||
leading: const CenteredLeading(Icon(Icons.star_rate_outlined)),
|
leading: const CenteredLeading(Icon(Icons.star_rate_outlined)),
|
||||||
title: const Text("App Bewerten"),
|
title: const Text('App bewerten'),
|
||||||
subtitle: getPlatformStoreName().wrapNullable((data) => Text("Im $data")),
|
subtitle: getPlatformStoreName().wrapNullable((data) => Text('Im $data')),
|
||||||
trailing: const Icon(Icons.arrow_right),
|
trailing: const Icon(Icons.arrow_right),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
InAppReview.instance.openStoreListing(appStoreId: "6458789560").then(
|
InAppReview.instance.openStoreListing(appStoreId: '6458789560').then(
|
||||||
(value) => InfoDialog.show(context, "Vielen Dank!"),
|
(value) => InfoDialog.show(context, 'Vielen Dank!'),
|
||||||
onError: (error) => InfoDialog.show(context, error.toString())
|
onError: (error) => InfoDialog.show(context, error.toString())
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@ -73,8 +73,8 @@ class Overhang extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: const CenteredLeading(Icon(Icons.feedback_outlined)),
|
leading: const CenteredLeading(Icon(Icons.feedback_outlined)),
|
||||||
title: const Text("Du hast eine Idee?"),
|
title: const Text('Du hast eine Idee?'),
|
||||||
subtitle: const Text("Fehler und Verbessungsvorschläge"),
|
subtitle: const Text('Fehler und Verbessungsvorschläge'),
|
||||||
trailing: const Icon(Icons.arrow_right),
|
trailing: const Icon(Icons.arrow_right),
|
||||||
onTap: () => showDialog(context: context, barrierDismissible: false, builder: (context) => const FeedbackDialog()),
|
onTap: () => showDialog(context: context, barrierDismissible: false, builder: (context) => const FeedbackDialog()),
|
||||||
),
|
),
|
||||||
|
@ -66,7 +66,7 @@ class _ChatInfoState extends State<ChatInfo> {
|
|||||||
if(participants != null) ...[
|
if(participants != null) ...[
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: const Icon(Icons.supervised_user_circle),
|
leading: const Icon(Icons.supervised_user_circle),
|
||||||
title: Text("${participants!.data.length} Teilnehmer"),
|
title: Text('${participants!.data.length} Teilnehmer'),
|
||||||
trailing: const Icon(Icons.arrow_right),
|
trailing: const Icon(Icons.arrow_right),
|
||||||
onTap: () => TalkNavigator.pushSplitView(context, ParticipantsListView(participants!)),
|
onTap: () => TalkNavigator.pushSplitView(context, ParticipantsListView(participants!)),
|
||||||
),
|
),
|
||||||
|
@ -16,7 +16,7 @@ class _ParticipantsListViewState extends State<ParticipantsListView> {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: const Text("Teilnehmende"),
|
title: const Text('Teilnehmende'),
|
||||||
),
|
),
|
||||||
body: ListView(
|
body: ListView(
|
||||||
children: widget.participantsResponse.data.map((participant) {
|
children: widget.participantsResponse.data.map((participant) {
|
||||||
|
@ -40,9 +40,9 @@ class _ChatListState extends State<ChatList> {
|
|||||||
|
|
||||||
ConfirmDialog(
|
ConfirmDialog(
|
||||||
icon: Icons.notifications_active_outlined,
|
icon: Icons.notifications_active_outlined,
|
||||||
title: "Benachrichtigungen aktivieren",
|
title: 'Benachrichtigungen aktivieren',
|
||||||
content: "Auf wunsch kannst du Push-Benachrichtigungen aktivieren. Deine Einstellungen kannst du jederzeit ändern.",
|
content: 'Auf wunsch kannst du Push-Benachrichtigungen aktivieren. Deine Einstellungen kannst du jederzeit ändern.',
|
||||||
confirmButton: "Weiter",
|
confirmButton: 'Weiter',
|
||||||
onConfirm: () {
|
onConfirm: () {
|
||||||
FirebaseMessaging.instance.requestPermission(
|
FirebaseMessaging.instance.requestPermission(
|
||||||
provisional: false
|
provisional: false
|
||||||
@ -53,7 +53,7 @@ class _ChatListState extends State<ChatList> {
|
|||||||
break;
|
break;
|
||||||
case AuthorizationStatus.denied:
|
case AuthorizationStatus.denied:
|
||||||
showDialog(context: context, builder: (context) => const AlertDialog(
|
showDialog(context: context, builder: (context) => const AlertDialog(
|
||||||
content: Text("Du kannst die Benachrichtigungen später jederzeit in den App-Einstellungen aktivieren."),
|
content: Text('Du kannst die Benachrichtigungen später jederzeit in den App-Einstellungen aktivieren.'),
|
||||||
));
|
));
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@ -79,7 +79,7 @@ class _ChatListState extends State<ChatList> {
|
|||||||
breakpoint: 1000,
|
breakpoint: 1000,
|
||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: const Text("Talk"),
|
title: const Text('Talk'),
|
||||||
actions: [
|
actions: [
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: const Icon(Icons.search),
|
icon: const Icon(Icons.search),
|
||||||
@ -91,16 +91,16 @@ class _ChatListState extends State<ChatList> {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
floatingActionButton: FloatingActionButton(
|
floatingActionButton: FloatingActionButton(
|
||||||
heroTag: "createChat",
|
heroTag: 'createChat',
|
||||||
backgroundColor: Theme.of(context).primaryColor,
|
backgroundColor: Theme.of(context).primaryColor,
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
showSearch(context: context, delegate: JoinChat()).then((username) {
|
showSearch(context: context, delegate: JoinChat()).then((username) {
|
||||||
if(username == null) return;
|
if(username == null) return;
|
||||||
|
|
||||||
ConfirmDialog(
|
ConfirmDialog(
|
||||||
title: "Chat starten",
|
title: 'Chat starten',
|
||||||
content: "Möchtest du einen Chat mit Nutzer '$username' starten?",
|
content: "Möchtest du einen Chat mit Nutzer '$username' starten?",
|
||||||
confirmButton: "Chat starten",
|
confirmButton: 'Chat starten',
|
||||||
onConfirm: () {
|
onConfirm: () {
|
||||||
CreateRoom(CreateRoomParams(
|
CreateRoom(CreateRoomParams(
|
||||||
roomType: 1,
|
roomType: 1,
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:marianum_mobile/extensions/dateTime.dart';
|
import '../../../extensions/dateTime.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
import '../../../api/marianumcloud/talk/chat/getChatResponse.dart';
|
import '../../../api/marianumcloud/talk/chat/getChatResponse.dart';
|
||||||
@ -51,8 +51,8 @@ class _ChatViewState extends State<ChatView> {
|
|||||||
data.getChatResponse.sortByTimestamp().forEach((element) {
|
data.getChatResponse.sortByTimestamp().forEach((element) {
|
||||||
DateTime elementDate = DateTime.fromMillisecondsSinceEpoch(element.timestamp * 1000);
|
DateTime elementDate = DateTime.fromMillisecondsSinceEpoch(element.timestamp * 1000);
|
||||||
|
|
||||||
if(element.systemMessage.contains("reaction")) return;
|
if(element.systemMessage.contains('reaction')) return;
|
||||||
int commonRead = int.parse(data.getChatResponse.headers?['x-chat-last-common-read'] ?? "0");
|
int commonRead = int.parse(data.getChatResponse.headers?['x-chat-last-common-read'] ?? '0');
|
||||||
|
|
||||||
if(!elementDate.isSameDay(lastDate)) {
|
if(!elementDate.isSameDay(lastDate)) {
|
||||||
lastDate = elementDate;
|
lastDate = elementDate;
|
||||||
@ -81,8 +81,8 @@ class _ChatViewState extends State<ChatView> {
|
|||||||
context: context,
|
context: context,
|
||||||
isSender: false,
|
isSender: false,
|
||||||
bubbleData: GetChatResponseObject.getTextDummy(
|
bubbleData: GetChatResponseObject.getTextDummy(
|
||||||
"Zurzeit können in dieser App nur die letzten 200 vergangenen Nachrichten angezeigt werden. "
|
'Zurzeit können in dieser App nur die letzten 200 vergangenen Nachrichten angezeigt werden. '
|
||||||
"Um ältere Nachrichten abzurufen verwende die Webversion unter https://cloud.marianum-fulda.de"
|
'Um ältere Nachrichten abzurufen verwende die Webversion unter https://cloud.marianum-fulda.de'
|
||||||
),
|
),
|
||||||
chatData: widget.room,
|
chatData: widget.room,
|
||||||
refetch: _query,
|
refetch: _query,
|
||||||
@ -111,7 +111,7 @@ class _ChatViewState extends State<ChatView> {
|
|||||||
body: Container(
|
body: Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
image: DecorationImage(
|
image: DecorationImage(
|
||||||
image: const AssetImage("assets/background/chat.png"),
|
image: const AssetImage('assets/background/chat.png'),
|
||||||
scale: 1.5,
|
scale: 1.5,
|
||||||
opacity: 1,
|
opacity: 1,
|
||||||
repeat: ImageRepeat.repeat,
|
repeat: ImageRepeat.repeat,
|
||||||
|
@ -5,7 +5,7 @@ import 'package:flutter/foundation.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:jiffy/jiffy.dart';
|
import 'package:jiffy/jiffy.dart';
|
||||||
import 'package:marianum_mobile/extensions/text.dart';
|
import '../../../../extensions/text.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
import '../../../../api/marianumcloud/talk/chat/getChatResponse.dart';
|
import '../../../../api/marianumcloud/talk/chat/getChatResponse.dart';
|
||||||
@ -114,7 +114,7 @@ class _ChatBubbleState extends State<ChatBubble> {
|
|||||||
);
|
);
|
||||||
|
|
||||||
Text timeText = Text(
|
Text timeText = Text(
|
||||||
Jiffy.parseFromMillisecondsSinceEpoch(widget.bubbleData.timestamp * 1000).format(pattern: "HH:mm"),
|
Jiffy.parseFromMillisecondsSinceEpoch(widget.bubbleData.timestamp * 1000).format(pattern: 'HH:mm'),
|
||||||
textAlign: TextAlign.end,
|
textAlign: TextAlign.end,
|
||||||
style: TextStyle(color: widget.timeIconColor, fontSize: widget.timeIconSize),
|
style: TextStyle(color: widget.timeIconColor, fontSize: widget.timeIconSize),
|
||||||
);
|
);
|
||||||
@ -184,7 +184,7 @@ class _ChatBubbleState extends State<ChatBubble> {
|
|||||||
),
|
),
|
||||||
onLongPress: () {
|
onLongPress: () {
|
||||||
showDialog(context: context, builder: (context) {
|
showDialog(context: context, builder: (context) {
|
||||||
List<String> commonReactions = ["👍", "👎", "😆", "❤️", "👀", "🤔"];
|
List<String> commonReactions = ['👍', '👎', '😆', '❤️', '👀', '🤔'];
|
||||||
bool canReact = widget.bubbleData.messageType == GetRoomResponseObjectMessageType.comment;
|
bool canReact = widget.bubbleData.messageType == GetRoomResponseObjectMessageType.comment;
|
||||||
return SimpleDialog(
|
return SimpleDialog(
|
||||||
children: [
|
children: [
|
||||||
@ -222,7 +222,7 @@ class _ChatBubbleState extends State<ChatBubble> {
|
|||||||
visible: canReact,
|
visible: canReact,
|
||||||
child: ListTile(
|
child: ListTile(
|
||||||
leading: const Icon(Icons.add_reaction_outlined),
|
leading: const Icon(Icons.add_reaction_outlined),
|
||||||
title: const Text("Reaktionen"),
|
title: const Text('Reaktionen'),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navigator.of(context).push(MaterialPageRoute(builder: (context) => MessageReactions(
|
Navigator.of(context).push(MaterialPageRoute(builder: (context) => MessageReactions(
|
||||||
token: widget.chatData.token,
|
token: widget.chatData.token,
|
||||||
@ -235,7 +235,7 @@ class _ChatBubbleState extends State<ChatBubble> {
|
|||||||
visible: !message.containsFile,
|
visible: !message.containsFile,
|
||||||
child: ListTile(
|
child: ListTile(
|
||||||
leading: const Icon(Icons.copy),
|
leading: const Icon(Icons.copy),
|
||||||
title: const Text("Nachricht kopieren"),
|
title: const Text('Nachricht kopieren'),
|
||||||
onTap: () => {
|
onTap: () => {
|
||||||
Clipboard.setData(ClipboardData(text: widget.bubbleData.message)),
|
Clipboard.setData(ClipboardData(text: widget.bubbleData.message)),
|
||||||
Navigator.of(context).pop(),
|
Navigator.of(context).pop(),
|
||||||
@ -256,7 +256,7 @@ class _ChatBubbleState extends State<ChatBubble> {
|
|||||||
visible: widget.isSender && DateTime.fromMillisecondsSinceEpoch(widget.bubbleData.timestamp * 1000).add(const Duration(hours: 6)).isAfter(DateTime.now()),
|
visible: widget.isSender && DateTime.fromMillisecondsSinceEpoch(widget.bubbleData.timestamp * 1000).add(const Duration(hours: 6)).isAfter(DateTime.now()),
|
||||||
child: ListTile(
|
child: ListTile(
|
||||||
leading: const Icon(Icons.delete_outline),
|
leading: const Icon(Icons.delete_outline),
|
||||||
title: const Text("Nachricht löschen"),
|
title: const Text('Nachricht löschen'),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
DeleteMessage(widget.chatData.token, widget.bubbleData.id).run().then((value) {
|
DeleteMessage(widget.chatData.token, widget.bubbleData.id).run().then((value) {
|
||||||
Provider.of<ChatProps>(context, listen: false).run();
|
Provider.of<ChatProps>(context, listen: false).run();
|
||||||
@ -276,12 +276,12 @@ class _ChatBubbleState extends State<ChatBubble> {
|
|||||||
if(downloadProgress > 0) {
|
if(downloadProgress > 0) {
|
||||||
showDialog(context: context, builder: (context) {
|
showDialog(context: context, builder: (context) {
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
title: const Text("Download abbrechen?"),
|
title: const Text('Download abbrechen?'),
|
||||||
content: const Text("Möchtest du den Download abbrechen?"),
|
content: const Text('Möchtest du den Download abbrechen?'),
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(onPressed: () {
|
TextButton(onPressed: () {
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
}, child: const Text("Nein")),
|
}, child: const Text('Nein')),
|
||||||
TextButton(onPressed: () {
|
TextButton(onPressed: () {
|
||||||
downloadCore?.then((value) {
|
downloadCore?.then((value) {
|
||||||
if(!value.isCancelled) value.cancel();
|
if(!value.isCancelled) value.cancel();
|
||||||
@ -291,7 +291,7 @@ class _ChatBubbleState extends State<ChatBubble> {
|
|||||||
downloadProgress = 0;
|
downloadProgress = 0;
|
||||||
downloadCore = null;
|
downloadCore = null;
|
||||||
});
|
});
|
||||||
}, child: const Text("Ja, Abbrechen"))
|
}, child: const Text('Ja, Abbrechen'))
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@ -336,7 +336,7 @@ class _ChatBubbleState extends State<ChatBubble> {
|
|||||||
return Container(
|
return Container(
|
||||||
margin: const EdgeInsets.only(right: 2.5, left: 2.5),
|
margin: const EdgeInsets.only(right: 2.5, left: 2.5),
|
||||||
child: ActionChip(
|
child: ActionChip(
|
||||||
label: Text("${e.key} ${e.value}"),
|
label: Text('${e.key} ${e.value}'),
|
||||||
visualDensity: const VisualDensity(vertical: VisualDensity.minimumDensity, horizontal: VisualDensity.minimumDensity),
|
visualDensity: const VisualDensity(vertical: VisualDensity.minimumDensity, horizontal: VisualDensity.minimumDensity),
|
||||||
padding: EdgeInsets.zero,
|
padding: EdgeInsets.zero,
|
||||||
backgroundColor: hasSelfReacted ? Theme.of(context).primaryColor : null,
|
backgroundColor: hasSelfReacted ? Theme.of(context).primaryColor : null,
|
||||||
|
@ -14,14 +14,14 @@ class ChatMessage {
|
|||||||
Map<String, RichObjectString>? originalData;
|
Map<String, RichObjectString>? originalData;
|
||||||
|
|
||||||
RichObjectString? file;
|
RichObjectString? file;
|
||||||
String content = "";
|
String content = '';
|
||||||
|
|
||||||
bool get containsFile => file != null;
|
bool get containsFile => file != null;
|
||||||
|
|
||||||
ChatMessage({required this.originalMessage, this.originalData}) {
|
ChatMessage({required this.originalMessage, this.originalData}) {
|
||||||
if(originalData?.containsKey("file") ?? false) {
|
if(originalData?.containsKey('file') ?? false) {
|
||||||
file = originalData?['file'];
|
file = originalData?['file'];
|
||||||
content = file?.name ?? "Datei";
|
content = file?.name ?? 'Datei';
|
||||||
} else {
|
} else {
|
||||||
content = RichObjectStringProcessor.parseToString(originalMessage, originalData);
|
content = RichObjectStringProcessor.parseToString(originalMessage, originalData);
|
||||||
}
|
}
|
||||||
@ -56,7 +56,7 @@ class ChatMessage {
|
|||||||
fadeInDuration: Duration.zero,
|
fadeInDuration: Duration.zero,
|
||||||
fadeOutDuration: Duration.zero,
|
fadeOutDuration: Duration.zero,
|
||||||
errorListener: (value) {},
|
errorListener: (value) {},
|
||||||
imageUrl: "https://${AccountData().buildHttpAuthString()}@${EndpointData().nextcloud().full()}/index.php/core/preview?fileId=${file!.id}&x=100&y=-1&a=1",
|
imageUrl: 'https://${AccountData().buildHttpAuthString()}@${EndpointData().nextcloud().full()}/index.php/core/preview?fileId=${file!.id}&x=100&y=-1&a=1',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,9 +41,9 @@ class _ChatTextfieldState extends State<ChatTextfield> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
String filename = "${path.split("/").last.split(".").first}-${const Uuid().v4()}.${path.split(".").last}";
|
String filename = "${path.split("/").last.split(".").first}-${const Uuid().v4()}.${path.split(".").last}";
|
||||||
String shareFolder = "MarianumMobile";
|
String shareFolder = 'MarianumMobile';
|
||||||
WebdavApi.webdav.then((webdav) {
|
WebdavApi.webdav.then((webdav) {
|
||||||
webdav.mkcol(PathUri.parse("/$shareFolder"));
|
webdav.mkcol(PathUri.parse('/$shareFolder'));
|
||||||
});
|
});
|
||||||
|
|
||||||
showDialog(context: context, builder: (context) => FileUploadDialog(
|
showDialog(context: context, builder: (context) => FileUploadDialog(
|
||||||
@ -55,7 +55,7 @@ class _ChatTextfieldState extends State<ChatTextfield> {
|
|||||||
FileSharingApi().share(FileSharingApiParams(
|
FileSharingApi().share(FileSharingApiParams(
|
||||||
shareType: 10,
|
shareType: 10,
|
||||||
shareWith: widget.sendToToken,
|
shareWith: widget.sendToToken,
|
||||||
path: "$shareFolder/$filename",
|
path: '$shareFolder/$filename',
|
||||||
)).then((value) => _query());
|
)).then((value) => _query());
|
||||||
},
|
},
|
||||||
), barrierDismissible: false);
|
), barrierDismissible: false);
|
||||||
@ -77,7 +77,7 @@ class _ChatTextfieldState extends State<ChatTextfield> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
_textBoxController.text = settings.val().talkSettings.drafts[widget.sendToToken] ?? "";
|
_textBoxController.text = settings.val().talkSettings.drafts[widget.sendToToken] ?? '';
|
||||||
|
|
||||||
return Stack(
|
return Stack(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
@ -95,12 +95,10 @@ class _ChatTextfieldState extends State<ChatTextfield> {
|
|||||||
children: [
|
children: [
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: const Icon(Icons.file_open),
|
leading: const Icon(Icons.file_open),
|
||||||
title: const Text("Aus Dateien auswählen"),
|
title: const Text('Aus Dateien auswählen'),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
context.loaderOverlay.show();
|
context.loaderOverlay.show();
|
||||||
FilePick.documentPick().then((value) {
|
FilePick.documentPick().then(mediaUpload);
|
||||||
mediaUpload(value);
|
|
||||||
});
|
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@ -108,7 +106,7 @@ class _ChatTextfieldState extends State<ChatTextfield> {
|
|||||||
visible: !Platform.isIOS,
|
visible: !Platform.isIOS,
|
||||||
child: ListTile(
|
child: ListTile(
|
||||||
leading: const Icon(Icons.image),
|
leading: const Icon(Icons.image),
|
||||||
title: const Text("Aus Gallerie auswählen"),
|
title: const Text('Aus Gallerie auswählen'),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
context.loaderOverlay.show();
|
context.loaderOverlay.show();
|
||||||
FilePick.galleryPick().then((value) {
|
FilePick.galleryPick().then((value) {
|
||||||
@ -147,12 +145,12 @@ class _ChatTextfieldState extends State<ChatTextfield> {
|
|||||||
maxLines: 7,
|
maxLines: 7,
|
||||||
minLines: 1,
|
minLines: 1,
|
||||||
decoration: const InputDecoration(
|
decoration: const InputDecoration(
|
||||||
hintText: "Nachricht schreiben...",
|
hintText: 'Nachricht schreiben...',
|
||||||
border: InputBorder.none,
|
border: InputBorder.none,
|
||||||
),
|
),
|
||||||
onChanged: (String text) {
|
onChanged: (String text) {
|
||||||
if(text.trim().toLowerCase() == "marbot marbot marbot") {
|
if(text.trim().toLowerCase() == 'marbot marbot marbot') {
|
||||||
var newText = "Roboter sind cool und so, aber Marbots sind besser!";
|
var newText = 'Roboter sind cool und so, aber Marbots sind besser!';
|
||||||
_textBoxController.text = newText;
|
_textBoxController.text = newText;
|
||||||
text = newText;
|
text = newText;
|
||||||
}
|
}
|
||||||
@ -175,8 +173,8 @@ class _ChatTextfieldState extends State<ChatTextfield> {
|
|||||||
setState(() {
|
setState(() {
|
||||||
isLoading = false;
|
isLoading = false;
|
||||||
});
|
});
|
||||||
_textBoxController.text = "";
|
_textBoxController.text = '';
|
||||||
setDraft("");
|
setDraft('');
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
backgroundColor: Theme.of(context).primaryColor,
|
backgroundColor: Theme.of(context).primaryColor,
|
||||||
|
@ -37,7 +37,7 @@ class _ChatTileState extends State<ChatTile> {
|
|||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
SharedPreferences.getInstance().then((value) => {
|
SharedPreferences.getInstance().then((value) => {
|
||||||
username = value.getString("username")!
|
username = value.getString('username')!
|
||||||
});
|
});
|
||||||
|
|
||||||
bool isGroup = widget.data.type != GetRoomResponseObjectConversationType.oneToOne;
|
bool isGroup = widget.data.type != GetRoomResponseObjectConversationType.oneToOne;
|
||||||
@ -110,7 +110,7 @@ class _ChatTileState extends State<ChatTile> {
|
|||||||
minHeight: 20,
|
minHeight: 20,
|
||||||
),
|
),
|
||||||
child: Text(
|
child: Text(
|
||||||
"${widget.data.unreadMessages}",
|
'${widget.data.unreadMessages}',
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
fontSize: 15,
|
fontSize: 15,
|
||||||
@ -132,7 +132,7 @@ class _ChatTileState extends State<ChatTile> {
|
|||||||
visible: widget.data.unreadMessages > 0,
|
visible: widget.data.unreadMessages > 0,
|
||||||
replacement: ListTile(
|
replacement: ListTile(
|
||||||
leading: const Icon(Icons.mark_chat_unread_outlined),
|
leading: const Icon(Icons.mark_chat_unread_outlined),
|
||||||
title: const Text("Als ungelesen markieren"),
|
title: const Text('Als ungelesen markieren'),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
SetReadMarker(widget.data.token, false).run().then((value) => widget.query(renew: true));
|
SetReadMarker(widget.data.token, false).run().then((value) => widget.query(renew: true));
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
@ -140,7 +140,7 @@ class _ChatTileState extends State<ChatTile> {
|
|||||||
),
|
),
|
||||||
child: ListTile(
|
child: ListTile(
|
||||||
leading: const Icon(Icons.mark_chat_read_outlined),
|
leading: const Icon(Icons.mark_chat_read_outlined),
|
||||||
title: const Text("Als gelesen markieren"),
|
title: const Text('Als gelesen markieren'),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
setCurrentAsRead();
|
setCurrentAsRead();
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
@ -151,7 +151,7 @@ class _ChatTileState extends State<ChatTile> {
|
|||||||
visible: widget.data.isFavorite,
|
visible: widget.data.isFavorite,
|
||||||
replacement: ListTile(
|
replacement: ListTile(
|
||||||
leading: const Icon(Icons.star_outline),
|
leading: const Icon(Icons.star_outline),
|
||||||
title: const Text("Zu Favoriten hinzufügen"),
|
title: const Text('Zu Favoriten hinzufügen'),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
SetFavorite(widget.data.token, true).run().then((value) => widget.query(renew: true));
|
SetFavorite(widget.data.token, true).run().then((value) => widget.query(renew: true));
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
@ -159,7 +159,7 @@ class _ChatTileState extends State<ChatTile> {
|
|||||||
),
|
),
|
||||||
child: ListTile(
|
child: ListTile(
|
||||||
leading: const Icon(Icons.stars_outlined),
|
leading: const Icon(Icons.stars_outlined),
|
||||||
title: const Text("Von Favoriten entfernen"),
|
title: const Text('Von Favoriten entfernen'),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
SetFavorite(widget.data.token, false).run().then((value) => widget.query(renew: true));
|
SetFavorite(widget.data.token, false).run().then((value) => widget.query(renew: true));
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
@ -168,12 +168,12 @@ class _ChatTileState extends State<ChatTile> {
|
|||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: const Icon(Icons.delete_outline),
|
leading: const Icon(Icons.delete_outline),
|
||||||
title: const Text("Konversation verlassen"),
|
title: const Text('Konversation verlassen'),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
ConfirmDialog(
|
ConfirmDialog(
|
||||||
title: "Chat verlassen",
|
title: 'Chat verlassen',
|
||||||
content: "Du benötigst ggf. eine Einladung um erneut beizutreten.",
|
content: 'Du benötigst ggf. eine Einladung um erneut beizutreten.',
|
||||||
confirmButton: "Löschen",
|
confirmButton: 'Löschen',
|
||||||
onConfirm: () {
|
onConfirm: () {
|
||||||
LeaveRoom(widget.data.token).run().then((value) => widget.query(renew: true));
|
LeaveRoom(widget.data.token).run().then((value) => widget.query(renew: true));
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
|
@ -17,10 +17,10 @@ class SplitViewPlaceholder extends StatelessWidget {
|
|||||||
data: MediaQuery.of(context).copyWith(
|
data: MediaQuery.of(context).copyWith(
|
||||||
invertColors: !AppTheme.isDarkMode(context),
|
invertColors: !AppTheme.isDarkMode(context),
|
||||||
),
|
),
|
||||||
child: Image.asset("assets/logo/icon.png", height: 200),
|
child: Image.asset('assets/logo/icon.png', height: 200),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 30),
|
const SizedBox(height: 30),
|
||||||
const Text("Marianum Fulda\nTalk", textAlign: TextAlign.center, style: TextStyle(fontSize: 30)),
|
const Text('Marianum Fulda\nTalk', textAlign: TextAlign.center, style: TextStyle(fontSize: 30)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
@ -33,7 +33,7 @@ class JoinChat extends SearchDelegate<String> {
|
|||||||
return const SizedBox.shrink();
|
return const SizedBox.shrink();
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
if(query.isNotEmpty) IconButton(onPressed: () => query = "", icon: const Icon(Icons.delete)),
|
if(query.isNotEmpty) IconButton(onPressed: () => query = '', icon: const Icon(Icons.delete)),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -48,7 +48,7 @@ class JoinChat extends SearchDelegate<String> {
|
|||||||
|
|
||||||
if(query.isEmpty) {
|
if(query.isEmpty) {
|
||||||
return const PlaceholderView(
|
return const PlaceholderView(
|
||||||
text: "Suche nach benutzern",
|
text: 'Suche nach benutzern',
|
||||||
icon: Icons.person_search_outlined,
|
icon: Icons.person_search_outlined,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -63,7 +63,7 @@ class JoinChat extends SearchDelegate<String> {
|
|||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
AutocompleteResponseObject object = snapshot.data!.data[index];
|
AutocompleteResponseObject object = snapshot.data!.data[index];
|
||||||
CircleAvatar circleAvatar = CircleAvatar(
|
CircleAvatar circleAvatar = CircleAvatar(
|
||||||
foregroundImage: Image.network("https://${EndpointData().nextcloud().full()}/avatar/${object.id}/128").image,
|
foregroundImage: Image.network('https://${EndpointData().nextcloud().full()}/avatar/${object.id}/128').image,
|
||||||
backgroundColor: Theme.of(context).primaryColor,
|
backgroundColor: Theme.of(context).primaryColor,
|
||||||
foregroundColor: Colors.white,
|
foregroundColor: Colors.white,
|
||||||
child: const Icon(Icons.person),
|
child: const Icon(Icons.person),
|
||||||
@ -80,7 +80,7 @@ class JoinChat extends SearchDelegate<String> {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
} else if(snapshot.hasError) {
|
} else if(snapshot.hasError) {
|
||||||
return const PlaceholderView(icon: Icons.search_off, text: "Ein fehler ist aufgetreten. Bist du mit dem Internet verbunden?");
|
return const PlaceholderView(icon: Icons.search_off, text: 'Ein fehler ist aufgetreten. Bist du mit dem Internet verbunden?');
|
||||||
}
|
}
|
||||||
|
|
||||||
return const Center(child: CircularProgressIndicator());
|
return const Center(child: CircularProgressIndicator());
|
||||||
|
@ -33,13 +33,13 @@ class _MessageReactionsState extends State<MessageReactions> {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: const Text("Reaktionen"),
|
title: const Text('Reaktionen'),
|
||||||
),
|
),
|
||||||
body: FutureBuilder(
|
body: FutureBuilder(
|
||||||
future: data,
|
future: data,
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
if(snapshot.connectionState == ConnectionState.waiting) return const LoadingSpinner();
|
if(snapshot.connectionState == ConnectionState.waiting) return const LoadingSpinner();
|
||||||
if(snapshot.data == null) return const PlaceholderView(icon: Icons.search_off_outlined, text: "Keine Reaktionen gefunden!");
|
if(snapshot.data == null) return const PlaceholderView(icon: Icons.search_off_outlined, text: 'Keine Reaktionen gefunden!');
|
||||||
return ListView(
|
return ListView(
|
||||||
children: [
|
children: [
|
||||||
...snapshot.data!.data.entries.map<Widget>((entry) {
|
...snapshot.data!.data.entries.map<Widget>((entry) {
|
||||||
@ -49,17 +49,17 @@ class _MessageReactionsState extends State<MessageReactions> {
|
|||||||
iconColor: Theme.of(context).colorScheme.onSurface,
|
iconColor: Theme.of(context).colorScheme.onSurface,
|
||||||
collapsedIconColor: Theme.of(context).colorScheme.onSurface,
|
collapsedIconColor: Theme.of(context).colorScheme.onSurface,
|
||||||
|
|
||||||
subtitle: const Text("Tippe für mehr"),
|
subtitle: const Text('Tippe für mehr'),
|
||||||
leading: CenteredLeading(Text(entry.key)),
|
leading: CenteredLeading(Text(entry.key)),
|
||||||
title: Text("${entry.value.length} mal reagiert"),
|
title: Text('${entry.value.length} mal reagiert'),
|
||||||
children: entry.value.map((e) {
|
children: entry.value.map((e) {
|
||||||
bool isSelf = AccountData().getUsername() == e.actorId;
|
bool isSelf = AccountData().getUsername() == e.actorId;
|
||||||
return ListTile(
|
return ListTile(
|
||||||
leading: UserAvatar(id: e.actorId, isGroup: false),
|
leading: UserAvatar(id: e.actorId, isGroup: false),
|
||||||
title: Text(e.actorDisplayName),
|
title: Text(e.actorDisplayName),
|
||||||
subtitle: isSelf
|
subtitle: isSelf
|
||||||
? const Text("Du")
|
? const Text('Du')
|
||||||
: e.actorType == GetReactionsResponseObjectActorType.guests ? const Text("Gast") : null,
|
: e.actorType == GetReactionsResponseObjectActorType.guests ? const Text('Gast') : null,
|
||||||
trailing: isSelf
|
trailing: isSelf
|
||||||
? null
|
? null
|
||||||
: Visibility(
|
: Visibility(
|
||||||
|
@ -11,7 +11,7 @@ class SearchChat extends SearchDelegate {
|
|||||||
@override
|
@override
|
||||||
List<Widget>? buildActions(BuildContext context) {
|
List<Widget>? buildActions(BuildContext context) {
|
||||||
return [
|
return [
|
||||||
if(query.isNotEmpty) IconButton(onPressed: () => query = "", icon: const Icon(Icons.delete)),
|
if(query.isNotEmpty) IconButton(onPressed: () => query = '', icon: const Icon(Icons.delete)),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,7 +53,7 @@ class _AppointmentComponentState extends State<AppointmentComponent> {
|
|||||||
FittedBox(
|
FittedBox(
|
||||||
fit: BoxFit.fitWidth,
|
fit: BoxFit.fitWidth,
|
||||||
child: Text(
|
child: Text(
|
||||||
(meeting.location == null || meeting.location!.isEmpty ? " " : meeting.location!),
|
(meeting.location == null || meeting.location!.isEmpty ? ' ' : meeting.location!),
|
||||||
maxLines: 3,
|
maxLines: 3,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
softWrap: true,
|
softWrap: true,
|
||||||
|
@ -27,9 +27,9 @@ import 'customTimetableEventEditDialog.dart';
|
|||||||
|
|
||||||
class AppointmentDetails {
|
class AppointmentDetails {
|
||||||
static String _getEventPrefix(String? code) {
|
static String _getEventPrefix(String? code) {
|
||||||
if(code == "cancelled") return "Entfällt: ";
|
if(code == 'cancelled') return 'Entfällt: ';
|
||||||
if(code == "irregular") return "Änderung: ";
|
if(code == 'irregular') return 'Änderung: ';
|
||||||
return code ?? "";
|
return code ?? '';
|
||||||
}
|
}
|
||||||
|
|
||||||
static void show(BuildContext context, TimetableProps webuntisData, Appointment appointment) {
|
static void show(BuildContext context, TimetableProps webuntisData, Appointment appointment) {
|
||||||
@ -65,13 +65,13 @@ class AppointmentDetails {
|
|||||||
try {
|
try {
|
||||||
subject = webuntisData.getSubjectsResponse.result.firstWhere((subject) => subject.id == timetableData.su[0].id);
|
subject = webuntisData.getSubjectsResponse.result.firstWhere((subject) => subject.id == timetableData.su[0].id);
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
subject = GetSubjectsResponseObject(0, "?", "Unbekannt", "?", true);
|
subject = GetSubjectsResponseObject(0, '?', 'Unbekannt', '?', true);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
room = webuntisData.getRoomsResponse.result.firstWhere((room) => room.id == timetableData.ro[0].id);
|
room = webuntisData.getRoomsResponse.result.firstWhere((room) => room.id == timetableData.ro[0].id);
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
room = GetRoomsResponseObject(0, "?", "Unbekannt", true, "?");
|
room = GetRoomsResponseObject(0, '?', 'Unbekannt', true, '?');
|
||||||
}
|
}
|
||||||
|
|
||||||
_bottomSheet(
|
_bottomSheet(
|
||||||
@ -99,7 +99,7 @@ class AppointmentDetails {
|
|||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: const Icon(Icons.room),
|
leading: const Icon(Icons.room),
|
||||||
title: Text("Raum: ${room.name} (${room.longName})"),
|
title: Text('Raum: ${room.name} (${room.longName})'),
|
||||||
trailing: IconButton(
|
trailing: IconButton(
|
||||||
icon: const Icon(Icons.house_outlined),
|
icon: const Icon(Icons.house_outlined),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
@ -111,7 +111,7 @@ class AppointmentDetails {
|
|||||||
leading: const Icon(Icons.person),
|
leading: const Icon(Icons.person),
|
||||||
title: timetableData.te.isNotEmpty
|
title: timetableData.te.isNotEmpty
|
||||||
? Text("Lehrkraft: ${timetableData.te[0].name} ${timetableData.te[0].longname.isNotEmpty ? "(${timetableData.te[0].longname})" : ""}")
|
? Text("Lehrkraft: ${timetableData.te[0].name} ${timetableData.te[0].longname.isNotEmpty ? "(${timetableData.te[0].longname})" : ""}")
|
||||||
: const Text("?"),
|
: const Text('?'),
|
||||||
trailing: Visibility(
|
trailing: Visibility(
|
||||||
visible: !kReleaseMode,
|
visible: !kReleaseMode,
|
||||||
child: IconButton(
|
child: IconButton(
|
||||||
@ -124,7 +124,7 @@ class AppointmentDetails {
|
|||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: const Icon(Icons.abc),
|
leading: const Icon(Icons.abc),
|
||||||
title: Text("Typ: ${timetableData.activityType}"),
|
title: Text('Typ: ${timetableData.activityType}'),
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: const Icon(Icons.people),
|
leading: const Icon(Icons.people),
|
||||||
@ -140,9 +140,9 @@ class AppointmentDetails {
|
|||||||
static Completer deleteCustomEvent(BuildContext context, CustomTimetableEvent appointment) {
|
static Completer deleteCustomEvent(BuildContext context, CustomTimetableEvent appointment) {
|
||||||
Completer future = Completer();
|
Completer future = Completer();
|
||||||
ConfirmDialog(
|
ConfirmDialog(
|
||||||
title: "Termin löschen",
|
title: 'Termin löschen',
|
||||||
content: "Der ${appointment.rrule.isEmpty ? "Termin" : "Serientermin"} wird unwiederruflich gelöscht.",
|
content: "Der ${appointment.rrule.isEmpty ? "Termin" : "Serientermin"} wird unwiederruflich gelöscht.",
|
||||||
confirmButton: "Löschen",
|
confirmButton: 'Löschen',
|
||||||
onConfirm: () {
|
onConfirm: () {
|
||||||
RemoveCustomTimetableEvent(
|
RemoveCustomTimetableEvent(
|
||||||
RemoveCustomTimetableEventParams(
|
RemoveCustomTimetableEventParams(
|
||||||
@ -186,14 +186,14 @@ class AppointmentDetails {
|
|||||||
builder: (context) => CustomTimetableEventEditDialog(existingEvent: appointment),
|
builder: (context) => CustomTimetableEventEditDialog(existingEvent: appointment),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
label: const Text("Bearbeiten"),
|
label: const Text('Bearbeiten'),
|
||||||
icon: const Icon(Icons.edit_outlined),
|
icon: const Icon(Icons.edit_outlined),
|
||||||
),
|
),
|
||||||
TextButton.icon(
|
TextButton.icon(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
deleteCustomEvent(context, appointment).future.then((value) => Navigator.of(context).pop());
|
deleteCustomEvent(context, appointment).future.then((value) => Navigator.of(context).pop());
|
||||||
},
|
},
|
||||||
label: const Text("Löschen"),
|
label: const Text('Löschen'),
|
||||||
icon: const Icon(Icons.delete_outline),
|
icon: const Icon(Icons.delete_outline),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@ -202,7 +202,7 @@ class AppointmentDetails {
|
|||||||
const Divider(),
|
const Divider(),
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: const Icon(Icons.info_outline),
|
leading: const Icon(Icons.info_outline),
|
||||||
title: Text(appointment.description.isEmpty ? "Keine Beschreibung" : appointment.description),
|
title: Text(appointment.description.isEmpty ? 'Keine Beschreibung' : appointment.description),
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: const CenteredLeading(Icon(Icons.repeat_outlined)),
|
leading: const CenteredLeading(Icon(Icons.repeat_outlined)),
|
||||||
@ -210,10 +210,10 @@ class AppointmentDetails {
|
|||||||
subtitle: FutureBuilder(
|
subtitle: FutureBuilder(
|
||||||
future: RruleL10nEn.create(),
|
future: RruleL10nEn.create(),
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
if(appointment.rrule.isEmpty) return const Text("Keine weiteren vorkomnisse");
|
if(appointment.rrule.isEmpty) return const Text('Keine weiteren vorkomnisse');
|
||||||
if(snapshot.data == null) return const Text("...");
|
if(snapshot.data == null) return const Text('...');
|
||||||
RecurrenceRule rrule = RecurrenceRule.fromString(appointment.rrule);
|
RecurrenceRule rrule = RecurrenceRule.fromString(appointment.rrule);
|
||||||
if(!rrule.canFullyConvertToText) return const Text("Keine genauere Angabe möglich.");
|
if(!rrule.canFullyConvertToText) return const Text('Keine genauere Angabe möglich.');
|
||||||
return Text(rrule.toText(l10n: snapshot.data!));
|
return Text(rrule.toText(l10n: snapshot.data!));
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@ -221,8 +221,8 @@ class AppointmentDetails {
|
|||||||
DebugTile(context).child(
|
DebugTile(context).child(
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: const CenteredLeading(Icon(Icons.rule)),
|
leading: const CenteredLeading(Icon(Icons.rule)),
|
||||||
title: const Text("RRule"),
|
title: const Text('RRule'),
|
||||||
subtitle: Text(appointment.rrule.isEmpty ? "Keine" : appointment.rrule),
|
subtitle: Text(appointment.rrule.isEmpty ? 'Keine' : appointment.rrule),
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
DebugTile(context).jsonData(appointment.toJson()),
|
DebugTile(context).jsonData(appointment.toJson()),
|
||||||
|
@ -15,16 +15,16 @@ class TimetableColors {
|
|||||||
static ColorModeDisplay getDisplayOptions(CustomTimetableColors color) {
|
static ColorModeDisplay getDisplayOptions(CustomTimetableColors color) {
|
||||||
switch(color) {
|
switch(color) {
|
||||||
case CustomTimetableColors.green:
|
case CustomTimetableColors.green:
|
||||||
return ColorModeDisplay(color: Colors.green, displayName: "Grün");
|
return ColorModeDisplay(color: Colors.green, displayName: 'Grün');
|
||||||
|
|
||||||
case CustomTimetableColors.blue:
|
case CustomTimetableColors.blue:
|
||||||
return ColorModeDisplay(color: Colors.blue, displayName: "Blau");
|
return ColorModeDisplay(color: Colors.blue, displayName: 'Blau');
|
||||||
|
|
||||||
case CustomTimetableColors.orange:
|
case CustomTimetableColors.orange:
|
||||||
return ColorModeDisplay(color: Colors.orange.shade800, displayName: "Orange");
|
return ColorModeDisplay(color: Colors.orange.shade800, displayName: 'Orange');
|
||||||
|
|
||||||
case CustomTimetableColors.red:
|
case CustomTimetableColors.red:
|
||||||
return ColorModeDisplay(color: DarkAppTheme.marianumRed, displayName: "Rot");
|
return ColorModeDisplay(color: DarkAppTheme.marianumRed, displayName: 'Rot');
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@ import 'dart:developer';
|
|||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:jiffy/jiffy.dart';
|
import 'package:jiffy/jiffy.dart';
|
||||||
import 'package:marianum_mobile/extensions/dateTime.dart';
|
import '../../../extensions/dateTime.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:rrule_generator/rrule_generator.dart';
|
import 'package:rrule_generator/rrule_generator.dart';
|
||||||
import 'package:time_range_picker/time_range_picker.dart';
|
import 'package:time_range_picker/time_range_picker.dart';
|
||||||
@ -32,7 +32,7 @@ class _AddCustomTimetableEventDialogState extends State<CustomTimetableEventEdit
|
|||||||
late TimeOfDay _endTime = widget.existingEvent?.endDate.toTimeOfDay() ?? const TimeOfDay(hour: 09, minute: 30);
|
late TimeOfDay _endTime = widget.existingEvent?.endDate.toTimeOfDay() ?? const TimeOfDay(hour: 09, minute: 30);
|
||||||
late final TextEditingController _eventName = TextEditingController(text: widget.existingEvent?.title);
|
late final TextEditingController _eventName = TextEditingController(text: widget.existingEvent?.title);
|
||||||
late final TextEditingController _eventDescription = TextEditingController(text: widget.existingEvent?.description);
|
late final TextEditingController _eventDescription = TextEditingController(text: widget.existingEvent?.description);
|
||||||
late String _recurringRule = widget.existingEvent?.rrule ?? "";
|
late String _recurringRule = widget.existingEvent?.rrule ?? '';
|
||||||
late CustomTimetableColors _customTimetableColor = CustomTimetableColors.values.firstWhere(
|
late CustomTimetableColors _customTimetableColor = CustomTimetableColors.values.firstWhere(
|
||||||
(element) => element.name == widget.existingEvent?.color,
|
(element) => element.name == widget.existingEvent?.color,
|
||||||
orElse: () => TimetableColors.defaultColor
|
orElse: () => TimetableColors.defaultColor
|
||||||
@ -64,7 +64,7 @@ class _AddCustomTimetableEventDialogState extends State<CustomTimetableEventEdit
|
|||||||
controller: _eventName,
|
controller: _eventName,
|
||||||
autofocus: true,
|
autofocus: true,
|
||||||
decoration: const InputDecoration(
|
decoration: const InputDecoration(
|
||||||
labelText: "Terminname",
|
labelText: 'Terminname',
|
||||||
border: OutlineInputBorder()
|
border: OutlineInputBorder()
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -75,7 +75,7 @@ class _AddCustomTimetableEventDialogState extends State<CustomTimetableEventEdit
|
|||||||
maxLines: 2,
|
maxLines: 2,
|
||||||
minLines: 2,
|
minLines: 2,
|
||||||
decoration: const InputDecoration(
|
decoration: const InputDecoration(
|
||||||
labelText: "Beschreibung",
|
labelText: 'Beschreibung',
|
||||||
border: OutlineInputBorder()
|
border: OutlineInputBorder()
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -84,7 +84,7 @@ class _AddCustomTimetableEventDialogState extends State<CustomTimetableEventEdit
|
|||||||
ListTile(
|
ListTile(
|
||||||
leading: const Icon(Icons.date_range_outlined),
|
leading: const Icon(Icons.date_range_outlined),
|
||||||
title: Text(Jiffy.parseFromDateTime(_date).yMMMd),
|
title: Text(Jiffy.parseFromDateTime(_date).yMMMd),
|
||||||
subtitle: const Text("Datum"),
|
subtitle: const Text('Datum'),
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
final DateTime? pickedDate = await showDatePicker(
|
final DateTime? pickedDate = await showDatePicker(
|
||||||
context: context,
|
context: context,
|
||||||
@ -101,8 +101,8 @@ class _AddCustomTimetableEventDialogState extends State<CustomTimetableEventEdit
|
|||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: const Icon(Icons.access_time_outlined),
|
leading: const Icon(Icons.access_time_outlined),
|
||||||
title: Text("${_startTime.format(context).toString()} - ${_endTime.format(context).toString()}"),
|
title: Text('${_startTime.format(context).toString()} - ${_endTime.format(context).toString()}'),
|
||||||
subtitle: const Text("Zeitraum"),
|
subtitle: const Text('Zeitraum'),
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
TimeRange timeRange = await showTimeRangePicker(
|
TimeRange timeRange = await showTimeRangePicker(
|
||||||
context: context,
|
context: context,
|
||||||
@ -112,8 +112,8 @@ class _AddCustomTimetableEventDialogState extends State<CustomTimetableEventEdit
|
|||||||
disabledColor: Colors.grey,
|
disabledColor: Colors.grey,
|
||||||
paintingStyle: PaintingStyle.fill,
|
paintingStyle: PaintingStyle.fill,
|
||||||
interval: const Duration(minutes: 5),
|
interval: const Duration(minutes: 5),
|
||||||
fromText: "Beginnend",
|
fromText: 'Beginnend',
|
||||||
toText: "Endend",
|
toText: 'Endend',
|
||||||
strokeColor: Theme.of(context).colorScheme.secondary,
|
strokeColor: Theme.of(context).colorScheme.secondary,
|
||||||
minDuration: const Duration(minutes: 15),
|
minDuration: const Duration(minutes: 15),
|
||||||
selectedColor: Theme.of(context).primaryColor,
|
selectedColor: Theme.of(context).primaryColor,
|
||||||
@ -129,7 +129,7 @@ class _AddCustomTimetableEventDialogState extends State<CustomTimetableEventEdit
|
|||||||
const Divider(),
|
const Divider(),
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: const Icon(Icons.color_lens_outlined),
|
leading: const Icon(Icons.color_lens_outlined),
|
||||||
title: const Text("Farbgebung"),
|
title: const Text('Farbgebung'),
|
||||||
trailing: DropdownButton<CustomTimetableColors>(
|
trailing: DropdownButton<CustomTimetableColors>(
|
||||||
value: _customTimetableColor,
|
value: _customTimetableColor,
|
||||||
icon: const Icon(Icons.arrow_drop_down),
|
icon: const Icon(Icons.arrow_drop_down),
|
||||||
@ -162,7 +162,7 @@ class _AddCustomTimetableEventDialogState extends State<CustomTimetableEventEdit
|
|||||||
initialRRule: _recurringRule,
|
initialRRule: _recurringRule,
|
||||||
textDelegate: const GermanRRuleTextDelegate(),
|
textDelegate: const GermanRRuleTextDelegate(),
|
||||||
onChange: (String newValue) {
|
onChange: (String newValue) {
|
||||||
log("Rule: $newValue");
|
log('Rule: $newValue');
|
||||||
setState(() {
|
setState(() {
|
||||||
_recurringRule = newValue;
|
_recurringRule = newValue;
|
||||||
});
|
});
|
||||||
@ -183,7 +183,7 @@ class _AddCustomTimetableEventDialogState extends State<CustomTimetableEventEdit
|
|||||||
if(!validate()) return;
|
if(!validate()) return;
|
||||||
|
|
||||||
CustomTimetableEvent editedEvent = CustomTimetableEvent(
|
CustomTimetableEvent editedEvent = CustomTimetableEvent(
|
||||||
id: "",
|
id: '',
|
||||||
title: _eventName.text,
|
title: _eventName.text,
|
||||||
description: _eventDescription.text,
|
description: _eventDescription.text,
|
||||||
startDate: _date.withTime(_startTime),
|
startDate: _date.withTime(_startTime),
|
||||||
@ -210,7 +210,7 @@ class _AddCustomTimetableEventDialogState extends State<CustomTimetableEventEdit
|
|||||||
} else {
|
} else {
|
||||||
UpdateCustomTimetableEvent(
|
UpdateCustomTimetableEvent(
|
||||||
UpdateCustomTimetableEventParams(
|
UpdateCustomTimetableEventParams(
|
||||||
widget.existingEvent?.id ?? "",
|
widget.existingEvent?.id ?? '',
|
||||||
editedEvent
|
editedEvent
|
||||||
)
|
)
|
||||||
).run().then((value) {
|
).run().then((value) {
|
||||||
@ -224,7 +224,7 @@ class _AddCustomTimetableEventDialogState extends State<CustomTimetableEventEdit
|
|||||||
|
|
||||||
|
|
||||||
},
|
},
|
||||||
child: Text(isEditingExisting ? "Speichern" : "Erstellen"),
|
child: Text(isEditingExisting ? 'Speichern' : 'Erstellen'),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
@ -33,12 +33,12 @@ class _TimeRegionComponentState extends State<TimeRegionComponent> {
|
|||||||
children: [
|
children: [
|
||||||
const SizedBox(height: 15),
|
const SizedBox(height: 15),
|
||||||
const Icon(Icons.cake),
|
const Icon(Icons.cake),
|
||||||
const Text("FREI"),
|
const Text('FREI'),
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
RotatedBox(
|
RotatedBox(
|
||||||
quarterTurns: 1,
|
quarterTurns: 1,
|
||||||
child: Text(
|
child: Text(
|
||||||
text.split(":").last,
|
text.split(':').last,
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:marianum_mobile/extensions/dateTime.dart';
|
import '../../../extensions/dateTime.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:syncfusion_flutter_calendar/calendar.dart';
|
import 'package:syncfusion_flutter_calendar/calendar.dart';
|
||||||
|
|
||||||
@ -57,7 +57,7 @@ class _TimetableState extends State<Timetable> {
|
|||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: const Text("Stunden & Vertretungsplan"),
|
title: const Text('Stunden & Vertretungsplan'),
|
||||||
actions: [
|
actions: [
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: const Icon(Icons.home_outlined),
|
icon: const Icon(Icons.home_outlined),
|
||||||
@ -74,11 +74,11 @@ class _TimetableState extends State<Timetable> {
|
|||||||
Icon icon;
|
Icon icon;
|
||||||
switch(e) {
|
switch(e) {
|
||||||
case CalendarActions.addEvent:
|
case CalendarActions.addEvent:
|
||||||
title = "Kalendereintrag hinzufügen";
|
title = 'Kalendereintrag hinzufügen';
|
||||||
icon = const Icon(Icons.add);
|
icon = const Icon(Icons.add);
|
||||||
case CalendarActions.viewEvents:
|
case CalendarActions.viewEvents:
|
||||||
default:
|
default:
|
||||||
title = "Kalendereinträge anzeigen";
|
title = 'Kalendereinträge anzeigen';
|
||||||
icon = const Icon(Icons.perm_contact_calendar_outlined);
|
icon = const Icon(Icons.perm_contact_calendar_outlined);
|
||||||
}
|
}
|
||||||
return PopupMenuItem<CalendarActions>(
|
return PopupMenuItem<CalendarActions>(
|
||||||
@ -112,9 +112,9 @@ class _TimetableState extends State<Timetable> {
|
|||||||
if(value.hasError) {
|
if(value.hasError) {
|
||||||
return PlaceholderView(
|
return PlaceholderView(
|
||||||
icon: Icons.calendar_month,
|
icon: Icons.calendar_month,
|
||||||
text: "Webuntis error: ${value.error.toString()}",
|
text: 'Webuntis error: ${value.error.toString()}',
|
||||||
button: TextButton(
|
button: TextButton(
|
||||||
child: const Text("Neu laden"),
|
child: const Text('Neu laden'),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
controller.displayDate = DateTime.now().add(const Duration(days: 2));
|
controller.displayDate = DateTime.now().add(const Duration(days: 2));
|
||||||
Provider.of<TimetableProps>(context, listen: false).resetWeek();
|
Provider.of<TimetableProps>(context, listen: false).resetWeek();
|
||||||
@ -155,8 +155,8 @@ class _TimetableState extends State<Timetable> {
|
|||||||
startHour: 07.5,
|
startHour: 07.5,
|
||||||
endHour: 16.5,
|
endHour: 16.5,
|
||||||
timeInterval: Duration(minutes: 30),
|
timeInterval: Duration(minutes: 30),
|
||||||
timeFormat: "HH:mm",
|
timeFormat: 'HH:mm',
|
||||||
dayFormat: "EE",
|
dayFormat: 'EE',
|
||||||
timeIntervalHeight: 40,
|
timeIntervalHeight: 40,
|
||||||
),
|
),
|
||||||
|
|
||||||
@ -259,10 +259,10 @@ class _TimetableState extends State<Timetable> {
|
|||||||
startTime: startTime,
|
startTime: startTime,
|
||||||
endTime: endTime,
|
endTime: endTime,
|
||||||
subject: subjects.result.firstWhere((subject) => subject.id == element.su[0].id).name,
|
subject: subjects.result.firstWhere((subject) => subject.id == element.su[0].id).name,
|
||||||
location: ""
|
location: ''
|
||||||
"${rooms.result.firstWhere((room) => room.id == element.ro[0].id).name}"
|
'${rooms.result.firstWhere((room) => room.id == element.ro[0].id).name}'
|
||||||
"\n"
|
'\n'
|
||||||
"${element.te.first.longname}",
|
'${element.te.first.longname}',
|
||||||
notes: element.activityType,
|
notes: element.activityType,
|
||||||
color: _getEventColor(element, startTime, endTime),
|
color: _getEventColor(element, startTime, endTime),
|
||||||
);
|
);
|
||||||
@ -272,7 +272,7 @@ class _TimetableState extends State<Timetable> {
|
|||||||
id: ArbitraryAppointment(webuntis: element),
|
id: ArbitraryAppointment(webuntis: element),
|
||||||
startTime: _parseWebuntisTimestamp(element.date, element.startTime),
|
startTime: _parseWebuntisTimestamp(element.date, element.startTime),
|
||||||
endTime: endTime,
|
endTime: endTime,
|
||||||
subject: "Änderung",
|
subject: 'Änderung',
|
||||||
notes: element.info,
|
notes: element.info,
|
||||||
location: 'Unbekannt',
|
location: 'Unbekannt',
|
||||||
color: endTime.isBefore(DateTime.now()) ? Theme.of(context).primaryColor.withAlpha(100) : Theme.of(context).primaryColor,
|
color: endTime.isBefore(DateTime.now()) ? Theme.of(context).primaryColor.withAlpha(100) : Theme.of(context).primaryColor,
|
||||||
@ -309,10 +309,10 @@ class _TimetableState extends State<Timetable> {
|
|||||||
int alpha = endTime.isBefore(DateTime.now()) ? 100 : 255;
|
int alpha = endTime.isBefore(DateTime.now()) ? 100 : 255;
|
||||||
|
|
||||||
// Cancelled
|
// Cancelled
|
||||||
if(webuntisElement.code == "cancelled") return const Color(0xff000000).withAlpha(alpha);
|
if(webuntisElement.code == 'cancelled') return const Color(0xff000000).withAlpha(alpha);
|
||||||
|
|
||||||
// Any changes or no teacher at this element
|
// Any changes or no teacher at this element
|
||||||
if(webuntisElement.code == "irregular" || webuntisElement.te.first.id == 0) return const Color(0xff8F19B3).withAlpha(alpha);
|
if(webuntisElement.code == 'irregular' || webuntisElement.te.first.id == 0) return const Color(0xff8F19B3).withAlpha(alpha);
|
||||||
|
|
||||||
// Event was in the past
|
// Event was in the past
|
||||||
if(endTime.isBefore(DateTime.now())) return Theme.of(context).primaryColor.withAlpha(alpha);
|
if(endTime.isBefore(DateTime.now())) return Theme.of(context).primaryColor.withAlpha(alpha);
|
||||||
@ -327,7 +327,7 @@ class _TimetableState extends State<Timetable> {
|
|||||||
bool _isCrossedOut(CalendarAppointmentDetails calendarEntry) {
|
bool _isCrossedOut(CalendarAppointmentDetails calendarEntry) {
|
||||||
ArbitraryAppointment appointment = calendarEntry.appointments.first.id as ArbitraryAppointment;
|
ArbitraryAppointment appointment = calendarEntry.appointments.first.id as ArbitraryAppointment;
|
||||||
if(appointment.hasWebuntis()) {
|
if(appointment.hasWebuntis()) {
|
||||||
return appointment.webuntis!.code == "cancelled";
|
return appointment.webuntis!.code == 'cancelled';
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user