From d71f0d33392911413d2e6cd1faca7f50e9115b22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elias=20M=C3=BCller?= Date: Tue, 30 May 2023 21:08:11 +0200 Subject: [PATCH] Added Marianum Message pdf reader and backend --- .idea/libraries/Dart_Packages.xml | 68 ++++++++++++++++++- .idea/libraries/Flutter_Plugins.xml | 3 + .../mhsl/message/getMessages/getMessages.dart | 18 +++++ .../message/getMessages/getMessagesCache.dart | 22 ++++++ .../getMessages/getMessagesResponse.dart | 27 ++++++++ .../getMessages/getMessagesResponse.g.dart | 39 +++++++++++ lib/api/mhsl/message/messageApi.dart | 22 ++++++ lib/app.dart | 10 +-- lib/data/message/messageProps.dart | 28 ++++++++ lib/main.dart | 3 + lib/screen/pages/more/message/message.dart | 44 ++++++++++-- .../pages/more/message/messageView.dart | 62 +++++++++++++++++ macos/Flutter/GeneratedPluginRegistrant.swift | 4 ++ pubspec.yaml | 1 + .../flutter/generated_plugin_registrant.cc | 3 + windows/flutter/generated_plugins.cmake | 1 + 16 files changed, 343 insertions(+), 12 deletions(-) create mode 100644 lib/api/mhsl/message/getMessages/getMessages.dart create mode 100644 lib/api/mhsl/message/getMessages/getMessagesCache.dart create mode 100644 lib/api/mhsl/message/getMessages/getMessagesResponse.dart create mode 100644 lib/api/mhsl/message/getMessages/getMessagesResponse.g.dart create mode 100644 lib/api/mhsl/message/messageApi.dart create mode 100644 lib/data/message/messageProps.dart create mode 100644 lib/screen/pages/more/message/messageView.dart diff --git a/.idea/libraries/Dart_Packages.xml b/.idea/libraries/Dart_Packages.xml index 7cb7313..5e60838 100644 --- a/.idea/libraries/Dart_Packages.xml +++ b/.idea/libraries/Dart_Packages.xml @@ -275,6 +275,20 @@ + + + + + + + + + + + + @@ -957,7 +971,7 @@ - @@ -968,6 +982,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1195,6 +1251,8 @@ + + @@ -1284,8 +1342,14 @@ - + + + + + + + diff --git a/.idea/libraries/Flutter_Plugins.xml b/.idea/libraries/Flutter_Plugins.xml index 5480177..c288cf5 100644 --- a/.idea/libraries/Flutter_Plugins.xml +++ b/.idea/libraries/Flutter_Plugins.xml @@ -28,6 +28,9 @@ + + + diff --git a/lib/api/mhsl/message/getMessages/getMessages.dart b/lib/api/mhsl/message/getMessages/getMessages.dart new file mode 100644 index 0000000..db5a813 --- /dev/null +++ b/lib/api/mhsl/message/getMessages/getMessages.dart @@ -0,0 +1,18 @@ +import 'dart:convert'; + +import 'package:http/http.dart' as http; +import 'package:marianum_mobile/api/mhsl/message/getMessages/getMessagesResponse.dart'; +import 'package:marianum_mobile/api/mhsl/message/messageApi.dart'; + +class GetMessages extends MessageApi { + + @override + GetMessagesResponse assemble(String raw) { + return GetMessagesResponse.fromJson(jsonDecode(raw)); + } + + @override + Future request(Uri uri) { + return http.get(uri); + } +} \ No newline at end of file diff --git a/lib/api/mhsl/message/getMessages/getMessagesCache.dart b/lib/api/mhsl/message/getMessages/getMessagesCache.dart new file mode 100644 index 0000000..d009e9f --- /dev/null +++ b/lib/api/mhsl/message/getMessages/getMessagesCache.dart @@ -0,0 +1,22 @@ +import 'dart:convert'; + +import 'package:marianum_mobile/api/mhsl/message/getMessages/getMessagesResponse.dart'; +import 'package:marianum_mobile/api/requestCache.dart'; + +import 'getMessages.dart'; + +class GetMessagesCache extends RequestCache { + GetMessagesCache({onUpdate, renew}) : super(RequestCache.cacheMinute, onUpdate, renew: renew) { + start("MarianumMobile", "message"); + } + + @override + GetMessagesResponse onLocalData(String json) { + return GetMessagesResponse.fromJson(jsonDecode(json)); + } + + @override + Future onLoad() { + return GetMessages().run(); + } +} \ No newline at end of file diff --git a/lib/api/mhsl/message/getMessages/getMessagesResponse.dart b/lib/api/mhsl/message/getMessages/getMessagesResponse.dart new file mode 100644 index 0000000..314928a --- /dev/null +++ b/lib/api/mhsl/message/getMessages/getMessagesResponse.dart @@ -0,0 +1,27 @@ +import 'package:json_annotation/json_annotation.dart'; +import 'package:marianum_mobile/api/apiResponse.dart'; + +part 'getMessagesResponse.g.dart'; + +@JsonSerializable(explicitToJson: true) +class GetMessagesResponse extends ApiResponse { + String base; + Set messages; + + GetMessagesResponse(this.base, this.messages); + + factory GetMessagesResponse.fromJson(Map json) => _$GetMessagesResponseFromJson(json); + Map toJson() => _$GetMessagesResponseToJson(this); +} + +@JsonSerializable(explicitToJson: true) +class GetMessagesResponseObject { + String name; + String date; + String url; + + GetMessagesResponseObject(this.name, this.date, this.url); + + factory GetMessagesResponseObject.fromJson(Map json) => _$GetMessagesResponseObjectFromJson(json); + Map toJson() => _$GetMessagesResponseObjectToJson(this); +} diff --git a/lib/api/mhsl/message/getMessages/getMessagesResponse.g.dart b/lib/api/mhsl/message/getMessages/getMessagesResponse.g.dart new file mode 100644 index 0000000..50498ba --- /dev/null +++ b/lib/api/mhsl/message/getMessages/getMessagesResponse.g.dart @@ -0,0 +1,39 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'getMessagesResponse.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +GetMessagesResponse _$GetMessagesResponseFromJson(Map json) => + GetMessagesResponse( + json['base'] as String, + (json['messages'] as List) + .map((e) => + GetMessagesResponseObject.fromJson(e as Map)) + .toSet(), + ); + +Map _$GetMessagesResponseToJson( + GetMessagesResponse instance) => + { + 'base': instance.base, + 'messages': instance.messages.map((e) => e.toJson()).toList(), + }; + +GetMessagesResponseObject _$GetMessagesResponseObjectFromJson( + Map json) => + GetMessagesResponseObject( + json['name'] as String, + json['date'] as String, + json['url'] as String, + ); + +Map _$GetMessagesResponseObjectToJson( + GetMessagesResponseObject instance) => + { + 'name': instance.name, + 'date': instance.date, + 'url': instance.url, + }; diff --git a/lib/api/mhsl/message/messageApi.dart b/lib/api/mhsl/message/messageApi.dart new file mode 100644 index 0000000..e85100d --- /dev/null +++ b/lib/api/mhsl/message/messageApi.dart @@ -0,0 +1,22 @@ +import 'package:http/http.dart' as http; +import '../../apiError.dart'; +import '../../apiRequest.dart'; + +abstract class MessageApi extends ApiRequest { + String path = "https://mhsl.eu/marianum/marianummobile/message/messages.json"; + http.Response? response; + + Future? request(Uri uri); + T assemble(String raw); + + Future run() async { + Uri endpoint = Uri.parse(path); + + http.Response? data = await request(endpoint); + if(data == null) { + throw ApiError("Request could not be dispatched!"); + } + + return assemble(data.body); + } +} \ No newline at end of file diff --git a/lib/app.dart b/lib/app.dart index d2e9dd9..8973470 100644 --- a/lib/app.dart +++ b/lib/app.dart @@ -49,11 +49,11 @@ class _AppState extends State { border: Border.symmetric(vertical: BorderSide.none, horizontal: BorderSide(color: Colors.grey, width: 1)) ), screenTransitionAnimation: const ScreenTransitionAnimation(animateTabTransition: true, curve: Curves.ease, duration: Duration(milliseconds: 200)), - screens: [ - const Timetable(), - const ChatList(), - Files(const []), - const Overhang(), + screens: const [ + Timetable(), + ChatList(), + Files([]), + Overhang(), ], items: [ PersistentBottomNavBarItem( diff --git a/lib/data/message/messageProps.dart b/lib/data/message/messageProps.dart new file mode 100644 index 0000000..2776e31 --- /dev/null +++ b/lib/data/message/messageProps.dart @@ -0,0 +1,28 @@ + +import 'package:marianum_mobile/api/mhsl/message/getMessages/getMessagesCache.dart'; +import 'package:marianum_mobile/api/mhsl/message/getMessages/getMessagesResponse.dart'; +import 'package:marianum_mobile/data/dataHolder.dart'; + +import '../../api/apiResponse.dart'; + +class MessageProps extends DataHolder { + GetMessagesResponse? _getMessagesResponse; + GetMessagesResponse get getMessagesResponse => _getMessagesResponse!; + + @override + List properties() { + return [_getMessagesResponse]; + } + + @override + void run({renew}) { + GetMessagesCache( + renew: renew, + onUpdate: (GetMessagesResponse data) => { + _getMessagesResponse = data, + notifyListeners(), + } + ); + } + +} \ No newline at end of file diff --git a/lib/main.dart b/lib/main.dart index d3472f2..05147bc 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -18,6 +18,7 @@ import 'data/chatList/chatListProps.dart'; import 'data/chatList/chatProps.dart'; import 'data/accountModel.dart'; import 'data/files/filesProps.dart'; +import 'data/message/messageProps.dart'; Future main() async { WidgetsFlutterBinding.ensureInitialized(); @@ -38,6 +39,8 @@ Future main() async { ChangeNotifierProvider(create: (context) => ChatListProps()), ChangeNotifierProvider(create: (context) => ChatProps()), ChangeNotifierProvider(create: (context) => FilesProps()), + + ChangeNotifierProvider(create: (context) => MessageProps()), ], child: const Main(), ) diff --git a/lib/screen/pages/more/message/message.dart b/lib/screen/pages/more/message/message.dart index 5d28eb2..e000ff3 100644 --- a/lib/screen/pages/more/message/message.dart +++ b/lib/screen/pages/more/message/message.dart @@ -1,5 +1,10 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; +import 'package:marianum_mobile/api/mhsl/message/getMessages/getMessagesResponse.dart'; +import 'package:marianum_mobile/screen/pages/more/message/messageView.dart'; +import 'package:provider/provider.dart'; + +import '../../../../data/message/messageProps.dart'; class Message extends StatefulWidget { const Message({Key? key}) : super(key: key); @@ -9,17 +14,46 @@ class Message extends StatefulWidget { } class _MessageState extends State { + @override + void initState() { + WidgetsBinding.instance.addPostFrameCallback((timeStamp) { + Provider.of(context, listen: false).run(); + }); + super.initState(); + } + @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text("Marianum Message"), ), - body: ListView.builder(itemBuilder: (context, index) { - return ListTile( - leading: const Icon(Icons.newspaper), - title: Text(index.toString()), - trailing: const Icon(Icons.arrow_right), + body: Consumer(builder: (context, value, child) { + if(value.primaryLoading()) return const Center(child: CircularProgressIndicator()); + + return RefreshIndicator( + child: ListView.builder( + itemCount: value.getMessagesResponse.messages.length, + itemBuilder: (context, index) { + GetMessagesResponseObject message = value.getMessagesResponse.messages.toList()[index]; + return ListTile( + leading: const Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [Icon(Icons.newspaper)], + ), + title: Text(message.name, overflow: TextOverflow.ellipsis), + subtitle: Text("vom ${message.date}"), + trailing: const Icon(Icons.arrow_right), + onTap: () { + Navigator.push(context, MaterialPageRoute(builder: (context) => MessageView(basePath: value.getMessagesResponse.base, message: message))); + }, + ); + } + ), + onRefresh: () { + Provider.of(context, listen: false).run(renew: true); + return Future.delayed(const Duration(seconds: 3)); + }, ); }), ); diff --git a/lib/screen/pages/more/message/messageView.dart b/lib/screen/pages/more/message/messageView.dart new file mode 100644 index 0000000..da42385 --- /dev/null +++ b/lib/screen/pages/more/message/messageView.dart @@ -0,0 +1,62 @@ +import 'package:flutter/material.dart'; +import 'package:marianum_mobile/api/mhsl/message/getMessages/getMessagesResponse.dart'; +import 'package:syncfusion_flutter_pdfviewer/pdfviewer.dart'; +import 'package:url_launcher/url_launcher.dart'; + +class MessageView extends StatefulWidget { + final String basePath; + final GetMessagesResponseObject message; + const MessageView({Key? key, required this.basePath, required this.message}) : super(key: key); + + @override + State createState() => _MessageViewState(); +} + +class _MessageViewState extends State { + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text(widget.message.name), + ), + body: SfPdfViewer.network( + widget.basePath + widget.message.url, + enableHyperlinkNavigation: true, + onDocumentLoadFailed: (PdfDocumentLoadFailedDetails e) { + Navigator.of(context).pop(); + showDialog(context: context, builder: (context) { + return AlertDialog( + title: const Text("Fehler beim öffnen"), + content: Text("Dokument '${widget.message.name}' konnte nicht geladen werden:\n${e.description}"), + actions: [ + TextButton(onPressed: () { + Navigator.of(context).pop(); + }, child: const Text("Ok")) + ], + ); + }); + }, + onHyperlinkClicked: (PdfHyperlinkClickedDetails e) { + showDialog( + context: context, + builder: (context) { + return AlertDialog( + title: const Text("Link öffnen"), + content: Text("Möchtest du den folgenden Link öffnen?\n${e.uri}"), + actions: [ + TextButton(onPressed: () { + Navigator.of(context).pop(); + }, child: const Text("Abbrechen")), + TextButton(onPressed: () { + launchUrl(Uri.parse(e.uri), mode: LaunchMode.externalApplication); + }, child: const Text("Öffnen")), + ], + ); + }, + ); + }, + ), + ); + } +} diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 33cba8b..7026e66 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -5,16 +5,20 @@ import FlutterMacOS import Foundation +import device_info_plus import package_info import path_provider_foundation import shared_preferences_foundation import sqflite +import syncfusion_pdfviewer_macos import url_launcher_macos func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { + DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin")) FLTPackageInfoPlugin.register(with: registry.registrar(forPlugin: "FLTPackageInfoPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin")) + SyncfusionFlutterPdfViewerPlugin.register(with: registry.registrar(forPlugin: "SyncfusionFlutterPdfViewerPlugin")) UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) } diff --git a/pubspec.yaml b/pubspec.yaml index 1ad6191..bb78d51 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -79,6 +79,7 @@ dependencies: syncfusion_flutter_calendar: ^21.2.4 async: ^2.11.0 animated_digit: ^3.2.1 + syncfusion_flutter_pdfviewer: ^21.2.8 dev_dependencies: flutter_test: diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index 4f78848..9871f6b 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -6,9 +6,12 @@ #include "generated_plugin_registrant.h" +#include #include void RegisterPlugins(flutter::PluginRegistry* registry) { + SyncfusionPdfviewerWindowsPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("SyncfusionPdfviewerWindowsPlugin")); UrlLauncherWindowsRegisterWithRegistrar( registry->GetRegistrarForPlugin("UrlLauncherWindows")); } diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index 88b22e5..2487723 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -3,6 +3,7 @@ # list(APPEND FLUTTER_PLUGIN_LIST + syncfusion_pdfviewer_windows url_launcher_windows )