Merge branch 'develop' into develop-connectedDoubleLessons
This commit is contained in:
commit
20d7b16ede
1
.gitignore
vendored
1
.gitignore
vendored
@ -349,3 +349,4 @@ hs_err_pid*
|
|||||||
# End of https://www.toptal.com/developers/gitignore/api/flutter,intellij,androidstudio,xcode,dart
|
# End of https://www.toptal.com/developers/gitignore/api/flutter,intellij,androidstudio,xcode,dart
|
||||||
|
|
||||||
*.idea*
|
*.idea*
|
||||||
|
**/.DS_store
|
||||||
|
@ -24,6 +24,10 @@ linter:
|
|||||||
rules:
|
rules:
|
||||||
# avoid_print: false # Uncomment to disable the `avoid_print` rule
|
# avoid_print: false # Uncomment to disable the `avoid_print` rule
|
||||||
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
|
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
|
||||||
|
file_names: false
|
||||||
|
prefer_relative_imports: true
|
||||||
|
unnecessary_lambdas: true
|
||||||
|
prefer_single_quotes: true
|
||||||
|
|
||||||
# Additional information about this file can be found at
|
# Additional information about this file can be found at
|
||||||
# https://dart.dev/guides/language/analysis-options
|
# https://dart.dev/guides/language/analysis-options
|
||||||
|
@ -5,6 +5,6 @@ class ApiError {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return "ApiError: $message";
|
return 'ApiError: $message';
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -6,11 +6,11 @@ import 'getHolidaysResponse.dart';
|
|||||||
|
|
||||||
class GetHolidays {
|
class GetHolidays {
|
||||||
Future<GetHolidaysResponse> query() async {
|
Future<GetHolidaysResponse> query() async {
|
||||||
String response = (await http.get(Uri.parse("https://ferien-api.de/api/v1/holidays/HE"))).body;
|
String response = (await http.get(Uri.parse('https://ferien-api.de/api/v1/holidays/HE'))).body;
|
||||||
return GetHolidaysResponse(
|
return GetHolidaysResponse(
|
||||||
List<GetHolidaysResponseObject>.from(
|
List<GetHolidaysResponseObject>.from(
|
||||||
jsonDecode(response).map<GetHolidaysResponseObject>(
|
jsonDecode(response).map<GetHolidaysResponseObject>(
|
||||||
(dynamic i) => GetHolidaysResponseObject.fromJson(i)
|
GetHolidaysResponseObject.fromJson
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
@ -6,7 +6,7 @@ import 'getHolidaysResponse.dart';
|
|||||||
|
|
||||||
class GetHolidaysCache extends RequestCache<GetHolidaysResponse> {
|
class GetHolidaysCache extends RequestCache<GetHolidaysResponse> {
|
||||||
GetHolidaysCache({onUpdate, renew}) : super(RequestCache.cacheDay, onUpdate, renew: renew) {
|
GetHolidaysCache({onUpdate, renew}) : super(RequestCache.cacheDay, onUpdate, renew: renew) {
|
||||||
start("MarianumMobile", "state-holidays");
|
start('MarianumMobile', 'state-holidays');
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -15,6 +15,7 @@ class GetHolidaysCache extends RequestCache<GetHolidaysResponse> {
|
|||||||
return GetHolidaysResponse(
|
return GetHolidaysResponse(
|
||||||
List<GetHolidaysResponseObject>.from(
|
List<GetHolidaysResponseObject>.from(
|
||||||
parsedListJson.map<GetHolidaysResponseObject>(
|
parsedListJson.map<GetHolidaysResponseObject>(
|
||||||
|
// ignore: unnecessary_lambdas
|
||||||
(dynamic i) => GetHolidaysResponseObject.fromJson(i)
|
(dynamic i) => GetHolidaysResponseObject.fromJson(i)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -11,21 +11,21 @@ import 'autocompleteResponse.dart';
|
|||||||
class AutocompleteApi {
|
class AutocompleteApi {
|
||||||
Future<AutocompleteResponse> find(String query) async {
|
Future<AutocompleteResponse> find(String query) async {
|
||||||
Map<String, dynamic> getParameters = {
|
Map<String, dynamic> getParameters = {
|
||||||
"search": query,
|
'search': query,
|
||||||
"itemType": " ",
|
'itemType': ' ',
|
||||||
"itemId": " ",
|
'itemId': ' ',
|
||||||
"shareTypes[]": ["0"],
|
'shareTypes[]': ['0'],
|
||||||
"limit": "10",
|
'limit': '10',
|
||||||
};
|
};
|
||||||
|
|
||||||
Map<String, String> headers = {};
|
Map<String, String> headers = {};
|
||||||
headers.putIfAbsent("Accept", () => "application/json");
|
headers.putIfAbsent('Accept', () => 'application/json');
|
||||||
headers.putIfAbsent("OCS-APIRequest", () => "true");
|
headers.putIfAbsent('OCS-APIRequest', () => 'true');
|
||||||
|
|
||||||
Uri endpoint = Uri.https("${AccountData().buildHttpAuthString()}@${EndpointData().nextcloud().domain}", "${EndpointData().nextcloud().path}/ocs/v2.php/core/autocomplete/get", getParameters);
|
Uri endpoint = Uri.https('${AccountData().buildHttpAuthString()}@${EndpointData().nextcloud().domain}', '${EndpointData().nextcloud().path}/ocs/v2.php/core/autocomplete/get', getParameters);
|
||||||
|
|
||||||
Response response = await http.get(endpoint, headers: headers);
|
Response response = await http.get(endpoint, headers: headers);
|
||||||
if(response.statusCode != HttpStatus.ok) throw Exception("Api call failed with ${response.statusCode}: ${response.body}");
|
if(response.statusCode != HttpStatus.ok) throw Exception('Api call failed with ${response.statusCode}: ${response.body}');
|
||||||
String result = response.body;
|
String result = response.body;
|
||||||
return AutocompleteResponse.fromJson(jsonDecode(result)['ocs']);
|
return AutocompleteResponse.fromJson(jsonDecode(result)['ocs']);
|
||||||
}
|
}
|
||||||
|
@ -10,14 +10,14 @@ import 'fileSharingApiParams.dart';
|
|||||||
class FileSharingApi {
|
class FileSharingApi {
|
||||||
Future<void> share(FileSharingApiParams query) async {
|
Future<void> share(FileSharingApiParams query) async {
|
||||||
Map<String, String> headers = {};
|
Map<String, String> headers = {};
|
||||||
headers.putIfAbsent("Accept", () => "application/json");
|
headers.putIfAbsent('Accept', () => 'application/json');
|
||||||
headers.putIfAbsent("OCS-APIRequest", () => "true");
|
headers.putIfAbsent('OCS-APIRequest', () => 'true');
|
||||||
|
|
||||||
Uri endpoint = Uri.https("${AccountData().buildHttpAuthString()}@${EndpointData().nextcloud().domain}", "${EndpointData().nextcloud().path}/ocs/v2.php/apps/files_sharing/api/v1/shares", query.toJson().map((key, value) => MapEntry(key, value.toString())));
|
Uri endpoint = Uri.https('${AccountData().buildHttpAuthString()}@${EndpointData().nextcloud().domain}', '${EndpointData().nextcloud().path}/ocs/v2.php/apps/files_sharing/api/v1/shares', query.toJson().map((key, value) => MapEntry(key, value.toString())));
|
||||||
Response response = await http.post(endpoint, headers: headers);
|
Response response = await http.post(endpoint, headers: headers);
|
||||||
|
|
||||||
if(response.statusCode != HttpStatus.ok) {
|
if(response.statusCode != HttpStatus.ok) {
|
||||||
throw Exception("Api call failed with ${response.statusCode}: ${response.body}");
|
throw Exception('Api call failed with ${response.statusCode}: ${response.body}');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -11,7 +11,7 @@ class GetChat extends TalkApi<GetChatResponse> {
|
|||||||
String chatToken;
|
String chatToken;
|
||||||
|
|
||||||
GetChatParams params;
|
GetChatParams params;
|
||||||
GetChat(this.chatToken, this.params) : super("v1/chat/$chatToken", null, getParameters: params.toJson());
|
GetChat(this.chatToken, this.params) : super('v1/chat/$chatToken', null, getParameters: params.toJson());
|
||||||
|
|
||||||
@override
|
@override
|
||||||
assemble(String raw) {
|
assemble(String raw) {
|
||||||
|
@ -9,7 +9,7 @@ class GetChatCache extends RequestCache<GetChatResponse> {
|
|||||||
String chatToken;
|
String chatToken;
|
||||||
|
|
||||||
GetChatCache({required onUpdate, required this.chatToken}) : super(RequestCache.cacheNothing, onUpdate) {
|
GetChatCache({required onUpdate, required this.chatToken}) : super(RequestCache.cacheNothing, onUpdate) {
|
||||||
start("MarianumMobile", "nc-chat-$chatToken");
|
start('MarianumMobile', 'nc-chat-$chatToken');
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -61,21 +61,21 @@ class GetChatResponseObject {
|
|||||||
|
|
||||||
static GetChatResponseObject getDateDummy(int timestamp) {
|
static GetChatResponseObject getDateDummy(int timestamp) {
|
||||||
DateTime elementDate = DateTime.fromMillisecondsSinceEpoch(timestamp * 1000);
|
DateTime elementDate = DateTime.fromMillisecondsSinceEpoch(timestamp * 1000);
|
||||||
return getTextDummy(Jiffy.parseFromDateTime(elementDate).format(pattern: "dd.MM.yyyy"));
|
return getTextDummy(Jiffy.parseFromDateTime(elementDate).format(pattern: 'dd.MM.yyyy'));
|
||||||
}
|
}
|
||||||
|
|
||||||
static GetChatResponseObject getTextDummy(String text) {
|
static GetChatResponseObject getTextDummy(String text) {
|
||||||
return GetChatResponseObject(
|
return GetChatResponseObject(
|
||||||
0,
|
0,
|
||||||
"",
|
'',
|
||||||
GetRoomResponseObjectMessageActorType.user,
|
GetRoomResponseObjectMessageActorType.user,
|
||||||
"",
|
'',
|
||||||
"",
|
'',
|
||||||
0,
|
0,
|
||||||
"",
|
'',
|
||||||
GetRoomResponseObjectMessageType.system,
|
GetRoomResponseObjectMessageType.system,
|
||||||
false,
|
false,
|
||||||
"",
|
'',
|
||||||
text,
|
text,
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
@ -113,10 +113,10 @@ class RichObjectString {
|
|||||||
}
|
}
|
||||||
|
|
||||||
enum RichObjectStringObjectType {
|
enum RichObjectStringObjectType {
|
||||||
@JsonValue("user") user,
|
@JsonValue('user') user,
|
||||||
@JsonValue("group") group,
|
@JsonValue('group') group,
|
||||||
@JsonValue("file") file,
|
@JsonValue('file') file,
|
||||||
@JsonValue("guest") guest,
|
@JsonValue('guest') guest,
|
||||||
@JsonValue("highlight") highlight,
|
@JsonValue('highlight') highlight,
|
||||||
@JsonValue("talk-poll") talkPoll,
|
@JsonValue('talk-poll') talkPoll,
|
||||||
}
|
}
|
@ -6,7 +6,7 @@ class RichObjectStringProcessor {
|
|||||||
if(data == null) return message;
|
if(data == null) return message;
|
||||||
|
|
||||||
data.forEach((key, value) {
|
data.forEach((key, value) {
|
||||||
message = message.replaceAll(RegExp("{$key}"), value.name);
|
message = message.replaceAll(RegExp('{$key}'), value.name);
|
||||||
});
|
});
|
||||||
|
|
||||||
return message;
|
return message;
|
||||||
|
@ -7,7 +7,7 @@ import 'createRoomParams.dart';
|
|||||||
class CreateRoom extends TalkApi {
|
class CreateRoom extends TalkApi {
|
||||||
CreateRoomParams params;
|
CreateRoomParams params;
|
||||||
|
|
||||||
CreateRoom(this.params) : super("v4/room", params);
|
CreateRoom(this.params) : super('v4/room', params);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
assemble(String raw) {
|
assemble(String raw) {
|
||||||
|
@ -7,7 +7,7 @@ import '../talkApi.dart';
|
|||||||
class DeleteMessage extends TalkApi {
|
class DeleteMessage extends TalkApi {
|
||||||
String chatToken;
|
String chatToken;
|
||||||
int messageId;
|
int messageId;
|
||||||
DeleteMessage(this.chatToken, this.messageId) : super("v1/chat/$chatToken/$messageId", null);
|
DeleteMessage(this.chatToken, this.messageId) : super('v1/chat/$chatToken/$messageId', null);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
assemble(String raw) {
|
assemble(String raw) {
|
||||||
|
@ -8,7 +8,7 @@ import 'deleteReactMessageParams.dart';
|
|||||||
class DeleteReactMessage extends TalkApi {
|
class DeleteReactMessage extends TalkApi {
|
||||||
String chatToken;
|
String chatToken;
|
||||||
int messageId;
|
int messageId;
|
||||||
DeleteReactMessage({required this.chatToken, required this.messageId, required DeleteReactMessageParams params}) : super("v1/reaction/$chatToken/$messageId", params);
|
DeleteReactMessage({required this.chatToken, required this.messageId, required DeleteReactMessageParams params}) : super('v1/reaction/$chatToken/$messageId', params);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
assemble(String raw) {
|
assemble(String raw) {
|
||||||
|
@ -7,7 +7,7 @@ import 'getParticipantsResponse.dart';
|
|||||||
|
|
||||||
class GetParticipants extends TalkApi<GetParticipantsResponse> {
|
class GetParticipants extends TalkApi<GetParticipantsResponse> {
|
||||||
String token;
|
String token;
|
||||||
GetParticipants(this.token) : super("v4/room/$token/participants", null);
|
GetParticipants(this.token) : super('v4/room/$token/participants', null);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
GetParticipantsResponse assemble(String raw) {
|
GetParticipantsResponse assemble(String raw) {
|
||||||
|
@ -8,7 +8,7 @@ class GetParticipantsCache extends RequestCache<GetParticipantsResponse> {
|
|||||||
String chatToken;
|
String chatToken;
|
||||||
|
|
||||||
GetParticipantsCache({required onUpdate, required this.chatToken}) : super(RequestCache.cacheNothing, onUpdate) {
|
GetParticipantsCache({required onUpdate, required this.chatToken}) : super(RequestCache.cacheNothing, onUpdate) {
|
||||||
start("MarianumMobile", "nc-chat-participants-$chatToken");
|
start('MarianumMobile', 'nc-chat-participants-$chatToken');
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -10,7 +10,7 @@ import 'getReactionsResponse.dart';
|
|||||||
class GetReactions extends TalkApi<GetReactionsResponse> {
|
class GetReactions extends TalkApi<GetReactionsResponse> {
|
||||||
String chatToken;
|
String chatToken;
|
||||||
int messageId;
|
int messageId;
|
||||||
GetReactions({required this.chatToken, required this.messageId}) : super("v1/reaction/$chatToken/$messageId", null);
|
GetReactions({required this.chatToken, required this.messageId}) : super('v1/reaction/$chatToken/$messageId', null);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
assemble(String raw) {
|
assemble(String raw) {
|
||||||
|
@ -28,6 +28,6 @@ class GetReactionsResponseObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
enum GetReactionsResponseObjectActorType {
|
enum GetReactionsResponseObjectActorType {
|
||||||
@JsonValue("guests") guests,
|
@JsonValue('guests') guests,
|
||||||
@JsonValue("users") users,
|
@JsonValue('users') users,
|
||||||
}
|
}
|
@ -6,7 +6,7 @@ import '../talkApi.dart';
|
|||||||
class LeaveRoom extends TalkApi {
|
class LeaveRoom extends TalkApi {
|
||||||
String chatToken;
|
String chatToken;
|
||||||
|
|
||||||
LeaveRoom(this.chatToken) : super("v4/room/$chatToken/participants/self", null);
|
LeaveRoom(this.chatToken) : super('v4/room/$chatToken/participants/self', null);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
assemble(String raw) {
|
assemble(String raw) {
|
||||||
|
@ -8,7 +8,7 @@ import 'reactMessageParams.dart';
|
|||||||
class ReactMessage extends TalkApi {
|
class ReactMessage extends TalkApi {
|
||||||
String chatToken;
|
String chatToken;
|
||||||
int messageId;
|
int messageId;
|
||||||
ReactMessage({required this.chatToken, required this.messageId, required ReactMessageParams params}) : super("v1/reaction/$chatToken/$messageId", params);
|
ReactMessage({required this.chatToken, required this.messageId, required ReactMessageParams params}) : super('v1/reaction/$chatToken/$messageId', params);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
assemble(String raw) {
|
assemble(String raw) {
|
||||||
|
@ -9,7 +9,7 @@ import 'getRoomResponse.dart';
|
|||||||
|
|
||||||
class GetRoom extends TalkApi<GetRoomResponse> {
|
class GetRoom extends TalkApi<GetRoomResponse> {
|
||||||
GetRoomParams params;
|
GetRoomParams params;
|
||||||
GetRoom(this.params) : super("v4/room", null, getParameters: params.toJson());
|
GetRoom(this.params) : super('v4/room', null, getParameters: params.toJson());
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ import 'getRoomResponse.dart';
|
|||||||
|
|
||||||
class GetRoomCache extends RequestCache<GetRoomResponse> {
|
class GetRoomCache extends RequestCache<GetRoomResponse> {
|
||||||
GetRoomCache({onUpdate, renew}) : super(RequestCache.cacheMinute, onUpdate, renew: renew) {
|
GetRoomCache({onUpdate, renew}) : super(RequestCache.cacheMinute, onUpdate, renew: renew) {
|
||||||
start("MarianumMobile", "nc-rooms");
|
start('MarianumMobile', 'nc-rooms');
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -19,10 +19,10 @@ class GetRoomResponse extends ApiResponse {
|
|||||||
final buffer = StringBuffer();
|
final buffer = StringBuffer();
|
||||||
|
|
||||||
if(favoritesToTop) {
|
if(favoritesToTop) {
|
||||||
buffer.write(chat.isFavorite ? "b" : "a");
|
buffer.write(chat.isFavorite ? 'b' : 'a');
|
||||||
}
|
}
|
||||||
if(unreadToTop) {
|
if(unreadToTop) {
|
||||||
buffer.write(chat.unreadMessages > 0 ? "b" : "a");
|
buffer.write(chat.unreadMessages > 0 ? 'b' : 'a');
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer.write(chat.lastActivity);
|
buffer.write(chat.lastActivity);
|
||||||
@ -152,16 +152,16 @@ enum GetRoomResponseObjectParticipantNotificationLevel {
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
enum GetRoomResponseObjectMessageActorType {
|
enum GetRoomResponseObjectMessageActorType {
|
||||||
@JsonValue("deleted_users") deletedUsers,
|
@JsonValue('deleted_users') deletedUsers,
|
||||||
@JsonValue("users") user,
|
@JsonValue('users') user,
|
||||||
@JsonValue("guests") guest,
|
@JsonValue('guests') guest,
|
||||||
@JsonValue("bots") bot,
|
@JsonValue('bots') bot,
|
||||||
@JsonValue("bridged") bridge,
|
@JsonValue('bridged') bridge,
|
||||||
}
|
}
|
||||||
|
|
||||||
enum GetRoomResponseObjectMessageType {
|
enum GetRoomResponseObjectMessageType {
|
||||||
@JsonValue("comment") comment,
|
@JsonValue('comment') comment,
|
||||||
@JsonValue("comment_deleted") deletedComment,
|
@JsonValue('comment_deleted') deletedComment,
|
||||||
@JsonValue("system") system,
|
@JsonValue('system') system,
|
||||||
@JsonValue("command") command,
|
@JsonValue('command') command,
|
||||||
}
|
}
|
@ -7,7 +7,7 @@ import 'sendMessageParams.dart';
|
|||||||
|
|
||||||
class SendMessage extends TalkApi {
|
class SendMessage extends TalkApi {
|
||||||
String chatToken;
|
String chatToken;
|
||||||
SendMessage(this.chatToken, SendMessageParams params) : super("v1/chat/$chatToken", params);
|
SendMessage(this.chatToken, SendMessageParams params) : super('v1/chat/$chatToken', params);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
assemble(String raw) {
|
assemble(String raw) {
|
||||||
|
@ -8,7 +8,7 @@ class SetFavorite extends TalkApi {
|
|||||||
String chatToken;
|
String chatToken;
|
||||||
bool favoriteState;
|
bool favoriteState;
|
||||||
|
|
||||||
SetFavorite(this.chatToken, this.favoriteState) : super("v4/room/$chatToken/favorite", null);
|
SetFavorite(this.chatToken, this.favoriteState) : super('v4/room/$chatToken/favorite', null);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
assemble(String raw) {
|
assemble(String raw) {
|
||||||
|
@ -10,7 +10,7 @@ class SetReadMarker extends TalkApi {
|
|||||||
bool readState;
|
bool readState;
|
||||||
SetReadMarkerParams? setReadMarkerParams;
|
SetReadMarkerParams? setReadMarkerParams;
|
||||||
|
|
||||||
SetReadMarker(this.chatToken, this.readState, {this.setReadMarkerParams}) : super("v1/chat/$chatToken/read", null, getParameters: setReadMarkerParams?.toJson()) {
|
SetReadMarker(this.chatToken, this.readState, {this.setReadMarkerParams}) : super('v1/chat/$chatToken/read', null, getParameters: setReadMarkerParams?.toJson()) {
|
||||||
if(readState) assert(setReadMarkerParams?.lastReadMessage != null);
|
if(readState) assert(setReadMarkerParams?.lastReadMessage != null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,21 +34,21 @@ abstract class TalkApi<T extends ApiResponse?> extends ApiRequest {
|
|||||||
getParameters?.update(key, (value) => value.toString());
|
getParameters?.update(key, (value) => value.toString());
|
||||||
});
|
});
|
||||||
|
|
||||||
Uri endpoint = Uri.https("${AccountData().buildHttpAuthString()}@${EndpointData().nextcloud().domain}", "${EndpointData().nextcloud().path}/ocs/v2.php/apps/spreed/api/$path", getParameters);
|
Uri endpoint = Uri.https('${AccountData().buildHttpAuthString()}@${EndpointData().nextcloud().domain}', '${EndpointData().nextcloud().path}/ocs/v2.php/apps/spreed/api/$path', getParameters);
|
||||||
|
|
||||||
headers ??= {};
|
headers ??= {};
|
||||||
headers?.putIfAbsent("Accept", () => "application/json");
|
headers?.putIfAbsent('Accept', () => 'application/json');
|
||||||
headers?.putIfAbsent("OCS-APIRequest", () => "true");
|
headers?.putIfAbsent('OCS-APIRequest', () => 'true');
|
||||||
|
|
||||||
http.Response? data;
|
http.Response? data;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
data = await request(endpoint, body, headers);
|
data = await request(endpoint, body, headers);
|
||||||
if(data == null) throw Exception("No response Data");
|
if(data == null) throw Exception('No response Data');
|
||||||
if(data.statusCode >= 400 || data.statusCode < 200) throw Exception("Response status code '${data.statusCode}' might indicate an error");
|
if(data.statusCode >= 400 || data.statusCode < 200) throw Exception("Response status code '${data.statusCode}' might indicate an error");
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
log(e.toString());
|
log(e.toString());
|
||||||
throw ApiError("Request $endpoint could not be dispatched: ${e.toString()}");
|
throw ApiError('Request $endpoint could not be dispatched: ${e.toString()}');
|
||||||
}
|
}
|
||||||
//dynamic jsonData = jsonDecode(data.body);
|
//dynamic jsonData = jsonDecode(data.body);
|
||||||
|
|
||||||
@ -59,10 +59,10 @@ abstract class TalkApi<T extends ApiResponse?> extends ApiRequest {
|
|||||||
return assembled;
|
return assembled;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// TODO report error
|
// TODO report error
|
||||||
log("Error assembling Talk API ${T.toString()} message: ${e.toString()} response on ${endpoint.path} with request body: $body and request headers: ${headers.toString()}");
|
log('Error assembling Talk API ${T.toString()} message: ${e.toString()} response on ${endpoint.path} with request body: $body and request headers: ${headers.toString()}');
|
||||||
}
|
}
|
||||||
|
|
||||||
throw Exception("Error assembling Talk API response");
|
throw Exception('Error assembling Talk API response');
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -7,6 +7,6 @@ class TalkError {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return "Talk - $status - ($code): $message";
|
return 'Talk - $status - ($code): $message';
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -24,7 +24,7 @@ class CacheableFile {
|
|||||||
CacheableFile.fromDavFile(WebDavFile file) {
|
CacheableFile.fromDavFile(WebDavFile file) {
|
||||||
path = file.path.path;
|
path = file.path.path;
|
||||||
isDirectory = file.isDirectory;
|
isDirectory = file.isDirectory;
|
||||||
name = file.isDirectory ? file.name : file.path.path.split("/").last;
|
name = file.isDirectory ? file.name : file.path.path.split('/').last;
|
||||||
mimeType = file.mimeType;
|
mimeType = file.mimeType;
|
||||||
size = file.size;
|
size = file.size;
|
||||||
eTag = file.etag;
|
eTag = file.etag;
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
|
|
||||||
import 'package:nextcloud/nextcloud.dart';
|
import 'package:nextcloud/nextcloud.dart';
|
||||||
|
|
||||||
import '../../../../../model/endpointData.dart';
|
|
||||||
import '../../webdavApi.dart';
|
import '../../webdavApi.dart';
|
||||||
import 'cacheableFile.dart';
|
import 'cacheableFile.dart';
|
||||||
import 'listFilesParams.dart';
|
import 'listFilesParams.dart';
|
||||||
@ -15,18 +14,19 @@ class ListFiles extends WebdavApi<ListFilesParams> {
|
|||||||
@override
|
@override
|
||||||
Future<ListFilesResponse> run() async {
|
Future<ListFilesResponse> run() async {
|
||||||
List<WebDavFile> davFiles = (await (await WebdavApi.webdav).propfind(PathUri.parse(params.path))).toWebDavFiles();
|
List<WebDavFile> davFiles = (await (await WebdavApi.webdav).propfind(PathUri.parse(params.path))).toWebDavFiles();
|
||||||
Set<CacheableFile> files = davFiles.map((e) => CacheableFile.fromDavFile(e)).toSet();
|
Set<CacheableFile> files = davFiles.map(CacheableFile.fromDavFile).toSet();
|
||||||
|
|
||||||
// webdav handles subdirectories wrong, this is a fix
|
// webdav handles subdirectories wrong, this is a fix
|
||||||
if(EndpointData().getEndpointMode() == EndpointMode.stage) {
|
// currently this fix is not needed anymore
|
||||||
files = files.map((e) { // somehow
|
// if(EndpointData().getEndpointMode() == EndpointMode.stage) {
|
||||||
e.path = e.path.split("mobile/cloud/remote.php/webdav")[1];
|
// files = files.map((e) { // somehow
|
||||||
return e;
|
// e.path = e.path.split("mobile/cloud/remote.php/webdav")[1];
|
||||||
}).toSet();
|
// return e;
|
||||||
}
|
// }).toSet();
|
||||||
|
// }
|
||||||
|
|
||||||
// somehow the current working folder is also listed, it is filtered here.
|
// somehow the current working folder is also listed, it is filtered here.
|
||||||
files.removeWhere((element) => element.path == "/${params.path}/" || element.path == "/");
|
files.removeWhere((element) => element.path == '/${params.path}/' || element.path == '/');
|
||||||
|
|
||||||
return ListFilesResponse(files);
|
return ListFilesResponse(files);
|
||||||
}
|
}
|
||||||
|
@ -10,9 +10,9 @@ class ListFilesCache extends RequestCache<ListFilesResponse> {
|
|||||||
String path;
|
String path;
|
||||||
|
|
||||||
ListFilesCache({required onUpdate, required this.path}) : super(RequestCache.cacheNothing, onUpdate) {
|
ListFilesCache({required onUpdate, required this.path}) : super(RequestCache.cacheNothing, onUpdate) {
|
||||||
var bytes = utf8.encode("MarianumMobile-$path");
|
var bytes = utf8.encode('MarianumMobile-$path');
|
||||||
String cacheName = md5.convert(bytes).toString();
|
String cacheName = md5.convert(bytes).toString();
|
||||||
start("MarianumMobile", "wd-folder-$cacheName");
|
start('MarianumMobile', 'wd-folder-$cacheName');
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -35,7 +35,7 @@ class ListFilesResponse extends ApiResponse {
|
|||||||
|
|
||||||
switch(sortOption) {
|
switch(sortOption) {
|
||||||
case SortOption.date:
|
case SortOption.date:
|
||||||
buffer.write(Jiffy.parseFromMillisecondsSinceEpoch(file.modifiedAt?.millisecondsSinceEpoch ?? 0).format(pattern: "yyyyMMddhhmmss"));
|
buffer.write(Jiffy.parseFromMillisecondsSinceEpoch(file.modifiedAt?.millisecondsSinceEpoch ?? 0).format(pattern: 'yyyyMMddhhmmss'));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SortOption.name:
|
case SortOption.name:
|
||||||
|
@ -18,10 +18,10 @@ abstract class WebdavApi<T> extends ApiRequest {
|
|||||||
static Future<String> webdavConnectString = buildWebdavConnectString();
|
static Future<String> webdavConnectString = buildWebdavConnectString();
|
||||||
|
|
||||||
static Future<WebDavClient> establishWebdavConnection() async {
|
static Future<WebDavClient> establishWebdavConnection() async {
|
||||||
return NextcloudClient(Uri.parse("https://${EndpointData().nextcloud().full()}"), password: AccountData().getPassword(), loginName: AccountData().getUsername()).webdav;
|
return NextcloudClient(Uri.parse('https://${EndpointData().nextcloud().full()}'), password: AccountData().getPassword(), loginName: AccountData().getUsername()).webdav;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<String> buildWebdavConnectString() async {
|
static Future<String> buildWebdavConnectString() async {
|
||||||
return "https://${AccountData().buildHttpAuthString()}@${EndpointData().nextcloud().full()}/remote.php/dav/files/${AccountData().getUsername()}/";
|
return 'https://${AccountData().buildHttpAuthString()}@${EndpointData().nextcloud().full()}/remote.php/dav/files/${AccountData().getUsername()}/';
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -6,7 +6,7 @@ import '../../mhslApi.dart';
|
|||||||
import 'getBreakersResponse.dart';
|
import 'getBreakersResponse.dart';
|
||||||
|
|
||||||
class GetBreakers extends MhslApi<GetBreakersResponse> {
|
class GetBreakers extends MhslApi<GetBreakersResponse> {
|
||||||
GetBreakers() : super("breaker/");
|
GetBreakers() : super('breaker/');
|
||||||
|
|
||||||
@override
|
@override
|
||||||
GetBreakersResponse assemble(String raw) {
|
GetBreakersResponse assemble(String raw) {
|
||||||
|
@ -6,7 +6,7 @@ import 'getBreakersResponse.dart';
|
|||||||
|
|
||||||
class GetBreakersCache extends RequestCache<GetBreakersResponse> {
|
class GetBreakersCache extends RequestCache<GetBreakersResponse> {
|
||||||
GetBreakersCache({onUpdate, renew}) : super(RequestCache.cacheMinute, onUpdate, renew: renew) {
|
GetBreakersCache({onUpdate, renew}) : super(RequestCache.cacheMinute, onUpdate, renew: renew) {
|
||||||
start("MarianumMobile", "breakers");
|
start('MarianumMobile', 'breakers');
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -28,9 +28,9 @@ class GetBreakersReponseObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
enum BreakerArea {
|
enum BreakerArea {
|
||||||
@JsonValue("GLOBAL") global,
|
@JsonValue('GLOBAL') global,
|
||||||
@JsonValue("TIMETABLE") timetable,
|
@JsonValue('TIMETABLE') timetable,
|
||||||
@JsonValue("TALK") talk,
|
@JsonValue('TALK') talk,
|
||||||
@JsonValue("FILES") files,
|
@JsonValue('FILES') files,
|
||||||
@JsonValue("MORE") more,
|
@JsonValue('MORE') more,
|
||||||
}
|
}
|
@ -13,6 +13,7 @@ class CustomTimetableEvent {
|
|||||||
DateTime startDate;
|
DateTime startDate;
|
||||||
@JsonKey(toJson: MhslApi.dateTimeToJson, fromJson: MhslApi.dateTimeFromJson)
|
@JsonKey(toJson: MhslApi.dateTimeToJson, fromJson: MhslApi.dateTimeFromJson)
|
||||||
DateTime endDate;
|
DateTime endDate;
|
||||||
|
String? color;
|
||||||
String rrule;
|
String rrule;
|
||||||
@JsonKey(toJson: MhslApi.dateTimeToJson, fromJson: MhslApi.dateTimeFromJson)
|
@JsonKey(toJson: MhslApi.dateTimeToJson, fromJson: MhslApi.dateTimeFromJson)
|
||||||
DateTime createdAt;
|
DateTime createdAt;
|
||||||
@ -20,7 +21,7 @@ class CustomTimetableEvent {
|
|||||||
DateTime updatedAt;
|
DateTime updatedAt;
|
||||||
|
|
||||||
CustomTimetableEvent({required this.id, required this.title, required this.description, required this.startDate,
|
CustomTimetableEvent({required this.id, required this.title, required this.description, required this.startDate,
|
||||||
required this.endDate, required this.rrule, required this.createdAt, required this.updatedAt});
|
required this.endDate, required this.color, required this.rrule, required this.createdAt, required this.updatedAt});
|
||||||
|
|
||||||
factory CustomTimetableEvent.fromJson(Map<String, dynamic> json) => _$CustomTimetableEventFromJson(json);
|
factory CustomTimetableEvent.fromJson(Map<String, dynamic> json) => _$CustomTimetableEventFromJson(json);
|
||||||
Map<String, dynamic> toJson() => _$CustomTimetableEventToJson(this);
|
Map<String, dynamic> toJson() => _$CustomTimetableEventToJson(this);
|
||||||
|
@ -14,6 +14,7 @@ CustomTimetableEvent _$CustomTimetableEventFromJson(
|
|||||||
description: json['description'] as String,
|
description: json['description'] as String,
|
||||||
startDate: MhslApi.dateTimeFromJson(json['startDate'] as String),
|
startDate: MhslApi.dateTimeFromJson(json['startDate'] as String),
|
||||||
endDate: MhslApi.dateTimeFromJson(json['endDate'] as String),
|
endDate: MhslApi.dateTimeFromJson(json['endDate'] as String),
|
||||||
|
color: json['color'] as String?,
|
||||||
rrule: json['rrule'] as String,
|
rrule: json['rrule'] as String,
|
||||||
createdAt: MhslApi.dateTimeFromJson(json['createdAt'] as String),
|
createdAt: MhslApi.dateTimeFromJson(json['createdAt'] as String),
|
||||||
updatedAt: MhslApi.dateTimeFromJson(json['updatedAt'] as String),
|
updatedAt: MhslApi.dateTimeFromJson(json['updatedAt'] as String),
|
||||||
@ -27,6 +28,7 @@ Map<String, dynamic> _$CustomTimetableEventToJson(
|
|||||||
'description': instance.description,
|
'description': instance.description,
|
||||||
'startDate': MhslApi.dateTimeToJson(instance.startDate),
|
'startDate': MhslApi.dateTimeToJson(instance.startDate),
|
||||||
'endDate': MhslApi.dateTimeToJson(instance.endDate),
|
'endDate': MhslApi.dateTimeToJson(instance.endDate),
|
||||||
|
'color': instance.color,
|
||||||
'rrule': instance.rrule,
|
'rrule': instance.rrule,
|
||||||
'createdAt': MhslApi.dateTimeToJson(instance.createdAt),
|
'createdAt': MhslApi.dateTimeToJson(instance.createdAt),
|
||||||
'updatedAt': MhslApi.dateTimeToJson(instance.updatedAt),
|
'updatedAt': MhslApi.dateTimeToJson(instance.updatedAt),
|
||||||
|
@ -9,11 +9,11 @@ import 'getCustomTimetableEventResponse.dart';
|
|||||||
|
|
||||||
class GetCustomTimetableEvent extends MhslApi<GetCustomTimetableEventResponse> {
|
class GetCustomTimetableEvent extends MhslApi<GetCustomTimetableEventResponse> {
|
||||||
GetCustomTimetableEventParams params;
|
GetCustomTimetableEventParams params;
|
||||||
GetCustomTimetableEvent(this.params) : super("server/timetable/customEvents?user=${params.user}");
|
GetCustomTimetableEvent(this.params) : super('server/timetable/customEvents?user=${params.user}');
|
||||||
|
|
||||||
@override
|
@override
|
||||||
GetCustomTimetableEventResponse assemble(String raw) {
|
GetCustomTimetableEventResponse assemble(String raw) {
|
||||||
return GetCustomTimetableEventResponse.fromJson({"events": jsonDecode(raw)});
|
return GetCustomTimetableEventResponse.fromJson({'events': jsonDecode(raw)});
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -9,7 +9,7 @@ class GetCustomTimetableEventCache extends RequestCache<GetCustomTimetableEventR
|
|||||||
GetCustomTimetableEventParams params;
|
GetCustomTimetableEventParams params;
|
||||||
|
|
||||||
GetCustomTimetableEventCache(this.params, {onUpdate, renew}) : super(RequestCache.cacheMinute, onUpdate, renew: renew) {
|
GetCustomTimetableEventCache(this.params, {onUpdate, renew}) : super(RequestCache.cacheMinute, onUpdate, renew: renew) {
|
||||||
start("MarianumMobile", "customTimetableEvents");
|
start('MarianumMobile', 'customTimetableEvents');
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -6,7 +6,7 @@ import '../../mhslApi.dart';
|
|||||||
import 'getMessagesResponse.dart';
|
import 'getMessagesResponse.dart';
|
||||||
|
|
||||||
class GetMessages extends MhslApi<GetMessagesResponse> {
|
class GetMessages extends MhslApi<GetMessagesResponse> {
|
||||||
GetMessages() : super("message/messages.json");
|
GetMessages() : super('message/messages.json');
|
||||||
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -6,7 +6,7 @@ import 'getMessagesResponse.dart';
|
|||||||
|
|
||||||
class GetMessagesCache extends RequestCache<GetMessagesResponse> {
|
class GetMessagesCache extends RequestCache<GetMessagesResponse> {
|
||||||
GetMessagesCache({onUpdate, renew}) : super(RequestCache.cacheMinute, onUpdate, renew: renew) {
|
GetMessagesCache({onUpdate, renew}) : super(RequestCache.cacheMinute, onUpdate, renew: renew) {
|
||||||
start("MarianumMobile", "message");
|
start('MarianumMobile', 'message');
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -15,21 +15,21 @@ abstract class MhslApi<T> extends ApiRequest {
|
|||||||
T assemble(String raw);
|
T assemble(String raw);
|
||||||
|
|
||||||
Future<T> run() async {
|
Future<T> run() async {
|
||||||
Uri endpoint = Uri.parse("https://mhsl.eu/marianum/marianummobile/$subpath");
|
Uri endpoint = Uri.parse('https://mhsl.eu/marianum/marianummobile/$subpath');
|
||||||
|
|
||||||
http.Response? data = await request(endpoint);
|
http.Response? data = await request(endpoint);
|
||||||
if(data == null) {
|
if(data == null) {
|
||||||
throw ApiError("Request could not be dispatched!");
|
throw ApiError('Request could not be dispatched!');
|
||||||
}
|
}
|
||||||
|
|
||||||
if(data.statusCode > 299) {
|
if(data.statusCode > 299) {
|
||||||
throw ApiError("Non 200 Status code from mhsl services: $subpath: ${data.statusCode}");
|
throw ApiError('Non 200 Status code from mhsl services: $subpath: ${data.statusCode}');
|
||||||
}
|
}
|
||||||
|
|
||||||
return assemble(utf8.decode(data.bodyBytes));
|
return assemble(utf8.decode(data.bodyBytes));
|
||||||
}
|
}
|
||||||
|
|
||||||
static String dateTimeToJson(DateTime time) => Jiffy.parseFromDateTime(time).format(pattern: "yyyy-MM-dd HH:mm:ss");
|
static String dateTimeToJson(DateTime time) => Jiffy.parseFromDateTime(time).format(pattern: 'yyyy-MM-dd HH:mm:ss');
|
||||||
static DateTime dateTimeFromJson(String time) => DateTime.parse(time);
|
static DateTime dateTimeFromJson(String time) => DateTime.parse(time);
|
||||||
|
|
||||||
}
|
}
|
@ -9,7 +9,7 @@ import 'notifyRegisterParams.dart';
|
|||||||
|
|
||||||
class NotifyRegister extends MhslApi<void> {
|
class NotifyRegister extends MhslApi<void> {
|
||||||
NotifyRegisterParams params;
|
NotifyRegisterParams params;
|
||||||
NotifyRegister(this.params) : super("notify/register/");
|
NotifyRegister(this.params) : super('notify/register/');
|
||||||
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -6,14 +6,12 @@ part 'addFeedbackParams.g.dart';
|
|||||||
class AddFeedbackParams {
|
class AddFeedbackParams {
|
||||||
String user;
|
String user;
|
||||||
String feedback;
|
String feedback;
|
||||||
String? screenshot;
|
|
||||||
int appVersion;
|
int appVersion;
|
||||||
|
|
||||||
|
|
||||||
AddFeedbackParams({
|
AddFeedbackParams({
|
||||||
required this.user,
|
required this.user,
|
||||||
required this.feedback,
|
required this.feedback,
|
||||||
this.screenshot,
|
|
||||||
required this.appVersion,
|
required this.appVersion,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -10,7 +10,6 @@ AddFeedbackParams _$AddFeedbackParamsFromJson(Map<String, dynamic> json) =>
|
|||||||
AddFeedbackParams(
|
AddFeedbackParams(
|
||||||
user: json['user'] as String,
|
user: json['user'] as String,
|
||||||
feedback: json['feedback'] as String,
|
feedback: json['feedback'] as String,
|
||||||
screenshot: json['screenshot'] as String?,
|
|
||||||
appVersion: json['appVersion'] as int,
|
appVersion: json['appVersion'] as int,
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -18,6 +17,5 @@ Map<String, dynamic> _$AddFeedbackParamsToJson(AddFeedbackParams instance) =>
|
|||||||
<String, dynamic>{
|
<String, dynamic>{
|
||||||
'user': instance.user,
|
'user': instance.user,
|
||||||
'feedback': instance.feedback,
|
'feedback': instance.feedback,
|
||||||
'screenshot': instance.screenshot,
|
|
||||||
'appVersion': instance.appVersion,
|
'appVersion': instance.appVersion,
|
||||||
};
|
};
|
||||||
|
@ -12,7 +12,7 @@ import 'updateUserIndexParams.dart';
|
|||||||
|
|
||||||
class UpdateUserIndex extends MhslApi<void> {
|
class UpdateUserIndex extends MhslApi<void> {
|
||||||
UpdateUserIndexParams params;
|
UpdateUserIndexParams params;
|
||||||
UpdateUserIndex(this.params) : super("server/userIndex/update");
|
UpdateUserIndex(this.params) : super('server/userIndex/update');
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void assemble(String raw) {}
|
void assemble(String raw) {}
|
||||||
@ -20,7 +20,7 @@ class UpdateUserIndex extends MhslApi<void> {
|
|||||||
@override
|
@override
|
||||||
Future<http.Response> request(Uri uri) {
|
Future<http.Response> request(Uri uri) {
|
||||||
String data = jsonEncode(params.toJson());
|
String data = jsonEncode(params.toJson());
|
||||||
log("Updating userindex:\n $data");
|
log('Updating userindex:\n $data');
|
||||||
return http.post(uri, body: data);
|
return http.post(uri, body: data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,8 +35,8 @@ abstract class RequestCache<T extends ApiResponse?> {
|
|||||||
onUpdate(newValue);
|
onUpdate(newValue);
|
||||||
|
|
||||||
Localstore.instance.collection(file).doc(document).set({
|
Localstore.instance.collection(file).doc(document).set({
|
||||||
"json": jsonEncode(newValue),
|
'json': jsonEncode(newValue),
|
||||||
"lastupdate": DateTime.now().millisecondsSinceEpoch
|
'lastupdate': DateTime.now().millisecondsSinceEpoch
|
||||||
});
|
});
|
||||||
} on WebuntisError catch(e) {
|
} on WebuntisError catch(e) {
|
||||||
onError(e);
|
onError(e);
|
||||||
|
@ -9,7 +9,7 @@ import 'authenticateResponse.dart';
|
|||||||
class Authenticate extends WebuntisApi {
|
class Authenticate extends WebuntisApi {
|
||||||
AuthenticateParams param;
|
AuthenticateParams param;
|
||||||
|
|
||||||
Authenticate(this.param) : super("authenticate", param, authenticatedResponse: false);
|
Authenticate(this.param) : super('authenticate', param, authenticatedResponse: false);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<AuthenticateResponse> run() async {
|
Future<AuthenticateResponse> run() async {
|
||||||
|
@ -4,7 +4,7 @@ import '../../webuntisApi.dart';
|
|||||||
import 'getHolidaysResponse.dart';
|
import 'getHolidaysResponse.dart';
|
||||||
|
|
||||||
class GetHolidays extends WebuntisApi {
|
class GetHolidays extends WebuntisApi {
|
||||||
GetHolidays() : super("getHolidays", null);
|
GetHolidays() : super('getHolidays', null);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<GetHolidaysResponse> run() async {
|
Future<GetHolidaysResponse> run() async {
|
||||||
|
@ -6,7 +6,7 @@ import 'getHolidaysResponse.dart';
|
|||||||
|
|
||||||
class GetHolidaysCache extends RequestCache<GetHolidaysResponse> {
|
class GetHolidaysCache extends RequestCache<GetHolidaysResponse> {
|
||||||
GetHolidaysCache({onUpdate}) : super(RequestCache.cacheDay, onUpdate) {
|
GetHolidaysCache({onUpdate}) : super(RequestCache.cacheDay, onUpdate) {
|
||||||
start("MarianumMobile", "wu-holidays");
|
start('MarianumMobile', 'wu-holidays');
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -5,7 +5,7 @@ import '../../webuntisApi.dart';
|
|||||||
import 'getRoomsResponse.dart';
|
import 'getRoomsResponse.dart';
|
||||||
|
|
||||||
class GetRooms extends WebuntisApi {
|
class GetRooms extends WebuntisApi {
|
||||||
GetRooms() : super("getRooms", null);
|
GetRooms() : super('getRooms', null);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<GetRoomsResponse> run() async {
|
Future<GetRoomsResponse> run() async {
|
||||||
@ -14,10 +14,10 @@ class GetRooms extends WebuntisApi {
|
|||||||
return finalize(GetRoomsResponse.fromJson(jsonDecode(rawAnswer)));
|
return finalize(GetRoomsResponse.fromJson(jsonDecode(rawAnswer)));
|
||||||
} catch(e, trace) {
|
} catch(e, trace) {
|
||||||
log(trace.toString());
|
log(trace.toString());
|
||||||
log("Failed to parse getRoom data with server response: $rawAnswer");
|
log('Failed to parse getRoom data with server response: $rawAnswer');
|
||||||
}
|
}
|
||||||
|
|
||||||
throw Exception("Failed to parse getRoom server response: $rawAnswer");
|
throw Exception('Failed to parse getRoom server response: $rawAnswer');
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -6,7 +6,7 @@ import 'getRoomsResponse.dart';
|
|||||||
|
|
||||||
class GetRoomsCache extends RequestCache<GetRoomsResponse> {
|
class GetRoomsCache extends RequestCache<GetRoomsResponse> {
|
||||||
GetRoomsCache({onUpdate}) : super(RequestCache.cacheHour, onUpdate) {
|
GetRoomsCache({onUpdate}) : super(RequestCache.cacheHour, onUpdate) {
|
||||||
start("MarianumMobile", "wu-rooms");
|
start('MarianumMobile', 'wu-rooms');
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -4,7 +4,7 @@ import '../../webuntisApi.dart';
|
|||||||
import 'getSubjectsResponse.dart';
|
import 'getSubjectsResponse.dart';
|
||||||
|
|
||||||
class GetSubjects extends WebuntisApi {
|
class GetSubjects extends WebuntisApi {
|
||||||
GetSubjects() : super("getSubjects", null);
|
GetSubjects() : super('getSubjects', null);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<GetSubjectsResponse> run() async {
|
Future<GetSubjectsResponse> run() async {
|
||||||
|
@ -6,7 +6,7 @@ import 'getSubjectsResponse.dart';
|
|||||||
|
|
||||||
class GetSubjectsCache extends RequestCache<GetSubjectsResponse> {
|
class GetSubjectsCache extends RequestCache<GetSubjectsResponse> {
|
||||||
GetSubjectsCache({onUpdate}) : super(RequestCache.cacheHour, onUpdate) {
|
GetSubjectsCache({onUpdate}) : super(RequestCache.cacheHour, onUpdate) {
|
||||||
start("MarianumMobile", "wu-subjects");
|
start('MarianumMobile', 'wu-subjects');
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -7,7 +7,7 @@ import 'getTimetableResponse.dart';
|
|||||||
class GetTimetable extends WebuntisApi {
|
class GetTimetable extends WebuntisApi {
|
||||||
GetTimetableParams params;
|
GetTimetableParams params;
|
||||||
|
|
||||||
GetTimetable(this.params) : super("getTimetable", params);
|
GetTimetable(this.params) : super('getTimetable', params);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<GetTimetableResponse> run() async {
|
Future<GetTimetableResponse> run() async {
|
||||||
|
@ -11,7 +11,7 @@ class GetTimetableCache extends RequestCache<GetTimetableResponse> {
|
|||||||
int enddate;
|
int enddate;
|
||||||
|
|
||||||
GetTimetableCache({required onUpdate, onError, required this.startdate, required this.enddate}) : super(RequestCache.cacheMinute, onUpdate, onError: onError) {
|
GetTimetableCache({required onUpdate, onError, required this.startdate, required this.enddate}) : super(RequestCache.cacheMinute, onUpdate, onError: onError) {
|
||||||
start("MarianumMobile", "wu-timetable-$startdate-$enddate");
|
start('MarianumMobile', 'wu-timetable-$startdate-$enddate');
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -67,10 +67,10 @@ class GetTimetableParamsOptions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
enum GetTimetableParamsOptionsFields {
|
enum GetTimetableParamsOptionsFields {
|
||||||
@JsonValue("id") id,
|
@JsonValue('id') id,
|
||||||
@JsonValue("name") name,
|
@JsonValue('name') name,
|
||||||
@JsonValue("longname") longname,
|
@JsonValue('longname') longname,
|
||||||
@JsonValue("externalkey") externalkey;
|
@JsonValue('externalkey') externalkey;
|
||||||
|
|
||||||
static List<GetTimetableParamsOptionsFields> all = [id, name, longname, externalkey];
|
static List<GetTimetableParamsOptionsFields> all = [id, name, longname, externalkey];
|
||||||
}
|
}
|
||||||
@ -88,7 +88,7 @@ class GetTimetableParamsOptionsElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
enum GetTimetableParamsOptionsElementKeyType {
|
enum GetTimetableParamsOptionsElementKeyType {
|
||||||
@JsonValue("id") id,
|
@JsonValue('id') id,
|
||||||
@JsonValue("name") name,
|
@JsonValue('name') name,
|
||||||
@JsonValue("externalkey") externalkey
|
@JsonValue('externalkey') externalkey
|
||||||
}
|
}
|
@ -9,7 +9,7 @@ import 'queries/authenticate/authenticate.dart';
|
|||||||
import 'webuntisError.dart';
|
import 'webuntisError.dart';
|
||||||
|
|
||||||
abstract class WebuntisApi extends ApiRequest {
|
abstract class WebuntisApi extends ApiRequest {
|
||||||
Uri endpoint = Uri.parse("https://${EndpointData().webuntis().full()}/WebUntis/jsonrpc.do?school=marianum-fulda");
|
Uri endpoint = Uri.parse('https://${EndpointData().webuntis().full()}/WebUntis/jsonrpc.do?school=marianum-fulda');
|
||||||
String method;
|
String method;
|
||||||
ApiParams? genericParam;
|
ApiParams? genericParam;
|
||||||
http.Response? response;
|
http.Response? response;
|
||||||
@ -22,11 +22,11 @@ abstract class WebuntisApi extends ApiRequest {
|
|||||||
Future<String> query(WebuntisApi untis) async {
|
Future<String> query(WebuntisApi untis) async {
|
||||||
String query = '{"id":"ID","method":"$method","params":${untis._body()},"jsonrpc":"2.0"}';
|
String query = '{"id":"ID","method":"$method","params":${untis._body()},"jsonrpc":"2.0"}';
|
||||||
|
|
||||||
String sessionId = "0";
|
String sessionId = '0';
|
||||||
if(authenticatedResponse) {
|
if(authenticatedResponse) {
|
||||||
sessionId = (await Authenticate.getSession()).sessionId;
|
sessionId = (await Authenticate.getSession()).sessionId;
|
||||||
}
|
}
|
||||||
http.Response data = await post(query, {"Cookie": "JSESSIONID=$sessionId"});
|
http.Response data = await post(query, {'Cookie': 'JSESSIONID=$sessionId'});
|
||||||
response = data;
|
response = data;
|
||||||
|
|
||||||
dynamic jsonData = jsonDecode(data.body);
|
dynamic jsonData = jsonDecode(data.body);
|
||||||
@ -49,7 +49,7 @@ abstract class WebuntisApi extends ApiRequest {
|
|||||||
Future<ApiResponse> run();
|
Future<ApiResponse> run();
|
||||||
|
|
||||||
String _body() {
|
String _body() {
|
||||||
return genericParam == null ? "{}" : jsonEncode(genericParam);
|
return genericParam == null ? '{}' : jsonEncode(genericParam);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<http.Response> post(String data, Map<String, String>? headers) async {
|
Future<http.Response> post(String data, Map<String, String>? headers) async {
|
||||||
@ -57,7 +57,7 @@ abstract class WebuntisApi extends ApiRequest {
|
|||||||
.post(endpoint, body: data, headers: headers)
|
.post(endpoint, body: data, headers: headers)
|
||||||
.timeout(
|
.timeout(
|
||||||
const Duration(seconds: 10),
|
const Duration(seconds: 10),
|
||||||
onTimeout: () => throw WebuntisError("Timeout", 1)
|
onTimeout: () => throw WebuntisError('Timeout', 1)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -6,6 +6,6 @@ class WebuntisError implements Exception {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return "WebUntis ($code): $message";
|
return 'WebUntis ($code): $message';
|
||||||
}
|
}
|
||||||
}
|
}
|
84
lib/app.dart
84
lib/app.dart
@ -5,7 +5,7 @@ import 'dart:developer';
|
|||||||
import 'package:easy_debounce/easy_throttle.dart';
|
import 'package:easy_debounce/easy_throttle.dart';
|
||||||
import 'package:firebase_messaging/firebase_messaging.dart';
|
import 'package:firebase_messaging/firebase_messaging.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:persistent_bottom_nav_bar_v2/persistent-tab-view.dart';
|
import 'package:persistent_bottom_nav_bar_v2/persistent_bottom_nav_bar_v2.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:badges/badges.dart' as badges;
|
import 'package:badges/badges.dart' as badges;
|
||||||
|
|
||||||
@ -39,14 +39,14 @@ class _AppState extends State<App> with WidgetsBindingObserver {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
void didChangeAppLifecycleState(AppLifecycleState state) {
|
void didChangeAppLifecycleState(AppLifecycleState state) {
|
||||||
log("AppLifecycle: ${state.toString()}");
|
log('AppLifecycle: ${state.toString()}');
|
||||||
|
|
||||||
if(state == AppLifecycleState.resumed) {
|
if(state == AppLifecycleState.resumed) {
|
||||||
EasyThrottle.throttle(
|
EasyThrottle.throttle(
|
||||||
"appLifecycleState",
|
'appLifecycleState',
|
||||||
const Duration(seconds: 10),
|
const Duration(seconds: 10),
|
||||||
() {
|
() {
|
||||||
log("Refreshing due to LifecycleChange");
|
log('Refreshing due to LifecycleChange');
|
||||||
NotificationTasks.updateProviders(context);
|
NotificationTasks.updateProviders(context);
|
||||||
Provider.of<TimetableProps>(context, listen: false).run();
|
Provider.of<TimetableProps>(context, listen: false).run();
|
||||||
}
|
}
|
||||||
@ -93,32 +93,27 @@ class _AppState extends State<App> with WidgetsBindingObserver {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return PersistentTabView(
|
return PersistentTabView(
|
||||||
context,
|
|
||||||
controller: App.bottomNavigator,
|
controller: App.bottomNavigator,
|
||||||
navBarStyle: NavBarStyle.style6,
|
gestureNavigationEnabled: true,
|
||||||
hideNavigationBarWhenKeyboardShows: true,
|
navBarOverlap: const NavBarOverlap.none(),
|
||||||
navBarHeight: MediaQuery.of(context).viewInsets.bottom > 0 ? 0.0 : kBottomNavigationBarHeight,
|
backgroundColor: Theme.of(context).colorScheme.primary,
|
||||||
backgroundColor: Theme.of(context).colorScheme.surface,
|
|
||||||
decoration: const NavBarDecoration(
|
screenTransitionAnimation: const ScreenTransitionAnimation(curve: Curves.easeOutQuad, duration: Duration(milliseconds: 200)),
|
||||||
border: Border(top: BorderSide(width: 1, color: Colors.grey)),
|
tabs: [
|
||||||
),
|
PersistentTabConfig(
|
||||||
screenTransitionAnimation: const ScreenTransitionAnimation(animateTabTransition: true, curve: Curves.ease, duration: Duration(milliseconds: 200)),
|
screen: const Breaker(breaker: BreakerArea.timetable, child: Timetable()),
|
||||||
screens: const [
|
item: ItemConfig(
|
||||||
Breaker(breaker: BreakerArea.timetable, child: Timetable()),
|
activeForegroundColor: Theme.of(context).primaryColor,
|
||||||
Breaker(breaker: BreakerArea.talk, child: ChatList()),
|
inactiveForegroundColor: Theme.of(context).colorScheme.secondary,
|
||||||
Breaker(breaker: BreakerArea.files, child: Files([])),
|
|
||||||
Breaker(breaker: BreakerArea.more, child: Overhang()),
|
|
||||||
],
|
|
||||||
items: [
|
|
||||||
PersistentBottomNavBarItem(
|
|
||||||
activeColorPrimary: Theme.of(context).primaryColor,
|
|
||||||
inactiveColorPrimary: Theme.of(context).colorScheme.secondary,
|
|
||||||
icon: const Icon(Icons.calendar_month),
|
icon: const Icon(Icons.calendar_month),
|
||||||
title: "Vertretung"
|
title: 'Vertretung'
|
||||||
),
|
),
|
||||||
PersistentBottomNavBarItem(
|
),
|
||||||
activeColorPrimary: Theme.of(context).primaryColor,
|
PersistentTabConfig(
|
||||||
inactiveColorPrimary: Theme.of(context).colorScheme.secondary,
|
screen: const Breaker(breaker: BreakerArea.talk, child: ChatList()),
|
||||||
|
item: ItemConfig(
|
||||||
|
activeForegroundColor: Theme.of(context).primaryColor,
|
||||||
|
inactiveForegroundColor: Theme.of(context).colorScheme.secondary,
|
||||||
icon: Consumer<ChatListProps>(
|
icon: Consumer<ChatListProps>(
|
||||||
builder: (context, value, child) {
|
builder: (context, value, child) {
|
||||||
if(value.primaryLoading()) return const Icon(Icons.chat);
|
if(value.primaryLoading()) return const Icon(Icons.chat);
|
||||||
@ -132,28 +127,41 @@ class _AppState extends State<App> with WidgetsBindingObserver {
|
|||||||
badgeColor: Theme.of(context).primaryColor,
|
badgeColor: Theme.of(context).primaryColor,
|
||||||
elevation: 1,
|
elevation: 1,
|
||||||
),
|
),
|
||||||
badgeContent: Text("$messages", style: const TextStyle(color: Colors.white, fontSize: 10, fontWeight: FontWeight.bold)),
|
badgeContent: Text('$messages', style: const TextStyle(color: Colors.white, fontSize: 10, fontWeight: FontWeight.bold)),
|
||||||
child: const Icon(Icons.chat),
|
child: const Icon(Icons.chat),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
title: "Talk",
|
title: 'Talk',
|
||||||
),
|
),
|
||||||
PersistentBottomNavBarItem(
|
),
|
||||||
activeColorPrimary: Theme.of(context).primaryColor,
|
PersistentTabConfig(
|
||||||
inactiveColorPrimary: Theme.of(context).colorScheme.secondary,
|
screen: const Breaker(breaker: BreakerArea.files, child: Files([])),
|
||||||
|
item: ItemConfig(
|
||||||
|
activeForegroundColor: Theme.of(context).primaryColor,
|
||||||
|
inactiveForegroundColor: Theme.of(context).colorScheme.secondary,
|
||||||
icon: const Icon(Icons.folder),
|
icon: const Icon(Icons.folder),
|
||||||
title: "Dateien"
|
title: 'Dateien'
|
||||||
),
|
),
|
||||||
PersistentBottomNavBarItem(
|
),
|
||||||
activeColorPrimary: Theme.of(context).primaryColor,
|
PersistentTabConfig(
|
||||||
inactiveColorPrimary: Theme.of(context).colorScheme.secondary,
|
screen: const Breaker(breaker: BreakerArea.more, child: Overhang()),
|
||||||
|
item: ItemConfig(
|
||||||
|
activeForegroundColor: Theme.of(context).primaryColor,
|
||||||
|
inactiveForegroundColor: Theme.of(context).colorScheme.secondary,
|
||||||
icon: const Icon(Icons.apps),
|
icon: const Icon(Icons.apps),
|
||||||
title: "Mehr"
|
title: 'Mehr'
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
navBarBuilder: (config) => Style6BottomNavBar(
|
||||||
|
navBarConfig: config,
|
||||||
|
navBarDecoration: NavBarDecoration(
|
||||||
|
border: const Border(top: BorderSide(width: 1, color: Colors.grey)),
|
||||||
|
color: Theme.of(context).colorScheme.surface,
|
||||||
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
5
lib/extensions/renderNotNull.dart
Normal file
5
lib/extensions/renderNotNull.dart
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
extension RenderNotNullExt<T> on T? {
|
||||||
|
R? wrapNullable<R>(R Function(T data) defaultValueCallback) {
|
||||||
|
return this != null ? defaultValueCallback(this as T) : null;
|
||||||
|
}
|
||||||
|
}
|
@ -2,7 +2,6 @@ import 'dart:async';
|
|||||||
import 'dart:developer';
|
import 'dart:developer';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:feedback/feedback.dart';
|
|
||||||
import 'package:firebase_core/firebase_core.dart';
|
import 'package:firebase_core/firebase_core.dart';
|
||||||
import 'package:firebase_messaging/firebase_messaging.dart';
|
import 'package:firebase_messaging/firebase_messaging.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
@ -30,7 +29,6 @@ import 'storage/base/settingsProvider.dart';
|
|||||||
import 'theming/darkAppTheme.dart';
|
import 'theming/darkAppTheme.dart';
|
||||||
import 'theming/lightAppTheme.dart';
|
import 'theming/lightAppTheme.dart';
|
||||||
import 'view/login/login.dart';
|
import 'view/login/login.dart';
|
||||||
import 'view/pages/more/feedback/feedbackForm.dart';
|
|
||||||
import 'widget/placeholderView.dart';
|
import 'widget/placeholderView.dart';
|
||||||
|
|
||||||
Future<void> main() async {
|
Future<void> main() async {
|
||||||
@ -40,7 +38,7 @@ Future<void> main() async {
|
|||||||
await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
|
await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
|
||||||
log("Firebase token: ${await FirebaseMessaging.instance.getToken() ?? "Error: no Firebase token!"}");
|
log("Firebase token: ${await FirebaseMessaging.instance.getToken() ?? "Error: no Firebase token!"}");
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log("Error initializing Firebase app!");
|
log('Error initializing Firebase app!');
|
||||||
}
|
}
|
||||||
|
|
||||||
ByteData data = await PlatformAssetBundle().load('assets/ca/lets-encrypt-r3.pem');
|
ByteData data = await PlatformAssetBundle().load('assets/ca/lets-encrypt-r3.pem');
|
||||||
@ -71,12 +69,7 @@ Future<void> main() async {
|
|||||||
ChangeNotifierProvider(create: (context) => MessageProps()),
|
ChangeNotifierProvider(create: (context) => MessageProps()),
|
||||||
ChangeNotifierProvider(create: (context) => HolidaysProps()),
|
ChangeNotifierProvider(create: (context) => HolidaysProps()),
|
||||||
],
|
],
|
||||||
child: BetterFeedback(
|
|
||||||
themeMode: ThemeMode.dark,
|
|
||||||
feedbackBuilder: (context, callback, scrollController) => FeedbackForm(callback: callback, scrollController: scrollController),
|
|
||||||
localeOverride: const Locale('de'),
|
|
||||||
child: const Main(),
|
child: const Main(),
|
||||||
),
|
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -93,7 +86,7 @@ class _MainState extends State<Main> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
Jiffy.setLocale("de");
|
Jiffy.setLocale('de');
|
||||||
|
|
||||||
AccountData().waitForPopulation().then((value) {
|
AccountData().waitForPopulation().then((value) {
|
||||||
Provider.of<AccountModel>(context, listen: false)
|
Provider.of<AccountModel>(context, listen: false)
|
||||||
@ -113,7 +106,12 @@ class _MainState extends State<Main> {
|
|||||||
textDirection: TextDirection.ltr,
|
textDirection: TextDirection.ltr,
|
||||||
child: Consumer<SettingsProvider>(
|
child: Consumer<SettingsProvider>(
|
||||||
builder: (context, settings, child) {
|
builder: (context, settings, child) {
|
||||||
|
var devToolsSettings = settings.val().devToolsSettings;
|
||||||
return MaterialApp(
|
return MaterialApp(
|
||||||
|
showPerformanceOverlay: devToolsSettings.showPerformanceOverlay,
|
||||||
|
checkerboardOffscreenLayers: devToolsSettings.checkerboardOffscreenLayers,
|
||||||
|
checkerboardRasterCacheImages: devToolsSettings.checkerboardRasterCacheImages,
|
||||||
|
|
||||||
debugShowCheckedModeBanner: false,
|
debugShowCheckedModeBanner: false,
|
||||||
localizationsDelegates: const [
|
localizationsDelegates: const [
|
||||||
...GlobalMaterialLocalizations.delegates,
|
...GlobalMaterialLocalizations.delegates,
|
||||||
@ -138,7 +136,7 @@ class _MainState extends State<Main> {
|
|||||||
switch(accountModel.state) {
|
switch(accountModel.state) {
|
||||||
case AccountModelState.loggedIn: return const App();
|
case AccountModelState.loggedIn: return const App();
|
||||||
case AccountModelState.loggedOut: return const Login();
|
case AccountModelState.loggedOut: return const Login();
|
||||||
case AccountModelState.undefined: return const PlaceholderView(icon: Icons.timer, text: "Daten werden geladen");
|
case AccountModelState.undefined: return const PlaceholderView(icon: Icons.timer, text: 'Daten werden geladen');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
@ -10,8 +10,8 @@ import 'package:shared_preferences/shared_preferences.dart';
|
|||||||
import 'accountModel.dart';
|
import 'accountModel.dart';
|
||||||
|
|
||||||
class AccountData {
|
class AccountData {
|
||||||
static const _usernameField = "username";
|
static const _usernameField = 'username';
|
||||||
static const _passwordField = "password";
|
static const _passwordField = 'password';
|
||||||
|
|
||||||
static final AccountData _instance = AccountData._construct();
|
static final AccountData _instance = AccountData._construct();
|
||||||
final Future<SharedPreferences> _storage = SharedPreferences.getInstance();
|
final Future<SharedPreferences> _storage = SharedPreferences.getInstance();
|
||||||
@ -29,21 +29,21 @@ class AccountData {
|
|||||||
String? _password;
|
String? _password;
|
||||||
|
|
||||||
String getUsername() {
|
String getUsername() {
|
||||||
if(_username == null) throw Exception("Username not initialized");
|
if(_username == null) throw Exception('Username not initialized');
|
||||||
return _username!;
|
return _username!;
|
||||||
}
|
}
|
||||||
|
|
||||||
String getPassword() {
|
String getPassword() {
|
||||||
if(_password == null) throw Exception("Password not initialized");
|
if(_password == null) throw Exception('Password not initialized');
|
||||||
return _password!;
|
return _password!;
|
||||||
}
|
}
|
||||||
|
|
||||||
String getUserSecret() {
|
String getUserSecret() {
|
||||||
return sha512.convert(utf8.encode("${AccountData().getUsername()}:${AccountData().getPassword()}")).toString();
|
return sha512.convert(utf8.encode('${AccountData().getUsername()}:${AccountData().getPassword()}')).toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<String> getDeviceId() async {
|
Future<String> getDeviceId() async {
|
||||||
return sha512.convert(utf8.encode("${getUserSecret()}@${await FirebaseMessaging.instance.getToken()}")).toString();
|
return sha512.convert(utf8.encode('${getUserSecret()}@${await FirebaseMessaging.instance.getToken()}')).toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> setData(String username, String password) async {
|
Future<void> setData(String username, String password) async {
|
||||||
@ -84,7 +84,7 @@ class AccountData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
String buildHttpAuthString() {
|
String buildHttpAuthString() {
|
||||||
if(!isPopulated()) throw Exception("AccountData (e.g. username or password) is not initialized!");
|
if(!isPopulated()) throw Exception('AccountData (e.g. username or password) is not initialized!');
|
||||||
return "$_username:$_password";
|
return '$_username:$_password';
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -26,7 +26,7 @@ class BreakerProps extends DataHolder {
|
|||||||
for(var key in breakers.regional.keys) {
|
for(var key in breakers.regional.keys) {
|
||||||
GetBreakersReponseObject value = breakers.regional[key]!;
|
GetBreakersReponseObject value = breakers.regional[key]!;
|
||||||
|
|
||||||
if(int.parse(key.split("b")[1]) >= selfVersion) {
|
if(int.parse(key.split('b')[1]) >= selfVersion) {
|
||||||
if(value.areas.contains(type)) return value.message;
|
if(value.areas.contains(type)) return value.message;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@ import '../../api/marianumcloud/talk/chat/getChatResponse.dart';
|
|||||||
import '../dataHolder.dart';
|
import '../dataHolder.dart';
|
||||||
|
|
||||||
class ChatProps extends DataHolder {
|
class ChatProps extends DataHolder {
|
||||||
String _queryToken = "";
|
String _queryToken = '';
|
||||||
DateTime _lastTokenSet = DateTime.now();
|
DateTime _lastTokenSet = DateTime.now();
|
||||||
|
|
||||||
GetChatResponse? _getChatResponse;
|
GetChatResponse? _getChatResponse;
|
||||||
|
@ -21,7 +21,7 @@ class Endpoint {
|
|||||||
String domain;
|
String domain;
|
||||||
String path;
|
String path;
|
||||||
|
|
||||||
Endpoint({required this.domain, this.path = ""});
|
Endpoint({required this.domain, this.path = ''});
|
||||||
|
|
||||||
String full() {
|
String full() {
|
||||||
return domain + path;
|
return domain + path;
|
||||||
@ -40,17 +40,17 @@ class EndpointData {
|
|||||||
EndpointMode getEndpointMode() {
|
EndpointMode getEndpointMode() {
|
||||||
late String existingName;
|
late String existingName;
|
||||||
existingName = AccountData().getUsername();
|
existingName = AccountData().getUsername();
|
||||||
return existingName.startsWith("google") ? EndpointMode.stage : EndpointMode.live;
|
return existingName.startsWith('google') ? EndpointMode.stage : EndpointMode.live;
|
||||||
}
|
}
|
||||||
|
|
||||||
Endpoint webuntis() {
|
Endpoint webuntis() {
|
||||||
return EndpointOptions(
|
return EndpointOptions(
|
||||||
live: Endpoint(
|
live: Endpoint(
|
||||||
domain: "peleus.webuntis.com",
|
domain: 'peleus.webuntis.com',
|
||||||
),
|
),
|
||||||
staged: Endpoint(
|
staged: Endpoint(
|
||||||
domain: "mhsl.eu",
|
domain: 'mhsl.eu',
|
||||||
path: "/marianum/marianummobile/webuntis/public/index.php/api"
|
path: '/marianum/marianummobile/webuntis/public/index.php/api'
|
||||||
),
|
),
|
||||||
).get(getEndpointMode());
|
).get(getEndpointMode());
|
||||||
}
|
}
|
||||||
@ -58,11 +58,11 @@ class EndpointData {
|
|||||||
Endpoint nextcloud() {
|
Endpoint nextcloud() {
|
||||||
return EndpointOptions(
|
return EndpointOptions(
|
||||||
live: Endpoint(
|
live: Endpoint(
|
||||||
domain: "cloud.marianum-fulda.de",
|
domain: 'cloud.marianum-fulda.de',
|
||||||
),
|
),
|
||||||
staged: Endpoint(
|
staged: Endpoint(
|
||||||
domain: "mhsl.eu",
|
domain: 'mhsl.eu',
|
||||||
path: "/marianum/marianummobile/cloud",
|
path: '/marianum/marianummobile/cloud',
|
||||||
)
|
)
|
||||||
).get(getEndpointMode());
|
).get(getEndpointMode());
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@ extension ExtendedList on List {
|
|||||||
|
|
||||||
class FilesProps extends DataHolder {
|
class FilesProps extends DataHolder {
|
||||||
List<String> folderPath = List<String>.empty(growable: true);
|
List<String> folderPath = List<String>.empty(growable: true);
|
||||||
String currentFolderName = "Home";
|
String currentFolderName = 'Home';
|
||||||
|
|
||||||
ListFilesResponse? _listFilesResponse;
|
ListFilesResponse? _listFilesResponse;
|
||||||
ListFilesResponse get listFilesResponse => _listFilesResponse!;
|
ListFilesResponse get listFilesResponse => _listFilesResponse!;
|
||||||
@ -32,7 +32,7 @@ class FilesProps extends DataHolder {
|
|||||||
_listFilesResponse = null;
|
_listFilesResponse = null;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
ListFilesCache(
|
ListFilesCache(
|
||||||
path: folderPath.isEmpty ? "/" : folderPath.join("/"),
|
path: folderPath.isEmpty ? '/' : folderPath.join('/'),
|
||||||
onUpdate: (ListFilesResponse data) => {
|
onUpdate: (ListFilesResponse data) => {
|
||||||
_listFilesResponse = data,
|
_listFilesResponse = data,
|
||||||
notifyListeners(),
|
notifyListeners(),
|
||||||
@ -48,7 +48,7 @@ class FilesProps extends DataHolder {
|
|||||||
|
|
||||||
void popFolder() {
|
void popFolder() {
|
||||||
folderPath.removeLast();
|
folderPath.removeLast();
|
||||||
if(folderPath.isEmpty) currentFolderName = "Home";
|
if(folderPath.isEmpty) currentFolderName = 'Home';
|
||||||
run();
|
run();
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -49,8 +49,8 @@ class TimetableProps extends DataHolder {
|
|||||||
@override
|
@override
|
||||||
void run({renew}) {
|
void run({renew}) {
|
||||||
GetTimetableCache(
|
GetTimetableCache(
|
||||||
startdate: int.parse(DateFormat("yyyyMMdd").format(startDate)),
|
startdate: int.parse(DateFormat('yyyyMMdd').format(startDate)),
|
||||||
enddate: int.parse(DateFormat("yyyyMMdd").format(endDate)),
|
enddate: int.parse(DateFormat('yyyyMMdd').format(endDate)),
|
||||||
onUpdate: (GetTimetableResponse data) => {
|
onUpdate: (GetTimetableResponse data) => {
|
||||||
_getTimetableResponse = data,
|
_getTimetableResponse = data,
|
||||||
notifyListeners(),
|
notifyListeners(),
|
||||||
|
@ -49,11 +49,11 @@ class NotificationController {
|
|||||||
|
|
||||||
DebugTile(context).run(() {
|
DebugTile(context).run(() {
|
||||||
showDialog(context: context, builder: (context) => AlertDialog(
|
showDialog(context: context, builder: (context) => AlertDialog(
|
||||||
title: const Text("Notification report"),
|
title: const Text('Notification report'),
|
||||||
content: Column(
|
content: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
const Text("Dieser Bericht wird angezeigt, da du den Entwicklermodus aktiviert hast und die App über eine Benachrichtigung geöffnet wurde."),
|
const Text('Dieser Bericht wird angezeigt, da du den Entwicklermodus aktiviert hast und die App über eine Benachrichtigung geöffnet wurde.'),
|
||||||
Text(JsonViewer.format(message.data)),
|
Text(JsonViewer.format(message.data)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
@ -10,13 +10,13 @@ import '../widget/confirmDialog.dart';
|
|||||||
class NotifyUpdater {
|
class NotifyUpdater {
|
||||||
static ConfirmDialog enableAfterDisclaimer(SettingsProvider settings) {
|
static ConfirmDialog enableAfterDisclaimer(SettingsProvider settings) {
|
||||||
return ConfirmDialog(
|
return ConfirmDialog(
|
||||||
title: "Warnung",
|
title: 'Warnung',
|
||||||
icon: Icons.warning_amber,
|
icon: Icons.warning_amber,
|
||||||
content: ""
|
content: ''
|
||||||
"Die Push-Benachrichtigungen werden durch mhsl.eu versendet.\n\n"
|
'Die Push-Benachrichtigungen werden durch mhsl.eu versendet.\n\n'
|
||||||
"Durch das aktivieren dieser Funktion wird dein Nutzername, dein Password und eine Geräte-ID von mhsl dauerhaft gespeichert und verarbeitet.\n\n"
|
'Durch das aktivieren dieser Funktion wird dein Nutzername, dein Password und eine Geräte-ID von mhsl dauerhaft gespeichert und verarbeitet.\n\n'
|
||||||
"Für mehr Informationen drücke lange auf die Einstellungsoption!",
|
'Für mehr Informationen drücke lange auf die Einstellungsoption!',
|
||||||
confirmButton: "Aktivieren",
|
confirmButton: 'Aktivieren',
|
||||||
onConfirm: () {
|
onConfirm: () {
|
||||||
FirebaseMessaging.instance.requestPermission(
|
FirebaseMessaging.instance.requestPermission(
|
||||||
provisional: false
|
provisional: false
|
||||||
@ -29,7 +29,7 @@ class NotifyUpdater {
|
|||||||
static void registerToServer() async {
|
static void registerToServer() async {
|
||||||
String? fcmToken = await FirebaseMessaging.instance.getToken();
|
String? fcmToken = await FirebaseMessaging.instance.getToken();
|
||||||
|
|
||||||
if(fcmToken == null) throw Exception("Failed to register push notification because there is no FBC token!");
|
if(fcmToken == null) throw Exception('Failed to register push notification because there is no FBC token!');
|
||||||
|
|
||||||
NotifyRegister(
|
NotifyRegister(
|
||||||
NotifyRegisterParams(
|
NotifyRegisterParams(
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:json_annotation/json_annotation.dart';
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
|
|
||||||
|
import '../devTools/devToolsSettings.dart';
|
||||||
import '../file/fileSettings.dart';
|
import '../file/fileSettings.dart';
|
||||||
import '../fileView/fileViewSettings.dart';
|
import '../fileView/fileViewSettings.dart';
|
||||||
import '../gradeAverages/gradeAveragesSettings.dart';
|
import '../gradeAverages/gradeAveragesSettings.dart';
|
||||||
@ -27,6 +28,7 @@ class Settings {
|
|||||||
HolidaysSettings holidaysSettings;
|
HolidaysSettings holidaysSettings;
|
||||||
FileViewSettings fileViewSettings;
|
FileViewSettings fileViewSettings;
|
||||||
NotificationSettings notificationSettings;
|
NotificationSettings notificationSettings;
|
||||||
|
DevToolsSettings devToolsSettings;
|
||||||
|
|
||||||
Settings({
|
Settings({
|
||||||
required this.appTheme,
|
required this.appTheme,
|
||||||
@ -38,6 +40,7 @@ class Settings {
|
|||||||
required this.holidaysSettings,
|
required this.holidaysSettings,
|
||||||
required this.fileViewSettings,
|
required this.fileViewSettings,
|
||||||
required this.notificationSettings,
|
required this.notificationSettings,
|
||||||
|
required this.devToolsSettings,
|
||||||
});
|
});
|
||||||
|
|
||||||
static String _themeToJson(ThemeMode m) => m.name;
|
static String _themeToJson(ThemeMode m) => m.name;
|
||||||
|
@ -23,6 +23,8 @@ Settings _$SettingsFromJson(Map<String, dynamic> json) => Settings(
|
|||||||
json['fileViewSettings'] as Map<String, dynamic>),
|
json['fileViewSettings'] as Map<String, dynamic>),
|
||||||
notificationSettings: NotificationSettings.fromJson(
|
notificationSettings: NotificationSettings.fromJson(
|
||||||
json['notificationSettings'] as Map<String, dynamic>),
|
json['notificationSettings'] as Map<String, dynamic>),
|
||||||
|
devToolsSettings: DevToolsSettings.fromJson(
|
||||||
|
json['devToolsSettings'] as Map<String, dynamic>),
|
||||||
);
|
);
|
||||||
|
|
||||||
Map<String, dynamic> _$SettingsToJson(Settings instance) => <String, dynamic>{
|
Map<String, dynamic> _$SettingsToJson(Settings instance) => <String, dynamic>{
|
||||||
@ -35,4 +37,5 @@ Map<String, dynamic> _$SettingsToJson(Settings instance) => <String, dynamic>{
|
|||||||
'holidaysSettings': instance.holidaysSettings.toJson(),
|
'holidaysSettings': instance.holidaysSettings.toJson(),
|
||||||
'fileViewSettings': instance.fileViewSettings.toJson(),
|
'fileViewSettings': instance.fileViewSettings.toJson(),
|
||||||
'notificationSettings': instance.notificationSettings.toJson(),
|
'notificationSettings': instance.notificationSettings.toJson(),
|
||||||
|
'devToolsSettings': instance.devToolsSettings.toJson(),
|
||||||
};
|
};
|
||||||
|
@ -8,7 +8,7 @@ import '../../view/settings/defaultSettings.dart';
|
|||||||
import 'settings.dart';
|
import 'settings.dart';
|
||||||
|
|
||||||
class SettingsProvider extends ChangeNotifier {
|
class SettingsProvider extends ChangeNotifier {
|
||||||
static const String _fieldName = "settings";
|
static const String _fieldName = 'settings';
|
||||||
|
|
||||||
late SharedPreferences _storage;
|
late SharedPreferences _storage;
|
||||||
late Settings _settings = DefaultSettings.get();
|
late Settings _settings = DefaultSettings.get();
|
||||||
@ -45,13 +45,13 @@ class SettingsProvider extends ChangeNotifier {
|
|||||||
_settings = Settings.fromJson(jsonDecode(_storage.getString(_fieldName)!));
|
_settings = Settings.fromJson(jsonDecode(_storage.getString(_fieldName)!));
|
||||||
} catch(exception) {
|
} catch(exception) {
|
||||||
try {
|
try {
|
||||||
log("Settings were changed, trying to recover from old Settings: ${exception.toString()}");
|
log('Settings were changed, trying to recover from old Settings: ${exception.toString()}');
|
||||||
_settings = Settings.fromJson(_mergeSettings(jsonDecode(_storage.getString(_fieldName)!), DefaultSettings.get().toJson()));
|
_settings = Settings.fromJson(_mergeSettings(jsonDecode(_storage.getString(_fieldName)!), DefaultSettings.get().toJson()));
|
||||||
log("Settings recovered successfully: ${_settings.toJson().toString()}");
|
log('Settings recovered successfully: ${_settings.toJson().toString()}');
|
||||||
} catch(exception) {
|
} catch(exception) {
|
||||||
log("Settings are defective and not recoverable, using defaults: ${exception.toString()}");
|
log('Settings are defective and not recoverable, using defaults: ${exception.toString()}');
|
||||||
_settings = DefaultSettings.get();
|
_settings = DefaultSettings.get();
|
||||||
log("Settings were reset to defaults!");
|
log('Settings were reset to defaults!');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
15
lib/storage/devTools/devToolsSettings.dart
Normal file
15
lib/storage/devTools/devToolsSettings.dart
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
|
|
||||||
|
part 'devToolsSettings.g.dart';
|
||||||
|
|
||||||
|
@JsonSerializable()
|
||||||
|
class DevToolsSettings {
|
||||||
|
bool showPerformanceOverlay;
|
||||||
|
bool checkerboardOffscreenLayers;
|
||||||
|
bool checkerboardRasterCacheImages;
|
||||||
|
|
||||||
|
DevToolsSettings({required this.showPerformanceOverlay, required this.checkerboardOffscreenLayers, required this.checkerboardRasterCacheImages});
|
||||||
|
|
||||||
|
factory DevToolsSettings.fromJson(Map<String, dynamic> json) => _$DevToolsSettingsFromJson(json);
|
||||||
|
Map<String, dynamic> toJson() => _$DevToolsSettingsToJson(this);
|
||||||
|
}
|
22
lib/storage/devTools/devToolsSettings.g.dart
Normal file
22
lib/storage/devTools/devToolsSettings.g.dart
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
part of 'devToolsSettings.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// JsonSerializableGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
DevToolsSettings _$DevToolsSettingsFromJson(Map<String, dynamic> json) =>
|
||||||
|
DevToolsSettings(
|
||||||
|
showPerformanceOverlay: json['showPerformanceOverlay'] as bool,
|
||||||
|
checkerboardOffscreenLayers: json['checkerboardOffscreenLayers'] as bool,
|
||||||
|
checkerboardRasterCacheImages:
|
||||||
|
json['checkerboardRasterCacheImages'] as bool,
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> _$DevToolsSettingsToJson(DevToolsSettings instance) =>
|
||||||
|
<String, dynamic>{
|
||||||
|
'showPerformanceOverlay': instance.showPerformanceOverlay,
|
||||||
|
'checkerboardOffscreenLayers': instance.checkerboardOffscreenLayers,
|
||||||
|
'checkerboardRasterCacheImages': instance.checkerboardRasterCacheImages,
|
||||||
|
};
|
@ -4,13 +4,13 @@ class AppTheme {
|
|||||||
static ThemeModeDisplay getDisplayOptions(ThemeMode theme) {
|
static ThemeModeDisplay getDisplayOptions(ThemeMode theme) {
|
||||||
switch(theme) {
|
switch(theme) {
|
||||||
case ThemeMode.system:
|
case ThemeMode.system:
|
||||||
return ThemeModeDisplay(icon: Icons.auto_fix_high_outlined, displayName: "Systemvorgabe");
|
return ThemeModeDisplay(icon: Icons.auto_fix_high_outlined, displayName: 'Systemvorgabe');
|
||||||
|
|
||||||
case ThemeMode.light:
|
case ThemeMode.light:
|
||||||
return ThemeModeDisplay(icon: Icons.wb_sunny_outlined, displayName: "Hell");
|
return ThemeModeDisplay(icon: Icons.wb_sunny_outlined, displayName: 'Hell');
|
||||||
|
|
||||||
case ThemeMode.dark:
|
case ThemeMode.dark:
|
||||||
return ThemeModeDisplay(icon: Icons.dark_mode_outlined, displayName: "Dunkel");
|
return ThemeModeDisplay(icon: Icons.dark_mode_outlined, displayName: 'Dunkel');
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@ class _LoginState extends State<Login> {
|
|||||||
bool displayDisclaimerText = true;
|
bool displayDisclaimerText = true;
|
||||||
|
|
||||||
String? _checkInput(value){
|
String? _checkInput(value){
|
||||||
return (value ?? "").length == 0 ? "Eingabe erforderlich" : null;
|
return (value ?? '').length == 0 ? 'Eingabe erforderlich' : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<String?> _login(LoginData data) async {
|
Future<String?> _login(LoginData data) async {
|
||||||
@ -42,7 +42,7 @@ class _LoginState extends State<Login> {
|
|||||||
} catch(e) {
|
} catch(e) {
|
||||||
await AccountData().removeData();
|
await AccountData().removeData();
|
||||||
log(e.toString());
|
log(e.toString());
|
||||||
return "Benutzername oder Password falsch! (${e.toString()})";
|
return 'Benutzername oder Password falsch! (${e.toString()})';
|
||||||
}
|
}
|
||||||
|
|
||||||
await Future.delayed(const Duration(seconds: 1));
|
await Future.delayed(const Duration(seconds: 1));
|
||||||
@ -51,14 +51,14 @@ class _LoginState extends State<Login> {
|
|||||||
|
|
||||||
Future<String> _resetPassword(String name) {
|
Future<String> _resetPassword(String name) {
|
||||||
return Future.delayed(Duration.zero).then((_) {
|
return Future.delayed(Duration.zero).then((_) {
|
||||||
return "Diese Funktion steht nicht zur Verfügung!";
|
return 'Diese Funktion steht nicht zur Verfügung!';
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return FlutterLogin(
|
return FlutterLogin(
|
||||||
logo: Image.asset("assets/logo/icon.png").image,
|
logo: Image.asset('assets/logo/icon.png').image,
|
||||||
|
|
||||||
userValidator: _checkInput,
|
userValidator: _checkInput,
|
||||||
passwordValidator: _checkInput,
|
passwordValidator: _checkInput,
|
||||||
@ -84,9 +84,9 @@ class _LoginState extends State<Login> {
|
|||||||
),
|
),
|
||||||
|
|
||||||
messages: LoginMessages(
|
messages: LoginMessages(
|
||||||
loginButton: "Anmelden",
|
loginButton: 'Anmelden',
|
||||||
userHint: "Nutzername",
|
userHint: 'Nutzername',
|
||||||
passwordHint: "Passwort",
|
passwordHint: 'Passwort',
|
||||||
),
|
),
|
||||||
|
|
||||||
disableCustomPageTransformer: true,
|
disableCustomPageTransformer: true,
|
||||||
@ -97,15 +97,15 @@ class _LoginState extends State<Login> {
|
|||||||
child: Visibility(
|
child: Visibility(
|
||||||
visible: displayDisclaimerText,
|
visible: displayDisclaimerText,
|
||||||
child: const Text(
|
child: const Text(
|
||||||
"Dies ist ein Inoffizieller Nextcloud & Webuntis Client und wird nicht vom Marianum selbst betrieben.\nKeinerlei Gewähr für Vollständigkeit, Richtigkeit und Aktualität!",
|
'Dies ist ein Inoffizieller Nextcloud & Webuntis Client und wird nicht vom Marianum selbst betrieben.\nKeinerlei Gewähr für Vollständigkeit, Richtigkeit und Aktualität!',
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
footer: "Marianum Fulda - Die persönliche Schule",
|
footer: 'Marianum Fulda - Die persönliche Schule',
|
||||||
title: "Marianum Fulda",
|
title: 'Marianum Fulda',
|
||||||
|
|
||||||
userType: LoginUserType.name,
|
userType: LoginUserType.name,
|
||||||
);
|
);
|
||||||
|
@ -6,11 +6,13 @@ import 'package:flowder/flowder.dart';
|
|||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:jiffy/jiffy.dart';
|
import 'package:jiffy/jiffy.dart';
|
||||||
|
import '../../../widget/infoDialog.dart';
|
||||||
import 'package:nextcloud/nextcloud.dart';
|
import 'package:nextcloud/nextcloud.dart';
|
||||||
import 'package:path_provider/path_provider.dart';
|
import 'package:path_provider/path_provider.dart';
|
||||||
|
|
||||||
import '../../../api/marianumcloud/webdav/queries/listFiles/cacheableFile.dart';
|
import '../../../api/marianumcloud/webdav/queries/listFiles/cacheableFile.dart';
|
||||||
import '../../../api/marianumcloud/webdav/webdavApi.dart';
|
import '../../../api/marianumcloud/webdav/webdavApi.dart';
|
||||||
|
import '../../../model/endpointData.dart';
|
||||||
import '../../../widget/centeredLeading.dart';
|
import '../../../widget/centeredLeading.dart';
|
||||||
import '../../../widget/confirmDialog.dart';
|
import '../../../widget/confirmDialog.dart';
|
||||||
import '../../../widget/fileViewer.dart';
|
import '../../../widget/fileViewer.dart';
|
||||||
@ -27,7 +29,7 @@ class FileElement extends StatefulWidget {
|
|||||||
Directory paths = await getTemporaryDirectory();
|
Directory paths = await getTemporaryDirectory();
|
||||||
|
|
||||||
var encodedPath = Uri.encodeComponent(remotePath);
|
var encodedPath = Uri.encodeComponent(remotePath);
|
||||||
encodedPath = encodedPath.replaceAll("%2F", "/");
|
encodedPath = encodedPath.replaceAll('%2F', '/');
|
||||||
|
|
||||||
String local = paths.path + Platform.pathSeparator + name;
|
String local = paths.path + Platform.pathSeparator + name;
|
||||||
|
|
||||||
@ -42,7 +44,7 @@ class FileElement extends StatefulWidget {
|
|||||||
onDone: () {
|
onDone: () {
|
||||||
//Future<OpenResult> result = OpenFile.open(local); // TODO legacy - refactor: remove onDone parameter
|
//Future<OpenResult> result = OpenFile.open(local); // TODO legacy - refactor: remove onDone parameter
|
||||||
Navigator.of(context).push(MaterialPageRoute(builder: (context) => FileViewer(path: local)));
|
Navigator.of(context).push(MaterialPageRoute(builder: (context) => FileViewer(path: local)));
|
||||||
onDone(OpenResult(message: "File viewer opened", type: ResultType.done));
|
onDone(OpenResult(message: 'File viewer opened', type: ResultType.done));
|
||||||
// result.then((value) => {
|
// result.then((value) => {
|
||||||
// onDone(value)
|
// onDone(value)
|
||||||
// });
|
// });
|
||||||
@ -69,21 +71,21 @@ class _FileElementState extends State<FileElement> {
|
|||||||
children: [
|
children: [
|
||||||
Container(
|
Container(
|
||||||
margin: const EdgeInsets.only(right: 10),
|
margin: const EdgeInsets.only(right: 10),
|
||||||
child: const Text("Download:"),
|
child: const Text('Download:'),
|
||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: LinearProgressIndicator(value: percent/100),
|
child: LinearProgressIndicator(value: percent/100),
|
||||||
),
|
),
|
||||||
Container(
|
Container(
|
||||||
margin: const EdgeInsets.only(left: 10),
|
margin: const EdgeInsets.only(left: 10),
|
||||||
child: Text("${percent.round()}%"),
|
child: Text('${percent.round()}%'),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return widget.file.isDirectory
|
return widget.file.isDirectory
|
||||||
? Text("geändert ${Jiffy.parseFromDateTime(widget.file.modifiedAt ?? DateTime.now()).fromNow()}")
|
? Text('geändert ${Jiffy.parseFromDateTime(widget.file.modifiedAt ?? DateTime.now()).fromNow()}')
|
||||||
: Text("${filesize(widget.file.size)}, ${Jiffy.parseFromDateTime(widget.file.modifiedAt ?? DateTime.now()).fromNow()}");
|
: Text('${filesize(widget.file.size)}, ${Jiffy.parseFromDateTime(widget.file.modifiedAt ?? DateTime.now()).fromNow()}');
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -103,14 +105,18 @@ class _FileElementState extends State<FileElement> {
|
|||||||
},
|
},
|
||||||
));
|
));
|
||||||
} else {
|
} else {
|
||||||
|
if(EndpointData().getEndpointMode() == EndpointMode.stage) {
|
||||||
|
InfoDialog.show(context, 'Virtuelle Dateien im Staging Prozess können nicht heruntergeladen werden!');
|
||||||
|
return;
|
||||||
|
}
|
||||||
if(widget.file.currentlyDownloading) {
|
if(widget.file.currentlyDownloading) {
|
||||||
showDialog(
|
showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) => ConfirmDialog(
|
builder: (context) => ConfirmDialog(
|
||||||
title: "Download abbrechen?",
|
title: 'Download abbrechen?',
|
||||||
content: "Möchtest du den Download abbrechen?",
|
content: 'Möchtest du den Download abbrechen?',
|
||||||
cancelButton: "Nein",
|
cancelButton: 'Nein',
|
||||||
confirmButton: "Ja, Abbrechen",
|
confirmButton: 'Ja, Abbrechen',
|
||||||
onConfirm: () {
|
onConfirm: () {
|
||||||
downloadCore?.then((value) {
|
downloadCore?.then((value) {
|
||||||
if(!value.isCancelled) value.cancel();
|
if(!value.isCancelled) value.cancel();
|
||||||
@ -137,7 +143,7 @@ class _FileElementState extends State<FileElement> {
|
|||||||
if(result.type != ResultType.done) {
|
if(result.type != ResultType.done) {
|
||||||
showDialog(context: context, builder: (context) {
|
showDialog(context: context, builder: (context) {
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
title: const Text("Download"),
|
title: const Text('Download'),
|
||||||
content: Text(result.message),
|
content: Text(result.message),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@ -157,12 +163,12 @@ class _FileElementState extends State<FileElement> {
|
|||||||
children: [
|
children: [
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: const Icon(Icons.delete_outline),
|
leading: const Icon(Icons.delete_outline),
|
||||||
title: const Text("Löschen"),
|
title: const Text('Löschen'),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
showDialog(context: context, builder: (context) => ConfirmDialog(
|
showDialog(context: context, builder: (context) => ConfirmDialog(
|
||||||
title: "Element löschen?",
|
title: 'Element löschen?',
|
||||||
content: "Das Element wird unwiederruflich gelöscht.",
|
content: 'Das Element wird unwiederruflich gelöscht.',
|
||||||
onConfirm: () {
|
onConfirm: () {
|
||||||
WebdavApi.webdav
|
WebdavApi.webdav
|
||||||
.then((value) => value.delete(PathUri.parse(widget.file.path)))
|
.then((value) => value.delete(PathUri.parse(widget.file.path)))
|
||||||
@ -175,7 +181,7 @@ class _FileElementState extends State<FileElement> {
|
|||||||
visible: !kReleaseMode,
|
visible: !kReleaseMode,
|
||||||
child: ListTile(
|
child: ListTile(
|
||||||
leading: const Icon(Icons.share_outlined),
|
leading: const Icon(Icons.share_outlined),
|
||||||
title: const Text("Teilen"),
|
title: const Text('Teilen'),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
UnimplementedDialog.show(context);
|
UnimplementedDialog.show(context);
|
||||||
|
@ -43,8 +43,8 @@ class _FileUploadDialogState extends State<FileUploadDialog> {
|
|||||||
setState(() {
|
setState(() {
|
||||||
state = FileUploadState.checkConflict;
|
state = FileUploadState.checkConflict;
|
||||||
});
|
});
|
||||||
List<WebDavResponse> result = (await webdavClient.propfind(PathUri.parse(widget.remotePath.join("/")))).responses;
|
List<WebDavResponse> result = (await webdavClient.propfind(PathUri.parse(widget.remotePath.join('/')))).responses;
|
||||||
if(result.any((element) => element.href!.endsWith("/$targetFileName"))) {
|
if(result.any((element) => element.href!.endsWith('/$targetFileName'))) {
|
||||||
setState(() {
|
setState(() {
|
||||||
state = FileUploadState.conflict;
|
state = FileUploadState.conflict;
|
||||||
});
|
});
|
||||||
@ -57,7 +57,7 @@ class _FileUploadDialogState extends State<FileUploadDialog> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<HttpClientResponse> uploadTask = webdavClient.putFile(File(widget.localPath), FileStat.statSync(widget.localPath), PathUri.parse(fullRemotePath)); // TODO use onProgress from putFile
|
Future<HttpClientResponse> uploadTask = webdavClient.putFile(File(widget.localPath), FileStat.statSync(widget.localPath), PathUri.parse(fullRemotePath)); // TODO use onProgress from putFile
|
||||||
uploadTask.then((value) => Future<HttpClientResponse?>.value(value)).catchError((e) {
|
uploadTask.then(Future<HttpClientResponse?>.value).catchError((e) {
|
||||||
setState(() {
|
setState(() {
|
||||||
state = FileUploadState.error;
|
state = FileUploadState.error;
|
||||||
});
|
});
|
||||||
@ -67,7 +67,7 @@ class _FileUploadDialogState extends State<FileUploadDialog> {
|
|||||||
|
|
||||||
cancelableOperation = CancelableOperation<HttpClientResponse>.fromFuture(
|
cancelableOperation = CancelableOperation<HttpClientResponse>.fromFuture(
|
||||||
uploadTask,
|
uploadTask,
|
||||||
onCancel: () => log("Upload cancelled"),
|
onCancel: () => log('Upload cancelled'),
|
||||||
);
|
);
|
||||||
|
|
||||||
cancelableOperation!.then((value) {
|
cancelableOperation!.then((value) {
|
||||||
@ -88,7 +88,7 @@ class _FileUploadDialogState extends State<FileUploadDialog> {
|
|||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
targetFileName = widget.fileName;
|
targetFileName = widget.fileName;
|
||||||
remoteFolderName = widget.remotePath.isNotEmpty ? widget.remotePath.last : "/";
|
remoteFolderName = widget.remotePath.isNotEmpty ? widget.remotePath.last : '/';
|
||||||
fileNameController.text = widget.fileName;
|
fileNameController.text = widget.fileName;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -96,7 +96,7 @@ class _FileUploadDialogState extends State<FileUploadDialog> {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
if(state == FileUploadState.naming) {
|
if(state == FileUploadState.naming) {
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
title: const Text("Datei hochladen"),
|
title: const Text('Datei hochladen'),
|
||||||
content: Column(
|
content: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
@ -107,7 +107,7 @@ class _FileUploadDialogState extends State<FileUploadDialog> {
|
|||||||
},
|
},
|
||||||
autocorrect: false,
|
autocorrect: false,
|
||||||
decoration: const InputDecoration(
|
decoration: const InputDecoration(
|
||||||
labelText: "Dateiname",
|
labelText: 'Dateiname',
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@ -115,10 +115,10 @@ class _FileUploadDialogState extends State<FileUploadDialog> {
|
|||||||
actions: [
|
actions: [
|
||||||
TextButton(onPressed: () {
|
TextButton(onPressed: () {
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
}, child: const Text("Abbrechen")),
|
}, child: const Text('Abbrechen')),
|
||||||
TextButton(onPressed: () async {
|
TextButton(onPressed: () async {
|
||||||
upload();
|
upload();
|
||||||
}, child: const Text("Hochladen")),
|
}, child: const Text('Hochladen')),
|
||||||
],
|
],
|
||||||
|
|
||||||
);
|
);
|
||||||
@ -127,7 +127,7 @@ class _FileUploadDialogState extends State<FileUploadDialog> {
|
|||||||
if(state == FileUploadState.conflict) {
|
if(state == FileUploadState.conflict) {
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
icon: const Icon(Icons.error_outline),
|
icon: const Icon(Icons.error_outline),
|
||||||
title: const Text("Datei konflikt"),
|
title: const Text('Datei konflikt'),
|
||||||
content: Column(
|
content: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
@ -139,10 +139,10 @@ class _FileUploadDialogState extends State<FileUploadDialog> {
|
|||||||
setState(() {
|
setState(() {
|
||||||
state = FileUploadState.naming;
|
state = FileUploadState.naming;
|
||||||
});
|
});
|
||||||
}, child: const Text("Datei umbenennen")),
|
}, child: const Text('Datei umbenennen')),
|
||||||
TextButton(onPressed: () {
|
TextButton(onPressed: () {
|
||||||
upload(override: true);
|
upload(override: true);
|
||||||
}, child: const Text("Datei überschreiben")),
|
}, child: const Text('Datei überschreiben')),
|
||||||
],
|
],
|
||||||
|
|
||||||
);
|
);
|
||||||
@ -151,15 +151,15 @@ class _FileUploadDialogState extends State<FileUploadDialog> {
|
|||||||
if(state == FileUploadState.upload || state == FileUploadState.checkConflict) {
|
if(state == FileUploadState.upload || state == FileUploadState.checkConflict) {
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
icon: const Icon(Icons.upload),
|
icon: const Icon(Icons.upload),
|
||||||
title: const Text("Hochladen"),
|
title: const Text('Hochladen'),
|
||||||
content: Column(
|
content: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
Visibility(
|
Visibility(
|
||||||
visible: state == FileUploadState.upload,
|
visible: state == FileUploadState.upload,
|
||||||
replacement: const Text("Prüfe auf dateikonflikte..."),
|
replacement: const Text('Prüfe auf dateikonflikte...'),
|
||||||
child: const Text("Upload läuft!\nDies kann je nach Dateigröße einige Zeit dauern...", textAlign: TextAlign.center),
|
child: const Text('Upload läuft!\nDies kann je nach Dateigröße einige Zeit dauern...', textAlign: TextAlign.center),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 30),
|
const SizedBox(height: 30),
|
||||||
const CircularProgressIndicator()
|
const CircularProgressIndicator()
|
||||||
@ -183,7 +183,7 @@ class _FileUploadDialogState extends State<FileUploadDialog> {
|
|||||||
}
|
}
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
icon: const Icon(Icons.done),
|
icon: const Icon(Icons.done),
|
||||||
title: const Text("Upload fertig"),
|
title: const Text('Upload fertig'),
|
||||||
content: Column(
|
content: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
@ -193,7 +193,7 @@ class _FileUploadDialogState extends State<FileUploadDialog> {
|
|||||||
actions: [
|
actions: [
|
||||||
TextButton(onPressed: () {
|
TextButton(onPressed: () {
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
}, child: const Text("Fertig")),
|
}, child: const Text('Fertig')),
|
||||||
],
|
],
|
||||||
|
|
||||||
);
|
);
|
||||||
@ -202,23 +202,23 @@ class _FileUploadDialogState extends State<FileUploadDialog> {
|
|||||||
if(state == FileUploadState.error) {
|
if(state == FileUploadState.error) {
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
icon: const Icon(Icons.error_outline),
|
icon: const Icon(Icons.error_outline),
|
||||||
title: const Text("Fehler"),
|
title: const Text('Fehler'),
|
||||||
content: const Column(
|
content: const Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
Text("Es ist ein Fehler aufgetreten!", textAlign: TextAlign.center),
|
Text('Es ist ein Fehler aufgetreten!', textAlign: TextAlign.center),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(onPressed: () {
|
TextButton(onPressed: () {
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
}, child: const Text("Schlißen")),
|
}, child: const Text('Schlißen')),
|
||||||
],
|
],
|
||||||
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw UnimplementedError("Invalid state");
|
throw UnimplementedError('Invalid state');
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -43,17 +43,17 @@ enum SortOption {
|
|||||||
class SortOptions {
|
class SortOptions {
|
||||||
static Map<SortOption, BetterSortOption> options = {
|
static Map<SortOption, BetterSortOption> options = {
|
||||||
SortOption.name: BetterSortOption(
|
SortOption.name: BetterSortOption(
|
||||||
displayName: "Name",
|
displayName: 'Name',
|
||||||
icon: Icons.sort_by_alpha_outlined,
|
icon: Icons.sort_by_alpha_outlined,
|
||||||
compare: (CacheableFile a, CacheableFile b) => a.name.compareTo(b.name)
|
compare: (CacheableFile a, CacheableFile b) => a.name.compareTo(b.name)
|
||||||
),
|
),
|
||||||
SortOption.date: BetterSortOption(
|
SortOption.date: BetterSortOption(
|
||||||
displayName: "Datum",
|
displayName: 'Datum',
|
||||||
icon: Icons.history_outlined,
|
icon: Icons.history_outlined,
|
||||||
compare: (CacheableFile a, CacheableFile b) => a.modifiedAt!.compareTo(b.modifiedAt!)
|
compare: (CacheableFile a, CacheableFile b) => a.modifiedAt!.compareTo(b.modifiedAt!)
|
||||||
),
|
),
|
||||||
SortOption.size: BetterSortOption(
|
SortOption.size: BetterSortOption(
|
||||||
displayName: "Größe",
|
displayName: 'Größe',
|
||||||
icon: Icons.sd_card_outlined,
|
icon: Icons.sd_card_outlined,
|
||||||
compare: (CacheableFile a, CacheableFile b) {
|
compare: (CacheableFile a, CacheableFile b) {
|
||||||
if(a.isDirectory || b.isDirectory) return a.isDirectory ? 1 : 0;
|
if(a.isDirectory || b.isDirectory) return a.isDirectory ? 1 : 0;
|
||||||
@ -88,7 +88,7 @@ class _FilesState extends State<Files> {
|
|||||||
|
|
||||||
void _query() {
|
void _query() {
|
||||||
ListFilesCache(
|
ListFilesCache(
|
||||||
path: widget.path.isEmpty ? "/" : widget.path.join("/"),
|
path: widget.path.isEmpty ? '/' : widget.path.join('/'),
|
||||||
onUpdate: (ListFilesResponse d) {
|
onUpdate: (ListFilesResponse d) {
|
||||||
if(!context.mounted) return; // prevent setState when widget is possibly already disposed
|
if(!context.mounted) return; // prevent setState when widget is possibly already disposed
|
||||||
d.files.removeWhere((element) => element.name.isEmpty || element.name == widget.path.lastOrNull());
|
d.files.removeWhere((element) => element.name.isEmpty || element.name == widget.path.lastOrNull());
|
||||||
@ -109,7 +109,7 @@ class _FilesState extends State<Files> {
|
|||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text(widget.path.isNotEmpty ? widget.path.last : "Dateien"),
|
title: Text(widget.path.isNotEmpty ? widget.path.last : 'Dateien'),
|
||||||
actions: [
|
actions: [
|
||||||
// IconButton(
|
// IconButton(
|
||||||
// icon: const Icon(Icons.search),
|
// icon: const Icon(Icons.search),
|
||||||
@ -127,7 +127,7 @@ class _FilesState extends State<Files> {
|
|||||||
children: [
|
children: [
|
||||||
Icon(e ? Icons.text_rotate_up : Icons.text_rotation_down, color: Theme.of(context).colorScheme.onSurface),
|
Icon(e ? Icons.text_rotate_up : Icons.text_rotation_down, color: Theme.of(context).colorScheme.onSurface),
|
||||||
const SizedBox(width: 15),
|
const SizedBox(width: 15),
|
||||||
Text(e ? "Aufsteigend" : "Absteigend")
|
Text(e ? 'Aufsteigend' : 'Absteigend')
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
)).toList();
|
)).toList();
|
||||||
@ -164,7 +164,7 @@ class _FilesState extends State<Files> {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
floatingActionButton: FloatingActionButton(
|
floatingActionButton: FloatingActionButton(
|
||||||
heroTag: "uploadFile",
|
heroTag: 'uploadFile',
|
||||||
backgroundColor: Theme.of(context).primaryColor,
|
backgroundColor: Theme.of(context).primaryColor,
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
showDialog(context: context, builder: (context) {
|
showDialog(context: context, builder: (context) {
|
||||||
@ -172,29 +172,29 @@ class _FilesState extends State<Files> {
|
|||||||
children: [
|
children: [
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: const Icon(Icons.create_new_folder_outlined),
|
leading: const Icon(Icons.create_new_folder_outlined),
|
||||||
title: const Text("Ordner erstellen"),
|
title: const Text('Ordner erstellen'),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
showDialog(context: context, builder: (context) {
|
showDialog(context: context, builder: (context) {
|
||||||
var inputController = TextEditingController();
|
var inputController = TextEditingController();
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
title: const Text("Neuer Ordner"),
|
title: const Text('Neuer Ordner'),
|
||||||
content: TextField(
|
content: TextField(
|
||||||
controller: inputController,
|
controller: inputController,
|
||||||
decoration: const InputDecoration(
|
decoration: const InputDecoration(
|
||||||
labelText: "Name",
|
labelText: 'Name',
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(onPressed: () {
|
TextButton(onPressed: () {
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
}, child: const Text("Abbrechen")),
|
}, child: const Text('Abbrechen')),
|
||||||
TextButton(onPressed: () {
|
TextButton(onPressed: () {
|
||||||
WebdavApi.webdav.then((webdav) {
|
WebdavApi.webdav.then((webdav) {
|
||||||
webdav.mkcol(PathUri.parse("${widget.path.join("/")}/${inputController.text}")).then((value) => _query());
|
webdav.mkcol(PathUri.parse("${widget.path.join("/")}/${inputController.text}")).then((value) => _query());
|
||||||
});
|
});
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
}, child: const Text("Ordner erstellen")),
|
}, child: const Text('Ordner erstellen')),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@ -202,12 +202,10 @@ class _FilesState extends State<Files> {
|
|||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: const Icon(Icons.upload_file),
|
leading: const Icon(Icons.upload_file),
|
||||||
title: const Text("Aus Dateien hochladen"),
|
title: const Text('Aus Dateien hochladen'),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
context.loaderOverlay.show();
|
context.loaderOverlay.show();
|
||||||
FilePick.documentPick().then((value) {
|
FilePick.documentPick().then(mediaUpload);
|
||||||
mediaUpload(value);
|
|
||||||
});
|
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@ -215,7 +213,7 @@ class _FilesState extends State<Files> {
|
|||||||
visible: !Platform.isIOS,
|
visible: !Platform.isIOS,
|
||||||
child: ListTile(
|
child: ListTile(
|
||||||
leading: const Icon(Icons.add_a_photo_outlined),
|
leading: const Icon(Icons.add_a_photo_outlined),
|
||||||
title: const Text("Aus Gallerie hochladen"),
|
title: const Text('Aus Gallerie hochladen'),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
context.loaderOverlay.show();
|
context.loaderOverlay.show();
|
||||||
FilePick.galleryPick().then((value) {
|
FilePick.galleryPick().then((value) {
|
||||||
@ -231,13 +229,14 @@ class _FilesState extends State<Files> {
|
|||||||
},
|
},
|
||||||
child: const Icon(Icons.add),
|
child: const Icon(Icons.add),
|
||||||
),
|
),
|
||||||
body: data == null ? const LoadingSpinner() : data!.files.isEmpty ? const PlaceholderView(icon: Icons.folder_off_rounded, text: "Der Ordner ist leer") : LoaderOverlay(
|
body: data == null ? const LoadingSpinner() : data!.files.isEmpty ? const PlaceholderView(icon: Icons.folder_off_rounded, text: 'Der Ordner ist leer') : LoaderOverlay(
|
||||||
child: RefreshIndicator(
|
child: RefreshIndicator(
|
||||||
onRefresh: () {
|
onRefresh: () {
|
||||||
_query();
|
_query();
|
||||||
return Future.delayed(const Duration(seconds: 3));
|
return Future.delayed(const Duration(seconds: 3));
|
||||||
},
|
},
|
||||||
child: ListView.builder(
|
child: ListView.builder(
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
itemCount: files.length,
|
itemCount: files.length,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
CacheableFile file = files.toList()[index];
|
CacheableFile file = files.toList()[index];
|
||||||
@ -257,6 +256,6 @@ class _FilesState extends State<Files> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var fileName = path.split(Platform.pathSeparator).last;
|
var fileName = path.split(Platform.pathSeparator).last;
|
||||||
showDialog(context: context, builder: (context) => FileUploadDialog(localPath: path, remotePath: widget.path, fileName: fileName, onUploadFinished: () => _query()), barrierDismissible: false);
|
showDialog(context: context, builder: (context) => FileUploadDialog(localPath: path, remotePath: widget.path, fileName: fileName, onUploadFinished: _query), barrierDismissible: false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
78
lib/view/pages/more/feedback/feedbackDialog.dart
Normal file
78
lib/view/pages/more/feedback/feedbackDialog.dart
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:package_info/package_info.dart';
|
||||||
|
|
||||||
|
import '../../../../api/mhsl/server/feedback/addFeedback.dart';
|
||||||
|
import '../../../../api/mhsl/server/feedback/addFeedbackParams.dart';
|
||||||
|
import '../../../../model/accountData.dart';
|
||||||
|
import '../../../../widget/infoDialog.dart';
|
||||||
|
|
||||||
|
class FeedbackDialog extends StatefulWidget {
|
||||||
|
const FeedbackDialog({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<FeedbackDialog> createState() => _FeedbackDialogState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _FeedbackDialogState extends State<FeedbackDialog> {
|
||||||
|
final TextEditingController _feedbackInput = TextEditingController();
|
||||||
|
String? _error;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return AlertDialog(
|
||||||
|
|
||||||
|
title: const Text('Feedback'),
|
||||||
|
content: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
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 SizedBox(height: 10),
|
||||||
|
TextField(
|
||||||
|
controller: _feedbackInput,
|
||||||
|
autofocus: true,
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
border: OutlineInputBorder(),
|
||||||
|
label: Text('Feedback und Verbesserungen')
|
||||||
|
),
|
||||||
|
// style: TextStyle(),
|
||||||
|
// expands: true,
|
||||||
|
minLines: 3,
|
||||||
|
maxLines: 5,
|
||||||
|
),
|
||||||
|
Visibility(
|
||||||
|
visible: _error != null,
|
||||||
|
child: Text('Senden fehlgeschlagen: $_error', style: const TextStyle(color: Colors.red))
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
actions: [
|
||||||
|
TextButton(onPressed: () => Navigator.of(context).pop(), child: const Text('Abbrechen')),
|
||||||
|
TextButton(
|
||||||
|
onPressed: () async {
|
||||||
|
AddFeedback(
|
||||||
|
AddFeedbackParams(
|
||||||
|
user: AccountData().getUserSecret(),
|
||||||
|
feedback: _feedbackInput.text,
|
||||||
|
appVersion: int.parse((await PackageInfo.fromPlatform()).buildNumber)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.run()
|
||||||
|
.then((value) {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
InfoDialog.show(context, 'Danke für dein Feedback!');
|
||||||
|
})
|
||||||
|
.catchError((error, trace) {
|
||||||
|
setState(() {
|
||||||
|
_error = error.toString();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
child: const Text('Senden'),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -1,99 +0,0 @@
|
|||||||
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
|
|
||||||
import '../../../../theming/darkAppTheme.dart';
|
|
||||||
import '../../../../widget/loadingSpinner.dart';
|
|
||||||
|
|
||||||
class FeedbackForm extends StatefulWidget {
|
|
||||||
final Future<void> Function(String, {Map<String, dynamic>? extras}) callback;
|
|
||||||
final ScrollController? scrollController;
|
|
||||||
const FeedbackForm({required this.scrollController, required this.callback, super.key});
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<FeedbackForm> createState() => _FeedbackFormState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _FeedbackFormState extends State<FeedbackForm> {
|
|
||||||
final TextEditingController _feedbackInput = TextEditingController();
|
|
||||||
bool _textFieldEmpty = false;
|
|
||||||
bool _isSending = false;
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
_feedbackInput.addListener(() {
|
|
||||||
setState(() {
|
|
||||||
_textFieldEmpty = _feedbackInput.text.isEmpty;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Theme(
|
|
||||||
data: DarkAppTheme.theme,
|
|
||||||
child: Visibility(
|
|
||||||
visible: !_isSending,
|
|
||||||
replacement: const LoadingSpinner(infoText: "Daten werden ermittelt"),
|
|
||||||
child: SingleChildScrollView(
|
|
||||||
controller: widget.scrollController,
|
|
||||||
padding: const EdgeInsets.all(20),
|
|
||||||
child: Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
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: InputDecoration(
|
|
||||||
border: const OutlineInputBorder(),
|
|
||||||
label: const Text("Dein Feedback"),
|
|
||||||
errorText: _textFieldEmpty ? "Bitte gib eine Beschreibung an" : null
|
|
||||||
),
|
|
||||||
minLines: 1,
|
|
||||||
maxLines: 2,
|
|
||||||
),
|
|
||||||
Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.end,
|
|
||||||
children: [
|
|
||||||
TextButton(
|
|
||||||
onPressed: () async {
|
|
||||||
if(_isSending) return;
|
|
||||||
if(_feedbackInput.text.isEmpty) {
|
|
||||||
setState(() {
|
|
||||||
_textFieldEmpty = true;
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
setState(() {
|
|
||||||
_isSending = true;
|
|
||||||
});
|
|
||||||
|
|
||||||
widget.callback(_feedbackInput.text);
|
|
||||||
},
|
|
||||||
child: const Text("Senden"),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: 40),
|
|
||||||
const Center(
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
"Feedback, mal süß wie Kuchen, mal sauer wie Gurken, doch immer ein Schlüssel fürs Wachsen und Lernen.",
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
),
|
|
||||||
SizedBox(height: 10),
|
|
||||||
Icon(Icons.emoji_objects_outlined)
|
|
||||||
],
|
|
||||||
)
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,32 +0,0 @@
|
|||||||
import 'dart:convert';
|
|
||||||
|
|
||||||
import 'package:feedback/feedback.dart';
|
|
||||||
import 'package:flutter/cupertino.dart';
|
|
||||||
import 'package:flutter/foundation.dart';
|
|
||||||
import 'package:loader_overlay/loader_overlay.dart';
|
|
||||||
import 'package:package_info/package_info.dart';
|
|
||||||
|
|
||||||
import '../../../../api/mhsl/server/feedback/addFeedback.dart';
|
|
||||||
import '../../../../api/mhsl/server/feedback/addFeedbackParams.dart';
|
|
||||||
import '../../../../model/accountData.dart';
|
|
||||||
import '../../../../widget/infoDialog.dart';
|
|
||||||
|
|
||||||
class FeedbackSender {
|
|
||||||
static send(BuildContext context, UserFeedback feedback) async {
|
|
||||||
BetterFeedback.of(context).hide();
|
|
||||||
context.loaderOverlay.show();
|
|
||||||
AddFeedbackParams params = AddFeedbackParams(
|
|
||||||
user: AccountData().getUserSecret(),
|
|
||||||
feedback: feedback.text,
|
|
||||||
screenshot: await compute((message) => base64Encode(message), feedback.screenshot),
|
|
||||||
appVersion: int.parse((await PackageInfo.fromPlatform()).buildNumber)
|
|
||||||
);
|
|
||||||
AddFeedback(params).run().then((value) {
|
|
||||||
InfoDialog.show(context, "Danke für dein Feedback!");
|
|
||||||
context.loaderOverlay.hide();
|
|
||||||
}).catchError((error, trace) {
|
|
||||||
InfoDialog.show(context, error.toString());
|
|
||||||
context.loaderOverlay.hide();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
@ -24,7 +24,7 @@ class _GradeAverageState extends State<GradeAverage> {
|
|||||||
|
|
||||||
String getGradeDisplay(int grade) {
|
String getGradeDisplay(int grade) {
|
||||||
if(gradeSystem) {
|
if(gradeSystem) {
|
||||||
return "Note $grade";
|
return 'Note $grade';
|
||||||
} else {
|
} else {
|
||||||
return "$grade Punkt${grade > 1 ? "e" : ""}";
|
return "$grade Punkt${grade > 1 ? "e" : ""}";
|
||||||
}
|
}
|
||||||
@ -48,22 +48,22 @@ class _GradeAverageState extends State<GradeAverage> {
|
|||||||
if(!settings.val().gradeAveragesSettings.askedForPreferredGradeSystem) {
|
if(!settings.val().gradeAveragesSettings.askedForPreferredGradeSystem) {
|
||||||
settings.val(write: true).gradeAveragesSettings.askedForPreferredGradeSystem = true;
|
settings.val(write: true).gradeAveragesSettings.askedForPreferredGradeSystem = true;
|
||||||
showDialog(context: context, builder: (context) => AlertDialog(
|
showDialog(context: context, builder: (context) => AlertDialog(
|
||||||
title: const Text("Notensystem"),
|
title: const Text('Notensystem'),
|
||||||
content: const Text("Wähle dein bevorzugtes Schulnotensystem"),
|
content: const Text('Wähle dein bevorzugtes Schulnotensystem'),
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
switchSystem(true);
|
switchSystem(true);
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
},
|
},
|
||||||
child: const Text("Realschule"),
|
child: const Text('Realschule'),
|
||||||
),
|
),
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
switchSystem(false);
|
switchSystem(false);
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
},
|
},
|
||||||
child: const Text("Oberstufe"),
|
child: const Text('Oberstufe'),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
));
|
));
|
||||||
@ -81,7 +81,7 @@ class _GradeAverageState extends State<GradeAverage> {
|
|||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: const Text("Notendurschnittsrechner"),
|
title: const Text('Notendurschnittsrechner'),
|
||||||
actions: [
|
actions: [
|
||||||
Visibility(
|
Visibility(
|
||||||
visible: grades.isNotEmpty,
|
visible: grades.isNotEmpty,
|
||||||
@ -89,9 +89,9 @@ class _GradeAverageState extends State<GradeAverage> {
|
|||||||
showDialog(
|
showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) => ConfirmDialog(
|
builder: (context) => ConfirmDialog(
|
||||||
title: "Zurücksetzen?",
|
title: 'Zurücksetzen?',
|
||||||
content: "Alle Einträge werden entfernt.",
|
content: 'Alle Einträge werden entfernt.',
|
||||||
confirmButton: "Zurücksetzen",
|
confirmButton: 'Zurücksetzen',
|
||||||
onConfirm: () {
|
onConfirm: () {
|
||||||
grades.clear();
|
grades.clear();
|
||||||
setState(() {});
|
setState(() {});
|
||||||
@ -109,7 +109,7 @@ class _GradeAverageState extends State<GradeAverage> {
|
|||||||
children: [
|
children: [
|
||||||
Icon(e ? Icons.calculate_outlined : Icons.school_outlined, color: Theme.of(context).colorScheme.onSurface),
|
Icon(e ? Icons.calculate_outlined : Icons.school_outlined, color: Theme.of(context).colorScheme.onSurface),
|
||||||
const SizedBox(width: 15),
|
const SizedBox(width: 15),
|
||||||
Text(e ? "Notensystem" : "Punktesystem"),
|
Text(e ? 'Notensystem' : 'Punktesystem'),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
)).toList(),
|
)).toList(),
|
||||||
@ -120,9 +120,9 @@ class _GradeAverageState extends State<GradeAverage> {
|
|||||||
showDialog(
|
showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) => ConfirmDialog(
|
builder: (context) => ConfirmDialog(
|
||||||
title: "Notensystem wechseln",
|
title: 'Notensystem wechseln',
|
||||||
content: "Beim wechsel des Notensystems werden alle Einträge zurückgesetzt.",
|
content: 'Beim wechsel des Notensystems werden alle Einträge zurückgesetzt.',
|
||||||
confirmButton: "Fortfahren",
|
confirmButton: 'Fortfahren',
|
||||||
onConfirm: () => switchSystem(e),
|
onConfirm: () => switchSystem(e),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@ -142,7 +142,7 @@ class _GradeAverageState extends State<GradeAverage> {
|
|||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
const Divider(),
|
const Divider(),
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
Text(gradeSystem ? "Wähle unten die Anzahl deiner jewiligen Noten aus" : "Wähle unten die Anzahl deiner jeweiligen Punkte aus"),
|
Text(gradeSystem ? 'Wähle unten die Anzahl deiner jewiligen Noten aus' : 'Wähle unten die Anzahl deiner jeweiligen Punkte aus'),
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: ListView.builder(
|
child: ListView.builder(
|
||||||
@ -169,7 +169,7 @@ class _GradeAverageState extends State<GradeAverage> {
|
|||||||
icon: const Icon(Icons.remove),
|
icon: const Icon(Icons.remove),
|
||||||
color: Theme.of(context).colorScheme.onSurface,
|
color: Theme.of(context).colorScheme.onSurface,
|
||||||
),
|
),
|
||||||
Text("${grades.where(isThis).length}", style: const TextStyle(fontSize: 15, fontWeight: FontWeight.bold)),
|
Text('${grades.where(isThis).length}', style: const TextStyle(fontSize: 15, fontWeight: FontWeight.bold)),
|
||||||
IconButton(
|
IconButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
setState(() {
|
setState(() {
|
||||||
|
@ -42,23 +42,23 @@ class _HolidaysState extends State<Holidays> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
String parseString(String enDate) {
|
String parseString(String enDate) {
|
||||||
return Jiffy.parse(enDate).format(pattern: "dd.MM.yyyy");
|
return Jiffy.parse(enDate).format(pattern: 'dd.MM.yyyy');
|
||||||
}
|
}
|
||||||
|
|
||||||
void showDisclaimer() {
|
void showDisclaimer() {
|
||||||
showDialog(context: context, builder: (context) {
|
showDialog(context: context, builder: (context) {
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
title: const Text("Richtigkeit und Bereitstellung der Daten"),
|
title: const Text('Richtigkeit und Bereitstellung der Daten'),
|
||||||
content: Column(
|
content: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
const Text(""
|
const Text(''
|
||||||
"Sämtliche Datumsangaben sind ohne Gewähr.\n"
|
'Sämtliche Datumsangaben sind ohne Gewähr.\n'
|
||||||
"Ich übernehme weder Verantwortung für die Richtigkeit der Daten noch hafte ich für wirtschaftliche Schäden die aus der Verwendung dieser Daten entstehen können.\n\n"
|
'Ich übernehme weder Verantwortung für die Richtigkeit der Daten noch hafte ich für wirtschaftliche Schäden die aus der Verwendung dieser Daten entstehen können.\n\n'
|
||||||
"Die Daten stammen von https://ferien-api.de/"),
|
'Die Daten stammen von https://ferien-api.de/'),
|
||||||
const SizedBox(height: 30),
|
const SizedBox(height: 30),
|
||||||
ListTile(
|
ListTile(
|
||||||
title: const Text("Diese Meldung nicht mehr anzeigen"),
|
title: const Text('Diese Meldung nicht mehr anzeigen'),
|
||||||
trailing: Checkbox(
|
trailing: Checkbox(
|
||||||
value: settings.val().holidaysSettings.dismissedDisclaimer,
|
value: settings.val().holidaysSettings.dismissedDisclaimer,
|
||||||
onChanged: (value) => settings.val(write: true).holidaysSettings.dismissedDisclaimer = value!,
|
onChanged: (value) => settings.val(write: true).holidaysSettings.dismissedDisclaimer = value!,
|
||||||
@ -67,8 +67,8 @@ class _HolidaysState extends State<Holidays> {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(child: const Text("ferien-api.de besuchen"), onPressed: () => ConfirmDialog.openBrowser(context, "https://ferien-api.de/")),
|
TextButton(child: const Text('ferien-api.de besuchen'), onPressed: () => ConfirmDialog.openBrowser(context, 'https://ferien-api.de/')),
|
||||||
TextButton(child: const Text("Okay"), onPressed: () => Navigator.of(context).pop()),
|
TextButton(child: const Text('Okay'), onPressed: () => Navigator.of(context).pop()),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@ -78,11 +78,11 @@ class _HolidaysState extends State<Holidays> {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: const Text("Schulferien in Hessen"),
|
title: const Text('Schulferien in Hessen'),
|
||||||
actions: [
|
actions: [
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: const Icon(Icons.warning_amber_outlined),
|
icon: const Icon(Icons.warning_amber_outlined),
|
||||||
onPressed: () => showDisclaimer(),
|
onPressed: showDisclaimer,
|
||||||
),
|
),
|
||||||
PopupMenuButton<bool>(
|
PopupMenuButton<bool>(
|
||||||
initialValue: settings.val().holidaysSettings.showPastEvents,
|
initialValue: settings.val().holidaysSettings.showPastEvents,
|
||||||
@ -95,7 +95,7 @@ class _HolidaysState extends State<Holidays> {
|
|||||||
children: [
|
children: [
|
||||||
Icon(e ? Icons.history_outlined : Icons.history_toggle_off_outlined, color: Theme.of(context).colorScheme.onSurface),
|
Icon(e ? Icons.history_outlined : Icons.history_toggle_off_outlined, color: Theme.of(context).colorScheme.onSurface),
|
||||||
const SizedBox(width: 15),
|
const SizedBox(width: 15),
|
||||||
Text(e ? "Alle anzeigen" : "Nur zukünftige anzeigen")
|
Text(e ? 'Alle anzeigen' : 'Nur zukünftige anzeigen')
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
)).toList();
|
)).toList();
|
||||||
@ -115,19 +115,19 @@ class _HolidaysState extends State<Holidays> {
|
|||||||
List<GetHolidaysResponseObject> holidays = value.getHolidaysResponse.data;
|
List<GetHolidaysResponseObject> holidays = value.getHolidaysResponse.data;
|
||||||
if(!showPastEvents) holidays = holidays.where((element) => DateTime.parse(element.end).isAfter(DateTime.now())).toList();
|
if(!showPastEvents) holidays = holidays.where((element) => DateTime.parse(element.end).isAfter(DateTime.now())).toList();
|
||||||
|
|
||||||
if(holidays.isEmpty) return const PlaceholderView(icon: Icons.search_off, text: "Es wurden keine Ferieneinträge gefunden!");
|
if(holidays.isEmpty) return const PlaceholderView(icon: Icons.search_off, text: 'Es wurden keine Ferieneinträge gefunden!');
|
||||||
|
|
||||||
return ListView.builder(
|
return ListView.builder(
|
||||||
itemCount: holidays.length,
|
itemCount: holidays.length,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
GetHolidaysResponseObject holiday = holidays[index];
|
GetHolidaysResponseObject holiday = holidays[index];
|
||||||
String holidayType = holiday.name.split(" ").first.capitalize();
|
String holidayType = holiday.name.split(' ').first.capitalize();
|
||||||
return ListTile(
|
return ListTile(
|
||||||
leading: const CenteredLeading(Icon(Icons.calendar_month)),
|
leading: const CenteredLeading(Icon(Icons.calendar_month)),
|
||||||
title: Text("$holidayType ab ${parseString(holiday.start)}"),
|
title: Text('$holidayType ab ${parseString(holiday.start)}'),
|
||||||
subtitle: Text("bis ${parseString(holiday.end)}"),
|
subtitle: Text('bis ${parseString(holiday.end)}'),
|
||||||
onTap: () => showDialog(context: context, builder: (context) => SimpleDialog(
|
onTap: () => showDialog(context: context, builder: (context) => SimpleDialog(
|
||||||
title: Text("$holidayType ${holiday.year} in Hessen"),
|
title: Text('$holidayType ${holiday.year} in Hessen'),
|
||||||
children: [
|
children: [
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: const CenteredLeading(Icon(Icons.signpost_outlined)),
|
leading: const CenteredLeading(Icon(Icons.signpost_outlined)),
|
||||||
@ -136,11 +136,11 @@ class _HolidaysState extends State<Holidays> {
|
|||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: const Icon(Icons.arrow_forward),
|
leading: const Icon(Icons.arrow_forward),
|
||||||
title: Text("vom ${parseString(holiday.start)}"),
|
title: Text('vom ${parseString(holiday.start)}'),
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: const Icon(Icons.arrow_back),
|
leading: const Icon(Icons.arrow_back),
|
||||||
title: Text("bis zum ${parseString(holiday.end)}"),
|
title: Text('bis zum ${parseString(holiday.end)}'),
|
||||||
),
|
),
|
||||||
Visibility(
|
Visibility(
|
||||||
visible: !DateTime.parse(holiday.start).difference(DateTime.now()).isNegative,
|
visible: !DateTime.parse(holiday.start).difference(DateTime.now()).isNegative,
|
||||||
|
@ -27,7 +27,7 @@ class _MessageState extends State<Message> {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: const Text("Marianum Message"),
|
title: const Text('Marianum Message'),
|
||||||
),
|
),
|
||||||
body: Consumer<MessageProps>(builder: (context, value, child) {
|
body: Consumer<MessageProps>(builder: (context, value, child) {
|
||||||
if(value.primaryLoading()) return const LoadingSpinner();
|
if(value.primaryLoading()) return const LoadingSpinner();
|
||||||
@ -43,7 +43,7 @@ class _MessageState extends State<Message> {
|
|||||||
children: [Icon(Icons.newspaper)],
|
children: [Icon(Icons.newspaper)],
|
||||||
),
|
),
|
||||||
title: Text(message.name, overflow: TextOverflow.ellipsis),
|
title: Text(message.name, overflow: TextOverflow.ellipsis),
|
||||||
subtitle: Text("vom ${message.date}"),
|
subtitle: Text('vom ${message.date}'),
|
||||||
trailing: const Icon(Icons.arrow_right),
|
trailing: const Icon(Icons.arrow_right),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navigator.push(context, MaterialPageRoute(builder: (context) => MessageView(basePath: value.getMessagesResponse.base, message: message)));
|
Navigator.push(context, MaterialPageRoute(builder: (context) => MessageView(basePath: value.getMessagesResponse.base, message: message)));
|
||||||
|
@ -29,12 +29,12 @@ class _MessageViewState extends State<MessageView> {
|
|||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
showDialog(context: context, builder: (context) {
|
showDialog(context: context, builder: (context) {
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
title: const Text("Fehler beim öffnen"),
|
title: const Text('Fehler beim öffnen'),
|
||||||
content: Text("Dokument '${widget.message.name}' konnte nicht geladen werden:\n${e.description}"),
|
content: Text("Dokument '${widget.message.name}' konnte nicht geladen werden:\n${e.description}"),
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(onPressed: () {
|
TextButton(onPressed: () {
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
}, child: const Text("Ok"))
|
}, child: const Text('Ok'))
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@ -43,9 +43,9 @@ class _MessageViewState extends State<MessageView> {
|
|||||||
showDialog(
|
showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) => ConfirmDialog(
|
builder: (context) => ConfirmDialog(
|
||||||
title: "Link öffnen",
|
title: 'Link öffnen',
|
||||||
content: "Möchtest du den folgenden Link öffnen?\n${e.uri}",
|
content: 'Möchtest du den folgenden Link öffnen?\n${e.uri}',
|
||||||
confirmButton: "Öffnen",
|
confirmButton: 'Öffnen',
|
||||||
onConfirm: () => launchUrl(Uri.parse(e.uri), mode: LaunchMode.externalApplication),
|
onConfirm: () => launchUrl(Uri.parse(e.uri), mode: LaunchMode.externalApplication),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -8,10 +8,10 @@ class Roomplan extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: const Text("Raumplan"),
|
title: const Text('Raumplan'),
|
||||||
),
|
),
|
||||||
body: PhotoView(
|
body: PhotoView(
|
||||||
imageProvider: Image.asset("assets/img/raumplan.jpg").image,
|
imageProvider: Image.asset('assets/img/raumplan.jpg').image,
|
||||||
minScale: 0.5,
|
minScale: 0.5,
|
||||||
maxScale: 2.0,
|
maxScale: 2.0,
|
||||||
backgroundDecoration: BoxDecoration(color: Theme.of(context).colorScheme.background),
|
backgroundDecoration: BoxDecoration(color: Theme.of(context).colorScheme.background),
|
||||||
|
@ -16,18 +16,18 @@ class _QrShareViewState extends State<QrShareView> {
|
|||||||
length: 2,
|
length: 2,
|
||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: const Text("Teile die App"),
|
title: const Text('Teile die App'),
|
||||||
bottom: const TabBar(
|
bottom: const TabBar(
|
||||||
tabs: [
|
tabs: [
|
||||||
Tab(icon: Icon(Icons.android_outlined), text: "Android"),
|
Tab(icon: Icon(Icons.android_outlined), text: 'Android'),
|
||||||
Tab(icon: Icon(Icons.apple_outlined), text: "iOS & iPadOS"),
|
Tab(icon: Icon(Icons.apple_outlined), text: 'iOS & iPadOS'),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
body: const TabBarView(
|
body: const TabBarView(
|
||||||
children: [
|
children: [
|
||||||
AppSharePlatformView("Für Android", "https://play.google.com/store/apps/details?id=eu.mhsl.marianum.mobile.client"),
|
AppSharePlatformView('Für Android', 'https://play.google.com/store/apps/details?id=eu.mhsl.marianum.mobile.client'),
|
||||||
AppSharePlatformView("Für iOS & iPad", "https://apps.apple.com/us/app/marianum-fulda/id6458789560"),
|
AppSharePlatformView('Für iOS & iPad', 'https://apps.apple.com/us/app/marianum-fulda/id6458789560'),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:loader_overlay/loader_overlay.dart';
|
|
||||||
import 'package:share_plus/share_plus.dart';
|
import 'package:share_plus/share_plus.dart';
|
||||||
|
|
||||||
import '../../../../widget/sharePositionOrigin.dart';
|
import '../../../../widget/sharePositionOrigin.dart';
|
||||||
@ -10,12 +9,11 @@ class SelectShareTypeDialog extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return LoaderOverlay(
|
return SimpleDialog(
|
||||||
child: SimpleDialog(
|
|
||||||
children: [
|
children: [
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: const Icon(Icons.qr_code_2_outlined),
|
leading: const Icon(Icons.qr_code_2_outlined),
|
||||||
title: const Text("Per QR-Code"),
|
title: const Text('Per QR-Code'),
|
||||||
trailing: const Icon(Icons.arrow_right),
|
trailing: const Icon(Icons.arrow_right),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navigator.of(context).push(MaterialPageRoute(builder: (context) => const QrShareView()));
|
Navigator.of(context).push(MaterialPageRoute(builder: (context) => const QrShareView()));
|
||||||
@ -23,21 +21,20 @@ class SelectShareTypeDialog extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: const Icon(Icons.link_outlined),
|
leading: const Icon(Icons.link_outlined),
|
||||||
title: const Text("Per Link teilen"),
|
title: const Text('Per Link teilen'),
|
||||||
trailing: const Icon(Icons.arrow_right),
|
trailing: const Icon(Icons.arrow_right),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Share.share(
|
Share.share(
|
||||||
sharePositionOrigin: SharePositionOrigin.get(context),
|
sharePositionOrigin: SharePositionOrigin.get(context),
|
||||||
subject: "App Teilen",
|
subject: 'App Teilen',
|
||||||
"Hol dir die für das Marianum maßgeschneiderte App:"
|
'Hol dir die für das Marianum maßgeschneiderte App:'
|
||||||
"\n\nAndroid: https://play.google.com/store/apps/details?id=eu.mhsl.marianum.mobile.client "
|
'\n\nAndroid: https://play.google.com/store/apps/details?id=eu.mhsl.marianum.mobile.client '
|
||||||
"\niOS: https://apps.apple.com/us/app/marianum-fulda/id6458789560 "
|
'\niOS: https://apps.apple.com/us/app/marianum-fulda/id6458789560 '
|
||||||
"\n\nViel Spaß!"
|
'\n\nViel Spaß!'
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,16 @@
|
|||||||
|
|
||||||
import 'package:feedback/feedback.dart';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:in_app_review/in_app_review.dart';
|
import 'package:in_app_review/in_app_review.dart';
|
||||||
|
import '../../extensions/renderNotNull.dart';
|
||||||
|
import 'package:persistent_bottom_nav_bar_v2/persistent_bottom_nav_bar_v2.dart';
|
||||||
|
|
||||||
import '../../widget/ListItem.dart';
|
import '../../widget/ListItem.dart';
|
||||||
import '../../widget/centeredLeading.dart';
|
import '../../widget/centeredLeading.dart';
|
||||||
import '../../widget/infoDialog.dart';
|
import '../../widget/infoDialog.dart';
|
||||||
import '../settings/settings.dart';
|
import '../settings/settings.dart';
|
||||||
import 'more/feedback/feedbackSender.dart';
|
import 'more/feedback/feedbackDialog.dart';
|
||||||
import 'more/gradeAverages/gradeAverage.dart';
|
import 'more/gradeAverages/gradeAverage.dart';
|
||||||
import 'more/holidays/holidays.dart';
|
import 'more/holidays/holidays.dart';
|
||||||
import 'more/message/message.dart';
|
import 'more/message/message.dart';
|
||||||
@ -21,21 +25,22 @@ class Overhang extends StatelessWidget {
|
|||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: const Text("Mehr"),
|
title: const Text('Mehr'),
|
||||||
actions: [
|
actions: [
|
||||||
IconButton(onPressed: () => Navigator.of(context).push(MaterialPageRoute(builder: (context) => const Settings())), icon: const Icon(Icons.settings))
|
IconButton(onPressed: () => pushScreen(context, screen: const Settings(), withNavBar: false), icon: const Icon(Icons.settings))
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
body: ListView(
|
body: ListView(
|
||||||
children: [
|
children: [
|
||||||
const ListItemNavigator(icon: Icons.newspaper, text: "Marianum Message", target: Message()),
|
const ListItemNavigator(icon: Icons.newspaper, text: 'Marianum Message', target: Message()),
|
||||||
const ListItemNavigator(icon: Icons.room, text: "Raumplan", target: Roomplan()),
|
const ListItemNavigator(icon: Icons.room, text: 'Raumplan', target: Roomplan()),
|
||||||
const ListItemNavigator(icon: Icons.calculate, text: "Notendurschnittsrechner", target: GradeAverage()),
|
const ListItemNavigator(icon: Icons.calculate, text: 'Notendurschnittsrechner', target: GradeAverage()),
|
||||||
const ListItemNavigator(icon: Icons.calendar_month, text: "Schulferien", target: Holidays()),
|
const ListItemNavigator(icon: Icons.calendar_month, text: 'Schulferien', target: Holidays()),
|
||||||
const Divider(),
|
const Divider(),
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: const Icon(Icons.share_outlined),
|
leading: const Icon(Icons.share_outlined),
|
||||||
title: const Text("Teile die App"),
|
title: const Text('Teile die App'),
|
||||||
|
subtitle: const Text('Mit Freunden und deiner Klasse teilen'),
|
||||||
trailing: const Icon(Icons.arrow_right),
|
trailing: const Icon(Icons.arrow_right),
|
||||||
onTap: () => showDialog(context: context, builder: (context) => const SelectShareTypeDialog())
|
onTap: () => showDialog(context: context, builder: (context) => const SelectShareTypeDialog())
|
||||||
),
|
),
|
||||||
@ -44,30 +49,32 @@ class Overhang extends StatelessWidget {
|
|||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
if(!snapshot.hasData) return const SizedBox.shrink();
|
if(!snapshot.hasData) return const SizedBox.shrink();
|
||||||
|
|
||||||
return Visibility(
|
String? getPlatformStoreName() {
|
||||||
visible: snapshot.requireData,
|
if(Platform.isAndroid) return 'Play store';
|
||||||
child: ListTile(
|
if(Platform.isIOS) return 'App store';
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ListTile(
|
||||||
leading: const CenteredLeading(Icon(Icons.star_rate_outlined)),
|
leading: const CenteredLeading(Icon(Icons.star_rate_outlined)),
|
||||||
title: const Text("App Bewerten"),
|
title: const Text('App bewerten'),
|
||||||
|
subtitle: getPlatformStoreName().wrapNullable((data) => Text('Im $data')),
|
||||||
trailing: const Icon(Icons.arrow_right),
|
trailing: const Icon(Icons.arrow_right),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
InAppReview.instance.openStoreListing(appStoreId: "6458789560").then(
|
InAppReview.instance.openStoreListing(appStoreId: '6458789560').then(
|
||||||
(value) => InfoDialog.show(context, "Vielen Dank!"),
|
(value) => InfoDialog.show(context, 'Vielen Dank!'),
|
||||||
onError: (error) => InfoDialog.show(context, error.toString())
|
onError: (error) => InfoDialog.show(context, error.toString())
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: const CenteredLeading(Icon(Icons.feedback_outlined)),
|
leading: const CenteredLeading(Icon(Icons.feedback_outlined)),
|
||||||
title: const Text("Du hast eine Idee?"),
|
title: const Text('Du hast eine Idee?'),
|
||||||
subtitle: const Text("Fehler und Verbessungsvorschläge"),
|
subtitle: const Text('Fehler und Verbessungsvorschläge'),
|
||||||
trailing: const Icon(Icons.arrow_right),
|
trailing: const Icon(Icons.arrow_right),
|
||||||
onTap: () {
|
onTap: () => showDialog(context: context, barrierDismissible: false, builder: (context) => const FeedbackDialog()),
|
||||||
BetterFeedback.of(context).show((UserFeedback feedback) => FeedbackSender.send(context, feedback));
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
@ -66,7 +66,7 @@ class _ChatInfoState extends State<ChatInfo> {
|
|||||||
if(participants != null) ...[
|
if(participants != null) ...[
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: const Icon(Icons.supervised_user_circle),
|
leading: const Icon(Icons.supervised_user_circle),
|
||||||
title: Text("${participants!.data.length} Teilnehmer"),
|
title: Text('${participants!.data.length} Teilnehmer'),
|
||||||
trailing: const Icon(Icons.arrow_right),
|
trailing: const Icon(Icons.arrow_right),
|
||||||
onTap: () => TalkNavigator.pushSplitView(context, ParticipantsListView(participants!)),
|
onTap: () => TalkNavigator.pushSplitView(context, ParticipantsListView(participants!)),
|
||||||
),
|
),
|
||||||
|
@ -16,7 +16,7 @@ class _ParticipantsListViewState extends State<ParticipantsListView> {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: const Text("Teilnehmende"),
|
title: const Text('Teilnehmende'),
|
||||||
),
|
),
|
||||||
body: ListView(
|
body: ListView(
|
||||||
children: widget.participantsResponse.data.map((participant) {
|
children: widget.participantsResponse.data.map((participant) {
|
||||||
|
@ -40,9 +40,9 @@ class _ChatListState extends State<ChatList> {
|
|||||||
|
|
||||||
ConfirmDialog(
|
ConfirmDialog(
|
||||||
icon: Icons.notifications_active_outlined,
|
icon: Icons.notifications_active_outlined,
|
||||||
title: "Benachrichtigungen aktivieren",
|
title: 'Benachrichtigungen aktivieren',
|
||||||
content: "Auf wunsch kannst du Push-Benachrichtigungen aktivieren. Deine Einstellungen kannst du jederzeit ändern.",
|
content: 'Auf wunsch kannst du Push-Benachrichtigungen aktivieren. Deine Einstellungen kannst du jederzeit ändern.',
|
||||||
confirmButton: "Weiter",
|
confirmButton: 'Weiter',
|
||||||
onConfirm: () {
|
onConfirm: () {
|
||||||
FirebaseMessaging.instance.requestPermission(
|
FirebaseMessaging.instance.requestPermission(
|
||||||
provisional: false
|
provisional: false
|
||||||
@ -53,7 +53,7 @@ class _ChatListState extends State<ChatList> {
|
|||||||
break;
|
break;
|
||||||
case AuthorizationStatus.denied:
|
case AuthorizationStatus.denied:
|
||||||
showDialog(context: context, builder: (context) => const AlertDialog(
|
showDialog(context: context, builder: (context) => const AlertDialog(
|
||||||
content: Text("Du kannst die Benachrichtigungen später jederzeit in den App-Einstellungen aktivieren."),
|
content: Text('Du kannst die Benachrichtigungen später jederzeit in den App-Einstellungen aktivieren.'),
|
||||||
));
|
));
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@ -79,7 +79,7 @@ class _ChatListState extends State<ChatList> {
|
|||||||
breakpoint: 1000,
|
breakpoint: 1000,
|
||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: const Text("Talk"),
|
title: const Text('Talk'),
|
||||||
actions: [
|
actions: [
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: const Icon(Icons.search),
|
icon: const Icon(Icons.search),
|
||||||
@ -91,16 +91,16 @@ class _ChatListState extends State<ChatList> {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
floatingActionButton: FloatingActionButton(
|
floatingActionButton: FloatingActionButton(
|
||||||
heroTag: "createChat",
|
heroTag: 'createChat',
|
||||||
backgroundColor: Theme.of(context).primaryColor,
|
backgroundColor: Theme.of(context).primaryColor,
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
showSearch(context: context, delegate: JoinChat()).then((username) {
|
showSearch(context: context, delegate: JoinChat()).then((username) {
|
||||||
if(username == null) return;
|
if(username == null) return;
|
||||||
|
|
||||||
ConfirmDialog(
|
ConfirmDialog(
|
||||||
title: "Chat starten",
|
title: 'Chat starten',
|
||||||
content: "Möchtest du einen Chat mit Nutzer '$username' starten?",
|
content: "Möchtest du einen Chat mit Nutzer '$username' starten?",
|
||||||
confirmButton: "Chat starten",
|
confirmButton: 'Chat starten',
|
||||||
onConfirm: () {
|
onConfirm: () {
|
||||||
CreateRoom(CreateRoomParams(
|
CreateRoom(CreateRoomParams(
|
||||||
roomType: 1,
|
roomType: 1,
|
||||||
@ -136,7 +136,10 @@ class _ChatListState extends State<ChatList> {
|
|||||||
_query(renew: true);
|
_query(renew: true);
|
||||||
return Future.delayed(const Duration(seconds: 3));
|
return Future.delayed(const Duration(seconds: 3));
|
||||||
},
|
},
|
||||||
child: ListView(children: chats),
|
child: ListView(
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
children: chats
|
||||||
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:loader_overlay/loader_overlay.dart';
|
import '../../../extensions/dateTime.dart';
|
||||||
import 'package:marianum_mobile/extensions/dateTime.dart';
|
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
import '../../../api/marianumcloud/talk/chat/getChatResponse.dart';
|
import '../../../api/marianumcloud/talk/chat/getChatResponse.dart';
|
||||||
@ -52,8 +51,8 @@ class _ChatViewState extends State<ChatView> {
|
|||||||
data.getChatResponse.sortByTimestamp().forEach((element) {
|
data.getChatResponse.sortByTimestamp().forEach((element) {
|
||||||
DateTime elementDate = DateTime.fromMillisecondsSinceEpoch(element.timestamp * 1000);
|
DateTime elementDate = DateTime.fromMillisecondsSinceEpoch(element.timestamp * 1000);
|
||||||
|
|
||||||
if(element.systemMessage.contains("reaction")) return;
|
if(element.systemMessage.contains('reaction')) return;
|
||||||
int commonRead = int.parse(data.getChatResponse.headers?['x-chat-last-common-read'] ?? "0");
|
int commonRead = int.parse(data.getChatResponse.headers?['x-chat-last-common-read'] ?? '0');
|
||||||
|
|
||||||
if(!elementDate.isSameDay(lastDate)) {
|
if(!elementDate.isSameDay(lastDate)) {
|
||||||
lastDate = elementDate;
|
lastDate = elementDate;
|
||||||
@ -82,8 +81,8 @@ class _ChatViewState extends State<ChatView> {
|
|||||||
context: context,
|
context: context,
|
||||||
isSender: false,
|
isSender: false,
|
||||||
bubbleData: GetChatResponseObject.getTextDummy(
|
bubbleData: GetChatResponseObject.getTextDummy(
|
||||||
"Zurzeit können in dieser App nur die letzten 200 vergangenen Nachrichten angezeigt werden. "
|
'Zurzeit können in dieser App nur die letzten 200 vergangenen Nachrichten angezeigt werden. '
|
||||||
"Um ältere Nachrichten abzurufen verwende die Webversion unter https://cloud.marianum-fulda.de"
|
'Um ältere Nachrichten abzurufen verwende die Webversion unter https://cloud.marianum-fulda.de'
|
||||||
),
|
),
|
||||||
chatData: widget.room,
|
chatData: widget.room,
|
||||||
refetch: _query,
|
refetch: _query,
|
||||||
@ -92,6 +91,7 @@ class _ChatViewState extends State<ChatView> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
|
backgroundColor: const Color(0xffefeae2),
|
||||||
appBar: ClickableAppBar(
|
appBar: ClickableAppBar(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
TalkNavigator.pushSplitView(context, ChatInfo(widget.room));
|
TalkNavigator.pushSplitView(context, ChatInfo(widget.room));
|
||||||
@ -111,14 +111,13 @@ class _ChatViewState extends State<ChatView> {
|
|||||||
body: Container(
|
body: Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
image: DecorationImage(
|
image: DecorationImage(
|
||||||
image: const AssetImage("assets/background/chat.png"),
|
image: const AssetImage('assets/background/chat.png'),
|
||||||
scale: 1.5,
|
scale: 1.5,
|
||||||
opacity: 1,
|
opacity: 1,
|
||||||
repeat: ImageRepeat.repeat,
|
repeat: ImageRepeat.repeat,
|
||||||
invertColors: AppTheme.isDarkMode(context)
|
invertColors: AppTheme.isDarkMode(context)
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
child: LoaderOverlay(
|
|
||||||
child: data.primaryLoading() ? const LoadingSpinner() : Column(
|
child: data.primaryLoading() ? const LoadingSpinner() : Column(
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
@ -130,11 +129,13 @@ class _ChatViewState extends State<ChatView> {
|
|||||||
),
|
),
|
||||||
Container(
|
Container(
|
||||||
color: Theme.of(context).colorScheme.background,
|
color: Theme.of(context).colorScheme.background,
|
||||||
child: SafeArea(child: ChatTextfield(widget.room.token)),
|
child: TalkNavigator.isSecondaryVisible(context)
|
||||||
|
? ChatTextfield(widget.room.token)
|
||||||
|
: SafeArea(child: ChatTextfield(widget.room.token)
|
||||||
|
),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
)
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -5,7 +5,7 @@ import 'package:flutter/foundation.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:jiffy/jiffy.dart';
|
import 'package:jiffy/jiffy.dart';
|
||||||
import 'package:marianum_mobile/extensions/text.dart';
|
import '../../../../extensions/text.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
import '../../../../api/marianumcloud/talk/chat/getChatResponse.dart';
|
import '../../../../api/marianumcloud/talk/chat/getChatResponse.dart';
|
||||||
@ -114,7 +114,7 @@ class _ChatBubbleState extends State<ChatBubble> {
|
|||||||
);
|
);
|
||||||
|
|
||||||
Text timeText = Text(
|
Text timeText = Text(
|
||||||
Jiffy.parseFromMillisecondsSinceEpoch(widget.bubbleData.timestamp * 1000).format(pattern: "HH:mm"),
|
Jiffy.parseFromMillisecondsSinceEpoch(widget.bubbleData.timestamp * 1000).format(pattern: 'HH:mm'),
|
||||||
textAlign: TextAlign.end,
|
textAlign: TextAlign.end,
|
||||||
style: TextStyle(color: widget.timeIconColor, fontSize: widget.timeIconSize),
|
style: TextStyle(color: widget.timeIconColor, fontSize: widget.timeIconSize),
|
||||||
);
|
);
|
||||||
@ -184,7 +184,7 @@ class _ChatBubbleState extends State<ChatBubble> {
|
|||||||
),
|
),
|
||||||
onLongPress: () {
|
onLongPress: () {
|
||||||
showDialog(context: context, builder: (context) {
|
showDialog(context: context, builder: (context) {
|
||||||
List<String> commonReactions = ["👍", "👎", "😆", "❤️", "👀", "🤔"];
|
List<String> commonReactions = ['👍', '👎', '😆', '❤️', '👀', '🤔'];
|
||||||
bool canReact = widget.bubbleData.messageType == GetRoomResponseObjectMessageType.comment;
|
bool canReact = widget.bubbleData.messageType == GetRoomResponseObjectMessageType.comment;
|
||||||
return SimpleDialog(
|
return SimpleDialog(
|
||||||
children: [
|
children: [
|
||||||
@ -222,7 +222,7 @@ class _ChatBubbleState extends State<ChatBubble> {
|
|||||||
visible: canReact,
|
visible: canReact,
|
||||||
child: ListTile(
|
child: ListTile(
|
||||||
leading: const Icon(Icons.add_reaction_outlined),
|
leading: const Icon(Icons.add_reaction_outlined),
|
||||||
title: const Text("Reaktionen"),
|
title: const Text('Reaktionen'),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navigator.of(context).push(MaterialPageRoute(builder: (context) => MessageReactions(
|
Navigator.of(context).push(MaterialPageRoute(builder: (context) => MessageReactions(
|
||||||
token: widget.chatData.token,
|
token: widget.chatData.token,
|
||||||
@ -235,7 +235,7 @@ class _ChatBubbleState extends State<ChatBubble> {
|
|||||||
visible: !message.containsFile,
|
visible: !message.containsFile,
|
||||||
child: ListTile(
|
child: ListTile(
|
||||||
leading: const Icon(Icons.copy),
|
leading: const Icon(Icons.copy),
|
||||||
title: const Text("Nachricht kopieren"),
|
title: const Text('Nachricht kopieren'),
|
||||||
onTap: () => {
|
onTap: () => {
|
||||||
Clipboard.setData(ClipboardData(text: widget.bubbleData.message)),
|
Clipboard.setData(ClipboardData(text: widget.bubbleData.message)),
|
||||||
Navigator.of(context).pop(),
|
Navigator.of(context).pop(),
|
||||||
@ -256,7 +256,7 @@ class _ChatBubbleState extends State<ChatBubble> {
|
|||||||
visible: widget.isSender && DateTime.fromMillisecondsSinceEpoch(widget.bubbleData.timestamp * 1000).add(const Duration(hours: 6)).isAfter(DateTime.now()),
|
visible: widget.isSender && DateTime.fromMillisecondsSinceEpoch(widget.bubbleData.timestamp * 1000).add(const Duration(hours: 6)).isAfter(DateTime.now()),
|
||||||
child: ListTile(
|
child: ListTile(
|
||||||
leading: const Icon(Icons.delete_outline),
|
leading: const Icon(Icons.delete_outline),
|
||||||
title: const Text("Nachricht löschen"),
|
title: const Text('Nachricht löschen'),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
DeleteMessage(widget.chatData.token, widget.bubbleData.id).run().then((value) {
|
DeleteMessage(widget.chatData.token, widget.bubbleData.id).run().then((value) {
|
||||||
Provider.of<ChatProps>(context, listen: false).run();
|
Provider.of<ChatProps>(context, listen: false).run();
|
||||||
@ -276,12 +276,12 @@ class _ChatBubbleState extends State<ChatBubble> {
|
|||||||
if(downloadProgress > 0) {
|
if(downloadProgress > 0) {
|
||||||
showDialog(context: context, builder: (context) {
|
showDialog(context: context, builder: (context) {
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
title: const Text("Download abbrechen?"),
|
title: const Text('Download abbrechen?'),
|
||||||
content: const Text("Möchtest du den Download abbrechen?"),
|
content: const Text('Möchtest du den Download abbrechen?'),
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(onPressed: () {
|
TextButton(onPressed: () {
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
}, child: const Text("Nein")),
|
}, child: const Text('Nein')),
|
||||||
TextButton(onPressed: () {
|
TextButton(onPressed: () {
|
||||||
downloadCore?.then((value) {
|
downloadCore?.then((value) {
|
||||||
if(!value.isCancelled) value.cancel();
|
if(!value.isCancelled) value.cancel();
|
||||||
@ -291,7 +291,7 @@ class _ChatBubbleState extends State<ChatBubble> {
|
|||||||
downloadProgress = 0;
|
downloadProgress = 0;
|
||||||
downloadCore = null;
|
downloadCore = null;
|
||||||
});
|
});
|
||||||
}, child: const Text("Ja, Abbrechen"))
|
}, child: const Text('Ja, Abbrechen'))
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@ -336,7 +336,7 @@ class _ChatBubbleState extends State<ChatBubble> {
|
|||||||
return Container(
|
return Container(
|
||||||
margin: const EdgeInsets.only(right: 2.5, left: 2.5),
|
margin: const EdgeInsets.only(right: 2.5, left: 2.5),
|
||||||
child: ActionChip(
|
child: ActionChip(
|
||||||
label: Text("${e.key} ${e.value}"),
|
label: Text('${e.key} ${e.value}'),
|
||||||
visualDensity: const VisualDensity(vertical: VisualDensity.minimumDensity, horizontal: VisualDensity.minimumDensity),
|
visualDensity: const VisualDensity(vertical: VisualDensity.minimumDensity, horizontal: VisualDensity.minimumDensity),
|
||||||
padding: EdgeInsets.zero,
|
padding: EdgeInsets.zero,
|
||||||
backgroundColor: hasSelfReacted ? Theme.of(context).primaryColor : null,
|
backgroundColor: hasSelfReacted ? Theme.of(context).primaryColor : null,
|
||||||
|
@ -14,14 +14,14 @@ class ChatMessage {
|
|||||||
Map<String, RichObjectString>? originalData;
|
Map<String, RichObjectString>? originalData;
|
||||||
|
|
||||||
RichObjectString? file;
|
RichObjectString? file;
|
||||||
String content = "";
|
String content = '';
|
||||||
|
|
||||||
bool get containsFile => file != null;
|
bool get containsFile => file != null;
|
||||||
|
|
||||||
ChatMessage({required this.originalMessage, this.originalData}) {
|
ChatMessage({required this.originalMessage, this.originalData}) {
|
||||||
if(originalData?.containsKey("file") ?? false) {
|
if(originalData?.containsKey('file') ?? false) {
|
||||||
file = originalData?['file'];
|
file = originalData?['file'];
|
||||||
content = file?.name ?? "Datei";
|
content = file?.name ?? 'Datei';
|
||||||
} else {
|
} else {
|
||||||
content = RichObjectStringProcessor.parseToString(originalMessage, originalData);
|
content = RichObjectStringProcessor.parseToString(originalMessage, originalData);
|
||||||
}
|
}
|
||||||
@ -56,7 +56,7 @@ class ChatMessage {
|
|||||||
fadeInDuration: Duration.zero,
|
fadeInDuration: Duration.zero,
|
||||||
fadeOutDuration: Duration.zero,
|
fadeOutDuration: Duration.zero,
|
||||||
errorListener: (value) {},
|
errorListener: (value) {},
|
||||||
imageUrl: "https://${AccountData().buildHttpAuthString()}@${EndpointData().nextcloud().full()}/index.php/core/preview?fileId=${file!.id}&x=100&y=-1&a=1",
|
imageUrl: 'https://${AccountData().buildHttpAuthString()}@${EndpointData().nextcloud().full()}/index.php/core/preview?fileId=${file!.id}&x=100&y=-1&a=1',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,9 +41,9 @@ class _ChatTextfieldState extends State<ChatTextfield> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
String filename = "${path.split("/").last.split(".").first}-${const Uuid().v4()}.${path.split(".").last}";
|
String filename = "${path.split("/").last.split(".").first}-${const Uuid().v4()}.${path.split(".").last}";
|
||||||
String shareFolder = "MarianumMobile";
|
String shareFolder = 'MarianumMobile';
|
||||||
WebdavApi.webdav.then((webdav) {
|
WebdavApi.webdav.then((webdav) {
|
||||||
webdav.mkcol(PathUri.parse("/$shareFolder"));
|
webdav.mkcol(PathUri.parse('/$shareFolder'));
|
||||||
});
|
});
|
||||||
|
|
||||||
showDialog(context: context, builder: (context) => FileUploadDialog(
|
showDialog(context: context, builder: (context) => FileUploadDialog(
|
||||||
@ -55,7 +55,7 @@ class _ChatTextfieldState extends State<ChatTextfield> {
|
|||||||
FileSharingApi().share(FileSharingApiParams(
|
FileSharingApi().share(FileSharingApiParams(
|
||||||
shareType: 10,
|
shareType: 10,
|
||||||
shareWith: widget.sendToToken,
|
shareWith: widget.sendToToken,
|
||||||
path: "$shareFolder/$filename",
|
path: '$shareFolder/$filename',
|
||||||
)).then((value) => _query());
|
)).then((value) => _query());
|
||||||
},
|
},
|
||||||
), barrierDismissible: false);
|
), barrierDismissible: false);
|
||||||
@ -77,7 +77,7 @@ class _ChatTextfieldState extends State<ChatTextfield> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
_textBoxController.text = settings.val().talkSettings.drafts[widget.sendToToken] ?? "";
|
_textBoxController.text = settings.val().talkSettings.drafts[widget.sendToToken] ?? '';
|
||||||
|
|
||||||
return Stack(
|
return Stack(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
@ -95,12 +95,10 @@ class _ChatTextfieldState extends State<ChatTextfield> {
|
|||||||
children: [
|
children: [
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: const Icon(Icons.file_open),
|
leading: const Icon(Icons.file_open),
|
||||||
title: const Text("Aus Dateien auswählen"),
|
title: const Text('Aus Dateien auswählen'),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
context.loaderOverlay.show();
|
context.loaderOverlay.show();
|
||||||
FilePick.documentPick().then((value) {
|
FilePick.documentPick().then(mediaUpload);
|
||||||
mediaUpload(value);
|
|
||||||
});
|
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@ -108,7 +106,7 @@ class _ChatTextfieldState extends State<ChatTextfield> {
|
|||||||
visible: !Platform.isIOS,
|
visible: !Platform.isIOS,
|
||||||
child: ListTile(
|
child: ListTile(
|
||||||
leading: const Icon(Icons.image),
|
leading: const Icon(Icons.image),
|
||||||
title: const Text("Aus Gallerie auswählen"),
|
title: const Text('Aus Gallerie auswählen'),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
context.loaderOverlay.show();
|
context.loaderOverlay.show();
|
||||||
FilePick.galleryPick().then((value) {
|
FilePick.galleryPick().then((value) {
|
||||||
@ -147,12 +145,12 @@ class _ChatTextfieldState extends State<ChatTextfield> {
|
|||||||
maxLines: 7,
|
maxLines: 7,
|
||||||
minLines: 1,
|
minLines: 1,
|
||||||
decoration: const InputDecoration(
|
decoration: const InputDecoration(
|
||||||
hintText: "Nachricht schreiben...",
|
hintText: 'Nachricht schreiben...',
|
||||||
border: InputBorder.none,
|
border: InputBorder.none,
|
||||||
),
|
),
|
||||||
onChanged: (String text) {
|
onChanged: (String text) {
|
||||||
if(text.trim().toLowerCase() == "marbot marbot marbot") {
|
if(text.trim().toLowerCase() == 'marbot marbot marbot') {
|
||||||
var newText = "Roboter sind cool und so, aber Marbots sind besser!";
|
var newText = 'Roboter sind cool und so, aber Marbots sind besser!';
|
||||||
_textBoxController.text = newText;
|
_textBoxController.text = newText;
|
||||||
text = newText;
|
text = newText;
|
||||||
}
|
}
|
||||||
@ -175,8 +173,8 @@ class _ChatTextfieldState extends State<ChatTextfield> {
|
|||||||
setState(() {
|
setState(() {
|
||||||
isLoading = false;
|
isLoading = false;
|
||||||
});
|
});
|
||||||
_textBoxController.text = "";
|
_textBoxController.text = '';
|
||||||
setDraft("");
|
setDraft('');
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
backgroundColor: Theme.of(context).primaryColor,
|
backgroundColor: Theme.of(context).primaryColor,
|
||||||
|
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