claude refactor #95
@@ -1,3 +1 @@
|
|||||||
class ApiParams {
|
class ApiParams {}
|
||||||
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,5 +1 @@
|
|||||||
|
class ApiRequest {}
|
||||||
|
|
||||||
class ApiRequest {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import 'package:http/http.dart' as http;
|
import 'package:http/http.dart' as http;
|
||||||
import 'package:json_annotation/json_annotation.dart';
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
|
|
||||||
abstract class ApiResponse {
|
abstract class ApiResponse {
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
late http.Response rawResponse;
|
late http.Response rawResponse;
|
||||||
|
|||||||
@@ -9,7 +9,8 @@ class AuthException extends AppException {
|
|||||||
super.technicalDetails,
|
super.technicalDetails,
|
||||||
}) : super(allowRetry: false);
|
}) : super(allowRetry: false);
|
||||||
|
|
||||||
factory AuthException.unauthorized({String? technicalDetails}) => AuthException(
|
factory AuthException.unauthorized({String? technicalDetails}) =>
|
||||||
|
AuthException(
|
||||||
statusCode: 401,
|
statusCode: 401,
|
||||||
userMessage: 'Bitte melde dich erneut an, um fortzufahren.',
|
userMessage: 'Bitte melde dich erneut an, um fortzufahren.',
|
||||||
technicalDetails: technicalDetails,
|
technicalDetails: technicalDetails,
|
||||||
|
|||||||
@@ -14,7 +14,8 @@ import 'server_exception.dart';
|
|||||||
import 'talk_exception.dart';
|
import 'talk_exception.dart';
|
||||||
import 'webuntis_exception.dart';
|
import 'webuntis_exception.dart';
|
||||||
|
|
||||||
const String _defaultFallback = 'Etwas ist schiefgelaufen. Bitte versuche es erneut.';
|
const String _defaultFallback =
|
||||||
|
'Etwas ist schiefgelaufen. Bitte versuche es erneut.';
|
||||||
const String _tlsErrorMessage =
|
const String _tlsErrorMessage =
|
||||||
'Die sichere Verbindung zum Server wurde abgelehnt (Zertifikat oder TLS-Fehler). '
|
'Die sichere Verbindung zum Server wurde abgelehnt (Zertifikat oder TLS-Fehler). '
|
||||||
'Häufige Ursachen: falsche Geräte-Uhrzeit oder ein WLAN mit Anmeldeseite (z.B. Café/Hotel).';
|
'Häufige Ursachen: falsche Geräte-Uhrzeit oder ein WLAN mit Anmeldeseite (z.B. Café/Hotel).';
|
||||||
@@ -28,9 +29,7 @@ AppException? _dioToAppException(DioException error) {
|
|||||||
case DioExceptionType.connectionError:
|
case DioExceptionType.connectionError:
|
||||||
return NetworkException(technicalDetails: error.message);
|
return NetworkException(technicalDetails: error.message);
|
||||||
case DioExceptionType.badCertificate:
|
case DioExceptionType.badCertificate:
|
||||||
return const NetworkException(
|
return const NetworkException(userMessage: _tlsErrorMessage);
|
||||||
userMessage: _tlsErrorMessage,
|
|
||||||
);
|
|
||||||
case DioExceptionType.badResponse:
|
case DioExceptionType.badResponse:
|
||||||
final status = error.response?.statusCode;
|
final status = error.response?.statusCode;
|
||||||
return ServerException(
|
return ServerException(
|
||||||
@@ -40,13 +39,15 @@ AppException? _dioToAppException(DioException error) {
|
|||||||
case DioExceptionType.cancel:
|
case DioExceptionType.cancel:
|
||||||
case DioExceptionType.unknown:
|
case DioExceptionType.unknown:
|
||||||
final inner = error.error;
|
final inner = error.error;
|
||||||
if (inner is SocketException) return NetworkException(technicalDetails: inner.message);
|
if (inner is SocketException) {
|
||||||
if (inner is HandshakeException) {
|
return NetworkException(technicalDetails: inner.message);
|
||||||
return const NetworkException(
|
}
|
||||||
userMessage: _tlsErrorMessage,
|
if (inner is HandshakeException) {
|
||||||
);
|
return const NetworkException(userMessage: _tlsErrorMessage);
|
||||||
|
}
|
||||||
|
if (inner is FormatException) {
|
||||||
|
return ParseException(technicalDetails: inner.message);
|
||||||
}
|
}
|
||||||
if (inner is FormatException) return ParseException(technicalDetails: inner.message);
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,12 +2,15 @@ import 'app_exception.dart';
|
|||||||
|
|
||||||
class NetworkException extends AppException {
|
class NetworkException extends AppException {
|
||||||
const NetworkException({
|
const NetworkException({
|
||||||
super.userMessage = 'Keine Internetverbindung. Bitte prüfe dein Netzwerk und versuche es erneut.',
|
super.userMessage =
|
||||||
|
'Keine Internetverbindung. Bitte prüfe dein Netzwerk und versuche es erneut.',
|
||||||
super.technicalDetails,
|
super.technicalDetails,
|
||||||
}) : super(allowRetry: true);
|
}) : super(allowRetry: true);
|
||||||
|
|
||||||
factory NetworkException.timeout({String? technicalDetails}) => NetworkException(
|
factory NetworkException.timeout({String? technicalDetails}) =>
|
||||||
userMessage: 'Der Server hat zu lange gebraucht. Bitte versuche es erneut.',
|
NetworkException(
|
||||||
|
userMessage:
|
||||||
|
'Der Server hat zu lange gebraucht. Bitte versuche es erneut.',
|
||||||
technicalDetails: technicalDetails,
|
technicalDetails: technicalDetails,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,9 @@ class ServerException extends AppException {
|
|||||||
String? userMessage,
|
String? userMessage,
|
||||||
super.technicalDetails,
|
super.technicalDetails,
|
||||||
}) : super(
|
}) : super(
|
||||||
userMessage: userMessage ?? 'Der Server hat gerade Probleme (Status $statusCode). Bitte später erneut versuchen.',
|
userMessage:
|
||||||
|
userMessage ??
|
||||||
|
'Der Server hat gerade Probleme (Status $statusCode). Bitte später erneut versuchen.',
|
||||||
allowRetry: true,
|
allowRetry: true,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,8 @@ class TalkException extends AppException {
|
|||||||
TalkException(this.source)
|
TalkException(this.source)
|
||||||
: super(
|
: super(
|
||||||
userMessage: _mapMessage(source),
|
userMessage: _mapMessage(source),
|
||||||
technicalDetails: 'Talk ${source.status} (${source.code}): ${source.message}',
|
technicalDetails:
|
||||||
|
'Talk ${source.status} (${source.code}): ${source.message}',
|
||||||
allowRetry: source.code >= 500,
|
allowRetry: source.code >= 500,
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -27,7 +28,9 @@ class TalkException extends AppException {
|
|||||||
if (e.code >= 500) {
|
if (e.code >= 500) {
|
||||||
return 'Talk-Server hat gerade Probleme (${e.code}).';
|
return 'Talk-Server hat gerade Probleme (${e.code}).';
|
||||||
}
|
}
|
||||||
return e.message.isNotEmpty ? e.message : 'Talk meldet einen Fehler (${e.code}).';
|
return e.message.isNotEmpty
|
||||||
|
? e.message
|
||||||
|
: 'Talk meldet einen Fehler (${e.code}).';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,9 +20,13 @@ class AutocompleteApi {
|
|||||||
);
|
);
|
||||||
final response = await http.get(endpoint, headers: NextcloudOcs.headers());
|
final response = await http.get(endpoint, headers: NextcloudOcs.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}',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
final decoded = jsonDecode(response.body) as Map<String, dynamic>;
|
final decoded = jsonDecode(response.body) as Map<String, dynamic>;
|
||||||
return AutocompleteResponse.fromJson(decoded['ocs'] as Map<String, dynamic>);
|
return AutocompleteResponse.fromJson(
|
||||||
|
decoded['ocs'] as Map<String, dynamic>,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,8 @@ class AutocompleteResponse {
|
|||||||
|
|
||||||
AutocompleteResponse(this.data);
|
AutocompleteResponse(this.data);
|
||||||
|
|
||||||
factory AutocompleteResponse.fromJson(Map<String, dynamic> json) => _$AutocompleteResponseFromJson(json);
|
factory AutocompleteResponse.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$AutocompleteResponseFromJson(json);
|
||||||
Map<String, dynamic> toJson() => _$AutocompleteResponseToJson(this);
|
Map<String, dynamic> toJson() => _$AutocompleteResponseToJson(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -22,9 +23,17 @@ class AutocompleteResponseObject {
|
|||||||
String? subline;
|
String? subline;
|
||||||
String? shareWithDisplayNameUniqe;
|
String? shareWithDisplayNameUniqe;
|
||||||
|
|
||||||
AutocompleteResponseObject(this.id, this.label, this.icon, this.source, this.status,
|
AutocompleteResponseObject(
|
||||||
this.subline, this.shareWithDisplayNameUniqe);
|
this.id,
|
||||||
|
this.label,
|
||||||
|
this.icon,
|
||||||
|
this.source,
|
||||||
|
this.status,
|
||||||
|
this.subline,
|
||||||
|
this.shareWithDisplayNameUniqe,
|
||||||
|
);
|
||||||
|
|
||||||
factory AutocompleteResponseObject.fromJson(Map<String, dynamic> json) => _$AutocompleteResponseObjectFromJson(json);
|
factory AutocompleteResponseObject.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$AutocompleteResponseObjectFromJson(json);
|
||||||
Map<String, dynamic> toJson() => _$AutocompleteResponseObjectToJson(this);
|
Map<String, dynamic> toJson() => _$AutocompleteResponseObjectToJson(this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,9 @@ class FileSharingApi {
|
|||||||
);
|
);
|
||||||
final response = await http.post(endpoint, headers: NextcloudOcs.headers());
|
final response = await http.post(endpoint, headers: NextcloudOcs.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}',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,9 +15,10 @@ class FileSharingApiParams {
|
|||||||
required this.shareWith,
|
required this.shareWith,
|
||||||
required this.path,
|
required this.path,
|
||||||
this.referenceId,
|
this.referenceId,
|
||||||
this.talkMetaData
|
this.talkMetaData,
|
||||||
});
|
});
|
||||||
|
|
||||||
factory FileSharingApiParams.fromJson(Map<String, dynamic> json) => _$FileSharingApiParamsFromJson(json);
|
factory FileSharingApiParams.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$FileSharingApiParamsFromJson(json);
|
||||||
Map<String, dynamic> toJson() => _$FileSharingApiParamsToJson(this);
|
Map<String, dynamic> toJson() => _$FileSharingApiParamsToJson(this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,39 +12,53 @@ class SetFavorite extends TalkApi {
|
|||||||
final String chatToken;
|
final String chatToken;
|
||||||
final bool favoriteState;
|
final 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
|
||||||
ApiResponse? assemble(String raw) => null;
|
ApiResponse? assemble(String raw) => null;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<http.Response> request(Uri uri, ApiParams? body, Map<String, String>? headers) =>
|
Future<http.Response> request(
|
||||||
favoriteState ? http.post(uri, headers: headers) : http.delete(uri, headers: headers);
|
Uri uri,
|
||||||
|
ApiParams? body,
|
||||||
|
Map<String, String>? headers,
|
||||||
|
) => favoriteState
|
||||||
|
? http.post(uri, headers: headers)
|
||||||
|
: http.delete(uri, headers: headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
class LeaveRoom extends TalkApi {
|
class LeaveRoom extends TalkApi {
|
||||||
final String chatToken;
|
final String chatToken;
|
||||||
|
|
||||||
LeaveRoom(this.chatToken) : super('v4/room/$chatToken/participants/self', null);
|
LeaveRoom(this.chatToken)
|
||||||
|
: super('v4/room/$chatToken/participants/self', null);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
ApiResponse? assemble(String raw) => null;
|
ApiResponse? assemble(String raw) => null;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<http.Response> request(Uri uri, ApiParams? body, Map<String, String>? headers) =>
|
Future<http.Response> request(
|
||||||
http.delete(uri, headers: headers);
|
Uri uri,
|
||||||
|
ApiParams? body,
|
||||||
|
Map<String, String>? headers,
|
||||||
|
) => http.delete(uri, headers: headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
class DeleteMessage extends TalkApi {
|
class DeleteMessage extends TalkApi {
|
||||||
final String chatToken;
|
final String chatToken;
|
||||||
final int messageId;
|
final 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
|
||||||
ApiResponse? assemble(String raw) => null;
|
ApiResponse? assemble(String raw) => null;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<http.Response> request(Uri uri, ApiParams? body, Map<String, String>? headers) =>
|
Future<http.Response> request(
|
||||||
http.delete(uri, headers: headers);
|
Uri uri,
|
||||||
|
ApiParams? body,
|
||||||
|
Map<String, String>? headers,
|
||||||
|
) => http.delete(uri, headers: headers);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,8 @@ 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
|
||||||
GetChatResponse assemble(String raw) {
|
GetChatResponse assemble(String raw) {
|
||||||
@@ -20,6 +21,9 @@ class GetChat extends TalkApi<GetChatResponse> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<Response> request(Uri uri, Object? body, Map<String, String>? headers) => http.get(uri, headers: headers);
|
Future<Response> request(
|
||||||
|
Uri uri,
|
||||||
|
Object? body,
|
||||||
|
Map<String, String>? headers,
|
||||||
|
) => http.get(uri, headers: headers);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,14 +21,17 @@ class GetChatParams extends ApiParams {
|
|||||||
this.lastCommonReadId,
|
this.lastCommonReadId,
|
||||||
this.timeout,
|
this.timeout,
|
||||||
this.setReadMarker,
|
this.setReadMarker,
|
||||||
this.includeLastKnown
|
this.includeLastKnown,
|
||||||
});
|
});
|
||||||
|
|
||||||
factory GetChatParams.fromJson(Map<String, dynamic> json) => _$GetChatParamsFromJson(json);
|
factory GetChatParams.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$GetChatParamsFromJson(json);
|
||||||
Map<String, dynamic> toJson() => _$GetChatParamsToJson(this);
|
Map<String, dynamic> toJson() => _$GetChatParamsToJson(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
enum GetChatParamsSwitch {
|
enum GetChatParamsSwitch {
|
||||||
@JsonValue(1) on,
|
@JsonValue(1)
|
||||||
@JsonValue(0) off,
|
on,
|
||||||
|
@JsonValue(0)
|
||||||
|
off,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,8 @@ class GetChatResponse extends ApiResponse {
|
|||||||
|
|
||||||
GetChatResponse(this.data);
|
GetChatResponse(this.data);
|
||||||
|
|
||||||
factory GetChatResponse.fromJson(Map<String, dynamic> json) => _$GetChatResponseFromJson(json);
|
factory GetChatResponse.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$GetChatResponseFromJson(json);
|
||||||
Map<String, dynamic> toJson() => _$GetChatResponseToJson(this);
|
Map<String, dynamic> toJson() => _$GetChatResponseToJson(this);
|
||||||
|
|
||||||
List<GetChatResponseObject> sortByTimestamp() {
|
List<GetChatResponseObject> sortByTimestamp() {
|
||||||
@@ -37,7 +38,8 @@ class GetChatResponseObject {
|
|||||||
String message;
|
String message;
|
||||||
Map<String, int>? reactions;
|
Map<String, int>? reactions;
|
||||||
List<String>? reactionsSelf;
|
List<String>? reactionsSelf;
|
||||||
@JsonKey(fromJson: _fromJson) Map<String, RichObjectString>? messageParameters;
|
@JsonKey(fromJson: _fromJson)
|
||||||
|
Map<String, RichObjectString>? messageParameters;
|
||||||
GetChatResponseObject? parent;
|
GetChatResponseObject? parent;
|
||||||
|
|
||||||
GetChatResponseObject(
|
GetChatResponseObject(
|
||||||
@@ -58,7 +60,8 @@ class GetChatResponseObject {
|
|||||||
this.parent,
|
this.parent,
|
||||||
);
|
);
|
||||||
|
|
||||||
factory GetChatResponseObject.fromJson(Map<String, dynamic> json) => _$GetChatResponseObjectFromJson(json);
|
factory GetChatResponseObject.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$GetChatResponseObjectFromJson(json);
|
||||||
Map<String, dynamic> toJson() => _$GetChatResponseObjectToJson(this);
|
Map<String, dynamic> toJson() => _$GetChatResponseObjectToJson(this);
|
||||||
|
|
||||||
static GetChatResponseObject getDateDummy(int timestamp) {
|
static GetChatResponseObject getDateDummy(int timestamp) {
|
||||||
@@ -66,7 +69,8 @@ class GetChatResponseObject {
|
|||||||
return getTextDummy(elementDate.formatDate());
|
return getTextDummy(elementDate.formatDate());
|
||||||
}
|
}
|
||||||
|
|
||||||
static GetChatResponseObject getTextDummy(String text) => GetChatResponseObject(
|
static GetChatResponseObject getTextDummy(String text) =>
|
||||||
|
GetChatResponseObject(
|
||||||
0,
|
0,
|
||||||
'',
|
'',
|
||||||
GetRoomResponseObjectMessageActorType.user,
|
GetRoomResponseObjectMessageActorType.user,
|
||||||
@@ -83,14 +87,16 @@ class GetChatResponseObject {
|
|||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
);
|
);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, RichObjectString>? _fromJson(dynamic json) {
|
Map<String, RichObjectString>? _fromJson(dynamic json) {
|
||||||
if (json is Map<String, dynamic>) {
|
if (json is Map<String, dynamic>) {
|
||||||
final data = <String, RichObjectString>{};
|
final data = <String, RichObjectString>{};
|
||||||
for (final element in json.keys) {
|
for (final element in json.keys) {
|
||||||
data.putIfAbsent(element, () => RichObjectString.fromJson(json[element] as Map<String, dynamic>));
|
data.putIfAbsent(
|
||||||
|
element,
|
||||||
|
() => RichObjectString.fromJson(json[element] as Map<String, dynamic>),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
@@ -109,17 +115,26 @@ class RichObjectString {
|
|||||||
|
|
||||||
RichObjectString(this.type, this.id, this.name, this.path, this.link);
|
RichObjectString(this.type, this.id, this.name, this.path, this.link);
|
||||||
|
|
||||||
factory RichObjectString.fromJson(Map<String, dynamic> json) => _$RichObjectStringFromJson(json);
|
factory RichObjectString.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$RichObjectStringFromJson(json);
|
||||||
Map<String, dynamic> toJson() => _$RichObjectStringToJson(this);
|
Map<String, dynamic> toJson() => _$RichObjectStringToJson(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
enum RichObjectStringObjectType {
|
enum RichObjectStringObjectType {
|
||||||
@JsonValue('user') user,
|
@JsonValue('user')
|
||||||
@JsonValue('group') group,
|
user,
|
||||||
@JsonValue('file') file,
|
@JsonValue('group')
|
||||||
@JsonValue('guest') guest,
|
group,
|
||||||
@JsonValue('highlight') highlight,
|
@JsonValue('file')
|
||||||
@JsonValue('talk-poll') talkPoll,
|
file,
|
||||||
@JsonValue('geo-location') geoLocation,
|
@JsonValue('guest')
|
||||||
@JsonValue('call') call,
|
guest,
|
||||||
|
@JsonValue('highlight')
|
||||||
|
highlight,
|
||||||
|
@JsonValue('talk-poll')
|
||||||
|
talkPoll,
|
||||||
|
@JsonValue('geo-location')
|
||||||
|
geoLocation,
|
||||||
|
@JsonValue('call')
|
||||||
|
call,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
|
|
||||||
import 'get_chat_response.dart';
|
import 'get_chat_response.dart';
|
||||||
|
|
||||||
class RichObjectStringProcessor {
|
class RichObjectStringProcessor {
|
||||||
static String parseToString(String message, Map<String, RichObjectString>? data) {
|
static String parseToString(
|
||||||
|
String message,
|
||||||
|
Map<String, RichObjectString>? data,
|
||||||
|
) {
|
||||||
if (data == null) return message;
|
if (data == null) return message;
|
||||||
|
|
||||||
data.forEach((key, value) {
|
data.forEach((key, value) {
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
|
|
||||||
import 'package:http/http.dart' as http;
|
import 'package:http/http.dart' as http;
|
||||||
import 'package:http/http.dart';
|
import 'package:http/http.dart';
|
||||||
|
|
||||||
import '../talk_api.dart';
|
import '../talk_api.dart';
|
||||||
import 'create_room_params.dart';
|
import 'create_room_params.dart';
|
||||||
|
|
||||||
class CreateRoom extends TalkApi {
|
class CreateRoom extends TalkApi {
|
||||||
CreateRoomParams params;
|
CreateRoomParams params;
|
||||||
|
|
||||||
@@ -13,9 +13,19 @@ class CreateRoom extends TalkApi {
|
|||||||
Null assemble(String raw) => null;
|
Null assemble(String raw) => null;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<Response>? request(Uri uri, Object? body, Map<String, String>? headers) {
|
Future<Response>? request(
|
||||||
|
Uri uri,
|
||||||
|
Object? body,
|
||||||
|
Map<String, String>? headers,
|
||||||
|
) {
|
||||||
if (body is CreateRoomParams) {
|
if (body is CreateRoomParams) {
|
||||||
return http.post(uri, headers: headers, body: body.toJson().map((key, value) => MapEntry(key, value.toString())));
|
return http.post(
|
||||||
|
uri,
|
||||||
|
headers: headers,
|
||||||
|
body: body.toJson().map(
|
||||||
|
(key, value) => MapEntry(key, value.toString()),
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -19,9 +19,10 @@ class CreateRoomParams extends ApiParams {
|
|||||||
this.source,
|
this.source,
|
||||||
this.roomName,
|
this.roomName,
|
||||||
this.objectType,
|
this.objectType,
|
||||||
this.objectId
|
this.objectId,
|
||||||
});
|
});
|
||||||
|
|
||||||
factory CreateRoomParams.fromJson(Map<String, dynamic> json) => _$CreateRoomParamsFromJson(json);
|
factory CreateRoomParams.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$CreateRoomParamsFromJson(json);
|
||||||
Map<String, dynamic> toJson() => _$CreateRoomParamsToJson(this);
|
Map<String, dynamic> toJson() => _$CreateRoomParamsToJson(this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,17 +8,24 @@ import 'delete_react_message_params.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
|
||||||
Null assemble(String raw) => null;
|
Null assemble(String raw) => null;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<Response>? request(Uri uri, ApiParams? body, Map<String, String>? headers) {
|
Future<Response>? request(
|
||||||
|
Uri uri,
|
||||||
|
ApiParams? body,
|
||||||
|
Map<String, String>? headers,
|
||||||
|
) {
|
||||||
if (body is DeleteReactMessageParams) {
|
if (body is DeleteReactMessageParams) {
|
||||||
return http.delete(uri, headers: headers, body: body.toJson());
|
return http.delete(uri, headers: headers, body: body.toJson());
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ class DeleteReactMessageParams extends ApiParams {
|
|||||||
|
|
||||||
DeleteReactMessageParams(this.reaction);
|
DeleteReactMessageParams(this.reaction);
|
||||||
|
|
||||||
factory DeleteReactMessageParams.fromJson(Map<String, dynamic> json) => _$DeleteReactMessageParamsFromJson(json);
|
factory DeleteReactMessageParams.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$DeleteReactMessageParamsFromJson(json);
|
||||||
Map<String, dynamic> toJson() => _$DeleteReactMessageParamsToJson(this);
|
Map<String, dynamic> toJson() => _$DeleteReactMessageParamsToJson(this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,10 +12,15 @@ class GetParticipants extends TalkApi<GetParticipantsResponse> {
|
|||||||
@override
|
@override
|
||||||
GetParticipantsResponse assemble(String raw) {
|
GetParticipantsResponse assemble(String raw) {
|
||||||
final decoded = jsonDecode(raw) as Map<String, dynamic>;
|
final decoded = jsonDecode(raw) as Map<String, dynamic>;
|
||||||
return GetParticipantsResponse.fromJson(decoded['ocs'] as Map<String, dynamic>);
|
return GetParticipantsResponse.fromJson(
|
||||||
|
decoded['ocs'] as Map<String, dynamic>,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<http.Response> request(Uri uri, Object? body, Map<String, String>? headers) => http.get(uri, headers: headers);
|
Future<http.Response> request(
|
||||||
|
Uri uri,
|
||||||
|
Object? body,
|
||||||
|
Map<String, String>? headers,
|
||||||
|
) => http.get(uri, headers: headers);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
import 'package:json_annotation/json_annotation.dart';
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
|
|
||||||
import '../../../api_response.dart';
|
import '../../../api_response.dart';
|
||||||
@@ -11,7 +10,8 @@ class GetParticipantsResponse extends ApiResponse {
|
|||||||
|
|
||||||
GetParticipantsResponse(this.data);
|
GetParticipantsResponse(this.data);
|
||||||
|
|
||||||
factory GetParticipantsResponse.fromJson(Map<String, dynamic> json) => _$GetParticipantsResponseFromJson(json);
|
factory GetParticipantsResponse.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$GetParticipantsResponseFromJson(json);
|
||||||
Map<String, dynamic> toJson() => _$GetParticipantsResponseToJson(this);
|
Map<String, dynamic> toJson() => _$GetParticipantsResponseToJson(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -48,28 +48,41 @@ class GetParticipantsResponseObject {
|
|||||||
this.status,
|
this.status,
|
||||||
this.statusIcon,
|
this.statusIcon,
|
||||||
this.statusMessage,
|
this.statusMessage,
|
||||||
this.roomToken);
|
this.roomToken,
|
||||||
|
);
|
||||||
|
|
||||||
factory GetParticipantsResponseObject.fromJson(Map<String, dynamic> json) => _$GetParticipantsResponseObjectFromJson(json);
|
factory GetParticipantsResponseObject.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$GetParticipantsResponseObjectFromJson(json);
|
||||||
Map<String, dynamic> toJson() => _$GetParticipantsResponseObjectToJson(this);
|
Map<String, dynamic> toJson() => _$GetParticipantsResponseObjectToJson(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
enum GetParticipantsResponseObjectParticipantType {
|
enum GetParticipantsResponseObjectParticipantType {
|
||||||
@JsonValue(1) owner('Besitzer'),
|
@JsonValue(1)
|
||||||
@JsonValue(2) moderator('Moderator'),
|
owner('Besitzer'),
|
||||||
@JsonValue(3) user('Teilnehmer'),
|
@JsonValue(2)
|
||||||
@JsonValue(4) guest('Gast'),
|
moderator('Moderator'),
|
||||||
@JsonValue(5) userFollowingPublicLink('Teilnehmer über Link'),
|
@JsonValue(3)
|
||||||
@JsonValue(6) guestWithModeratorPermissions('Gast Moderator');
|
user('Teilnehmer'),
|
||||||
|
@JsonValue(4)
|
||||||
|
guest('Gast'),
|
||||||
|
@JsonValue(5)
|
||||||
|
userFollowingPublicLink('Teilnehmer über Link'),
|
||||||
|
@JsonValue(6)
|
||||||
|
guestWithModeratorPermissions('Gast Moderator');
|
||||||
|
|
||||||
const GetParticipantsResponseObjectParticipantType(this.prettyName);
|
const GetParticipantsResponseObjectParticipantType(this.prettyName);
|
||||||
final String prettyName;
|
final String prettyName;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum GetParticipantsResponseObjectParticipantsInCallFlags {
|
enum GetParticipantsResponseObjectParticipantsInCallFlags {
|
||||||
@JsonValue(0) disconnected,
|
@JsonValue(0)
|
||||||
@JsonValue(1) inCall,
|
disconnected,
|
||||||
@JsonValue(2) providesAudio,
|
@JsonValue(1)
|
||||||
@JsonValue(3) providesVideo,
|
inCall,
|
||||||
@JsonValue(4) usesSipDialIn
|
@JsonValue(2)
|
||||||
|
providesAudio,
|
||||||
|
@JsonValue(3)
|
||||||
|
providesVideo,
|
||||||
|
@JsonValue(4)
|
||||||
|
usesSipDialIn,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,14 +8,21 @@ import 'get_poll_state_response.dart';
|
|||||||
class GetPollState extends TalkApi<GetPollStateResponse> {
|
class GetPollState extends TalkApi<GetPollStateResponse> {
|
||||||
String token;
|
String token;
|
||||||
int pollId;
|
int pollId;
|
||||||
GetPollState({required this.token, required this.pollId}) : super('v1/poll/$token/$pollId', null);
|
GetPollState({required this.token, required this.pollId})
|
||||||
|
: super('v1/poll/$token/$pollId', null);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
GetPollStateResponse assemble(String raw) {
|
GetPollStateResponse assemble(String raw) {
|
||||||
final decoded = jsonDecode(raw) as Map<String, dynamic>;
|
final decoded = jsonDecode(raw) as Map<String, dynamic>;
|
||||||
return GetPollStateResponse.fromJson(decoded['ocs'] as Map<String, dynamic>);
|
return GetPollStateResponse.fromJson(
|
||||||
|
decoded['ocs'] as Map<String, dynamic>,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<http.Response> request(Uri uri, Object? body, Map<String, String>? headers) => http.get(uri, headers: headers);
|
Future<http.Response> request(
|
||||||
|
Uri uri,
|
||||||
|
Object? body,
|
||||||
|
Map<String, String>? headers,
|
||||||
|
) => http.get(uri, headers: headers);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,8 @@ class GetPollStateResponse extends ApiResponse {
|
|||||||
|
|
||||||
GetPollStateResponse(this.data);
|
GetPollStateResponse(this.data);
|
||||||
|
|
||||||
factory GetPollStateResponse.fromJson(Map<String, dynamic> json) => _$GetPollStateResponseFromJson(json);
|
factory GetPollStateResponse.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$GetPollStateResponseFromJson(json);
|
||||||
Map<String, dynamic> toJson() => _$GetPollStateResponseToJson(this);
|
Map<String, dynamic> toJson() => _$GetPollStateResponseToJson(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -43,8 +44,10 @@ class GetPollStateResponseObject {
|
|||||||
this.maxVotes,
|
this.maxVotes,
|
||||||
this.votedSelf,
|
this.votedSelf,
|
||||||
this.numVoters,
|
this.numVoters,
|
||||||
this.details);
|
this.details,
|
||||||
|
);
|
||||||
|
|
||||||
factory GetPollStateResponseObject.fromJson(Map<String, dynamic> json) => _$GetPollStateResponseObjectFromJson(json);
|
factory GetPollStateResponseObject.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$GetPollStateResponseObjectFromJson(json);
|
||||||
Map<String, dynamic> toJson() => _$GetPollStateResponseObjectToJson(this);
|
Map<String, dynamic> toJson() => _$GetPollStateResponseObjectToJson(this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,15 +10,21 @@ import 'get_reactions_response.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
|
||||||
GetReactionsResponse assemble(String raw) {
|
GetReactionsResponse assemble(String raw) {
|
||||||
final decoded = jsonDecode(raw) as Map<String, dynamic>;
|
final decoded = jsonDecode(raw) as Map<String, dynamic>;
|
||||||
return GetReactionsResponse.fromJson(decoded['ocs'] as Map<String, dynamic>);
|
return GetReactionsResponse.fromJson(
|
||||||
|
decoded['ocs'] as Map<String, dynamic>,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<Response>? request(Uri uri, ApiParams? body, Map<String, String>? headers) => http.get(uri, headers: headers);
|
Future<Response>? request(
|
||||||
|
Uri uri,
|
||||||
|
ApiParams? body,
|
||||||
|
Map<String, String>? headers,
|
||||||
|
) => http.get(uri, headers: headers);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,8 @@ class GetReactionsResponse extends ApiResponse {
|
|||||||
|
|
||||||
GetReactionsResponse(this.data);
|
GetReactionsResponse(this.data);
|
||||||
|
|
||||||
factory GetReactionsResponse.fromJson(Map<String, dynamic> json) => _$GetReactionsResponseFromJson(json);
|
factory GetReactionsResponse.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$GetReactionsResponseFromJson(json);
|
||||||
Map<String, dynamic> toJson() => _$GetReactionsResponseToJson(this);
|
Map<String, dynamic> toJson() => _$GetReactionsResponseToJson(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -21,13 +22,21 @@ class GetReactionsResponseObject {
|
|||||||
String actorDisplayName;
|
String actorDisplayName;
|
||||||
int timestamp;
|
int timestamp;
|
||||||
|
|
||||||
GetReactionsResponseObject(this.actorType, this.actorId, this.actorDisplayName, this.timestamp);
|
GetReactionsResponseObject(
|
||||||
|
this.actorType,
|
||||||
|
this.actorId,
|
||||||
|
this.actorDisplayName,
|
||||||
|
this.timestamp,
|
||||||
|
);
|
||||||
|
|
||||||
factory GetReactionsResponseObject.fromJson(Map<String, dynamic> json) => _$GetReactionsResponseObjectFromJson(json);
|
factory GetReactionsResponseObject.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$GetReactionsResponseObjectFromJson(json);
|
||||||
Map<String, dynamic> toJson() => _$GetReactionsResponseObjectToJson(this);
|
Map<String, dynamic> toJson() => _$GetReactionsResponseObjectToJson(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
enum GetReactionsResponseObjectActorType {
|
enum GetReactionsResponseObjectActorType {
|
||||||
@JsonValue('guests') guests,
|
@JsonValue('guests')
|
||||||
@JsonValue('users') users,
|
guests,
|
||||||
|
@JsonValue('users')
|
||||||
|
users,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,17 +8,24 @@ import 'react_message_params.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
|
||||||
Null assemble(String raw) => null;
|
Null assemble(String raw) => null;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<Response>? request(Uri uri, ApiParams? body, Map<String, String>? headers) {
|
Future<Response>? request(
|
||||||
|
Uri uri,
|
||||||
|
ApiParams? body,
|
||||||
|
Map<String, String>? headers,
|
||||||
|
) {
|
||||||
if (body is ReactMessageParams) {
|
if (body is ReactMessageParams) {
|
||||||
return http.post(uri, headers: headers, body: body.toJson());
|
return http.post(uri, headers: headers, body: body.toJson());
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ class ReactMessageParams extends ApiParams {
|
|||||||
|
|
||||||
ReactMessageParams(this.reaction);
|
ReactMessageParams(this.reaction);
|
||||||
|
|
||||||
factory ReactMessageParams.fromJson(Map<String, dynamic> json) => _$ReactMessageParamsFromJson(json);
|
factory ReactMessageParams.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$ReactMessageParamsFromJson(json);
|
||||||
Map<String, dynamic> toJson() => _$ReactMessageParamsToJson(this);
|
Map<String, dynamic> toJson() => _$ReactMessageParamsToJson(this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,13 +6,10 @@ import '../talk_api.dart';
|
|||||||
import 'get_room_params.dart';
|
import 'get_room_params.dart';
|
||||||
import 'get_room_response.dart';
|
import 'get_room_response.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());
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
GetRoomResponse assemble(String raw) {
|
GetRoomResponse assemble(String raw) {
|
||||||
final decoded = jsonDecode(raw) as Map<String, dynamic>;
|
final decoded = jsonDecode(raw) as Map<String, dynamic>;
|
||||||
@@ -20,6 +17,9 @@ class GetRoom extends TalkApi<GetRoomResponse> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<http.Response> request(Uri uri, Object? body, Map<String, String>? headers) => http.get(uri, headers: headers);
|
Future<http.Response> request(
|
||||||
|
Uri uri,
|
||||||
|
Object? body,
|
||||||
|
Map<String, String>? headers,
|
||||||
|
) => http.get(uri, headers: headers);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
import 'package:json_annotation/json_annotation.dart';
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
|
|
||||||
import '../../../api_params.dart';
|
import '../../../api_params.dart';
|
||||||
@@ -8,18 +7,22 @@ part 'get_room_params.g.dart';
|
|||||||
@JsonSerializable(explicitToJson: true)
|
@JsonSerializable(explicitToJson: true)
|
||||||
class GetRoomParams extends ApiParams {
|
class GetRoomParams extends ApiParams {
|
||||||
GetRoomParamsStatusUpdate? noStatusUpdate;
|
GetRoomParamsStatusUpdate? noStatusUpdate;
|
||||||
@JsonKey(toJson: _format) bool? includeStatus;
|
@JsonKey(toJson: _format)
|
||||||
|
bool? includeStatus;
|
||||||
int? modifiedSince;
|
int? modifiedSince;
|
||||||
|
|
||||||
GetRoomParams({this.noStatusUpdate, this.includeStatus, this.modifiedSince});
|
GetRoomParams({this.noStatusUpdate, this.includeStatus, this.modifiedSince});
|
||||||
|
|
||||||
factory GetRoomParams.fromJson(Map<String, dynamic> json) => _$GetRoomParamsFromJson(json);
|
factory GetRoomParams.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$GetRoomParamsFromJson(json);
|
||||||
Map<String, dynamic> toJson() => _$GetRoomParamsToJson(this);
|
Map<String, dynamic> toJson() => _$GetRoomParamsToJson(this);
|
||||||
|
|
||||||
static String _format(bool? v) => v.toString();
|
static String _format(bool? v) => v.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
enum GetRoomParamsStatusUpdate {
|
enum GetRoomParamsStatusUpdate {
|
||||||
@JsonValue(0) defaults,
|
@JsonValue(0)
|
||||||
@JsonValue(1) keepAlive,
|
defaults,
|
||||||
|
@JsonValue(1)
|
||||||
|
keepAlive,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,10 +11,15 @@ class GetRoomResponse extends ApiResponse {
|
|||||||
|
|
||||||
GetRoomResponse(this.data);
|
GetRoomResponse(this.data);
|
||||||
|
|
||||||
factory GetRoomResponse.fromJson(Map<String, dynamic> json) => _$GetRoomResponseFromJson(json);
|
factory GetRoomResponse.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$GetRoomResponseFromJson(json);
|
||||||
Map<String, dynamic> toJson() => _$GetRoomResponseToJson(this);
|
Map<String, dynamic> toJson() => _$GetRoomResponseToJson(this);
|
||||||
|
|
||||||
List<GetRoomResponseObject> sortBy({bool lastActivity = true, required bool favoritesToTop, required bool unreadToTop}) {
|
List<GetRoomResponseObject> sortBy({
|
||||||
|
bool lastActivity = true,
|
||||||
|
required bool favoritesToTop,
|
||||||
|
required bool unreadToTop,
|
||||||
|
}) {
|
||||||
for (var chat in data) {
|
for (var chat in data) {
|
||||||
final buffer = StringBuffer();
|
final buffer = StringBuffer();
|
||||||
|
|
||||||
@@ -98,40 +103,62 @@ class GetRoomResponseObject {
|
|||||||
this.lastMessage,
|
this.lastMessage,
|
||||||
this.status,
|
this.status,
|
||||||
this.statusIcon,
|
this.statusIcon,
|
||||||
this.statusMessage);
|
this.statusMessage,
|
||||||
|
);
|
||||||
|
|
||||||
factory GetRoomResponseObject.fromJson(Map<String, dynamic> json) => _$GetRoomResponseObjectFromJson(json);
|
factory GetRoomResponseObject.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$GetRoomResponseObjectFromJson(json);
|
||||||
Map<String, dynamic> toJson() => _$GetRoomResponseObjectToJson(this);
|
Map<String, dynamic> toJson() => _$GetRoomResponseObjectToJson(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
enum GetRoomResponseObjectConversationType {
|
enum GetRoomResponseObjectConversationType {
|
||||||
@JsonValue(1) oneToOne,
|
@JsonValue(1)
|
||||||
@JsonValue(2) group,
|
oneToOne,
|
||||||
@JsonValue(3) public,
|
@JsonValue(2)
|
||||||
@JsonValue(4) changelog,
|
group,
|
||||||
@JsonValue(5) deleted,
|
@JsonValue(3)
|
||||||
@JsonValue(6) noteToSelf,
|
public,
|
||||||
|
@JsonValue(4)
|
||||||
|
changelog,
|
||||||
|
@JsonValue(5)
|
||||||
|
deleted,
|
||||||
|
@JsonValue(6)
|
||||||
|
noteToSelf,
|
||||||
}
|
}
|
||||||
|
|
||||||
enum GetRoomResponseObjectParticipantNotificationLevel {
|
enum GetRoomResponseObjectParticipantNotificationLevel {
|
||||||
@JsonValue(0) defaultLevel,
|
@JsonValue(0)
|
||||||
@JsonValue(1) alwaysNotify,
|
defaultLevel,
|
||||||
@JsonValue(2) notifyOnMention,
|
@JsonValue(1)
|
||||||
@JsonValue(3) neverNotify,
|
alwaysNotify,
|
||||||
|
@JsonValue(2)
|
||||||
|
notifyOnMention,
|
||||||
|
@JsonValue(3)
|
||||||
|
neverNotify,
|
||||||
}
|
}
|
||||||
|
|
||||||
enum GetRoomResponseObjectMessageActorType {
|
enum GetRoomResponseObjectMessageActorType {
|
||||||
@JsonValue('deleted_users') deletedUsers,
|
@JsonValue('deleted_users')
|
||||||
@JsonValue('users') user,
|
deletedUsers,
|
||||||
@JsonValue('guests') guest,
|
@JsonValue('users')
|
||||||
@JsonValue('bots') bot,
|
user,
|
||||||
@JsonValue('bridged') bridge,
|
@JsonValue('guests')
|
||||||
|
guest,
|
||||||
|
@JsonValue('bots')
|
||||||
|
bot,
|
||||||
|
@JsonValue('bridged')
|
||||||
|
bridge,
|
||||||
}
|
}
|
||||||
|
|
||||||
enum GetRoomResponseObjectMessageType {
|
enum GetRoomResponseObjectMessageType {
|
||||||
@JsonValue('comment') comment,
|
@JsonValue('comment')
|
||||||
@JsonValue('voice-message') voiceMessage,
|
comment,
|
||||||
@JsonValue('comment_deleted') deletedComment,
|
@JsonValue('voice-message')
|
||||||
@JsonValue('system') system,
|
voiceMessage,
|
||||||
@JsonValue('command') command,
|
@JsonValue('comment_deleted')
|
||||||
|
deletedComment,
|
||||||
|
@JsonValue('system')
|
||||||
|
system,
|
||||||
|
@JsonValue('command')
|
||||||
|
command,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,17 +7,21 @@ import 'send_message_params.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
|
||||||
Null assemble(String raw) => null;
|
Null assemble(String raw) => null;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<Response>? request(Uri uri, ApiParams? body, Map<String, String>? headers) {
|
Future<Response>? request(
|
||||||
|
Uri uri,
|
||||||
|
ApiParams? body,
|
||||||
|
Map<String, String>? headers,
|
||||||
|
) {
|
||||||
if (body is SendMessageParams) {
|
if (body is SendMessageParams) {
|
||||||
return http.post(uri, headers: headers, body: body.toJson());
|
return http.post(uri, headers: headers, body: body.toJson());
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ class SendMessageParams extends ApiParams {
|
|||||||
|
|
||||||
SendMessageParams(this.message, {this.replyTo});
|
SendMessageParams(this.message, {this.replyTo});
|
||||||
|
|
||||||
factory SendMessageParams.fromJson(Map<String, dynamic> json) => _$SendMessageParamsFromJson(json);
|
factory SendMessageParams.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$SendMessageParamsFromJson(json);
|
||||||
Map<String, dynamic> toJson() => _$SendMessageParamsToJson(this);
|
Map<String, dynamic> toJson() => _$SendMessageParamsToJson(this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
import 'package:http/http.dart' as http;
|
import 'package:http/http.dart' as http;
|
||||||
import 'package:http/http.dart';
|
import 'package:http/http.dart';
|
||||||
|
|
||||||
@@ -10,7 +9,12 @@ 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -18,13 +22,15 @@ class SetReadMarker extends TalkApi {
|
|||||||
Null assemble(String raw) => null;
|
Null assemble(String raw) => null;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<Response> request(Uri uri, Object? body, Map<String, String>? headers) {
|
Future<Response> request(
|
||||||
|
Uri uri,
|
||||||
|
Object? body,
|
||||||
|
Map<String, String>? headers,
|
||||||
|
) {
|
||||||
if (readState) {
|
if (readState) {
|
||||||
|
|
||||||
return http.post(uri, headers: headers);
|
return http.post(uri, headers: headers);
|
||||||
} else {
|
} else {
|
||||||
return http.delete(uri, headers: headers);
|
return http.delete(uri, headers: headers);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,10 +8,9 @@ part 'set_read_marker_params.g.dart';
|
|||||||
class SetReadMarkerParams extends ApiParams {
|
class SetReadMarkerParams extends ApiParams {
|
||||||
int? lastReadMessage;
|
int? lastReadMessage;
|
||||||
|
|
||||||
SetReadMarkerParams({
|
SetReadMarkerParams({this.lastReadMessage});
|
||||||
this.lastReadMessage
|
|
||||||
});
|
|
||||||
|
|
||||||
factory SetReadMarkerParams.fromJson(Map<String, dynamic> json) => _$SetReadMarkerParamsFromJson(json);
|
factory SetReadMarkerParams.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$SetReadMarkerParamsFromJson(json);
|
||||||
Map<String, dynamic> toJson() => _$SetReadMarkerParamsToJson(this);
|
Map<String, dynamic> toJson() => _$SetReadMarkerParamsToJson(this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,12 +14,7 @@ import '../../errors/parse_exception.dart';
|
|||||||
import '../../errors/server_exception.dart';
|
import '../../errors/server_exception.dart';
|
||||||
import '../nextcloud_ocs.dart';
|
import '../nextcloud_ocs.dart';
|
||||||
|
|
||||||
enum TalkApiMethod {
|
enum TalkApiMethod { get, post, put, delete }
|
||||||
get,
|
|
||||||
post,
|
|
||||||
put,
|
|
||||||
delete,
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract class TalkApi<T extends ApiResponse?> extends ApiRequest {
|
abstract class TalkApi<T extends ApiResponse?> extends ApiRequest {
|
||||||
String path;
|
String path;
|
||||||
@@ -31,11 +26,18 @@ abstract class TalkApi<T extends ApiResponse?> extends ApiRequest {
|
|||||||
|
|
||||||
TalkApi(this.path, this.body, {this.headers, this.getParameters});
|
TalkApi(this.path, this.body, {this.headers, this.getParameters});
|
||||||
|
|
||||||
Future<http.Response>? request(Uri uri, ApiParams? body, Map<String, String>? headers);
|
Future<http.Response>? request(
|
||||||
|
Uri uri,
|
||||||
|
ApiParams? body,
|
||||||
|
Map<String, String>? headers,
|
||||||
|
);
|
||||||
T assemble(String raw);
|
T assemble(String raw);
|
||||||
|
|
||||||
Future<T> run() async {
|
Future<T> run() async {
|
||||||
final endpoint = NextcloudOcs.uri('apps/spreed/api/$path', queryParameters: getParameters);
|
final endpoint = NextcloudOcs.uri(
|
||||||
|
'apps/spreed/api/$path',
|
||||||
|
queryParameters: getParameters,
|
||||||
|
);
|
||||||
final mergedHeaders = {...NextcloudOcs.headers(), ...?headers};
|
final mergedHeaders = {...NextcloudOcs.headers(), ...?headers};
|
||||||
|
|
||||||
final http.Response data;
|
final http.Response data;
|
||||||
@@ -60,8 +62,12 @@ abstract class TalkApi<T extends ApiResponse?> extends ApiRequest {
|
|||||||
if (status < 200 || status >= 300) {
|
if (status < 200 || status >= 300) {
|
||||||
final detail = 'Talk $endpoint -> HTTP $status';
|
final detail = 'Talk $endpoint -> HTTP $status';
|
||||||
log(detail);
|
log(detail);
|
||||||
if (status == 401) throw AuthException.unauthorized(technicalDetails: detail);
|
if (status == 401) {
|
||||||
if (status == 403) throw AuthException.forbidden(technicalDetails: detail);
|
throw AuthException.unauthorized(technicalDetails: detail);
|
||||||
|
}
|
||||||
|
if (status == 403) {
|
||||||
|
throw AuthException.forbidden(technicalDetails: detail);
|
||||||
|
}
|
||||||
if (status == 404) throw NotFoundException(technicalDetails: detail);
|
if (status == 404) throw NotFoundException(technicalDetails: detail);
|
||||||
throw ServerException(statusCode: status, technicalDetails: detail);
|
throw ServerException(statusCode: status, technicalDetails: detail);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
|
|
||||||
|
|
||||||
import '../../../../api_response.dart';
|
import '../../../../api_response.dart';
|
||||||
import '../../webdav_api.dart';
|
import '../../webdav_api.dart';
|
||||||
import 'download_file_params.dart';
|
import 'download_file_params.dart';
|
||||||
|
|||||||
@@ -10,8 +10,13 @@ class DownloadFileParams extends ApiParams {
|
|||||||
String localTargetPath;
|
String localTargetPath;
|
||||||
String filename;
|
String filename;
|
||||||
|
|
||||||
DownloadFileParams(this.webdavSourcePath, this.localTargetPath, this.filename);
|
DownloadFileParams(
|
||||||
|
this.webdavSourcePath,
|
||||||
|
this.localTargetPath,
|
||||||
|
this.filename,
|
||||||
|
);
|
||||||
|
|
||||||
factory DownloadFileParams.fromJson(Map<String, dynamic> json) => _$DownloadFileParamsFromJson(json);
|
factory DownloadFileParams.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$DownloadFileParamsFromJson(json);
|
||||||
Map<String, dynamic> toJson() => _$DownloadFileParamsToJson(this);
|
Map<String, dynamic> toJson() => _$DownloadFileParamsToJson(this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
import 'package:json_annotation/json_annotation.dart';
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
|
|
||||||
part 'download_file_response.g.dart';
|
part 'download_file_response.g.dart';
|
||||||
@@ -9,6 +8,7 @@ class DownloadFileResponse {
|
|||||||
|
|
||||||
DownloadFileResponse(this.path);
|
DownloadFileResponse(this.path);
|
||||||
|
|
||||||
factory DownloadFileResponse.fromJson(Map<String, dynamic> json) => _$DownloadFileResponseFromJson(json);
|
factory DownloadFileResponse.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$DownloadFileResponseFromJson(json);
|
||||||
Map<String, dynamic> toJson() => _$DownloadFileResponseToJson(this);
|
Map<String, dynamic> toJson() => _$DownloadFileResponseToJson(this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,16 @@ class CacheableFile {
|
|||||||
DateTime? modifiedAt;
|
DateTime? modifiedAt;
|
||||||
String? sort;
|
String? sort;
|
||||||
|
|
||||||
CacheableFile({required this.path, required this.isDirectory, required this.name, this.mimeType, this.size, this.eTag, this.createdAt, this.modifiedAt});
|
CacheableFile({
|
||||||
|
required this.path,
|
||||||
|
required this.isDirectory,
|
||||||
|
required this.name,
|
||||||
|
this.mimeType,
|
||||||
|
this.size,
|
||||||
|
this.eTag,
|
||||||
|
this.createdAt,
|
||||||
|
this.modifiedAt,
|
||||||
|
});
|
||||||
|
|
||||||
CacheableFile.fromDavFile(WebDavFile file) {
|
CacheableFile.fromDavFile(WebDavFile file) {
|
||||||
path = file.path.path;
|
path = file.path.path;
|
||||||
@@ -28,6 +37,7 @@ class CacheableFile {
|
|||||||
modifiedAt = file.lastModified;
|
modifiedAt = file.lastModified;
|
||||||
}
|
}
|
||||||
|
|
||||||
factory CacheableFile.fromJson(Map<String, dynamic> json) => _$CacheableFileFromJson(json);
|
factory CacheableFile.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$CacheableFileFromJson(json);
|
||||||
Map<String, dynamic> toJson() => _$CacheableFileToJson(this);
|
Map<String, dynamic> toJson() => _$CacheableFileToJson(this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
import 'package:nextcloud/nextcloud.dart';
|
import 'package:nextcloud/nextcloud.dart';
|
||||||
|
|
||||||
import '../../webdav_api.dart';
|
import '../../webdav_api.dart';
|
||||||
@@ -26,12 +25,15 @@ class ListFiles extends WebdavApi<ListFilesParams> {
|
|||||||
Future<ListFilesResponse> run() async {
|
Future<ListFilesResponse> run() async {
|
||||||
final webdav = await WebdavApi.webdav;
|
final webdav = await WebdavApi.webdav;
|
||||||
final timeout = _isRoot ? _rootTimeout : _subfolderTimeout;
|
final timeout = _isRoot ? _rootTimeout : _subfolderTimeout;
|
||||||
final davFiles = (await webdav.propfind(PathUri.parse(params.path)).timeout(timeout)).toWebDavFiles();
|
final davFiles =
|
||||||
|
(await webdav.propfind(PathUri.parse(params.path)).timeout(timeout))
|
||||||
|
.toWebDavFiles();
|
||||||
final files = davFiles.map(CacheableFile.fromDavFile).toSet();
|
final files = davFiles.map(CacheableFile.fromDavFile).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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,9 @@ class ListFilesCache extends SimpleCache<ListFilesResponse> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static String _documentId(String path) {
|
static String _documentId(String path) {
|
||||||
final cacheName = md5.convert(utf8.encode('MarianumMobile-$path')).toString();
|
final cacheName = md5
|
||||||
|
.convert(utf8.encode('MarianumMobile-$path'))
|
||||||
|
.toString();
|
||||||
return 'wd-folder-$cacheName';
|
return 'wd-folder-$cacheName';
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35,7 +37,10 @@ class ListFilesCache extends SimpleCache<ListFilesResponse> {
|
|||||||
/// `_FilesView` for that path via [CacheInvalidationBus] so it refetches
|
/// `_FilesView` for that path via [CacheInvalidationBus] so it refetches
|
||||||
/// even while it is sitting in the background of the navigation stack.
|
/// even while it is sitting in the background of the navigation stack.
|
||||||
static Future<void> invalidate(String path) async {
|
static Future<void> invalidate(String path) async {
|
||||||
await Localstore.instance.collection(RequestCache.collection).doc(_documentId(path)).delete();
|
await Localstore.instance
|
||||||
|
.collection(RequestCache.collection)
|
||||||
|
.doc(_documentId(path))
|
||||||
|
.delete();
|
||||||
CacheInvalidationBus.notifyListFiles(path);
|
CacheInvalidationBus.notifyListFiles(path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ class ListFilesParams extends ApiParams {
|
|||||||
|
|
||||||
ListFilesParams(this.path);
|
ListFilesParams(this.path);
|
||||||
|
|
||||||
factory ListFilesParams.fromJson(Map<String, dynamic> json) => _$ListFilesParamsFromJson(json);
|
factory ListFilesParams.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$ListFilesParamsFromJson(json);
|
||||||
Map<String, dynamic> toJson() => _$ListFilesParamsToJson(this);
|
Map<String, dynamic> toJson() => _$ListFilesParamsToJson(this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,15 +13,32 @@ class ListFilesResponse extends ApiResponse {
|
|||||||
|
|
||||||
ListFilesResponse(this.files);
|
ListFilesResponse(this.files);
|
||||||
|
|
||||||
factory ListFilesResponse.fromJson(Map<String, dynamic> json) => _$ListFilesResponseFromJson(json);
|
factory ListFilesResponse.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$ListFilesResponseFromJson(json);
|
||||||
Map<String, dynamic> toJson() => _$ListFilesResponseToJson(this);
|
Map<String, dynamic> toJson() => _$ListFilesResponseToJson(this);
|
||||||
|
|
||||||
List<CacheableFile> sortBy({bool foldersToTop = true, SortOption sortOption = SortOption.name, bool reversed = false}) {
|
List<CacheableFile> sortBy({
|
||||||
|
bool foldersToTop = true,
|
||||||
|
SortOption sortOption = SortOption.name,
|
||||||
|
bool reversed = false,
|
||||||
|
}) {
|
||||||
var list = List<CacheableFile>.empty(growable: true);
|
var list = List<CacheableFile>.empty(growable: true);
|
||||||
|
|
||||||
if (foldersToTop) {
|
if (foldersToTop) {
|
||||||
list.addAll(_sort(files.where((element) => element.isDirectory).toSet(), reversed: reversed, sortOption: sortOption));
|
list.addAll(
|
||||||
list.addAll(_sort(files.where((element) => !element.isDirectory).toSet(), reversed: reversed, sortOption: sortOption));
|
_sort(
|
||||||
|
files.where((element) => element.isDirectory).toSet(),
|
||||||
|
reversed: reversed,
|
||||||
|
sortOption: sortOption,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
list.addAll(
|
||||||
|
_sort(
|
||||||
|
files.where((element) => !element.isDirectory).toSet(),
|
||||||
|
reversed: reversed,
|
||||||
|
sortOption: sortOption,
|
||||||
|
),
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
list.addAll(_sort(files, reversed: reversed, sortOption: sortOption));
|
list.addAll(_sort(files, reversed: reversed, sortOption: sortOption));
|
||||||
}
|
}
|
||||||
@@ -29,13 +46,21 @@ class ListFilesResponse extends ApiResponse {
|
|||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<CacheableFile> _sort(Set<CacheableFile> files, {SortOption sortOption = SortOption.name, bool reversed = false}) {
|
List<CacheableFile> _sort(
|
||||||
|
Set<CacheableFile> files, {
|
||||||
|
SortOption sortOption = SortOption.name,
|
||||||
|
bool reversed = false,
|
||||||
|
}) {
|
||||||
for (var file in files) {
|
for (var file in files) {
|
||||||
final buffer = StringBuffer();
|
final buffer = StringBuffer();
|
||||||
|
|
||||||
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:
|
||||||
@@ -50,7 +75,6 @@ class ListFilesResponse extends ApiResponse {
|
|||||||
file.sort = buffer.toString();
|
file.sort = buffer.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
var list = files.toList()..sort((a, b) => b.sort!.compareTo(a.sort!));
|
var list = files.toList()..sort((a, b) => b.sort!.compareTo(a.sort!));
|
||||||
return reversed ? list.reversed.toList() : list;
|
return reversed ? list.reversed.toList() : list;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,12 @@ abstract class WebdavApi<T> extends ApiRequest {
|
|||||||
|
|
||||||
static Future<WebDavClient> webdav = establishWebdavConnection();
|
static Future<WebDavClient> webdav = establishWebdavConnection();
|
||||||
|
|
||||||
static Future<WebDavClient> establishWebdavConnection() async => NextcloudClient(Uri.parse('https://${EndpointData().nextcloud().full()}'), password: AccountData().getPassword(), loginName: AccountData().getUsername()).webdav;
|
static Future<WebDavClient> establishWebdavConnection() async =>
|
||||||
|
NextcloudClient(
|
||||||
|
Uri.parse('https://${EndpointData().nextcloud().full()}'),
|
||||||
|
password: AccountData().getPassword(),
|
||||||
|
loginName: AccountData().getUsername(),
|
||||||
|
).webdav;
|
||||||
|
|
||||||
/// Builds the WebDAV download URL without embedded credentials. Callers must
|
/// Builds the WebDAV download URL without embedded credentials. Callers must
|
||||||
/// authenticate via the [AccountData.authHeaders] header instead.
|
/// authenticate via the [AccountData.authHeaders] header instead.
|
||||||
|
|||||||
@@ -9,9 +9,9 @@ class GetBreakers extends MhslApi<GetBreakersResponse> {
|
|||||||
GetBreakers() : super('breaker/');
|
GetBreakers() : super('breaker/');
|
||||||
|
|
||||||
@override
|
@override
|
||||||
GetBreakersResponse assemble(String raw) => GetBreakersResponse.fromJson(jsonDecode(raw) as Map<String, dynamic>);
|
GetBreakersResponse assemble(String raw) =>
|
||||||
|
GetBreakersResponse.fromJson(jsonDecode(raw) as Map<String, dynamic>);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<Response>? request(Uri uri) => http.get(uri);
|
Future<Response>? request(Uri uri) => http.get(uri);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
import 'package:json_annotation/json_annotation.dart';
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
|
|
||||||
import '../../../api_response.dart';
|
import '../../../api_response.dart';
|
||||||
@@ -12,7 +11,8 @@ class GetBreakersResponse extends ApiResponse {
|
|||||||
|
|
||||||
GetBreakersResponse(this.global, this.regional);
|
GetBreakersResponse(this.global, this.regional);
|
||||||
|
|
||||||
factory GetBreakersResponse.fromJson(Map<String, dynamic> json) => _$GetBreakersResponseFromJson(json);
|
factory GetBreakersResponse.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$GetBreakersResponseFromJson(json);
|
||||||
Map<String, dynamic> toJson() => _$GetBreakersResponseToJson(this);
|
Map<String, dynamic> toJson() => _$GetBreakersResponseToJson(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -23,14 +23,20 @@ class GetBreakersReponseObject {
|
|||||||
|
|
||||||
GetBreakersReponseObject(this.areas, this.message);
|
GetBreakersReponseObject(this.areas, this.message);
|
||||||
|
|
||||||
factory GetBreakersReponseObject.fromJson(Map<String, dynamic> json) => _$GetBreakersReponseObjectFromJson(json);
|
factory GetBreakersReponseObject.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$GetBreakersReponseObjectFromJson(json);
|
||||||
Map<String, dynamic> toJson() => _$GetBreakersReponseObjectToJson(this);
|
Map<String, dynamic> toJson() => _$GetBreakersReponseObjectToJson(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
enum BreakerArea {
|
enum BreakerArea {
|
||||||
@JsonValue('GLOBAL') global,
|
@JsonValue('GLOBAL')
|
||||||
@JsonValue('TIMETABLE') timetable,
|
global,
|
||||||
@JsonValue('TALK') talk,
|
@JsonValue('TIMETABLE')
|
||||||
@JsonValue('FILES') files,
|
timetable,
|
||||||
@JsonValue('MORE') more,
|
@JsonValue('TALK')
|
||||||
|
talk,
|
||||||
|
@JsonValue('FILES')
|
||||||
|
files,
|
||||||
|
@JsonValue('MORE')
|
||||||
|
more,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ class AddCustomTimetableEventParams {
|
|||||||
|
|
||||||
AddCustomTimetableEventParams(this.user, this.event);
|
AddCustomTimetableEventParams(this.user, this.event);
|
||||||
|
|
||||||
factory AddCustomTimetableEventParams.fromJson(Map<String, dynamic> json) => _$AddCustomTimetableEventParamsFromJson(json);
|
factory AddCustomTimetableEventParams.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$AddCustomTimetableEventParamsFromJson(json);
|
||||||
Map<String, dynamic> toJson() => _$AddCustomTimetableEventParamsToJson(this);
|
Map<String, dynamic> toJson() => _$AddCustomTimetableEventParamsToJson(this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,9 +20,19 @@ class CustomTimetableEvent {
|
|||||||
@JsonKey(toJson: MhslApi.dateTimeToJson, fromJson: MhslApi.dateTimeFromJson)
|
@JsonKey(toJson: MhslApi.dateTimeToJson, fromJson: MhslApi.dateTimeFromJson)
|
||||||
DateTime updatedAt;
|
DateTime updatedAt;
|
||||||
|
|
||||||
CustomTimetableEvent({required this.id, required this.title, required this.description, required this.startDate,
|
CustomTimetableEvent({
|
||||||
required this.endDate, required this.color, required this.rrule, required this.createdAt, required this.updatedAt});
|
required this.id,
|
||||||
|
required this.title,
|
||||||
|
required this.description,
|
||||||
|
required this.startDate,
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,10 +9,12 @@ import 'get_custom_timetable_event_response.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.fromJson({'events': jsonDecode(raw)});
|
GetCustomTimetableEventResponse assemble(String raw) =>
|
||||||
|
GetCustomTimetableEventResponse.fromJson({'events': jsonDecode(raw)});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<Response>? request(Uri uri) => http.get(uri);
|
Future<Response>? request(Uri uri) => http.get(uri);
|
||||||
|
|||||||
@@ -3,7 +3,8 @@ import 'get_custom_timetable_event.dart';
|
|||||||
import 'get_custom_timetable_event_params.dart';
|
import 'get_custom_timetable_event_params.dart';
|
||||||
import 'get_custom_timetable_event_response.dart';
|
import 'get_custom_timetable_event_response.dart';
|
||||||
|
|
||||||
class GetCustomTimetableEventCache extends SimpleCache<GetCustomTimetableEventResponse> {
|
class GetCustomTimetableEventCache
|
||||||
|
extends SimpleCache<GetCustomTimetableEventResponse> {
|
||||||
GetCustomTimetableEventCache(
|
GetCustomTimetableEventCache(
|
||||||
GetCustomTimetableEventParams params, {
|
GetCustomTimetableEventParams params, {
|
||||||
super.onUpdate,
|
super.onUpdate,
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ class GetCustomTimetableEventParams {
|
|||||||
|
|
||||||
GetCustomTimetableEventParams(this.user);
|
GetCustomTimetableEventParams(this.user);
|
||||||
|
|
||||||
factory GetCustomTimetableEventParams.fromJson(Map<String, dynamic> json) => _$GetCustomTimetableEventParamsFromJson(json);
|
factory GetCustomTimetableEventParams.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$GetCustomTimetableEventParamsFromJson(json);
|
||||||
Map<String, dynamic> toJson() => _$GetCustomTimetableEventParamsToJson(this);
|
Map<String, dynamic> toJson() => _$GetCustomTimetableEventParamsToJson(this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ class GetCustomTimetableEventResponse extends ApiResponse {
|
|||||||
|
|
||||||
GetCustomTimetableEventResponse(this.events);
|
GetCustomTimetableEventResponse(this.events);
|
||||||
|
|
||||||
factory GetCustomTimetableEventResponse.fromJson(Map<String, dynamic> json) => _$GetCustomTimetableEventResponseFromJson(json);
|
factory GetCustomTimetableEventResponse.fromJson(Map<String, dynamic> json) =>
|
||||||
Map<String, dynamic> toJson() => _$GetCustomTimetableEventResponseToJson(this);
|
_$GetCustomTimetableEventResponseFromJson(json);
|
||||||
|
Map<String, dynamic> toJson() =>
|
||||||
|
_$GetCustomTimetableEventResponseToJson(this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,11 +9,13 @@ import 'remove_custom_timetable_event_params.dart';
|
|||||||
class RemoveCustomTimetableEvent extends MhslApi<void> {
|
class RemoveCustomTimetableEvent extends MhslApi<void> {
|
||||||
RemoveCustomTimetableEventParams params;
|
RemoveCustomTimetableEventParams params;
|
||||||
|
|
||||||
RemoveCustomTimetableEvent(this.params) : super('server/timetable/customEvents');
|
RemoveCustomTimetableEvent(this.params)
|
||||||
|
: super('server/timetable/customEvents');
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void assemble(String raw) {}
|
void assemble(String raw) {}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<Response>? request(Uri uri) => http.delete(uri, body: jsonEncode(params.toJson()));
|
Future<Response>? request(Uri uri) =>
|
||||||
|
http.delete(uri, body: jsonEncode(params.toJson()));
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-2
@@ -8,6 +8,9 @@ class RemoveCustomTimetableEventParams {
|
|||||||
|
|
||||||
RemoveCustomTimetableEventParams(this.id);
|
RemoveCustomTimetableEventParams(this.id);
|
||||||
|
|
||||||
factory RemoveCustomTimetableEventParams.fromJson(Map<String, dynamic> json) => _$RemoveCustomTimetableEventParamsFromJson(json);
|
factory RemoveCustomTimetableEventParams.fromJson(
|
||||||
Map<String, dynamic> toJson() => _$RemoveCustomTimetableEventParamsToJson(this);
|
Map<String, dynamic> json,
|
||||||
|
) => _$RemoveCustomTimetableEventParamsFromJson(json);
|
||||||
|
Map<String, dynamic> toJson() =>
|
||||||
|
_$RemoveCustomTimetableEventParamsToJson(this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,11 +9,13 @@ import 'update_custom_timetable_event_params.dart';
|
|||||||
class UpdateCustomTimetableEvent extends MhslApi<void> {
|
class UpdateCustomTimetableEvent extends MhslApi<void> {
|
||||||
UpdateCustomTimetableEventParams params;
|
UpdateCustomTimetableEventParams params;
|
||||||
|
|
||||||
UpdateCustomTimetableEvent(this.params) : super('server/timetable/customEvents');
|
UpdateCustomTimetableEvent(this.params)
|
||||||
|
: super('server/timetable/customEvents');
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void assemble(String raw) {}
|
void assemble(String raw) {}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<Response>? request(Uri uri) => http.patch(uri, body: jsonEncode(params.toJson()));
|
Future<Response>? request(Uri uri) =>
|
||||||
|
http.patch(uri, body: jsonEncode(params.toJson()));
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-3
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
import 'package:json_annotation/json_annotation.dart';
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
|
|
||||||
import '../custom_timetable_event.dart';
|
import '../custom_timetable_event.dart';
|
||||||
@@ -12,6 +11,9 @@ class UpdateCustomTimetableEventParams {
|
|||||||
|
|
||||||
UpdateCustomTimetableEventParams(this.id, this.event);
|
UpdateCustomTimetableEventParams(this.id, this.event);
|
||||||
|
|
||||||
factory UpdateCustomTimetableEventParams.fromJson(Map<String, dynamic> json) => _$UpdateCustomTimetableEventParamsFromJson(json);
|
factory UpdateCustomTimetableEventParams.fromJson(
|
||||||
Map<String, dynamic> toJson() => _$UpdateCustomTimetableEventParamsToJson(this);
|
Map<String, dynamic> json,
|
||||||
|
) => _$UpdateCustomTimetableEventParamsFromJson(json);
|
||||||
|
Map<String, dynamic> toJson() =>
|
||||||
|
_$UpdateCustomTimetableEventParamsToJson(this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,9 @@ abstract class MhslApi<T> extends ApiRequest {
|
|||||||
T assemble(String raw);
|
T assemble(String raw);
|
||||||
|
|
||||||
Future<T> run() async {
|
Future<T> run() async {
|
||||||
final endpoint = Uri.parse('https://mhsl.eu/marianum/marianummobile/$subpath');
|
final endpoint = Uri.parse(
|
||||||
|
'https://mhsl.eu/marianum/marianummobile/$subpath',
|
||||||
|
);
|
||||||
|
|
||||||
final http.Response data;
|
final http.Response data;
|
||||||
try {
|
try {
|
||||||
@@ -54,6 +56,7 @@ abstract class MhslApi<T> extends ApiRequest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:developer';
|
import 'dart:developer';
|
||||||
|
|
||||||
@@ -11,11 +10,8 @@ class NotifyRegister extends MhslApi<void> {
|
|||||||
NotifyRegisterParams params;
|
NotifyRegisterParams params;
|
||||||
NotifyRegister(this.params) : super('notify/register/');
|
NotifyRegister(this.params) : super('notify/register/');
|
||||||
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void assemble(String raw) {
|
void assemble(String raw) {}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<http.Response> request(Uri uri) {
|
Future<http.Response> request(Uri uri) {
|
||||||
|
|||||||
@@ -11,9 +11,10 @@ class NotifyRegisterParams {
|
|||||||
NotifyRegisterParams({
|
NotifyRegisterParams({
|
||||||
required this.username,
|
required this.username,
|
||||||
required this.password,
|
required this.password,
|
||||||
required this.fcmToken
|
required this.fcmToken,
|
||||||
});
|
});
|
||||||
|
|
||||||
factory NotifyRegisterParams.fromJson(Map<String, dynamic> json) => _$NotifyRegisterParamsFromJson(json);
|
factory NotifyRegisterParams.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$NotifyRegisterParamsFromJson(json);
|
||||||
Map<String, dynamic> toJson() => _$NotifyRegisterParamsToJson(this);
|
Map<String, dynamic> toJson() => _$NotifyRegisterParamsToJson(this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import 'package:http/http.dart' as http;
|
|||||||
import '../../mhsl_api.dart';
|
import '../../mhsl_api.dart';
|
||||||
import 'add_feedback_params.dart';
|
import 'add_feedback_params.dart';
|
||||||
|
|
||||||
|
|
||||||
class AddFeedback extends MhslApi<void> {
|
class AddFeedback extends MhslApi<void> {
|
||||||
AddFeedbackParams params;
|
AddFeedbackParams params;
|
||||||
AddFeedback(this.params) : super('server/feedback');
|
AddFeedback(this.params) : super('server/feedback');
|
||||||
@@ -15,5 +14,6 @@ class AddFeedback extends MhslApi<void> {
|
|||||||
void assemble(String raw) {}
|
void assemble(String raw) {}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<Response>? request(Uri uri) => http.post(uri, body: jsonEncode(params.toJson()));
|
Future<Response>? request(Uri uri) =>
|
||||||
|
http.post(uri, body: jsonEncode(params.toJson()));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ class AddFeedbackParams {
|
|||||||
String? screenshot;
|
String? screenshot;
|
||||||
int appVersion;
|
int appVersion;
|
||||||
|
|
||||||
|
|
||||||
AddFeedbackParams({
|
AddFeedbackParams({
|
||||||
required this.user,
|
required this.user,
|
||||||
required this.feedback,
|
required this.feedback,
|
||||||
@@ -17,6 +16,7 @@ class AddFeedbackParams {
|
|||||||
required this.appVersion,
|
required this.appVersion,
|
||||||
});
|
});
|
||||||
|
|
||||||
factory AddFeedbackParams.fromJson(Map<String, dynamic> json) => _$AddFeedbackParamsFromJson(json);
|
factory AddFeedbackParams.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$AddFeedbackParamsFromJson(json);
|
||||||
Map<String, dynamic> toJson() => _$AddFeedbackParamsToJson(this);
|
Map<String, dynamic> toJson() => _$AddFeedbackParamsToJson(this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,15 +10,15 @@ class UpdateUserIndexParams {
|
|||||||
int appVersion;
|
int appVersion;
|
||||||
String deviceInfo;
|
String deviceInfo;
|
||||||
|
|
||||||
|
|
||||||
UpdateUserIndexParams({
|
UpdateUserIndexParams({
|
||||||
required this.user,
|
required this.user,
|
||||||
required this.username,
|
required this.username,
|
||||||
required this.device,
|
required this.device,
|
||||||
required this.appVersion,
|
required this.appVersion,
|
||||||
required this.deviceInfo
|
required this.deviceInfo,
|
||||||
});
|
});
|
||||||
|
|
||||||
factory UpdateUserIndexParams.fromJson(Map<String, dynamic> json) => _$UpdateUserIndexParamsFromJson(json);
|
factory UpdateUserIndexParams.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$UpdateUserIndexParamsFromJson(json);
|
||||||
Map<String, dynamic> toJson() => _$UpdateUserIndexParamsToJson(this);
|
Map<String, dynamic> toJson() => _$UpdateUserIndexParamsToJson(this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:developer';
|
import 'dart:developer';
|
||||||
@@ -26,14 +25,18 @@ class UpdateUserIndex extends MhslApi<void> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static Future<void> index() async {
|
static Future<void> index() async {
|
||||||
unawaited(UpdateUserIndex(
|
unawaited(
|
||||||
|
UpdateUserIndex(
|
||||||
UpdateUserIndexParams(
|
UpdateUserIndexParams(
|
||||||
username: AccountData().getUsername(),
|
username: AccountData().getUsername(),
|
||||||
user: AccountData().getUserSecret(),
|
user: AccountData().getUserSecret(),
|
||||||
device: await AccountData().getDeviceId(),
|
device: await AccountData().getDeviceId(),
|
||||||
appVersion: int.parse((await PackageInfo.fromPlatform()).buildNumber),
|
appVersion: int.parse((await PackageInfo.fromPlatform()).buildNumber),
|
||||||
deviceInfo: jsonEncode((await DeviceInfoPlugin().deviceInfo).data).toString(),
|
deviceInfo: jsonEncode(
|
||||||
|
(await DeviceInfoPlugin().deviceInfo).data,
|
||||||
|
).toString(),
|
||||||
),
|
),
|
||||||
).run());
|
).run(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+22
-13
@@ -49,7 +49,10 @@ abstract class RequestCache<T extends ApiResponse?> {
|
|||||||
|
|
||||||
Future<void> start(String document) async {
|
Future<void> start(String document) async {
|
||||||
try {
|
try {
|
||||||
final tableData = await Localstore.instance.collection(collection).doc(document).get();
|
final tableData = await Localstore.instance
|
||||||
|
.collection(collection)
|
||||||
|
.doc(document)
|
||||||
|
.get();
|
||||||
if (tableData != null) {
|
if (tableData != null) {
|
||||||
final cached = onLocalData(tableData['json'] as String);
|
final cached = onLocalData(tableData['json'] as String);
|
||||||
onUpdate?.call(cached);
|
onUpdate?.call(cached);
|
||||||
@@ -57,7 +60,8 @@ abstract class RequestCache<T extends ApiResponse?> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final lastUpdate = (tableData?['lastupdate'] as num?) ?? 0;
|
final lastUpdate = (tableData?['lastupdate'] as num?) ?? 0;
|
||||||
if (DateTime.now().millisecondsSinceEpoch - (maxCacheTime * 1000) < lastUpdate) {
|
if (DateTime.now().millisecondsSinceEpoch - (maxCacheTime * 1000) <
|
||||||
|
lastUpdate) {
|
||||||
if (renew == null || !renew!) return;
|
if (renew == null || !renew!) return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,10 +69,12 @@ abstract class RequestCache<T extends ApiResponse?> {
|
|||||||
final newValue = await onLoad();
|
final newValue = await onLoad();
|
||||||
onUpdate?.call(newValue);
|
onUpdate?.call(newValue);
|
||||||
onNetworkData?.call(newValue);
|
onNetworkData?.call(newValue);
|
||||||
unawaited(Localstore.instance.collection(collection).doc(document).set({
|
unawaited(
|
||||||
|
Localstore.instance.collection(collection).doc(document).set({
|
||||||
'json': jsonEncode(newValue),
|
'json': jsonEncode(newValue),
|
||||||
'lastupdate': DateTime.now().millisecondsSinceEpoch,
|
'lastupdate': DateTime.now().millisecondsSinceEpoch,
|
||||||
}));
|
}),
|
||||||
|
);
|
||||||
} on Exception catch (e) {
|
} on Exception catch (e) {
|
||||||
onError(e);
|
onError(e);
|
||||||
}
|
}
|
||||||
@@ -79,7 +85,6 @@ abstract class RequestCache<T extends ApiResponse?> {
|
|||||||
|
|
||||||
T onLocalData(String json);
|
T onLocalData(String json);
|
||||||
Future<T> onLoad();
|
Future<T> onLoad();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Concrete [RequestCache] that takes the two overrides as constructor
|
/// Concrete [RequestCache] that takes the two overrides as constructor
|
||||||
@@ -112,7 +117,8 @@ class SimpleCache<T extends ApiResponse?> extends RequestCache<T> {
|
|||||||
Future<T> onLoad() => _loader();
|
Future<T> onLoad() => _loader();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
T onLocalData(String json) => _fromJson(jsonDecode(json) as Map<String, dynamic>);
|
T onLocalData(String json) =>
|
||||||
|
_fromJson(jsonDecode(json) as Map<String, dynamic>);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Captures the latest cache payload (cached or network) and rethrows the
|
/// Captures the latest cache payload (cached or network) and rethrows the
|
||||||
@@ -120,24 +126,27 @@ class SimpleCache<T extends ApiResponse?> extends RequestCache<T> {
|
|||||||
/// `latest`/`capturedError`/`await ready` boilerplate that DataProviders
|
/// `latest`/`capturedError`/`await ready` boilerplate that DataProviders
|
||||||
/// otherwise repeat per endpoint.
|
/// otherwise repeat per endpoint.
|
||||||
Future<T> resolveFromCache<T extends ApiResponse?>(
|
Future<T> resolveFromCache<T extends ApiResponse?>(
|
||||||
RequestCache<T> Function(void Function(T) onUpdate, void Function(Exception) onError) build, {
|
RequestCache<T> Function(
|
||||||
|
void Function(T) onUpdate,
|
||||||
|
void Function(Exception) onError,
|
||||||
|
)
|
||||||
|
build, {
|
||||||
void Function(Object)? onError,
|
void Function(Object)? onError,
|
||||||
String? operationName,
|
String? operationName,
|
||||||
}) async {
|
}) async {
|
||||||
T? latest;
|
T? latest;
|
||||||
Object? capturedError;
|
Object? capturedError;
|
||||||
final cache = build(
|
final cache = build((data) => latest = data, (e) {
|
||||||
(data) => latest = data,
|
|
||||||
(e) {
|
|
||||||
capturedError = e;
|
capturedError = e;
|
||||||
onError?.call(e);
|
onError?.call(e);
|
||||||
},
|
});
|
||||||
);
|
|
||||||
await cache.ready;
|
await cache.ready;
|
||||||
if (latest != null) return latest as T;
|
if (latest != null) return latest as T;
|
||||||
final err = capturedError;
|
final err = capturedError;
|
||||||
if (err != null) throw err;
|
if (err != null) throw err;
|
||||||
throw ParseException(
|
throw ParseException(
|
||||||
technicalDetails: operationName != null ? 'No data and no error from $operationName' : null,
|
technicalDetails: operationName != null
|
||||||
|
? 'No data and no error from $operationName'
|
||||||
|
: null,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,8 @@ import 'authenticate_response.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 {
|
||||||
@@ -17,7 +18,11 @@ class Authenticate extends WebuntisApi {
|
|||||||
try {
|
try {
|
||||||
final rawAnswer = await query(this);
|
final rawAnswer = await query(this);
|
||||||
final decoded = jsonDecode(rawAnswer) as Map<String, dynamic>;
|
final decoded = jsonDecode(rawAnswer) as Map<String, dynamic>;
|
||||||
final response = finalize(AuthenticateResponse.fromJson(decoded['result'] as Map<String, dynamic>));
|
final response = finalize(
|
||||||
|
AuthenticateResponse.fromJson(
|
||||||
|
decoded['result'] as Map<String, dynamic>,
|
||||||
|
),
|
||||||
|
);
|
||||||
_lastResponse = response;
|
_lastResponse = response;
|
||||||
if (!awaitedResponse.isCompleted) awaitedResponse.complete();
|
if (!awaitedResponse.isCompleted) awaitedResponse.complete();
|
||||||
return response;
|
return response;
|
||||||
@@ -43,7 +48,7 @@ class Authenticate extends WebuntisApi {
|
|||||||
AuthenticateParams(
|
AuthenticateParams(
|
||||||
user: AccountData().getUsername(),
|
user: AccountData().getUsername(),
|
||||||
password: AccountData().getPassword(),
|
password: AccountData().getPassword(),
|
||||||
)
|
),
|
||||||
).run();
|
).run();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,6 +62,5 @@ class Authenticate extends WebuntisApi {
|
|||||||
await createSession();
|
await createSession();
|
||||||
}
|
}
|
||||||
return _lastResponse!;
|
return _lastResponse!;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,12 +6,12 @@ part 'authenticate_params.g.dart';
|
|||||||
|
|
||||||
@JsonSerializable()
|
@JsonSerializable()
|
||||||
class AuthenticateParams extends ApiParams {
|
class AuthenticateParams extends ApiParams {
|
||||||
|
|
||||||
String user;
|
String user;
|
||||||
String password;
|
String password;
|
||||||
|
|
||||||
AuthenticateParams({required this.user, required this.password});
|
AuthenticateParams({required this.user, required this.password});
|
||||||
factory AuthenticateParams.fromJson(Map<String, dynamic> json) => _$AuthenticateParamsFromJson(json);
|
factory AuthenticateParams.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$AuthenticateParamsFromJson(json);
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => _$AuthenticateParamsToJson(this);
|
Map<String, dynamic> toJson() => _$AuthenticateParamsToJson(this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,14 +6,19 @@ part 'authenticate_response.g.dart';
|
|||||||
|
|
||||||
@JsonSerializable()
|
@JsonSerializable()
|
||||||
class AuthenticateResponse extends ApiResponse {
|
class AuthenticateResponse extends ApiResponse {
|
||||||
|
|
||||||
String sessionId;
|
String sessionId;
|
||||||
int personType;
|
int personType;
|
||||||
int personId;
|
int personId;
|
||||||
int klasseId;
|
int klasseId;
|
||||||
|
|
||||||
AuthenticateResponse(this.sessionId, this.personType, this.personId, this.klasseId);
|
AuthenticateResponse(
|
||||||
|
this.sessionId,
|
||||||
|
this.personType,
|
||||||
|
this.personId,
|
||||||
|
this.klasseId,
|
||||||
|
);
|
||||||
|
|
||||||
factory AuthenticateResponse.fromJson(Map<String, dynamic> json) => _$AuthenticateResponseFromJson(json);
|
factory AuthenticateResponse.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$AuthenticateResponseFromJson(json);
|
||||||
Map<String, dynamic> toJson() => _$AuthenticateResponseToJson(this);
|
Map<String, dynamic> toJson() => _$AuthenticateResponseToJson(this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,10 +9,17 @@ class GetHolidays extends WebuntisApi {
|
|||||||
@override
|
@override
|
||||||
Future<GetHolidaysResponse> run() async {
|
Future<GetHolidaysResponse> run() async {
|
||||||
final rawAnswer = await query(this);
|
final rawAnswer = await query(this);
|
||||||
return finalize(GetHolidaysResponse.fromJson(jsonDecode(rawAnswer) as Map<String, dynamic>));
|
return finalize(
|
||||||
|
GetHolidaysResponse.fromJson(
|
||||||
|
jsonDecode(rawAnswer) as Map<String, dynamic>,
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
static GetHolidaysResponseObject? find(GetHolidaysResponse holidaysResponse, {DateTime? time}) {
|
static GetHolidaysResponseObject? find(
|
||||||
|
GetHolidaysResponse holidaysResponse, {
|
||||||
|
DateTime? time,
|
||||||
|
}) {
|
||||||
time ??= DateTime.now();
|
time ??= DateTime.now();
|
||||||
time = DateTime(time.year, time.month, time.day, 0, 0, 0, 0, 0);
|
time = DateTime(time.year, time.month, time.day, 0, 0, 0, 0, 0);
|
||||||
|
|
||||||
@@ -24,5 +31,4 @@ class GetHolidays extends WebuntisApi {
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,8 @@ class GetHolidaysResponse extends ApiResponse {
|
|||||||
|
|
||||||
GetHolidaysResponse(this.result);
|
GetHolidaysResponse(this.result);
|
||||||
|
|
||||||
factory GetHolidaysResponse.fromJson(Map<String, dynamic> json) => _$GetHolidaysResponseFromJson(json);
|
factory GetHolidaysResponse.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$GetHolidaysResponseFromJson(json);
|
||||||
Map<String, dynamic> toJson() => _$GetHolidaysResponseToJson(this);
|
Map<String, dynamic> toJson() => _$GetHolidaysResponseToJson(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -22,8 +23,15 @@ class GetHolidaysResponseObject {
|
|||||||
int startDate;
|
int startDate;
|
||||||
int endDate;
|
int endDate;
|
||||||
|
|
||||||
GetHolidaysResponseObject(this.id, this.name, this.longName, this.startDate, this.endDate);
|
GetHolidaysResponseObject(
|
||||||
|
this.id,
|
||||||
|
this.name,
|
||||||
|
this.longName,
|
||||||
|
this.startDate,
|
||||||
|
this.endDate,
|
||||||
|
);
|
||||||
|
|
||||||
factory GetHolidaysResponseObject.fromJson(Map<String, dynamic> json) => _$GetHolidaysResponseObjectFromJson(json);
|
factory GetHolidaysResponseObject.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$GetHolidaysResponseObjectFromJson(json);
|
||||||
Map<String, dynamic> toJson() => _$GetHolidaysResponseObjectToJson(this);
|
Map<String, dynamic> toJson() => _$GetHolidaysResponseObjectToJson(this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,11 @@ class GetRooms extends WebuntisApi {
|
|||||||
Future<GetRoomsResponse> run() async {
|
Future<GetRoomsResponse> run() async {
|
||||||
final rawAnswer = await query(this);
|
final rawAnswer = await query(this);
|
||||||
try {
|
try {
|
||||||
return finalize(GetRoomsResponse.fromJson(jsonDecode(rawAnswer) as Map<String, dynamic>));
|
return finalize(
|
||||||
|
GetRoomsResponse.fromJson(
|
||||||
|
jsonDecode(rawAnswer) as Map<String, dynamic>,
|
||||||
|
),
|
||||||
|
);
|
||||||
} 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');
|
||||||
@@ -19,5 +23,4 @@ class GetRooms extends WebuntisApi {
|
|||||||
|
|
||||||
throw Exception('Failed to parse getRoom server response: $rawAnswer');
|
throw Exception('Failed to parse getRoom server response: $rawAnswer');
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,8 @@ class GetRoomsResponse extends ApiResponse {
|
|||||||
|
|
||||||
GetRoomsResponse(this.result);
|
GetRoomsResponse(this.result);
|
||||||
|
|
||||||
factory GetRoomsResponse.fromJson(Map<String, dynamic> json) => _$GetRoomsResponseFromJson(json);
|
factory GetRoomsResponse.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$GetRoomsResponseFromJson(json);
|
||||||
Map<String, dynamic> toJson() => _$GetRoomsResponseToJson(this);
|
Map<String, dynamic> toJson() => _$GetRoomsResponseToJson(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -22,8 +23,15 @@ class GetRoomsResponseObject {
|
|||||||
bool active;
|
bool active;
|
||||||
String building;
|
String building;
|
||||||
|
|
||||||
GetRoomsResponseObject(this.id, this.name, this.longName, this.active, this.building);
|
GetRoomsResponseObject(
|
||||||
|
this.id,
|
||||||
|
this.name,
|
||||||
|
this.longName,
|
||||||
|
this.active,
|
||||||
|
this.building,
|
||||||
|
);
|
||||||
|
|
||||||
factory GetRoomsResponseObject.fromJson(Map<String, dynamic> json) => _$GetRoomsResponseObjectFromJson(json);
|
factory GetRoomsResponseObject.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$GetRoomsResponseObjectFromJson(json);
|
||||||
Map<String, dynamic> toJson() => _$GetRoomsResponseObjectToJson(this);
|
Map<String, dynamic> toJson() => _$GetRoomsResponseObjectToJson(this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,10 @@ class GetSubjects extends WebuntisApi {
|
|||||||
@override
|
@override
|
||||||
Future<GetSubjectsResponse> run() async {
|
Future<GetSubjectsResponse> run() async {
|
||||||
final rawAnswer = await query(this);
|
final rawAnswer = await query(this);
|
||||||
return finalize(GetSubjectsResponse.fromJson(jsonDecode(rawAnswer) as Map<String, dynamic>));
|
return finalize(
|
||||||
|
GetSubjectsResponse.fromJson(
|
||||||
|
jsonDecode(rawAnswer) as Map<String, dynamic>,
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,8 @@ class GetSubjectsResponse extends ApiResponse {
|
|||||||
|
|
||||||
GetSubjectsResponse(this.result);
|
GetSubjectsResponse(this.result);
|
||||||
|
|
||||||
factory GetSubjectsResponse.fromJson(Map<String, dynamic> json) => _$GetSubjectsResponseFromJson(json);
|
factory GetSubjectsResponse.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$GetSubjectsResponseFromJson(json);
|
||||||
Map<String, dynamic> toJson() => _$GetSubjectsResponseToJson(this);
|
Map<String, dynamic> toJson() => _$GetSubjectsResponseToJson(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -22,8 +23,15 @@ class GetSubjectsResponseObject {
|
|||||||
String alternateName;
|
String alternateName;
|
||||||
bool active;
|
bool active;
|
||||||
|
|
||||||
GetSubjectsResponseObject(this.id, this.name, this.longName, this.alternateName, this.active);
|
GetSubjectsResponseObject(
|
||||||
|
this.id,
|
||||||
|
this.name,
|
||||||
|
this.longName,
|
||||||
|
this.alternateName,
|
||||||
|
this.active,
|
||||||
|
);
|
||||||
|
|
||||||
factory GetSubjectsResponseObject.fromJson(Map<String, dynamic> json) => _$GetSubjectsResponseObjectFromJson(json);
|
factory GetSubjectsResponseObject.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$GetSubjectsResponseObjectFromJson(json);
|
||||||
Map<String, dynamic> toJson() => _$GetSubjectsResponseObjectToJson(this);
|
Map<String, dynamic> toJson() => _$GetSubjectsResponseObjectToJson(this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,12 +11,20 @@ class GetTimegridUnits extends WebuntisApi {
|
|||||||
Future<GetTimegridUnitsResponse> run() async {
|
Future<GetTimegridUnitsResponse> run() async {
|
||||||
final rawAnswer = await query(this);
|
final rawAnswer = await query(this);
|
||||||
try {
|
try {
|
||||||
return finalize(GetTimegridUnitsResponse.fromJson(jsonDecode(rawAnswer) as Map<String, dynamic>));
|
return finalize(
|
||||||
|
GetTimegridUnitsResponse.fromJson(
|
||||||
|
jsonDecode(rawAnswer) as Map<String, dynamic>,
|
||||||
|
),
|
||||||
|
);
|
||||||
} catch (e, trace) {
|
} catch (e, trace) {
|
||||||
log(trace.toString());
|
log(trace.toString());
|
||||||
log('Failed to parse getTimegridUnits data with server response: $rawAnswer');
|
log(
|
||||||
|
'Failed to parse getTimegridUnits data with server response: $rawAnswer',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw Exception('Failed to parse getTimegridUnits server response: $rawAnswer');
|
throw Exception(
|
||||||
|
'Failed to parse getTimegridUnits server response: $rawAnswer',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,8 @@ class GetTimegridUnitsResponse extends ApiResponse {
|
|||||||
|
|
||||||
GetTimegridUnitsResponse(this.result);
|
GetTimegridUnitsResponse(this.result);
|
||||||
|
|
||||||
factory GetTimegridUnitsResponse.fromJson(Map<String, dynamic> json) => _$GetTimegridUnitsResponseFromJson(json);
|
factory GetTimegridUnitsResponse.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$GetTimegridUnitsResponseFromJson(json);
|
||||||
Map<String, dynamic> toJson() => _$GetTimegridUnitsResponseToJson(this);
|
Map<String, dynamic> toJson() => _$GetTimegridUnitsResponseToJson(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -21,7 +22,8 @@ class GetTimegridUnitsResponseDay {
|
|||||||
|
|
||||||
GetTimegridUnitsResponseDay(this.day, this.timeUnits);
|
GetTimegridUnitsResponseDay(this.day, this.timeUnits);
|
||||||
|
|
||||||
factory GetTimegridUnitsResponseDay.fromJson(Map<String, dynamic> json) => _$GetTimegridUnitsResponseDayFromJson(json);
|
factory GetTimegridUnitsResponseDay.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$GetTimegridUnitsResponseDayFromJson(json);
|
||||||
Map<String, dynamic> toJson() => _$GetTimegridUnitsResponseDayToJson(this);
|
Map<String, dynamic> toJson() => _$GetTimegridUnitsResponseDayToJson(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -33,6 +35,7 @@ class GetTimegridUnitsResponseUnit {
|
|||||||
|
|
||||||
GetTimegridUnitsResponseUnit(this.name, this.startTime, this.endTime);
|
GetTimegridUnitsResponseUnit(this.name, this.startTime, this.endTime);
|
||||||
|
|
||||||
factory GetTimegridUnitsResponseUnit.fromJson(Map<String, dynamic> json) => _$GetTimegridUnitsResponseUnitFromJson(json);
|
factory GetTimegridUnitsResponseUnit.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$GetTimegridUnitsResponseUnitFromJson(json);
|
||||||
Map<String, dynamic> toJson() => _$GetTimegridUnitsResponseUnitToJson(this);
|
Map<String, dynamic> toJson() => _$GetTimegridUnitsResponseUnitToJson(this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,10 @@ class GetTimetable extends WebuntisApi {
|
|||||||
@override
|
@override
|
||||||
Future<GetTimetableResponse> run() async {
|
Future<GetTimetableResponse> run() async {
|
||||||
final rawAnswer = await query(this);
|
final rawAnswer = await query(this);
|
||||||
return finalize(GetTimetableResponse.fromJson(jsonDecode(rawAnswer) as Map<String, dynamic>));
|
return finalize(
|
||||||
|
GetTimetableResponse.fromJson(
|
||||||
|
jsonDecode(rawAnswer) as Map<String, dynamic>,
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,11 +10,11 @@ class GetTimetableParams extends ApiParams {
|
|||||||
|
|
||||||
GetTimetableParams({required this.options});
|
GetTimetableParams({required this.options});
|
||||||
|
|
||||||
factory GetTimetableParams.fromJson(Map<String, dynamic> json) => _$GetTimetableParamsFromJson(json);
|
factory GetTimetableParams.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$GetTimetableParamsFromJson(json);
|
||||||
Map<String, dynamic> toJson() => _$GetTimetableParamsToJson(this);
|
Map<String, dynamic> toJson() => _$GetTimetableParamsToJson(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@JsonSerializable(explicitToJson: true)
|
@JsonSerializable(explicitToJson: true)
|
||||||
class GetTimetableParamsOptions {
|
class GetTimetableParamsOptions {
|
||||||
GetTimetableParamsOptionsElement element;
|
GetTimetableParamsOptionsElement element;
|
||||||
@@ -59,20 +59,30 @@ class GetTimetableParamsOptions {
|
|||||||
this.klasseFields,
|
this.klasseFields,
|
||||||
this.roomFields,
|
this.roomFields,
|
||||||
this.subjectFields,
|
this.subjectFields,
|
||||||
this.teacherFields
|
this.teacherFields,
|
||||||
});
|
});
|
||||||
|
|
||||||
factory GetTimetableParamsOptions.fromJson(Map<String, dynamic> json) => _$GetTimetableParamsOptionsFromJson(json);
|
factory GetTimetableParamsOptions.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$GetTimetableParamsOptionsFromJson(json);
|
||||||
Map<String, dynamic> toJson() => _$GetTimetableParamsOptionsToJson(this);
|
Map<String, dynamic> toJson() => _$GetTimetableParamsOptionsToJson(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
enum GetTimetableParamsOptionsFields {
|
enum GetTimetableParamsOptionsFields {
|
||||||
@JsonValue('id') id,
|
@JsonValue('id')
|
||||||
@JsonValue('name') name,
|
id,
|
||||||
@JsonValue('longname') longname,
|
@JsonValue('name')
|
||||||
@JsonValue('externalkey') externalkey;
|
name,
|
||||||
|
@JsonValue('longname')
|
||||||
|
longname,
|
||||||
|
@JsonValue('externalkey')
|
||||||
|
externalkey;
|
||||||
|
|
||||||
static List<GetTimetableParamsOptionsFields> all = [id, name, longname, externalkey];
|
static List<GetTimetableParamsOptionsFields> all = [
|
||||||
|
id,
|
||||||
|
name,
|
||||||
|
longname,
|
||||||
|
externalkey,
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsonSerializable()
|
@JsonSerializable()
|
||||||
@@ -82,13 +92,23 @@ class GetTimetableParamsOptionsElement {
|
|||||||
@JsonKey(includeIfNull: false)
|
@JsonKey(includeIfNull: false)
|
||||||
GetTimetableParamsOptionsElementKeyType? keyType;
|
GetTimetableParamsOptionsElementKeyType? keyType;
|
||||||
|
|
||||||
GetTimetableParamsOptionsElement({required this.id, required this.type, this.keyType});
|
GetTimetableParamsOptionsElement({
|
||||||
factory GetTimetableParamsOptionsElement.fromJson(Map<String, dynamic> json) => _$GetTimetableParamsOptionsElementFromJson(json);
|
required this.id,
|
||||||
Map<String, dynamic> toJson() => _$GetTimetableParamsOptionsElementToJson(this);
|
required this.type,
|
||||||
|
this.keyType,
|
||||||
|
});
|
||||||
|
factory GetTimetableParamsOptionsElement.fromJson(
|
||||||
|
Map<String, dynamic> json,
|
||||||
|
) => _$GetTimetableParamsOptionsElementFromJson(json);
|
||||||
|
Map<String, dynamic> toJson() =>
|
||||||
|
_$GetTimetableParamsOptionsElementToJson(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
enum GetTimetableParamsOptionsElementKeyType {
|
enum GetTimetableParamsOptionsElementKeyType {
|
||||||
@JsonValue('id') id,
|
@JsonValue('id')
|
||||||
@JsonValue('name') name,
|
id,
|
||||||
@JsonValue('externalkey') externalkey
|
@JsonValue('name')
|
||||||
|
name,
|
||||||
|
@JsonValue('externalkey')
|
||||||
|
externalkey,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,9 +10,9 @@ class GetTimetableResponse extends ApiResponse {
|
|||||||
|
|
||||||
GetTimetableResponse(this.result);
|
GetTimetableResponse(this.result);
|
||||||
|
|
||||||
factory GetTimetableResponse.fromJson(Map<String, dynamic> json) => _$GetTimetableResponseFromJson(json);
|
factory GetTimetableResponse.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$GetTimetableResponseFromJson(json);
|
||||||
Map<String, dynamic> toJson() => _$GetTimetableResponseToJson(this);
|
Map<String, dynamic> toJson() => _$GetTimetableResponseToJson(this);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsonSerializable(explicitToJson: true)
|
@JsonSerializable(explicitToJson: true)
|
||||||
@@ -55,10 +55,11 @@ class GetTimetableResponseObject {
|
|||||||
required this.kl,
|
required this.kl,
|
||||||
required this.te,
|
required this.te,
|
||||||
required this.su,
|
required this.su,
|
||||||
required this.ro
|
required this.ro,
|
||||||
});
|
});
|
||||||
|
|
||||||
factory GetTimetableResponseObject.fromJson(Map<String, dynamic> json) => _$GetTimetableResponseObjectFromJson(json);
|
factory GetTimetableResponseObject.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$GetTimetableResponseObjectFromJson(json);
|
||||||
Map<String, dynamic> toJson() => _$GetTimetableResponseObjectToJson(this);
|
Map<String, dynamic> toJson() => _$GetTimetableResponseObjectToJson(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,8 +69,11 @@ class GetTimetableResponseObjectFields {
|
|||||||
|
|
||||||
GetTimetableResponseObjectFields(this.te);
|
GetTimetableResponseObjectFields(this.te);
|
||||||
|
|
||||||
factory GetTimetableResponseObjectFields.fromJson(Map<String, dynamic> json) => _$GetTimetableResponseObjectFieldsFromJson(json);
|
factory GetTimetableResponseObjectFields.fromJson(
|
||||||
Map<String, dynamic> toJson() => _$GetTimetableResponseObjectFieldsToJson(this);
|
Map<String, dynamic> json,
|
||||||
|
) => _$GetTimetableResponseObjectFieldsFromJson(json);
|
||||||
|
Map<String, dynamic> toJson() =>
|
||||||
|
_$GetTimetableResponseObjectFieldsToJson(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsonSerializable()
|
@JsonSerializable()
|
||||||
@@ -79,10 +83,18 @@ class GetTimetableResponseObjectFieldsObject {
|
|||||||
String? longname;
|
String? longname;
|
||||||
String? externalkey;
|
String? externalkey;
|
||||||
|
|
||||||
GetTimetableResponseObjectFieldsObject({this.id, this.name, this.longname, this.externalkey});
|
GetTimetableResponseObjectFieldsObject({
|
||||||
|
this.id,
|
||||||
|
this.name,
|
||||||
|
this.longname,
|
||||||
|
this.externalkey,
|
||||||
|
});
|
||||||
|
|
||||||
factory GetTimetableResponseObjectFieldsObject.fromJson(Map<String, dynamic> json) => _$GetTimetableResponseObjectFieldsObjectFromJson(json);
|
factory GetTimetableResponseObjectFieldsObject.fromJson(
|
||||||
Map<String, dynamic> toJson() => _$GetTimetableResponseObjectFieldsObjectToJson(this);
|
Map<String, dynamic> json,
|
||||||
|
) => _$GetTimetableResponseObjectFieldsObjectFromJson(json);
|
||||||
|
Map<String, dynamic> toJson() =>
|
||||||
|
_$GetTimetableResponseObjectFieldsObjectToJson(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsonSerializable()
|
@JsonSerializable()
|
||||||
@@ -92,10 +104,17 @@ class GetTimetableResponseObjectClass {
|
|||||||
String longname;
|
String longname;
|
||||||
String? externalkey;
|
String? externalkey;
|
||||||
|
|
||||||
GetTimetableResponseObjectClass(this.id, this.name, this.longname, this.externalkey);
|
GetTimetableResponseObjectClass(
|
||||||
|
this.id,
|
||||||
|
this.name,
|
||||||
|
this.longname,
|
||||||
|
this.externalkey,
|
||||||
|
);
|
||||||
|
|
||||||
factory GetTimetableResponseObjectClass.fromJson(Map<String, dynamic> json) => _$GetTimetableResponseObjectClassFromJson(json);
|
factory GetTimetableResponseObjectClass.fromJson(Map<String, dynamic> json) =>
|
||||||
Map<String, dynamic> toJson() => _$GetTimetableResponseObjectClassToJson(this);
|
_$GetTimetableResponseObjectClassFromJson(json);
|
||||||
|
Map<String, dynamic> toJson() =>
|
||||||
|
_$GetTimetableResponseObjectClassToJson(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsonSerializable()
|
@JsonSerializable()
|
||||||
@@ -107,11 +126,20 @@ class GetTimetableResponseObjectTeacher {
|
|||||||
String? orgname;
|
String? orgname;
|
||||||
String? externalkey;
|
String? externalkey;
|
||||||
|
|
||||||
|
GetTimetableResponseObjectTeacher(
|
||||||
|
this.id,
|
||||||
|
this.name,
|
||||||
|
this.longname,
|
||||||
|
this.orgid,
|
||||||
|
this.orgname,
|
||||||
|
this.externalkey,
|
||||||
|
);
|
||||||
|
|
||||||
GetTimetableResponseObjectTeacher(this.id, this.name, this.longname, this.orgid, this.orgname, this.externalkey);
|
factory GetTimetableResponseObjectTeacher.fromJson(
|
||||||
|
Map<String, dynamic> json,
|
||||||
factory GetTimetableResponseObjectTeacher.fromJson(Map<String, dynamic> json) => _$GetTimetableResponseObjectTeacherFromJson(json);
|
) => _$GetTimetableResponseObjectTeacherFromJson(json);
|
||||||
Map<String, dynamic> toJson() => _$GetTimetableResponseObjectTeacherToJson(this);
|
Map<String, dynamic> toJson() =>
|
||||||
|
_$GetTimetableResponseObjectTeacherToJson(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsonSerializable()
|
@JsonSerializable()
|
||||||
@@ -122,8 +150,11 @@ class GetTimetableResponseObjectSubject {
|
|||||||
|
|
||||||
GetTimetableResponseObjectSubject(this.id, this.name, this.longname);
|
GetTimetableResponseObjectSubject(this.id, this.name, this.longname);
|
||||||
|
|
||||||
factory GetTimetableResponseObjectSubject.fromJson(Map<String, dynamic> json) => _$GetTimetableResponseObjectSubjectFromJson(json);
|
factory GetTimetableResponseObjectSubject.fromJson(
|
||||||
Map<String, dynamic> toJson() => _$GetTimetableResponseObjectSubjectToJson(this);
|
Map<String, dynamic> json,
|
||||||
|
) => _$GetTimetableResponseObjectSubjectFromJson(json);
|
||||||
|
Map<String, dynamic> toJson() =>
|
||||||
|
_$GetTimetableResponseObjectSubjectToJson(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsonSerializable()
|
@JsonSerializable()
|
||||||
@@ -134,6 +165,7 @@ class GetTimetableResponseObjectRoom {
|
|||||||
|
|
||||||
GetTimetableResponseObjectRoom(this.id, this.name, this.longname);
|
GetTimetableResponseObjectRoom(this.id, this.name, this.longname);
|
||||||
|
|
||||||
factory GetTimetableResponseObjectRoom.fromJson(Map<String, dynamic> json) => _$GetTimetableResponseObjectRoomFromJson(json);
|
factory GetTimetableResponseObjectRoom.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$GetTimetableResponseObjectRoomFromJson(json);
|
||||||
Map<String, dynamic> toJson() => _$GetTimetableResponseObjectRoomToJson(this);
|
Map<String, dynamic> toJson() => _$GetTimetableResponseObjectRoomToJson(this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,10 +9,14 @@ import '../queries/get_subjects/get_subjects_response.dart';
|
|||||||
/// When a record is missing the resolver returns a placeholder fallback
|
/// When a record is missing the resolver returns a placeholder fallback
|
||||||
/// instead of `null` so call sites stay branch-free.
|
/// instead of `null` so call sites stay branch-free.
|
||||||
class LessonResolver {
|
class LessonResolver {
|
||||||
static GetSubjectsResponseObject resolveSubject(TimetableState state, int? id) {
|
static GetSubjectsResponseObject resolveSubject(
|
||||||
|
TimetableState state,
|
||||||
|
int? id,
|
||||||
|
) {
|
||||||
final fallback = GetSubjectsResponseObject(0, '?', 'Unbekannt', '?', true);
|
final fallback = GetSubjectsResponseObject(0, '?', 'Unbekannt', '?', true);
|
||||||
if (id == null) return fallback;
|
if (id == null) return fallback;
|
||||||
return state.subjects?.result.firstWhereOrNull((s) => s.id == id) ?? fallback;
|
return state.subjects?.result.firstWhereOrNull((s) => s.id == id) ??
|
||||||
|
fallback;
|
||||||
}
|
}
|
||||||
|
|
||||||
static GetRoomsResponseObject resolveRoom(TimetableState state, int? id) {
|
static GetRoomsResponseObject resolveRoom(TimetableState state, int? id) {
|
||||||
@@ -61,9 +65,7 @@ class LessonFormatter {
|
|||||||
/// optional longname (rendered in parentheses if it differs from `name`),
|
/// optional longname (rendered in parentheses if it differs from `name`),
|
||||||
/// and optional extra info (joined with `·`).
|
/// and optional extra info (joined with `·`).
|
||||||
static String formatLine(String name, {String? longname, String? extra}) {
|
static String formatLine(String name, {String? longname, String? extra}) {
|
||||||
final parts = <String>[
|
final parts = <String>[if (name.isNotEmpty) name else '?'];
|
||||||
if (name.isNotEmpty) name else '?',
|
|
||||||
];
|
|
||||||
final ln = (longname ?? '').trim();
|
final ln = (longname ?? '').trim();
|
||||||
if (ln.isNotEmpty && ln != name) parts.add('($ln)');
|
if (ln.isNotEmpty && ln != name) parts.add('($ln)');
|
||||||
final ex = (extra ?? '').trim();
|
final ex = (extra ?? '').trim();
|
||||||
|
|||||||
@@ -14,18 +14,24 @@ import 'queries/authenticate/authenticate.dart';
|
|||||||
import 'webuntis_error.dart';
|
import 'webuntis_error.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;
|
||||||
|
|
||||||
bool authenticatedResponse;
|
bool authenticatedResponse;
|
||||||
|
|
||||||
WebuntisApi(this.method, this.genericParam, {this.authenticatedResponse = true});
|
WebuntisApi(
|
||||||
|
this.method,
|
||||||
|
this.genericParam, {
|
||||||
|
this.authenticatedResponse = true,
|
||||||
|
});
|
||||||
|
|
||||||
Future<String> query(WebuntisApi untis, {bool retry = false}) async {
|
Future<String> query(WebuntisApi untis, {bool retry = false}) async {
|
||||||
final body = '{"id":"ID","method":"$method","params":${untis._body()},"jsonrpc":"2.0"}';
|
final body =
|
||||||
|
'{"id":"ID","method":"$method","params":${untis._body()},"jsonrpc":"2.0"}';
|
||||||
|
|
||||||
var sessionId = '0';
|
var sessionId = '0';
|
||||||
if (authenticatedResponse) {
|
if (authenticatedResponse) {
|
||||||
@@ -38,13 +44,20 @@ abstract class WebuntisApi extends ApiRequest {
|
|||||||
try {
|
try {
|
||||||
jsonData = jsonDecode(data.body) as Map<String, dynamic>;
|
jsonData = jsonDecode(data.body) as Map<String, dynamic>;
|
||||||
} on FormatException catch (e) {
|
} on FormatException catch (e) {
|
||||||
throw ParseException(technicalDetails: 'WebUntis JSON decode: ${e.message}');
|
throw ParseException(
|
||||||
|
technicalDetails: 'WebUntis JSON decode: ${e.message}',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
final error = jsonData['error'] as Map<String, dynamic>?;
|
final error = jsonData['error'] as Map<String, dynamic>?;
|
||||||
if (error != null) {
|
if (error != null) {
|
||||||
final code = error['code'] as int;
|
final code = error['code'] as int;
|
||||||
if (code == -8520) {
|
if (code == -8520) {
|
||||||
if (retry) throw WebuntisError('Authentication was tried (probably session timeout), but was not successful!', -8520);
|
if (retry) {
|
||||||
|
throw WebuntisError(
|
||||||
|
'Authentication was tried (probably session timeout), but was not successful!',
|
||||||
|
-8520,
|
||||||
|
);
|
||||||
|
}
|
||||||
await Authenticate.createSession();
|
await Authenticate.createSession();
|
||||||
return query(untis, retry: true);
|
return query(untis, retry: true);
|
||||||
} else {
|
} else {
|
||||||
@@ -65,14 +78,22 @@ abstract class WebuntisApi extends ApiRequest {
|
|||||||
|
|
||||||
Future<http.Response> post(String data, Map<String, String>? headers) async {
|
Future<http.Response> post(String data, Map<String, String>? headers) async {
|
||||||
try {
|
try {
|
||||||
return await http.post(endpoint, body: data, headers: headers).timeout(
|
return await http
|
||||||
|
.post(endpoint, body: data, headers: headers)
|
||||||
|
.timeout(
|
||||||
const Duration(seconds: 10),
|
const Duration(seconds: 10),
|
||||||
onTimeout: () => throw NetworkException.timeout(technicalDetails: 'WebUntis $method timed out after 10s'),
|
onTimeout: () => throw NetworkException.timeout(
|
||||||
|
technicalDetails: 'WebUntis $method timed out after 10s',
|
||||||
|
),
|
||||||
);
|
);
|
||||||
} on SocketException catch (e) {
|
} on SocketException catch (e) {
|
||||||
throw NetworkException(technicalDetails: 'WebUntis $method: ${e.message}');
|
throw NetworkException(
|
||||||
|
technicalDetails: 'WebUntis $method: ${e.message}',
|
||||||
|
);
|
||||||
} on http.ClientException catch (e) {
|
} on http.ClientException catch (e) {
|
||||||
throw NetworkException(technicalDetails: 'WebUntis $method: ${e.message}');
|
throw NetworkException(
|
||||||
|
technicalDetails: 'WebUntis $method: ${e.message}',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+12
-4
@@ -95,7 +95,9 @@ class _AppState extends State<App> with WidgetsBindingObserver {
|
|||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
NotificationController.onForegroundMessageHandler(message, context);
|
NotificationController.onForegroundMessageHandler(message, context);
|
||||||
});
|
});
|
||||||
FirebaseMessaging.onBackgroundMessage(NotificationController.onBackgroundMessageHandler);
|
FirebaseMessaging.onBackgroundMessage(
|
||||||
|
NotificationController.onBackgroundMessageHandler,
|
||||||
|
);
|
||||||
|
|
||||||
FirebaseMessaging.onMessageOpenedApp.listen((message) {
|
FirebaseMessaging.onMessageOpenedApp.listen((message) {
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
@@ -119,7 +121,9 @@ class _AppState extends State<App> with WidgetsBindingObserver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) => BlocBuilder<SettingsCubit, model.Settings>(
|
Widget build(
|
||||||
|
BuildContext context,
|
||||||
|
) => BlocBuilder<SettingsCubit, model.Settings>(
|
||||||
builder: (context, _) {
|
builder: (context, _) {
|
||||||
final bottomBarModules = AppModule.getBottomBarModules(context);
|
final bottomBarModules = AppModule.getBottomBarModules(context);
|
||||||
final totalTabs = bottomBarModules.length + 1;
|
final totalTabs = bottomBarModules.length + 1;
|
||||||
@@ -136,7 +140,9 @@ class _AppState extends State<App> with WidgetsBindingObserver {
|
|||||||
// user sees stale content. Re-mounting clears those stacks; the
|
// user sees stale content. Re-mounting clears those stacks; the
|
||||||
// trade-off (losing in-tab pushed routes on a settings change) is
|
// trade-off (losing in-tab pushed routes on a settings change) is
|
||||||
// acceptable since the user explicitly re-shaped the bar.
|
// acceptable since the user explicitly re-shaped the bar.
|
||||||
final layoutKey = ValueKey('${bottomBarModules.map((m) => m.module.name).join('|')}|more');
|
final layoutKey = ValueKey(
|
||||||
|
'${bottomBarModules.map((m) => m.module.name).join('|')}|more',
|
||||||
|
);
|
||||||
|
|
||||||
if (totalTabs != _knownTotalTabs) {
|
if (totalTabs != _knownTotalTabs) {
|
||||||
var targetIndex = currentIndex;
|
var targetIndex = currentIndex;
|
||||||
@@ -153,7 +159,9 @@ class _AppState extends State<App> with WidgetsBindingObserver {
|
|||||||
// new PTV mounts cleanly.
|
// new PTV mounts cleanly.
|
||||||
if (targetIndex != currentIndex) {
|
if (targetIndex != currentIndex) {
|
||||||
Main.bottomNavigator.removeListener(_onTabControllerChanged);
|
Main.bottomNavigator.removeListener(_onTabControllerChanged);
|
||||||
Main.bottomNavigator = PersistentTabController(initialIndex: targetIndex);
|
Main.bottomNavigator = PersistentTabController(
|
||||||
|
initialIndex: targetIndex,
|
||||||
|
);
|
||||||
Main.bottomNavigator.addListener(_onTabControllerChanged);
|
Main.bottomNavigator.addListener(_onTabControllerChanged);
|
||||||
_userOnLastTab = targetIndex == totalTabs - 1;
|
_userOnLastTab = targetIndex == totalTabs - 1;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,11 +2,14 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:jiffy/jiffy.dart';
|
import 'package:jiffy/jiffy.dart';
|
||||||
|
|
||||||
extension IsSameDay on DateTime {
|
extension IsSameDay on DateTime {
|
||||||
bool isSameDay(DateTime other) => year == other.year && month == other.month && day == other.day;
|
bool isSameDay(DateTime other) =>
|
||||||
|
year == other.year && month == other.month && day == other.day;
|
||||||
|
|
||||||
DateTime nextWeekday(int day) => add(Duration(days: (day - weekday) % DateTime.daysPerWeek));
|
DateTime nextWeekday(int day) =>
|
||||||
|
add(Duration(days: (day - weekday) % DateTime.daysPerWeek));
|
||||||
|
|
||||||
DateTime withTime(TimeOfDay time) => copyWith(hour: time.hour, minute: time.minute);
|
DateTime withTime(TimeOfDay time) =>
|
||||||
|
copyWith(hour: time.hour, minute: time.minute);
|
||||||
|
|
||||||
TimeOfDay toTimeOfDay() => TimeOfDay(hour: hour, minute: minute);
|
TimeOfDay toTimeOfDay() => TimeOfDay(hour: hour, minute: minute);
|
||||||
|
|
||||||
@@ -25,15 +28,20 @@ extension IsSameDay on DateTime {
|
|||||||
extension DateTimeFormatting on DateTime {
|
extension DateTimeFormatting on DateTime {
|
||||||
String formatHm() => Jiffy.parseFromDateTime(this).format(pattern: 'HH:mm');
|
String formatHm() => Jiffy.parseFromDateTime(this).format(pattern: 'HH:mm');
|
||||||
|
|
||||||
String formatDate() => Jiffy.parseFromDateTime(this).format(pattern: 'dd.MM.yyyy');
|
String formatDate() =>
|
||||||
|
Jiffy.parseFromDateTime(this).format(pattern: 'dd.MM.yyyy');
|
||||||
|
|
||||||
String formatDateTime() => Jiffy.parseFromDateTime(this).format(pattern: 'dd.MM.yyyy HH:mm');
|
String formatDateTime() =>
|
||||||
|
Jiffy.parseFromDateTime(this).format(pattern: 'dd.MM.yyyy HH:mm');
|
||||||
|
|
||||||
String formatDateShort() => Jiffy.parseFromDateTime(this).format(pattern: 'dd.MM.');
|
String formatDateShort() =>
|
||||||
|
Jiffy.parseFromDateTime(this).format(pattern: 'dd.MM.');
|
||||||
|
|
||||||
String formatDateShortHm() => Jiffy.parseFromDateTime(this).format(pattern: 'dd.MM. HH:mm');
|
String formatDateShortHm() =>
|
||||||
|
Jiffy.parseFromDateTime(this).format(pattern: 'dd.MM. HH:mm');
|
||||||
|
|
||||||
String formatMonthYear() => Jiffy.parseFromDateTime(this).format(pattern: 'MMMM yyyy');
|
String formatMonthYear() =>
|
||||||
|
Jiffy.parseFromDateTime(this).format(pattern: 'MMMM yyyy');
|
||||||
|
|
||||||
String formatRelative() => Jiffy.parseFromDateTime(this).fromNow();
|
String formatRelative() => Jiffy.parseFromDateTime(this).fromNow();
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
extension RenderNotNullExt<T> on T? {
|
extension RenderNotNullExt<T> on T? {
|
||||||
R? wrapNullable<R>(R Function(T data) defaultValueCallback) => this != null ? defaultValueCallback(this as T) : null;
|
R? wrapNullable<R>(R Function(T data) defaultValueCallback) =>
|
||||||
|
this != null ? defaultValueCallback(this as T) : null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ extension TextExt on Text {
|
|||||||
final textPainter = TextPainter(
|
final textPainter = TextPainter(
|
||||||
text: TextSpan(text: data, style: style),
|
text: TextSpan(text: data, style: style),
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
textDirection: TextDirection.ltr
|
textDirection: TextDirection.ltr,
|
||||||
)..layout(minWidth: 0, maxWidth: double.infinity);
|
)..layout(minWidth: 0, maxWidth: double.infinity);
|
||||||
return textPainter.size;
|
return textPainter.size;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,5 +5,6 @@ extension TimeOfDayExt on TimeOfDay {
|
|||||||
|
|
||||||
bool isAfter(TimeOfDay other) => hour > other.hour && minute > other.minute;
|
bool isAfter(TimeOfDay other) => hour > other.hour && minute > other.minute;
|
||||||
|
|
||||||
TimeOfDay add({int hours = 0, int minutes = 0}) => replacing(hour: hour + hours, minute: minute + minutes);
|
TimeOfDay add({int hours = 0, int minutes = 0}) =>
|
||||||
|
replacing(hour: hour + hours, minute: minute + minutes);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,7 +63,8 @@ class DefaultFirebaseOptions {
|
|||||||
messagingSenderId: '522850592536',
|
messagingSenderId: '522850592536',
|
||||||
projectId: 'marmobile-33b10',
|
projectId: 'marmobile-33b10',
|
||||||
storageBucket: 'marmobile-33b10.appspot.com',
|
storageBucket: 'marmobile-33b10.appspot.com',
|
||||||
iosClientId: '522850592536-edj90sbbnkjqe3aqui37j8enu93v4fk8.apps.googleusercontent.com',
|
iosClientId:
|
||||||
|
'522850592536-edj90sbbnkjqe3aqui37j8enu93v4fk8.apps.googleusercontent.com',
|
||||||
iosBundleId: 'eu.mhsl.marianum.mobile.client',
|
iosBundleId: 'eu.mhsl.marianum.mobile.client',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
+46
-19
@@ -40,19 +40,28 @@ Future<void> main() async {
|
|||||||
log('MarianumMobile started');
|
log('MarianumMobile started');
|
||||||
WidgetsFlutterBinding.ensureInitialized();
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
|
|
||||||
void addCertificateAsTrusted(ByteData certificate) =>
|
void addCertificateAsTrusted(ByteData certificate) => SecurityContext
|
||||||
SecurityContext.defaultContext.setTrustedCertificatesBytes(certificate.buffer.asUint8List());
|
.defaultContext
|
||||||
|
.setTrustedCertificatesBytes(certificate.buffer.asUint8List());
|
||||||
|
|
||||||
final initialisationTasks = [
|
final initialisationTasks = [
|
||||||
Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform)
|
Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform)
|
||||||
.then<void>((_) {})
|
.then<void>((_) {})
|
||||||
.onError((error, _) => log('Error initializing Firebase: $error')),
|
.onError((error, _) => log('Error initializing Firebase: $error')),
|
||||||
PlatformAssetBundle().load('assets/ca/lets-encrypt-r3.pem').then(addCertificateAsTrusted),
|
PlatformAssetBundle()
|
||||||
PlatformAssetBundle().load('assets/ca/lets-encrypt-r10.pem').then(addCertificateAsTrusted),
|
.load('assets/ca/lets-encrypt-r3.pem')
|
||||||
PlatformAssetBundle().load('assets/ca/lets-encrypt-r13.pem').then(addCertificateAsTrusted),
|
.then(addCertificateAsTrusted),
|
||||||
|
PlatformAssetBundle()
|
||||||
|
.load('assets/ca/lets-encrypt-r10.pem')
|
||||||
|
.then(addCertificateAsTrusted),
|
||||||
|
PlatformAssetBundle()
|
||||||
|
.load('assets/ca/lets-encrypt-r13.pem')
|
||||||
|
.then(addCertificateAsTrusted),
|
||||||
Future(() async {
|
Future(() async {
|
||||||
final storage = await HydratedStorage.build(
|
final storage = await HydratedStorage.build(
|
||||||
storageDirectory: HydratedStorageDirectory((await getTemporaryDirectory()).path),
|
storageDirectory: HydratedStorageDirectory(
|
||||||
|
(await getTemporaryDirectory()).path,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
HydratedBloc.storage = storage;
|
HydratedBloc.storage = storage;
|
||||||
}),
|
}),
|
||||||
@@ -91,7 +100,10 @@ Future<void> main() async {
|
|||||||
// Capture uncaught Flutter and platform errors so they show up in logs
|
// Capture uncaught Flutter and platform errors so they show up in logs
|
||||||
// instead of being silently swallowed.
|
// instead of being silently swallowed.
|
||||||
FlutterError.onError = (details) {
|
FlutterError.onError = (details) {
|
||||||
log('Uncaught Flutter error: ${details.exception}', stackTrace: details.stack);
|
log(
|
||||||
|
'Uncaught Flutter error: ${details.exception}',
|
||||||
|
stackTrace: details.stack,
|
||||||
|
);
|
||||||
FlutterError.presentError(details);
|
FlutterError.presentError(details);
|
||||||
};
|
};
|
||||||
PlatformDispatcher.instance.onError = (error, stack) {
|
PlatformDispatcher.instance.onError = (error, stack) {
|
||||||
@@ -104,9 +116,13 @@ Future<void> main() async {
|
|||||||
MultiBlocProvider(
|
MultiBlocProvider(
|
||||||
providers: [
|
providers: [
|
||||||
BlocProvider<SettingsCubit>(create: (_) => SettingsCubit()),
|
BlocProvider<SettingsCubit>(create: (_) => SettingsCubit()),
|
||||||
BlocProvider<AccountBloc>(create: (_) => AccountBloc(
|
BlocProvider<AccountBloc>(
|
||||||
initialStatus: AccountData().isPopulated() ? AccountStatus.loggedIn : AccountStatus.loggedOut,
|
create: (_) => AccountBloc(
|
||||||
)),
|
initialStatus: AccountData().isPopulated()
|
||||||
|
? AccountStatus.loggedIn
|
||||||
|
: AccountStatus.loggedOut,
|
||||||
|
),
|
||||||
|
),
|
||||||
BlocProvider<BreakerBloc>(create: (_) => BreakerBloc()),
|
BlocProvider<BreakerBloc>(create: (_) => BreakerBloc()),
|
||||||
BlocProvider<ChatListBloc>(create: (_) => ChatListBloc()),
|
BlocProvider<ChatListBloc>(create: (_) => ChatListBloc()),
|
||||||
BlocProvider<ChatBloc>(create: (_) => ChatBloc()),
|
BlocProvider<ChatBloc>(create: (_) => ChatBloc()),
|
||||||
@@ -120,7 +136,9 @@ Future<void> main() async {
|
|||||||
class Main extends StatefulWidget {
|
class Main extends StatefulWidget {
|
||||||
const Main({super.key});
|
const Main({super.key});
|
||||||
|
|
||||||
static PersistentTabController bottomNavigator = PersistentTabController(initialIndex: 0);
|
static PersistentTabController bottomNavigator = PersistentTabController(
|
||||||
|
initialIndex: 0,
|
||||||
|
);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<Main> createState() => _MainState();
|
State<Main> createState() => _MainState();
|
||||||
@@ -134,7 +152,9 @@ class _MainState extends State<Main> {
|
|||||||
|
|
||||||
AccountData().waitForPopulation().then((value) {
|
AccountData().waitForPopulation().then((value) {
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
context.read<AccountBloc>().setStatus(value ? AccountStatus.loggedIn : AccountStatus.loggedOut);
|
context.read<AccountBloc>().setStatus(
|
||||||
|
value ? AccountStatus.loggedIn : AccountStatus.loggedOut,
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -146,8 +166,10 @@ class _MainState extends State<Main> {
|
|||||||
final devToolsSettings = settings.devToolsSettings;
|
final devToolsSettings = settings.devToolsSettings;
|
||||||
return MaterialApp(
|
return MaterialApp(
|
||||||
showPerformanceOverlay: devToolsSettings.showPerformanceOverlay,
|
showPerformanceOverlay: devToolsSettings.showPerformanceOverlay,
|
||||||
checkerboardOffscreenLayers: devToolsSettings.checkerboardOffscreenLayers,
|
checkerboardOffscreenLayers:
|
||||||
checkerboardRasterCacheImages: devToolsSettings.checkerboardRasterCacheImages,
|
devToolsSettings.checkerboardOffscreenLayers,
|
||||||
|
checkerboardRasterCacheImages:
|
||||||
|
devToolsSettings.checkerboardRasterCacheImages,
|
||||||
debugShowCheckedModeBanner: false,
|
debugShowCheckedModeBanner: false,
|
||||||
localizationsDelegates: const [
|
localizationsDelegates: const [
|
||||||
...GlobalMaterialLocalizations.delegates,
|
...GlobalMaterialLocalizations.delegates,
|
||||||
@@ -172,7 +194,8 @@ class _MainState extends State<Main> {
|
|||||||
child: Breaker(
|
child: Breaker(
|
||||||
breaker: BreakerArea.global,
|
breaker: BreakerArea.global,
|
||||||
child: BlocConsumer<AccountBloc, AccountState>(
|
child: BlocConsumer<AccountBloc, AccountState>(
|
||||||
listenWhen: (previous, current) => previous.status != current.status,
|
listenWhen: (previous, current) =>
|
||||||
|
previous.status != current.status,
|
||||||
listener: (context, accountState) {
|
listener: (context, accountState) {
|
||||||
if (accountState.status != AccountStatus.loggedOut) return;
|
if (accountState.status != AccountStatus.loggedOut) return;
|
||||||
// Routes pushed via AppRoutes (e.g. Settings) live on the
|
// Routes pushed via AppRoutes (e.g. Settings) live on the
|
||||||
@@ -196,13 +219,15 @@ class _MainState extends State<Main> {
|
|||||||
// is already torn down. Resetting blocs while App is
|
// is already torn down. Resetting blocs while App is
|
||||||
// still in front caused a black-frame race.
|
// still in front caused a black-frame race.
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
unawaited(_wipeUserState(
|
unawaited(
|
||||||
|
_wipeUserState(
|
||||||
settingsCubit: settingsCubit,
|
settingsCubit: settingsCubit,
|
||||||
timetableBloc: timetableBloc,
|
timetableBloc: timetableBloc,
|
||||||
chatListBloc: chatListBloc,
|
chatListBloc: chatListBloc,
|
||||||
chatBloc: chatBloc,
|
chatBloc: chatBloc,
|
||||||
breakerBloc: breakerBloc,
|
breakerBloc: breakerBloc,
|
||||||
));
|
),
|
||||||
|
);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
builder: (context, accountState) {
|
builder: (context, accountState) {
|
||||||
@@ -220,8 +245,10 @@ class _MainState extends State<Main> {
|
|||||||
children: [
|
children: [
|
||||||
AppProgressIndicator.large(color: Colors.white),
|
AppProgressIndicator.large(color: Colors.white),
|
||||||
SizedBox(height: 16),
|
SizedBox(height: 16),
|
||||||
Text('Konto wird geladen…',
|
Text(
|
||||||
style: TextStyle(color: Colors.white)),
|
'Konto wird geladen…',
|
||||||
|
style: TextStyle(color: Colors.white),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -34,11 +34,16 @@ class AccountData {
|
|||||||
return _password!;
|
return _password!;
|
||||||
}
|
}
|
||||||
|
|
||||||
String getUserSecret() =>
|
String getUserSecret() => sha512
|
||||||
sha512.convert(utf8.encode('${getUsername()}:${getPassword()}')).toString();
|
.convert(utf8.encode('${getUsername()}:${getPassword()}'))
|
||||||
|
.toString();
|
||||||
|
|
||||||
Future<String> getDeviceId() async => sha512
|
Future<String> getDeviceId() async => sha512
|
||||||
.convert(utf8.encode('${getUserSecret()}@${await FirebaseMessaging.instance.getToken()}'))
|
.convert(
|
||||||
|
utf8.encode(
|
||||||
|
'${getUserSecret()}@${await FirebaseMessaging.instance.getToken()}',
|
||||||
|
),
|
||||||
|
)
|
||||||
.toString();
|
.toString();
|
||||||
|
|
||||||
Future<void> setData(String username, String password) async {
|
Future<void> setData(String username, String password) async {
|
||||||
@@ -92,7 +97,11 @@ class AccountData {
|
|||||||
/// Prefer this over embedding credentials in URLs — error logs and crash
|
/// Prefer this over embedding credentials in URLs — error logs and crash
|
||||||
/// reports often capture the URL but not headers.
|
/// reports often capture the URL but not headers.
|
||||||
String getBasicAuthHeader() {
|
String getBasicAuthHeader() {
|
||||||
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 'Basic ${base64Encode(utf8.encode('$_username:$_password'))}';
|
return 'Basic ${base64Encode(utf8.encode('$_username:$_password'))}';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,11 +4,20 @@ import '../api/request_cache.dart';
|
|||||||
|
|
||||||
class DataCleaner {
|
class DataCleaner {
|
||||||
static Future<void> cleanOldCache() async {
|
static Future<void> cleanOldCache() async {
|
||||||
final cacheData = await Localstore.instance.collection(RequestCache.collection).get();
|
final cacheData = await Localstore.instance
|
||||||
|
.collection(RequestCache.collection)
|
||||||
|
.get();
|
||||||
cacheData?.forEach((key, value) async {
|
cacheData?.forEach((key, value) async {
|
||||||
final lastUpdate = DateTime.fromMillisecondsSinceEpoch(value['lastupdate'] as int);
|
final lastUpdate = DateTime.fromMillisecondsSinceEpoch(
|
||||||
if (DateTime.now().subtract(const Duration(days: 200)).isAfter(lastUpdate)) {
|
value['lastupdate'] as int,
|
||||||
await Localstore.instance.collection(RequestCache.collection).doc(key.split('/').last).delete();
|
);
|
||||||
|
if (DateTime.now()
|
||||||
|
.subtract(const Duration(days: 200))
|
||||||
|
.isAfter(lastUpdate)) {
|
||||||
|
await Localstore.instance
|
||||||
|
.collection(RequestCache.collection)
|
||||||
|
.doc(key.split('/').last)
|
||||||
|
.delete();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,6 @@
|
|||||||
|
|
||||||
import 'account_data.dart';
|
import 'account_data.dart';
|
||||||
|
|
||||||
enum EndpointMode {
|
enum EndpointMode { live, stage }
|
||||||
live,
|
|
||||||
stage,
|
|
||||||
}
|
|
||||||
|
|
||||||
class EndpointOptions {
|
class EndpointOptions {
|
||||||
Endpoint live;
|
Endpoint live;
|
||||||
@@ -36,27 +32,21 @@ 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() => EndpointOptions(
|
Endpoint webuntis() => EndpointOptions(
|
||||||
live: Endpoint(
|
live: Endpoint(domain: 'marianum-fulda.webuntis.com'),
|
||||||
domain: 'marianum-fulda.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());
|
||||||
|
|
||||||
Endpoint nextcloud() => EndpointOptions(
|
Endpoint nextcloud() => EndpointOptions(
|
||||||
live: Endpoint(
|
live: Endpoint(domain: 'cloud.marianum-fulda.de'),
|
||||||
domain: 'cloud.marianum-fulda.de',
|
staged: Endpoint(domain: 'mhsl.eu', path: '/marianum/marianummobile/cloud'),
|
||||||
),
|
|
||||||
staged: Endpoint(
|
|
||||||
domain: 'mhsl.eu',
|
|
||||||
path: '/marianum/marianummobile/cloud',
|
|
||||||
)
|
|
||||||
).get(getEndpointMode());
|
).get(getEndpointMode());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,13 +13,22 @@ class NotificationController {
|
|||||||
NotificationTasks.updateBadgeCount(message);
|
NotificationTasks.updateBadgeCount(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<void> onForegroundMessageHandler(RemoteMessage message, BuildContext context) async {
|
static Future<void> onForegroundMessageHandler(
|
||||||
|
RemoteMessage message,
|
||||||
|
BuildContext context,
|
||||||
|
) async {
|
||||||
NotificationTasks.updateProviders(context);
|
NotificationTasks.updateProviders(context);
|
||||||
NotificationTasks.updateBadgeCount(message);
|
NotificationTasks.updateBadgeCount(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<void> onAppOpenedByNotification(RemoteMessage message, BuildContext context) async {
|
static Future<void> onAppOpenedByNotification(
|
||||||
NotificationTasks.navigateToTalk(context, chatToken: _extractChatToken(message));
|
RemoteMessage message,
|
||||||
|
BuildContext context,
|
||||||
|
) async {
|
||||||
|
NotificationTasks.navigateToTalk(
|
||||||
|
context,
|
||||||
|
chatToken: _extractChatToken(message),
|
||||||
|
);
|
||||||
NotificationTasks.updateProviders(context);
|
NotificationTasks.updateProviders(context);
|
||||||
|
|
||||||
DebugTile(context).run(() {
|
DebugTile(context).run(() {
|
||||||
|
|||||||
@@ -7,16 +7,15 @@ class NotificationService {
|
|||||||
|
|
||||||
NotificationService._internal();
|
NotificationService._internal();
|
||||||
|
|
||||||
FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
|
FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
|
||||||
|
FlutterLocalNotificationsPlugin();
|
||||||
|
|
||||||
Future<void> initializeNotifications() async {
|
Future<void> initializeNotifications() async {
|
||||||
const androidSettings = AndroidInitializationSettings(
|
const androidSettings = AndroidInitializationSettings(
|
||||||
'@mipmap/ic_launcher'
|
'@mipmap/ic_launcher',
|
||||||
);
|
|
||||||
|
|
||||||
final iosSettings = DarwinInitializationSettings(
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
final iosSettings = DarwinInitializationSettings();
|
||||||
|
|
||||||
final initializationSettings = InitializationSettings(
|
final initializationSettings = InitializationSettings(
|
||||||
android: androidSettings,
|
android: androidSettings,
|
||||||
@@ -24,13 +23,16 @@ class NotificationService {
|
|||||||
);
|
);
|
||||||
|
|
||||||
await flutterLocalNotificationsPlugin.initialize(
|
await flutterLocalNotificationsPlugin.initialize(
|
||||||
settings: initializationSettings
|
settings: initializationSettings,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> showNotification({required String title, required String body, required int badgeCount}) async {
|
Future<void> showNotification({
|
||||||
const androidPlatformChannelSpecifics =
|
required String title,
|
||||||
AndroidNotificationDetails(
|
required String body,
|
||||||
|
required int badgeCount,
|
||||||
|
}) async {
|
||||||
|
const androidPlatformChannelSpecifics = AndroidNotificationDetails(
|
||||||
'marmobile',
|
'marmobile',
|
||||||
'Marianum Fulda',
|
'Marianum Fulda',
|
||||||
importance: Importance.defaultImportance,
|
importance: Importance.defaultImportance,
|
||||||
@@ -42,14 +44,14 @@ class NotificationService {
|
|||||||
|
|
||||||
const platformChannelSpecifics = NotificationDetails(
|
const platformChannelSpecifics = NotificationDetails(
|
||||||
android: androidPlatformChannelSpecifics,
|
android: androidPlatformChannelSpecifics,
|
||||||
iOS: iosPlatformChannelSpecifics
|
iOS: iosPlatformChannelSpecifics,
|
||||||
);
|
);
|
||||||
|
|
||||||
await flutterLocalNotificationsPlugin.show(
|
await flutterLocalNotificationsPlugin.show(
|
||||||
id: 0,
|
id: 0,
|
||||||
title: title,
|
title: title,
|
||||||
body: body,
|
body: body,
|
||||||
notificationDetails: platformChannelSpecifics
|
notificationDetails: platformChannelSpecifics,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,9 @@ import '../state/app/modules/chat_list/bloc/chat_list_bloc.dart';
|
|||||||
|
|
||||||
class NotificationTasks {
|
class NotificationTasks {
|
||||||
static void updateBadgeCount(RemoteMessage notification) {
|
static void updateBadgeCount(RemoteMessage notification) {
|
||||||
FlutterAppBadge.count(int.parse((notification.data['unreadCount'] as String?) ?? '0'));
|
FlutterAppBadge.count(
|
||||||
|
int.parse((notification.data['unreadCount'] as String?) ?? '0'),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void updateProviders(BuildContext context) {
|
static void updateProviders(BuildContext context) {
|
||||||
|
|||||||
@@ -10,16 +10,21 @@ import '../state/app/modules/settings/bloc/settings_cubit.dart';
|
|||||||
import '../widget/confirm_dialog.dart';
|
import '../widget/confirm_dialog.dart';
|
||||||
|
|
||||||
class NotifyUpdater {
|
class NotifyUpdater {
|
||||||
static ConfirmDialog enableAfterDisclaimer(SettingsCubit settings) => ConfirmDialog(
|
static ConfirmDialog enableAfterDisclaimer(
|
||||||
|
SettingsCubit settings,
|
||||||
|
) => 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: () {
|
||||||
unawaited(FirebaseMessaging.instance.requestPermission(provisional: false));
|
unawaited(
|
||||||
|
FirebaseMessaging.instance.requestPermission(provisional: false),
|
||||||
|
);
|
||||||
settings.val(write: true).notificationSettings.enabled = true;
|
settings.val(write: true).notificationSettings.enabled = true;
|
||||||
unawaited(NotifyUpdater.registerToServer());
|
unawaited(NotifyUpdater.registerToServer());
|
||||||
},
|
},
|
||||||
@@ -28,15 +33,19 @@ class NotifyUpdater {
|
|||||||
static Future<void> registerToServer() async {
|
static Future<void> registerToServer() async {
|
||||||
final fcmToken = await FirebaseMessaging.instance.getToken();
|
final fcmToken = await FirebaseMessaging.instance.getToken();
|
||||||
if (fcmToken == null) {
|
if (fcmToken == null) {
|
||||||
throw Exception('Failed to register push notification because there is no FBC token!');
|
throw Exception(
|
||||||
|
'Failed to register push notification because there is no FBC token!',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
unawaited(NotifyRegister(
|
unawaited(
|
||||||
|
NotifyRegister(
|
||||||
NotifyRegisterParams(
|
NotifyRegisterParams(
|
||||||
username: AccountData().getUsername(),
|
username: AccountData().getUsername(),
|
||||||
password: AccountData().getPassword(),
|
password: AccountData().getPassword(),
|
||||||
fcmToken: fcmToken,
|
fcmToken: fcmToken,
|
||||||
),
|
),
|
||||||
).run());
|
).run(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+29
-10
@@ -38,7 +38,11 @@ class AppRoutes {
|
|||||||
pushScreen(context, withNavBar: false, screen: Files(path: path));
|
pushScreen(context, withNavBar: false, screen: Files(path: path));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void openFileViewer(BuildContext context, String localPath, {bool openExternal = false}) {
|
static void openFileViewer(
|
||||||
|
BuildContext context,
|
||||||
|
String localPath, {
|
||||||
|
bool openExternal = false,
|
||||||
|
}) {
|
||||||
pushScreen(
|
pushScreen(
|
||||||
context,
|
context,
|
||||||
withNavBar: false,
|
withNavBar: false,
|
||||||
@@ -50,7 +54,11 @@ class AppRoutes {
|
|||||||
pushScreen(context, withNavBar: false, screen: const CustomEventsView());
|
pushScreen(context, withNavBar: false, screen: const CustomEventsView());
|
||||||
}
|
}
|
||||||
|
|
||||||
static void openMarianumMessage(BuildContext context, String basePath, MarianumMessage message) {
|
static void openMarianumMessage(
|
||||||
|
BuildContext context,
|
||||||
|
String basePath,
|
||||||
|
MarianumMessage message,
|
||||||
|
) {
|
||||||
pushScreen(
|
pushScreen(
|
||||||
context,
|
context,
|
||||||
withNavBar: false,
|
withNavBar: false,
|
||||||
@@ -82,7 +90,11 @@ class AppRoutes {
|
|||||||
pushScreen(context, withNavBar: false, screen: const Roomplan());
|
pushScreen(context, withNavBar: false, screen: const Roomplan());
|
||||||
}
|
}
|
||||||
|
|
||||||
static void openMessageReactions(BuildContext context, String token, int messageId) {
|
static void openMessageReactions(
|
||||||
|
BuildContext context,
|
||||||
|
String token,
|
||||||
|
int messageId,
|
||||||
|
) {
|
||||||
pushScreen(
|
pushScreen(
|
||||||
context,
|
context,
|
||||||
withNavBar: false,
|
withNavBar: false,
|
||||||
@@ -113,7 +125,9 @@ class AppRoutes {
|
|||||||
try {
|
try {
|
||||||
context.read<ChatListBloc>().refresh();
|
context.read<ChatListBloc>().refresh();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (kDebugMode) debugPrint('openChatByToken: ChatListBloc refresh failed: $e');
|
if (kDebugMode) {
|
||||||
|
debugPrint('openChatByToken: ChatListBloc refresh failed: $e');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -130,7 +144,10 @@ class AppRoutes {
|
|||||||
if (room == null) return null;
|
if (room == null) return null;
|
||||||
|
|
||||||
final isGroup = room.type != GetRoomResponseObjectConversationType.oneToOne;
|
final isGroup = room.type != GetRoomResponseObjectConversationType.oneToOne;
|
||||||
final avatar = UserAvatar(id: isGroup ? room.token : room.name, isGroup: isGroup);
|
final avatar = UserAvatar(
|
||||||
|
id: isGroup ? room.token : room.name,
|
||||||
|
isGroup: isGroup,
|
||||||
|
);
|
||||||
return ResolvedPendingChat(
|
return ResolvedPendingChat(
|
||||||
room: room,
|
room: room,
|
||||||
selfId: AccountData().getUsername(),
|
selfId: AccountData().getUsername(),
|
||||||
@@ -138,7 +155,10 @@ class AppRoutes {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
static GetRoomResponseObject? _findRoomByToken(GetRoomResponse? rooms, String token) {
|
static GetRoomResponseObject? _findRoomByToken(
|
||||||
|
GetRoomResponse? rooms,
|
||||||
|
String token,
|
||||||
|
) {
|
||||||
if (rooms == null) return null;
|
if (rooms == null) return null;
|
||||||
for (final room in rooms.data) {
|
for (final room in rooms.data) {
|
||||||
if (room.token == token) return room;
|
if (room.token == token) return room;
|
||||||
@@ -155,10 +175,9 @@ class AppRoutes {
|
|||||||
/// Switches the bottom navigation to [module]. Returns false when the
|
/// Switches the bottom navigation to [module]. Returns false when the
|
||||||
/// module is not currently in the bottom bar.
|
/// module is not currently in the bottom bar.
|
||||||
static bool goToTab(BuildContext context, Modules module) {
|
static bool goToTab(BuildContext context, Modules module) {
|
||||||
final index = AppModule.getBottomBarModules(context)
|
final index = AppModule.getBottomBarModules(
|
||||||
.map((e) => e.module)
|
context,
|
||||||
.toList()
|
).map((e) => e.module).toList().indexOf(module);
|
||||||
.indexOf(module);
|
|
||||||
if (index == -1) return false;
|
if (index == -1) return false;
|
||||||
Main.bottomNavigator.jumpToTab(index);
|
Main.bottomNavigator.jumpToTab(index);
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import 'package:dio/dio.dart';
|
|||||||
import '../../infrastructure/data_loader/data_loader.dart';
|
import '../../infrastructure/data_loader/data_loader.dart';
|
||||||
|
|
||||||
abstract class HolidayDataLoader<TResult> extends DataLoader<TResult> {
|
abstract class HolidayDataLoader<TResult> extends DataLoader<TResult> {
|
||||||
HolidayDataLoader() : super(Dio(BaseOptions(
|
HolidayDataLoader()
|
||||||
baseUrl: 'https://ferien-api.de/api/v1/',
|
: super(Dio(BaseOptions(baseUrl: 'https://ferien-api.de/api/v1/')));
|
||||||
)));
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,8 @@ import 'package:dio/dio.dart';
|
|||||||
import '../../infrastructure/data_loader/data_loader.dart';
|
import '../../infrastructure/data_loader/data_loader.dart';
|
||||||
|
|
||||||
abstract class MhslDataLoader<TResult> extends DataLoader<TResult> {
|
abstract class MhslDataLoader<TResult> extends DataLoader<TResult> {
|
||||||
MhslDataLoader() : super(Dio(BaseOptions(
|
MhslDataLoader()
|
||||||
baseUrl: 'https://mhsl.eu/marianum/marianummobile/'
|
: super(
|
||||||
)));
|
Dio(BaseOptions(baseUrl: 'https://mhsl.eu/marianum/marianummobile/')),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,10 +14,14 @@ abstract class DataLoader<TResult> {
|
|||||||
Future<TResult> run() async {
|
Future<TResult> run() async {
|
||||||
final response = await fetch();
|
final response = await fetch();
|
||||||
try {
|
try {
|
||||||
return assemble(DataLoaderResult(
|
return assemble(
|
||||||
|
DataLoaderResult(
|
||||||
json: jsonDecode(response.data!),
|
json: jsonDecode(response.data!),
|
||||||
headers: response.headers.map.map((key, value) => MapEntry(key, value.join(';'))),
|
headers: response.headers.map.map(
|
||||||
));
|
(key, value) => MapEntry(key, value.join(';')),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
} catch (e, stack) {
|
} catch (e, stack) {
|
||||||
log('DataLoader assemble failed', error: e, stackTrace: stack);
|
log('DataLoader assemble failed', error: e, stackTrace: stack);
|
||||||
rethrow;
|
rethrow;
|
||||||
@@ -34,7 +38,8 @@ class DataLoaderResult {
|
|||||||
|
|
||||||
Map<String, dynamic> asMap() => json as Map<String, dynamic>;
|
Map<String, dynamic> asMap() => json as Map<String, dynamic>;
|
||||||
List<dynamic> asList() => json as List<dynamic>;
|
List<dynamic> asList() => json as List<dynamic>;
|
||||||
List<Map<String, dynamic>> asListOfMaps() => asList().map((e) => e as Map<String, dynamic>).toList();
|
List<Map<String, dynamic>> asListOfMaps() =>
|
||||||
|
asList().map((e) => e as Map<String, dynamic>).toList();
|
||||||
|
|
||||||
DataLoaderResult({required this.json, required this.headers});
|
DataLoaderResult({required this.json, required this.headers});
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user