diff --git a/.idea/libraries/Dart_Packages.xml b/.idea/libraries/Dart_Packages.xml index 8959fcc..d1b4b25 100644 --- a/.idea/libraries/Dart_Packages.xml +++ b/.idea/libraries/Dart_Packages.xml @@ -5,14 +5,14 @@ - - @@ -128,6 +128,27 @@ + + + + + + + + + + + + + + + + + + @@ -254,6 +275,20 @@ + + + + + + + + + + + + @@ -464,6 +499,13 @@ + + + + + + @@ -481,42 +523,49 @@ - - - - - - + + + + + + @@ -537,7 +586,7 @@ - @@ -597,59 +646,59 @@ - + - - - - - - - - @@ -702,6 +751,20 @@ + + + + + + + + + + + + @@ -730,6 +793,13 @@ + + + + + + @@ -775,56 +845,63 @@ - - - - - - - - + + + + + + @@ -856,13 +933,6 @@ - - - - - - @@ -894,10 +964,9 @@ - - - + + @@ -914,6 +983,9 @@ + + + @@ -931,6 +1003,8 @@ + + @@ -958,17 +1032,19 @@ + - - - - - - + + + + + + + - + @@ -977,38 +1053,42 @@ - - - - - - - - + + + + + + + + + + + - - - - - - - - + + + + + + + + + diff --git a/.idea/libraries/Flutter_Plugins.xml b/.idea/libraries/Flutter_Plugins.xml index 9f76510..29ea25b 100644 --- a/.idea/libraries/Flutter_Plugins.xml +++ b/.idea/libraries/Flutter_Plugins.xml @@ -1,25 +1,26 @@ - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + diff --git a/lib/api/marianumcloud/talk/chat/richObjectStringProcessor.dart b/lib/api/marianumcloud/talk/chat/richObjectStringProcessor.dart index 4bef792..27241a9 100644 --- a/lib/api/marianumcloud/talk/chat/richObjectStringProcessor.dart +++ b/lib/api/marianumcloud/talk/chat/richObjectStringProcessor.dart @@ -1,7 +1,12 @@ +import 'dart:convert'; + +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:flutter/material.dart'; import 'package:marianum_mobile/api/marianumcloud/talk/chat/getChatResponse.dart'; +import 'package:shared_preferences/shared_preferences.dart'; class RichObjectStringProcessor { - static String parse(String message, Map? data) { + static String parseTextPreview(String message, Map? data) { if(data == null) return message; data.forEach((key, value) { @@ -10,4 +15,31 @@ class RichObjectStringProcessor { return message; } + + static Future parseAnyToWidget(String message, Map? data) async { + if(data == null) return Text(message); + if(!message.contains(RegExp("{file}"))) return Text(parseTextPreview(message, data)); + + SharedPreferences preferences = await SharedPreferences.getInstance(); + + Widget? back; + data.forEach((key, value) { + back = CachedNetworkImage( + errorWidget: (context, url, error) { + return Text("Datei: ${value.name}", style: const TextStyle(fontWeight: FontWeight.bold)); + }, + alignment: Alignment.center, + placeholder: (context, url) { + return const Padding(padding: EdgeInsets.all(10), child: CircularProgressIndicator()); + }, + fadeInDuration: const Duration(seconds: 1), + imageUrl: "https://cloud.marianum-fulda.de/core/preview?fileId=${value.id}&x=110&y=-1&a=1", + httpHeaders: { + "Authorization": "Basic ${base64.encode(utf8.encode("${preferences.getString("username")}:${preferences.getString("password")}"))}" + }, + ); + }); + + return back ?? Text("NOPE"); + } } \ No newline at end of file diff --git a/lib/screen/pages/talk/chatList.dart b/lib/screen/pages/talk/chatList.dart index 65d1ad6..d5d2d71 100644 --- a/lib/screen/pages/talk/chatList.dart +++ b/lib/screen/pages/talk/chatList.dart @@ -56,7 +56,7 @@ class _ChatListState extends State { chats.add(ListTile( title: Text(chatRoom.displayName), - subtitle: Text("${Jiffy.unixFromSecondsSinceEpoch(chatRoom.lastMessage.timestamp).fromNow()}: ${RichObjectStringProcessor.parse(chatRoom.lastMessage.message.replaceAll("\n", " "), chatRoom.lastMessage.messageParameters)}", overflow: TextOverflow.ellipsis), + subtitle: Text("${Jiffy.unixFromSecondsSinceEpoch(chatRoom.lastMessage.timestamp).fromNow()}: ${RichObjectStringProcessor.parseTextPreview(chatRoom.lastMessage.message.replaceAll("\n", " "), chatRoom.lastMessage.messageParameters)}", overflow: TextOverflow.ellipsis), trailing: Visibility( visible: chatRoom.unreadMessages > 0, child: Container( @@ -83,7 +83,7 @@ class _ChatListState extends State { onTap: () async { Navigator.of(context).push(MaterialPageRoute(builder: (context) { return ChatView( - user: chatRoom, + room: chatRoom, selfId: username, avatar: circleAvatar, ); diff --git a/lib/screen/pages/talk/chatView.dart b/lib/screen/pages/talk/chatView.dart index 575851c..1fabbf4 100644 --- a/lib/screen/pages/talk/chatView.dart +++ b/lib/screen/pages/talk/chatView.dart @@ -10,11 +10,11 @@ import 'package:marianum_mobile/data/chatList/chatProps.dart'; import 'package:provider/provider.dart'; class ChatView extends StatefulWidget { - final GetRoomResponseObject user; + final GetRoomResponseObject room; final String selfId; final CircleAvatar avatar; - const ChatView({Key? key, required this.user, required this.selfId, required this.avatar}) : super(key: key); + const ChatView({Key? key, required this.room, required this.selfId, required this.avatar}) : super(key: key); @override State createState() => _ChatViewState(); @@ -56,7 +56,7 @@ class _ChatViewState extends State { super.initState(); WidgetsBinding.instance.addPostFrameCallback((timeStamp) { - Provider.of(context, listen: false).setQueryToken(widget.user.token); + Provider.of(context, listen: false).setQueryToken(widget.room.token); }); } @@ -67,11 +67,13 @@ class _ChatViewState extends State { List messages = List.empty(growable: true); if(!data.primaryLoading()) { - bool showMetadata = true; + bool showActorDisplayName = true; + bool showBubbleTime = true; data.getChatResponse.sortByTimestamp().forEach((element) { - showMetadata = element.messageType == GetRoomResponseObjectMessageType.comment; + showActorDisplayName = element.messageType == GetRoomResponseObjectMessageType.comment && widget.room.type != GetRoomResponseObjectConversationType.oneToOne; + showBubbleTime = element.messageType != GetRoomResponseObjectMessageType.system; BubbleStyle currentStyle; if(element.messageType == GetRoomResponseObjectMessageType.comment) { @@ -84,38 +86,85 @@ class _ChatViewState extends State { currentStyle = styleSystem; } + var _actorTextStyle = TextStyle(color: Theme.of(context).primaryColor, fontWeight: FontWeight.bold); messages.add(Bubble( margin: BubbleEdges.only(bottom: element == data.getChatResponse.sortByTimestamp().last ? 20 : 0), style: currentStyle, - child: Stack( - children: [ - Visibility( - visible: showMetadata, - child: Positioned( - top: 0, - left: 0, - child: Text(element.actorDisplayName, style: TextStyle(fontWeight: FontWeight.bold, color: Theme.of(context).primaryColor)), + child: Container( + constraints: BoxConstraints( + maxWidth: MediaQuery.of(context).size.width * 0.9, + minWidth: _textSize(element.actorDisplayName, _actorTextStyle).width, + ), + child: Stack( + children: [ + Padding( + padding: EdgeInsets.only(bottom: showBubbleTime ? 18 : 0, top: showActorDisplayName ? 18 : 0), + child: FutureBuilder( + future: RichObjectStringProcessor.parseAnyToWidget(element.message, element.messageParameters), + builder: (context, snapshot) { + if(!snapshot.hasData) return const CircularProgressIndicator(); + return snapshot.data ?? const Icon(Icons.error); + }, + ) ), - ), - Padding( - padding: EdgeInsets.symmetric(vertical: showMetadata ? 18 : 0), - child: Text(RichObjectStringProcessor.parse(element.message, element.messageParameters)), - ), - Visibility( - visible: showMetadata, - child: Positioned( - bottom: 0, - right: 0, - child: Text( - "${Jiffy.unixFromSecondsSinceEpoch(element.timestamp).yMMMMd} - ${Jiffy.unixFromSecondsSinceEpoch(element.timestamp).format("HH:mm")}", - style: TextStyle(color: Theme.of(context).disabledColor), + Visibility( + visible: showActorDisplayName, + child: Positioned( + top: 0, + left: 0, + child: Text( + element.actorDisplayName, + textAlign: TextAlign.start, + style: _actorTextStyle, + ), ), ), - ), - ], + Visibility( + visible: showBubbleTime, + child: Positioned( + bottom: 0, + right: 0, + child: Text( + Jiffy.unixFromSecondsSinceEpoch(element.timestamp).format("HH:mm"), + textAlign: TextAlign.end, + style: const TextStyle(color: Colors.grey, fontSize: 12), + ), + ), + ), + ], + ), ), + + + // Stack( + // children: [ + // Visibility( + // visible: showMetadata, + // child: Positioned( + // top: 0, + // left: 0, + // child: Expanded(child: Text(element.actorDisplayName, style: TextStyle(fontWeight: FontWeight.bold, color: Theme.of(context).primaryColor))), + // ), + // ), + // Padding( + // padding: EdgeInsets.symmetric(vertical: showMetadata ? 18 : 0), + // child: Text(RichObjectStringProcessor.parse(element.message, element.messageParameters)), + // ), + // Visibility( + // visible: showMetadata, + // child: Positioned( + // bottom: 0, + // right: 0, + // child: Text( + // "${Jiffy.unixFromSecondsSinceEpoch(element.timestamp).yMMMMd} - ${Jiffy.unixFromSecondsSinceEpoch(element.timestamp).format("HH:mm")}", + // style: TextStyle(color: Theme.of(context).disabledColor), + // ), + // ), + // ), + // ], + // ), )); }); } @@ -127,7 +176,9 @@ class _ChatViewState extends State { children: [ widget.avatar, const SizedBox(width: 10), - Text(widget.user.displayName, overflow: TextOverflow.ellipsis, maxLines: 1), + Expanded( + child: Text(widget.room.displayName, overflow: TextOverflow.ellipsis, maxLines: 1), + ) ], ), ), @@ -175,7 +226,7 @@ class _ChatViewState extends State { setState(() { sending = true; }); - SendMessage(widget.user.token, SendMessageParams(_textBoxController.text)).run().then((value) => { + SendMessage(widget.room.token, SendMessageParams(_textBoxController.text)).run().then((value) => { Provider.of(context, listen: false).run(), _textBoxController.text = "", setState(() { @@ -196,4 +247,13 @@ class _ChatViewState extends State { }, ); } + + Size _textSize(String text, TextStyle style) { + final TextPainter textPainter = TextPainter( + text: TextSpan(text: text, style: style), + maxLines: 1, + textDirection: TextDirection.ltr) + ..layout(minWidth: 0, maxWidth: double.infinity); + return textPainter.size; + } } diff --git a/lib/screen/settings/debug/debugOverview.dart b/lib/screen/settings/debug/debugOverview.dart index 00366be..d19e845 100644 --- a/lib/screen/settings/debug/debugOverview.dart +++ b/lib/screen/settings/debug/debugOverview.dart @@ -31,6 +31,7 @@ class _DebugOverviewState extends State { leading: const Icon(Icons.delete_forever), title: const Text("Cache löschen"), onTap: () { + PaintingBinding.instance.imageCache.clear(); storage.collection("MarianumMobile").delete().then((value) => { Navigator.pop(context) }); diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index b19945c..63ad0d1 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -7,10 +7,12 @@ import Foundation import path_provider_foundation import shared_preferences_foundation +import sqflite import url_launcher_macos func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) + SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin")) UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) } diff --git a/pubspec.yaml b/pubspec.yaml index ad2297b..524af28 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -47,16 +47,13 @@ dependencies: json_annotation: ^4.8.0 localstore: ^1.2.3 intl: ^0.17.0 - webdav: #^1.0.9 - git: - url: https://github.com/timestee/dart-webdav.git - ref: 1a70d3f7236484ed170f688980020b344d729d39 nextcloud: git: url: https://github.com/provokateurin/nextcloud-neon path: packages/nextcloud flutter_launcher_icons: ^0.11.0 pretty_json: ^2.0.0 + cached_network_image: ^3.2.3 dependency_overrides: xml: ^6.2.2