Compare commits
30 Commits
develop-bl
...
develop
Author | SHA1 | Date | |
---|---|---|---|
0a66858d93 | |||
49428680de | |||
0c676dc3d6 | |||
c702b610c5 | |||
5938c6b3c3 | |||
c44b0464a4 | |||
9d0cf8e313 | |||
f0009dad88 | |||
905206f242 | |||
769fbc1b6a | |||
8daf57bcee | |||
33d488946a | |||
41a5e021c5 | |||
8f58893553 | |||
626d3d5564 | |||
d833cdb733 | |||
8868914a76 | |||
70e6f82b10 | |||
6651613331 | |||
9ad0f624de | |||
82c143f847 | |||
1fdf731b81 | |||
2d3ccd25b4 | |||
385ee806d6 | |||
92aef41031 | |||
65b29ec4b8 | |||
9f51d68531 | |||
5bc4ba6332 | |||
e9739ac2d5 | |||
4d3a33dd9b |
android
assets/img
lib
api
apiResponse.dartrequestCache.dart
app.dartmain.dartholidays
marianumcloud
mhsl
breaker
getBreakers
customTimetableEvent
server
userIndex
update
webuntis
queries
authenticate
getHolidays
getRooms
getSubjects
getTimetable
model
notification
state
app
basis
infrastructure
modules
storage
base
general
timetable
theming
view
widget
@ -25,9 +25,10 @@ if (flutterVersionName == null) {
|
||||
android {
|
||||
namespace "eu.mhsl.marianum.mobile.client"
|
||||
compileSdk flutter.compileSdkVersion
|
||||
ndkVersion flutter.ndkVersion
|
||||
ndkVersion "27.0.12077973"
|
||||
|
||||
compileOptions {
|
||||
coreLibraryDesugaringEnabled true
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
@ -41,11 +42,8 @@ android {
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
|
||||
applicationId "eu.mhsl.marianum.mobile.client"
|
||||
// You can update the following values to match your application needs.
|
||||
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
|
||||
minSdkVersion 21
|
||||
minSdkVersion 26
|
||||
targetSdkVersion flutter.targetSdkVersion
|
||||
versionCode flutterVersionCode.toInteger()
|
||||
versionName flutterVersionName
|
||||
@ -66,5 +64,6 @@ flutter {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'com.android.support:multidex:1.0.3'
|
||||
implementation 'com.android.support:multidex:2.0.1'
|
||||
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.3'
|
||||
}
|
||||
|
@ -1,3 +1,6 @@
|
||||
org.gradle.jvmargs=-Xmx4G
|
||||
android.useAndroidX=true
|
||||
android.enableJetifier=true
|
||||
android.defaults.buildfeatures.buildconfig=true
|
||||
android.nonTransitiveRClass=false
|
||||
android.nonFinalResIds=false
|
||||
|
@ -1,6 +1,7 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.1-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
@ -19,7 +19,7 @@ pluginManagement {
|
||||
|
||||
plugins {
|
||||
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
|
||||
id "com.android.application" version "7.3.0" apply false
|
||||
id "com.android.application" version '8.7.3' apply false
|
||||
id "org.jetbrains.kotlin.android" version "1.8.10" apply false
|
||||
}
|
||||
|
||||
|
Binary file not shown.
Before ![]() (image error) Size: 108 KiB After ![]() (image error) Size: 101 KiB ![]() ![]() |
@ -5,5 +5,5 @@ abstract class ApiResponse {
|
||||
late http.Response rawResponse;
|
||||
|
||||
@JsonKey(includeIfNull: false)
|
||||
late Map<String, String>? headers;
|
||||
Map<String, String>? headers;
|
||||
}
|
||||
|
@ -16,19 +16,12 @@ GetHolidaysResponse _$GetHolidaysResponseFromJson(Map<String, dynamic> json) =>
|
||||
(k, e) => MapEntry(k, e as String),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$GetHolidaysResponseToJson(GetHolidaysResponse instance) {
|
||||
final val = <String, dynamic>{};
|
||||
|
||||
void writeNotNull(String key, dynamic value) {
|
||||
if (value != null) {
|
||||
val[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
writeNotNull('headers', instance.headers);
|
||||
val['data'] = instance.data.map((e) => e.toJson()).toList();
|
||||
return val;
|
||||
}
|
||||
Map<String, dynamic> _$GetHolidaysResponseToJson(
|
||||
GetHolidaysResponse instance) =>
|
||||
<String, dynamic>{
|
||||
if (instance.headers case final value?) 'headers': value,
|
||||
'data': instance.data.map((e) => e.toJson()).toList(),
|
||||
};
|
||||
|
||||
GetHolidaysResponseObject _$GetHolidaysResponseObjectFromJson(
|
||||
Map<String, dynamic> json) =>
|
||||
|
@ -18,7 +18,7 @@ class AutocompleteResponseObject {
|
||||
String label;
|
||||
String? icon;
|
||||
String? source;
|
||||
List<String>? status;
|
||||
String? status;
|
||||
String? subline;
|
||||
String? shareWithDisplayNameUniqe;
|
||||
|
||||
|
@ -28,7 +28,7 @@ AutocompleteResponseObject _$AutocompleteResponseObjectFromJson(
|
||||
json['label'] as String,
|
||||
json['icon'] as String?,
|
||||
json['source'] as String?,
|
||||
(json['status'] as List<dynamic>?)?.map((e) => e as String).toList(),
|
||||
json['status'] as String?,
|
||||
json['subline'] as String?,
|
||||
json['shareWithDisplayNameUniqe'] as String?,
|
||||
);
|
||||
|
@ -20,27 +20,22 @@ GetChatParams _$GetChatParamsFromJson(Map<String, dynamic> json) =>
|
||||
_$GetChatParamsSwitchEnumMap, json['includeLastKnown']),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$GetChatParamsToJson(GetChatParams instance) {
|
||||
final val = <String, dynamic>{
|
||||
'lookIntoFuture': _$GetChatParamsSwitchEnumMap[instance.lookIntoFuture]!,
|
||||
};
|
||||
|
||||
void writeNotNull(String key, dynamic value) {
|
||||
if (value != null) {
|
||||
val[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
writeNotNull('limit', instance.limit);
|
||||
writeNotNull('lastKnownMessageId', instance.lastKnownMessageId);
|
||||
writeNotNull('lastCommonReadId', instance.lastCommonReadId);
|
||||
writeNotNull('timeout', instance.timeout);
|
||||
writeNotNull(
|
||||
'setReadMarker', _$GetChatParamsSwitchEnumMap[instance.setReadMarker]);
|
||||
writeNotNull('includeLastKnown',
|
||||
_$GetChatParamsSwitchEnumMap[instance.includeLastKnown]);
|
||||
return val;
|
||||
}
|
||||
Map<String, dynamic> _$GetChatParamsToJson(GetChatParams instance) =>
|
||||
<String, dynamic>{
|
||||
'lookIntoFuture': _$GetChatParamsSwitchEnumMap[instance.lookIntoFuture]!,
|
||||
if (instance.limit case final value?) 'limit': value,
|
||||
if (instance.lastKnownMessageId case final value?)
|
||||
'lastKnownMessageId': value,
|
||||
if (instance.lastCommonReadId case final value?)
|
||||
'lastCommonReadId': value,
|
||||
if (instance.timeout case final value?) 'timeout': value,
|
||||
if (_$GetChatParamsSwitchEnumMap[instance.setReadMarker]
|
||||
case final value?)
|
||||
'setReadMarker': value,
|
||||
if (_$GetChatParamsSwitchEnumMap[instance.includeLastKnown]
|
||||
case final value?)
|
||||
'includeLastKnown': value,
|
||||
};
|
||||
|
||||
const _$GetChatParamsSwitchEnumMap = {
|
||||
GetChatParamsSwitch.on: 1,
|
||||
|
@ -15,19 +15,11 @@ GetChatResponse _$GetChatResponseFromJson(Map<String, dynamic> json) =>
|
||||
(k, e) => MapEntry(k, e as String),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$GetChatResponseToJson(GetChatResponse instance) {
|
||||
final val = <String, dynamic>{};
|
||||
|
||||
void writeNotNull(String key, dynamic value) {
|
||||
if (value != null) {
|
||||
val[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
writeNotNull('headers', instance.headers);
|
||||
val['data'] = instance.data.map((e) => e.toJson()).toList();
|
||||
return val;
|
||||
}
|
||||
Map<String, dynamic> _$GetChatResponseToJson(GetChatResponse instance) =>
|
||||
<String, dynamic>{
|
||||
if (instance.headers case final value?) 'headers': value,
|
||||
'data': instance.data.map((e) => e.toJson()).toList(),
|
||||
};
|
||||
|
||||
GetChatResponseObject _$GetChatResponseObjectFromJson(
|
||||
Map<String, dynamic> json) =>
|
||||
|
@ -18,19 +18,11 @@ GetParticipantsResponse _$GetParticipantsResponseFromJson(
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$GetParticipantsResponseToJson(
|
||||
GetParticipantsResponse instance) {
|
||||
final val = <String, dynamic>{};
|
||||
|
||||
void writeNotNull(String key, dynamic value) {
|
||||
if (value != null) {
|
||||
val[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
writeNotNull('headers', instance.headers);
|
||||
val['data'] = instance.data.map((e) => e.toJson()).toList();
|
||||
return val;
|
||||
}
|
||||
GetParticipantsResponse instance) =>
|
||||
<String, dynamic>{
|
||||
if (instance.headers case final value?) 'headers': value,
|
||||
'data': instance.data.map((e) => e.toJson()).toList(),
|
||||
};
|
||||
|
||||
GetParticipantsResponseObject _$GetParticipantsResponseObjectFromJson(
|
||||
Map<String, dynamic> json) =>
|
||||
|
@ -22,20 +22,12 @@ GetReactionsResponse _$GetReactionsResponseFromJson(
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$GetReactionsResponseToJson(
|
||||
GetReactionsResponse instance) {
|
||||
final val = <String, dynamic>{};
|
||||
|
||||
void writeNotNull(String key, dynamic value) {
|
||||
if (value != null) {
|
||||
val[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
writeNotNull('headers', instance.headers);
|
||||
val['data'] = instance.data
|
||||
.map((k, e) => MapEntry(k, e.map((e) => e.toJson()).toList()));
|
||||
return val;
|
||||
}
|
||||
GetReactionsResponse instance) =>
|
||||
<String, dynamic>{
|
||||
if (instance.headers case final value?) 'headers': value,
|
||||
'data': instance.data
|
||||
.map((k, e) => MapEntry(k, e.map((e) => e.toJson()).toList())),
|
||||
};
|
||||
|
||||
GetReactionsResponseObject _$GetReactionsResponseObjectFromJson(
|
||||
Map<String, dynamic> json) =>
|
||||
|
@ -110,6 +110,7 @@ enum GetRoomResponseObjectConversationType {
|
||||
@JsonValue(3) public,
|
||||
@JsonValue(4) changelog,
|
||||
@JsonValue(5) deleted,
|
||||
@JsonValue(6) noteToSelf,
|
||||
}
|
||||
|
||||
enum GetRoomResponseObjectParticipantNotificationLevel {
|
||||
|
@ -15,19 +15,11 @@ GetRoomResponse _$GetRoomResponseFromJson(Map<String, dynamic> json) =>
|
||||
(k, e) => MapEntry(k, e as String),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$GetRoomResponseToJson(GetRoomResponse instance) {
|
||||
final val = <String, dynamic>{};
|
||||
|
||||
void writeNotNull(String key, dynamic value) {
|
||||
if (value != null) {
|
||||
val[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
writeNotNull('headers', instance.headers);
|
||||
val['data'] = instance.data.map((e) => e.toJson()).toList();
|
||||
return val;
|
||||
}
|
||||
Map<String, dynamic> _$GetRoomResponseToJson(GetRoomResponse instance) =>
|
||||
<String, dynamic>{
|
||||
if (instance.headers case final value?) 'headers': value,
|
||||
'data': instance.data.map((e) => e.toJson()).toList(),
|
||||
};
|
||||
|
||||
GetRoomResponseObject _$GetRoomResponseObjectFromJson(
|
||||
Map<String, dynamic> json) =>
|
||||
@ -110,6 +102,7 @@ const _$GetRoomResponseObjectConversationTypeEnumMap = {
|
||||
GetRoomResponseObjectConversationType.public: 3,
|
||||
GetRoomResponseObjectConversationType.changelog: 4,
|
||||
GetRoomResponseObjectConversationType.deleted: 5,
|
||||
GetRoomResponseObjectConversationType.noteToSelf: 6,
|
||||
};
|
||||
|
||||
const _$GetRoomResponseObjectParticipantNotificationLevelEnumMap = {
|
||||
|
@ -12,17 +12,8 @@ SendMessageParams _$SendMessageParamsFromJson(Map<String, dynamic> json) =>
|
||||
replyTo: json['replyTo'] as String?,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$SendMessageParamsToJson(SendMessageParams instance) {
|
||||
final val = <String, dynamic>{
|
||||
'message': instance.message,
|
||||
};
|
||||
|
||||
void writeNotNull(String key, dynamic value) {
|
||||
if (value != null) {
|
||||
val[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
writeNotNull('replyTo', instance.replyTo);
|
||||
return val;
|
||||
}
|
||||
Map<String, dynamic> _$SendMessageParamsToJson(SendMessageParams instance) =>
|
||||
<String, dynamic>{
|
||||
'message': instance.message,
|
||||
if (instance.replyTo case final value?) 'replyTo': value,
|
||||
};
|
||||
|
@ -58,11 +58,9 @@ abstract class TalkApi<T extends ApiResponse?> extends ApiRequest {
|
||||
assembled?.headers = data.headers;
|
||||
return assembled;
|
||||
} catch (e) {
|
||||
// TODO report error
|
||||
log('Error assembling Talk API ${T.toString()} message: ${e.toString()} response on ${endpoint.path} with request body: $body and request headers: ${headers.toString()}');
|
||||
var message = 'Error assembling Talk API ${T.toString()} message: ${e.toString()} response with request body: $body and request headers: ${headers.toString()}';
|
||||
log(message);
|
||||
throw Exception(message);
|
||||
}
|
||||
|
||||
throw Exception('Error assembling Talk API response');
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -15,16 +15,8 @@ ListFilesResponse _$ListFilesResponseFromJson(Map<String, dynamic> json) =>
|
||||
(k, e) => MapEntry(k, e as String),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$ListFilesResponseToJson(ListFilesResponse instance) {
|
||||
final val = <String, dynamic>{};
|
||||
|
||||
void writeNotNull(String key, dynamic value) {
|
||||
if (value != null) {
|
||||
val[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
writeNotNull('headers', instance.headers);
|
||||
val['files'] = instance.files.map((e) => e.toJson()).toList();
|
||||
return val;
|
||||
}
|
||||
Map<String, dynamic> _$ListFilesResponseToJson(ListFilesResponse instance) =>
|
||||
<String, dynamic>{
|
||||
if (instance.headers case final value?) 'headers': value,
|
||||
'files': instance.files.map((e) => e.toJson()).toList(),
|
||||
};
|
||||
|
@ -17,20 +17,13 @@ GetBreakersResponse _$GetBreakersResponseFromJson(Map<String, dynamic> json) =>
|
||||
(k, e) => MapEntry(k, e as String),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$GetBreakersResponseToJson(GetBreakersResponse instance) {
|
||||
final val = <String, dynamic>{};
|
||||
|
||||
void writeNotNull(String key, dynamic value) {
|
||||
if (value != null) {
|
||||
val[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
writeNotNull('headers', instance.headers);
|
||||
val['global'] = instance.global.toJson();
|
||||
val['regional'] = instance.regional.map((k, e) => MapEntry(k, e.toJson()));
|
||||
return val;
|
||||
}
|
||||
Map<String, dynamic> _$GetBreakersResponseToJson(
|
||||
GetBreakersResponse instance) =>
|
||||
<String, dynamic>{
|
||||
if (instance.headers case final value?) 'headers': value,
|
||||
'global': instance.global.toJson(),
|
||||
'regional': instance.regional.map((k, e) => MapEntry(k, e.toJson())),
|
||||
};
|
||||
|
||||
GetBreakersReponseObject _$GetBreakersReponseObjectFromJson(
|
||||
Map<String, dynamic> json) =>
|
||||
|
@ -17,16 +17,8 @@ GetCustomTimetableEventResponse _$GetCustomTimetableEventResponseFromJson(
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$GetCustomTimetableEventResponseToJson(
|
||||
GetCustomTimetableEventResponse instance) {
|
||||
final val = <String, dynamic>{};
|
||||
|
||||
void writeNotNull(String key, dynamic value) {
|
||||
if (value != null) {
|
||||
val[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
writeNotNull('headers', instance.headers);
|
||||
val['events'] = instance.events;
|
||||
return val;
|
||||
}
|
||||
GetCustomTimetableEventResponse instance) =>
|
||||
<String, dynamic>{
|
||||
if (instance.headers case final value?) 'headers': value,
|
||||
'events': instance.events,
|
||||
};
|
||||
|
@ -4,7 +4,7 @@ import 'dart:developer';
|
||||
|
||||
import 'package:device_info_plus/device_info_plus.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:package_info/package_info.dart';
|
||||
import 'package:package_info_plus/package_info_plus.dart';
|
||||
|
||||
import '../../../../../model/accountData.dart';
|
||||
import '../../../mhslApi.dart';
|
||||
|
@ -3,7 +3,6 @@ import 'dart:convert';
|
||||
import 'package:localstore/localstore.dart';
|
||||
|
||||
import 'apiResponse.dart';
|
||||
import 'webuntis/webuntisError.dart';
|
||||
|
||||
abstract class RequestCache<T extends ApiResponse?> {
|
||||
static const int cacheNothing = 0;
|
||||
@ -40,7 +39,7 @@ abstract class RequestCache<T extends ApiResponse?> {
|
||||
'json': jsonEncode(newValue),
|
||||
'lastupdate': DateTime.now().millisecondsSinceEpoch
|
||||
});
|
||||
} on WebuntisError catch(e) {
|
||||
} on Exception catch(e) {
|
||||
onError(e);
|
||||
}
|
||||
}
|
||||
|
@ -18,19 +18,11 @@ AuthenticateResponse _$AuthenticateResponseFromJson(
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$AuthenticateResponseToJson(
|
||||
AuthenticateResponse instance) {
|
||||
final val = <String, dynamic>{};
|
||||
|
||||
void writeNotNull(String key, dynamic value) {
|
||||
if (value != null) {
|
||||
val[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
writeNotNull('headers', instance.headers);
|
||||
val['sessionId'] = instance.sessionId;
|
||||
val['personType'] = instance.personType;
|
||||
val['personId'] = instance.personId;
|
||||
val['klasseId'] = instance.klasseId;
|
||||
return val;
|
||||
}
|
||||
AuthenticateResponse instance) =>
|
||||
<String, dynamic>{
|
||||
if (instance.headers case final value?) 'headers': value,
|
||||
'sessionId': instance.sessionId,
|
||||
'personType': instance.personType,
|
||||
'personId': instance.personId,
|
||||
'klasseId': instance.klasseId,
|
||||
};
|
||||
|
@ -16,19 +16,12 @@ GetHolidaysResponse _$GetHolidaysResponseFromJson(Map<String, dynamic> json) =>
|
||||
(k, e) => MapEntry(k, e as String),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$GetHolidaysResponseToJson(GetHolidaysResponse instance) {
|
||||
final val = <String, dynamic>{};
|
||||
|
||||
void writeNotNull(String key, dynamic value) {
|
||||
if (value != null) {
|
||||
val[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
writeNotNull('headers', instance.headers);
|
||||
val['result'] = instance.result.map((e) => e.toJson()).toList();
|
||||
return val;
|
||||
}
|
||||
Map<String, dynamic> _$GetHolidaysResponseToJson(
|
||||
GetHolidaysResponse instance) =>
|
||||
<String, dynamic>{
|
||||
if (instance.headers case final value?) 'headers': value,
|
||||
'result': instance.result.map((e) => e.toJson()).toList(),
|
||||
};
|
||||
|
||||
GetHolidaysResponseObject _$GetHolidaysResponseObjectFromJson(
|
||||
Map<String, dynamic> json) =>
|
||||
|
@ -16,19 +16,11 @@ GetRoomsResponse _$GetRoomsResponseFromJson(Map<String, dynamic> json) =>
|
||||
(k, e) => MapEntry(k, e as String),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$GetRoomsResponseToJson(GetRoomsResponse instance) {
|
||||
final val = <String, dynamic>{};
|
||||
|
||||
void writeNotNull(String key, dynamic value) {
|
||||
if (value != null) {
|
||||
val[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
writeNotNull('headers', instance.headers);
|
||||
val['result'] = instance.result.map((e) => e.toJson()).toList();
|
||||
return val;
|
||||
}
|
||||
Map<String, dynamic> _$GetRoomsResponseToJson(GetRoomsResponse instance) =>
|
||||
<String, dynamic>{
|
||||
if (instance.headers case final value?) 'headers': value,
|
||||
'result': instance.result.map((e) => e.toJson()).toList(),
|
||||
};
|
||||
|
||||
GetRoomsResponseObject _$GetRoomsResponseObjectFromJson(
|
||||
Map<String, dynamic> json) =>
|
||||
|
@ -16,19 +16,12 @@ GetSubjectsResponse _$GetSubjectsResponseFromJson(Map<String, dynamic> json) =>
|
||||
(k, e) => MapEntry(k, e as String),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$GetSubjectsResponseToJson(GetSubjectsResponse instance) {
|
||||
final val = <String, dynamic>{};
|
||||
|
||||
void writeNotNull(String key, dynamic value) {
|
||||
if (value != null) {
|
||||
val[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
writeNotNull('headers', instance.headers);
|
||||
val['result'] = instance.result.map((e) => e.toJson()).toList();
|
||||
return val;
|
||||
}
|
||||
Map<String, dynamic> _$GetSubjectsResponseToJson(
|
||||
GetSubjectsResponse instance) =>
|
||||
<String, dynamic>{
|
||||
if (instance.headers case final value?) 'headers': value,
|
||||
'result': instance.result.map((e) => e.toJson()).toList(),
|
||||
};
|
||||
|
||||
GetSubjectsResponseObject _$GetSubjectsResponseObjectFromJson(
|
||||
Map<String, dynamic> json) =>
|
||||
|
@ -46,48 +46,41 @@ GetTimetableParamsOptions _$GetTimetableParamsOptionsFromJson(
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$GetTimetableParamsOptionsToJson(
|
||||
GetTimetableParamsOptions instance) {
|
||||
final val = <String, dynamic>{
|
||||
'element': instance.element.toJson(),
|
||||
};
|
||||
|
||||
void writeNotNull(String key, dynamic value) {
|
||||
if (value != null) {
|
||||
val[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
writeNotNull('startDate', instance.startDate);
|
||||
writeNotNull('endDate', instance.endDate);
|
||||
writeNotNull('onlyBaseTimetable', instance.onlyBaseTimetable);
|
||||
writeNotNull('showBooking', instance.showBooking);
|
||||
writeNotNull('showInfo', instance.showInfo);
|
||||
writeNotNull('showSubstText', instance.showSubstText);
|
||||
writeNotNull('showLsText', instance.showLsText);
|
||||
writeNotNull('showLsNumber', instance.showLsNumber);
|
||||
writeNotNull('showStudentgroup', instance.showStudentgroup);
|
||||
writeNotNull(
|
||||
'klasseFields',
|
||||
instance.klasseFields
|
||||
?.map((e) => _$GetTimetableParamsOptionsFieldsEnumMap[e]!)
|
||||
.toList());
|
||||
writeNotNull(
|
||||
'roomFields',
|
||||
instance.roomFields
|
||||
?.map((e) => _$GetTimetableParamsOptionsFieldsEnumMap[e]!)
|
||||
.toList());
|
||||
writeNotNull(
|
||||
'subjectFields',
|
||||
instance.subjectFields
|
||||
?.map((e) => _$GetTimetableParamsOptionsFieldsEnumMap[e]!)
|
||||
.toList());
|
||||
writeNotNull(
|
||||
'teacherFields',
|
||||
instance.teacherFields
|
||||
?.map((e) => _$GetTimetableParamsOptionsFieldsEnumMap[e]!)
|
||||
.toList());
|
||||
return val;
|
||||
}
|
||||
GetTimetableParamsOptions instance) =>
|
||||
<String, dynamic>{
|
||||
'element': instance.element.toJson(),
|
||||
if (instance.startDate case final value?) 'startDate': value,
|
||||
if (instance.endDate case final value?) 'endDate': value,
|
||||
if (instance.onlyBaseTimetable case final value?)
|
||||
'onlyBaseTimetable': value,
|
||||
if (instance.showBooking case final value?) 'showBooking': value,
|
||||
if (instance.showInfo case final value?) 'showInfo': value,
|
||||
if (instance.showSubstText case final value?) 'showSubstText': value,
|
||||
if (instance.showLsText case final value?) 'showLsText': value,
|
||||
if (instance.showLsNumber case final value?) 'showLsNumber': value,
|
||||
if (instance.showStudentgroup case final value?)
|
||||
'showStudentgroup': value,
|
||||
if (instance.klasseFields
|
||||
?.map((e) => _$GetTimetableParamsOptionsFieldsEnumMap[e]!)
|
||||
.toList()
|
||||
case final value?)
|
||||
'klasseFields': value,
|
||||
if (instance.roomFields
|
||||
?.map((e) => _$GetTimetableParamsOptionsFieldsEnumMap[e]!)
|
||||
.toList()
|
||||
case final value?)
|
||||
'roomFields': value,
|
||||
if (instance.subjectFields
|
||||
?.map((e) => _$GetTimetableParamsOptionsFieldsEnumMap[e]!)
|
||||
.toList()
|
||||
case final value?)
|
||||
'subjectFields': value,
|
||||
if (instance.teacherFields
|
||||
?.map((e) => _$GetTimetableParamsOptionsFieldsEnumMap[e]!)
|
||||
.toList()
|
||||
case final value?)
|
||||
'teacherFields': value,
|
||||
};
|
||||
|
||||
const _$GetTimetableParamsOptionsFieldsEnumMap = {
|
||||
GetTimetableParamsOptionsFields.id: 'id',
|
||||
@ -106,22 +99,14 @@ GetTimetableParamsOptionsElement _$GetTimetableParamsOptionsElementFromJson(
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$GetTimetableParamsOptionsElementToJson(
|
||||
GetTimetableParamsOptionsElement instance) {
|
||||
final val = <String, dynamic>{
|
||||
'id': instance.id,
|
||||
'type': instance.type,
|
||||
};
|
||||
|
||||
void writeNotNull(String key, dynamic value) {
|
||||
if (value != null) {
|
||||
val[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
writeNotNull('keyType',
|
||||
_$GetTimetableParamsOptionsElementKeyTypeEnumMap[instance.keyType]);
|
||||
return val;
|
||||
}
|
||||
GetTimetableParamsOptionsElement instance) =>
|
||||
<String, dynamic>{
|
||||
'id': instance.id,
|
||||
'type': instance.type,
|
||||
if (_$GetTimetableParamsOptionsElementKeyTypeEnumMap[instance.keyType]
|
||||
case final value?)
|
||||
'keyType': value,
|
||||
};
|
||||
|
||||
const _$GetTimetableParamsOptionsElementKeyTypeEnumMap = {
|
||||
GetTimetableParamsOptionsElementKeyType.id: 'id',
|
||||
|
@ -18,19 +18,11 @@ GetTimetableResponse _$GetTimetableResponseFromJson(
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$GetTimetableResponseToJson(
|
||||
GetTimetableResponse instance) {
|
||||
final val = <String, dynamic>{};
|
||||
|
||||
void writeNotNull(String key, dynamic value) {
|
||||
if (value != null) {
|
||||
val[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
writeNotNull('headers', instance.headers);
|
||||
val['result'] = instance.result.map((e) => e.toJson()).toList();
|
||||
return val;
|
||||
}
|
||||
GetTimetableResponse instance) =>
|
||||
<String, dynamic>{
|
||||
if (instance.headers case final value?) 'headers': value,
|
||||
'result': instance.result.map((e) => e.toJson()).toList(),
|
||||
};
|
||||
|
||||
GetTimetableResponseObject _$GetTimetableResponseObjectFromJson(
|
||||
Map<String, dynamic> json) =>
|
||||
|
74
lib/app.dart
74
lib/app.dart
@ -8,7 +8,6 @@ import 'package:flutter/material.dart';
|
||||
import 'state/app/modules/app_modules.dart';
|
||||
import 'package:persistent_bottom_nav_bar_v2/persistent_bottom_nav_bar_v2.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:badges/badges.dart' as badges;
|
||||
|
||||
import 'api/mhsl/breaker/getBreakers/getBreakersResponse.dart';
|
||||
import 'api/mhsl/server/userIndex/update/updateUserindex.dart';
|
||||
@ -93,57 +92,34 @@ class _AppState extends State<App> with WidgetsBindingObserver {
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => PersistentTabView(
|
||||
controller: Main.bottomNavigator,
|
||||
navBarOverlap: const NavBarOverlap.none(),
|
||||
backgroundColor: Theme.of(context).colorScheme.primary,
|
||||
handleAndroidBackButtonPress: true,
|
||||
Widget build(BuildContext context) => Consumer<SettingsProvider>(builder: (context, settings, child) => PersistentTabView(
|
||||
controller: Main.bottomNavigator,
|
||||
navBarOverlap: const NavBarOverlap.none(),
|
||||
backgroundColor: Theme.of(context).colorScheme.primary,
|
||||
handleAndroidBackButtonPress: false,
|
||||
|
||||
screenTransitionAnimation: const ScreenTransitionAnimation(curve: Curves.easeOutQuad, duration: Duration(milliseconds: 200)),
|
||||
tabs: [
|
||||
AppModule.getModule(Modules.timetable).toBottomTab(context),
|
||||
AppModule.getModule(Modules.talk).toBottomTab(
|
||||
context,
|
||||
itemBuilder: (icon) => Consumer<ChatListProps>(
|
||||
builder: (context, value, child) {
|
||||
if(value.primaryLoading()) return Icon(icon);
|
||||
var messages = value.getRoomsResponse.data.map((e) => e.unreadMessages).reduce((a, b) => a+b);
|
||||
return badges.Badge(
|
||||
showBadge: messages > 0,
|
||||
position: badges.BadgePosition.topEnd(top: -3, end: -3),
|
||||
stackFit: StackFit.loose,
|
||||
badgeStyle: badges.BadgeStyle(
|
||||
padding: const EdgeInsets.all(3),
|
||||
badgeColor: Theme.of(context).primaryColor,
|
||||
elevation: 1,
|
||||
),
|
||||
badgeContent: Text('$messages', style: const TextStyle(color: Colors.white, fontSize: 10, fontWeight: FontWeight.bold)),
|
||||
child: Icon(icon),
|
||||
);
|
||||
},
|
||||
screenTransitionAnimation: const ScreenTransitionAnimation(curve: Curves.easeOutQuad, duration: Duration(milliseconds: 200)),
|
||||
tabs: [
|
||||
...AppModule.getBottomBarModules(context).map((e) => e.toBottomTab(context)),
|
||||
|
||||
PersistentTabConfig(
|
||||
screen: const Breaker(breaker: BreakerArea.more, child: Overhang()),
|
||||
item: ItemConfig(
|
||||
activeForegroundColor: Theme.of(context).primaryColor,
|
||||
inactiveForegroundColor: Theme.of(context).colorScheme.secondary,
|
||||
icon: const Icon(Icons.apps),
|
||||
title: 'Mehr'
|
||||
),
|
||||
),
|
||||
],
|
||||
navBarBuilder: (config) => Style6BottomNavBar(
|
||||
navBarConfig: config,
|
||||
navBarDecoration: NavBarDecoration(
|
||||
border: const Border(top: BorderSide(width: 1, color: Colors.grey)),
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
),
|
||||
),
|
||||
AppModule.getModule(Modules.blocFiles).toBottomTab(context),
|
||||
AppModule.getModule(Modules.files).toBottomTab(context),
|
||||
|
||||
PersistentTabConfig(
|
||||
screen: const Breaker(breaker: BreakerArea.more, child: Overhang()),
|
||||
item: ItemConfig(
|
||||
activeForegroundColor: Theme.of(context).primaryColor,
|
||||
inactiveForegroundColor: Theme.of(context).colorScheme.secondary,
|
||||
icon: const Icon(Icons.apps),
|
||||
title: 'Mehr'
|
||||
),
|
||||
),
|
||||
],
|
||||
navBarBuilder: (config) => Style6BottomNavBar(
|
||||
navBarConfig: config,
|
||||
navBarDecoration: NavBarDecoration(
|
||||
border: const Border(top: BorderSide(width: 1, color: Colors.grey)),
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
),
|
||||
),
|
||||
);
|
||||
));
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
|
@ -7,13 +7,13 @@ import 'package:firebase_messaging/firebase_messaging.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||
import 'package:hydrated_bloc/hydrated_bloc.dart';
|
||||
import 'package:jiffy/jiffy.dart';
|
||||
import 'package:loader_overlay/loader_overlay.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:persistent_bottom_nav_bar_v2/persistent_bottom_nav_bar_v2.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||
|
||||
import 'api/mhsl/breaker/getBreakers/getBreakersResponse.dart';
|
||||
import 'app.dart';
|
||||
@ -42,13 +42,15 @@ Future<void> main() async {
|
||||
var initialisationTasks = [
|
||||
Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform)
|
||||
.then((value) async => log("Firebase token: ${await FirebaseMessaging.instance.getToken() ?? "Error: no Firebase token!"}"))
|
||||
.onError((error, stackTrace) => log('Error initializing Firebase: $error', stackTrace: stackTrace)),
|
||||
.onError((error, stackTrace) => log('Error initializing Firebase: $error')),
|
||||
|
||||
PlatformAssetBundle().load('assets/ca/lets-encrypt-r3.pem').then(addCertificateAsTrusted),
|
||||
PlatformAssetBundle().load('assets/ca/lets-encrypt-r10.pem').then(addCertificateAsTrusted),
|
||||
|
||||
Future(() async {
|
||||
await HydratedStorage.build(storageDirectory: await getTemporaryDirectory()).then((storage) => HydratedBloc.storage = storage);
|
||||
await HydratedStorage.build(
|
||||
storageDirectory: HydratedStorageDirectory((await getTemporaryDirectory()).path)
|
||||
).then((storage) => HydratedBloc.storage = storage);
|
||||
})
|
||||
];
|
||||
|
||||
|
@ -23,7 +23,11 @@ class _BreakerState extends State<Breaker> {
|
||||
builder: (context, value, child) {
|
||||
var blocked = value.isBlocked(widget.breaker);
|
||||
if(blocked != null) {
|
||||
return PlaceholderView(icon: Icons.security_outlined, text: "Die App/ Dieser Bereich wurde als Schutzmaßnahme deaktiviert!\n\n${blocked.isEmpty ? "Es wurde vom Server kein Grund übermittelt." : blocked}");
|
||||
return PlaceholderView(
|
||||
icon: Icons.app_blocking_outlined,
|
||||
text: 'Die App / Dieser Bereich ist zurzeit nicht verfügbar!\n\n'
|
||||
"${blocked.isEmpty ? "Es wurde vom Server kein Grund übermittelt.\nAktualisiere die App und versuche es später erneut" : blocked}"
|
||||
);
|
||||
}
|
||||
|
||||
return widget.child;
|
||||
|
@ -1,4 +1,5 @@
|
||||
import 'package:package_info/package_info.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:package_info_plus/package_info_plus.dart';
|
||||
|
||||
import '../../api/apiResponse.dart';
|
||||
import '../../api/mhsl/breaker/getBreakers/getBreakersCache.dart';
|
||||
@ -12,6 +13,8 @@ class BreakerProps extends DataHolder {
|
||||
PackageInfo? packageInfo;
|
||||
|
||||
String? isBlocked(BreakerArea? type) {
|
||||
if(kDebugMode) return null;
|
||||
|
||||
if(packageInfo == null) {
|
||||
PackageInfo.fromPlatform().then((value) => packageInfo = value);
|
||||
return null;
|
||||
|
@ -1,5 +1,5 @@
|
||||
|
||||
import 'package:flutter_app_badger/flutter_app_badger.dart';
|
||||
import 'package:flutter_app_badge/flutter_app_badge.dart';
|
||||
|
||||
import '../../api/apiResponse.dart';
|
||||
import '../../api/marianumcloud/talk/room/getRoomCache.dart';
|
||||
@ -20,7 +20,7 @@ class ChatListProps extends DataHolder {
|
||||
onUpdate: (GetRoomResponse data) => {
|
||||
_getRoomResponse = data,
|
||||
notifyListeners(),
|
||||
FlutterAppBadger.updateBadgeCount(data.data.map((e) => e.unreadMessages).reduce((a, b) => a+b))
|
||||
FlutterAppBadge.count(data.data.map((e) => e.unreadMessages).reduce((a, b) => a+b))
|
||||
}
|
||||
);
|
||||
}
|
||||
|
@ -44,7 +44,7 @@ class NotificationController {
|
||||
}
|
||||
|
||||
static Future<void> onAppOpenedByNotification(RemoteMessage message, BuildContext context) async {
|
||||
NotificationTasks.navigateToTalk();
|
||||
NotificationTasks.navigateToTalk(context);
|
||||
NotificationTasks.updateProviders(context);
|
||||
|
||||
DebugTile(context).run(() {
|
||||
|
@ -15,9 +15,6 @@ class NotificationService {
|
||||
);
|
||||
|
||||
final iosSettings = DarwinInitializationSettings(
|
||||
onDidReceiveLocalNotification: (id, title, body, payload) {
|
||||
// TODO Navigate to Talk section (This runs when an Notification is tapped)
|
||||
},
|
||||
);
|
||||
|
||||
|
||||
|
@ -1,15 +1,16 @@
|
||||
import 'package:firebase_messaging/firebase_messaging.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter_app_badger/flutter_app_badger.dart';
|
||||
import 'package:flutter_app_badge/flutter_app_badge.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
import '../main.dart';
|
||||
import '../model/chatList/chatListProps.dart';
|
||||
import '../model/chatList/chatProps.dart';
|
||||
import '../state/app/modules/app_modules.dart';
|
||||
|
||||
class NotificationTasks {
|
||||
static void updateBadgeCount(RemoteMessage notification) {
|
||||
FlutterAppBadger.updateBadgeCount(int.parse(notification.data['unreadCount'] ?? 0));
|
||||
FlutterAppBadge.count(int.parse(notification.data['unreadCount'] ?? 0));
|
||||
}
|
||||
|
||||
static void updateProviders(BuildContext context) {
|
||||
@ -17,7 +18,9 @@ class NotificationTasks {
|
||||
Provider.of<ChatProps>(context, listen: false).run();
|
||||
}
|
||||
|
||||
static void navigateToTalk() {
|
||||
Main.bottomNavigator.jumpToTab(1);
|
||||
static void navigateToTalk(BuildContext context) {
|
||||
var talkTab = AppModule.getBottomBarModules(context).map((e) => e.module).toList().indexOf(Modules.talk);
|
||||
if(talkTab == -1) return;
|
||||
Main.bottomNavigator.jumpToTab(talkTab);
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
import 'package:dio/dio.dart';
|
||||
|
||||
import '../../infrastructure/dataLoader/http_data_loader.dart';
|
||||
import '../../infrastructure/dataLoader/data_loader.dart';
|
||||
|
||||
abstract class HolidayDataLoader<TResult> extends HttpDataLoader<TResult> {
|
||||
abstract class HolidayDataLoader<TResult> extends DataLoader<TResult> {
|
||||
HolidayDataLoader() : super(Dio(BaseOptions(
|
||||
baseUrl: 'https://ferien-api.de/api/v1/',
|
||||
)));
|
||||
|
@ -1,8 +1,8 @@
|
||||
import 'package:dio/dio.dart';
|
||||
|
||||
import '../../infrastructure/dataLoader/http_data_loader.dart';
|
||||
import '../../infrastructure/dataLoader/data_loader.dart';
|
||||
|
||||
abstract class MhslDataLoader<TResult> extends HttpDataLoader<TResult> {
|
||||
abstract class MhslDataLoader<TResult> extends DataLoader<TResult> {
|
||||
MhslDataLoader() : super(Dio(BaseOptions(
|
||||
baseUrl: 'https://mhsl.eu/marianum/marianummobile/'
|
||||
)));
|
||||
|
@ -2,21 +2,26 @@ import 'dart:convert';
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
abstract class HttpDataLoader<TResult> {
|
||||
abstract class DataLoader<TResult> {
|
||||
final Dio dio;
|
||||
HttpDataLoader(this.dio) {
|
||||
DataLoader(this.dio) {
|
||||
dio.options.connectTimeout = const Duration(seconds: 10).inMilliseconds;
|
||||
dio.options.sendTimeout = const Duration(seconds: 30).inMilliseconds;
|
||||
dio.options.receiveTimeout = const Duration(seconds: 30).inMilliseconds;
|
||||
}
|
||||
|
||||
Future<TResult> run() async {
|
||||
var response = await fetch();
|
||||
var fetcher = fetch();
|
||||
await Future.wait([
|
||||
fetcher,
|
||||
Future.delayed(const Duration(milliseconds: 500)) // TODO tune or remove
|
||||
]);
|
||||
|
||||
var response = await fetcher;
|
||||
try {
|
||||
return assemble(DataLoaderResult(
|
||||
json: await compute(jsonDecode, response.data!),
|
||||
json: jsonDecode(response.data!),
|
||||
headers: response.headers.map.map((key, value) => MapEntry(key, value.join(';'))),
|
||||
));
|
||||
} catch(trace, e) {
|
@ -19,7 +19,9 @@ mixin _$LoadableStateState {
|
||||
List<ConnectivityResult>? get connections =>
|
||||
throw _privateConstructorUsedError;
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
/// Create a copy of LoadableStateState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
$LoadableStateStateCopyWith<LoadableStateState> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
@ -43,6 +45,8 @@ class _$LoadableStateStateCopyWithImpl<$Res, $Val extends LoadableStateState>
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
/// Create a copy of LoadableStateState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
@ -76,6 +80,8 @@ class __$$LoadableStateStateImplCopyWithImpl<$Res>
|
||||
$Res Function(_$LoadableStateStateImpl) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
/// Create a copy of LoadableStateState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
@ -125,7 +131,9 @@ class _$LoadableStateStateImpl implements _LoadableStateState {
|
||||
int get hashCode => Object.hash(
|
||||
runtimeType, const DeepCollectionEquality().hash(_connections));
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
/// Create a copy of LoadableStateState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$LoadableStateStateImplCopyWith<_$LoadableStateStateImpl> get copyWith =>
|
||||
@ -140,8 +148,11 @@ abstract class _LoadableStateState implements LoadableStateState {
|
||||
|
||||
@override
|
||||
List<ConnectivityResult>? get connections;
|
||||
|
||||
/// Create a copy of LoadableStateState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@JsonKey(ignore: true)
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
_$$LoadableStateStateImplCopyWith<_$LoadableStateStateImpl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
@ -22,7 +22,9 @@ mixin _$LoadableState<TState> {
|
||||
void Function()? get reFetch => throw _privateConstructorUsedError;
|
||||
LoadingError? get error => throw _privateConstructorUsedError;
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
/// Create a copy of LoadableState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
$LoadableStateCopyWith<TState, LoadableState<TState>> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
@ -54,6 +56,8 @@ class _$LoadableStateCopyWithImpl<TState, $Res,
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
/// Create a copy of LoadableState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
@ -87,6 +91,8 @@ class _$LoadableStateCopyWithImpl<TState, $Res,
|
||||
) as $Val);
|
||||
}
|
||||
|
||||
/// Create a copy of LoadableState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
$LoadingErrorCopyWith<$Res>? get error {
|
||||
@ -128,6 +134,8 @@ class __$$LoadableStateImplCopyWithImpl<TState, $Res>
|
||||
$Res Function(_$LoadableStateImpl<TState>) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
/// Create a copy of LoadableState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
@ -207,7 +215,9 @@ class _$LoadableStateImpl<TState> extends _LoadableState<TState> {
|
||||
int get hashCode => Object.hash(runtimeType, isLoading,
|
||||
const DeepCollectionEquality().hash(data), lastFetch, reFetch, error);
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
/// Create a copy of LoadableState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$LoadableStateImplCopyWith<TState, _$LoadableStateImpl<TState>>
|
||||
@ -234,8 +244,11 @@ abstract class _LoadableState<TState> extends LoadableState<TState> {
|
||||
void Function()? get reFetch;
|
||||
@override
|
||||
LoadingError? get error;
|
||||
|
||||
/// Create a copy of LoadableState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@JsonKey(ignore: true)
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
_$$LoadableStateImplCopyWith<TState, _$LoadableStateImpl<TState>>
|
||||
get copyWith => throw _privateConstructorUsedError;
|
||||
}
|
||||
|
@ -19,7 +19,9 @@ mixin _$LoadingError {
|
||||
String get message => throw _privateConstructorUsedError;
|
||||
bool get allowRetry => throw _privateConstructorUsedError;
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
/// Create a copy of LoadingError
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
$LoadingErrorCopyWith<LoadingError> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
@ -43,6 +45,8 @@ class _$LoadingErrorCopyWithImpl<$Res, $Val extends LoadingError>
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
/// Create a copy of LoadingError
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
@ -81,6 +85,8 @@ class __$$LoadingErrorImplCopyWithImpl<$Res>
|
||||
_$LoadingErrorImpl _value, $Res Function(_$LoadingErrorImpl) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
/// Create a copy of LoadingError
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
@ -129,7 +135,9 @@ class _$LoadingErrorImpl implements _LoadingError {
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType, message, allowRetry);
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
/// Create a copy of LoadingError
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$LoadingErrorImplCopyWith<_$LoadingErrorImpl> get copyWith =>
|
||||
@ -145,8 +153,11 @@ abstract class _LoadingError implements LoadingError {
|
||||
String get message;
|
||||
@override
|
||||
bool get allowRetry;
|
||||
|
||||
/// Create a copy of LoadingError
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@JsonKey(ignore: true)
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
_$$LoadingErrorImplCopyWith<_$LoadingErrorImpl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
@ -17,16 +17,13 @@ class LoadableStateConsumer<TController extends Bloc<LoadableHydratedBlocEvent<T
|
||||
final Widget Function(TState state, bool loading) child;
|
||||
final void Function(TState state)? onLoad;
|
||||
final bool wrapWithScrollView;
|
||||
final TController? controllerByValue;
|
||||
const LoadableStateConsumer({required this.child, this.onLoad, this.wrapWithScrollView = false, this.controllerByValue = null, super.key});
|
||||
const LoadableStateConsumer({required this.child, this.onLoad, this.wrapWithScrollView = false, super.key});
|
||||
|
||||
static Duration animationDuration = const Duration(milliseconds: 200);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var loadableState = controllerByValue != null
|
||||
? controllerByValue!.state
|
||||
: context.watch<TController>().state;
|
||||
var loadableState = context.watch<TController>().state;
|
||||
|
||||
if(!loadableState.isLoading && onLoad != null) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((timeStamp) => onLoad!(loadableState.data));
|
||||
|
@ -30,17 +30,16 @@ abstract class LoadableHydratedBloc<
|
||||
isLoading: state.isLoading,
|
||||
data: event.state(innerState ?? fromNothing()),
|
||||
lastFetch: state.lastFetch,
|
||||
reFetch: fetch,
|
||||
reFetch: retry,
|
||||
error: state.error,
|
||||
));
|
||||
if(event.fetch) fetch();
|
||||
});
|
||||
|
||||
on<DataGathered<TState>>((event, emit) => emit(LoadableState(
|
||||
isLoading: false,
|
||||
data: event.state(innerState ?? fromNothing()),
|
||||
lastFetch: DateTime.now().millisecondsSinceEpoch,
|
||||
reFetch: fetch,
|
||||
reFetch: retry,
|
||||
error: null,
|
||||
)));
|
||||
|
||||
@ -56,7 +55,7 @@ abstract class LoadableHydratedBloc<
|
||||
isLoading: false,
|
||||
data: innerState,
|
||||
lastFetch: state.lastFetch,
|
||||
reFetch: fetch,
|
||||
reFetch: retry,
|
||||
error: event.error
|
||||
)));
|
||||
|
||||
@ -67,14 +66,19 @@ abstract class LoadableHydratedBloc<
|
||||
TState? get innerState => state.data;
|
||||
TRepository get repo => _repository;
|
||||
|
||||
void retry() {
|
||||
log('Fetch retry triggered for ${TState.toString()}');
|
||||
add(RefetchStarted<TState>());
|
||||
fetch();
|
||||
}
|
||||
|
||||
void fetch() {
|
||||
log('Fetching data for ${TState.toString()}');
|
||||
add(RefetchStarted<TState>());
|
||||
gatherData().catchError(
|
||||
(e) {
|
||||
log('Error while fetching ${TState.toString()}: ${e.toString()}');
|
||||
add(Error(LoadingError(
|
||||
message: e.toString(),
|
||||
message: e.message ?? e.toString(),
|
||||
allowRetry: true,
|
||||
)));
|
||||
},
|
||||
@ -88,7 +92,7 @@ abstract class LoadableHydratedBloc<
|
||||
var rawData = LoadableSaveContext.unwrap(json);
|
||||
return LoadableState(
|
||||
isLoading: true,
|
||||
data: fromStorage(rawData.data), // TODO fromStorage in isolate
|
||||
data: fromStorage(rawData.data),
|
||||
lastFetch: rawData.meta.timestamp,
|
||||
reFetch: null,
|
||||
error: null,
|
||||
@ -99,7 +103,7 @@ abstract class LoadableHydratedBloc<
|
||||
Map<String, dynamic>? toJson(LoadableState<TState> state) {
|
||||
Map<String, dynamic>? data;
|
||||
try {
|
||||
data = state.data == null ? null : toStorage(state.data); // TODO toStorage in isolate
|
||||
data = state.data == null ? null : toStorage(state.data);
|
||||
} catch(e) {
|
||||
log('Failed to save state ${TState.toString()}: ${e.toString()}');
|
||||
}
|
||||
|
3
lib/state/app/infrastructure/utilityWidgets/loadableHydratedBloc/loadable_hydrated_bloc_event.dart
3
lib/state/app/infrastructure/utilityWidgets/loadableHydratedBloc/loadable_hydrated_bloc_event.dart
@ -3,8 +3,7 @@ import '../../loadableState/loading_error.dart';
|
||||
class LoadableHydratedBlocEvent<TState> {}
|
||||
class Emit<TState> extends LoadableHydratedBlocEvent<TState> {
|
||||
final TState Function(TState state) state;
|
||||
final bool fetch;
|
||||
Emit(this.state, {this.fetch = false});
|
||||
Emit(this.state);
|
||||
}
|
||||
class DataGathered<TState> extends LoadableHydratedBlocEvent<TState> {
|
||||
final TState Function(TState state) state;
|
||||
|
21
lib/state/app/infrastructure/utilityWidgets/loadableHydratedBloc/loadable_save_context.freezed.dart
21
lib/state/app/infrastructure/utilityWidgets/loadableHydratedBloc/loadable_save_context.freezed.dart
@ -22,8 +22,12 @@ LoadableSaveContext _$LoadableSaveContextFromJson(Map<String, dynamic> json) {
|
||||
mixin _$LoadableSaveContext {
|
||||
int get timestamp => throw _privateConstructorUsedError;
|
||||
|
||||
/// Serializes this LoadableSaveContext to a JSON map.
|
||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
@JsonKey(ignore: true)
|
||||
|
||||
/// Create a copy of LoadableSaveContext
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
$LoadableSaveContextCopyWith<LoadableSaveContext> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
@ -47,6 +51,8 @@ class _$LoadableSaveContextCopyWithImpl<$Res, $Val extends LoadableSaveContext>
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
/// Create a copy of LoadableSaveContext
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
@ -80,6 +86,8 @@ class __$$LoadableSaveContextImplCopyWithImpl<$Res>
|
||||
$Res Function(_$LoadableSaveContextImpl) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
/// Create a copy of LoadableSaveContext
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
@ -119,11 +127,13 @@ class _$LoadableSaveContextImpl extends _LoadableSaveContext {
|
||||
other.timestamp == timestamp));
|
||||
}
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType, timestamp);
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
/// Create a copy of LoadableSaveContext
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$LoadableSaveContextImplCopyWith<_$LoadableSaveContextImpl> get copyWith =>
|
||||
@ -148,8 +158,11 @@ abstract class _LoadableSaveContext extends LoadableSaveContext {
|
||||
|
||||
@override
|
||||
int get timestamp;
|
||||
|
||||
/// Create a copy of LoadableSaveContext
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@JsonKey(ignore: true)
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
_$$LoadableSaveContextImplCopyWith<_$LoadableSaveContextImpl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
@ -1,48 +0,0 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:bloc/bloc.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'bloc_module.dart';
|
||||
|
||||
class SwappingBloc<TBloc> {
|
||||
final TBloc initialBloc;
|
||||
final StreamController<TBloc> updater = StreamController<TBloc>();
|
||||
|
||||
SwappingBloc(this.initialBloc);
|
||||
|
||||
void swap(TBloc bloc) {
|
||||
updater.add(bloc);
|
||||
}
|
||||
}
|
||||
|
||||
class SwappingBlocModule<TBloc extends StateStreamableSource<TState>, TState> extends StatefulWidget {
|
||||
final SwappingBloc<TBloc> bloc;
|
||||
final Widget Function(BuildContext context, TBloc bloc, TState state) child;
|
||||
const SwappingBlocModule({super.key, required this.bloc, required this.child});
|
||||
|
||||
@override
|
||||
State<SwappingBlocModule<TBloc, TState>> createState() => _SwappingBlocModuleState<TBloc, TState>();
|
||||
}
|
||||
|
||||
class _SwappingBlocModuleState<TBloc extends StateStreamableSource<TState>, TState> extends State<SwappingBlocModule<TBloc, TState>> {
|
||||
late TBloc bloc;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
bloc = widget.bloc.initialBloc;
|
||||
widget.bloc.updater.stream.listen((event) {
|
||||
setState(() {
|
||||
bloc = event;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => BlocModule<TBloc, TState>(
|
||||
autoRebuild: true,
|
||||
create: (context) => bloc,
|
||||
child: (context, bloc, state) => widget.child(context, bloc, state),
|
||||
);
|
||||
}
|
@ -1,51 +1,129 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:persistent_bottom_nav_bar_v2/persistent_bottom_nav_bar_v2.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
import '../../../api/mhsl/breaker/getBreakers/getBreakersResponse.dart';
|
||||
import '../../../model/breakers/Breaker.dart';
|
||||
import '../../../model/chatList/chatListProps.dart';
|
||||
import '../../../storage/base/settingsProvider.dart';
|
||||
import '../../../view/pages/files/files.dart';
|
||||
import '../../../view/pages/more/roomplan/roomplan.dart';
|
||||
import '../../../view/pages/talk/chatList.dart';
|
||||
import '../../../view/pages/timetable/timetable.dart';
|
||||
import '../../../widget/centeredLeading.dart';
|
||||
import 'files/view/files_view.dart';
|
||||
import 'gradeAverages/view/grade_averages_view.dart';
|
||||
import 'holidays/view/holidays_view.dart';
|
||||
import 'marianumMessage/view/marianum_message_list_view.dart';
|
||||
|
||||
import 'package:badges/badges.dart' as badges;
|
||||
|
||||
class AppModule {
|
||||
Modules module;
|
||||
String name;
|
||||
IconData icon;
|
||||
Widget Function() icon;
|
||||
BreakerArea breakerArea;
|
||||
Widget Function() create;
|
||||
|
||||
AppModule(this.name, this.icon, this.create);
|
||||
AppModule(this.module, {required this.name, required this.icon, this.breakerArea = BreakerArea.global, required this.create});
|
||||
|
||||
static Map<Modules, AppModule> modules() => {
|
||||
Modules.timetable: AppModule('Vertretung', Icons.calendar_month, Timetable.new),
|
||||
Modules.talk: AppModule('Talk', Icons.chat, ChatList.new),
|
||||
Modules.files: AppModule('Files', Icons.folder, Files.new),
|
||||
Modules.blocFiles: AppModule('BlocFiles', Icons.folder, FilesView.new),
|
||||
Modules.marianumMessage: AppModule('Marianum Message', Icons.newspaper, MarianumMessageListView.new),
|
||||
Modules.roomPlan: AppModule('Raumplan', Icons.location_pin, Roomplan.new),
|
||||
Modules.gradeAveragesCalculator: AppModule('Notendurschnittsrechner', Icons.calculate, GradeAveragesView.new),
|
||||
Modules.holidays: AppModule('Schulferien', Icons.flight, HolidaysView.new),
|
||||
};
|
||||
static Map<Modules, AppModule> modules(BuildContext context, { showFiltered = false }) {
|
||||
var settings = Provider.of<SettingsProvider>(context, listen: false);
|
||||
var available = {
|
||||
Modules.timetable: AppModule(
|
||||
Modules.timetable,
|
||||
name: 'Vertretung',
|
||||
icon: () => Icon(Icons.calendar_month),
|
||||
breakerArea: BreakerArea.timetable,
|
||||
create: Timetable.new,
|
||||
),
|
||||
Modules.talk: AppModule(
|
||||
Modules.talk,
|
||||
name: 'Talk',
|
||||
icon: () => Consumer<ChatListProps>(
|
||||
builder: (context, value, child) {
|
||||
if(value.primaryLoading()) return Icon(Icons.chat);
|
||||
var messages = value.getRoomsResponse.data.map((e) => e.unreadMessages).reduce((a, b) => a+b);
|
||||
return badges.Badge(
|
||||
showBadge: messages > 0,
|
||||
position: badges.BadgePosition.topEnd(top: -3, end: -3),
|
||||
stackFit: StackFit.loose,
|
||||
badgeStyle: badges.BadgeStyle(
|
||||
padding: const EdgeInsets.all(3),
|
||||
badgeColor: Theme.of(context).primaryColor,
|
||||
elevation: 1,
|
||||
),
|
||||
badgeContent: Text('$messages', style: const TextStyle(color: Colors.white, fontSize: 10, fontWeight: FontWeight.bold)),
|
||||
child: Icon(Icons.chat),
|
||||
);
|
||||
},
|
||||
),
|
||||
breakerArea: BreakerArea.talk,
|
||||
create: ChatList.new,
|
||||
),
|
||||
Modules.files: AppModule(
|
||||
Modules.files,
|
||||
name: 'Dateien',
|
||||
icon: () => Icon(Icons.folder),
|
||||
breakerArea: BreakerArea.files,
|
||||
create: Files.new,
|
||||
),
|
||||
Modules.marianumMessage: AppModule(
|
||||
Modules.marianumMessage,
|
||||
name: 'Marianum Message',
|
||||
icon: () => Icon(Icons.newspaper),
|
||||
breakerArea: BreakerArea.more,
|
||||
create: MarianumMessageListView.new,
|
||||
),
|
||||
Modules.roomPlan: AppModule(
|
||||
Modules.roomPlan,
|
||||
name: 'Raumplan',
|
||||
icon: () => Icon(Icons.location_pin),
|
||||
breakerArea: BreakerArea.more,
|
||||
create: Roomplan.new,
|
||||
),
|
||||
Modules.gradeAveragesCalculator: AppModule(
|
||||
Modules.gradeAveragesCalculator,
|
||||
name: 'Notendurschnittsrechner',
|
||||
icon: () => Icon(Icons.calculate),
|
||||
breakerArea: BreakerArea.more,
|
||||
create: GradeAveragesView.new,
|
||||
),
|
||||
Modules.holidays: AppModule(
|
||||
Modules.holidays,
|
||||
name: 'Schulferien',
|
||||
icon: () => Icon(Icons.flight),
|
||||
breakerArea: BreakerArea.more,
|
||||
create: HolidaysView.new,
|
||||
),
|
||||
};
|
||||
|
||||
static AppModule getModule(Modules module) => modules()[module]!;
|
||||
if(!showFiltered) available.removeWhere((key, value) => settings.val().modulesSettings.hiddenModules.contains(key));
|
||||
|
||||
Widget toListTile(BuildContext context) => ListTile(
|
||||
leading: CenteredLeading(Icon(icon)),
|
||||
return { for (var element in settings.val().modulesSettings.moduleOrder.where((element) => available.containsKey(element))) element : available[element]! };
|
||||
}
|
||||
|
||||
static List<AppModule> getBottomBarModules(BuildContext context) => modules(context).values.toList().getRange(0, 3).toList();
|
||||
static List<AppModule> getOverhangModules(BuildContext context) => modules(context).values.skip(3).toList();
|
||||
|
||||
Widget toListTile(BuildContext context, {Key? key, bool isReorder = false, Function()? onVisibleChange, bool isVisible = true}) => ListTile(
|
||||
key: key,
|
||||
leading: CenteredLeading(icon()),
|
||||
title: Text(name),
|
||||
onTap: () => pushScreen(context, withNavBar: false, screen: create()),
|
||||
trailing: const Icon(Icons.arrow_right),
|
||||
onTap: isReorder ? null : () => pushScreen(context, withNavBar: false, screen: create()),
|
||||
trailing: isReorder
|
||||
? Row(mainAxisSize: MainAxisSize.min, children: [
|
||||
IconButton(onPressed: onVisibleChange, icon: Icon(isVisible ? Icons.visibility_outlined : Icons.visibility_off_outlined)),
|
||||
Icon(Icons.drag_handle_outlined)
|
||||
])
|
||||
: const Icon(Icons.arrow_right),
|
||||
);
|
||||
|
||||
PersistentTabConfig toBottomTab(BuildContext context, {Widget Function(IconData icon)? itemBuilder}) => PersistentTabConfig(
|
||||
screen: Breaker(breaker: BreakerArea.global, child: create()),
|
||||
PersistentTabConfig toBottomTab(BuildContext context, {Widget Function(IconData icon)? iconBuilder}) => PersistentTabConfig(
|
||||
screen: Breaker(breaker: breakerArea, child: create()),
|
||||
item: ItemConfig(
|
||||
activeForegroundColor: Theme.of(context).primaryColor,
|
||||
inactiveForegroundColor: Theme.of(context).colorScheme.secondary,
|
||||
icon: itemBuilder == null ? Icon(icon) : itemBuilder(icon),
|
||||
icon: icon(),
|
||||
title: name
|
||||
),
|
||||
);
|
||||
@ -55,7 +133,6 @@ enum Modules {
|
||||
timetable,
|
||||
talk,
|
||||
files,
|
||||
blocFiles,
|
||||
marianumMessage,
|
||||
roomPlan,
|
||||
gradeAveragesCalculator,
|
||||
|
@ -1,71 +0,0 @@
|
||||
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:sorted/sorted.dart';
|
||||
|
||||
import '../../../infrastructure/utilityWidgets/loadableHydratedBloc/loadable_hydrated_bloc.dart';
|
||||
import '../../../infrastructure/utilityWidgets/loadableHydratedBloc/loadable_hydrated_bloc_event.dart';
|
||||
import '../repository/files_repository.dart';
|
||||
import 'files_event.dart';
|
||||
import 'files_state.dart';
|
||||
|
||||
class FilesBloc extends LoadableHydratedBloc<FilesEvent, FilesState, FilesRepository> {
|
||||
static const String basePath = '/';
|
||||
|
||||
FilesBloc() {
|
||||
add(Emit((state) => state.copyWith(currentFolder: basePath)));
|
||||
on<EnterFolder>((event, emit) {
|
||||
add(Emit((state) {
|
||||
log('setFolder');
|
||||
return state.copyWith(currentFolder: event.absolutePath);
|
||||
}, fetch: true));
|
||||
});
|
||||
}
|
||||
|
||||
List<File>? getVisibleFiles() => innerState?.files[innerState?.currentFolder];
|
||||
String getCurrentFolder() => innerState?.currentFolder ?? basePath;
|
||||
String getCurrentFolderName() {
|
||||
var folder = innerState?.currentFolder.split('/').reversed.elementAt(1);
|
||||
return folder!.isEmpty ? 'Dateien' : folder;
|
||||
}
|
||||
bool canGoBack() => innerState?.currentFolder != basePath;
|
||||
String goBackLocation() {
|
||||
var pathSegments = innerState?.currentFolder.split(basePath) ?? [];
|
||||
if (pathSegments.isNotEmpty) {
|
||||
pathSegments.removeLast();
|
||||
pathSegments.removeLast();
|
||||
}
|
||||
return pathSegments.join(basePath) + basePath;
|
||||
}
|
||||
|
||||
List currentSortConfiguration() => [
|
||||
SortedComparable<File, DateTime>((file) => file.updatedAt ?? DateTime.now()),
|
||||
];
|
||||
|
||||
@override
|
||||
FilesState fromNothing() => const FilesState(currentFolder: basePath, files: {});
|
||||
|
||||
@override
|
||||
FilesState fromStorage(Map<String, dynamic> json) => FilesState.fromJson(json);
|
||||
|
||||
@override
|
||||
Future<void> gatherData() async {
|
||||
var fetchFolder = getCurrentFolder();
|
||||
log(fetchFolder);
|
||||
var files = await repo.getFileList(fetchFolder);
|
||||
var newFileMap = Map.of(innerState?.files ?? <String, List<File>>{});
|
||||
newFileMap[fetchFolder] = files;
|
||||
if(fetchFolder != getCurrentFolder()) {
|
||||
log('Fetch aborted due to folder change (expected "$fetchFolder" got "${getCurrentFolder()}")');
|
||||
return;
|
||||
}
|
||||
add(DataGathered((state) => state.copyWith(files: newFileMap)));
|
||||
}
|
||||
|
||||
@override
|
||||
FilesRepository repository() => FilesRepository();
|
||||
|
||||
@override
|
||||
Map<String, dynamic>? toStorage(FilesState state) => state.toJson();
|
||||
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
|
||||
import '../../../infrastructure/utilityWidgets/loadableHydratedBloc/loadable_hydrated_bloc_event.dart';
|
||||
import 'files_state.dart';
|
||||
|
||||
sealed class FilesEvent extends LoadableHydratedBlocEvent<FilesState> {}
|
||||
class EnterFolder extends FilesEvent {
|
||||
String absolutePath;
|
||||
EnterFolder(this.absolutePath);
|
||||
}
|
||||
|
@ -1,29 +0,0 @@
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
part 'files_state.freezed.dart';
|
||||
part 'files_state.g.dart';
|
||||
|
||||
@freezed
|
||||
class FilesState with _$FilesState {
|
||||
const factory FilesState({
|
||||
required String currentFolder,
|
||||
required Map<String, List<File>> files,
|
||||
}) = _FilesState;
|
||||
|
||||
factory FilesState.fromJson(Map<String, Object?> json) => _$FilesStateFromJson(json);
|
||||
}
|
||||
|
||||
@freezed
|
||||
class File with _$File {
|
||||
const factory File({
|
||||
required String path,
|
||||
required bool isFolder,
|
||||
required String name,
|
||||
required DateTime? createdAt,
|
||||
required DateTime? updatedAt,
|
||||
required int? size,
|
||||
required String? mimeType,
|
||||
}) = _File;
|
||||
|
||||
factory File.fromJson(Map<String, Object?> json) => _$FileFromJson(json);
|
||||
}
|
@ -1,439 +0,0 @@
|
||||
// coverage:ignore-file
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
|
||||
|
||||
part of 'files_state.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// FreezedGenerator
|
||||
// **************************************************************************
|
||||
|
||||
T _$identity<T>(T value) => value;
|
||||
|
||||
final _privateConstructorUsedError = UnsupportedError(
|
||||
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');
|
||||
|
||||
FilesState _$FilesStateFromJson(Map<String, dynamic> json) {
|
||||
return _FilesState.fromJson(json);
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
mixin _$FilesState {
|
||||
String get currentFolder => throw _privateConstructorUsedError;
|
||||
Map<String, List<File>> get files => throw _privateConstructorUsedError;
|
||||
|
||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
@JsonKey(ignore: true)
|
||||
$FilesStateCopyWith<FilesState> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class $FilesStateCopyWith<$Res> {
|
||||
factory $FilesStateCopyWith(
|
||||
FilesState value, $Res Function(FilesState) then) =
|
||||
_$FilesStateCopyWithImpl<$Res, FilesState>;
|
||||
@useResult
|
||||
$Res call({String currentFolder, Map<String, List<File>> files});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$FilesStateCopyWithImpl<$Res, $Val extends FilesState>
|
||||
implements $FilesStateCopyWith<$Res> {
|
||||
_$FilesStateCopyWithImpl(this._value, this._then);
|
||||
|
||||
// ignore: unused_field
|
||||
final $Val _value;
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? currentFolder = null,
|
||||
Object? files = null,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
currentFolder: null == currentFolder
|
||||
? _value.currentFolder
|
||||
: currentFolder // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
files: null == files
|
||||
? _value.files
|
||||
: files // ignore: cast_nullable_to_non_nullable
|
||||
as Map<String, List<File>>,
|
||||
) as $Val);
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$$FilesStateImplCopyWith<$Res>
|
||||
implements $FilesStateCopyWith<$Res> {
|
||||
factory _$$FilesStateImplCopyWith(
|
||||
_$FilesStateImpl value, $Res Function(_$FilesStateImpl) then) =
|
||||
__$$FilesStateImplCopyWithImpl<$Res>;
|
||||
@override
|
||||
@useResult
|
||||
$Res call({String currentFolder, Map<String, List<File>> files});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$FilesStateImplCopyWithImpl<$Res>
|
||||
extends _$FilesStateCopyWithImpl<$Res, _$FilesStateImpl>
|
||||
implements _$$FilesStateImplCopyWith<$Res> {
|
||||
__$$FilesStateImplCopyWithImpl(
|
||||
_$FilesStateImpl _value, $Res Function(_$FilesStateImpl) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? currentFolder = null,
|
||||
Object? files = null,
|
||||
}) {
|
||||
return _then(_$FilesStateImpl(
|
||||
currentFolder: null == currentFolder
|
||||
? _value.currentFolder
|
||||
: currentFolder // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
files: null == files
|
||||
? _value._files
|
||||
: files // ignore: cast_nullable_to_non_nullable
|
||||
as Map<String, List<File>>,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@JsonSerializable()
|
||||
class _$FilesStateImpl implements _FilesState {
|
||||
const _$FilesStateImpl(
|
||||
{required this.currentFolder,
|
||||
required final Map<String, List<File>> files})
|
||||
: _files = files;
|
||||
|
||||
factory _$FilesStateImpl.fromJson(Map<String, dynamic> json) =>
|
||||
_$$FilesStateImplFromJson(json);
|
||||
|
||||
@override
|
||||
final String currentFolder;
|
||||
final Map<String, List<File>> _files;
|
||||
@override
|
||||
Map<String, List<File>> get files {
|
||||
if (_files is EqualUnmodifiableMapView) return _files;
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableMapView(_files);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'FilesState(currentFolder: $currentFolder, files: $files)';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$FilesStateImpl &&
|
||||
(identical(other.currentFolder, currentFolder) ||
|
||||
other.currentFolder == currentFolder) &&
|
||||
const DeepCollectionEquality().equals(other._files, _files));
|
||||
}
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@override
|
||||
int get hashCode => Object.hash(
|
||||
runtimeType, currentFolder, const DeepCollectionEquality().hash(_files));
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$FilesStateImplCopyWith<_$FilesStateImpl> get copyWith =>
|
||||
__$$FilesStateImplCopyWithImpl<_$FilesStateImpl>(this, _$identity);
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return _$$FilesStateImplToJson(
|
||||
this,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
abstract class _FilesState implements FilesState {
|
||||
const factory _FilesState(
|
||||
{required final String currentFolder,
|
||||
required final Map<String, List<File>> files}) = _$FilesStateImpl;
|
||||
|
||||
factory _FilesState.fromJson(Map<String, dynamic> json) =
|
||||
_$FilesStateImpl.fromJson;
|
||||
|
||||
@override
|
||||
String get currentFolder;
|
||||
@override
|
||||
Map<String, List<File>> get files;
|
||||
@override
|
||||
@JsonKey(ignore: true)
|
||||
_$$FilesStateImplCopyWith<_$FilesStateImpl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
File _$FileFromJson(Map<String, dynamic> json) {
|
||||
return _File.fromJson(json);
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
mixin _$File {
|
||||
String get path => throw _privateConstructorUsedError;
|
||||
bool get isFolder => throw _privateConstructorUsedError;
|
||||
String get name => throw _privateConstructorUsedError;
|
||||
DateTime? get createdAt => throw _privateConstructorUsedError;
|
||||
DateTime? get updatedAt => throw _privateConstructorUsedError;
|
||||
int? get size => throw _privateConstructorUsedError;
|
||||
String? get mimeType => throw _privateConstructorUsedError;
|
||||
|
||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
@JsonKey(ignore: true)
|
||||
$FileCopyWith<File> get copyWith => throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class $FileCopyWith<$Res> {
|
||||
factory $FileCopyWith(File value, $Res Function(File) then) =
|
||||
_$FileCopyWithImpl<$Res, File>;
|
||||
@useResult
|
||||
$Res call(
|
||||
{String path,
|
||||
bool isFolder,
|
||||
String name,
|
||||
DateTime? createdAt,
|
||||
DateTime? updatedAt,
|
||||
int? size,
|
||||
String? mimeType});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$FileCopyWithImpl<$Res, $Val extends File>
|
||||
implements $FileCopyWith<$Res> {
|
||||
_$FileCopyWithImpl(this._value, this._then);
|
||||
|
||||
// ignore: unused_field
|
||||
final $Val _value;
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? path = null,
|
||||
Object? isFolder = null,
|
||||
Object? name = null,
|
||||
Object? createdAt = freezed,
|
||||
Object? updatedAt = freezed,
|
||||
Object? size = freezed,
|
||||
Object? mimeType = freezed,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
path: null == path
|
||||
? _value.path
|
||||
: path // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
isFolder: null == isFolder
|
||||
? _value.isFolder
|
||||
: isFolder // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
name: null == name
|
||||
? _value.name
|
||||
: name // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
createdAt: freezed == createdAt
|
||||
? _value.createdAt
|
||||
: createdAt // ignore: cast_nullable_to_non_nullable
|
||||
as DateTime?,
|
||||
updatedAt: freezed == updatedAt
|
||||
? _value.updatedAt
|
||||
: updatedAt // ignore: cast_nullable_to_non_nullable
|
||||
as DateTime?,
|
||||
size: freezed == size
|
||||
? _value.size
|
||||
: size // ignore: cast_nullable_to_non_nullable
|
||||
as int?,
|
||||
mimeType: freezed == mimeType
|
||||
? _value.mimeType
|
||||
: mimeType // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
) as $Val);
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$$FileImplCopyWith<$Res> implements $FileCopyWith<$Res> {
|
||||
factory _$$FileImplCopyWith(
|
||||
_$FileImpl value, $Res Function(_$FileImpl) then) =
|
||||
__$$FileImplCopyWithImpl<$Res>;
|
||||
@override
|
||||
@useResult
|
||||
$Res call(
|
||||
{String path,
|
||||
bool isFolder,
|
||||
String name,
|
||||
DateTime? createdAt,
|
||||
DateTime? updatedAt,
|
||||
int? size,
|
||||
String? mimeType});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$FileImplCopyWithImpl<$Res>
|
||||
extends _$FileCopyWithImpl<$Res, _$FileImpl>
|
||||
implements _$$FileImplCopyWith<$Res> {
|
||||
__$$FileImplCopyWithImpl(_$FileImpl _value, $Res Function(_$FileImpl) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? path = null,
|
||||
Object? isFolder = null,
|
||||
Object? name = null,
|
||||
Object? createdAt = freezed,
|
||||
Object? updatedAt = freezed,
|
||||
Object? size = freezed,
|
||||
Object? mimeType = freezed,
|
||||
}) {
|
||||
return _then(_$FileImpl(
|
||||
path: null == path
|
||||
? _value.path
|
||||
: path // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
isFolder: null == isFolder
|
||||
? _value.isFolder
|
||||
: isFolder // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
name: null == name
|
||||
? _value.name
|
||||
: name // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
createdAt: freezed == createdAt
|
||||
? _value.createdAt
|
||||
: createdAt // ignore: cast_nullable_to_non_nullable
|
||||
as DateTime?,
|
||||
updatedAt: freezed == updatedAt
|
||||
? _value.updatedAt
|
||||
: updatedAt // ignore: cast_nullable_to_non_nullable
|
||||
as DateTime?,
|
||||
size: freezed == size
|
||||
? _value.size
|
||||
: size // ignore: cast_nullable_to_non_nullable
|
||||
as int?,
|
||||
mimeType: freezed == mimeType
|
||||
? _value.mimeType
|
||||
: mimeType // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@JsonSerializable()
|
||||
class _$FileImpl implements _File {
|
||||
const _$FileImpl(
|
||||
{required this.path,
|
||||
required this.isFolder,
|
||||
required this.name,
|
||||
required this.createdAt,
|
||||
required this.updatedAt,
|
||||
required this.size,
|
||||
required this.mimeType});
|
||||
|
||||
factory _$FileImpl.fromJson(Map<String, dynamic> json) =>
|
||||
_$$FileImplFromJson(json);
|
||||
|
||||
@override
|
||||
final String path;
|
||||
@override
|
||||
final bool isFolder;
|
||||
@override
|
||||
final String name;
|
||||
@override
|
||||
final DateTime? createdAt;
|
||||
@override
|
||||
final DateTime? updatedAt;
|
||||
@override
|
||||
final int? size;
|
||||
@override
|
||||
final String? mimeType;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'File(path: $path, isFolder: $isFolder, name: $name, createdAt: $createdAt, updatedAt: $updatedAt, size: $size, mimeType: $mimeType)';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$FileImpl &&
|
||||
(identical(other.path, path) || other.path == path) &&
|
||||
(identical(other.isFolder, isFolder) ||
|
||||
other.isFolder == isFolder) &&
|
||||
(identical(other.name, name) || other.name == name) &&
|
||||
(identical(other.createdAt, createdAt) ||
|
||||
other.createdAt == createdAt) &&
|
||||
(identical(other.updatedAt, updatedAt) ||
|
||||
other.updatedAt == updatedAt) &&
|
||||
(identical(other.size, size) || other.size == size) &&
|
||||
(identical(other.mimeType, mimeType) ||
|
||||
other.mimeType == mimeType));
|
||||
}
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@override
|
||||
int get hashCode => Object.hash(
|
||||
runtimeType, path, isFolder, name, createdAt, updatedAt, size, mimeType);
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$FileImplCopyWith<_$FileImpl> get copyWith =>
|
||||
__$$FileImplCopyWithImpl<_$FileImpl>(this, _$identity);
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return _$$FileImplToJson(
|
||||
this,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
abstract class _File implements File {
|
||||
const factory _File(
|
||||
{required final String path,
|
||||
required final bool isFolder,
|
||||
required final String name,
|
||||
required final DateTime? createdAt,
|
||||
required final DateTime? updatedAt,
|
||||
required final int? size,
|
||||
required final String? mimeType}) = _$FileImpl;
|
||||
|
||||
factory _File.fromJson(Map<String, dynamic> json) = _$FileImpl.fromJson;
|
||||
|
||||
@override
|
||||
String get path;
|
||||
@override
|
||||
bool get isFolder;
|
||||
@override
|
||||
String get name;
|
||||
@override
|
||||
DateTime? get createdAt;
|
||||
@override
|
||||
DateTime? get updatedAt;
|
||||
@override
|
||||
int? get size;
|
||||
@override
|
||||
String? get mimeType;
|
||||
@override
|
||||
@JsonKey(ignore: true)
|
||||
_$$FileImplCopyWith<_$FileImpl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
@ -1,50 +0,0 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'files_state.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
_$FilesStateImpl _$$FilesStateImplFromJson(Map<String, dynamic> json) =>
|
||||
_$FilesStateImpl(
|
||||
currentFolder: json['currentFolder'] as String,
|
||||
files: (json['files'] as Map<String, dynamic>).map(
|
||||
(k, e) => MapEntry(
|
||||
k,
|
||||
(e as List<dynamic>)
|
||||
.map((e) => File.fromJson(e as Map<String, dynamic>))
|
||||
.toList()),
|
||||
),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$FilesStateImplToJson(_$FilesStateImpl instance) =>
|
||||
<String, dynamic>{
|
||||
'currentFolder': instance.currentFolder,
|
||||
'files': instance.files,
|
||||
};
|
||||
|
||||
_$FileImpl _$$FileImplFromJson(Map<String, dynamic> json) => _$FileImpl(
|
||||
path: json['path'] as String,
|
||||
isFolder: json['isFolder'] as bool,
|
||||
name: json['name'] as String,
|
||||
createdAt: json['createdAt'] == null
|
||||
? null
|
||||
: DateTime.parse(json['createdAt'] as String),
|
||||
updatedAt: json['updatedAt'] == null
|
||||
? null
|
||||
: DateTime.parse(json['updatedAt'] as String),
|
||||
size: (json['size'] as num?)?.toInt(),
|
||||
mimeType: json['mimeType'] as String?,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$FileImplToJson(_$FileImpl instance) =>
|
||||
<String, dynamic>{
|
||||
'path': instance.path,
|
||||
'isFolder': instance.isFolder,
|
||||
'name': instance.name,
|
||||
'createdAt': instance.createdAt?.toIso8601String(),
|
||||
'updatedAt': instance.updatedAt?.toIso8601String(),
|
||||
'size': instance.size,
|
||||
'mimeType': instance.mimeType,
|
||||
};
|
@ -1,31 +0,0 @@
|
||||
|
||||
import 'package:nextcloud/nextcloud.dart';
|
||||
|
||||
import '../../../../../api/marianumcloud/webdav/webdavApi.dart';
|
||||
import '../../../infrastructure/repository/repository.dart';
|
||||
import '../bloc/files_state.dart';
|
||||
|
||||
class FilesRepository extends Repository<FilesState> {
|
||||
Future<List<File>> getFileList(String path) async {
|
||||
var webdav = await WebdavApi.webdav;
|
||||
var response = await webdav.propfind(PathUri.parse(path)); // TODO move to custom data loader
|
||||
var davFiles = response.toWebDavFiles();
|
||||
|
||||
davFiles.removeWhere((file) {
|
||||
var filePath = file.path.path;
|
||||
return filePath.isEmpty || filePath == path;
|
||||
});
|
||||
|
||||
var files = davFiles.map((davFile) => File(
|
||||
path: davFile.path.path,
|
||||
isFolder: davFile.isDirectory,
|
||||
name: davFile.name,
|
||||
createdAt: davFile.createdDate,
|
||||
updatedAt: davFile.lastModified,
|
||||
size: davFile.size,
|
||||
mimeType: davFile.mimeType
|
||||
));
|
||||
|
||||
return files.toList();
|
||||
}
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import '../../../infrastructure/loadableState/loadable_state.dart';
|
||||
import '../../../infrastructure/utilityWidgets/bloc_module.dart';
|
||||
import '../bloc/files_bloc.dart';
|
||||
import '../bloc/files_state.dart';
|
||||
import 'folder_view.dart';
|
||||
|
||||
class FilesView extends StatelessWidget {
|
||||
const FilesView({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => BlocModule<FilesBloc, LoadableState<FilesState>>(
|
||||
create: (context) => FilesBloc(),
|
||||
autoRebuild: true,
|
||||
child: (context, bloc, state) => FolderView(bloc),
|
||||
);
|
||||
}
|
@ -1,57 +0,0 @@
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:filesize/filesize.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:jiffy/jiffy.dart';
|
||||
|
||||
import '../../../../../widget/centeredLeading.dart';
|
||||
import '../../../../../widget/list_view_util.dart';
|
||||
import '../../../infrastructure/loadableState/view/loadable_state_consumer.dart';
|
||||
import '../bloc/files_bloc.dart';
|
||||
import '../bloc/files_event.dart';
|
||||
import '../bloc/files_state.dart';
|
||||
|
||||
class FolderView extends StatelessWidget {
|
||||
final FilesBloc bloc;
|
||||
const FolderView(this.bloc, {super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => Scaffold(
|
||||
appBar: AppBar(
|
||||
leading: !bloc.canGoBack() ? null : IconButton(
|
||||
icon: const Icon(Icons.arrow_back),
|
||||
onPressed: () {
|
||||
bloc.add(EnterFolder(bloc.goBackLocation()));
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
title: Text(bloc.getCurrentFolderName()),
|
||||
|
||||
actions: [
|
||||
IconButton(onPressed: () {
|
||||
log(bloc.innerState?.toJson().toString() ?? 'leer');
|
||||
}, icon: const Icon(Icons.bug_report)),
|
||||
IconButton(onPressed: () {
|
||||
bloc.add(EnterFolder('/'));
|
||||
}, icon: const Icon(Icons.home)),
|
||||
],
|
||||
),
|
||||
body: LoadableStateConsumer<FilesBloc, FilesState>(
|
||||
controllerByValue: bloc,
|
||||
child: (state, loading) => ListViewUtil.fromList<File>(bloc.getVisibleFiles(), (file) => ListTile(
|
||||
leading: CenteredLeading(Icon(file.isFolder ? Icons.folder : Icons.description_outlined)),
|
||||
title: Text(file.name),
|
||||
subtitle: file.isFolder
|
||||
? Text('geändert ${Jiffy.parseFromDateTime(file.updatedAt ?? DateTime.now()).fromNow()}')
|
||||
: Text('${filesize(file.size)}, ${Jiffy.parseFromDateTime(file.updatedAt ?? DateTime.now()).fromNow()}'),
|
||||
trailing: Icon(file.isFolder ? Icons.arrow_right : null),
|
||||
onTap: () {
|
||||
log(file.path);
|
||||
if(!file.isFolder) return;
|
||||
Navigator.of(context).push(MaterialPageRoute(builder: (context) => FolderView(bloc)));
|
||||
bloc.add(EnterFolder(file.path));
|
||||
},
|
||||
))
|
||||
)
|
||||
);
|
||||
}
|
@ -24,8 +24,12 @@ mixin _$GradeAveragesState {
|
||||
throw _privateConstructorUsedError;
|
||||
List<int> get grades => throw _privateConstructorUsedError;
|
||||
|
||||
/// Serializes this GradeAveragesState to a JSON map.
|
||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
@JsonKey(ignore: true)
|
||||
|
||||
/// Create a copy of GradeAveragesState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
$GradeAveragesStateCopyWith<GradeAveragesState> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
@ -49,6 +53,8 @@ class _$GradeAveragesStateCopyWithImpl<$Res, $Val extends GradeAveragesState>
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
/// Create a copy of GradeAveragesState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
@ -87,6 +93,8 @@ class __$$GradeAveragesStateImplCopyWithImpl<$Res>
|
||||
$Res Function(_$GradeAveragesStateImpl) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
/// Create a copy of GradeAveragesState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
@ -141,12 +149,14 @@ class _$GradeAveragesStateImpl implements _GradeAveragesState {
|
||||
const DeepCollectionEquality().equals(other._grades, _grades));
|
||||
}
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(
|
||||
runtimeType, gradingSystem, const DeepCollectionEquality().hash(_grades));
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
/// Create a copy of GradeAveragesState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$GradeAveragesStateImplCopyWith<_$GradeAveragesStateImpl> get copyWith =>
|
||||
@ -173,8 +183,11 @@ abstract class _GradeAveragesState implements GradeAveragesState {
|
||||
GradeAveragesGradingSystem get gradingSystem;
|
||||
@override
|
||||
List<int> get grades;
|
||||
|
||||
/// Create a copy of GradeAveragesState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@JsonKey(ignore: true)
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
_$$GradeAveragesStateImplCopyWith<_$GradeAveragesStateImpl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
@ -24,8 +24,12 @@ mixin _$HolidaysState {
|
||||
bool get showDisclaimer => throw _privateConstructorUsedError;
|
||||
List<Holiday> get holidays => throw _privateConstructorUsedError;
|
||||
|
||||
/// Serializes this HolidaysState to a JSON map.
|
||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
@JsonKey(ignore: true)
|
||||
|
||||
/// Create a copy of HolidaysState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
$HolidaysStateCopyWith<HolidaysState> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
@ -50,6 +54,8 @@ class _$HolidaysStateCopyWithImpl<$Res, $Val extends HolidaysState>
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
/// Create a copy of HolidaysState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
@ -94,6 +100,8 @@ class __$$HolidaysStateImplCopyWithImpl<$Res>
|
||||
_$HolidaysStateImpl _value, $Res Function(_$HolidaysStateImpl) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
/// Create a copy of HolidaysState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
@ -171,12 +179,14 @@ class _$HolidaysStateImpl
|
||||
const DeepCollectionEquality().equals(other._holidays, _holidays));
|
||||
}
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType, showPastHolidays, showDisclaimer,
|
||||
const DeepCollectionEquality().hash(_holidays));
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
/// Create a copy of HolidaysState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$HolidaysStateImplCopyWith<_$HolidaysStateImpl> get copyWith =>
|
||||
@ -205,8 +215,11 @@ abstract class _HolidaysState implements HolidaysState {
|
||||
bool get showDisclaimer;
|
||||
@override
|
||||
List<Holiday> get holidays;
|
||||
|
||||
/// Create a copy of HolidaysState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@JsonKey(ignore: true)
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
_$$HolidaysStateImplCopyWith<_$HolidaysStateImpl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
@ -224,8 +237,12 @@ mixin _$Holiday {
|
||||
String get name => throw _privateConstructorUsedError;
|
||||
String get slug => throw _privateConstructorUsedError;
|
||||
|
||||
/// Serializes this Holiday to a JSON map.
|
||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
@JsonKey(ignore: true)
|
||||
|
||||
/// Create a copy of Holiday
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
$HolidayCopyWith<Holiday> get copyWith => throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
@ -253,6 +270,8 @@ class _$HolidayCopyWithImpl<$Res, $Val extends Holiday>
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
/// Create a copy of Holiday
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
@ -316,6 +335,8 @@ class __$$HolidayImplCopyWithImpl<$Res>
|
||||
_$HolidayImpl _value, $Res Function(_$HolidayImpl) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
/// Create a copy of Holiday
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
@ -414,12 +435,14 @@ class _$HolidayImpl with DiagnosticableTreeMixin implements _Holiday {
|
||||
(identical(other.slug, slug) || other.slug == slug));
|
||||
}
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode =>
|
||||
Object.hash(runtimeType, start, end, year, stateCode, name, slug);
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
/// Create a copy of Holiday
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$HolidayImplCopyWith<_$HolidayImpl> get copyWith =>
|
||||
@ -456,8 +479,11 @@ abstract class _Holiday implements Holiday {
|
||||
String get name;
|
||||
@override
|
||||
String get slug;
|
||||
|
||||
/// Create a copy of Holiday
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@JsonKey(ignore: true)
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
_$$HolidayImplCopyWith<_$HolidayImpl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import 'package:dio/dio.dart';
|
||||
|
||||
import '../../../basis/dataloader/holiday_data_loader.dart';
|
||||
import '../../../infrastructure/dataLoader/http_data_loader.dart';
|
||||
import '../../../infrastructure/dataLoader/data_loader.dart';
|
||||
import '../bloc/holidays_state.dart';
|
||||
|
||||
class HolidaysGetHolidays extends HolidayDataLoader<List<Holiday>> {
|
||||
|
@ -22,8 +22,12 @@ MarianumMessageState _$MarianumMessageStateFromJson(Map<String, dynamic> json) {
|
||||
mixin _$MarianumMessageState {
|
||||
MarianumMessageList get messageList => throw _privateConstructorUsedError;
|
||||
|
||||
/// Serializes this MarianumMessageState to a JSON map.
|
||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
@JsonKey(ignore: true)
|
||||
|
||||
/// Create a copy of MarianumMessageState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
$MarianumMessageStateCopyWith<MarianumMessageState> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
@ -50,6 +54,8 @@ class _$MarianumMessageStateCopyWithImpl<$Res,
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
/// Create a copy of MarianumMessageState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
@ -63,6 +69,8 @@ class _$MarianumMessageStateCopyWithImpl<$Res,
|
||||
) as $Val);
|
||||
}
|
||||
|
||||
/// Create a copy of MarianumMessageState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
$MarianumMessageListCopyWith<$Res> get messageList {
|
||||
@ -94,6 +102,8 @@ class __$$MarianumMessageStateImplCopyWithImpl<$Res>
|
||||
$Res Function(_$MarianumMessageStateImpl) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
/// Create a copy of MarianumMessageState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
@ -133,11 +143,13 @@ class _$MarianumMessageStateImpl implements _MarianumMessageState {
|
||||
other.messageList == messageList));
|
||||
}
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType, messageList);
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
/// Create a copy of MarianumMessageState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$MarianumMessageStateImplCopyWith<_$MarianumMessageStateImpl>
|
||||
@ -163,8 +175,11 @@ abstract class _MarianumMessageState implements MarianumMessageState {
|
||||
|
||||
@override
|
||||
MarianumMessageList get messageList;
|
||||
|
||||
/// Create a copy of MarianumMessageState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@JsonKey(ignore: true)
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
_$$MarianumMessageStateImplCopyWith<_$MarianumMessageStateImpl>
|
||||
get copyWith => throw _privateConstructorUsedError;
|
||||
}
|
||||
@ -178,8 +193,12 @@ mixin _$MarianumMessageList {
|
||||
String get base => throw _privateConstructorUsedError;
|
||||
List<MarianumMessage> get messages => throw _privateConstructorUsedError;
|
||||
|
||||
/// Serializes this MarianumMessageList to a JSON map.
|
||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
@JsonKey(ignore: true)
|
||||
|
||||
/// Create a copy of MarianumMessageList
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
$MarianumMessageListCopyWith<MarianumMessageList> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
@ -203,6 +222,8 @@ class _$MarianumMessageListCopyWithImpl<$Res, $Val extends MarianumMessageList>
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
/// Create a copy of MarianumMessageList
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
@ -241,6 +262,8 @@ class __$$MarianumMessageListImplCopyWithImpl<$Res>
|
||||
$Res Function(_$MarianumMessageListImpl) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
/// Create a copy of MarianumMessageList
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
@ -294,12 +317,14 @@ class _$MarianumMessageListImpl implements _MarianumMessageList {
|
||||
const DeepCollectionEquality().equals(other._messages, _messages));
|
||||
}
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(
|
||||
runtimeType, base, const DeepCollectionEquality().hash(_messages));
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
/// Create a copy of MarianumMessageList
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$MarianumMessageListImplCopyWith<_$MarianumMessageListImpl> get copyWith =>
|
||||
@ -327,8 +352,11 @@ abstract class _MarianumMessageList implements MarianumMessageList {
|
||||
String get base;
|
||||
@override
|
||||
List<MarianumMessage> get messages;
|
||||
|
||||
/// Create a copy of MarianumMessageList
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@JsonKey(ignore: true)
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
_$$MarianumMessageListImplCopyWith<_$MarianumMessageListImpl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
@ -343,8 +371,12 @@ mixin _$MarianumMessage {
|
||||
String get date => throw _privateConstructorUsedError;
|
||||
String get url => throw _privateConstructorUsedError;
|
||||
|
||||
/// Serializes this MarianumMessage to a JSON map.
|
||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
@JsonKey(ignore: true)
|
||||
|
||||
/// Create a copy of MarianumMessage
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
$MarianumMessageCopyWith<MarianumMessage> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
@ -368,6 +400,8 @@ class _$MarianumMessageCopyWithImpl<$Res, $Val extends MarianumMessage>
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
/// Create a copy of MarianumMessage
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
@ -411,6 +445,8 @@ class __$$MarianumMessageImplCopyWithImpl<$Res>
|
||||
_$MarianumMessageImpl _value, $Res Function(_$MarianumMessageImpl) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
/// Create a copy of MarianumMessage
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
@ -466,11 +502,13 @@ class _$MarianumMessageImpl implements _MarianumMessage {
|
||||
(identical(other.url, url) || other.url == url));
|
||||
}
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType, name, date, url);
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
/// Create a copy of MarianumMessage
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$MarianumMessageImplCopyWith<_$MarianumMessageImpl> get copyWith =>
|
||||
@ -500,8 +538,11 @@ abstract class _MarianumMessage implements MarianumMessage {
|
||||
String get date;
|
||||
@override
|
||||
String get url;
|
||||
|
||||
/// Create a copy of MarianumMessage
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@JsonKey(ignore: true)
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
_$$MarianumMessageImplCopyWith<_$MarianumMessageImpl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
import 'package:dio/dio.dart';
|
||||
|
||||
import '../../../infrastructure/dataLoader/http_data_loader.dart';
|
||||
import '../../../infrastructure/dataLoader/data_loader.dart';
|
||||
import '../../../basis/dataloader/mhsl_data_loader.dart';
|
||||
import '../bloc/marianum_message_state.dart';
|
||||
|
||||
|
@ -4,6 +4,7 @@ import 'package:json_annotation/json_annotation.dart';
|
||||
import '../devTools/devToolsSettings.dart';
|
||||
import '../file/fileSettings.dart';
|
||||
import '../fileView/fileViewSettings.dart';
|
||||
import '../general/modulesSettings.dart';
|
||||
import '../holidays/holidaysSettings.dart';
|
||||
import '../notification/notificationSettings.dart';
|
||||
import '../talk/talkSettings.dart';
|
||||
@ -20,6 +21,7 @@ class Settings {
|
||||
ThemeMode appTheme;
|
||||
bool devToolsEnabled;
|
||||
|
||||
ModulesSettings modulesSettings;
|
||||
TimetableSettings timetableSettings;
|
||||
TalkSettings talkSettings;
|
||||
FileSettings fileSettings;
|
||||
@ -31,6 +33,7 @@ class Settings {
|
||||
Settings({
|
||||
required this.appTheme,
|
||||
required this.devToolsEnabled,
|
||||
required this.modulesSettings,
|
||||
required this.timetableSettings,
|
||||
required this.talkSettings,
|
||||
required this.fileSettings,
|
||||
|
@ -9,6 +9,8 @@ part of 'settings.dart';
|
||||
Settings _$SettingsFromJson(Map<String, dynamic> json) => Settings(
|
||||
appTheme: Settings._themeFromJson(json['appTheme'] as String),
|
||||
devToolsEnabled: json['devToolsEnabled'] as bool,
|
||||
modulesSettings: ModulesSettings.fromJson(
|
||||
json['modulesSettings'] as Map<String, dynamic>),
|
||||
timetableSettings: TimetableSettings.fromJson(
|
||||
json['timetableSettings'] as Map<String, dynamic>),
|
||||
talkSettings:
|
||||
@ -28,6 +30,7 @@ Settings _$SettingsFromJson(Map<String, dynamic> json) => Settings(
|
||||
Map<String, dynamic> _$SettingsToJson(Settings instance) => <String, dynamic>{
|
||||
'appTheme': Settings._themeToJson(instance.appTheme),
|
||||
'devToolsEnabled': instance.devToolsEnabled,
|
||||
'modulesSettings': instance.modulesSettings.toJson(),
|
||||
'timetableSettings': instance.timetableSettings.toJson(),
|
||||
'talkSettings': instance.talkSettings.toJson(),
|
||||
'fileSettings': instance.fileSettings.toJson(),
|
||||
|
19
lib/storage/general/modulesSettings.dart
Normal file
19
lib/storage/general/modulesSettings.dart
Normal file
@ -0,0 +1,19 @@
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
import '../../state/app/modules/app_modules.dart';
|
||||
|
||||
part 'modulesSettings.g.dart';
|
||||
|
||||
@JsonSerializable()
|
||||
class ModulesSettings {
|
||||
List<Modules> moduleOrder;
|
||||
List<Modules> hiddenModules;
|
||||
|
||||
ModulesSettings({
|
||||
required this.moduleOrder,
|
||||
required this.hiddenModules
|
||||
});
|
||||
|
||||
factory ModulesSettings.fromJson(Map<String, dynamic> json) => _$ModulesSettingsFromJson(json);
|
||||
Map<String, dynamic> toJson() => _$ModulesSettingsToJson(this);
|
||||
}
|
35
lib/storage/general/modulesSettings.g.dart
Normal file
35
lib/storage/general/modulesSettings.g.dart
Normal file
@ -0,0 +1,35 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'modulesSettings.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
ModulesSettings _$ModulesSettingsFromJson(Map<String, dynamic> json) =>
|
||||
ModulesSettings(
|
||||
moduleOrder: (json['moduleOrder'] as List<dynamic>)
|
||||
.map((e) => $enumDecode(_$ModulesEnumMap, e))
|
||||
.toList(),
|
||||
hiddenModules: (json['hiddenModules'] as List<dynamic>)
|
||||
.map((e) => $enumDecode(_$ModulesEnumMap, e))
|
||||
.toList(),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$ModulesSettingsToJson(ModulesSettings instance) =>
|
||||
<String, dynamic>{
|
||||
'moduleOrder':
|
||||
instance.moduleOrder.map((e) => _$ModulesEnumMap[e]!).toList(),
|
||||
'hiddenModules':
|
||||
instance.hiddenModules.map((e) => _$ModulesEnumMap[e]!).toList(),
|
||||
};
|
||||
|
||||
const _$ModulesEnumMap = {
|
||||
Modules.timetable: 'timetable',
|
||||
Modules.talk: 'talk',
|
||||
Modules.files: 'files',
|
||||
Modules.marianumMessage: 'marianumMessage',
|
||||
Modules.roomPlan: 'roomPlan',
|
||||
Modules.gradeAveragesCalculator: 'gradeAveragesCalculator',
|
||||
Modules.holidays: 'holidays',
|
||||
};
|
@ -1,12 +1,18 @@
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
|
||||
import '../../view/pages/timetable/timetableNameMode.dart';
|
||||
|
||||
part 'timetableSettings.g.dart';
|
||||
|
||||
@JsonSerializable()
|
||||
class TimetableSettings {
|
||||
bool connectDoubleLessons;
|
||||
TimetableNameMode timetableNameMode;
|
||||
|
||||
TimetableSettings({required this.connectDoubleLessons});
|
||||
TimetableSettings({
|
||||
required this.connectDoubleLessons,
|
||||
required this.timetableNameMode
|
||||
});
|
||||
|
||||
factory TimetableSettings.fromJson(Map<String, dynamic> json) => _$TimetableSettingsFromJson(json);
|
||||
Map<String, dynamic> toJson() => _$TimetableSettingsToJson(this);
|
||||
|
@ -9,9 +9,19 @@ part of 'timetableSettings.dart';
|
||||
TimetableSettings _$TimetableSettingsFromJson(Map<String, dynamic> json) =>
|
||||
TimetableSettings(
|
||||
connectDoubleLessons: json['connectDoubleLessons'] as bool,
|
||||
timetableNameMode:
|
||||
$enumDecode(_$TimetableNameModeEnumMap, json['timetableNameMode']),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$TimetableSettingsToJson(TimetableSettings instance) =>
|
||||
<String, dynamic>{
|
||||
'connectDoubleLessons': instance.connectDoubleLessons,
|
||||
'timetableNameMode':
|
||||
_$TimetableNameModeEnumMap[instance.timetableNameMode]!,
|
||||
};
|
||||
|
||||
const _$TimetableNameModeEnumMap = {
|
||||
TimetableNameMode.name: 'name',
|
||||
TimetableNameMode.longName: 'longName',
|
||||
TimetableNameMode.alternateName: 'alternateName',
|
||||
};
|
||||
|
@ -1,26 +1,21 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../widget/dropdownDisplay.dart';
|
||||
|
||||
class AppTheme {
|
||||
static ThemeModeDisplay getDisplayOptions(ThemeMode theme) {
|
||||
static DropdownDisplay getDisplayOptions(ThemeMode theme) {
|
||||
switch(theme) {
|
||||
case ThemeMode.system:
|
||||
return ThemeModeDisplay(icon: Icons.auto_fix_high_outlined, displayName: 'Systemvorgabe');
|
||||
return DropdownDisplay(icon: Icons.auto_fix_high_outlined, displayName: 'Systemvorgabe');
|
||||
|
||||
case ThemeMode.light:
|
||||
return ThemeModeDisplay(icon: Icons.wb_sunny_outlined, displayName: 'Hell');
|
||||
return DropdownDisplay(icon: Icons.wb_sunny_outlined, displayName: 'Hell');
|
||||
|
||||
case ThemeMode.dark:
|
||||
return ThemeModeDisplay(icon: Icons.dark_mode_outlined, displayName: 'Dunkel');
|
||||
return DropdownDisplay(icon: Icons.dark_mode_outlined, displayName: 'Dunkel');
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
static bool isDarkMode(BuildContext context) => Theme.of(context).brightness == Brightness.dark;
|
||||
}
|
||||
|
||||
class ThemeModeDisplay {
|
||||
final IconData icon;
|
||||
final String displayName;
|
||||
|
||||
ThemeModeDisplay({required this.icon, required this.displayName});
|
||||
}
|
||||
|
@ -8,6 +8,6 @@ class LightAppTheme {
|
||||
colorScheme: ColorScheme.fromSeed(seedColor: marianumRed),
|
||||
floatingActionButtonTheme: const FloatingActionButtonThemeData(
|
||||
foregroundColor: Colors.white
|
||||
),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ import 'dart:typed_data';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:image_picker/image_picker.dart';
|
||||
import 'package:loader_overlay/loader_overlay.dart';
|
||||
import 'package:package_info/package_info.dart';
|
||||
import 'package:package_info_plus/package_info_plus.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:badges/badges.dart' as badges;
|
||||
|
||||
@ -70,7 +70,7 @@ class _FeedbackDialogState extends State<FeedbackDialog> {
|
||||
decoration: InputDecoration(
|
||||
border: const OutlineInputBorder(),
|
||||
label: const Text('Feedback und Verbesserungen'),
|
||||
errorText: _textFieldEmpty ? 'Bitte gib eine Beschreibung an???' : null,
|
||||
errorText: _textFieldEmpty ? 'Bitte gib eine Beschreibung an!' : null,
|
||||
),
|
||||
minLines: 4,
|
||||
maxLines: 7,
|
||||
|
@ -3,76 +3,124 @@ import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:in_app_review/in_app_review.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import '../../extensions/renderNotNull.dart';
|
||||
import 'package:persistent_bottom_nav_bar_v2/persistent_bottom_nav_bar_v2.dart';
|
||||
|
||||
import '../../state/app/modules/app_modules.dart';
|
||||
import '../../storage/base/settingsProvider.dart';
|
||||
import '../../widget/centeredLeading.dart';
|
||||
import '../../widget/infoDialog.dart';
|
||||
import '../settings/defaultSettings.dart';
|
||||
import '../settings/settings.dart';
|
||||
import 'more/feedback/feedbackDialog.dart';
|
||||
import 'more/share/selectShareTypeDialog.dart';
|
||||
|
||||
class Overhang extends StatelessWidget {
|
||||
class Overhang extends StatefulWidget {
|
||||
const Overhang({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => Scaffold(
|
||||
State<Overhang> createState() => _OverhangState();
|
||||
}
|
||||
|
||||
class _OverhangState extends State<Overhang> {
|
||||
bool editMode = false;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => Consumer<SettingsProvider>(builder: (context, settings, child) => Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Mehr'),
|
||||
actions: [
|
||||
IconButton(onPressed: () => pushScreen(context, screen: const Settings(), withNavBar: false), icon: const Icon(Icons.settings))
|
||||
if(editMode) IconButton(
|
||||
onPressed: settings.val().modulesSettings.toJson().toString() != DefaultSettings.get().modulesSettings.toJson().toString()
|
||||
? () => settings.val(write: true).modulesSettings = DefaultSettings.get().modulesSettings
|
||||
: null,
|
||||
icon: Icon(Icons.undo_outlined)
|
||||
),
|
||||
IconButton(onPressed: () => setState(() => editMode = !editMode), icon: Icon(Icons.edit_note_outlined), color: editMode ? Theme.of(context).primaryColor : null),
|
||||
IconButton(onPressed: editMode ? null : () => pushScreen(context, screen: const Settings(), withNavBar: false), icon: const Icon(Icons.settings)),
|
||||
],
|
||||
),
|
||||
body: ListView(
|
||||
children: [
|
||||
AppModule.getModule(Modules.marianumMessage).toListTile(context),
|
||||
AppModule.getModule(Modules.roomPlan).toListTile(context),
|
||||
AppModule.getModule(Modules.gradeAveragesCalculator).toListTile(context),
|
||||
AppModule.getModule(Modules.holidays).toListTile(context),
|
||||
body: editMode ? _sorting() : _overhang(),
|
||||
));
|
||||
|
||||
const Divider(),
|
||||
Widget _sorting() => Consumer<SettingsProvider>(builder: (context, settings, child) {
|
||||
void changeVisibility(Modules module) {
|
||||
var hidden = settings.val(write: true).modulesSettings.hiddenModules;
|
||||
hidden.contains(module) ? hidden.remove(module) : (hidden.length < 3 ? hidden.add(module) : null);
|
||||
}
|
||||
|
||||
ListTile(
|
||||
return ReorderableListView(
|
||||
header: const Center(
|
||||
heightFactor: 2,
|
||||
child: Text('Halte und ziehe einen Eintrag, um ihn zu verschieben.\nEs können 3 Bereiche ausgeblendet werden.', textAlign: TextAlign.center)
|
||||
),
|
||||
children: AppModule.modules(context, showFiltered: true)
|
||||
.map((key, value) => MapEntry(key, value.toListTile(
|
||||
context,
|
||||
key: Key(key.name),
|
||||
isReorder: true,
|
||||
onVisibleChange: () => changeVisibility(key),
|
||||
isVisible: !settings.val().modulesSettings.hiddenModules.contains(key)
|
||||
)))
|
||||
.values
|
||||
.toList(),
|
||||
onReorder: (oldIndex, newIndex) {
|
||||
if (newIndex > oldIndex) newIndex -= 1;
|
||||
|
||||
var order = settings.val().modulesSettings.moduleOrder.toList();
|
||||
final movedModule = order.removeAt(oldIndex);
|
||||
order.insert(newIndex, movedModule);
|
||||
settings.val(write: true).modulesSettings.moduleOrder = order;
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
Widget _overhang() => ListView(
|
||||
children: [
|
||||
...AppModule.getOverhangModules(context).map((e) => e.toListTile(context)),
|
||||
|
||||
const Divider(),
|
||||
|
||||
ListTile(
|
||||
leading: const Icon(Icons.share_outlined),
|
||||
title: const Text('Teile die App'),
|
||||
subtitle: const Text('Mit Freunden und deiner Klasse teilen'),
|
||||
trailing: const Icon(Icons.arrow_right),
|
||||
onTap: () => showDialog(context: context, builder: (context) => const SelectShareTypeDialog())
|
||||
),
|
||||
FutureBuilder(
|
||||
future: InAppReview.instance.isAvailable(),
|
||||
builder: (context, snapshot) {
|
||||
if(!snapshot.hasData) return const SizedBox.shrink();
|
||||
),
|
||||
FutureBuilder(
|
||||
future: InAppReview.instance.isAvailable(),
|
||||
builder: (context, snapshot) {
|
||||
if(!snapshot.hasData) return const SizedBox.shrink();
|
||||
|
||||
String? getPlatformStoreName() {
|
||||
if(Platform.isAndroid) return 'Play store';
|
||||
if(Platform.isIOS) return 'App store';
|
||||
return null;
|
||||
}
|
||||
String? getPlatformStoreName() {
|
||||
if(Platform.isAndroid) return 'Play store';
|
||||
if(Platform.isIOS) return 'App store';
|
||||
return null;
|
||||
}
|
||||
|
||||
return ListTile(
|
||||
leading: const CenteredLeading(Icon(Icons.star_rate_outlined)),
|
||||
title: const Text('App bewerten'),
|
||||
subtitle: getPlatformStoreName().wrapNullable((data) => Text('Im $data')),
|
||||
trailing: const Icon(Icons.arrow_right),
|
||||
onTap: () {
|
||||
InAppReview.instance.openStoreListing(appStoreId: '6458789560').then(
|
||||
(value) => InfoDialog.show(context, 'Vielen Dank!'),
|
||||
onError: (error) => InfoDialog.show(context, error.toString())
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
ListTile(
|
||||
leading: const CenteredLeading(Icon(Icons.feedback_outlined)),
|
||||
title: const Text('Du hast eine Idee?'),
|
||||
subtitle: const Text('Fehler und Verbessungsvorschläge'),
|
||||
trailing: const Icon(Icons.arrow_right),
|
||||
onTap: () => pushScreen(context, withNavBar: false, screen: const FeedbackDialog()),
|
||||
),
|
||||
],
|
||||
),
|
||||
return ListTile(
|
||||
leading: const CenteredLeading(Icon(Icons.star_rate_outlined)),
|
||||
title: const Text('App bewerten'),
|
||||
subtitle: getPlatformStoreName().wrapNullable((data) => Text('Im $data')),
|
||||
trailing: const Icon(Icons.arrow_right),
|
||||
onTap: () {
|
||||
InAppReview.instance.openStoreListing(appStoreId: '6458789560').then(
|
||||
(value) => InfoDialog.show(context, 'Vielen Dank!'),
|
||||
onError: (error) => InfoDialog.show(context, error.toString())
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
ListTile(
|
||||
leading: const CenteredLeading(Icon(Icons.feedback_outlined)),
|
||||
title: const Text('Du hast eine Idee?'),
|
||||
subtitle: const Text('Fehler und Verbessungsvorschläge'),
|
||||
trailing: const Icon(Icons.arrow_right),
|
||||
onTap: () => pushScreen(context, withNavBar: false, screen: const FeedbackDialog()),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
@ -3,26 +3,28 @@ import 'package:flutter/material.dart';
|
||||
import '../../../../../api/marianumcloud/talk/getParticipants/getParticipantsResponse.dart';
|
||||
import '../../../../../widget/userAvatar.dart';
|
||||
|
||||
class ParticipantsListView extends StatefulWidget {
|
||||
class ParticipantsListView extends StatelessWidget {
|
||||
final GetParticipantsResponse participantsResponse;
|
||||
const ParticipantsListView(this.participantsResponse, {super.key});
|
||||
|
||||
@override
|
||||
State<ParticipantsListView> createState() => _ParticipantsListViewState();
|
||||
}
|
||||
Widget build(BuildContext context) {
|
||||
final participants = participantsResponse.data.map((participant) => ListTile(
|
||||
leading: UserAvatar(id: participant.actorId),
|
||||
title: Text(participant.displayName),
|
||||
subtitle: participant.statusMessage != null ? Text(participant.statusMessage!) : null,
|
||||
)).toList();
|
||||
|
||||
class _ParticipantsListViewState extends State<ParticipantsListView> {
|
||||
@override
|
||||
Widget build(BuildContext context) => Scaffold(
|
||||
lastname(participant) => participant.title.toString().split(' ').last;
|
||||
participants.sort((a, b) => lastname(a).compareTo(lastname(b)));
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Teilnehmende'),
|
||||
),
|
||||
body: ListView(
|
||||
children: widget.participantsResponse.data.map((participant) => ListTile(
|
||||
leading: UserAvatar(id: participant.actorId),
|
||||
title: Text(participant.displayName),
|
||||
subtitle: participant.statusMessage != null ? Text(participant.statusMessage!) : null,
|
||||
)).toList(),
|
||||
children: participants,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -130,7 +130,7 @@ class _ChatBubbleState extends State<ChatBubble> with SingleTickerProviderStateM
|
||||
emojis.EmojiPicker(
|
||||
config: emojis.Config(
|
||||
height: 256,
|
||||
swapCategoryAndBottomBar: true,
|
||||
// swapCategoryAndBottomBar: true, // TODO this property is no longer supported, need to find an replacement
|
||||
emojiViewConfig: emojis.EmojiViewConfig(
|
||||
backgroundColor: Theme.of(context).canvasColor,
|
||||
recentsLimit: 67,
|
||||
@ -148,7 +148,7 @@ class _ChatBubbleState extends State<ChatBubble> with SingleTickerProviderStateM
|
||||
),
|
||||
searchViewConfig: emojis.SearchViewConfig(
|
||||
backgroundColor: Theme.of(context).dividerColor,
|
||||
buttonColor: Theme.of(context).dividerColor,
|
||||
// buttonColor: Theme.of(context).dividerColor, // TODO property no longer supported
|
||||
hintText: 'Suchen',
|
||||
buttonIconColor: Colors.white,
|
||||
),
|
||||
@ -282,14 +282,15 @@ class _ChatBubbleState extends State<ChatBubble> with SingleTickerProviderStateM
|
||||
if(!widget.bubbleData.isReplyable) return;
|
||||
var dx = details.delta.dx - _dragStartPosition.dx;
|
||||
setState(() {
|
||||
_position = (_position.dx + dx).abs() > 30 ? Offset(_position.dx, 0) : Offset(_position.dx + dx, 0);
|
||||
_position = (_position.dx + dx).abs() > 60 ? Offset(_position.dx, 0) : Offset(_position.dx + dx, 0);
|
||||
});
|
||||
},
|
||||
onHorizontalDragEnd: (DragEndDetails details) {
|
||||
var isAction = _position.dx.abs() > 50;
|
||||
setState(() {
|
||||
_position = const Offset(0, 0);
|
||||
});
|
||||
if(widget.bubbleData.isReplyable) {
|
||||
if(widget.bubbleData.isReplyable && isAction) {
|
||||
Provider.of<ChatProps>(context, listen: false).setReferenceMessageId(widget.bubbleData.id, context, widget.chatData.token);
|
||||
}
|
||||
},
|
||||
|
@ -36,8 +36,8 @@ class ChatMessage {
|
||||
);
|
||||
}
|
||||
|
||||
return CachedNetworkImage(
|
||||
errorWidget: (context, url, error) => Padding(padding: const EdgeInsets.only(top: 10), child: Row(
|
||||
return Padding(padding: const EdgeInsets.only(top: 5), child: CachedNetworkImage(
|
||||
errorWidget: (context, url, error) => Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
@ -46,14 +46,14 @@ class ChatMessage {
|
||||
Flexible(child: Text(file!.name, maxLines: 2, overflow: TextOverflow.ellipsis, style: const TextStyle(fontWeight: FontWeight.bold))),
|
||||
const SizedBox(width: 10),
|
||||
],
|
||||
)),
|
||||
),
|
||||
alignment: Alignment.center,
|
||||
placeholder: (context, url) => const Padding(padding: EdgeInsets.all(10), child: CircularProgressIndicator()),
|
||||
placeholder: (context, url) => const Padding(padding: EdgeInsets.all(15), child: SizedBox(width: 50, child: LinearProgressIndicator())),
|
||||
fadeInDuration: Duration.zero,
|
||||
fadeOutDuration: Duration.zero,
|
||||
errorListener: (value) {},
|
||||
imageUrl: 'https://${AccountData().buildHttpAuthString()}@${EndpointData().nextcloud().full()}/index.php/core/preview?fileId=${file!.id}&x=100&y=-1&a=1',
|
||||
);
|
||||
));
|
||||
}
|
||||
|
||||
Future<void> onOpen(LinkableElement link) async {
|
||||
|
@ -19,6 +19,7 @@ import 'customTimetableColors.dart';
|
||||
import 'customTimetableEventEditDialog.dart';
|
||||
import 'timeRegionComponent.dart';
|
||||
import 'timetableEvents.dart';
|
||||
import 'timetableNameMode.dart';
|
||||
import 'viewCustomTimetableEvents.dart';
|
||||
|
||||
class Timetable extends StatefulWidget {
|
||||
@ -72,7 +73,6 @@ class _TimetableState extends State<Timetable> {
|
||||
title = 'Kalendereintrag hinzufügen';
|
||||
icon = const Icon(Icons.add);
|
||||
case CalendarActions.viewEvents:
|
||||
default:
|
||||
title = 'Kalendereinträge anzeigen';
|
||||
icon = const Icon(Icons.perm_contact_calendar_outlined);
|
||||
}
|
||||
@ -123,6 +123,7 @@ class _TimetableState extends State<Timetable> {
|
||||
|
||||
return RefreshIndicator(
|
||||
child: SfCalendar(
|
||||
timeZone: 'W. Europe Standard Time',
|
||||
view: CalendarView.workWeek,
|
||||
dataSource: _buildTableEvents(value),
|
||||
|
||||
@ -300,11 +301,20 @@ class _TimetableState extends State<Timetable> {
|
||||
try {
|
||||
var startTime = _parseWebuntisTimestamp(element.date, element.startTime);
|
||||
var endTime = _parseWebuntisTimestamp(element.date, element.endTime);
|
||||
|
||||
var subject = subjects.result.firstWhere((subject) => subject.id == element.su[0].id);
|
||||
var subjectName = {
|
||||
TimetableNameMode.name: subject.name,
|
||||
TimetableNameMode.longName: subject.longName,
|
||||
TimetableNameMode.alternateName: subject.alternateName,
|
||||
}[settings.val().timetableSettings.timetableNameMode];
|
||||
|
||||
|
||||
return Appointment(
|
||||
id: ArbitraryAppointment(webuntis: element),
|
||||
startTime: startTime,
|
||||
endTime: endTime,
|
||||
subject: subjects.result.firstWhere((subject) => subject.id == element.su[0].id).name,
|
||||
subject: subjectName!,
|
||||
location: ''
|
||||
'${rooms.result.firstWhere((room) => room.id == element.ro[0].id).name}'
|
||||
'\n'
|
||||
@ -358,6 +368,9 @@ class _TimetableState extends State<Timetable> {
|
||||
// Any changes or no teacher at this element
|
||||
if(webuntisElement.code == 'irregular' || webuntisElement.te.first.id == 0) return const Color(0xff8F19B3).withAlpha(alpha);
|
||||
|
||||
// Teacher has changed
|
||||
if(webuntisElement.te.any((element) => element.orgname != null)) return const Color(0xFF29639B).withAlpha(alpha);
|
||||
|
||||
// Event was in the past
|
||||
if(endTime.isBefore(DateTime.now())) return Theme.of(context).primaryColor.withAlpha(alpha);
|
||||
|
||||
|
25
lib/view/pages/timetable/timetableNameMode.dart
Normal file
25
lib/view/pages/timetable/timetableNameMode.dart
Normal file
@ -0,0 +1,25 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../../widget/dropdownDisplay.dart';
|
||||
|
||||
enum TimetableNameMode {
|
||||
name,
|
||||
longName,
|
||||
alternateName
|
||||
}
|
||||
|
||||
class TimetableNameModes {
|
||||
static DropdownDisplay getDisplayOptions(TimetableNameMode theme) {
|
||||
switch(theme) {
|
||||
case TimetableNameMode.name:
|
||||
return DropdownDisplay(icon: Icons.device_unknown_outlined, displayName: 'Name');
|
||||
|
||||
case TimetableNameMode.longName:
|
||||
return DropdownDisplay(icon: Icons.perm_device_info_outlined, displayName: 'Langname');
|
||||
|
||||
case TimetableNameMode.alternateName:
|
||||
return DropdownDisplay(icon: Icons.on_device_training_outlined, displayName: 'Kurzform');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,22 +2,38 @@ import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../state/app/modules/app_modules.dart';
|
||||
import '../../storage/base/settings.dart';
|
||||
import '../../storage/devTools/devToolsSettings.dart';
|
||||
import '../../storage/file/fileSettings.dart';
|
||||
import '../../storage/fileView/fileViewSettings.dart';
|
||||
import '../../storage/general/modulesSettings.dart';
|
||||
import '../../storage/holidays/holidaysSettings.dart';
|
||||
import '../../storage/notification/notificationSettings.dart';
|
||||
import '../../storage/talk/talkSettings.dart';
|
||||
import '../../storage/timetable/timetableSettings.dart';
|
||||
import '../pages/files/files.dart';
|
||||
import '../pages/timetable/timetableNameMode.dart';
|
||||
|
||||
class DefaultSettings {
|
||||
static Settings get() => Settings(
|
||||
appTheme: ThemeMode.system,
|
||||
devToolsEnabled: false,
|
||||
modulesSettings: ModulesSettings(
|
||||
moduleOrder: [
|
||||
Modules.timetable,
|
||||
Modules.talk,
|
||||
Modules.files,
|
||||
Modules.marianumMessage,
|
||||
Modules.roomPlan,
|
||||
Modules.gradeAveragesCalculator,
|
||||
Modules.holidays
|
||||
],
|
||||
hiddenModules: [],
|
||||
),
|
||||
timetableSettings: TimetableSettings(
|
||||
connectDoubleLessons: false,
|
||||
timetableNameMode: TimetableNameMode.name
|
||||
),
|
||||
talkSettings: TalkSettings(
|
||||
sortFavoritesToTop: true,
|
||||
|
@ -10,15 +10,15 @@ import '../../widget/confirmDialog.dart';
|
||||
import '../../widget/debug/cacheView.dart';
|
||||
import '../../widget/debug/jsonViewer.dart';
|
||||
|
||||
class DevToolsSettingsDialog extends StatefulWidget {
|
||||
class DevToolsSettings extends StatefulWidget {
|
||||
final SettingsProvider settings;
|
||||
const DevToolsSettingsDialog({required this.settings, super.key});
|
||||
const DevToolsSettings({required this.settings, super.key});
|
||||
|
||||
@override
|
||||
State<DevToolsSettingsDialog> createState() => _DevToolsSettingsDialogState();
|
||||
State<DevToolsSettings> createState() => _DevToolsSettingsState();
|
||||
}
|
||||
|
||||
class _DevToolsSettingsDialogState extends State<DevToolsSettingsDialog> {
|
||||
class _DevToolsSettingsState extends State<DevToolsSettings> {
|
||||
@override
|
||||
Widget build(BuildContext context) => Column(
|
||||
children: [
|
||||
@ -59,9 +59,9 @@ class _DevToolsSettingsDialogState extends State<DevToolsSettingsDialog> {
|
||||
),
|
||||
ListTile(
|
||||
leading: const CenteredLeading(Icon(Icons.image_outlined)),
|
||||
title: const Text('Cached Thumbnails löschen'),
|
||||
subtitle: Text('etwa ${filesize(PaintingBinding.instance.imageCache.currentSizeBytes)}'),
|
||||
onTap: () {
|
||||
title: const Text('Thumb-storage'),
|
||||
subtitle: Text('etwa ${filesize(PaintingBinding.instance.imageCache.currentSizeBytes)}\nLange tippen um zu löschen'),
|
||||
onLongPress: () {
|
||||
ConfirmDialog(
|
||||
title: 'Thumbs cache löschen',
|
||||
content: 'Alle zwischengespeicherten Bilder werden gelöscht.',
|
||||
@ -69,7 +69,6 @@ class _DevToolsSettingsDialogState extends State<DevToolsSettingsDialog> {
|
||||
onConfirm: () => PaintingBinding.instance.imageCache.clear(),
|
||||
).asDialog(context);
|
||||
},
|
||||
trailing: const Icon(Icons.arrow_right),
|
||||
),
|
||||
ListTile(
|
||||
leading: const CenteredLeading(Icon(Icons.settings_applications_outlined)),
|
||||
@ -80,7 +79,7 @@ class _DevToolsSettingsDialogState extends State<DevToolsSettingsDialog> {
|
||||
},
|
||||
onLongPress: () {
|
||||
ConfirmDialog(
|
||||
title: 'App-Speicher löschen',
|
||||
title: 'Einstellungen löschen',
|
||||
content: 'Alle Einstellungen gehen verloren! Accountdaten sowie App-Daten sind nicht betroffen.',
|
||||
confirmButton: 'Unwiederruflich Löschen',
|
||||
onConfirm: () {
|
||||
@ -112,7 +111,7 @@ class _DevToolsSettingsDialogState extends State<DevToolsSettingsDialog> {
|
||||
),
|
||||
ListTile(
|
||||
leading: const CenteredLeading(Icon(Icons.data_object)),
|
||||
title: const Text('BLOC State cache'),
|
||||
title: const Text('BLOC-storage state cache'),
|
||||
subtitle: const Text('Lange tippen um zu löschen'),
|
||||
onTap: () {
|
||||
// Navigator.push(context, MaterialPageRoute(builder: (context) => const CacheView()));
|
||||
@ -125,7 +124,6 @@ class _DevToolsSettingsDialogState extends State<DevToolsSettingsDialog> {
|
||||
onConfirm: () => HydratedBloc.storage.clear(),
|
||||
).asDialog(context);
|
||||
},
|
||||
trailing: const Icon(Icons.arrow_right),
|
||||
),
|
||||
],
|
||||
);
|
@ -2,7 +2,7 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:jiffy/jiffy.dart';
|
||||
import 'package:package_info/package_info.dart';
|
||||
import 'package:package_info_plus/package_info_plus.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
@ -14,8 +14,9 @@ import '../../theming/appTheme.dart';
|
||||
import '../../widget/centeredLeading.dart';
|
||||
import '../../widget/confirmDialog.dart';
|
||||
import '../../widget/debug/cacheView.dart';
|
||||
import '../pages/timetable/timetableNameMode.dart';
|
||||
import 'defaultSettings.dart';
|
||||
import 'devToolsSettingsDialog.dart';
|
||||
import 'devToolsSettings.dart';
|
||||
import 'privacyInfo.dart';
|
||||
|
||||
class Settings extends StatefulWidget {
|
||||
@ -95,6 +96,29 @@ class _SettingsState extends State<Settings> {
|
||||
|
||||
const Divider(),
|
||||
|
||||
ListTile(
|
||||
leading: const Icon(Icons.abc_outlined),
|
||||
title: const Text('Fachbezeichnung'),
|
||||
trailing: DropdownButton<TimetableNameMode>(
|
||||
value: settings.val().timetableSettings.timetableNameMode,
|
||||
icon: Icon(Icons.arrow_drop_down),
|
||||
items: TimetableNameMode.values.map((e) => DropdownMenuItem(
|
||||
value: e,
|
||||
enabled: e != settings.val().timetableSettings.timetableNameMode,
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(TimetableNameModes.getDisplayOptions(e).icon),
|
||||
const SizedBox(width: 10),
|
||||
Text(TimetableNameModes.getDisplayOptions(e).displayName),
|
||||
],
|
||||
),
|
||||
)).toList(),
|
||||
onChanged: (value) {
|
||||
settings.val(write: true).timetableSettings.timetableNameMode = value!;
|
||||
Provider.of<TimetableProps>(context, listen: false).run(renew: false);
|
||||
},
|
||||
)
|
||||
),
|
||||
ListTile(
|
||||
leading: const Icon(Icons.calendar_view_day_outlined),
|
||||
title: const Text('Doppelstunden zusammenhängend anzeigen'),
|
||||
@ -131,34 +155,6 @@ class _SettingsState extends State<Settings> {
|
||||
),
|
||||
),
|
||||
|
||||
const Divider(),
|
||||
|
||||
ListTile(
|
||||
leading: const Icon(Icons.drive_folder_upload_outlined),
|
||||
title: const Text('Ordner in Dateien nach oben sortieren'),
|
||||
trailing: Checkbox(
|
||||
value: settings.val().fileSettings.sortFoldersToTop,
|
||||
onChanged: (e) {
|
||||
settings.val(write: true).fileSettings.sortFoldersToTop = e!;
|
||||
},
|
||||
),
|
||||
),
|
||||
|
||||
const Divider(),
|
||||
|
||||
ListTile(
|
||||
leading: const Icon(Icons.open_in_new_outlined),
|
||||
title: const Text('Dateien immer mit Systemdialog öffnen'),
|
||||
trailing: Checkbox(
|
||||
value: settings.val().fileViewSettings.alwaysOpenExternally,
|
||||
onChanged: (e) {
|
||||
settings.val(write: true).fileViewSettings.alwaysOpenExternally = e!;
|
||||
},
|
||||
),
|
||||
),
|
||||
|
||||
const Divider(),
|
||||
|
||||
ListTile(
|
||||
leading: const CenteredLeading(Icon(Icons.notifications_active_outlined)),
|
||||
title: const Text('Push-Benachrichtigungen aktivieren'),
|
||||
@ -190,6 +186,30 @@ class _SettingsState extends State<Settings> {
|
||||
|
||||
const Divider(),
|
||||
|
||||
ListTile(
|
||||
leading: const Icon(Icons.drive_folder_upload_outlined),
|
||||
title: const Text('Ordner in Dateien nach oben sortieren'),
|
||||
trailing: Checkbox(
|
||||
value: settings.val().fileSettings.sortFoldersToTop,
|
||||
onChanged: (e) {
|
||||
settings.val(write: true).fileSettings.sortFoldersToTop = e!;
|
||||
},
|
||||
),
|
||||
),
|
||||
|
||||
ListTile(
|
||||
leading: const Icon(Icons.open_in_new_outlined),
|
||||
title: const Text('Dateien immer mit Systemdialog öffnen'),
|
||||
trailing: Checkbox(
|
||||
value: settings.val().fileViewSettings.alwaysOpenExternally,
|
||||
onChanged: (e) {
|
||||
settings.val(write: true).fileViewSettings.alwaysOpenExternally = e!;
|
||||
},
|
||||
),
|
||||
),
|
||||
|
||||
const Divider(),
|
||||
|
||||
ListTile(
|
||||
leading: const Icon(Icons.live_help_outlined),
|
||||
title: const Text('Informationen und Lizenzen'),
|
||||
@ -245,14 +265,11 @@ class _SettingsState extends State<Settings> {
|
||||
|
||||
const Divider(),
|
||||
|
||||
Visibility(
|
||||
visible: !kReleaseMode,
|
||||
child: ListTile(
|
||||
leading: const CenteredLeading(Icon(Icons.code)),
|
||||
title: const Text('Quellcode MarianumMobile/Client'),
|
||||
subtitle: const Text('GNU GPL v3'),
|
||||
onTap: () => ConfirmDialog.openBrowser(context, 'https://mhsl.eu/gitea/MarianumMobile/Client'),
|
||||
),
|
||||
ListTile(
|
||||
leading: const CenteredLeading(Icon(Icons.code)),
|
||||
title: const Text('Quellcode MarianumMobile/Client'),
|
||||
subtitle: const Text('GNU GPL v3'),
|
||||
onTap: () => ConfirmDialog.openBrowser(context, 'https://mhsl.eu/gitea/MarianumMobile/Client'),
|
||||
),
|
||||
|
||||
ListTile(
|
||||
@ -287,7 +304,7 @@ class _SettingsState extends State<Settings> {
|
||||
|
||||
Visibility(
|
||||
visible: settings.val().devToolsEnabled,
|
||||
child: DevToolsSettingsDialog(settings: settings),
|
||||
child: DevToolsSettings(settings: settings),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
@ -3,7 +3,6 @@ import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'package:filesize/filesize.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:jiffy/jiffy.dart';
|
||||
import 'package:localstore/localstore.dart';
|
||||
|
||||
@ -41,7 +40,7 @@ class _CacheViewState extends State<CacheView> {
|
||||
@override
|
||||
Widget build(BuildContext context) => Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Lokaler cache'),
|
||||
title: const Text('Cache storage'),
|
||||
),
|
||||
body: FutureBuilder(
|
||||
future: files,
|
||||
@ -58,27 +57,7 @@ class _CacheViewState extends State<CacheView> {
|
||||
title: Text(filename),
|
||||
subtitle: Text("${filesize(jsonEncode(element).length * 8)}, ${Jiffy.parseFromMillisecondsSinceEpoch(element['lastupdate']).fromNow()}"),
|
||||
trailing: const Icon(Icons.arrow_right),
|
||||
onTap: () {
|
||||
Navigator.push(context, MaterialPageRoute(builder: (context) => JsonViewer(title: filename, data: jsonDecode(element['json'])),));
|
||||
},
|
||||
onLongPress: () {
|
||||
showDialog(context: context, builder: (context) => SimpleDialog(
|
||||
children: [
|
||||
const ListTile(
|
||||
leading: Icon(Icons.delete_forever),
|
||||
title: Text('Diese Datei löschen'),
|
||||
),
|
||||
ListTile(
|
||||
leading: const Icon(Icons.copy),
|
||||
title: const Text('Dateitext kopieren'),
|
||||
onTap: () {
|
||||
Clipboard.setData(ClipboardData(text: jsonEncode(element)));
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
)
|
||||
],
|
||||
));
|
||||
},
|
||||
onTap: () => JsonViewer.asDialog(context, jsonDecode(element['json'])),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
8
lib/widget/dropdownDisplay.dart
Normal file
8
lib/widget/dropdownDisplay.dart
Normal file
@ -0,0 +1,8 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class DropdownDisplay {
|
||||
final IconData icon;
|
||||
final String displayName;
|
||||
|
||||
DropdownDisplay({required this.icon, required this.displayName});
|
||||
}
|
@ -7,29 +7,26 @@ class PlaceholderView extends StatelessWidget {
|
||||
const PlaceholderView({super.key, required this.icon, required this.text, this.button});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => DefaultTextStyle(
|
||||
style: const TextStyle(),
|
||||
child: Center(
|
||||
child: Container(
|
||||
margin: const EdgeInsets.only(top: 100, left: 20, right: 20),
|
||||
child: Column(
|
||||
children: [
|
||||
Container(
|
||||
margin: const EdgeInsets.all(30),
|
||||
child: Icon(icon, color: Colors.grey, size: 60),
|
||||
),
|
||||
Text(text,
|
||||
style: const TextStyle(
|
||||
fontSize: 20,
|
||||
color: Colors.grey,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const SizedBox(height: 30),
|
||||
if(button != null) button!,
|
||||
],
|
||||
),
|
||||
Widget build(BuildContext context) => Scaffold(
|
||||
body: Center(
|
||||
child: Container(
|
||||
margin: const EdgeInsets.only(top: 100, left: 20, right: 20),
|
||||
child: Column(
|
||||
children: [
|
||||
Container(
|
||||
margin: const EdgeInsets.all(30),
|
||||
child: Icon(icon, size: 60),
|
||||
),
|
||||
Text(
|
||||
text,
|
||||
style: const TextStyle(fontSize: 20,),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const SizedBox(height: 30),
|
||||
if(button != null) button!,
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
),
|
||||
);
|
||||
}
|
||||
|
126
pubspec.yaml
126
pubspec.yaml
@ -17,7 +17,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
|
||||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
||||
# In Windows, build-name is used as the major, minor, and patch parts
|
||||
# of the product and file versions while build-number is used as the build suffix.
|
||||
version: 0.0.9+35
|
||||
version: 0.1.3+41
|
||||
|
||||
environment:
|
||||
sdk: '>3.0.0'
|
||||
@ -30,7 +30,7 @@ environment:
|
||||
# versions available, run `flutter pub outdated`.
|
||||
|
||||
dependency_overrides:
|
||||
#xml: ^6.2.2
|
||||
intl: any
|
||||
|
||||
dependencies:
|
||||
flutter:
|
||||
@ -39,86 +39,82 @@ dependencies:
|
||||
flutter_localizations:
|
||||
sdk: flutter
|
||||
|
||||
# The following adds the Cupertino Icons font to your application.
|
||||
# Use with the CupertinoIcons class for iOS style icons.
|
||||
cupertino_icons: ^1.0.2
|
||||
flutter_native_splash: ^2.2.14
|
||||
flutter_login: ^5.0.0
|
||||
animated_digit: ^3.2.3
|
||||
async: ^2.11.0
|
||||
badges: ^3.1.2
|
||||
better_open_file: ^3.6.5
|
||||
bloc: ^9.0.0
|
||||
bottom_sheet: ^4.0.4
|
||||
bubble: ^1.2.1
|
||||
http: ^1.1.0
|
||||
shared_preferences: ^2.0.15
|
||||
provider: ^6.0.4
|
||||
jiffy: ^6.1.0
|
||||
json_annotation: ^4.8.1
|
||||
localstore: ^1.2.3
|
||||
intl: ^0.18.0
|
||||
nextcloud:
|
||||
git:
|
||||
url: https://github.com/provokateurin/nextcloud-neon
|
||||
path: packages/nextcloud
|
||||
ref: 3683491a94670393e46cbc83ad85b994f7df7481
|
||||
flutter_launcher_icons: ^0.13.1
|
||||
pretty_json: ^2.0.0
|
||||
cached_network_image: ^3.2.3
|
||||
url_launcher: ^6.1.10
|
||||
flutter_linkify: ^6.0.0
|
||||
cached_network_image: ^3.4.1
|
||||
connectivity_plus: ^6.1.2
|
||||
crypto: ^3.0.6
|
||||
cupertino_icons: ^1.0.8
|
||||
device_info_plus: ^11.2.1
|
||||
dio: ^4.0.6
|
||||
easy_debounce: ^2.0.3
|
||||
emoji_picker_flutter: ^4.3.0
|
||||
fast_rsa: ^3.7.1
|
||||
file_picker: ^8.1.7
|
||||
filesize: ^2.0.1
|
||||
path_provider: ^2.0.13
|
||||
better_open_file: ^3.6.4
|
||||
firebase_core: ^3.10.1
|
||||
firebase_in_app_messaging: ^0.8.1+1
|
||||
firebase_messaging: ^15.2.1
|
||||
flowder:
|
||||
git:
|
||||
url: https://github.com/Harsh223/flowder.git
|
||||
persistent_bottom_nav_bar_v2: ^5.0.0
|
||||
badges: ^3.0.2
|
||||
image_picker: ^1.0.0
|
||||
file_picker: ^8.0.0+1
|
||||
loader_overlay: ^4.0.0
|
||||
crypto: ^3.0.3
|
||||
package_info: ^2.0.2
|
||||
syncfusion_flutter_calendar: ^24.1.44
|
||||
async: ^2.11.0
|
||||
animated_digit: ^3.2.1
|
||||
syncfusion_flutter_pdfviewer: ^24.1.44
|
||||
photo_view: ^0.14.0
|
||||
uuid: ^4.2.2
|
||||
firebase_messaging: ^14.6.5
|
||||
firebase_core: ^2.15.0
|
||||
firebase_in_app_messaging: ^0.7.3+4
|
||||
flutter_local_notifications: ^17.0.0
|
||||
fast_rsa: ^3.6.1
|
||||
share_plus: ^8.0.2
|
||||
flutter_app_badge: ^2.0.2
|
||||
flutter_bloc: ^9.0.0
|
||||
flutter_launcher_icons: ^0.14.3
|
||||
flutter_linkify: ^6.0.0
|
||||
flutter_local_notifications: ^18.0.1
|
||||
flutter_login: ^5.0.0
|
||||
flutter_native_splash: ^2.4.4
|
||||
flutter_split_view: ^0.1.2
|
||||
bottom_sheet: ^4.0.0
|
||||
device_info_plus: ^9.0.3
|
||||
flutter_app_badger: ^1.5.0
|
||||
freezed_annotation: ^2.4.4
|
||||
http: ^1.3.0
|
||||
hydrated_bloc: ^10.0.0
|
||||
image_picker: ^1.1.2
|
||||
in_app_review: ^2.0.10
|
||||
jiffy: ^6.2.1
|
||||
json_annotation: ^4.9.0
|
||||
loader_overlay: ^5.0.0
|
||||
localstore: ^1.4.0
|
||||
nextcloud:
|
||||
git:
|
||||
path: packages/nextcloud
|
||||
ref: 3683491a94670393e46cbc83ad85b994f7df7481
|
||||
url: https://github.com/provokateurin/nextcloud-neon
|
||||
package_info_plus: ^8.1.3
|
||||
path_provider: ^2.1.5
|
||||
persistent_bottom_nav_bar_v2: ^5.3.1
|
||||
photo_view: ^0.15.0
|
||||
pretty_json: ^2.0.0
|
||||
provider: ^6.1.2
|
||||
qr_flutter: ^4.1.0
|
||||
easy_debounce: ^2.0.3
|
||||
rrule_generator: ^0.7.0+1
|
||||
rrule: ^0.2.16
|
||||
time_range_picker: ^2.2.0
|
||||
in_app_review: ^2.0.8
|
||||
emoji_picker_flutter: ^2.1.1
|
||||
bloc: ^8.1.4
|
||||
flutter_bloc: ^8.1.5
|
||||
freezed_annotation: ^2.4.1
|
||||
connectivity_plus: ^6.0.3
|
||||
hydrated_bloc: ^9.1.5
|
||||
dio: ^4.0.6
|
||||
sorted: ^2.1.0
|
||||
rrule: ^0.2.17
|
||||
rrule_generator: ^0.9.0
|
||||
share_plus: ^10.1.4
|
||||
shared_preferences: ^2.3.5
|
||||
syncfusion_flutter_calendar: ^28.1.41
|
||||
syncfusion_flutter_pdfviewer: ^28.1.41
|
||||
time_range_picker: ^2.3.0
|
||||
url_launcher: ^6.3.1
|
||||
uuid: ^4.5.1
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
json_serializable: ^6.7.1
|
||||
build_runner: ^2.4.9
|
||||
json_serializable: any
|
||||
build_runner: any
|
||||
|
||||
# The "flutter_lints" package below contains a set of recommended lints to
|
||||
# encourage good coding practices. The lint set provided by the package is
|
||||
# activated in the `analysis_options.yaml` file located at the root of your
|
||||
# package. See that file for information about deactivating specific lint
|
||||
# rules and activating additional ones.
|
||||
flutter_lints: ^3.0.1
|
||||
freezed: ^2.5.2
|
||||
flutter_lints: any
|
||||
freezed: any
|
||||
|
||||
# For information on the generic Dart part of this file, see the
|
||||
# following page: https://dart.dev/tools/pub/pubspec
|
||||
|
Reference in New Issue
Block a user