Merge branch 'develop' into feature-highEduGraduationCalculator

# Conflicts:
#	lib/view/pages/overhang.dart
This commit is contained in:
Elias Müller 2024-03-29 18:32:59 +01:00
commit 5b34afd6cb
115 changed files with 554 additions and 554 deletions

BIN
.DS_Store vendored

Binary file not shown.

1
.gitignore vendored
View File

@ -349,3 +349,4 @@ hs_err_pid*
# End of https://www.toptal.com/developers/gitignore/api/flutter,intellij,androidstudio,xcode,dart
*.idea*
**/.DS_store

View File

@ -24,6 +24,10 @@ linter:
rules:
# avoid_print: false # Uncomment to disable the `avoid_print` 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
# https://dart.dev/guides/language/analysis-options

View File

@ -5,6 +5,6 @@ class ApiError {
@override
String toString() {
return "ApiError: $message";
return 'ApiError: $message';
}
}

View File

@ -6,11 +6,11 @@ import 'getHolidaysResponse.dart';
class GetHolidays {
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(
List<GetHolidaysResponseObject>.from(
jsonDecode(response).map<GetHolidaysResponseObject>(
(dynamic i) => GetHolidaysResponseObject.fromJson(i)
GetHolidaysResponseObject.fromJson
)
)
);

View File

@ -6,7 +6,7 @@ import 'getHolidaysResponse.dart';
class GetHolidaysCache extends RequestCache<GetHolidaysResponse> {
GetHolidaysCache({onUpdate, renew}) : super(RequestCache.cacheDay, onUpdate, renew: renew) {
start("MarianumMobile", "state-holidays");
start('MarianumMobile', 'state-holidays');
}
@override
@ -15,6 +15,7 @@ class GetHolidaysCache extends RequestCache<GetHolidaysResponse> {
return GetHolidaysResponse(
List<GetHolidaysResponseObject>.from(
parsedListJson.map<GetHolidaysResponseObject>(
// ignore: unnecessary_lambdas
(dynamic i) => GetHolidaysResponseObject.fromJson(i)
)
)

View File

@ -11,21 +11,21 @@ import 'autocompleteResponse.dart';
class AutocompleteApi {
Future<AutocompleteResponse> find(String query) async {
Map<String, dynamic> getParameters = {
"search": query,
"itemType": " ",
"itemId": " ",
"shareTypes[]": ["0"],
"limit": "10",
'search': query,
'itemType': ' ',
'itemId': ' ',
'shareTypes[]': ['0'],
'limit': '10',
};
Map<String, String> headers = {};
headers.putIfAbsent("Accept", () => "application/json");
headers.putIfAbsent("OCS-APIRequest", () => "true");
headers.putIfAbsent('Accept', () => 'application/json');
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);
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;
return AutocompleteResponse.fromJson(jsonDecode(result)['ocs']);
}

View File

@ -10,14 +10,14 @@ import 'fileSharingApiParams.dart';
class FileSharingApi {
Future<void> share(FileSharingApiParams query) async {
Map<String, String> headers = {};
headers.putIfAbsent("Accept", () => "application/json");
headers.putIfAbsent("OCS-APIRequest", () => "true");
headers.putIfAbsent('Accept', () => 'application/json');
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);
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}');
}
}
}

View File

@ -11,7 +11,7 @@ class GetChat extends TalkApi<GetChatResponse> {
String chatToken;
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
assemble(String raw) {

View File

@ -9,7 +9,7 @@ class GetChatCache extends RequestCache<GetChatResponse> {
String chatToken;
GetChatCache({required onUpdate, required this.chatToken}) : super(RequestCache.cacheNothing, onUpdate) {
start("MarianumMobile", "nc-chat-$chatToken");
start('MarianumMobile', 'nc-chat-$chatToken');
}
@override

View File

@ -61,21 +61,21 @@ class GetChatResponseObject {
static GetChatResponseObject getDateDummy(int timestamp) {
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) {
return GetChatResponseObject(
0,
"",
'',
GetRoomResponseObjectMessageActorType.user,
"",
"",
'',
'',
0,
"",
'',
GetRoomResponseObjectMessageType.system,
false,
"",
'',
text,
null,
null,
@ -113,10 +113,10 @@ class RichObjectString {
}
enum RichObjectStringObjectType {
@JsonValue("user") user,
@JsonValue("group") group,
@JsonValue("file") file,
@JsonValue("guest") guest,
@JsonValue("highlight") highlight,
@JsonValue("talk-poll") talkPoll,
@JsonValue('user') user,
@JsonValue('group') group,
@JsonValue('file') file,
@JsonValue('guest') guest,
@JsonValue('highlight') highlight,
@JsonValue('talk-poll') talkPoll,
}

View File

@ -6,7 +6,7 @@ class RichObjectStringProcessor {
if(data == null) return message;
data.forEach((key, value) {
message = message.replaceAll(RegExp("{$key}"), value.name);
message = message.replaceAll(RegExp('{$key}'), value.name);
});
return message;

View File

@ -7,7 +7,7 @@ import 'createRoomParams.dart';
class CreateRoom extends TalkApi {
CreateRoomParams params;
CreateRoom(this.params) : super("v4/room", params);
CreateRoom(this.params) : super('v4/room', params);
@override
assemble(String raw) {

View File

@ -7,7 +7,7 @@ import '../talkApi.dart';
class DeleteMessage extends TalkApi {
String chatToken;
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
assemble(String raw) {

View File

@ -8,7 +8,7 @@ import 'deleteReactMessageParams.dart';
class DeleteReactMessage extends TalkApi {
String chatToken;
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
assemble(String raw) {

View File

@ -7,7 +7,7 @@ import 'getParticipantsResponse.dart';
class GetParticipants extends TalkApi<GetParticipantsResponse> {
String token;
GetParticipants(this.token) : super("v4/room/$token/participants", null);
GetParticipants(this.token) : super('v4/room/$token/participants', null);
@override
GetParticipantsResponse assemble(String raw) {

View File

@ -8,7 +8,7 @@ class GetParticipantsCache extends RequestCache<GetParticipantsResponse> {
String chatToken;
GetParticipantsCache({required onUpdate, required this.chatToken}) : super(RequestCache.cacheNothing, onUpdate) {
start("MarianumMobile", "nc-chat-participants-$chatToken");
start('MarianumMobile', 'nc-chat-participants-$chatToken');
}
@override

View File

@ -10,7 +10,7 @@ import 'getReactionsResponse.dart';
class GetReactions extends TalkApi<GetReactionsResponse> {
String chatToken;
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
assemble(String raw) {

View File

@ -28,6 +28,6 @@ class GetReactionsResponseObject {
}
enum GetReactionsResponseObjectActorType {
@JsonValue("guests") guests,
@JsonValue("users") users,
@JsonValue('guests') guests,
@JsonValue('users') users,
}

View File

@ -6,7 +6,7 @@ import '../talkApi.dart';
class LeaveRoom extends TalkApi {
String chatToken;
LeaveRoom(this.chatToken) : super("v4/room/$chatToken/participants/self", null);
LeaveRoom(this.chatToken) : super('v4/room/$chatToken/participants/self', null);
@override
assemble(String raw) {

View File

@ -8,7 +8,7 @@ import 'reactMessageParams.dart';
class ReactMessage extends TalkApi {
String chatToken;
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
assemble(String raw) {

View File

@ -9,7 +9,7 @@ import 'getRoomResponse.dart';
class GetRoom extends TalkApi<GetRoomResponse> {
GetRoomParams params;
GetRoom(this.params) : super("v4/room", null, getParameters: params.toJson());
GetRoom(this.params) : super('v4/room', null, getParameters: params.toJson());

View File

@ -8,7 +8,7 @@ import 'getRoomResponse.dart';
class GetRoomCache extends RequestCache<GetRoomResponse> {
GetRoomCache({onUpdate, renew}) : super(RequestCache.cacheMinute, onUpdate, renew: renew) {
start("MarianumMobile", "nc-rooms");
start('MarianumMobile', 'nc-rooms');
}
@override

View File

@ -19,10 +19,10 @@ class GetRoomResponse extends ApiResponse {
final buffer = StringBuffer();
if(favoritesToTop) {
buffer.write(chat.isFavorite ? "b" : "a");
buffer.write(chat.isFavorite ? 'b' : 'a');
}
if(unreadToTop) {
buffer.write(chat.unreadMessages > 0 ? "b" : "a");
buffer.write(chat.unreadMessages > 0 ? 'b' : 'a');
}
buffer.write(chat.lastActivity);
@ -152,16 +152,16 @@ enum GetRoomResponseObjectParticipantNotificationLevel {
// }
enum GetRoomResponseObjectMessageActorType {
@JsonValue("deleted_users") deletedUsers,
@JsonValue("users") user,
@JsonValue("guests") guest,
@JsonValue("bots") bot,
@JsonValue("bridged") bridge,
@JsonValue('deleted_users') deletedUsers,
@JsonValue('users') user,
@JsonValue('guests') guest,
@JsonValue('bots') bot,
@JsonValue('bridged') bridge,
}
enum GetRoomResponseObjectMessageType {
@JsonValue("comment") comment,
@JsonValue("comment_deleted") deletedComment,
@JsonValue("system") system,
@JsonValue("command") command,
@JsonValue('comment') comment,
@JsonValue('comment_deleted') deletedComment,
@JsonValue('system') system,
@JsonValue('command') command,
}

View File

@ -7,7 +7,7 @@ import 'sendMessageParams.dart';
class SendMessage extends TalkApi {
String chatToken;
SendMessage(this.chatToken, SendMessageParams params) : super("v1/chat/$chatToken", params);
SendMessage(this.chatToken, SendMessageParams params) : super('v1/chat/$chatToken', params);
@override
assemble(String raw) {

View File

@ -8,7 +8,7 @@ class SetFavorite extends TalkApi {
String chatToken;
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
assemble(String raw) {

View File

@ -10,7 +10,7 @@ class SetReadMarker extends TalkApi {
bool readState;
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);
}

View File

@ -34,21 +34,21 @@ abstract class TalkApi<T extends ApiResponse?> extends ApiRequest {
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?.putIfAbsent("Accept", () => "application/json");
headers?.putIfAbsent("OCS-APIRequest", () => "true");
headers?.putIfAbsent('Accept', () => 'application/json');
headers?.putIfAbsent('OCS-APIRequest', () => 'true');
http.Response? data;
try {
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");
} catch(e) {
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);
@ -59,10 +59,10 @@ abstract class TalkApi<T extends ApiResponse?> extends ApiRequest {
return assembled;
} catch (e) {
// 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');
}
}

View File

@ -7,6 +7,6 @@ class TalkError {
@override
String toString() {
return "Talk - $status - ($code): $message";
return 'Talk - $status - ($code): $message';
}
}

View File

@ -24,7 +24,7 @@ class CacheableFile {
CacheableFile.fromDavFile(WebDavFile file) {
path = file.path.path;
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;
size = file.size;
eTag = file.etag;

View File

@ -14,7 +14,7 @@ class ListFiles extends WebdavApi<ListFilesParams> {
@override
Future<ListFilesResponse> run() async {
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
// 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.
files.removeWhere((element) => element.path == "/${params.path}/" || element.path == "/");
files.removeWhere((element) => element.path == '/${params.path}/' || element.path == '/');
return ListFilesResponse(files);
}

View File

@ -10,9 +10,9 @@ class ListFilesCache extends RequestCache<ListFilesResponse> {
String path;
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();
start("MarianumMobile", "wd-folder-$cacheName");
start('MarianumMobile', 'wd-folder-$cacheName');
}
@override

View File

@ -35,7 +35,7 @@ class ListFilesResponse extends ApiResponse {
switch(sortOption) {
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;
case SortOption.name:

View File

@ -18,10 +18,10 @@ abstract class WebdavApi<T> extends ApiRequest {
static Future<String> webdavConnectString = buildWebdavConnectString();
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 {
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()}/';
}
}

View File

@ -6,7 +6,7 @@ import '../../mhslApi.dart';
import 'getBreakersResponse.dart';
class GetBreakers extends MhslApi<GetBreakersResponse> {
GetBreakers() : super("breaker/");
GetBreakers() : super('breaker/');
@override
GetBreakersResponse assemble(String raw) {

View File

@ -6,7 +6,7 @@ import 'getBreakersResponse.dart';
class GetBreakersCache extends RequestCache<GetBreakersResponse> {
GetBreakersCache({onUpdate, renew}) : super(RequestCache.cacheMinute, onUpdate, renew: renew) {
start("MarianumMobile", "breakers");
start('MarianumMobile', 'breakers');
}
@override

View File

@ -28,9 +28,9 @@ class GetBreakersReponseObject {
}
enum BreakerArea {
@JsonValue("GLOBAL") global,
@JsonValue("TIMETABLE") timetable,
@JsonValue("TALK") talk,
@JsonValue("FILES") files,
@JsonValue("MORE") more,
@JsonValue('GLOBAL') global,
@JsonValue('TIMETABLE') timetable,
@JsonValue('TALK') talk,
@JsonValue('FILES') files,
@JsonValue('MORE') more,
}

View File

@ -9,11 +9,11 @@ import 'getCustomTimetableEventResponse.dart';
class GetCustomTimetableEvent extends MhslApi<GetCustomTimetableEventResponse> {
GetCustomTimetableEventParams params;
GetCustomTimetableEvent(this.params) : super("server/timetable/customEvents?user=${params.user}");
GetCustomTimetableEvent(this.params) : super('server/timetable/customEvents?user=${params.user}');
@override
GetCustomTimetableEventResponse assemble(String raw) {
return GetCustomTimetableEventResponse.fromJson({"events": jsonDecode(raw)});
return GetCustomTimetableEventResponse.fromJson({'events': jsonDecode(raw)});
}
@override

View File

@ -9,7 +9,7 @@ class GetCustomTimetableEventCache extends RequestCache<GetCustomTimetableEventR
GetCustomTimetableEventParams params;
GetCustomTimetableEventCache(this.params, {onUpdate, renew}) : super(RequestCache.cacheMinute, onUpdate, renew: renew) {
start("MarianumMobile", "customTimetableEvents");
start('MarianumMobile', 'customTimetableEvents');
}
@override

View File

@ -6,7 +6,7 @@ import '../../mhslApi.dart';
import 'getMessagesResponse.dart';
class GetMessages extends MhslApi<GetMessagesResponse> {
GetMessages() : super("message/messages.json");
GetMessages() : super('message/messages.json');
@override

View File

@ -6,7 +6,7 @@ import 'getMessagesResponse.dart';
class GetMessagesCache extends RequestCache<GetMessagesResponse> {
GetMessagesCache({onUpdate, renew}) : super(RequestCache.cacheMinute, onUpdate, renew: renew) {
start("MarianumMobile", "message");
start('MarianumMobile', 'message');
}
@override

View File

@ -15,21 +15,21 @@ abstract class MhslApi<T> extends ApiRequest {
T assemble(String raw);
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);
if(data == null) {
throw ApiError("Request could not be dispatched!");
throw ApiError('Request could not be dispatched!');
}
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));
}
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);
}

View File

@ -9,7 +9,7 @@ import 'notifyRegisterParams.dart';
class NotifyRegister extends MhslApi<void> {
NotifyRegisterParams params;
NotifyRegister(this.params) : super("notify/register/");
NotifyRegister(this.params) : super('notify/register/');
@override

View File

@ -12,7 +12,7 @@ import 'updateUserIndexParams.dart';
class UpdateUserIndex extends MhslApi<void> {
UpdateUserIndexParams params;
UpdateUserIndex(this.params) : super("server/userIndex/update");
UpdateUserIndex(this.params) : super('server/userIndex/update');
@override
void assemble(String raw) {}
@ -20,7 +20,7 @@ class UpdateUserIndex extends MhslApi<void> {
@override
Future<http.Response> request(Uri uri) {
String data = jsonEncode(params.toJson());
log("Updating userindex:\n $data");
log('Updating userindex:\n $data');
return http.post(uri, body: data);
}

View File

@ -36,8 +36,8 @@ abstract class RequestCache<T extends ApiResponse?> {
onUpdate(newValue as T);
Localstore.instance.collection(file).doc(document).set({
"json": jsonEncode(newValue),
"lastupdate": DateTime.now().millisecondsSinceEpoch
'json': jsonEncode(newValue),
'lastupdate': DateTime.now().millisecondsSinceEpoch
});
} catch(e) {
log("Error while fetching/ parsing. Raw server response: ${newValue?.rawResponse.body ?? "no response"}");

View File

@ -9,7 +9,7 @@ import 'authenticateResponse.dart';
class Authenticate extends WebuntisApi {
AuthenticateParams param;
Authenticate(this.param) : super("authenticate", param, authenticatedResponse: false);
Authenticate(this.param) : super('authenticate', param, authenticatedResponse: false);
@override
Future<AuthenticateResponse> run() async {

View File

@ -4,7 +4,7 @@ import '../../webuntisApi.dart';
import 'getHolidaysResponse.dart';
class GetHolidays extends WebuntisApi {
GetHolidays() : super("getHolidays", null);
GetHolidays() : super('getHolidays', null);
@override
Future<GetHolidaysResponse> run() async {

View File

@ -6,7 +6,7 @@ import 'getHolidaysResponse.dart';
class GetHolidaysCache extends RequestCache<GetHolidaysResponse> {
GetHolidaysCache({onUpdate}) : super(RequestCache.cacheDay, onUpdate) {
start("MarianumMobile", "wu-holidays");
start('MarianumMobile', 'wu-holidays');
}
@override

View File

@ -5,7 +5,7 @@ import '../../webuntisApi.dart';
import 'getRoomsResponse.dart';
class GetRooms extends WebuntisApi {
GetRooms() : super("getRooms", null);
GetRooms() : super('getRooms', null);
@override
Future<GetRoomsResponse> run() async {
@ -14,10 +14,10 @@ class GetRooms extends WebuntisApi {
return finalize(GetRoomsResponse.fromJson(jsonDecode(rawAnswer)));
} catch(e, trace) {
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');
}
}

View File

@ -6,7 +6,7 @@ import 'getRoomsResponse.dart';
class GetRoomsCache extends RequestCache<GetRoomsResponse> {
GetRoomsCache({onUpdate}) : super(RequestCache.cacheHour, onUpdate) {
start("MarianumMobile", "wu-rooms");
start('MarianumMobile', 'wu-rooms');
}
@override

View File

@ -4,7 +4,7 @@ import '../../webuntisApi.dart';
import 'getSubjectsResponse.dart';
class GetSubjects extends WebuntisApi {
GetSubjects() : super("getSubjects", null);
GetSubjects() : super('getSubjects', null);
@override
Future<GetSubjectsResponse> run() async {

View File

@ -6,7 +6,7 @@ import 'getSubjectsResponse.dart';
class GetSubjectsCache extends RequestCache<GetSubjectsResponse> {
GetSubjectsCache({onUpdate}) : super(RequestCache.cacheHour, onUpdate) {
start("MarianumMobile", "wu-subjects");
start('MarianumMobile', 'wu-subjects');
}
@override

View File

@ -7,7 +7,7 @@ import 'getTimetableResponse.dart';
class GetTimetable extends WebuntisApi {
GetTimetableParams params;
GetTimetable(this.params) : super("getTimetable", params);
GetTimetable(this.params) : super('getTimetable', params);
@override
Future<GetTimetableResponse> run() async {

View File

@ -11,7 +11,7 @@ class GetTimetableCache extends RequestCache<GetTimetableResponse> {
int enddate;
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

View File

@ -67,10 +67,10 @@ class GetTimetableParamsOptions {
}
enum GetTimetableParamsOptionsFields {
@JsonValue("id") id,
@JsonValue("name") name,
@JsonValue("longname") longname,
@JsonValue("externalkey") externalkey;
@JsonValue('id') id,
@JsonValue('name') name,
@JsonValue('longname') longname,
@JsonValue('externalkey') externalkey;
static List<GetTimetableParamsOptionsFields> all = [id, name, longname, externalkey];
}
@ -88,7 +88,7 @@ class GetTimetableParamsOptionsElement {
}
enum GetTimetableParamsOptionsElementKeyType {
@JsonValue("id") id,
@JsonValue("name") name,
@JsonValue("externalkey") externalkey
@JsonValue('id') id,
@JsonValue('name') name,
@JsonValue('externalkey') externalkey
}

View File

@ -9,7 +9,7 @@ import 'queries/authenticate/authenticate.dart';
import 'webuntisError.dart';
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;
ApiParams? genericParam;
http.Response? response;
@ -22,11 +22,11 @@ abstract class WebuntisApi extends ApiRequest {
Future<String> query(WebuntisApi untis) async {
String query = '{"id":"ID","method":"$method","params":${untis._body()},"jsonrpc":"2.0"}';
String sessionId = "0";
String sessionId = '0';
if(authenticatedResponse) {
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;
dynamic jsonData = jsonDecode(data.body);
@ -49,7 +49,7 @@ abstract class WebuntisApi extends ApiRequest {
Future<ApiResponse> run();
String _body() {
return genericParam == null ? "{}" : jsonEncode(genericParam);
return genericParam == null ? '{}' : jsonEncode(genericParam);
}
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)
.timeout(
const Duration(seconds: 10),
onTimeout: () => throw WebuntisError("Timeout", 1)
onTimeout: () => throw WebuntisError('Timeout', 1)
);
}
}

View File

@ -6,6 +6,6 @@ class WebuntisError implements Exception {
@override
String toString() {
return "WebUntis ($code): $message";
return 'WebUntis ($code): $message';
}
}

View File

@ -39,14 +39,14 @@ class _AppState extends State<App> with WidgetsBindingObserver {
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
log("AppLifecycle: ${state.toString()}");
log('AppLifecycle: ${state.toString()}');
if(state == AppLifecycleState.resumed) {
EasyThrottle.throttle(
"appLifecycleState",
'appLifecycleState',
const Duration(seconds: 10),
() {
log("Refreshing due to LifecycleChange");
log('Refreshing due to LifecycleChange');
NotificationTasks.updateProviders(context);
Provider.of<TimetableProps>(context, listen: false).run();
}
@ -106,7 +106,7 @@ class _AppState extends State<App> with WidgetsBindingObserver {
activeForegroundColor: Theme.of(context).primaryColor,
inactiveForegroundColor: Theme.of(context).colorScheme.secondary,
icon: const Icon(Icons.calendar_month),
title: "Vertretung"
title: 'Vertretung'
),
),
PersistentTabConfig(
@ -127,12 +127,12 @@ class _AppState extends State<App> with WidgetsBindingObserver {
badgeColor: Theme.of(context).primaryColor,
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),
);
},
),
title: "Talk",
title: 'Talk',
),
),
PersistentTabConfig(
@ -141,7 +141,7 @@ class _AppState extends State<App> with WidgetsBindingObserver {
activeForegroundColor: Theme.of(context).primaryColor,
inactiveForegroundColor: Theme.of(context).colorScheme.secondary,
icon: const Icon(Icons.folder),
title: "Dateien"
title: 'Dateien'
),
),
PersistentTabConfig(
@ -150,7 +150,7 @@ class _AppState extends State<App> with WidgetsBindingObserver {
activeForegroundColor: Theme.of(context).primaryColor,
inactiveForegroundColor: Theme.of(context).colorScheme.secondary,
icon: const Icon(Icons.apps),
title: "Mehr"
title: 'Mehr'
),
),
],

View File

@ -39,7 +39,7 @@ Future<void> main() async {
await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
log("Firebase token: ${await FirebaseMessaging.instance.getToken() ?? "Error: no Firebase token!"}");
} catch (e) {
log("Error initializing Firebase app!");
log('Error initializing Firebase app!');
}
ByteData data = await PlatformAssetBundle().load('assets/ca/lets-encrypt-r3.pem');
@ -89,7 +89,7 @@ class _MainState extends State<Main> {
@override
void initState() {
Jiffy.setLocale("de");
Jiffy.setLocale('de');
AccountData().waitForPopulation().then((value) {
Provider.of<AccountModel>(context, listen: false)
@ -139,7 +139,7 @@ class _MainState extends State<Main> {
switch(accountModel.state) {
case AccountModelState.loggedIn: return const App();
case AccountModelState.loggedOut: return const Login();
case AccountModelState.undefined: return const PlaceholderView(icon: Icons.timer, text: "Daten werden geladen");
case AccountModelState.undefined: return const PlaceholderView(icon: Icons.timer, text: 'Daten werden geladen');
}
},
)

View File

@ -10,8 +10,8 @@ import 'package:shared_preferences/shared_preferences.dart';
import 'accountModel.dart';
class AccountData {
static const _usernameField = "username";
static const _passwordField = "password";
static const _usernameField = 'username';
static const _passwordField = 'password';
static final AccountData _instance = AccountData._construct();
final Future<SharedPreferences> _storage = SharedPreferences.getInstance();
@ -29,21 +29,21 @@ class AccountData {
String? _password;
String getUsername() {
if(_username == null) throw Exception("Username not initialized");
if(_username == null) throw Exception('Username not initialized');
return _username!;
}
String getPassword() {
if(_password == null) throw Exception("Password not initialized");
if(_password == null) throw Exception('Password not initialized');
return _password!;
}
String getUserSecret() {
return sha512.convert(utf8.encode("${AccountData().getUsername()}:${AccountData().getPassword()}")).toString();
return sha512.convert(utf8.encode('${AccountData().getUsername()}:${AccountData().getPassword()}')).toString();
}
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 {
@ -84,7 +84,7 @@ class AccountData {
}
String buildHttpAuthString() {
if(!isPopulated()) throw Exception("AccountData (e.g. username or password) is not initialized!");
return "$_username:$_password";
if(!isPopulated()) throw Exception('AccountData (e.g. username or password) is not initialized!');
return '$_username:$_password';
}
}

View File

@ -26,7 +26,7 @@ class BreakerProps extends DataHolder {
for(var key in breakers.regional.keys) {
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;
}
}

View File

@ -4,7 +4,7 @@ import '../../api/marianumcloud/talk/chat/getChatResponse.dart';
import '../dataHolder.dart';
class ChatProps extends DataHolder {
String _queryToken = "";
String _queryToken = '';
DateTime _lastTokenSet = DateTime.now();
GetChatResponse? _getChatResponse;

View File

@ -21,7 +21,7 @@ class Endpoint {
String domain;
String path;
Endpoint({required this.domain, this.path = ""});
Endpoint({required this.domain, this.path = ''});
String full() {
return domain + path;
@ -40,17 +40,17 @@ class EndpointData {
EndpointMode getEndpointMode() {
late String existingName;
existingName = AccountData().getUsername();
return existingName.startsWith("google") ? EndpointMode.stage : EndpointMode.live;
return existingName.startsWith('google') ? EndpointMode.stage : EndpointMode.live;
}
Endpoint webuntis() {
return EndpointOptions(
live: Endpoint(
domain: "peleus.webuntis.com",
domain: 'peleus.webuntis.com',
),
staged: Endpoint(
domain: "mhsl.eu",
path: "/marianum/marianummobile/webuntis/public/index.php/api"
domain: 'mhsl.eu',
path: '/marianum/marianummobile/webuntis/public/index.php/api'
),
).get(getEndpointMode());
}
@ -58,11 +58,11 @@ class EndpointData {
Endpoint nextcloud() {
return EndpointOptions(
live: Endpoint(
domain: "cloud.marianum-fulda.de",
domain: 'cloud.marianum-fulda.de',
),
staged: Endpoint(
domain: "mhsl.eu",
path: "/marianum/marianummobile/cloud",
domain: 'mhsl.eu',
path: '/marianum/marianummobile/cloud',
)
).get(getEndpointMode());
}

View File

@ -12,7 +12,7 @@ extension ExtendedList on List {
class FilesProps extends DataHolder {
List<String> folderPath = List<String>.empty(growable: true);
String currentFolderName = "Home";
String currentFolderName = 'Home';
ListFilesResponse? _listFilesResponse;
ListFilesResponse get listFilesResponse => _listFilesResponse!;
@ -32,7 +32,7 @@ class FilesProps extends DataHolder {
_listFilesResponse = null;
notifyListeners();
ListFilesCache(
path: folderPath.isEmpty ? "/" : folderPath.join("/"),
path: folderPath.isEmpty ? '/' : folderPath.join('/'),
onUpdate: (ListFilesResponse data) => {
_listFilesResponse = data,
notifyListeners(),
@ -48,7 +48,7 @@ class FilesProps extends DataHolder {
void popFolder() {
folderPath.removeLast();
if(folderPath.isEmpty) currentFolderName = "Home";
if(folderPath.isEmpty) currentFolderName = 'Home';
run();
}
}

View File

@ -49,8 +49,8 @@ class TimetableProps extends DataHolder {
@override
void run({renew}) {
GetTimetableCache(
startdate: int.parse(DateFormat("yyyyMMdd").format(startDate)),
enddate: int.parse(DateFormat("yyyyMMdd").format(endDate)),
startdate: int.parse(DateFormat('yyyyMMdd').format(startDate)),
enddate: int.parse(DateFormat('yyyyMMdd').format(endDate)),
onUpdate: (GetTimetableResponse data) => {
_getTimetableResponse = data,
notifyListeners(),

View File

@ -49,11 +49,11 @@ class NotificationController {
DebugTile(context).run(() {
showDialog(context: context, builder: (context) => AlertDialog(
title: const Text("Notification report"),
title: const Text('Notification report'),
content: Column(
mainAxisSize: MainAxisSize.min,
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)),
],
),

View File

@ -10,13 +10,13 @@ import '../widget/confirmDialog.dart';
class NotifyUpdater {
static ConfirmDialog enableAfterDisclaimer(SettingsProvider settings) {
return ConfirmDialog(
title: "Warnung",
title: 'Warnung',
icon: Icons.warning_amber,
content: ""
"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"
"Für mehr Informationen drücke lange auf die Einstellungsoption!",
confirmButton: "Aktivieren",
content: ''
'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'
'Für mehr Informationen drücke lange auf die Einstellungsoption!',
confirmButton: 'Aktivieren',
onConfirm: () {
FirebaseMessaging.instance.requestPermission(
provisional: false
@ -29,7 +29,7 @@ class NotifyUpdater {
static void registerToServer() async {
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(
NotifyRegisterParams(

View File

@ -8,7 +8,7 @@ import '../../view/settings/defaultSettings.dart';
import 'settings.dart';
class SettingsProvider extends ChangeNotifier {
static const String _fieldName = "settings";
static const String _fieldName = 'settings';
late SharedPreferences _storage;
late Settings _settings = DefaultSettings.get();
@ -45,13 +45,13 @@ class SettingsProvider extends ChangeNotifier {
_settings = Settings.fromJson(jsonDecode(_storage.getString(_fieldName)!));
} catch(exception) {
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()));
log("Settings recovered successfully: ${_settings.toJson().toString()}");
log('Settings recovered successfully: ${_settings.toJson().toString()}');
} 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();
log("Settings were reset to defaults!");
log('Settings were reset to defaults!');
}
}

View File

@ -4,13 +4,13 @@ class AppTheme {
static ThemeModeDisplay getDisplayOptions(ThemeMode theme) {
switch(theme) {
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:
return ThemeModeDisplay(icon: Icons.wb_sunny_outlined, displayName: "Hell");
return ThemeModeDisplay(icon: Icons.wb_sunny_outlined, displayName: 'Hell');
case ThemeMode.dark:
return ThemeModeDisplay(icon: Icons.dark_mode_outlined, displayName: "Dunkel");
return ThemeModeDisplay(icon: Icons.dark_mode_outlined, displayName: 'Dunkel');
}
}

View File

@ -21,7 +21,7 @@ class _LoginState extends State<Login> {
bool displayDisclaimerText = true;
String? _checkInput(value){
return (value ?? "").length == 0 ? "Eingabe erforderlich" : null;
return (value ?? '').length == 0 ? 'Eingabe erforderlich' : null;
}
Future<String?> _login(LoginData data) async {
@ -42,7 +42,7 @@ class _LoginState extends State<Login> {
} catch(e) {
await AccountData().removeData();
log(e.toString());
return "Benutzername oder Password falsch! (${e.toString()})";
return 'Benutzername oder Password falsch! (${e.toString()})';
}
await Future.delayed(const Duration(seconds: 1));
@ -51,14 +51,14 @@ class _LoginState extends State<Login> {
Future<String> _resetPassword(String name) {
return Future.delayed(Duration.zero).then((_) {
return "Diese Funktion steht nicht zur Verfügung!";
return 'Diese Funktion steht nicht zur Verfügung!';
});
}
@override
Widget build(BuildContext context) {
return FlutterLogin(
logo: Image.asset("assets/logo/icon.png").image,
logo: Image.asset('assets/logo/icon.png').image,
userValidator: _checkInput,
passwordValidator: _checkInput,
@ -84,9 +84,9 @@ class _LoginState extends State<Login> {
),
messages: LoginMessages(
loginButton: "Anmelden",
userHint: "Nutzername",
passwordHint: "Passwort",
loginButton: 'Anmelden',
userHint: 'Nutzername',
passwordHint: 'Passwort',
),
disableCustomPageTransformer: true,
@ -97,15 +97,15 @@ class _LoginState extends State<Login> {
child: Visibility(
visible: displayDisclaimerText,
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,
),
),
),
),
footer: "Marianum Fulda - Die persönliche Schule",
title: "Marianum Fulda",
footer: 'Marianum Fulda - Die persönliche Schule',
title: 'Marianum Fulda',
userType: LoginUserType.name,
);

View File

@ -6,7 +6,7 @@ import 'package:flowder/flowder.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:jiffy/jiffy.dart';
import 'package:marianum_mobile/widget/infoDialog.dart';
import '../../../widget/infoDialog.dart';
import 'package:nextcloud/nextcloud.dart';
import 'package:path_provider/path_provider.dart';
@ -29,7 +29,7 @@ class FileElement extends StatefulWidget {
Directory paths = await getTemporaryDirectory();
var encodedPath = Uri.encodeComponent(remotePath);
encodedPath = encodedPath.replaceAll("%2F", "/");
encodedPath = encodedPath.replaceAll('%2F', '/');
String local = paths.path + Platform.pathSeparator + name;
@ -44,7 +44,7 @@ class FileElement extends StatefulWidget {
onDone: () {
//Future<OpenResult> result = OpenFile.open(local); // TODO legacy - refactor: remove onDone parameter
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) => {
// onDone(value)
// });
@ -71,21 +71,21 @@ class _FileElementState extends State<FileElement> {
children: [
Container(
margin: const EdgeInsets.only(right: 10),
child: const Text("Download:"),
child: const Text('Download:'),
),
Expanded(
child: LinearProgressIndicator(value: percent/100),
),
Container(
margin: const EdgeInsets.only(left: 10),
child: Text("${percent.round()}%"),
child: Text('${percent.round()}%'),
),
],
);
}
return widget.file.isDirectory
? 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('geändert ${Jiffy.parseFromDateTime(widget.file.modifiedAt ?? DateTime.now()).fromNow()}')
: Text('${filesize(widget.file.size)}, ${Jiffy.parseFromDateTime(widget.file.modifiedAt ?? DateTime.now()).fromNow()}');
}
@override
@ -106,17 +106,17 @@ class _FileElementState extends State<FileElement> {
));
} else {
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;
}
if(widget.file.currentlyDownloading) {
showDialog(
context: context,
builder: (context) => ConfirmDialog(
title: "Download abbrechen?",
content: "Möchtest du den Download abbrechen?",
cancelButton: "Nein",
confirmButton: "Ja, Abbrechen",
title: 'Download abbrechen?',
content: 'Möchtest du den Download abbrechen?',
cancelButton: 'Nein',
confirmButton: 'Ja, Abbrechen',
onConfirm: () {
downloadCore?.then((value) {
if(!value.isCancelled) value.cancel();
@ -143,7 +143,7 @@ class _FileElementState extends State<FileElement> {
if(result.type != ResultType.done) {
showDialog(context: context, builder: (context) {
return AlertDialog(
title: const Text("Download"),
title: const Text('Download'),
content: Text(result.message),
);
});
@ -163,12 +163,12 @@ class _FileElementState extends State<FileElement> {
children: [
ListTile(
leading: const Icon(Icons.delete_outline),
title: const Text("Löschen"),
title: const Text('Löschen'),
onTap: () {
Navigator.of(context).pop();
showDialog(context: context, builder: (context) => ConfirmDialog(
title: "Element löschen?",
content: "Das Element wird unwiederruflich gelöscht.",
title: 'Element löschen?',
content: 'Das Element wird unwiederruflich gelöscht.',
onConfirm: () {
WebdavApi.webdav
.then((value) => value.delete(PathUri.parse(widget.file.path)))
@ -181,7 +181,7 @@ class _FileElementState extends State<FileElement> {
visible: !kReleaseMode,
child: ListTile(
leading: const Icon(Icons.share_outlined),
title: const Text("Teilen"),
title: const Text('Teilen'),
onTap: () {
Navigator.of(context).pop();
UnimplementedDialog.show(context);

View File

@ -43,8 +43,8 @@ class _FileUploadDialogState extends State<FileUploadDialog> {
setState(() {
state = FileUploadState.checkConflict;
});
List<WebDavResponse> result = (await webdavClient.propfind(PathUri.parse(widget.remotePath.join("/")))).responses;
if(result.any((element) => element.href!.endsWith("/$targetFileName"))) {
List<WebDavResponse> result = (await webdavClient.propfind(PathUri.parse(widget.remotePath.join('/')))).responses;
if(result.any((element) => element.href!.endsWith('/$targetFileName'))) {
setState(() {
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
uploadTask.then((value) => Future<HttpClientResponse?>.value(value)).catchError((e) {
uploadTask.then(Future<HttpClientResponse?>.value).catchError((e) {
setState(() {
state = FileUploadState.error;
});
@ -67,7 +67,7 @@ class _FileUploadDialogState extends State<FileUploadDialog> {
cancelableOperation = CancelableOperation<HttpClientResponse>.fromFuture(
uploadTask,
onCancel: () => log("Upload cancelled"),
onCancel: () => log('Upload cancelled'),
);
cancelableOperation!.then((value) {
@ -88,7 +88,7 @@ class _FileUploadDialogState extends State<FileUploadDialog> {
void initState() {
super.initState();
targetFileName = widget.fileName;
remoteFolderName = widget.remotePath.isNotEmpty ? widget.remotePath.last : "/";
remoteFolderName = widget.remotePath.isNotEmpty ? widget.remotePath.last : '/';
fileNameController.text = widget.fileName;
}
@ -96,7 +96,7 @@ class _FileUploadDialogState extends State<FileUploadDialog> {
Widget build(BuildContext context) {
if(state == FileUploadState.naming) {
return AlertDialog(
title: const Text("Datei hochladen"),
title: const Text('Datei hochladen'),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
@ -107,7 +107,7 @@ class _FileUploadDialogState extends State<FileUploadDialog> {
},
autocorrect: false,
decoration: const InputDecoration(
labelText: "Dateiname",
labelText: 'Dateiname',
),
),
],
@ -115,10 +115,10 @@ class _FileUploadDialogState extends State<FileUploadDialog> {
actions: [
TextButton(onPressed: () {
Navigator.of(context).pop();
}, child: const Text("Abbrechen")),
}, child: const Text('Abbrechen')),
TextButton(onPressed: () async {
upload();
}, child: const Text("Hochladen")),
}, child: const Text('Hochladen')),
],
);
@ -127,7 +127,7 @@ class _FileUploadDialogState extends State<FileUploadDialog> {
if(state == FileUploadState.conflict) {
return AlertDialog(
icon: const Icon(Icons.error_outline),
title: const Text("Datei konflikt"),
title: const Text('Datei konflikt'),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
@ -139,10 +139,10 @@ class _FileUploadDialogState extends State<FileUploadDialog> {
setState(() {
state = FileUploadState.naming;
});
}, child: const Text("Datei umbenennen")),
}, child: const Text('Datei umbenennen')),
TextButton(onPressed: () {
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) {
return AlertDialog(
icon: const Icon(Icons.upload),
title: const Text("Hochladen"),
title: const Text('Hochladen'),
content: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Visibility(
visible: state == FileUploadState.upload,
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),
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),
),
const SizedBox(height: 30),
const CircularProgressIndicator()
@ -183,7 +183,7 @@ class _FileUploadDialogState extends State<FileUploadDialog> {
}
return AlertDialog(
icon: const Icon(Icons.done),
title: const Text("Upload fertig"),
title: const Text('Upload fertig'),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
@ -193,7 +193,7 @@ class _FileUploadDialogState extends State<FileUploadDialog> {
actions: [
TextButton(onPressed: () {
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) {
return AlertDialog(
icon: const Icon(Icons.error_outline),
title: const Text("Fehler"),
title: const Text('Fehler'),
content: const Column(
mainAxisSize: MainAxisSize.min,
children: [
Text("Es ist ein Fehler aufgetreten!", textAlign: TextAlign.center),
Text('Es ist ein Fehler aufgetreten!', textAlign: TextAlign.center),
],
),
actions: [
TextButton(onPressed: () {
Navigator.of(context).pop();
}, child: const Text("Schlißen")),
}, child: const Text('Schlißen')),
],
);
}
throw UnimplementedError("Invalid state");
throw UnimplementedError('Invalid state');
}
}

View File

@ -43,17 +43,17 @@ enum SortOption {
class SortOptions {
static Map<SortOption, BetterSortOption> options = {
SortOption.name: BetterSortOption(
displayName: "Name",
displayName: 'Name',
icon: Icons.sort_by_alpha_outlined,
compare: (CacheableFile a, CacheableFile b) => a.name.compareTo(b.name)
),
SortOption.date: BetterSortOption(
displayName: "Datum",
displayName: 'Datum',
icon: Icons.history_outlined,
compare: (CacheableFile a, CacheableFile b) => a.modifiedAt!.compareTo(b.modifiedAt!)
),
SortOption.size: BetterSortOption(
displayName: "Größe",
displayName: 'Größe',
icon: Icons.sd_card_outlined,
compare: (CacheableFile a, CacheableFile b) {
if(a.isDirectory || b.isDirectory) return a.isDirectory ? 1 : 0;
@ -88,7 +88,7 @@ class _FilesState extends State<Files> {
void _query() {
ListFilesCache(
path: widget.path.isEmpty ? "/" : widget.path.join("/"),
path: widget.path.isEmpty ? '/' : widget.path.join('/'),
onUpdate: (ListFilesResponse d) {
if(!context.mounted) return; // prevent setState when widget is possibly already disposed
d.files.removeWhere((element) => element.name.isEmpty || element.name == widget.path.lastOrNull());
@ -109,7 +109,7 @@ class _FilesState extends State<Files> {
return Scaffold(
appBar: AppBar(
title: Text(widget.path.isNotEmpty ? widget.path.last : "Dateien"),
title: Text(widget.path.isNotEmpty ? widget.path.last : 'Dateien'),
actions: [
// IconButton(
// icon: const Icon(Icons.search),
@ -127,7 +127,7 @@ class _FilesState extends State<Files> {
children: [
Icon(e ? Icons.text_rotate_up : Icons.text_rotation_down, color: Theme.of(context).colorScheme.onSurface),
const SizedBox(width: 15),
Text(e ? "Aufsteigend" : "Absteigend")
Text(e ? 'Aufsteigend' : 'Absteigend')
],
)
)).toList();
@ -164,7 +164,7 @@ class _FilesState extends State<Files> {
],
),
floatingActionButton: FloatingActionButton(
heroTag: "uploadFile",
heroTag: 'uploadFile',
backgroundColor: Theme.of(context).primaryColor,
onPressed: () {
showDialog(context: context, builder: (context) {
@ -172,29 +172,29 @@ class _FilesState extends State<Files> {
children: [
ListTile(
leading: const Icon(Icons.create_new_folder_outlined),
title: const Text("Ordner erstellen"),
title: const Text('Ordner erstellen'),
onTap: () {
Navigator.of(context).pop();
showDialog(context: context, builder: (context) {
var inputController = TextEditingController();
return AlertDialog(
title: const Text("Neuer Ordner"),
title: const Text('Neuer Ordner'),
content: TextField(
controller: inputController,
decoration: const InputDecoration(
labelText: "Name",
labelText: 'Name',
),
),
actions: [
TextButton(onPressed: () {
Navigator.of(context).pop();
}, child: const Text("Abbrechen")),
}, child: const Text('Abbrechen')),
TextButton(onPressed: () {
WebdavApi.webdav.then((webdav) {
webdav.mkcol(PathUri.parse("${widget.path.join("/")}/${inputController.text}")).then((value) => _query());
});
Navigator.of(context).pop();
}, child: const Text("Ordner erstellen")),
}, child: const Text('Ordner erstellen')),
],
);
});
@ -202,12 +202,10 @@ class _FilesState extends State<Files> {
),
ListTile(
leading: const Icon(Icons.upload_file),
title: const Text("Aus Dateien hochladen"),
title: const Text('Aus Dateien hochladen'),
onTap: () {
context.loaderOverlay.show();
FilePick.documentPick().then((value) {
mediaUpload(value);
});
FilePick.documentPick().then(mediaUpload);
Navigator.of(context).pop();
},
),
@ -215,7 +213,7 @@ class _FilesState extends State<Files> {
visible: !Platform.isIOS,
child: ListTile(
leading: const Icon(Icons.add_a_photo_outlined),
title: const Text("Aus Gallerie hochladen"),
title: const Text('Aus Gallerie hochladen'),
onTap: () {
context.loaderOverlay.show();
FilePick.galleryPick().then((value) {
@ -231,7 +229,7 @@ class _FilesState extends State<Files> {
},
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(
onRefresh: () {
_query();
@ -258,6 +256,6 @@ class _FilesState extends State<Files> {
}
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);
}
}

View File

@ -22,20 +22,20 @@ class _FeedbackDialogState extends State<FeedbackDialog> {
Widget build(BuildContext context) {
return AlertDialog(
title: const Text("Feedback"),
title: const Text('Feedback'),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
const Text("Feedback, Anregungen, Ideen, Fehler und Verbesserungen"),
const Text('Feedback, Anregungen, Ideen, Fehler und Verbesserungen'),
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),
TextField(
controller: _feedbackInput,
autofocus: true,
decoration: const InputDecoration(
border: OutlineInputBorder(),
label: Text("Feedback und Verbesserungen")
label: Text('Feedback und Verbesserungen')
),
// style: TextStyle(),
// expands: true,
@ -44,12 +44,12 @@ class _FeedbackDialogState extends State<FeedbackDialog> {
),
Visibility(
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: [
TextButton(onPressed: () => Navigator.of(context).pop(), child: const Text("Abbrechen")),
TextButton(onPressed: () => Navigator.of(context).pop(), child: const Text('Abbrechen')),
TextButton(
onPressed: () async {
AddFeedback(
@ -62,7 +62,7 @@ class _FeedbackDialogState extends State<FeedbackDialog> {
.run()
.then((value) {
Navigator.of(context).pop();
InfoDialog.show(context, "Danke für dein Feedback!");
InfoDialog.show(context, 'Danke für dein Feedback!');
})
.catchError((error, trace) {
setState(() {
@ -70,7 +70,7 @@ class _FeedbackDialogState extends State<FeedbackDialog> {
});
});
},
child: const Text("Senden"),
child: const Text('Senden'),
)
],
);

View File

@ -24,7 +24,7 @@ class _GradeAverageState extends State<GradeAverage> {
String getGradeDisplay(int grade) {
if(gradeSystem) {
return "Note $grade";
return 'Note $grade';
} else {
return "$grade Punkt${grade > 1 ? "e" : ""}";
}
@ -48,22 +48,22 @@ class _GradeAverageState extends State<GradeAverage> {
if(!settings.val().gradeAveragesSettings.askedForPreferredGradeSystem) {
settings.val(write: true).gradeAveragesSettings.askedForPreferredGradeSystem = true;
showDialog(context: context, builder: (context) => AlertDialog(
title: const Text("Notensystem"),
content: const Text("Wähle dein bevorzugtes Schulnotensystem"),
title: const Text('Notensystem'),
content: const Text('Wähle dein bevorzugtes Schulnotensystem'),
actions: [
TextButton(
onPressed: () {
switchSystem(true);
Navigator.of(context).pop();
},
child: const Text("Realschule"),
child: const Text('Realschule'),
),
TextButton(
onPressed: () {
switchSystem(false);
Navigator.of(context).pop();
},
child: const Text("Oberstufe"),
child: const Text('Oberstufe'),
),
],
));
@ -81,7 +81,7 @@ class _GradeAverageState extends State<GradeAverage> {
return Scaffold(
appBar: AppBar(
title: const Text("Notendurschnittsrechner"),
title: const Text('Notendurschnittsrechner'),
actions: [
Visibility(
visible: grades.isNotEmpty,
@ -89,9 +89,9 @@ class _GradeAverageState extends State<GradeAverage> {
showDialog(
context: context,
builder: (context) => ConfirmDialog(
title: "Zurücksetzen?",
content: "Alle Einträge werden entfernt.",
confirmButton: "Zurücksetzen",
title: 'Zurücksetzen?',
content: 'Alle Einträge werden entfernt.',
confirmButton: 'Zurücksetzen',
onConfirm: () {
grades.clear();
setState(() {});
@ -109,7 +109,7 @@ class _GradeAverageState extends State<GradeAverage> {
children: [
Icon(e ? Icons.calculate_outlined : Icons.school_outlined, color: Theme.of(context).colorScheme.onSurface),
const SizedBox(width: 15),
Text(e ? "Notensystem" : "Punktesystem"),
Text(e ? 'Notensystem' : 'Punktesystem'),
],
),
)).toList(),
@ -120,9 +120,9 @@ class _GradeAverageState extends State<GradeAverage> {
showDialog(
context: context,
builder: (context) => ConfirmDialog(
title: "Notensystem wechseln",
content: "Beim wechsel des Notensystems werden alle Einträge zurückgesetzt.",
confirmButton: "Fortfahren",
title: 'Notensystem wechseln',
content: 'Beim wechsel des Notensystems werden alle Einträge zurückgesetzt.',
confirmButton: 'Fortfahren',
onConfirm: () => switchSystem(e),
),
);
@ -142,7 +142,7 @@ class _GradeAverageState extends State<GradeAverage> {
const SizedBox(height: 10),
const Divider(),
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),
Expanded(
child: ListView.builder(
@ -169,7 +169,7 @@ class _GradeAverageState extends State<GradeAverage> {
icon: const Icon(Icons.remove),
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(
onPressed: () {
setState(() {

View File

@ -42,23 +42,23 @@ class _HolidaysState extends State<Holidays> {
}
String parseString(String enDate) {
return Jiffy.parse(enDate).format(pattern: "dd.MM.yyyy");
return Jiffy.parse(enDate).format(pattern: 'dd.MM.yyyy');
}
void showDisclaimer() {
showDialog(context: context, builder: (context) {
return AlertDialog(
title: const Text("Richtigkeit und Bereitstellung der Daten"),
title: const Text('Richtigkeit und Bereitstellung der Daten'),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
const Text(""
"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"
"Die Daten stammen von https://ferien-api.de/"),
const Text(''
'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'
'Die Daten stammen von https://ferien-api.de/'),
const SizedBox(height: 30),
ListTile(
title: const Text("Diese Meldung nicht mehr anzeigen"),
title: const Text('Diese Meldung nicht mehr anzeigen'),
trailing: Checkbox(
value: settings.val().holidaysSettings.dismissedDisclaimer,
onChanged: (value) => settings.val(write: true).holidaysSettings.dismissedDisclaimer = value!,
@ -67,8 +67,8 @@ class _HolidaysState extends State<Holidays> {
],
),
actions: [
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('ferien-api.de besuchen'), onPressed: () => ConfirmDialog.openBrowser(context, 'https://ferien-api.de/')),
TextButton(child: const Text('Okay'), onPressed: () => Navigator.of(context).pop()),
],
);
});
@ -78,11 +78,11 @@ class _HolidaysState extends State<Holidays> {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Schulferien in Hessen"),
title: const Text('Schulferien in Hessen'),
actions: [
IconButton(
icon: const Icon(Icons.warning_amber_outlined),
onPressed: () => showDisclaimer(),
onPressed: showDisclaimer,
),
PopupMenuButton<bool>(
initialValue: settings.val().holidaysSettings.showPastEvents,
@ -95,7 +95,7 @@ class _HolidaysState extends State<Holidays> {
children: [
Icon(e ? Icons.history_outlined : Icons.history_toggle_off_outlined, color: Theme.of(context).colorScheme.onSurface),
const SizedBox(width: 15),
Text(e ? "Alle anzeigen" : "Nur zukünftige anzeigen")
Text(e ? 'Alle anzeigen' : 'Nur zukünftige anzeigen')
],
)
)).toList();
@ -115,19 +115,19 @@ class _HolidaysState extends State<Holidays> {
List<GetHolidaysResponseObject> holidays = value.getHolidaysResponse.data;
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(
itemCount: holidays.length,
itemBuilder: (context, index) {
GetHolidaysResponseObject holiday = holidays[index];
String holidayType = holiday.name.split(" ").first.capitalize();
String holidayType = holiday.name.split(' ').first.capitalize();
return ListTile(
leading: const CenteredLeading(Icon(Icons.calendar_month)),
title: Text("$holidayType ab ${parseString(holiday.start)}"),
subtitle: Text("bis ${parseString(holiday.end)}"),
title: Text('$holidayType ab ${parseString(holiday.start)}'),
subtitle: Text('bis ${parseString(holiday.end)}'),
onTap: () => showDialog(context: context, builder: (context) => SimpleDialog(
title: Text("$holidayType ${holiday.year} in Hessen"),
title: Text('$holidayType ${holiday.year} in Hessen'),
children: [
ListTile(
leading: const CenteredLeading(Icon(Icons.signpost_outlined)),
@ -136,11 +136,11 @@ class _HolidaysState extends State<Holidays> {
),
ListTile(
leading: const Icon(Icons.arrow_forward),
title: Text("vom ${parseString(holiday.start)}"),
title: Text('vom ${parseString(holiday.start)}'),
),
ListTile(
leading: const Icon(Icons.arrow_back),
title: Text("bis zum ${parseString(holiday.end)}"),
title: Text('bis zum ${parseString(holiday.end)}'),
),
Visibility(
visible: !DateTime.parse(holiday.start).difference(DateTime.now()).isNegative,

View File

@ -27,7 +27,7 @@ class _MessageState extends State<Message> {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Marianum Message"),
title: const Text('Marianum Message'),
),
body: Consumer<MessageProps>(builder: (context, value, child) {
if(value.primaryLoading()) return const LoadingSpinner();
@ -43,7 +43,7 @@ class _MessageState extends State<Message> {
children: [Icon(Icons.newspaper)],
),
title: Text(message.name, overflow: TextOverflow.ellipsis),
subtitle: Text("vom ${message.date}"),
subtitle: Text('vom ${message.date}'),
trailing: const Icon(Icons.arrow_right),
onTap: () {
Navigator.push(context, MaterialPageRoute(builder: (context) => MessageView(basePath: value.getMessagesResponse.base, message: message)));

View File

@ -29,12 +29,12 @@ class _MessageViewState extends State<MessageView> {
Navigator.of(context).pop();
showDialog(context: context, builder: (context) {
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}"),
actions: [
TextButton(onPressed: () {
Navigator.of(context).pop();
}, child: const Text("Ok"))
}, child: const Text('Ok'))
],
);
});
@ -43,9 +43,9 @@ class _MessageViewState extends State<MessageView> {
showDialog(
context: context,
builder: (context) => ConfirmDialog(
title: "Link öffnen",
content: "Möchtest du den folgenden Link öffnen?\n${e.uri}",
confirmButton: "Öffnen",
title: 'Link öffnen',
content: 'Möchtest du den folgenden Link öffnen?\n${e.uri}',
confirmButton: 'Öffnen',
onConfirm: () => launchUrl(Uri.parse(e.uri), mode: LaunchMode.externalApplication),
),
);

View File

@ -8,10 +8,10 @@ class Roomplan extends StatelessWidget {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Raumplan"),
title: const Text('Raumplan'),
),
body: PhotoView(
imageProvider: Image.asset("assets/img/raumplan.jpg").image,
imageProvider: Image.asset('assets/img/raumplan.jpg').image,
minScale: 0.5,
maxScale: 2.0,
backgroundDecoration: BoxDecoration(color: Theme.of(context).colorScheme.background),

View File

@ -16,18 +16,18 @@ class _QrShareViewState extends State<QrShareView> {
length: 2,
child: Scaffold(
appBar: AppBar(
title: const Text("Teile die App"),
title: const Text('Teile die App'),
bottom: const TabBar(
tabs: [
Tab(icon: Icon(Icons.android_outlined), text: "Android"),
Tab(icon: Icon(Icons.apple_outlined), text: "iOS & iPadOS"),
Tab(icon: Icon(Icons.android_outlined), text: 'Android'),
Tab(icon: Icon(Icons.apple_outlined), text: 'iOS & iPadOS'),
],
),
),
body: const TabBarView(
children: [
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 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'),
],
),
),

View File

@ -13,7 +13,7 @@ class SelectShareTypeDialog extends StatelessWidget {
children: [
ListTile(
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),
onTap: () {
Navigator.of(context).push(MaterialPageRoute(builder: (context) => const QrShareView()));
@ -21,16 +21,16 @@ class SelectShareTypeDialog extends StatelessWidget {
),
ListTile(
leading: const Icon(Icons.link_outlined),
title: const Text("Per Link teilen"),
title: const Text('Per Link teilen'),
trailing: const Icon(Icons.arrow_right),
onTap: () {
Share.share(
sharePositionOrigin: SharePositionOrigin.get(context),
subject: "App Teilen",
"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 "
"\niOS: https://apps.apple.com/us/app/marianum-fulda/id6458789560 "
"\n\nViel Spaß!"
subject: 'App Teilen',
'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 '
'\niOS: https://apps.apple.com/us/app/marianum-fulda/id6458789560 '
'\n\nViel Spaß!'
);
},
)

View File

@ -3,7 +3,7 @@ import 'dart:io';
import 'package:flutter/material.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 '../../widget/ListItem.dart';
@ -26,23 +26,23 @@ class Overhang extends StatelessWidget {
return Scaffold(
appBar: AppBar(
title: const Text("Mehr"),
title: const Text('Mehr'),
actions: [
IconButton(onPressed: () => pushScreen(context, screen: const Settings(), withNavBar: false), icon: const Icon(Icons.settings))
],
),
body: ListView(
children: [
const ListItemNavigator(icon: Icons.newspaper, text: "Marianum Message", target: Message()),
const ListItemNavigator(icon: Icons.room, text: "Raumplan", target: Roomplan()),
const ListItemNavigator(icon: Icons.calculate, text: "Notendurschnittsrechner", target: GradeAverage()),
const ListItemNavigator(icon: Icons.newspaper, text: 'Marianum Message', target: Message()),
const ListItemNavigator(icon: Icons.room, text: 'Raumplan', target: Roomplan()),
const ListItemNavigator(icon: Icons.calculate, text: 'Notendurschnittsrechner', target: GradeAverage()),
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(),
ListTile(
leading: const Icon(Icons.share_outlined),
title: const Text("Teile die App"),
subtitle: const Text("Mit Freunden und deiner Klasse teilen"),
title: const Text('Teile die App'),
subtitle: const Text('Mit Freunden und deiner Klasse teilen'),
trailing: const Icon(Icons.arrow_right),
onTap: () => showDialog(context: context, builder: (context) => const SelectShareTypeDialog())
),
@ -52,19 +52,19 @@ class Overhang extends StatelessWidget {
if(!snapshot.hasData) return const SizedBox.shrink();
String? getPlatformStoreName() {
if(Platform.isAndroid) return "Play store";
if(Platform.isIOS) return "App store";
if(Platform.isAndroid) return 'Play store';
if(Platform.isIOS) return 'App store';
return null;
}
return ListTile(
leading: const CenteredLeading(Icon(Icons.star_rate_outlined)),
title: const Text("App Bewerten"),
subtitle: getPlatformStoreName().wrapNullable((data) => Text("Im $data")),
title: const Text('App bewerten'),
subtitle: getPlatformStoreName().wrapNullable((data) => Text('Im $data')),
trailing: const Icon(Icons.arrow_right),
onTap: () {
InAppReview.instance.openStoreListing(appStoreId: "6458789560").then(
(value) => InfoDialog.show(context, "Vielen Dank!"),
InAppReview.instance.openStoreListing(appStoreId: '6458789560').then(
(value) => InfoDialog.show(context, 'Vielen Dank!'),
onError: (error) => InfoDialog.show(context, error.toString())
);
},
@ -73,8 +73,8 @@ class Overhang extends StatelessWidget {
),
ListTile(
leading: const CenteredLeading(Icon(Icons.feedback_outlined)),
title: const Text("Du hast eine Idee?"),
subtitle: const Text("Fehler und Verbessungsvorschläge"),
title: const Text('Du hast eine Idee?'),
subtitle: const Text('Fehler und Verbessungsvorschläge'),
trailing: const Icon(Icons.arrow_right),
onTap: () => showDialog(context: context, barrierDismissible: false, builder: (context) => const FeedbackDialog()),
),

View File

@ -66,7 +66,7 @@ class _ChatInfoState extends State<ChatInfo> {
if(participants != null) ...[
ListTile(
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),
onTap: () => TalkNavigator.pushSplitView(context, ParticipantsListView(participants!)),
),

View File

@ -16,7 +16,7 @@ class _ParticipantsListViewState extends State<ParticipantsListView> {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Teilnehmende"),
title: const Text('Teilnehmende'),
),
body: ListView(
children: widget.participantsResponse.data.map((participant) {

View File

@ -40,9 +40,9 @@ class _ChatListState extends State<ChatList> {
ConfirmDialog(
icon: Icons.notifications_active_outlined,
title: "Benachrichtigungen aktivieren",
content: "Auf wunsch kannst du Push-Benachrichtigungen aktivieren. Deine Einstellungen kannst du jederzeit ändern.",
confirmButton: "Weiter",
title: 'Benachrichtigungen aktivieren',
content: 'Auf wunsch kannst du Push-Benachrichtigungen aktivieren. Deine Einstellungen kannst du jederzeit ändern.',
confirmButton: 'Weiter',
onConfirm: () {
FirebaseMessaging.instance.requestPermission(
provisional: false
@ -53,7 +53,7 @@ class _ChatListState extends State<ChatList> {
break;
case AuthorizationStatus.denied:
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;
default:
@ -79,7 +79,7 @@ class _ChatListState extends State<ChatList> {
breakpoint: 1000,
child: Scaffold(
appBar: AppBar(
title: const Text("Talk"),
title: const Text('Talk'),
actions: [
IconButton(
icon: const Icon(Icons.search),
@ -91,16 +91,16 @@ class _ChatListState extends State<ChatList> {
],
),
floatingActionButton: FloatingActionButton(
heroTag: "createChat",
heroTag: 'createChat',
backgroundColor: Theme.of(context).primaryColor,
onPressed: () async {
showSearch(context: context, delegate: JoinChat()).then((username) {
if(username == null) return;
ConfirmDialog(
title: "Chat starten",
title: 'Chat starten',
content: "Möchtest du einen Chat mit Nutzer '$username' starten?",
confirmButton: "Chat starten",
confirmButton: 'Chat starten',
onConfirm: () {
CreateRoom(CreateRoomParams(
roomType: 1,

View File

@ -1,6 +1,6 @@
import 'package:flutter/material.dart';
import 'package:marianum_mobile/extensions/dateTime.dart';
import '../../../extensions/dateTime.dart';
import 'package:provider/provider.dart';
import '../../../api/marianumcloud/talk/chat/getChatResponse.dart';
@ -51,8 +51,8 @@ class _ChatViewState extends State<ChatView> {
data.getChatResponse.sortByTimestamp().forEach((element) {
DateTime elementDate = DateTime.fromMillisecondsSinceEpoch(element.timestamp * 1000);
if(element.systemMessage.contains("reaction")) return;
int commonRead = int.parse(data.getChatResponse.headers?['x-chat-last-common-read'] ?? "0");
if(element.systemMessage.contains('reaction')) return;
int commonRead = int.parse(data.getChatResponse.headers?['x-chat-last-common-read'] ?? '0');
if(!elementDate.isSameDay(lastDate)) {
lastDate = elementDate;
@ -81,8 +81,8 @@ class _ChatViewState extends State<ChatView> {
context: context,
isSender: false,
bubbleData: GetChatResponseObject.getTextDummy(
"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"
'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'
),
chatData: widget.room,
refetch: _query,
@ -111,7 +111,7 @@ class _ChatViewState extends State<ChatView> {
body: Container(
decoration: BoxDecoration(
image: DecorationImage(
image: const AssetImage("assets/background/chat.png"),
image: const AssetImage('assets/background/chat.png'),
scale: 1.5,
opacity: 1,
repeat: ImageRepeat.repeat,

View File

@ -5,7 +5,7 @@ import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:jiffy/jiffy.dart';
import 'package:marianum_mobile/extensions/text.dart';
import '../../../../extensions/text.dart';
import 'package:provider/provider.dart';
import '../../../../api/marianumcloud/talk/chat/getChatResponse.dart';
@ -114,7 +114,7 @@ class _ChatBubbleState extends State<ChatBubble> {
);
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,
style: TextStyle(color: widget.timeIconColor, fontSize: widget.timeIconSize),
);
@ -184,7 +184,7 @@ class _ChatBubbleState extends State<ChatBubble> {
),
onLongPress: () {
showDialog(context: context, builder: (context) {
List<String> commonReactions = ["👍", "👎", "😆", "❤️", "👀", "🤔"];
List<String> commonReactions = ['👍', '👎', '😆', '❤️', '👀', '🤔'];
bool canReact = widget.bubbleData.messageType == GetRoomResponseObjectMessageType.comment;
return SimpleDialog(
children: [
@ -222,7 +222,7 @@ class _ChatBubbleState extends State<ChatBubble> {
visible: canReact,
child: ListTile(
leading: const Icon(Icons.add_reaction_outlined),
title: const Text("Reaktionen"),
title: const Text('Reaktionen'),
onTap: () {
Navigator.of(context).push(MaterialPageRoute(builder: (context) => MessageReactions(
token: widget.chatData.token,
@ -235,7 +235,7 @@ class _ChatBubbleState extends State<ChatBubble> {
visible: !message.containsFile,
child: ListTile(
leading: const Icon(Icons.copy),
title: const Text("Nachricht kopieren"),
title: const Text('Nachricht kopieren'),
onTap: () => {
Clipboard.setData(ClipboardData(text: widget.bubbleData.message)),
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()),
child: ListTile(
leading: const Icon(Icons.delete_outline),
title: const Text("Nachricht löschen"),
title: const Text('Nachricht löschen'),
onTap: () {
DeleteMessage(widget.chatData.token, widget.bubbleData.id).run().then((value) {
Provider.of<ChatProps>(context, listen: false).run();
@ -276,12 +276,12 @@ class _ChatBubbleState extends State<ChatBubble> {
if(downloadProgress > 0) {
showDialog(context: context, builder: (context) {
return AlertDialog(
title: const Text("Download abbrechen?"),
content: const Text("Möchtest du den Download abbrechen?"),
title: const Text('Download abbrechen?'),
content: const Text('Möchtest du den Download abbrechen?'),
actions: [
TextButton(onPressed: () {
Navigator.of(context).pop();
}, child: const Text("Nein")),
}, child: const Text('Nein')),
TextButton(onPressed: () {
downloadCore?.then((value) {
if(!value.isCancelled) value.cancel();
@ -291,7 +291,7 @@ class _ChatBubbleState extends State<ChatBubble> {
downloadProgress = 0;
downloadCore = null;
});
}, child: const Text("Ja, Abbrechen"))
}, child: const Text('Ja, Abbrechen'))
],
);
});
@ -336,7 +336,7 @@ class _ChatBubbleState extends State<ChatBubble> {
return Container(
margin: const EdgeInsets.only(right: 2.5, left: 2.5),
child: ActionChip(
label: Text("${e.key} ${e.value}"),
label: Text('${e.key} ${e.value}'),
visualDensity: const VisualDensity(vertical: VisualDensity.minimumDensity, horizontal: VisualDensity.minimumDensity),
padding: EdgeInsets.zero,
backgroundColor: hasSelfReacted ? Theme.of(context).primaryColor : null,

View File

@ -14,14 +14,14 @@ class ChatMessage {
Map<String, RichObjectString>? originalData;
RichObjectString? file;
String content = "";
String content = '';
bool get containsFile => file != null;
ChatMessage({required this.originalMessage, this.originalData}) {
if(originalData?.containsKey("file") ?? false) {
if(originalData?.containsKey('file') ?? false) {
file = originalData?['file'];
content = file?.name ?? "Datei";
content = file?.name ?? 'Datei';
} else {
content = RichObjectStringProcessor.parseToString(originalMessage, originalData);
}
@ -56,7 +56,7 @@ class ChatMessage {
fadeInDuration: Duration.zero,
fadeOutDuration: Duration.zero,
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',
);
}

View File

@ -41,9 +41,9 @@ class _ChatTextfieldState extends State<ChatTextfield> {
}
String filename = "${path.split("/").last.split(".").first}-${const Uuid().v4()}.${path.split(".").last}";
String shareFolder = "MarianumMobile";
String shareFolder = 'MarianumMobile';
WebdavApi.webdav.then((webdav) {
webdav.mkcol(PathUri.parse("/$shareFolder"));
webdav.mkcol(PathUri.parse('/$shareFolder'));
});
showDialog(context: context, builder: (context) => FileUploadDialog(
@ -55,7 +55,7 @@ class _ChatTextfieldState extends State<ChatTextfield> {
FileSharingApi().share(FileSharingApiParams(
shareType: 10,
shareWith: widget.sendToToken,
path: "$shareFolder/$filename",
path: '$shareFolder/$filename',
)).then((value) => _query());
},
), barrierDismissible: false);
@ -77,7 +77,7 @@ class _ChatTextfieldState extends State<ChatTextfield> {
@override
Widget build(BuildContext context) {
_textBoxController.text = settings.val().talkSettings.drafts[widget.sendToToken] ?? "";
_textBoxController.text = settings.val().talkSettings.drafts[widget.sendToToken] ?? '';
return Stack(
children: <Widget>[
@ -95,12 +95,10 @@ class _ChatTextfieldState extends State<ChatTextfield> {
children: [
ListTile(
leading: const Icon(Icons.file_open),
title: const Text("Aus Dateien auswählen"),
title: const Text('Aus Dateien auswählen'),
onTap: () {
context.loaderOverlay.show();
FilePick.documentPick().then((value) {
mediaUpload(value);
});
FilePick.documentPick().then(mediaUpload);
Navigator.of(context).pop();
},
),
@ -108,7 +106,7 @@ class _ChatTextfieldState extends State<ChatTextfield> {
visible: !Platform.isIOS,
child: ListTile(
leading: const Icon(Icons.image),
title: const Text("Aus Gallerie auswählen"),
title: const Text('Aus Gallerie auswählen'),
onTap: () {
context.loaderOverlay.show();
FilePick.galleryPick().then((value) {
@ -147,12 +145,12 @@ class _ChatTextfieldState extends State<ChatTextfield> {
maxLines: 7,
minLines: 1,
decoration: const InputDecoration(
hintText: "Nachricht schreiben...",
hintText: 'Nachricht schreiben...',
border: InputBorder.none,
),
onChanged: (String text) {
if(text.trim().toLowerCase() == "marbot marbot marbot") {
var newText = "Roboter sind cool und so, aber Marbots sind besser!";
if(text.trim().toLowerCase() == 'marbot marbot marbot') {
var newText = 'Roboter sind cool und so, aber Marbots sind besser!';
_textBoxController.text = newText;
text = newText;
}
@ -175,8 +173,8 @@ class _ChatTextfieldState extends State<ChatTextfield> {
setState(() {
isLoading = false;
});
_textBoxController.text = "";
setDraft("");
_textBoxController.text = '';
setDraft('');
});
},
backgroundColor: Theme.of(context).primaryColor,

View File

@ -37,7 +37,7 @@ class _ChatTileState extends State<ChatTile> {
void initState() {
super.initState();
SharedPreferences.getInstance().then((value) => {
username = value.getString("username")!
username = value.getString('username')!
});
bool isGroup = widget.data.type != GetRoomResponseObjectConversationType.oneToOne;
@ -110,7 +110,7 @@ class _ChatTileState extends State<ChatTile> {
minHeight: 20,
),
child: Text(
"${widget.data.unreadMessages}",
'${widget.data.unreadMessages}',
style: const TextStyle(
color: Colors.white,
fontSize: 15,
@ -132,7 +132,7 @@ class _ChatTileState extends State<ChatTile> {
visible: widget.data.unreadMessages > 0,
replacement: ListTile(
leading: const Icon(Icons.mark_chat_unread_outlined),
title: const Text("Als ungelesen markieren"),
title: const Text('Als ungelesen markieren'),
onTap: () {
SetReadMarker(widget.data.token, false).run().then((value) => widget.query(renew: true));
Navigator.of(context).pop();
@ -140,7 +140,7 @@ class _ChatTileState extends State<ChatTile> {
),
child: ListTile(
leading: const Icon(Icons.mark_chat_read_outlined),
title: const Text("Als gelesen markieren"),
title: const Text('Als gelesen markieren'),
onTap: () {
setCurrentAsRead();
Navigator.of(context).pop();
@ -151,7 +151,7 @@ class _ChatTileState extends State<ChatTile> {
visible: widget.data.isFavorite,
replacement: ListTile(
leading: const Icon(Icons.star_outline),
title: const Text("Zu Favoriten hinzufügen"),
title: const Text('Zu Favoriten hinzufügen'),
onTap: () {
SetFavorite(widget.data.token, true).run().then((value) => widget.query(renew: true));
Navigator.of(context).pop();
@ -159,7 +159,7 @@ class _ChatTileState extends State<ChatTile> {
),
child: ListTile(
leading: const Icon(Icons.stars_outlined),
title: const Text("Von Favoriten entfernen"),
title: const Text('Von Favoriten entfernen'),
onTap: () {
SetFavorite(widget.data.token, false).run().then((value) => widget.query(renew: true));
Navigator.of(context).pop();
@ -168,12 +168,12 @@ class _ChatTileState extends State<ChatTile> {
),
ListTile(
leading: const Icon(Icons.delete_outline),
title: const Text("Konversation verlassen"),
title: const Text('Konversation verlassen'),
onTap: () {
ConfirmDialog(
title: "Chat verlassen",
content: "Du benötigst ggf. eine Einladung um erneut beizutreten.",
confirmButton: "Löschen",
title: 'Chat verlassen',
content: 'Du benötigst ggf. eine Einladung um erneut beizutreten.',
confirmButton: 'Löschen',
onConfirm: () {
LeaveRoom(widget.data.token).run().then((value) => widget.query(renew: true));
Navigator.of(context).pop();

View File

@ -17,10 +17,10 @@ class SplitViewPlaceholder extends StatelessWidget {
data: MediaQuery.of(context).copyWith(
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 Text("Marianum Fulda\nTalk", textAlign: TextAlign.center, style: TextStyle(fontSize: 30)),
const Text('Marianum Fulda\nTalk', textAlign: TextAlign.center, style: TextStyle(fontSize: 30)),
],
),
)

View File

@ -33,7 +33,7 @@ class JoinChat extends SearchDelegate<String> {
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) {
return const PlaceholderView(
text: "Suche nach benutzern",
text: 'Suche nach benutzern',
icon: Icons.person_search_outlined,
);
}
@ -63,7 +63,7 @@ class JoinChat extends SearchDelegate<String> {
itemBuilder: (context, index) {
AutocompleteResponseObject object = snapshot.data!.data[index];
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,
foregroundColor: Colors.white,
child: const Icon(Icons.person),
@ -80,7 +80,7 @@ class JoinChat extends SearchDelegate<String> {
}
);
} 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());

View File

@ -33,13 +33,13 @@ class _MessageReactionsState extends State<MessageReactions> {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Reaktionen"),
title: const Text('Reaktionen'),
),
body: FutureBuilder(
future: data,
builder: (context, snapshot) {
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(
children: [
...snapshot.data!.data.entries.map<Widget>((entry) {
@ -49,17 +49,17 @@ class _MessageReactionsState extends State<MessageReactions> {
iconColor: 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)),
title: Text("${entry.value.length} mal reagiert"),
title: Text('${entry.value.length} mal reagiert'),
children: entry.value.map((e) {
bool isSelf = AccountData().getUsername() == e.actorId;
return ListTile(
leading: UserAvatar(id: e.actorId, isGroup: false),
title: Text(e.actorDisplayName),
subtitle: isSelf
? const Text("Du")
: e.actorType == GetReactionsResponseObjectActorType.guests ? const Text("Gast") : null,
? const Text('Du')
: e.actorType == GetReactionsResponseObjectActorType.guests ? const Text('Gast') : null,
trailing: isSelf
? null
: Visibility(

View File

@ -11,7 +11,7 @@ class SearchChat extends SearchDelegate {
@override
List<Widget>? buildActions(BuildContext context) {
return [
if(query.isNotEmpty) IconButton(onPressed: () => query = "", icon: const Icon(Icons.delete)),
if(query.isNotEmpty) IconButton(onPressed: () => query = '', icon: const Icon(Icons.delete)),
];
}

View File

@ -53,7 +53,7 @@ class _AppointmentComponentState extends State<AppointmentComponent> {
FittedBox(
fit: BoxFit.fitWidth,
child: Text(
(meeting.location == null || meeting.location!.isEmpty ? " " : meeting.location!),
(meeting.location == null || meeting.location!.isEmpty ? ' ' : meeting.location!),
maxLines: 3,
overflow: TextOverflow.ellipsis,
softWrap: true,

View File

@ -27,9 +27,9 @@ import 'customTimetableEventEditDialog.dart';
class AppointmentDetails {
static String _getEventPrefix(String? code) {
if(code == "cancelled") return "Entfällt: ";
if(code == "irregular") return "Änderung: ";
return code ?? "";
if(code == 'cancelled') return 'Entfällt: ';
if(code == 'irregular') return 'Änderung: ';
return code ?? '';
}
static void show(BuildContext context, TimetableProps webuntisData, Appointment appointment) {
@ -65,13 +65,13 @@ class AppointmentDetails {
try {
subject = webuntisData.getSubjectsResponse.result.firstWhere((subject) => subject.id == timetableData.su[0].id);
} catch(e) {
subject = GetSubjectsResponseObject(0, "?", "Unbekannt", "?", true);
subject = GetSubjectsResponseObject(0, '?', 'Unbekannt', '?', true);
}
try {
room = webuntisData.getRoomsResponse.result.firstWhere((room) => room.id == timetableData.ro[0].id);
} catch(e) {
room = GetRoomsResponseObject(0, "?", "Unbekannt", true, "?");
room = GetRoomsResponseObject(0, '?', 'Unbekannt', true, '?');
}
_bottomSheet(
@ -99,7 +99,7 @@ class AppointmentDetails {
),
ListTile(
leading: const Icon(Icons.room),
title: Text("Raum: ${room.name} (${room.longName})"),
title: Text('Raum: ${room.name} (${room.longName})'),
trailing: IconButton(
icon: const Icon(Icons.house_outlined),
onPressed: () {
@ -111,7 +111,7 @@ class AppointmentDetails {
leading: const Icon(Icons.person),
title: timetableData.te.isNotEmpty
? Text("Lehrkraft: ${timetableData.te[0].name} ${timetableData.te[0].longname.isNotEmpty ? "(${timetableData.te[0].longname})" : ""}")
: const Text("?"),
: const Text('?'),
trailing: Visibility(
visible: !kReleaseMode,
child: IconButton(
@ -124,7 +124,7 @@ class AppointmentDetails {
),
ListTile(
leading: const Icon(Icons.abc),
title: Text("Typ: ${timetableData.activityType}"),
title: Text('Typ: ${timetableData.activityType}'),
),
ListTile(
leading: const Icon(Icons.people),
@ -140,9 +140,9 @@ class AppointmentDetails {
static Completer deleteCustomEvent(BuildContext context, CustomTimetableEvent appointment) {
Completer future = Completer();
ConfirmDialog(
title: "Termin löschen",
title: 'Termin löschen',
content: "Der ${appointment.rrule.isEmpty ? "Termin" : "Serientermin"} wird unwiederruflich gelöscht.",
confirmButton: "Löschen",
confirmButton: 'Löschen',
onConfirm: () {
RemoveCustomTimetableEvent(
RemoveCustomTimetableEventParams(
@ -186,14 +186,14 @@ class AppointmentDetails {
builder: (context) => CustomTimetableEventEditDialog(existingEvent: appointment),
);
},
label: const Text("Bearbeiten"),
label: const Text('Bearbeiten'),
icon: const Icon(Icons.edit_outlined),
),
TextButton.icon(
onPressed: () {
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),
),
],
@ -202,7 +202,7 @@ class AppointmentDetails {
const Divider(),
ListTile(
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(
leading: const CenteredLeading(Icon(Icons.repeat_outlined)),
@ -210,10 +210,10 @@ class AppointmentDetails {
subtitle: FutureBuilder(
future: RruleL10nEn.create(),
builder: (context, snapshot) {
if(appointment.rrule.isEmpty) return const Text("Keine weiteren vorkomnisse");
if(snapshot.data == null) return const Text("...");
if(appointment.rrule.isEmpty) return const Text('Keine weiteren vorkomnisse');
if(snapshot.data == null) return const Text('...');
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!));
},
)
@ -221,8 +221,8 @@ class AppointmentDetails {
DebugTile(context).child(
ListTile(
leading: const CenteredLeading(Icon(Icons.rule)),
title: const Text("RRule"),
subtitle: Text(appointment.rrule.isEmpty ? "Keine" : appointment.rrule),
title: const Text('RRule'),
subtitle: Text(appointment.rrule.isEmpty ? 'Keine' : appointment.rrule),
)
),
DebugTile(context).jsonData(appointment.toJson()),

View File

@ -15,16 +15,16 @@ class TimetableColors {
static ColorModeDisplay getDisplayOptions(CustomTimetableColors color) {
switch(color) {
case CustomTimetableColors.green:
return ColorModeDisplay(color: Colors.green, displayName: "Grün");
return ColorModeDisplay(color: Colors.green, displayName: 'Grün');
case CustomTimetableColors.blue:
return ColorModeDisplay(color: Colors.blue, displayName: "Blau");
return ColorModeDisplay(color: Colors.blue, displayName: 'Blau');
case CustomTimetableColors.orange:
return ColorModeDisplay(color: Colors.orange.shade800, displayName: "Orange");
return ColorModeDisplay(color: Colors.orange.shade800, displayName: 'Orange');
case CustomTimetableColors.red:
return ColorModeDisplay(color: DarkAppTheme.marianumRed, displayName: "Rot");
return ColorModeDisplay(color: DarkAppTheme.marianumRed, displayName: 'Rot');
}
}

View File

@ -3,7 +3,7 @@ import 'dart:developer';
import 'package:flutter/material.dart';
import 'package:jiffy/jiffy.dart';
import 'package:marianum_mobile/extensions/dateTime.dart';
import '../../../extensions/dateTime.dart';
import 'package:provider/provider.dart';
import 'package:rrule_generator/rrule_generator.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 final TextEditingController _eventName = TextEditingController(text: widget.existingEvent?.title);
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(
(element) => element.name == widget.existingEvent?.color,
orElse: () => TimetableColors.defaultColor
@ -64,7 +64,7 @@ class _AddCustomTimetableEventDialogState extends State<CustomTimetableEventEdit
controller: _eventName,
autofocus: true,
decoration: const InputDecoration(
labelText: "Terminname",
labelText: 'Terminname',
border: OutlineInputBorder()
),
),
@ -75,7 +75,7 @@ class _AddCustomTimetableEventDialogState extends State<CustomTimetableEventEdit
maxLines: 2,
minLines: 2,
decoration: const InputDecoration(
labelText: "Beschreibung",
labelText: 'Beschreibung',
border: OutlineInputBorder()
),
),
@ -84,7 +84,7 @@ class _AddCustomTimetableEventDialogState extends State<CustomTimetableEventEdit
ListTile(
leading: const Icon(Icons.date_range_outlined),
title: Text(Jiffy.parseFromDateTime(_date).yMMMd),
subtitle: const Text("Datum"),
subtitle: const Text('Datum'),
onTap: () async {
final DateTime? pickedDate = await showDatePicker(
context: context,
@ -101,8 +101,8 @@ class _AddCustomTimetableEventDialogState extends State<CustomTimetableEventEdit
),
ListTile(
leading: const Icon(Icons.access_time_outlined),
title: Text("${_startTime.format(context).toString()} - ${_endTime.format(context).toString()}"),
subtitle: const Text("Zeitraum"),
title: Text('${_startTime.format(context).toString()} - ${_endTime.format(context).toString()}'),
subtitle: const Text('Zeitraum'),
onTap: () async {
TimeRange timeRange = await showTimeRangePicker(
context: context,
@ -112,8 +112,8 @@ class _AddCustomTimetableEventDialogState extends State<CustomTimetableEventEdit
disabledColor: Colors.grey,
paintingStyle: PaintingStyle.fill,
interval: const Duration(minutes: 5),
fromText: "Beginnend",
toText: "Endend",
fromText: 'Beginnend',
toText: 'Endend',
strokeColor: Theme.of(context).colorScheme.secondary,
minDuration: const Duration(minutes: 15),
selectedColor: Theme.of(context).primaryColor,
@ -129,7 +129,7 @@ class _AddCustomTimetableEventDialogState extends State<CustomTimetableEventEdit
const Divider(),
ListTile(
leading: const Icon(Icons.color_lens_outlined),
title: const Text("Farbgebung"),
title: const Text('Farbgebung'),
trailing: DropdownButton<CustomTimetableColors>(
value: _customTimetableColor,
icon: const Icon(Icons.arrow_drop_down),
@ -162,7 +162,7 @@ class _AddCustomTimetableEventDialogState extends State<CustomTimetableEventEdit
initialRRule: _recurringRule,
textDelegate: const GermanRRuleTextDelegate(),
onChange: (String newValue) {
log("Rule: $newValue");
log('Rule: $newValue');
setState(() {
_recurringRule = newValue;
});
@ -183,7 +183,7 @@ class _AddCustomTimetableEventDialogState extends State<CustomTimetableEventEdit
if(!validate()) return;
CustomTimetableEvent editedEvent = CustomTimetableEvent(
id: "",
id: '',
title: _eventName.text,
description: _eventDescription.text,
startDate: _date.withTime(_startTime),
@ -210,7 +210,7 @@ class _AddCustomTimetableEventDialogState extends State<CustomTimetableEventEdit
} else {
UpdateCustomTimetableEvent(
UpdateCustomTimetableEventParams(
widget.existingEvent?.id ?? "",
widget.existingEvent?.id ?? '',
editedEvent
)
).run().then((value) {
@ -224,7 +224,7 @@ class _AddCustomTimetableEventDialogState extends State<CustomTimetableEventEdit
},
child: Text(isEditingExisting ? "Speichern" : "Erstellen"),
child: Text(isEditingExisting ? 'Speichern' : 'Erstellen'),
),
],
);

View File

@ -33,12 +33,12 @@ class _TimeRegionComponentState extends State<TimeRegionComponent> {
children: [
const SizedBox(height: 15),
const Icon(Icons.cake),
const Text("FREI"),
const Text('FREI'),
const SizedBox(height: 10),
RotatedBox(
quarterTurns: 1,
child: Text(
text.split(":").last,
text.split(':').last,
maxLines: 1,
style: const TextStyle(
fontWeight: FontWeight.bold,

View File

@ -2,7 +2,7 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:marianum_mobile/extensions/dateTime.dart';
import '../../../extensions/dateTime.dart';
import 'package:provider/provider.dart';
import 'package:syncfusion_flutter_calendar/calendar.dart';
@ -57,7 +57,7 @@ class _TimetableState extends State<Timetable> {
return Scaffold(
appBar: AppBar(
title: const Text("Stunden & Vertretungsplan"),
title: const Text('Stunden & Vertretungsplan'),
actions: [
IconButton(
icon: const Icon(Icons.home_outlined),
@ -74,11 +74,11 @@ class _TimetableState extends State<Timetable> {
Icon icon;
switch(e) {
case CalendarActions.addEvent:
title = "Kalendereintrag hinzufügen";
title = 'Kalendereintrag hinzufügen';
icon = const Icon(Icons.add);
case CalendarActions.viewEvents:
default:
title = "Kalendereinträge anzeigen";
title = 'Kalendereinträge anzeigen';
icon = const Icon(Icons.perm_contact_calendar_outlined);
}
return PopupMenuItem<CalendarActions>(
@ -112,9 +112,9 @@ class _TimetableState extends State<Timetable> {
if(value.hasError) {
return PlaceholderView(
icon: Icons.calendar_month,
text: "Webuntis error: ${value.error.toString()}",
text: 'Webuntis error: ${value.error.toString()}',
button: TextButton(
child: const Text("Neu laden"),
child: const Text('Neu laden'),
onPressed: () {
controller.displayDate = DateTime.now().add(const Duration(days: 2));
Provider.of<TimetableProps>(context, listen: false).resetWeek();
@ -155,8 +155,8 @@ class _TimetableState extends State<Timetable> {
startHour: 07.5,
endHour: 16.5,
timeInterval: Duration(minutes: 30),
timeFormat: "HH:mm",
dayFormat: "EE",
timeFormat: 'HH:mm',
dayFormat: 'EE',
timeIntervalHeight: 40,
),
@ -259,10 +259,10 @@ class _TimetableState extends State<Timetable> {
startTime: startTime,
endTime: endTime,
subject: subjects.result.firstWhere((subject) => subject.id == element.su[0].id).name,
location: ""
"${rooms.result.firstWhere((room) => room.id == element.ro[0].id).name}"
"\n"
"${element.te.first.longname}",
location: ''
'${rooms.result.firstWhere((room) => room.id == element.ro[0].id).name}'
'\n'
'${element.te.first.longname}',
notes: element.activityType,
color: _getEventColor(element, startTime, endTime),
);
@ -272,7 +272,7 @@ class _TimetableState extends State<Timetable> {
id: ArbitraryAppointment(webuntis: element),
startTime: _parseWebuntisTimestamp(element.date, element.startTime),
endTime: endTime,
subject: "Änderung",
subject: 'Änderung',
notes: element.info,
location: 'Unbekannt',
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;
// 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
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
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) {
ArbitraryAppointment appointment = calendarEntry.appointments.first.id as ArbitraryAppointment;
if(appointment.hasWebuntis()) {
return appointment.webuntis!.code == "cancelled";
return appointment.webuntis!.code == 'cancelled';
}
return false;
}

Some files were not shown because too many files have changed in this diff Show More