Compare commits
26 Commits
65b29ec4b8
...
develop-gr
Author | SHA1 | Date | |
---|---|---|---|
421ee9179d | |||
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 |
@ -28,6 +28,7 @@ android {
|
|||||||
ndkVersion "27.0.12077973"
|
ndkVersion "27.0.12077973"
|
||||||
|
|
||||||
compileOptions {
|
compileOptions {
|
||||||
|
coreLibraryDesugaringEnabled true
|
||||||
sourceCompatibility JavaVersion.VERSION_1_8
|
sourceCompatibility JavaVersion.VERSION_1_8
|
||||||
targetCompatibility JavaVersion.VERSION_1_8
|
targetCompatibility JavaVersion.VERSION_1_8
|
||||||
}
|
}
|
||||||
@ -64,5 +65,5 @@ flutter {
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation 'com.android.support:multidex:2.0.1'
|
implementation 'com.android.support:multidex:2.0.1'
|
||||||
implementation 'com.android.tools:desugar_jdk_libs:2.0.3'
|
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.3'
|
||||||
}
|
}
|
||||||
|
0
android/app/proguard-rules.pro
vendored
Normal file
0
android/app/proguard-rules.pro
vendored
Normal file
@ -1,6 +1,6 @@
|
|||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
|
||||||
networkTimeout=10000
|
networkTimeout=10000
|
||||||
validateDistributionUrl=true
|
validateDistributionUrl=true
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
@ -19,7 +19,7 @@ pluginManagement {
|
|||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
|
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
|
||||||
id "com.android.application" version '8.1.4' apply false
|
id "com.android.application" version '8.7.3' apply false
|
||||||
id "org.jetbrains.kotlin.android" version "1.8.10" apply false
|
id "org.jetbrains.kotlin.android" version "1.8.10" apply false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 108 KiB After Width: | Height: | Size: 101 KiB |
@ -5,5 +5,5 @@ abstract class ApiResponse {
|
|||||||
late http.Response rawResponse;
|
late http.Response rawResponse;
|
||||||
|
|
||||||
@JsonKey(includeIfNull: false)
|
@JsonKey(includeIfNull: false)
|
||||||
late Map<String, String>? headers;
|
Map<String, String>? headers;
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,7 @@ class AutocompleteResponseObject {
|
|||||||
String label;
|
String label;
|
||||||
String? icon;
|
String? icon;
|
||||||
String? source;
|
String? source;
|
||||||
List<String>? status;
|
String? status;
|
||||||
String? subline;
|
String? subline;
|
||||||
String? shareWithDisplayNameUniqe;
|
String? shareWithDisplayNameUniqe;
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ AutocompleteResponseObject _$AutocompleteResponseObjectFromJson(
|
|||||||
json['label'] as String,
|
json['label'] as String,
|
||||||
json['icon'] as String?,
|
json['icon'] as String?,
|
||||||
json['source'] 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['subline'] as String?,
|
||||||
json['shareWithDisplayNameUniqe'] as String?,
|
json['shareWithDisplayNameUniqe'] as String?,
|
||||||
);
|
);
|
||||||
|
@ -55,12 +55,15 @@ class GetParticipantsResponseObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
enum GetParticipantsResponseObjectParticipantType {
|
enum GetParticipantsResponseObjectParticipantType {
|
||||||
@JsonValue(1) owner,
|
@JsonValue(1) owner('Besitzer'),
|
||||||
@JsonValue(2) moderator,
|
@JsonValue(2) moderator('Moderator'),
|
||||||
@JsonValue(3) user,
|
@JsonValue(3) user('Benutzer'),
|
||||||
@JsonValue(4) guest,
|
@JsonValue(4) guest('Gast'),
|
||||||
@JsonValue(5) userFollowingPublicLink,
|
@JsonValue(5) userFollowingPublicLink('Link Nutzer'),
|
||||||
@JsonValue(6) guestWithModeratorPermissions
|
@JsonValue(6) guestWithModeratorPermissions('Gast Moderator');
|
||||||
|
|
||||||
|
const GetParticipantsResponseObjectParticipantType(this.prettyName);
|
||||||
|
final String prettyName;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum GetParticipantsResponseObjectParticipantsInCallFlags {
|
enum GetParticipantsResponseObjectParticipantsInCallFlags {
|
||||||
|
@ -110,6 +110,7 @@ enum GetRoomResponseObjectConversationType {
|
|||||||
@JsonValue(3) public,
|
@JsonValue(3) public,
|
||||||
@JsonValue(4) changelog,
|
@JsonValue(4) changelog,
|
||||||
@JsonValue(5) deleted,
|
@JsonValue(5) deleted,
|
||||||
|
@JsonValue(6) noteToSelf,
|
||||||
}
|
}
|
||||||
|
|
||||||
enum GetRoomResponseObjectParticipantNotificationLevel {
|
enum GetRoomResponseObjectParticipantNotificationLevel {
|
||||||
|
@ -102,6 +102,7 @@ const _$GetRoomResponseObjectConversationTypeEnumMap = {
|
|||||||
GetRoomResponseObjectConversationType.public: 3,
|
GetRoomResponseObjectConversationType.public: 3,
|
||||||
GetRoomResponseObjectConversationType.changelog: 4,
|
GetRoomResponseObjectConversationType.changelog: 4,
|
||||||
GetRoomResponseObjectConversationType.deleted: 5,
|
GetRoomResponseObjectConversationType.deleted: 5,
|
||||||
|
GetRoomResponseObjectConversationType.noteToSelf: 6,
|
||||||
};
|
};
|
||||||
|
|
||||||
const _$GetRoomResponseObjectParticipantNotificationLevelEnumMap = {
|
const _$GetRoomResponseObjectParticipantNotificationLevelEnumMap = {
|
||||||
|
@ -58,11 +58,9 @@ abstract class TalkApi<T extends ApiResponse?> extends ApiRequest {
|
|||||||
assembled?.headers = data.headers;
|
assembled?.headers = data.headers;
|
||||||
return assembled;
|
return assembled;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// TODO report error
|
var message = 'Error assembling Talk API ${T.toString()} message: ${e.toString()} response with request body: $body and request headers: ${headers.toString()}';
|
||||||
log('Error assembling Talk API ${T.toString()} message: ${e.toString()} response on ${endpoint.path} with request body: $body and request headers: ${headers.toString()}');
|
log(message);
|
||||||
|
throw Exception(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw Exception('Error assembling Talk API response');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,6 @@ import 'dart:convert';
|
|||||||
import 'package:localstore/localstore.dart';
|
import 'package:localstore/localstore.dart';
|
||||||
|
|
||||||
import 'apiResponse.dart';
|
import 'apiResponse.dart';
|
||||||
import 'webuntis/webuntisError.dart';
|
|
||||||
|
|
||||||
abstract class RequestCache<T extends ApiResponse?> {
|
abstract class RequestCache<T extends ApiResponse?> {
|
||||||
static const int cacheNothing = 0;
|
static const int cacheNothing = 0;
|
||||||
@ -40,7 +39,7 @@ abstract class RequestCache<T extends ApiResponse?> {
|
|||||||
'json': jsonEncode(newValue),
|
'json': jsonEncode(newValue),
|
||||||
'lastupdate': DateTime.now().millisecondsSinceEpoch
|
'lastupdate': DateTime.now().millisecondsSinceEpoch
|
||||||
});
|
});
|
||||||
} on WebuntisError catch(e) {
|
} on Exception catch(e) {
|
||||||
onError(e);
|
onError(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
29
lib/app.dart
29
lib/app.dart
@ -8,7 +8,6 @@ import 'package:flutter/material.dart';
|
|||||||
import 'state/app/modules/app_modules.dart';
|
import 'state/app/modules/app_modules.dart';
|
||||||
import 'package:persistent_bottom_nav_bar_v2/persistent_bottom_nav_bar_v2.dart';
|
import 'package:persistent_bottom_nav_bar_v2/persistent_bottom_nav_bar_v2.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:badges/badges.dart' as badges;
|
|
||||||
|
|
||||||
import 'api/mhsl/breaker/getBreakers/getBreakersResponse.dart';
|
import 'api/mhsl/breaker/getBreakers/getBreakersResponse.dart';
|
||||||
import 'api/mhsl/server/userIndex/update/updateUserindex.dart';
|
import 'api/mhsl/server/userIndex/update/updateUserindex.dart';
|
||||||
@ -93,7 +92,7 @@ class _AppState extends State<App> with WidgetsBindingObserver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) => PersistentTabView(
|
Widget build(BuildContext context) => Consumer<SettingsProvider>(builder: (context, settings, child) => PersistentTabView(
|
||||||
controller: Main.bottomNavigator,
|
controller: Main.bottomNavigator,
|
||||||
navBarOverlap: const NavBarOverlap.none(),
|
navBarOverlap: const NavBarOverlap.none(),
|
||||||
backgroundColor: Theme.of(context).colorScheme.primary,
|
backgroundColor: Theme.of(context).colorScheme.primary,
|
||||||
@ -101,29 +100,7 @@ class _AppState extends State<App> with WidgetsBindingObserver {
|
|||||||
|
|
||||||
screenTransitionAnimation: const ScreenTransitionAnimation(curve: Curves.easeOutQuad, duration: Duration(milliseconds: 200)),
|
screenTransitionAnimation: const ScreenTransitionAnimation(curve: Curves.easeOutQuad, duration: Duration(milliseconds: 200)),
|
||||||
tabs: [
|
tabs: [
|
||||||
AppModule.getModule(Modules.timetable).toBottomTab(context),
|
...AppModule.getBottomBarModules(context).map((e) => e.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),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
AppModule.getModule(Modules.files).toBottomTab(context),
|
|
||||||
|
|
||||||
PersistentTabConfig(
|
PersistentTabConfig(
|
||||||
screen: const Breaker(breaker: BreakerArea.more, child: Overhang()),
|
screen: const Breaker(breaker: BreakerArea.more, child: Overhang()),
|
||||||
@ -142,7 +119,7 @@ class _AppState extends State<App> with WidgetsBindingObserver {
|
|||||||
color: Theme.of(context).colorScheme.surface,
|
color: Theme.of(context).colorScheme.surface,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
));
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
|
@ -23,7 +23,11 @@ class _BreakerState extends State<Breaker> {
|
|||||||
builder: (context, value, child) {
|
builder: (context, value, child) {
|
||||||
var blocked = value.isBlocked(widget.breaker);
|
var blocked = value.isBlocked(widget.breaker);
|
||||||
if(blocked != null) {
|
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;
|
return widget.child;
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:package_info_plus/package_info_plus.dart';
|
import 'package:package_info_plus/package_info_plus.dart';
|
||||||
|
|
||||||
import '../../api/apiResponse.dart';
|
import '../../api/apiResponse.dart';
|
||||||
@ -12,6 +13,8 @@ class BreakerProps extends DataHolder {
|
|||||||
PackageInfo? packageInfo;
|
PackageInfo? packageInfo;
|
||||||
|
|
||||||
String? isBlocked(BreakerArea? type) {
|
String? isBlocked(BreakerArea? type) {
|
||||||
|
if(kDebugMode) return null;
|
||||||
|
|
||||||
if(packageInfo == null) {
|
if(packageInfo == null) {
|
||||||
PackageInfo.fromPlatform().then((value) => packageInfo = value);
|
PackageInfo.fromPlatform().then((value) => packageInfo = value);
|
||||||
return null;
|
return null;
|
||||||
|
@ -44,7 +44,7 @@ class NotificationController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static Future<void> onAppOpenedByNotification(RemoteMessage message, BuildContext context) async {
|
static Future<void> onAppOpenedByNotification(RemoteMessage message, BuildContext context) async {
|
||||||
NotificationTasks.navigateToTalk();
|
NotificationTasks.navigateToTalk(context);
|
||||||
NotificationTasks.updateProviders(context);
|
NotificationTasks.updateProviders(context);
|
||||||
|
|
||||||
DebugTile(context).run(() {
|
DebugTile(context).run(() {
|
||||||
|
@ -6,6 +6,7 @@ import 'package:provider/provider.dart';
|
|||||||
import '../main.dart';
|
import '../main.dart';
|
||||||
import '../model/chatList/chatListProps.dart';
|
import '../model/chatList/chatListProps.dart';
|
||||||
import '../model/chatList/chatProps.dart';
|
import '../model/chatList/chatProps.dart';
|
||||||
|
import '../state/app/modules/app_modules.dart';
|
||||||
|
|
||||||
class NotificationTasks {
|
class NotificationTasks {
|
||||||
static void updateBadgeCount(RemoteMessage notification) {
|
static void updateBadgeCount(RemoteMessage notification) {
|
||||||
@ -17,7 +18,9 @@ class NotificationTasks {
|
|||||||
Provider.of<ChatProps>(context, listen: false).run();
|
Provider.of<ChatProps>(context, listen: false).run();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void navigateToTalk() {
|
static void navigateToTalk(BuildContext context) {
|
||||||
Main.bottomNavigator.jumpToTab(1);
|
var talkTab = AppModule.getBottomBarModules(context).map((e) => e.module).toList().indexOf(Modules.talk);
|
||||||
|
if(talkTab == -1) return;
|
||||||
|
Main.bottomNavigator.jumpToTab(talkTab);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,11 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:persistent_bottom_nav_bar_v2/persistent_bottom_nav_bar_v2.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 '../../../api/mhsl/breaker/getBreakers/getBreakersResponse.dart';
|
||||||
import '../../../model/breakers/Breaker.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/files/files.dart';
|
||||||
import '../../../view/pages/more/roomplan/roomplan.dart';
|
import '../../../view/pages/more/roomplan/roomplan.dart';
|
||||||
import '../../../view/pages/talk/chatList.dart';
|
import '../../../view/pages/talk/chatList.dart';
|
||||||
@ -12,38 +15,115 @@ import 'gradeAverages/view/grade_averages_view.dart';
|
|||||||
import 'holidays/view/holidays_view.dart';
|
import 'holidays/view/holidays_view.dart';
|
||||||
import 'marianumMessage/view/marianum_message_list_view.dart';
|
import 'marianumMessage/view/marianum_message_list_view.dart';
|
||||||
|
|
||||||
|
import 'package:badges/badges.dart' as badges;
|
||||||
|
|
||||||
class AppModule {
|
class AppModule {
|
||||||
|
Modules module;
|
||||||
String name;
|
String name;
|
||||||
IconData icon;
|
Widget Function() icon;
|
||||||
|
BreakerArea breakerArea;
|
||||||
Widget Function() create;
|
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() => {
|
static Map<Modules, AppModule> modules(BuildContext context, { showFiltered = false }) {
|
||||||
Modules.timetable: AppModule('Vertretung', Icons.calendar_month, Timetable.new),
|
var settings = Provider.of<SettingsProvider>(context, listen: false);
|
||||||
Modules.talk: AppModule('Talk', Icons.chat, ChatList.new),
|
var available = {
|
||||||
Modules.files: AppModule('Files', Icons.folder, Files.new),
|
Modules.timetable: AppModule(
|
||||||
Modules.marianumMessage: AppModule('Marianum Message', Icons.newspaper, MarianumMessageListView.new),
|
Modules.timetable,
|
||||||
Modules.roomPlan: AppModule('Raumplan', Icons.location_pin, Roomplan.new),
|
name: 'Vertretung',
|
||||||
Modules.gradeAveragesCalculator: AppModule('Notendurschnittsrechner', Icons.calculate, GradeAveragesView.new),
|
icon: () => Icon(Icons.calendar_month),
|
||||||
Modules.holidays: AppModule('Schulferien', Icons.flight, HolidaysView.new),
|
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(
|
return { for (var element in settings.val().modulesSettings.moduleOrder.where((element) => available.containsKey(element))) element : available[element]! };
|
||||||
leading: CenteredLeading(Icon(icon)),
|
}
|
||||||
|
|
||||||
|
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),
|
title: Text(name),
|
||||||
onTap: () => pushScreen(context, withNavBar: false, screen: create()),
|
onTap: isReorder ? null : () => pushScreen(context, withNavBar: false, screen: create()),
|
||||||
trailing: const Icon(Icons.arrow_right),
|
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(
|
PersistentTabConfig toBottomTab(BuildContext context, {Widget Function(IconData icon)? iconBuilder}) => PersistentTabConfig(
|
||||||
screen: Breaker(breaker: BreakerArea.global, child: create()),
|
screen: Breaker(breaker: breakerArea, child: create()),
|
||||||
item: ItemConfig(
|
item: ItemConfig(
|
||||||
activeForegroundColor: Theme.of(context).primaryColor,
|
activeForegroundColor: Theme.of(context).primaryColor,
|
||||||
inactiveForegroundColor: Theme.of(context).colorScheme.secondary,
|
inactiveForegroundColor: Theme.of(context).colorScheme.secondary,
|
||||||
icon: itemBuilder == null ? Icon(icon) : itemBuilder(icon),
|
icon: icon(),
|
||||||
title: name
|
title: name
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -4,6 +4,7 @@ import 'package:json_annotation/json_annotation.dart';
|
|||||||
import '../devTools/devToolsSettings.dart';
|
import '../devTools/devToolsSettings.dart';
|
||||||
import '../file/fileSettings.dart';
|
import '../file/fileSettings.dart';
|
||||||
import '../fileView/fileViewSettings.dart';
|
import '../fileView/fileViewSettings.dart';
|
||||||
|
import '../general/modulesSettings.dart';
|
||||||
import '../holidays/holidaysSettings.dart';
|
import '../holidays/holidaysSettings.dart';
|
||||||
import '../notification/notificationSettings.dart';
|
import '../notification/notificationSettings.dart';
|
||||||
import '../talk/talkSettings.dart';
|
import '../talk/talkSettings.dart';
|
||||||
@ -20,6 +21,7 @@ class Settings {
|
|||||||
ThemeMode appTheme;
|
ThemeMode appTheme;
|
||||||
bool devToolsEnabled;
|
bool devToolsEnabled;
|
||||||
|
|
||||||
|
ModulesSettings modulesSettings;
|
||||||
TimetableSettings timetableSettings;
|
TimetableSettings timetableSettings;
|
||||||
TalkSettings talkSettings;
|
TalkSettings talkSettings;
|
||||||
FileSettings fileSettings;
|
FileSettings fileSettings;
|
||||||
@ -31,6 +33,7 @@ class Settings {
|
|||||||
Settings({
|
Settings({
|
||||||
required this.appTheme,
|
required this.appTheme,
|
||||||
required this.devToolsEnabled,
|
required this.devToolsEnabled,
|
||||||
|
required this.modulesSettings,
|
||||||
required this.timetableSettings,
|
required this.timetableSettings,
|
||||||
required this.talkSettings,
|
required this.talkSettings,
|
||||||
required this.fileSettings,
|
required this.fileSettings,
|
||||||
|
@ -9,6 +9,8 @@ part of 'settings.dart';
|
|||||||
Settings _$SettingsFromJson(Map<String, dynamic> json) => Settings(
|
Settings _$SettingsFromJson(Map<String, dynamic> json) => Settings(
|
||||||
appTheme: Settings._themeFromJson(json['appTheme'] as String),
|
appTheme: Settings._themeFromJson(json['appTheme'] as String),
|
||||||
devToolsEnabled: json['devToolsEnabled'] as bool,
|
devToolsEnabled: json['devToolsEnabled'] as bool,
|
||||||
|
modulesSettings: ModulesSettings.fromJson(
|
||||||
|
json['modulesSettings'] as Map<String, dynamic>),
|
||||||
timetableSettings: TimetableSettings.fromJson(
|
timetableSettings: TimetableSettings.fromJson(
|
||||||
json['timetableSettings'] as Map<String, dynamic>),
|
json['timetableSettings'] as Map<String, dynamic>),
|
||||||
talkSettings:
|
talkSettings:
|
||||||
@ -28,6 +30,7 @@ Settings _$SettingsFromJson(Map<String, dynamic> json) => Settings(
|
|||||||
Map<String, dynamic> _$SettingsToJson(Settings instance) => <String, dynamic>{
|
Map<String, dynamic> _$SettingsToJson(Settings instance) => <String, dynamic>{
|
||||||
'appTheme': Settings._themeToJson(instance.appTheme),
|
'appTheme': Settings._themeToJson(instance.appTheme),
|
||||||
'devToolsEnabled': instance.devToolsEnabled,
|
'devToolsEnabled': instance.devToolsEnabled,
|
||||||
|
'modulesSettings': instance.modulesSettings.toJson(),
|
||||||
'timetableSettings': instance.timetableSettings.toJson(),
|
'timetableSettings': instance.timetableSettings.toJson(),
|
||||||
'talkSettings': instance.talkSettings.toJson(),
|
'talkSettings': instance.talkSettings.toJson(),
|
||||||
'fileSettings': instance.fileSettings.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',
|
||||||
|
};
|
@ -70,7 +70,7 @@ class _FeedbackDialogState extends State<FeedbackDialog> {
|
|||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
border: const OutlineInputBorder(),
|
border: const OutlineInputBorder(),
|
||||||
label: const Text('Feedback und Verbesserungen'),
|
label: const Text('Feedback und Verbesserungen'),
|
||||||
errorText: _textFieldEmpty ? 'Bitte gib eine Beschreibung an???' : null,
|
errorText: _textFieldEmpty ? 'Bitte gib eine Beschreibung an!' : null,
|
||||||
),
|
),
|
||||||
minLines: 4,
|
minLines: 4,
|
||||||
maxLines: 7,
|
maxLines: 7,
|
||||||
|
@ -3,33 +3,82 @@ import 'dart:io';
|
|||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:in_app_review/in_app_review.dart';
|
import 'package:in_app_review/in_app_review.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
import '../../extensions/renderNotNull.dart';
|
import '../../extensions/renderNotNull.dart';
|
||||||
import 'package:persistent_bottom_nav_bar_v2/persistent_bottom_nav_bar_v2.dart';
|
import 'package:persistent_bottom_nav_bar_v2/persistent_bottom_nav_bar_v2.dart';
|
||||||
|
|
||||||
import '../../state/app/modules/app_modules.dart';
|
import '../../state/app/modules/app_modules.dart';
|
||||||
|
import '../../storage/base/settingsProvider.dart';
|
||||||
import '../../widget/centeredLeading.dart';
|
import '../../widget/centeredLeading.dart';
|
||||||
import '../../widget/infoDialog.dart';
|
import '../../widget/infoDialog.dart';
|
||||||
|
import '../settings/defaultSettings.dart';
|
||||||
import '../settings/settings.dart';
|
import '../settings/settings.dart';
|
||||||
import 'more/feedback/feedbackDialog.dart';
|
import 'more/feedback/feedbackDialog.dart';
|
||||||
import 'more/share/selectShareTypeDialog.dart';
|
import 'more/share/selectShareTypeDialog.dart';
|
||||||
|
|
||||||
class Overhang extends StatelessWidget {
|
class Overhang extends StatefulWidget {
|
||||||
const Overhang({super.key});
|
const Overhang({super.key});
|
||||||
|
|
||||||
@override
|
@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(
|
appBar: AppBar(
|
||||||
title: const Text('Mehr'),
|
title: const Text('Mehr'),
|
||||||
actions: [
|
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(
|
body: editMode ? _sorting() : _overhang(),
|
||||||
|
));
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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: [
|
children: [
|
||||||
AppModule.getModule(Modules.marianumMessage).toListTile(context),
|
...AppModule.getOverhangModules(context).map((e) => e.toListTile(context)),
|
||||||
AppModule.getModule(Modules.roomPlan).toListTile(context),
|
|
||||||
AppModule.getModule(Modules.gradeAveragesCalculator).toListTile(context),
|
|
||||||
AppModule.getModule(Modules.holidays).toListTile(context),
|
|
||||||
|
|
||||||
const Divider(),
|
const Divider(),
|
||||||
|
|
||||||
@ -73,6 +122,5 @@ class Overhang extends StatelessWidget {
|
|||||||
onTap: () => pushScreen(context, withNavBar: false, screen: const FeedbackDialog()),
|
onTap: () => pushScreen(context, withNavBar: false, screen: const FeedbackDialog()),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,28 +1,52 @@
|
|||||||
|
import 'package:collection/collection.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import '../../../../../api/marianumcloud/talk/getParticipants/getParticipantsResponse.dart';
|
import '../../../../../api/marianumcloud/talk/getParticipants/getParticipantsResponse.dart';
|
||||||
import '../../../../../widget/userAvatar.dart';
|
import '../../../../../widget/userAvatar.dart';
|
||||||
|
|
||||||
class ParticipantsListView extends StatefulWidget {
|
class ParticipantsListView extends StatelessWidget {
|
||||||
final GetParticipantsResponse participantsResponse;
|
final GetParticipantsResponse participantsResponse;
|
||||||
const ParticipantsListView(this.participantsResponse, {super.key});
|
const ParticipantsListView(this.participantsResponse, {super.key});
|
||||||
|
|
||||||
@override
|
@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> {
|
lastname(participant) => participant.displayName.toString().split(' ').last;
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) => Scaffold(
|
final participants = participantsResponse.data
|
||||||
|
.sorted((a, b) => lastname(a).compareTo(lastname(b)))
|
||||||
|
.sorted((a, b) => a.participantType.index.compareTo(b.participantType.index));
|
||||||
|
var groupedParticipants = participants.groupListsBy((participant) => participant.participantType);
|
||||||
|
|
||||||
|
// Map<GetParticipantsResponseObjectParticipantType, List<GetParticipantsResponseObject>>
|
||||||
|
|
||||||
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: const Text('Teilnehmende'),
|
title: const Text('Teilnehmende'),
|
||||||
),
|
),
|
||||||
body: ListView(
|
body: ListView(
|
||||||
children: widget.participantsResponse.data.map((participant) => ListTile(
|
children: [
|
||||||
|
...groupedParticipants.entries.map((entry) => Column(
|
||||||
|
children: [
|
||||||
|
ListTile(
|
||||||
|
title: Text(entry.key.prettyName),
|
||||||
|
titleTextStyle: TextStyle(fontWeight: FontWeight.bold),
|
||||||
|
),
|
||||||
|
...entry.value.map((participant) => ListTile(
|
||||||
leading: UserAvatar(id: participant.actorId),
|
leading: UserAvatar(id: participant.actorId),
|
||||||
title: Text(participant.displayName),
|
title: Text(participant.displayName),
|
||||||
subtitle: participant.statusMessage != null ? Text(participant.statusMessage!) : null,
|
subtitle: participant.statusMessage != null ? Text(participant.statusMessage!) : null,
|
||||||
)).toList(),
|
)),
|
||||||
),
|
Divider(),
|
||||||
|
],
|
||||||
|
))
|
||||||
|
],
|
||||||
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
@ -282,14 +282,15 @@ class _ChatBubbleState extends State<ChatBubble> with SingleTickerProviderStateM
|
|||||||
if(!widget.bubbleData.isReplyable) return;
|
if(!widget.bubbleData.isReplyable) return;
|
||||||
var dx = details.delta.dx - _dragStartPosition.dx;
|
var dx = details.delta.dx - _dragStartPosition.dx;
|
||||||
setState(() {
|
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) {
|
onHorizontalDragEnd: (DragEndDetails details) {
|
||||||
|
var isAction = _position.dx.abs() > 50;
|
||||||
setState(() {
|
setState(() {
|
||||||
_position = const Offset(0, 0);
|
_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);
|
Provider.of<ChatProps>(context, listen: false).setReferenceMessageId(widget.bubbleData.id, context, widget.chatData.token);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -36,8 +36,8 @@ class ChatMessage {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return CachedNetworkImage(
|
return Padding(padding: const EdgeInsets.only(top: 5), child: CachedNetworkImage(
|
||||||
errorWidget: (context, url, error) => Padding(padding: const EdgeInsets.only(top: 10), child: Row(
|
errorWidget: (context, url, error) => Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
@ -46,14 +46,14 @@ class ChatMessage {
|
|||||||
Flexible(child: Text(file!.name, maxLines: 2, overflow: TextOverflow.ellipsis, style: const TextStyle(fontWeight: FontWeight.bold))),
|
Flexible(child: Text(file!.name, maxLines: 2, overflow: TextOverflow.ellipsis, style: const TextStyle(fontWeight: FontWeight.bold))),
|
||||||
const SizedBox(width: 10),
|
const SizedBox(width: 10),
|
||||||
],
|
],
|
||||||
)),
|
),
|
||||||
alignment: Alignment.center,
|
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,
|
fadeInDuration: Duration.zero,
|
||||||
fadeOutDuration: Duration.zero,
|
fadeOutDuration: Duration.zero,
|
||||||
errorListener: (value) {},
|
errorListener: (value) {},
|
||||||
imageUrl: 'https://${AccountData().buildHttpAuthString()}@${EndpointData().nextcloud().full()}/index.php/core/preview?fileId=${file!.id}&x=100&y=-1&a=1',
|
imageUrl: 'https://${AccountData().buildHttpAuthString()}@${EndpointData().nextcloud().full()}/index.php/core/preview?fileId=${file!.id}&x=100&y=-1&a=1',
|
||||||
);
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> onOpen(LinkableElement link) async {
|
Future<void> onOpen(LinkableElement link) async {
|
||||||
|
@ -123,6 +123,7 @@ class _TimetableState extends State<Timetable> {
|
|||||||
|
|
||||||
return RefreshIndicator(
|
return RefreshIndicator(
|
||||||
child: SfCalendar(
|
child: SfCalendar(
|
||||||
|
timeZone: 'W. Europe Standard Time',
|
||||||
view: CalendarView.workWeek,
|
view: CalendarView.workWeek,
|
||||||
dataSource: _buildTableEvents(value),
|
dataSource: _buildTableEvents(value),
|
||||||
|
|
||||||
@ -367,6 +368,9 @@ class _TimetableState extends State<Timetable> {
|
|||||||
// Any changes or no teacher at this element
|
// Any changes or no teacher at this element
|
||||||
if(webuntisElement.code == 'irregular' || webuntisElement.te.first.id == 0) return const Color(0xff8F19B3).withAlpha(alpha);
|
if(webuntisElement.code == 'irregular' || webuntisElement.te.first.id == 0) return const Color(0xff8F19B3).withAlpha(alpha);
|
||||||
|
|
||||||
|
// Teacher has changed
|
||||||
|
if(webuntisElement.te.any((element) => element.orgname != null)) return const Color(0xFF29639B).withAlpha(alpha);
|
||||||
|
|
||||||
// Event was in the past
|
// Event was in the past
|
||||||
if(endTime.isBefore(DateTime.now())) return Theme.of(context).primaryColor.withAlpha(alpha);
|
if(endTime.isBefore(DateTime.now())) return Theme.of(context).primaryColor.withAlpha(alpha);
|
||||||
|
|
||||||
|
@ -2,10 +2,12 @@ import 'dart:io';
|
|||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
import '../../state/app/modules/app_modules.dart';
|
||||||
import '../../storage/base/settings.dart';
|
import '../../storage/base/settings.dart';
|
||||||
import '../../storage/devTools/devToolsSettings.dart';
|
import '../../storage/devTools/devToolsSettings.dart';
|
||||||
import '../../storage/file/fileSettings.dart';
|
import '../../storage/file/fileSettings.dart';
|
||||||
import '../../storage/fileView/fileViewSettings.dart';
|
import '../../storage/fileView/fileViewSettings.dart';
|
||||||
|
import '../../storage/general/modulesSettings.dart';
|
||||||
import '../../storage/holidays/holidaysSettings.dart';
|
import '../../storage/holidays/holidaysSettings.dart';
|
||||||
import '../../storage/notification/notificationSettings.dart';
|
import '../../storage/notification/notificationSettings.dart';
|
||||||
import '../../storage/talk/talkSettings.dart';
|
import '../../storage/talk/talkSettings.dart';
|
||||||
@ -17,6 +19,18 @@ class DefaultSettings {
|
|||||||
static Settings get() => Settings(
|
static Settings get() => Settings(
|
||||||
appTheme: ThemeMode.system,
|
appTheme: ThemeMode.system,
|
||||||
devToolsEnabled: false,
|
devToolsEnabled: false,
|
||||||
|
modulesSettings: ModulesSettings(
|
||||||
|
moduleOrder: [
|
||||||
|
Modules.timetable,
|
||||||
|
Modules.talk,
|
||||||
|
Modules.files,
|
||||||
|
Modules.marianumMessage,
|
||||||
|
Modules.roomPlan,
|
||||||
|
Modules.gradeAveragesCalculator,
|
||||||
|
Modules.holidays
|
||||||
|
],
|
||||||
|
hiddenModules: [],
|
||||||
|
),
|
||||||
timetableSettings: TimetableSettings(
|
timetableSettings: TimetableSettings(
|
||||||
connectDoubleLessons: false,
|
connectDoubleLessons: false,
|
||||||
timetableNameMode: TimetableNameMode.name
|
timetableNameMode: TimetableNameMode.name
|
||||||
|
@ -10,15 +10,15 @@ import '../../widget/confirmDialog.dart';
|
|||||||
import '../../widget/debug/cacheView.dart';
|
import '../../widget/debug/cacheView.dart';
|
||||||
import '../../widget/debug/jsonViewer.dart';
|
import '../../widget/debug/jsonViewer.dart';
|
||||||
|
|
||||||
class DevToolsSettingsDialog extends StatefulWidget {
|
class DevToolsSettings extends StatefulWidget {
|
||||||
final SettingsProvider settings;
|
final SettingsProvider settings;
|
||||||
const DevToolsSettingsDialog({required this.settings, super.key});
|
const DevToolsSettings({required this.settings, super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<DevToolsSettingsDialog> createState() => _DevToolsSettingsDialogState();
|
State<DevToolsSettings> createState() => _DevToolsSettingsState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _DevToolsSettingsDialogState extends State<DevToolsSettingsDialog> {
|
class _DevToolsSettingsState extends State<DevToolsSettings> {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) => Column(
|
Widget build(BuildContext context) => Column(
|
||||||
children: [
|
children: [
|
||||||
@ -59,9 +59,9 @@ class _DevToolsSettingsDialogState extends State<DevToolsSettingsDialog> {
|
|||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: const CenteredLeading(Icon(Icons.image_outlined)),
|
leading: const CenteredLeading(Icon(Icons.image_outlined)),
|
||||||
title: const Text('Cached Thumbnails löschen'),
|
title: const Text('Thumb-storage'),
|
||||||
subtitle: Text('etwa ${filesize(PaintingBinding.instance.imageCache.currentSizeBytes)}'),
|
subtitle: Text('etwa ${filesize(PaintingBinding.instance.imageCache.currentSizeBytes)}\nLange tippen um zu löschen'),
|
||||||
onTap: () {
|
onLongPress: () {
|
||||||
ConfirmDialog(
|
ConfirmDialog(
|
||||||
title: 'Thumbs cache löschen',
|
title: 'Thumbs cache löschen',
|
||||||
content: 'Alle zwischengespeicherten Bilder werden gelöscht.',
|
content: 'Alle zwischengespeicherten Bilder werden gelöscht.',
|
||||||
@ -69,7 +69,6 @@ class _DevToolsSettingsDialogState extends State<DevToolsSettingsDialog> {
|
|||||||
onConfirm: () => PaintingBinding.instance.imageCache.clear(),
|
onConfirm: () => PaintingBinding.instance.imageCache.clear(),
|
||||||
).asDialog(context);
|
).asDialog(context);
|
||||||
},
|
},
|
||||||
trailing: const Icon(Icons.arrow_right),
|
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: const CenteredLeading(Icon(Icons.settings_applications_outlined)),
|
leading: const CenteredLeading(Icon(Icons.settings_applications_outlined)),
|
||||||
@ -80,7 +79,7 @@ class _DevToolsSettingsDialogState extends State<DevToolsSettingsDialog> {
|
|||||||
},
|
},
|
||||||
onLongPress: () {
|
onLongPress: () {
|
||||||
ConfirmDialog(
|
ConfirmDialog(
|
||||||
title: 'App-Speicher löschen',
|
title: 'Einstellungen löschen',
|
||||||
content: 'Alle Einstellungen gehen verloren! Accountdaten sowie App-Daten sind nicht betroffen.',
|
content: 'Alle Einstellungen gehen verloren! Accountdaten sowie App-Daten sind nicht betroffen.',
|
||||||
confirmButton: 'Unwiederruflich Löschen',
|
confirmButton: 'Unwiederruflich Löschen',
|
||||||
onConfirm: () {
|
onConfirm: () {
|
||||||
@ -112,7 +111,7 @@ class _DevToolsSettingsDialogState extends State<DevToolsSettingsDialog> {
|
|||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: const CenteredLeading(Icon(Icons.data_object)),
|
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'),
|
subtitle: const Text('Lange tippen um zu löschen'),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
// Navigator.push(context, MaterialPageRoute(builder: (context) => const CacheView()));
|
// Navigator.push(context, MaterialPageRoute(builder: (context) => const CacheView()));
|
||||||
@ -125,7 +124,6 @@ class _DevToolsSettingsDialogState extends State<DevToolsSettingsDialog> {
|
|||||||
onConfirm: () => HydratedBloc.storage.clear(),
|
onConfirm: () => HydratedBloc.storage.clear(),
|
||||||
).asDialog(context);
|
).asDialog(context);
|
||||||
},
|
},
|
||||||
trailing: const Icon(Icons.arrow_right),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
@ -16,7 +16,7 @@ import '../../widget/confirmDialog.dart';
|
|||||||
import '../../widget/debug/cacheView.dart';
|
import '../../widget/debug/cacheView.dart';
|
||||||
import '../pages/timetable/timetableNameMode.dart';
|
import '../pages/timetable/timetableNameMode.dart';
|
||||||
import 'defaultSettings.dart';
|
import 'defaultSettings.dart';
|
||||||
import 'devToolsSettingsDialog.dart';
|
import 'devToolsSettings.dart';
|
||||||
import 'privacyInfo.dart';
|
import 'privacyInfo.dart';
|
||||||
|
|
||||||
class Settings extends StatefulWidget {
|
class Settings extends StatefulWidget {
|
||||||
@ -155,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(
|
ListTile(
|
||||||
leading: const CenteredLeading(Icon(Icons.notifications_active_outlined)),
|
leading: const CenteredLeading(Icon(Icons.notifications_active_outlined)),
|
||||||
title: const Text('Push-Benachrichtigungen aktivieren'),
|
title: const Text('Push-Benachrichtigungen aktivieren'),
|
||||||
@ -214,6 +186,30 @@ class _SettingsState extends State<Settings> {
|
|||||||
|
|
||||||
const Divider(),
|
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(
|
ListTile(
|
||||||
leading: const Icon(Icons.live_help_outlined),
|
leading: const Icon(Icons.live_help_outlined),
|
||||||
title: const Text('Informationen und Lizenzen'),
|
title: const Text('Informationen und Lizenzen'),
|
||||||
@ -269,15 +265,12 @@ class _SettingsState extends State<Settings> {
|
|||||||
|
|
||||||
const Divider(),
|
const Divider(),
|
||||||
|
|
||||||
Visibility(
|
ListTile(
|
||||||
visible: !kReleaseMode,
|
|
||||||
child: ListTile(
|
|
||||||
leading: const CenteredLeading(Icon(Icons.code)),
|
leading: const CenteredLeading(Icon(Icons.code)),
|
||||||
title: const Text('Quellcode MarianumMobile/Client'),
|
title: const Text('Quellcode MarianumMobile/Client'),
|
||||||
subtitle: const Text('GNU GPL v3'),
|
subtitle: const Text('GNU GPL v3'),
|
||||||
onTap: () => ConfirmDialog.openBrowser(context, 'https://mhsl.eu/gitea/MarianumMobile/Client'),
|
onTap: () => ConfirmDialog.openBrowser(context, 'https://mhsl.eu/gitea/MarianumMobile/Client'),
|
||||||
),
|
),
|
||||||
),
|
|
||||||
|
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: const Icon(Icons.developer_mode_outlined),
|
leading: const Icon(Icons.developer_mode_outlined),
|
||||||
@ -311,7 +304,7 @@ class _SettingsState extends State<Settings> {
|
|||||||
|
|
||||||
Visibility(
|
Visibility(
|
||||||
visible: settings.val().devToolsEnabled,
|
visible: settings.val().devToolsEnabled,
|
||||||
child: DevToolsSettingsDialog(settings: settings),
|
child: DevToolsSettings(settings: settings),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
@ -3,7 +3,6 @@ import 'dart:async';
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'package:filesize/filesize.dart';
|
import 'package:filesize/filesize.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
|
||||||
import 'package:jiffy/jiffy.dart';
|
import 'package:jiffy/jiffy.dart';
|
||||||
import 'package:localstore/localstore.dart';
|
import 'package:localstore/localstore.dart';
|
||||||
|
|
||||||
@ -41,7 +40,7 @@ class _CacheViewState extends State<CacheView> {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) => Scaffold(
|
Widget build(BuildContext context) => Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: const Text('Lokaler cache'),
|
title: const Text('Cache storage'),
|
||||||
),
|
),
|
||||||
body: FutureBuilder(
|
body: FutureBuilder(
|
||||||
future: files,
|
future: files,
|
||||||
@ -58,27 +57,7 @@ class _CacheViewState extends State<CacheView> {
|
|||||||
title: Text(filename),
|
title: Text(filename),
|
||||||
subtitle: Text("${filesize(jsonEncode(element).length * 8)}, ${Jiffy.parseFromMillisecondsSinceEpoch(element['lastupdate']).fromNow()}"),
|
subtitle: Text("${filesize(jsonEncode(element).length * 8)}, ${Jiffy.parseFromMillisecondsSinceEpoch(element['lastupdate']).fromNow()}"),
|
||||||
trailing: const Icon(Icons.arrow_right),
|
trailing: const Icon(Icons.arrow_right),
|
||||||
onTap: () {
|
onTap: () => JsonViewer.asDialog(context, jsonDecode(element['json'])),
|
||||||
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();
|
|
||||||
},
|
|
||||||
)
|
|
||||||
],
|
|
||||||
));
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
@ -7,22 +7,19 @@ class PlaceholderView extends StatelessWidget {
|
|||||||
const PlaceholderView({super.key, required this.icon, required this.text, this.button});
|
const PlaceholderView({super.key, required this.icon, required this.text, this.button});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) => DefaultTextStyle(
|
Widget build(BuildContext context) => Scaffold(
|
||||||
style: const TextStyle(),
|
body: Center(
|
||||||
child: Center(
|
|
||||||
child: Container(
|
child: Container(
|
||||||
margin: const EdgeInsets.only(top: 100, left: 20, right: 20),
|
margin: const EdgeInsets.only(top: 100, left: 20, right: 20),
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
Container(
|
Container(
|
||||||
margin: const EdgeInsets.all(30),
|
margin: const EdgeInsets.all(30),
|
||||||
child: Icon(icon, color: Colors.grey, size: 60),
|
child: Icon(icon, size: 60),
|
||||||
),
|
|
||||||
Text(text,
|
|
||||||
style: const TextStyle(
|
|
||||||
fontSize: 20,
|
|
||||||
color: Colors.grey,
|
|
||||||
),
|
),
|
||||||
|
Text(
|
||||||
|
text,
|
||||||
|
style: const TextStyle(fontSize: 20,),
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 30),
|
const SizedBox(height: 30),
|
||||||
|
@ -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
|
# 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
|
# 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.
|
# 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:
|
environment:
|
||||||
sdk: '>3.0.0'
|
sdk: '>3.0.0'
|
||||||
@ -101,6 +101,7 @@ dependencies:
|
|||||||
time_range_picker: ^2.3.0
|
time_range_picker: ^2.3.0
|
||||||
url_launcher: ^6.3.1
|
url_launcher: ^6.3.1
|
||||||
uuid: ^4.5.1
|
uuid: ^4.5.1
|
||||||
|
collection: ^1.19.0
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
Reference in New Issue
Block a user