merged to origin/devlop
This commit is contained in:
@ -20,9 +20,7 @@ class Login extends StatefulWidget {
|
||||
class _LoginState extends State<Login> {
|
||||
bool displayDisclaimerText = true;
|
||||
|
||||
String? _checkInput(value){
|
||||
return (value ?? '').length == 0 ? 'Eingabe erforderlich' : null;
|
||||
}
|
||||
String? _checkInput(value)=> (value ?? '').length == 0 ? 'Eingabe erforderlich' : null;
|
||||
|
||||
Future<String?> _login(LoginData data) async {
|
||||
await AccountData().removeData();
|
||||
@ -49,15 +47,10 @@ class _LoginState extends State<Login> {
|
||||
return null;
|
||||
}
|
||||
|
||||
Future<String> _resetPassword(String name) {
|
||||
return Future.delayed(Duration.zero).then((_) {
|
||||
return 'Diese Funktion steht nicht zur Verfügung!';
|
||||
});
|
||||
}
|
||||
Future<String> _resetPassword(String name) => Future.delayed(Duration.zero).then((_) => 'Diese Funktion steht nicht zur Verfügung!');
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return FlutterLogin(
|
||||
Widget build(BuildContext context) => FlutterLogin(
|
||||
logo: Image.asset('assets/logo/icon.png').image,
|
||||
|
||||
userValidator: _checkInput,
|
||||
@ -109,5 +102,4 @@ class _LoginState extends State<Login> {
|
||||
|
||||
userType: LoginUserType.name,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -26,14 +26,14 @@ class FileElement extends StatefulWidget {
|
||||
const FileElement(this.file, this.path, this.refetch, {super.key});
|
||||
|
||||
static Future<DownloaderCore> download(BuildContext context, String remotePath, String name, Function(double) onProgress, Function(OpenResult) onDone) async {
|
||||
Directory paths = await getTemporaryDirectory();
|
||||
var paths = await getTemporaryDirectory();
|
||||
|
||||
var encodedPath = Uri.encodeComponent(remotePath);
|
||||
encodedPath = encodedPath.replaceAll('%2F', '/');
|
||||
|
||||
String local = paths.path + Platform.pathSeparator + name;
|
||||
var local = paths.path + Platform.pathSeparator + name;
|
||||
|
||||
DownloaderUtils options = DownloaderUtils(
|
||||
var options = DownloaderUtils(
|
||||
progressCallback: (current, total) {
|
||||
final progress = (current / total) * 100;
|
||||
onProgress(progress);
|
||||
@ -52,7 +52,7 @@ class FileElement extends StatefulWidget {
|
||||
);
|
||||
|
||||
return await Flowder.download(
|
||||
"${await WebdavApi.webdavConnectString}$encodedPath",
|
||||
'${await WebdavApi.webdavConnectString}$encodedPath',
|
||||
options,
|
||||
);
|
||||
}
|
||||
@ -89,8 +89,7 @@ class _FileElementState extends State<FileElement> {
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ListTile(
|
||||
Widget build(BuildContext context) => ListTile(
|
||||
leading: CenteredLeading(
|
||||
Icon(widget.file.isDirectory ? Icons.folder : Icons.description_outlined)
|
||||
),
|
||||
@ -100,9 +99,7 @@ class _FileElementState extends State<FileElement> {
|
||||
onTap: () {
|
||||
if(widget.file.isDirectory) {
|
||||
Navigator.of(context).push(MaterialPageRoute(
|
||||
builder: (context) {
|
||||
return Files(widget.path.toList()..add(widget.file.name));
|
||||
},
|
||||
builder: (context) => Files(widget.path.toList()..add(widget.file.name)),
|
||||
));
|
||||
} else {
|
||||
if(EndpointData().getEndpointMode() == EndpointMode.stage) {
|
||||
@ -141,12 +138,10 @@ class _FileElementState extends State<FileElement> {
|
||||
setState(() => percent = progress);
|
||||
}, (result) {
|
||||
if(result.type != ResultType.done) {
|
||||
showDialog(context: context, builder: (context) {
|
||||
return AlertDialog(
|
||||
showDialog(context: context, builder: (context) => AlertDialog(
|
||||
title: const Text('Download'),
|
||||
content: Text(result.message),
|
||||
);
|
||||
});
|
||||
));
|
||||
}
|
||||
|
||||
setState(() {
|
||||
@ -158,8 +153,7 @@ class _FileElementState extends State<FileElement> {
|
||||
}
|
||||
},
|
||||
onLongPress: () {
|
||||
showDialog(context: context, builder: (context) {
|
||||
return SimpleDialog(
|
||||
showDialog(context: context, builder: (context) => SimpleDialog(
|
||||
children: [
|
||||
ListTile(
|
||||
leading: const Icon(Icons.delete_outline),
|
||||
@ -189,9 +183,7 @@ class _FileElementState extends State<FileElement> {
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
});
|
||||
));
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
0
lib/view/pages/files/fileUploadDialog.dart
Normal file
0
lib/view/pages/files/fileUploadDialog.dart
Normal file
@ -113,7 +113,7 @@ class _FilesState extends State<Files> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
List<CacheableFile> files = data?.sortBy(
|
||||
var files = data?.sortBy(
|
||||
sortOption: currentSort,
|
||||
foldersToTop: Provider.of<SettingsProvider>(context).val().fileSettings.sortFoldersToTop,
|
||||
reversed: currentSortDirection
|
||||
@ -131,8 +131,7 @@ class _FilesState extends State<Files> {
|
||||
// ),
|
||||
PopupMenuButton<bool>(
|
||||
icon: Icon(currentSortDirection ? Icons.text_rotate_up : Icons.text_rotation_down),
|
||||
itemBuilder: (context) {
|
||||
return [true, false].map((e) => PopupMenuItem<bool>(
|
||||
itemBuilder: (context) => [true, false].map((e) => PopupMenuItem<bool>(
|
||||
value: e,
|
||||
enabled: e != currentSortDirection,
|
||||
child: Row(
|
||||
@ -142,8 +141,7 @@ class _FilesState extends State<Files> {
|
||||
Text(e ? 'Aufsteigend' : 'Absteigend')
|
||||
],
|
||||
)
|
||||
)).toList();
|
||||
},
|
||||
)).toList(),
|
||||
onSelected: (e) {
|
||||
setState(() {
|
||||
currentSortDirection = e;
|
||||
@ -153,8 +151,7 @@ class _FilesState extends State<Files> {
|
||||
),
|
||||
PopupMenuButton<SortOption>(
|
||||
icon: const Icon(Icons.sort),
|
||||
itemBuilder: (context) {
|
||||
return SortOptions.options.keys.map((key) => PopupMenuItem<SortOption>(
|
||||
itemBuilder: (context) => SortOptions.options.keys.map((key) => PopupMenuItem<SortOption>(
|
||||
value: key,
|
||||
enabled: key != currentSort,
|
||||
child: Row(
|
||||
@ -164,8 +161,7 @@ class _FilesState extends State<Files> {
|
||||
Text(SortOptions.getOption(key).displayName),
|
||||
],
|
||||
)
|
||||
)).toList();
|
||||
},
|
||||
)).toList(),
|
||||
onSelected: (e) {
|
||||
setState(() {
|
||||
currentSort = e;
|
||||
@ -179,8 +175,7 @@ class _FilesState extends State<Files> {
|
||||
heroTag: 'uploadFile',
|
||||
backgroundColor: Theme.of(context).primaryColor,
|
||||
onPressed: () {
|
||||
showDialog(context: context, builder: (context) {
|
||||
return SimpleDialog(
|
||||
showDialog(context: context, builder: (context) => SimpleDialog(
|
||||
children: [
|
||||
ListTile(
|
||||
leading: const Icon(Icons.create_new_folder_outlined),
|
||||
@ -234,8 +229,7 @@ class _FilesState extends State<Files> {
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
});
|
||||
));
|
||||
},
|
||||
child: const Icon(Icons.add),
|
||||
),
|
||||
@ -249,7 +243,7 @@ class _FilesState extends State<Files> {
|
||||
padding: EdgeInsets.zero,
|
||||
itemCount: files.length,
|
||||
itemBuilder: (context, index) {
|
||||
CacheableFile file = files.toList()[index];
|
||||
var file = files.toList()[index];
|
||||
return FileElement(file, widget.path, _query);
|
||||
},
|
||||
),
|
||||
|
@ -44,8 +44,7 @@ class _FeedbackDialogState extends State<FeedbackDialog> {
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
Widget build(BuildContext context) => Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Feedback'),
|
||||
),
|
||||
@ -125,7 +124,7 @@ class _FeedbackDialogState extends State<FeedbackDialog> {
|
||||
onPressed: () async {
|
||||
context.loaderOverlay.show();
|
||||
var imageData = await (await FilePick.galleryPick())?.readAsBytes();
|
||||
context.loaderOverlay.hide();
|
||||
if(context.mounted) context.loaderOverlay.hide();
|
||||
setState(() {
|
||||
_image = imageData;
|
||||
});
|
||||
@ -170,6 +169,4 @@ class _FeedbackDialogState extends State<FeedbackDialog> {
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,6 @@ import 'package:flutter/material.dart';
|
||||
import 'package:jiffy/jiffy.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
import '../../../../api/holidays/getHolidaysResponse.dart';
|
||||
import '../../../../model/holidays/holidaysProps.dart';
|
||||
import '../../../../storage/base/settingsProvider.dart';
|
||||
import '../../../../widget/centeredLeading.dart';
|
||||
@ -22,9 +21,7 @@ class Holidays extends StatefulWidget {
|
||||
}
|
||||
|
||||
extension StringExtension on String {
|
||||
String capitalize() {
|
||||
return "${this[0].toUpperCase()}${substring(1).toLowerCase()}";
|
||||
}
|
||||
String capitalize() => '${this[0].toUpperCase()}${substring(1).toLowerCase()}';
|
||||
}
|
||||
|
||||
class _HolidaysState extends State<Holidays> {
|
||||
@ -41,13 +38,10 @@ class _HolidaysState extends State<Holidays> {
|
||||
super.initState();
|
||||
}
|
||||
|
||||
String parseString(String enDate) {
|
||||
return Jiffy.parse(enDate).format(pattern: 'dd.MM.yyyy');
|
||||
}
|
||||
String parseString(String enDate) => Jiffy.parse(enDate).format(pattern: 'dd.MM.yyyy');
|
||||
|
||||
void showDisclaimer() {
|
||||
showDialog(context: context, builder: (context) {
|
||||
return AlertDialog(
|
||||
showDialog(context: context, builder: (context) => AlertDialog(
|
||||
title: const Text('Richtigkeit und Bereitstellung der Daten'),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
@ -70,13 +64,11 @@ class _HolidaysState extends State<Holidays> {
|
||||
TextButton(child: const Text('ferien-api.de besuchen'), onPressed: () => ConfirmDialog.openBrowser(context, 'https://ferien-api.de/')),
|
||||
TextButton(child: const Text('Okay'), onPressed: () => Navigator.of(context).pop()),
|
||||
],
|
||||
);
|
||||
});
|
||||
));
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
Widget build(BuildContext context) => Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Schulferien in Hessen'),
|
||||
actions: [
|
||||
@ -87,8 +79,7 @@ class _HolidaysState extends State<Holidays> {
|
||||
PopupMenuButton<bool>(
|
||||
initialValue: settings.val().holidaysSettings.showPastEvents,
|
||||
icon: const Icon(Icons.manage_history_outlined),
|
||||
itemBuilder: (context) {
|
||||
return [true, false].map((e) => PopupMenuItem<bool>(
|
||||
itemBuilder: (context) => [true, false].map((e) => PopupMenuItem<bool>(
|
||||
value: e,
|
||||
enabled: e != showPastEvents,
|
||||
child: Row(
|
||||
@ -98,8 +89,7 @@ class _HolidaysState extends State<Holidays> {
|
||||
Text(e ? 'Alle anzeigen' : 'Nur zukünftige anzeigen')
|
||||
],
|
||||
)
|
||||
)).toList();
|
||||
},
|
||||
)).toList(),
|
||||
onSelected: (e) {
|
||||
setState(() {
|
||||
showPastEvents = e;
|
||||
@ -112,7 +102,7 @@ class _HolidaysState extends State<Holidays> {
|
||||
body: Consumer<HolidaysProps>(builder: (context, value, child) {
|
||||
if(value.primaryLoading()) return const LoadingSpinner();
|
||||
|
||||
List<GetHolidaysResponseObject> holidays = value.getHolidaysResponse.data;
|
||||
var holidays = value.getHolidaysResponse.data;
|
||||
if(!showPastEvents) holidays = holidays.where((element) => DateTime.parse(element.end).isAfter(DateTime.now())).toList();
|
||||
|
||||
if(holidays.isEmpty) return const PlaceholderView(icon: Icons.search_off, text: 'Es wurden keine Ferieneinträge gefunden!');
|
||||
@ -120,8 +110,8 @@ class _HolidaysState extends State<Holidays> {
|
||||
return ListView.builder(
|
||||
itemCount: holidays.length,
|
||||
itemBuilder: (context, index) {
|
||||
GetHolidaysResponseObject holiday = holidays[index];
|
||||
String holidayType = holiday.name.split(' ').first.capitalize();
|
||||
var holiday = holidays[index];
|
||||
var holidayType = holiday.name.split(' ').first.capitalize();
|
||||
return ListTile(
|
||||
leading: const CenteredLeading(Icon(Icons.calendar_month)),
|
||||
title: Text('$holidayType ab ${parseString(holiday.start)}'),
|
||||
@ -164,5 +154,4 @@ class _HolidaysState extends State<Holidays> {
|
||||
},
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
import '../../../../api/mhsl/message/getMessages/getMessagesResponse.dart';
|
||||
import '../../../../model/message/messageProps.dart';
|
||||
import '../../../../widget/loadingSpinner.dart';
|
||||
import 'messageView.dart';
|
||||
@ -24,8 +23,7 @@ class _MessageState extends State<Message> {
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
Widget build(BuildContext context) => Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Marianum Message'),
|
||||
),
|
||||
@ -36,7 +34,7 @@ class _MessageState extends State<Message> {
|
||||
child: ListView.builder(
|
||||
itemCount: value.getMessagesResponse.messages.length,
|
||||
itemBuilder: (context, index) {
|
||||
GetMessagesResponseObject message = value.getMessagesResponse.messages.toList()[index];
|
||||
var message = value.getMessagesResponse.messages.toList()[index];
|
||||
return ListTile(
|
||||
leading: const Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
@ -58,5 +56,4 @@ class _MessageState extends State<Message> {
|
||||
);
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -17,8 +17,7 @@ class MessageView extends StatefulWidget {
|
||||
class _MessageViewState extends State<MessageView> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
Widget build(BuildContext context) => Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(widget.message.name),
|
||||
),
|
||||
@ -27,8 +26,7 @@ class _MessageViewState extends State<MessageView> {
|
||||
enableHyperlinkNavigation: true,
|
||||
onDocumentLoadFailed: (PdfDocumentLoadFailedDetails e) {
|
||||
Navigator.of(context).pop();
|
||||
showDialog(context: context, builder: (context) {
|
||||
return AlertDialog(
|
||||
showDialog(context: context, builder: (context) => AlertDialog(
|
||||
title: const Text('Fehler beim öffnen'),
|
||||
content: Text("Dokument '${widget.message.name}' konnte nicht geladen werden:\n${e.description}"),
|
||||
actions: [
|
||||
@ -36,8 +34,7 @@ class _MessageViewState extends State<MessageView> {
|
||||
Navigator.of(context).pop();
|
||||
}, child: const Text('Ok'))
|
||||
],
|
||||
);
|
||||
});
|
||||
));
|
||||
},
|
||||
onHyperlinkClicked: (PdfHyperlinkClickedDetails e) {
|
||||
showDialog(
|
||||
@ -52,5 +49,4 @@ class _MessageViewState extends State<MessageView> {
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -5,8 +5,7 @@ class Roomplan extends StatelessWidget {
|
||||
const Roomplan({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
Widget build(BuildContext context) => Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Raumplan'),
|
||||
),
|
||||
@ -17,5 +16,4 @@ class Roomplan extends StatelessWidget {
|
||||
backgroundDecoration: BoxDecoration(color: Theme.of(context).colorScheme.background),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ class AppSharePlatformView extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Color foregroundColor = Theme.of(context).colorScheme.onBackground;
|
||||
var foregroundColor = Theme.of(context).colorScheme.onBackground;
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
|
@ -11,8 +11,7 @@ class QrShareView extends StatefulWidget {
|
||||
|
||||
class _QrShareViewState extends State<QrShareView> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return DefaultTabController(
|
||||
Widget build(BuildContext context) => DefaultTabController(
|
||||
length: 2,
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
@ -32,5 +31,4 @@ class _QrShareViewState extends State<QrShareView> {
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -8,8 +8,7 @@ class SelectShareTypeDialog extends StatelessWidget {
|
||||
const SelectShareTypeDialog({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SimpleDialog(
|
||||
Widget build(BuildContext context) => SimpleDialog(
|
||||
children: [
|
||||
ListTile(
|
||||
leading: const Icon(Icons.qr_code_2_outlined),
|
||||
@ -36,5 +35,4 @@ class SelectShareTypeDialog extends StatelessWidget {
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -21,9 +21,7 @@ class Overhang extends StatelessWidget {
|
||||
const Overhang({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
||||
return Scaffold(
|
||||
Widget build(BuildContext context) => Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Mehr'),
|
||||
actions: [
|
||||
@ -79,5 +77,4 @@ class Overhang extends StatelessWidget {
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -35,7 +35,7 @@ class _ChatInfoState extends State<ChatInfo> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
bool isGroup = widget.room.type != GetRoomResponseObjectConversationType.oneToOne;
|
||||
var isGroup = widget.room.type != GetRoomResponseObjectConversationType.oneToOne;
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(widget.room.displayName),
|
||||
|
@ -13,20 +13,16 @@ class ParticipantsListView extends StatefulWidget {
|
||||
|
||||
class _ParticipantsListViewState extends State<ParticipantsListView> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
Widget build(BuildContext context) => Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Teilnehmende'),
|
||||
),
|
||||
body: ListView(
|
||||
children: widget.participantsResponse.data.map((participant) {
|
||||
return ListTile(
|
||||
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(),
|
||||
)).toList(),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -119,14 +119,14 @@ class _ChatListState extends State<ChatList> {
|
||||
|
||||
if(data.primaryLoading()) return const LoadingSpinner();
|
||||
latestData = data;
|
||||
List<ChatTile> chats = [];
|
||||
var chats = <ChatTile>[];
|
||||
for (var chatRoom in data.getRoomsResponse.sortBy(
|
||||
lastActivity: true,
|
||||
favoritesToTop: Provider.of<SettingsProvider>(context).val().talkSettings.sortFavoritesToTop,
|
||||
unreadToTop: Provider.of<SettingsProvider>(context).val().talkSettings.sortUnreadToTop,
|
||||
)
|
||||
) {
|
||||
bool hasDraft = settings.val().talkSettings.drafts.containsKey(chatRoom.token);
|
||||
var hasDraft = settings.val().talkSettings.drafts.containsKey(chatRoom.token);
|
||||
chats.add(ChatTile(data: chatRoom, query: _query, hasDraft: hasDraft));
|
||||
}
|
||||
|
||||
@ -146,4 +146,4 @@ class _ChatListState extends State<ChatList> {
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -40,19 +40,18 @@ class _ChatViewState extends State<ChatView> {
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Consumer<ChatProps>(
|
||||
Widget build(BuildContext context) => Consumer<ChatProps>(
|
||||
builder: (context, data, child) {
|
||||
List<Widget> messages = List<Widget>.empty(growable: true);
|
||||
var messages = List<Widget>.empty(growable: true);
|
||||
|
||||
if(!data.primaryLoading()) {
|
||||
|
||||
DateTime lastDate = DateTime.now();
|
||||
var lastDate = DateTime.now();
|
||||
data.getChatResponse.sortByTimestamp().forEach((element) {
|
||||
DateTime elementDate = DateTime.fromMillisecondsSinceEpoch(element.timestamp * 1000);
|
||||
var elementDate = DateTime.fromMillisecondsSinceEpoch(element.timestamp * 1000);
|
||||
|
||||
if(element.systemMessage.contains('reaction')) return;
|
||||
int commonRead = int.parse(data.getChatResponse.headers?['x-chat-last-common-read'] ?? '0');
|
||||
var commonRead = int.parse(data.getChatResponse.headers?['x-chat-last-common-read'] ?? '0');
|
||||
|
||||
if(!elementDate.isSameDay(lastDate)) {
|
||||
lastDate = elementDate;
|
||||
@ -140,5 +139,4 @@ class _ChatViewState extends State<ChatView> {
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -51,15 +51,13 @@ class ChatBubble extends StatefulWidget {
|
||||
|
||||
class _ChatBubbleState extends State<ChatBubble> {
|
||||
|
||||
BubbleStyle getSystemStyle() {
|
||||
return BubbleStyle(
|
||||
BubbleStyle getSystemStyle() => BubbleStyle(
|
||||
color: AppTheme.isDarkMode(context) ? const Color(0xff182229) : Colors.white,
|
||||
borderWidth: 1,
|
||||
elevation: 2,
|
||||
margin: const BubbleEdges.only(bottom: 20, top: 10),
|
||||
alignment: Alignment.center,
|
||||
);
|
||||
}
|
||||
|
||||
BubbleStyle getRemoteStyle(bool seamless) {
|
||||
var color = AppTheme.isDarkMode(context) ? const Color(0xff202c33) : Colors.white;
|
||||
@ -105,17 +103,17 @@ class _ChatBubbleState extends State<ChatBubble> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
message = ChatMessage(originalMessage: widget.bubbleData.message, originalData: widget.bubbleData.messageParameters);
|
||||
bool showActorDisplayName = widget.bubbleData.messageType == GetRoomResponseObjectMessageType.comment && widget.chatData.type != GetRoomResponseObjectConversationType.oneToOne;
|
||||
bool showBubbleTime = widget.bubbleData.messageType != GetRoomResponseObjectMessageType.system;
|
||||
var showActorDisplayName = widget.bubbleData.messageType == GetRoomResponseObjectMessageType.comment && widget.chatData.type != GetRoomResponseObjectConversationType.oneToOne;
|
||||
var showBubbleTime = widget.bubbleData.messageType != GetRoomResponseObjectMessageType.system;
|
||||
|
||||
Text actorText = Text(
|
||||
var actorText = Text(
|
||||
widget.bubbleData.actorDisplayName,
|
||||
textAlign: TextAlign.start,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(color: Theme.of(context).primaryColor, fontWeight: FontWeight.bold),
|
||||
);
|
||||
|
||||
Text timeText = Text(
|
||||
var timeText = Text(
|
||||
Jiffy.parseFromMillisecondsSinceEpoch(widget.bubbleData.timestamp * 1000).format(pattern: 'HH:mm'),
|
||||
textAlign: TextAlign.end,
|
||||
style: TextStyle(color: widget.timeIconColor, fontSize: widget.timeIconSize),
|
||||
@ -186,8 +184,8 @@ class _ChatBubbleState extends State<ChatBubble> {
|
||||
),
|
||||
onLongPress: () {
|
||||
showDialog(context: context, builder: (context) {
|
||||
List<String> commonReactions = ['👍', '👎', '😆', '❤️', '👀'];
|
||||
bool canReact = widget.bubbleData.messageType == GetRoomResponseObjectMessageType.comment;
|
||||
var commonReactions = <String>['👍', '👎', '😆', '❤️', '👀'];
|
||||
var canReact = widget.bubbleData.messageType == GetRoomResponseObjectMessageType.comment;
|
||||
return SimpleDialog(
|
||||
children: [
|
||||
Visibility(
|
||||
@ -217,8 +215,7 @@ class _ChatBubbleState extends State<ChatBubble> {
|
||||
),
|
||||
IconButton(
|
||||
onPressed: () {
|
||||
showDialog(context: context, builder: (context) {
|
||||
return AlertDialog(
|
||||
showDialog(context: context, builder: (context) => AlertDialog(
|
||||
contentPadding: const EdgeInsets.all(15),
|
||||
titlePadding: const EdgeInsets.only(left: 6, top: 15),
|
||||
title: Row(
|
||||
@ -278,8 +275,7 @@ class _ChatBubbleState extends State<ChatBubble> {
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
});
|
||||
));
|
||||
},
|
||||
style: IconButton.styleFrom(
|
||||
padding: EdgeInsets.zero,
|
||||
@ -350,8 +346,7 @@ class _ChatBubbleState extends State<ChatBubble> {
|
||||
if(message.file == null) return;
|
||||
|
||||
if(downloadProgress > 0) {
|
||||
showDialog(context: context, builder: (context) {
|
||||
return AlertDialog(
|
||||
showDialog(context: context, builder: (context) => AlertDialog(
|
||||
title: const Text('Download abbrechen?'),
|
||||
content: const Text('Möchtest du den Download abbrechen?'),
|
||||
actions: [
|
||||
@ -369,8 +364,7 @@ class _ChatBubbleState extends State<ChatBubble> {
|
||||
});
|
||||
}, child: const Text('Ja, Abbrechen'))
|
||||
],
|
||||
);
|
||||
});
|
||||
));
|
||||
|
||||
return;
|
||||
}
|
||||
@ -388,11 +382,9 @@ class _ChatBubbleState extends State<ChatBubble> {
|
||||
});
|
||||
|
||||
if(result.type != ResultType.done) {
|
||||
showDialog(context: context, builder: (context) {
|
||||
return AlertDialog(
|
||||
showDialog(context: context, builder: (context) => AlertDialog(
|
||||
content: Text(result.message),
|
||||
);
|
||||
});
|
||||
));
|
||||
}
|
||||
});
|
||||
},
|
||||
@ -408,7 +400,7 @@ class _ChatBubbleState extends State<ChatBubble> {
|
||||
alignment: widget.isSender ? WrapAlignment.end : WrapAlignment.start,
|
||||
crossAxisAlignment: WrapCrossAlignment.start,
|
||||
children: widget.bubbleData.reactions?.entries.map<Widget>((e) {
|
||||
bool hasSelfReacted = widget.bubbleData.reactionsSelf?.contains(e.key) ?? false;
|
||||
var hasSelfReacted = widget.bubbleData.reactionsSelf?.contains(e.key) ?? false;
|
||||
return Container(
|
||||
margin: const EdgeInsets.only(right: 2.5, left: 2.5),
|
||||
child: ActionChip(
|
||||
|
@ -37,8 +37,7 @@ class ChatMessage {
|
||||
}
|
||||
|
||||
return CachedNetworkImage(
|
||||
errorWidget: (context, url, error) {
|
||||
return Padding(padding: const EdgeInsets.only(top: 10), child: Row(
|
||||
errorWidget: (context, url, error) => Padding(padding: const EdgeInsets.only(top: 10), child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
@ -47,12 +46,9 @@ 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) {
|
||||
return const Padding(padding: EdgeInsets.all(10), child: CircularProgressIndicator());
|
||||
},
|
||||
placeholder: (context, url) => const Padding(padding: EdgeInsets.all(10), child: CircularProgressIndicator()),
|
||||
fadeInDuration: Duration.zero,
|
||||
fadeOutDuration: Duration.zero,
|
||||
errorListener: (value) {},
|
||||
@ -60,9 +56,9 @@ class ChatMessage {
|
||||
);
|
||||
}
|
||||
|
||||
void onOpen(LinkableElement link) async {
|
||||
Future<void> onOpen(LinkableElement link) async {
|
||||
if(await canLaunchUrlString(link.url)) {
|
||||
await launchUrlString(link.url);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -66,7 +66,6 @@ class _ChatTextfieldState extends State<ChatTextfield> {
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
void setDraft(String text) {
|
||||
if(text.isNotEmpty) {
|
||||
settings.val(write: true).talkSettings.drafts[widget.sendToToken] = text;
|
||||
@ -96,8 +95,7 @@ class _ChatTextfieldState extends State<ChatTextfield> {
|
||||
children: <Widget>[
|
||||
GestureDetector(
|
||||
onTap: (){
|
||||
showDialog(context: context, builder: (context) {
|
||||
return SimpleDialog(
|
||||
showDialog(context: context, builder: (context) => SimpleDialog(
|
||||
children: [
|
||||
ListTile(
|
||||
leading: const Icon(Icons.file_open),
|
||||
@ -121,8 +119,7 @@ class _ChatTextfieldState extends State<ChatTextfield> {
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
});
|
||||
));
|
||||
},
|
||||
child: Material(
|
||||
elevation: 5,
|
||||
|
@ -40,7 +40,7 @@ class _ChatTileState extends State<ChatTile> {
|
||||
username = value.getString('username')!
|
||||
});
|
||||
|
||||
bool isGroup = widget.data.type != GetRoomResponseObjectConversationType.oneToOne;
|
||||
var isGroup = widget.data.type != GetRoomResponseObjectConversationType.oneToOne;
|
||||
circleAvatar = UserAvatar(id: isGroup ? widget.data.token : widget.data.name, isGroup: isGroup);
|
||||
}
|
||||
|
||||
@ -56,10 +56,7 @@ class _ChatTileState extends State<ChatTile> {
|
||||
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
||||
return Consumer<ChatProps>(builder: (context, chatData, child) {
|
||||
return ListTile(
|
||||
Widget build(BuildContext context) => Consumer<ChatProps>(builder: (context, chatData, child) => ListTile(
|
||||
style: ListTileStyle.list,
|
||||
tileColor: chatData.currentToken() == widget.data.token && TalkNavigator.isSecondaryVisible(context)
|
||||
? Theme.of(context).primaryColor.withAlpha(100)
|
||||
@ -120,7 +117,7 @@ class _ChatTileState extends State<ChatTile> {
|
||||
),
|
||||
onTap: () async {
|
||||
setCurrentAsRead();
|
||||
ChatView view = ChatView(room: widget.data, selfId: username, avatar: circleAvatar);
|
||||
var view = ChatView(room: widget.data, selfId: username, avatar: circleAvatar);
|
||||
TalkNavigator.pushSplitView(context, view, overrideToSingleSubScreen: true);
|
||||
Provider.of<ChatProps>(context, listen: false).setQueryToken(widget.data.token);
|
||||
},
|
||||
@ -185,7 +182,5 @@ class _ChatTileState extends State<ChatTile> {
|
||||
],
|
||||
));
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
));
|
||||
}
|
||||
|
@ -6,8 +6,7 @@ class SplitViewPlaceholder extends StatelessWidget {
|
||||
const SplitViewPlaceholder({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
Widget build(BuildContext context) => Scaffold(
|
||||
appBar: AppBar(),
|
||||
body: Center(
|
||||
child: Column(
|
||||
@ -25,5 +24,4 @@ class SplitViewPlaceholder extends StatelessWidget {
|
||||
),
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -11,8 +11,7 @@ class JoinChat extends SearchDelegate<String> {
|
||||
CancelableOperation<AutocompleteResponse>? future;
|
||||
|
||||
@override
|
||||
List<Widget>? buildActions(BuildContext context) {
|
||||
return [
|
||||
List<Widget>? buildActions(BuildContext context) => [
|
||||
if(future != null && query.isNotEmpty) FutureBuilder(
|
||||
future: future!.value,
|
||||
builder: (context, snapshot) {
|
||||
@ -35,12 +34,9 @@ class JoinChat extends SearchDelegate<String> {
|
||||
),
|
||||
if(query.isNotEmpty) IconButton(onPressed: () => query = '', icon: const Icon(Icons.delete)),
|
||||
];
|
||||
}
|
||||
|
||||
@override
|
||||
Widget? buildLeading(BuildContext context) {
|
||||
return null;
|
||||
}
|
||||
Widget? buildLeading(BuildContext context) => null;
|
||||
|
||||
@override
|
||||
Widget buildResults(BuildContext context) {
|
||||
@ -61,8 +57,8 @@ class JoinChat extends SearchDelegate<String> {
|
||||
return ListView.builder(
|
||||
itemCount: snapshot.data!.data.length,
|
||||
itemBuilder: (context, index) {
|
||||
AutocompleteResponseObject object = snapshot.data!.data[index];
|
||||
CircleAvatar circleAvatar = CircleAvatar(
|
||||
var object = snapshot.data!.data[index];
|
||||
var circleAvatar = CircleAvatar(
|
||||
foregroundImage: Image.network('https://${EndpointData().nextcloud().full()}/avatar/${object.id}/128').image,
|
||||
backgroundColor: Theme.of(context).primaryColor,
|
||||
foregroundColor: Colors.white,
|
||||
@ -89,8 +85,6 @@ class JoinChat extends SearchDelegate<String> {
|
||||
}
|
||||
|
||||
@override
|
||||
Widget buildSuggestions(BuildContext context) {
|
||||
return buildResults(context);
|
||||
}
|
||||
Widget buildSuggestions(BuildContext context) => buildResults(context);
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -30,8 +30,7 @@ class _MessageReactionsState extends State<MessageReactions> {
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
Widget build(BuildContext context) => Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Reaktionen'),
|
||||
),
|
||||
@ -42,8 +41,7 @@ class _MessageReactionsState extends State<MessageReactions> {
|
||||
if(snapshot.data == null) return const PlaceholderView(icon: Icons.search_off_outlined, text: 'Keine Reaktionen gefunden!');
|
||||
return ListView(
|
||||
children: [
|
||||
...snapshot.data!.data.entries.map<Widget>((entry) {
|
||||
return ExpansionTile(
|
||||
...snapshot.data!.data.entries.map<Widget>((entry) => ExpansionTile(
|
||||
textColor: Theme.of(context).colorScheme.onSurface,
|
||||
collapsedTextColor: Theme.of(context).colorScheme.onSurface,
|
||||
iconColor: Theme.of(context).colorScheme.onSurface,
|
||||
@ -53,7 +51,7 @@ class _MessageReactionsState extends State<MessageReactions> {
|
||||
leading: CenteredLeading(Text(entry.key)),
|
||||
title: Text('${entry.value.length} mal reagiert'),
|
||||
children: entry.value.map((e) {
|
||||
bool isSelf = AccountData().getUsername() == e.actorId;
|
||||
var isSelf = AccountData().getUsername() == e.actorId;
|
||||
return ListTile(
|
||||
leading: UserAvatar(id: e.actorId, isGroup: false),
|
||||
title: Text(e.actorDisplayName),
|
||||
@ -71,12 +69,10 @@ class _MessageReactionsState extends State<MessageReactions> {
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
);
|
||||
})
|
||||
))
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -9,16 +9,12 @@ class SearchChat extends SearchDelegate {
|
||||
SearchChat(this.chats);
|
||||
|
||||
@override
|
||||
List<Widget>? buildActions(BuildContext context) {
|
||||
return [
|
||||
List<Widget>? buildActions(BuildContext context) => [
|
||||
if(query.isNotEmpty) IconButton(onPressed: () => query = '', icon: const Icon(Icons.delete)),
|
||||
];
|
||||
}
|
||||
|
||||
@override
|
||||
Widget? buildLeading(BuildContext context) {
|
||||
return null;
|
||||
}
|
||||
Widget? buildLeading(BuildContext context) => null;
|
||||
|
||||
@override
|
||||
Widget buildResults(BuildContext context) {
|
||||
@ -35,7 +31,5 @@ class SearchChat extends SearchDelegate {
|
||||
}
|
||||
|
||||
@override
|
||||
Widget buildSuggestions(BuildContext context) {
|
||||
return buildResults(context);
|
||||
}
|
||||
}
|
||||
Widget buildSuggestions(BuildContext context) => buildResults(context);
|
||||
}
|
||||
|
@ -9,10 +9,10 @@ class TalkNavigator {
|
||||
|
||||
static void pushSplitView(BuildContext context, Widget view, {bool overrideToSingleSubScreen = false}) {
|
||||
if(isSecondaryVisible(context)) {
|
||||
SplitViewState splitView = SplitView.of(context);
|
||||
var splitView = SplitView.of(context);
|
||||
overrideToSingleSubScreen ? splitView.setSecondary(view) : splitView.push(view);
|
||||
} else {
|
||||
pushScreen(context, screen: view, withNavBar: false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -13,4 +13,4 @@ class CrossPainter extends CustomPainter {
|
||||
|
||||
@override
|
||||
bool shouldRepaint(CrossPainter oldDelegate) => false;
|
||||
}
|
||||
}
|
||||
|
@ -33,7 +33,7 @@ class AppointmentDetails {
|
||||
}
|
||||
|
||||
static void show(BuildContext context, TimetableProps webuntisData, Appointment appointment) {
|
||||
(appointment.id as ArbitraryAppointment).handlers(
|
||||
(appointment.id! as ArbitraryAppointment).handlers(
|
||||
(webuntis) => _webuntis(context, webuntisData, appointment, webuntis),
|
||||
(customData) => _custom(context, webuntisData, customData)
|
||||
);
|
||||
@ -76,21 +76,18 @@ class AppointmentDetails {
|
||||
|
||||
_bottomSheet(
|
||||
context,
|
||||
(context) {
|
||||
return Center(
|
||||
(context) => Center(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text("${_getEventPrefix(timetableData.code)}${subject.alternateName}", textAlign: TextAlign.center, style: const TextStyle(fontSize: 25), overflow: TextOverflow.ellipsis),
|
||||
Text('${_getEventPrefix(timetableData.code)}${subject.alternateName}', textAlign: TextAlign.center, style: const TextStyle(fontSize: 25), overflow: TextOverflow.ellipsis),
|
||||
Text(subject.longName),
|
||||
Text("${Jiffy.parseFromDateTime(appointment.startTime).format(pattern: "HH:mm")} - ${Jiffy.parseFromDateTime(appointment.endTime).format(pattern: "HH:mm")}", style: const TextStyle(fontSize: 15)),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
|
||||
(context) {
|
||||
return SliverChildListDelegate(
|
||||
(context) => SliverChildListDelegate(
|
||||
[
|
||||
const Divider(),
|
||||
ListTile(
|
||||
@ -132,13 +129,12 @@ class AppointmentDetails {
|
||||
),
|
||||
DebugTile(context).jsonData(timetableData.toJson()),
|
||||
],
|
||||
);
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
static Completer deleteCustomEvent(BuildContext context, CustomTimetableEvent appointment) {
|
||||
Completer future = Completer();
|
||||
var future = Completer();
|
||||
ConfirmDialog(
|
||||
title: 'Termin löschen',
|
||||
content: "Der ${appointment.rrule.isEmpty ? "Termin" : "Serientermin"} wird unwiederruflich gelöscht.",
|
||||
@ -160,8 +156,7 @@ class AppointmentDetails {
|
||||
static void _custom(BuildContext context, TimetableProps webuntisData, CustomTimetableEvent appointment) {
|
||||
_bottomSheet(
|
||||
context,
|
||||
(context) {
|
||||
return Center(
|
||||
(context) => Center(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
@ -169,10 +164,8 @@ class AppointmentDetails {
|
||||
Text("${Jiffy.parseFromDateTime(appointment.startDate).format(pattern: "HH:mm")} - ${Jiffy.parseFromDateTime(appointment.endDate).format(pattern: "HH:mm")}", style: const TextStyle(fontSize: 15)),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
(context) {
|
||||
return SliverChildListDelegate(
|
||||
),
|
||||
(context) => SliverChildListDelegate(
|
||||
[
|
||||
const Divider(),
|
||||
Center(
|
||||
@ -212,7 +205,7 @@ class AppointmentDetails {
|
||||
builder: (context, snapshot) {
|
||||
if(appointment.rrule.isEmpty) return const Text('Keine weiteren vorkomnisse');
|
||||
if(snapshot.data == null) return const Text('...');
|
||||
RecurrenceRule rrule = RecurrenceRule.fromString(appointment.rrule);
|
||||
var rrule = RecurrenceRule.fromString(appointment.rrule);
|
||||
if(!rrule.canFullyConvertToText) return const Text('Keine genauere Angabe möglich.');
|
||||
return Text(rrule.toText(l10n: snapshot.data!));
|
||||
},
|
||||
@ -227,8 +220,7 @@ class AppointmentDetails {
|
||||
),
|
||||
DebugTile(context).jsonData(appointment.toJson()),
|
||||
]
|
||||
);
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,16 +8,12 @@ class ArbitraryAppointment {
|
||||
|
||||
ArbitraryAppointment({this.webuntis, this.custom});
|
||||
|
||||
bool hasWebuntis() {
|
||||
return webuntis != null;
|
||||
}
|
||||
bool hasWebuntis() => webuntis != null;
|
||||
|
||||
bool hasCustom() {
|
||||
return custom != null;
|
||||
}
|
||||
bool hasCustom() => custom != null;
|
||||
|
||||
void handlers(void Function(GetTimetableResponseObject webuntisData) webuntis, void Function(CustomTimetableEvent customData) custom) {
|
||||
if(hasWebuntis()) webuntis(this.webuntis!);
|
||||
if(hasCustom()) custom(this.custom!);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -29,9 +29,7 @@ class TimetableColors {
|
||||
}
|
||||
}
|
||||
|
||||
static Color getColorFromString(String color) {
|
||||
return getDisplayOptions(CustomTimetableColors.values.firstWhere((element) => element.name == color, orElse: () => TimetableColors.defaultColor)).color;
|
||||
}
|
||||
static Color getColorFromString(String color) => getDisplayOptions(CustomTimetableColors.values.firstWhere((element) => element.name == color, orElse: () => TimetableColors.defaultColor)).color;
|
||||
}
|
||||
|
||||
class ColorModeDisplay {
|
||||
@ -39,4 +37,4 @@ class ColorModeDisplay {
|
||||
final String displayName;
|
||||
|
||||
ColorModeDisplay({required this.color, required this.displayName});
|
||||
}
|
||||
}
|
||||
|
@ -51,8 +51,7 @@ class _AddCustomTimetableEventDialogState extends State<CustomTimetableEventEdit
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AlertDialog(
|
||||
Widget build(BuildContext context) => AlertDialog(
|
||||
insetPadding: const EdgeInsets.all(20),
|
||||
contentPadding: const EdgeInsets.all(10),
|
||||
title: const Text('Termin hinzufügen'),
|
||||
@ -89,7 +88,7 @@ class _AddCustomTimetableEventDialogState extends State<CustomTimetableEventEdit
|
||||
title: Text(Jiffy.parseFromDateTime(_date).yMMMd),
|
||||
subtitle: const Text('Datum'),
|
||||
onTap: () async {
|
||||
final DateTime? pickedDate = await showDatePicker(
|
||||
final pickedDate = await showDatePicker(
|
||||
context: context,
|
||||
initialDate: _date,
|
||||
firstDate: DateTime.now().subtract(const Duration(days: 30)),
|
||||
@ -185,7 +184,7 @@ class _AddCustomTimetableEventDialogState extends State<CustomTimetableEventEdit
|
||||
onPressed: () {
|
||||
if(!validate()) return;
|
||||
|
||||
CustomTimetableEvent editedEvent = CustomTimetableEvent(
|
||||
var editedEvent = CustomTimetableEvent(
|
||||
id: '',
|
||||
title: _eventName.text,
|
||||
description: _eventDescription.text,
|
||||
@ -231,5 +230,4 @@ class _AddCustomTimetableEventDialogState extends State<CustomTimetableEventEdit
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -12,8 +12,8 @@ class TimeRegionComponent extends StatefulWidget {
|
||||
class _TimeRegionComponentState extends State<TimeRegionComponent> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
String text = widget.details.region.text!;
|
||||
Color? color = widget.details.region.color;
|
||||
var text = widget.details.region.text!;
|
||||
var color = widget.details.region.color;
|
||||
|
||||
if (text == 'centerIcon') {
|
||||
return Container(
|
||||
|
@ -7,8 +7,6 @@ import 'package:provider/provider.dart';
|
||||
import 'package:syncfusion_flutter_calendar/calendar.dart';
|
||||
|
||||
import '../../../api/webuntis/queries/getHolidays/getHolidaysResponse.dart';
|
||||
import '../../../api/webuntis/queries/getRooms/getRoomsResponse.dart';
|
||||
import '../../../api/webuntis/queries/getSubjects/getSubjectsResponse.dart';
|
||||
import '../../../api/webuntis/queries/getTimetable/getTimetableResponse.dart';
|
||||
import '../../../model/timetable/timetableProps.dart';
|
||||
import '../../../storage/base/settingsProvider.dart';
|
||||
@ -53,9 +51,7 @@ class _TimetableState extends State<Timetable> {
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
||||
return Scaffold(
|
||||
Widget build(BuildContext context) => Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Stunden & Vertretungsplan'),
|
||||
actions: [
|
||||
@ -67,8 +63,7 @@ class _TimetableState extends State<Timetable> {
|
||||
),
|
||||
PopupMenuButton<CalendarActions>(
|
||||
icon: const Icon(Icons.edit_calendar_outlined),
|
||||
itemBuilder: (context) {
|
||||
return CalendarActions.values.map(
|
||||
itemBuilder: (context) => CalendarActions.values.map(
|
||||
(e) {
|
||||
String title;
|
||||
Icon icon;
|
||||
@ -89,8 +84,7 @@ class _TimetableState extends State<Timetable> {
|
||||
)
|
||||
);
|
||||
}
|
||||
).toList();
|
||||
},
|
||||
).toList(),
|
||||
onSelected: (value) {
|
||||
switch(value) {
|
||||
case CalendarActions.addEvent:
|
||||
@ -125,7 +119,7 @@ class _TimetableState extends State<Timetable> {
|
||||
|
||||
if(value.primaryLoading()) return const LoadingSpinner();
|
||||
|
||||
GetHolidaysResponse holidays = value.getHolidaysResponse;
|
||||
var holidays = value.getHolidaysResponse;
|
||||
|
||||
return RefreshIndicator(
|
||||
child: SfCalendar(
|
||||
@ -181,7 +175,6 @@ class _TimetableState extends State<Timetable> {
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
@ -190,19 +183,18 @@ class _TimetableState extends State<Timetable> {
|
||||
}
|
||||
|
||||
List<TimeRegion> _buildSpecialTimeRegions(GetHolidaysResponse holidays) {
|
||||
DateTime lastMonday = DateTime.now().subtract(const Duration(days: 14)).nextWeekday(DateTime.monday);
|
||||
DateTime firstBreak = lastMonday.copyWith(hour: 10, minute: 15);
|
||||
DateTime secondBreak = lastMonday.copyWith(hour: 13, minute: 50);
|
||||
var lastMonday = DateTime.now().subtract(const Duration(days: 14)).nextWeekday(DateTime.monday);
|
||||
var firstBreak = lastMonday.copyWith(hour: 10, minute: 15);
|
||||
var secondBreak = lastMonday.copyWith(hour: 13, minute: 50);
|
||||
|
||||
Iterable<TimeRegion> holidayList = holidays.result.map((holiday) {
|
||||
DateTime startDay = _parseWebuntisTimestamp(holiday.startDate, 0);
|
||||
int dayCount = _parseWebuntisTimestamp(holiday.endDate, 0)
|
||||
var holidayList = holidays.result.map((holiday) {
|
||||
var startDay = _parseWebuntisTimestamp(holiday.startDate, 0);
|
||||
var dayCount = _parseWebuntisTimestamp(holiday.endDate, 0)
|
||||
.difference(startDay)
|
||||
.inDays;
|
||||
List<DateTime> days = List.generate(dayCount, (index) => startDay.add(Duration(days: index)));
|
||||
var days = List<DateTime>.generate(dayCount, (index) => startDay.add(Duration(days: index)));
|
||||
|
||||
return days.map((holidayDay) {
|
||||
return TimeRegion(
|
||||
return days.map((holidayDay) => TimeRegion(
|
||||
startTime: holidayDay.copyWith(hour: 07, minute: 55),
|
||||
endTime: holidayDay.copyWith(hour: 16, minute: 30),
|
||||
text: 'holiday:${holiday.name}',
|
||||
@ -211,13 +203,10 @@ class _TimetableState extends State<Timetable> {
|
||||
.disabledColor
|
||||
.withAlpha(50),
|
||||
iconData: Icons.holiday_village_outlined
|
||||
);
|
||||
});
|
||||
));
|
||||
}).expand((e) => e);
|
||||
|
||||
bool isInHoliday(DateTime time) {
|
||||
return holidayList.any((element) => element.startTime.isSameDay(time));
|
||||
}
|
||||
bool isInHoliday(DateTime time) => holidayList.any((element) => element.startTime.isSameDay(time));
|
||||
|
||||
return [
|
||||
...holidayList,
|
||||
@ -246,34 +235,34 @@ class _TimetableState extends State<Timetable> {
|
||||
|
||||
List<GetTimetableResponseObject> _removeDuplicates(TimetableProps data, Duration maxTimeBetweenDouble) {
|
||||
|
||||
List<GetTimetableResponseObject> timetableList = data.getTimetableResponse.result.toList();
|
||||
var timetableList = data.getTimetableResponse.result.toList();
|
||||
|
||||
if(timetableList.isEmpty) return timetableList;
|
||||
|
||||
timetableList.sort((a, b) => _parseWebuntisTimestamp(a.date, a.startTime).compareTo(_parseWebuntisTimestamp(b.date, b.startTime)));
|
||||
|
||||
GetTimetableResponseObject previousElement = timetableList.first;
|
||||
var previousElement = timetableList.first;
|
||||
for(var i = 1; i < timetableList.length; i++) {
|
||||
GetTimetableResponseObject currentElement = timetableList.elementAt(i);
|
||||
var currentElement = timetableList.elementAt(i);
|
||||
|
||||
bool isSameLesson() {
|
||||
int? currentSubjectId = currentElement.su.firstOrNull?.id;
|
||||
int? previousSubjectId = previousElement.su.firstOrNull?.id;
|
||||
var currentSubjectId = currentElement.su.firstOrNull?.id;
|
||||
var previousSubjectId = previousElement.su.firstOrNull?.id;
|
||||
|
||||
if(currentSubjectId == null || previousSubjectId == null || currentSubjectId != previousSubjectId) return false;
|
||||
|
||||
int? currentRoomId = currentElement.ro.firstOrNull?.id;
|
||||
int? previousRoomId = previousElement.ro.firstOrNull?.id;
|
||||
var currentRoomId = currentElement.ro.firstOrNull?.id;
|
||||
var previousRoomId = previousElement.ro.firstOrNull?.id;
|
||||
|
||||
if(currentRoomId != previousRoomId) return false;
|
||||
|
||||
int? currentTeacherId = currentElement.te.firstOrNull?.id;
|
||||
int? previousTeacherId = previousElement.te.firstOrNull?.id;
|
||||
var currentTeacherId = currentElement.te.firstOrNull?.id;
|
||||
var previousTeacherId = previousElement.te.firstOrNull?.id;
|
||||
|
||||
if(currentTeacherId != previousTeacherId) return false;
|
||||
|
||||
String? currentStatusCode = currentElement.code;
|
||||
String? previousStatusCode = previousElement.code;
|
||||
var currentStatusCode = currentElement.code;
|
||||
var previousStatusCode = previousElement.code;
|
||||
|
||||
if(currentStatusCode != previousStatusCode) return false;
|
||||
|
||||
@ -297,20 +286,20 @@ class _TimetableState extends State<Timetable> {
|
||||
|
||||
TimetableEvents _buildTableEvents(TimetableProps data) {
|
||||
|
||||
List<GetTimetableResponseObject> timetableList = data.getTimetableResponse.result.toList();
|
||||
var timetableList = data.getTimetableResponse.result.toList();
|
||||
|
||||
if(settings.val().timetableSettings.connectDoubleLessons) {
|
||||
timetableList = _removeDuplicates(data, const Duration(minutes: 5));
|
||||
}
|
||||
|
||||
List<Appointment> appointments = timetableList.map((element) {
|
||||
var appointments = timetableList.map((element) {
|
||||
|
||||
GetRoomsResponse rooms = data.getRoomsResponse;
|
||||
GetSubjectsResponse subjects = data.getSubjectsResponse;
|
||||
var rooms = data.getRoomsResponse;
|
||||
var subjects = data.getSubjectsResponse;
|
||||
|
||||
try {
|
||||
DateTime startTime = _parseWebuntisTimestamp(element.date, element.startTime);
|
||||
DateTime endTime = _parseWebuntisTimestamp(element.date, element.endTime);
|
||||
var startTime = _parseWebuntisTimestamp(element.date, element.startTime);
|
||||
var endTime = _parseWebuntisTimestamp(element.date, element.endTime);
|
||||
return Appointment(
|
||||
id: ArbitraryAppointment(webuntis: element),
|
||||
startTime: startTime,
|
||||
@ -324,7 +313,7 @@ class _TimetableState extends State<Timetable> {
|
||||
color: _getEventColor(element, startTime, endTime),
|
||||
);
|
||||
} catch(e) {
|
||||
DateTime endTime = _parseWebuntisTimestamp(element.date, element.endTime);
|
||||
var endTime = _parseWebuntisTimestamp(element.date, element.endTime);
|
||||
return Appointment(
|
||||
id: ArbitraryAppointment(webuntis: element),
|
||||
startTime: _parseWebuntisTimestamp(element.date, element.startTime),
|
||||
@ -339,8 +328,7 @@ class _TimetableState extends State<Timetable> {
|
||||
}
|
||||
}).toList();
|
||||
|
||||
appointments.addAll(data.getCustomTimetableEventResponse.events.map((customEvent) {
|
||||
return Appointment(
|
||||
appointments.addAll(data.getCustomTimetableEventResponse.events.map((customEvent) => Appointment(
|
||||
id: ArbitraryAppointment(custom: customEvent),
|
||||
startTime: customEvent.startDate,
|
||||
endTime: customEvent.endDate,
|
||||
@ -350,20 +338,19 @@ class _TimetableState extends State<Timetable> {
|
||||
color: TimetableColors.getColorFromString(customEvent.color ?? TimetableColors.defaultColor.name),
|
||||
startTimeZone: '',
|
||||
endTimeZone: '',
|
||||
);
|
||||
}));
|
||||
)));
|
||||
|
||||
return TimetableEvents(appointments);
|
||||
}
|
||||
|
||||
DateTime _parseWebuntisTimestamp(int date, int time) {
|
||||
String timeString = time.toString().padLeft(4, '0');
|
||||
var timeString = time.toString().padLeft(4, '0');
|
||||
return DateTime.parse('$date ${timeString.substring(0, 2)}:${timeString.substring(2, 4)}');
|
||||
}
|
||||
|
||||
Color _getEventColor(GetTimetableResponseObject webuntisElement, DateTime startTime, DateTime endTime) {
|
||||
// Make element darker, when it already took place
|
||||
int alpha = endTime.isBefore(DateTime.now()) ? 100 : 255;
|
||||
var alpha = endTime.isBefore(DateTime.now()) ? 100 : 255;
|
||||
|
||||
// Cancelled
|
||||
if(webuntisElement.code == 'cancelled') return const Color(0xff000000).withAlpha(alpha);
|
||||
@ -382,7 +369,7 @@ class _TimetableState extends State<Timetable> {
|
||||
}
|
||||
|
||||
bool _isCrossedOut(CalendarAppointmentDetails calendarEntry) {
|
||||
ArbitraryAppointment appointment = calendarEntry.appointments.first.id as ArbitraryAppointment;
|
||||
var appointment = calendarEntry.appointments.first.id as ArbitraryAppointment;
|
||||
if(appointment.hasWebuntis()) {
|
||||
return appointment.webuntis!.code == 'cancelled';
|
||||
}
|
||||
|
@ -5,4 +5,4 @@ class TimetableEvents extends CalendarDataSource {
|
||||
TimetableEvents(List<Appointment> source) {
|
||||
appointments = source;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -34,8 +34,7 @@ class _ViewCustomTimetableEventsState extends State<ViewCustomTimetableEvents> {
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
Widget build(BuildContext context) => Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Eigene Termine'),
|
||||
actions: [
|
||||
@ -49,8 +48,7 @@ class _ViewCustomTimetableEventsState extends State<ViewCustomTimetableEvents> {
|
||||
if(value.primaryLoading()) return const LoadingSpinner();
|
||||
|
||||
var listView = ListView(
|
||||
children: value.getCustomTimetableEventResponse.events.map((e) {
|
||||
return ListTile(
|
||||
children: value.getCustomTimetableEventResponse.events.map((e) => ListTile(
|
||||
title: Text(e.title),
|
||||
subtitle: Text("${e.rrule.isNotEmpty ? "wiederholdend, " : ""}beginnend ${Jiffy.parseFromDateTime(e.startDate).fromNow()}"),
|
||||
leading: CenteredLeading(Icon(e.rrule.isEmpty ? Icons.event_outlined : Icons.event_repeat_outlined)),
|
||||
@ -71,8 +69,7 @@ class _ViewCustomTimetableEventsState extends State<ViewCustomTimetableEvents> {
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
)).toList(),
|
||||
);
|
||||
|
||||
var placeholder = PlaceholderView(
|
||||
@ -95,5 +92,4 @@ class _ViewCustomTimetableEventsState extends State<ViewCustomTimetableEvents> {
|
||||
);
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -14,8 +14,7 @@ import '../../storage/timetable/timetableSettings.dart';
|
||||
import '../pages/files/files.dart';
|
||||
|
||||
class DefaultSettings {
|
||||
static Settings get() {
|
||||
return Settings(
|
||||
static Settings get() => Settings(
|
||||
appTheme: ThemeMode.system,
|
||||
devToolsEnabled: false,
|
||||
gradeAveragesSettings: GradeAveragesSettings(
|
||||
@ -53,5 +52,4 @@ class DefaultSettings {
|
||||
showPerformanceOverlay: false,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -19,8 +19,7 @@ class DevToolsSettingsDialog extends StatefulWidget {
|
||||
|
||||
class _DevToolsSettingsDialogState extends State<DevToolsSettingsDialog> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
Widget build(BuildContext context) => Column(
|
||||
children: [
|
||||
ListTile(
|
||||
leading: const CenteredLeading(Icon(Icons.speed_outlined)),
|
||||
@ -95,14 +94,10 @@ class _DevToolsSettingsDialogState extends State<DevToolsSettingsDialog> {
|
||||
title: const Text('Cache-storage JSON dump'),
|
||||
subtitle: FutureBuilder(
|
||||
future: const CacheView().totalSize(),
|
||||
builder: (context, snapshot) {
|
||||
return Text("etwa ${snapshot.hasError ? "?" : snapshot.hasData ? filesize(snapshot.data) : "..."}\nLange tippen um zu löschen");
|
||||
},
|
||||
builder: (context, snapshot) => Text("etwa ${snapshot.hasError ? "?" : snapshot.hasData ? filesize(snapshot.data) : "..."}\nLange tippen um zu löschen"),
|
||||
),
|
||||
onTap: () {
|
||||
Navigator.push(context, MaterialPageRoute(builder: (context) {
|
||||
return const CacheView();
|
||||
}));
|
||||
Navigator.push(context, MaterialPageRoute(builder: (context) => const CacheView()));
|
||||
},
|
||||
onLongPress: () {
|
||||
ConfirmDialog(
|
||||
@ -116,5 +111,4 @@ class _DevToolsSettingsDialogState extends State<DevToolsSettingsDialog> {
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -11,8 +11,7 @@ class PrivacyInfo {
|
||||
PrivacyInfo({required this.providerText, required this.imprintUrl, required this.privacyUrl});
|
||||
|
||||
void showPopup(BuildContext context) {
|
||||
showDialog(context: context, builder: (context) {
|
||||
return SimpleDialog(
|
||||
showDialog(context: context, builder: (context) => SimpleDialog(
|
||||
title: Text('Betreiberinformation | $providerText'),
|
||||
children: [
|
||||
ListTile(
|
||||
@ -28,7 +27,6 @@ class PrivacyInfo {
|
||||
onTap: () => ConfirmDialog.openBrowser(context, privacyUrl),
|
||||
),
|
||||
],
|
||||
);
|
||||
});
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -36,10 +36,7 @@ class _SettingsState extends State<Settings> {
|
||||
bool developerMode = false;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
||||
return Consumer<SettingsProvider>(builder: (context, settings, child) {
|
||||
return Scaffold(
|
||||
Widget build(BuildContext context) => Consumer<SettingsProvider>(builder: (context, settings, child) => Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Einstellungen'),
|
||||
),
|
||||
@ -180,11 +177,11 @@ class _SettingsState extends State<Settings> {
|
||||
onLongPress: () => showDialog(context: context, builder: (context) => AlertDialog(
|
||||
title: const Text('Info über Push'),
|
||||
content: const SingleChildScrollView(child: Text(''
|
||||
"Aufgrund technischer Limitationen müssen Push-nachrichten über einen externen Server - hier 'mhsl.eu' (Author dieser App) - erfolgen.\n\n"
|
||||
"Aufgrund technischer Limitationen müssen Push-Nachrichten über einen externen Server - hier 'mhsl.eu' (Author dieser App) - erfolgen.\n\n"
|
||||
'Wenn Push aktiviert wird, werden deine Zugangsdaten und ein Token verschlüsselt an den Betreiber gesendet und von ihm unverschlüsselt gespeichert.\n\n'
|
||||
'Der extene Server verwendet die Zugangsdaten um sich maschinell in Nextcloud Talk anzumelden und via Websockets auf neue Nachrichten zu warten.\n\n'
|
||||
'Wenn eine neue Nachricht eintrifft wird dein Telefon via FBC-Messaging (Google Firebase Push) vom externen Server benachrichtigt.\n\n'
|
||||
'Behalte im Hinterkopf, dass deine Zugangsdaten auf einem externen Server gespeichert werden und dies trots bester Absichten ein Sicherheitsrisiko sein kann!'
|
||||
'Behalte im Hinterkopf, dass deine Zugangsdaten auf einem externen Server gespeichert werden und dies trotz bester Absichten ein Sicherheitsrisiko sein kann!'
|
||||
)),
|
||||
actions: [
|
||||
TextButton(onPressed: () => Navigator.of(context).pop(), child: const Text('Zurück'))
|
||||
@ -218,8 +215,7 @@ class _SettingsState extends State<Settings> {
|
||||
leading: const Icon(Icons.policy_outlined),
|
||||
title: const Text('Impressum & Datenschutz'),
|
||||
onTap: () {
|
||||
showDialog(context: context, builder: (context) {
|
||||
return SimpleDialog(
|
||||
showDialog(context: context, builder: (context) => SimpleDialog(
|
||||
children: [
|
||||
ListTile(
|
||||
leading: const CenteredLeading(Icon(Icons.school_outlined)),
|
||||
@ -243,8 +239,7 @@ class _SettingsState extends State<Settings> {
|
||||
onTap: () => PrivacyInfo(providerText: 'mhsl', imprintUrl: 'https://mhsl.eu/id.html', privacyUrl: 'https://mhsl.eu/datenschutz.html').showPopup(context),
|
||||
),
|
||||
],
|
||||
);
|
||||
});
|
||||
));
|
||||
},
|
||||
trailing: const Icon(Icons.arrow_right),
|
||||
),
|
||||
@ -297,7 +292,5 @@ class _SettingsState extends State<Settings> {
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
));
|
||||
}
|
||||
|
Reference in New Issue
Block a user