Merge branch 'develop' into feature-highEduGraduationCalculator
# Conflicts: # lib/view/pages/overhang.dart
This commit is contained in:
commit
5b34afd6cb
1
.gitignore
vendored
1
.gitignore
vendored
@ -349,3 +349,4 @@ hs_err_pid*
|
||||
# End of https://www.toptal.com/developers/gitignore/api/flutter,intellij,androidstudio,xcode,dart
|
||||
|
||||
*.idea*
|
||||
**/.DS_store
|
||||
|
@ -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
|
||||
|
@ -5,6 +5,6 @@ class ApiError {
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return "ApiError: $message";
|
||||
return 'ApiError: $message';
|
||||
}
|
||||
}
|
@ -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
|
||||
)
|
||||
)
|
||||
);
|
||||
|
@ -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)
|
||||
)
|
||||
)
|
||||
|
@ -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']);
|
||||
}
|
||||
|
@ -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}');
|
||||
}
|
||||
}
|
||||
}
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
}
|
@ -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;
|
||||
|
@ -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) {
|
||||
|
@ -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) {
|
||||
|
@ -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) {
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -28,6 +28,6 @@ class GetReactionsResponseObject {
|
||||
}
|
||||
|
||||
enum GetReactionsResponseObjectActorType {
|
||||
@JsonValue("guests") guests,
|
||||
@JsonValue("users") users,
|
||||
@JsonValue('guests') guests,
|
||||
@JsonValue('users') users,
|
||||
}
|
@ -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) {
|
||||
|
@ -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) {
|
||||
|
@ -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());
|
||||
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
}
|
@ -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) {
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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');
|
||||
}
|
||||
|
||||
}
|
@ -7,6 +7,6 @@ class TalkError {
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return "Talk - $status - ($code): $message";
|
||||
return 'Talk - $status - ($code): $message';
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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()}/';
|
||||
}
|
||||
}
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
}
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
||||
}
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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"}");
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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');
|
||||
}
|
||||
|
||||
}
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
@ -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)
|
||||
);
|
||||
}
|
||||
}
|
@ -6,6 +6,6 @@ class WebuntisError implements Exception {
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return "WebUntis ($code): $message";
|
||||
return 'WebUntis ($code): $message';
|
||||
}
|
||||
}
|
16
lib/app.dart
16
lib/app.dart
@ -39,14 +39,14 @@ class _AppState extends State<App> with WidgetsBindingObserver {
|
||||
|
||||
@override
|
||||
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'
|
||||
),
|
||||
),
|
||||
],
|
||||
|
@ -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');
|
||||
}
|
||||
},
|
||||
)
|
||||
|
@ -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';
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
@ -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(),
|
||||
|
@ -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)),
|
||||
],
|
||||
),
|
||||
|
@ -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(
|
||||
|
@ -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!');
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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');
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
);
|
||||
|
@ -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);
|
||||
|
@ -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');
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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'),
|
||||
)
|
||||
],
|
||||
);
|
||||
|
@ -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(() {
|
||||
|
@ -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,
|
||||
|
@ -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)));
|
||||
|
@ -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),
|
||||
),
|
||||
);
|
||||
|
@ -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),
|
||||
|
@ -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'),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
@ -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ß!'
|
||||
);
|
||||
},
|
||||
)
|
||||
|
@ -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()),
|
||||
),
|
||||
|
@ -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!)),
|
||||
),
|
||||
|
@ -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) {
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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',
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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();
|
||||
|
@ -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)),
|
||||
],
|
||||
),
|
||||
)
|
||||
|
@ -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());
|
||||
|
@ -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(
|
||||
|
@ -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)),
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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()),
|
||||
|
@ -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');
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -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'),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
@ -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,
|
||||
|
@ -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
Loading…
x
Reference in New Issue
Block a user