diff --git a/ios/Podfile.lock b/ios/Podfile.lock index cad6d16..4531c84 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -211,6 +211,9 @@ PODS: - shared_preferences_foundation (0.0.1): - Flutter - FlutterMacOS + - sqflite_darwin (0.0.4): + - Flutter + - FlutterMacOS - SwiftyGif (5.4.5) - url_launcher_ios (0.0.1): - Flutter @@ -256,6 +259,7 @@ DEPENDENCIES: - screen_brightness_ios (from `.symlinks/plugins/screen_brightness_ios/ios`) - share_plus (from `.symlinks/plugins/share_plus/ios`) - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`) + - sqflite_darwin (from `.symlinks/plugins/sqflite_darwin/darwin`) - url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`) - video_compress (from `.symlinks/plugins/video_compress/ios`) - volume_controller (from `.symlinks/plugins/volume_controller/ios`) @@ -343,6 +347,8 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/share_plus/ios" shared_preferences_foundation: :path: ".symlinks/plugins/shared_preferences_foundation/darwin" + sqflite_darwin: + :path: ".symlinks/plugins/sqflite_darwin/darwin" url_launcher_ios: :path: ".symlinks/plugins/url_launcher_ios/ios" video_compress: @@ -401,6 +407,7 @@ SPEC CHECKSUMS: SDWebImage: 73c6079366fea25fa4bb9640d5fb58f0893facd8 share_plus: 8b6f8b3447e494cca5317c8c3073de39b3600d1f shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78 + sqflite_darwin: 5a7236e3b501866c1c9befc6771dfd73ffb8702d SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4 url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe video_compress: fce97e4fb1dfd88175aa07d2ffc8a2f297f87fbe diff --git a/lib/widgets/chat/chat_message.dart b/lib/widgets/chat/chat_message.dart index 93684e6..de1f150 100644 --- a/lib/widgets/chat/chat_message.dart +++ b/lib/widgets/chat/chat_message.dart @@ -109,11 +109,11 @@ class ChatMessage extends StatelessWidget { AccountImage( content: user?.avatar, radius: 12, - ).padding(right: 6), + ).padding(right: 8), Text( (data.sender.nick?.isNotEmpty ?? false) ? data.sender.nick! : user?.nick ?? 'unknown', ).bold(), - const Gap(6), + const Gap(8), Text( dateFormatter.format(data.createdAt.toLocal()), ).fontSize(13), diff --git a/lib/widgets/chat/chat_message_input.dart b/lib/widgets/chat/chat_message_input.dart index 697ca31..0aa19ab 100644 --- a/lib/widgets/chat/chat_message_input.dart +++ b/lib/widgets/chat/chat_message_input.dart @@ -161,75 +161,84 @@ class ChatMessageInputState extends State { .animate(const Duration(milliseconds: 300), Curves.fastEaseInToSlowEaseOut), SingleChildScrollView( physics: const NeverScrollableScrollPhysics(), - child: Padding( - padding: _replyingMessage != null ? const EdgeInsets.only(top: 8) : EdgeInsets.zero, - child: _replyingMessage != null - ? MaterialBanner( - padding: const EdgeInsets.only(left: 16.0), - leading: const Icon(Symbols.reply), - backgroundColor: Colors.transparent, - content: SingleChildScrollView( - physics: const NeverScrollableScrollPhysics(), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - if (_replyingMessage?.body['text'] != null) - MarkdownTextContent( - content: _replyingMessage?.body['text'], - ), - ], + child: _replyingMessage != null + ? Container( + padding: const EdgeInsets.only(left: 16, right: 16), + decoration: BoxDecoration( + borderRadius: const BorderRadius.all(Radius.circular(8)), + border: Border( + bottom: BorderSide( + color: Theme.of(context).dividerColor, + width: 1 / MediaQuery.of(context).devicePixelRatio, ), ), - actions: [ - TextButton( + ), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + const Icon(Symbols.reply, size: 20), + const Gap(8), + Expanded( + child: Text( + _replyingMessage?.body['text'] ?? '${_replyingMessage?.sender.nick}', + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + ), + const Gap(16), + InkWell( child: Text('cancel'.tr()), - onPressed: () { + onTap: () { setState(() => _replyingMessage = null); }, ), ], - ) - : const SizedBox.shrink(), - ), + ).padding(vertical: 8), + ) + : const SizedBox.shrink(), ) - .height(_replyingMessage != null ? 54 + 8 : 0, animate: true) + .height(_replyingMessage != null ? 38 : 0, animate: true) .animate(const Duration(milliseconds: 300), Curves.fastEaseInToSlowEaseOut), SingleChildScrollView( physics: const NeverScrollableScrollPhysics(), - child: Padding( - padding: _editingMessage != null ? const EdgeInsets.only(top: 8) : EdgeInsets.zero, - child: _editingMessage != null - ? MaterialBanner( - padding: const EdgeInsets.only(left: 16.0), - leading: const Icon(Symbols.edit), - backgroundColor: Colors.transparent, - content: SingleChildScrollView( - physics: const NeverScrollableScrollPhysics(), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - if (_editingMessage?.body['text'] != null) - MarkdownTextContent( - content: _editingMessage?.body['text'], - ), - ], + child: _editingMessage != null + ? Container( + padding: const EdgeInsets.only(left: 16, right: 16), + decoration: BoxDecoration( + borderRadius: const BorderRadius.all(Radius.circular(8)), + border: Border( + bottom: BorderSide( + color: Theme.of(context).dividerColor, + width: 1 / MediaQuery.of(context).devicePixelRatio, ), ), - actions: [ - TextButton( + ), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + const Icon(Symbols.edit, size: 20), + const Gap(8), + Expanded( + child: Text( + _editingMessage?.body['text'] ?? '${_editingMessage?.sender.nick}', + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + ), + const Gap(16), + InkWell( child: Text('cancel'.tr()), - onPressed: () { + onTap: () { + _contentController.clear(); setState(() => _editingMessage = null); }, ), ], - ) - : const SizedBox.shrink(), - ), + ).padding(vertical: 8), + ) + : const SizedBox.shrink(), ) - .height(_editingMessage != null ? 54 + 8 : 0, animate: true) + .height(_editingMessage != null ? 38 : 0, animate: true) .animate(const Duration(milliseconds: 300), Curves.fastEaseInToSlowEaseOut), SizedBox( height: 56, diff --git a/lib/widgets/universal_image.dart b/lib/widgets/universal_image.dart index b0229fb..4a4aa17 100644 --- a/lib/widgets/universal_image.dart +++ b/lib/widgets/universal_image.dart @@ -1,4 +1,4 @@ -import 'package:extended_image/extended_image.dart'; +import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:material_symbols_icons/symbols.dart'; @@ -7,6 +7,7 @@ import 'package:styled_widget/styled_widget.dart'; import 'package:flutter_animate/flutter_animate.dart'; // Keep this import to make the web image render work +import 'package:cached_network_image_platform_interface/cached_network_image_platform_interface.dart'; import 'package:surface/providers/config.dart'; class UniversalImage extends StatelessWidget { @@ -33,54 +34,64 @@ class UniversalImage extends StatelessWidget { @override Widget build(BuildContext context) { - final quality = filterQuality ?? context.read().imageQuality; + final devicePixelRatio = MediaQuery.of(context).devicePixelRatio; + final double? resizeHeight = cacheHeight != null ? (cacheHeight! * devicePixelRatio) : null; + final double? resizeWidth = cacheWidth != null ? (cacheWidth! * devicePixelRatio) : null; - return ExtendedImage.network( - url, + return Image( + filterQuality: filterQuality ?? context.read().imageQuality, + image: kIsWeb + ? UniversalImage.provider(url) + : ResizeImage( + UniversalImage.provider(url), + width: resizeWidth?.round(), + height: resizeHeight?.round(), + policy: ResizeImagePolicy.fit, + ), width: width, height: height, fit: fit, - cache: true, - compressionRatio: kIsWeb ? 1 : switch(quality) { - FilterQuality.high => 1, - FilterQuality.medium => 0.75, - FilterQuality.low => 0.5, - FilterQuality.none => 0.25, - }, - filterQuality: quality, - enableLoadState: true, - retries: 3, - loadStateChanged: (ExtendedImageState state) { - if (state.extendedImageLoadState == LoadState.completed) { - return state.completedWidget; - } else if (state.extendedImageLoadState == LoadState.failed) { - return Material( - color: Theme.of(context).colorScheme.surface, - child: Container( - constraints: const BoxConstraints(maxWidth: 280), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - AnimateWidgetExtensions(Icon(Symbols.close, size: 24)) - .animate(onPlay: (e) => e.repeat(reverse: true)) - .fade(duration: 500.ms), - Text( - state.lastException.toString(), - textAlign: TextAlign.center, + loadingBuilder: noProgressIndicator + ? null + : (BuildContext context, Widget child, ImageChunkEvent? loadingProgress) { + if (loadingProgress == null) return child; + return Center( + child: TweenAnimationBuilder( + tween: Tween( + begin: 0, + end: loadingProgress.expectedTotalBytes != null + ? loadingProgress.cumulativeBytesLoaded / loadingProgress.expectedTotalBytes! + : 0, ), - ], - ).center(), - ), - ); - } - return Center( - child: CircularProgressIndicator( - value: state.loadingProgress != null - ? state.loadingProgress!.cumulativeBytesLoaded / state.loadingProgress!.expectedTotalBytes! - : null, - ), - ); - }, + duration: const Duration(milliseconds: 300), + builder: (context, value, _) => CircularProgressIndicator( + value: loadingProgress.expectedTotalBytes != null ? value.toDouble() : null, + ), + ), + ); + }, + errorBuilder: noErrorWidget + ? null + : (context, error, stackTrace) { + return Material( + color: Theme.of(context).colorScheme.surface, + child: Container( + constraints: const BoxConstraints(maxWidth: 280), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + AnimateWidgetExtensions(Icon(Symbols.close, size: 24)) + .animate(onPlay: (e) => e.repeat(reverse: true)) + .fade(duration: 500.ms), + Text( + error.toString(), + textAlign: TextAlign.center, + ), + ], + ).center(), + ), + ); + }, ); } @@ -88,10 +99,9 @@ class UniversalImage extends StatelessWidget { // This place used to use network image or cached network image depending on the platform. // But now the cached network image is working on every platform. // So we just use it now. - return ExtendedNetworkImageProvider( + return CachedNetworkImageProvider( url, - cache: true, - retries: 3, + imageRenderMethodForWeb: ImageRenderMethodForWeb.HttpGet, ); } } diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 33f45a5..9cdba3d 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -26,6 +26,7 @@ import path_provider_foundation import screen_brightness_macos import share_plus import shared_preferences_foundation +import sqflite_darwin import url_launcher_macos import video_compress import wakelock_plus @@ -52,6 +53,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { ScreenBrightnessMacosPlugin.register(with: registry.registrar(forPlugin: "ScreenBrightnessMacosPlugin")) SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) + SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin")) UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) VideoCompressPlugin.register(with: registry.registrar(forPlugin: "VideoCompressPlugin")) WakelockPlusMacosPlugin.register(with: registry.registrar(forPlugin: "WakelockPlusMacosPlugin")) diff --git a/pubspec.lock b/pubspec.lock index 48daf85..01d82d8 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -182,6 +182,30 @@ packages: url: "https://pub.dev" source: hosted version: "8.9.3" + cached_network_image: + dependency: "direct main" + description: + name: cached_network_image + sha256: "7c1183e361e5c8b0a0f21a28401eecdbde252441106a9816400dd4c2b2424916" + url: "https://pub.dev" + source: hosted + version: "3.4.1" + cached_network_image_platform_interface: + dependency: transitive + description: + name: cached_network_image_platform_interface + sha256: "35814b016e37fbdc91f7ae18c8caf49ba5c88501813f73ce8a07027a395e2829" + url: "https://pub.dev" + source: hosted + version: "4.1.1" + cached_network_image_web: + dependency: transitive + description: + name: cached_network_image_web + sha256: "980842f4e8e2535b8dbd3d5ca0b1f0ba66bf61d14cc3a17a9b4788a3685ba062" + url: "https://pub.dev" + source: hosted + version: "1.3.1" cassowary: dependency: transitive description: @@ -430,22 +454,6 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.7" - extended_image: - dependency: "direct main" - description: - name: extended_image - sha256: "93890a88d89ce017789f6c031c32ad8d2c685f1a5c25c169550746d973ca5e44" - url: "https://pub.dev" - source: hosted - version: "9.0.9" - extended_image_library: - dependency: transitive - description: - name: extended_image_library - sha256: "9a94ec9314aa206cfa35f16145c3cd6e2c924badcc670eaaca8a3a8063a68cd7" - url: "https://pub.dev" - source: hosted - version: "4.0.5" fading_edge_scrollview: dependency: transitive description: @@ -635,6 +643,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.2.2" + flutter_cache_manager: + dependency: transitive + description: + name: flutter_cache_manager + sha256: "400b6592f16a4409a7f2bb929a9a7e38c72cceb8ffb99ee57bbf2cb2cecf8386" + url: "https://pub.dev" + source: hosted + version: "3.4.1" flutter_colorpicker: dependency: "direct main" description: @@ -874,14 +890,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.2.2" - http_client_helper: - dependency: transitive - description: - name: http_client_helper - sha256: "8a9127650734da86b5c73760de2b404494c968a3fd55602045ffec789dac3cb1" - url: "https://pub.dev" - source: hosted - version: "3.0.0" http_multi_server: dependency: transitive description: @@ -1242,6 +1250,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.5.0" + octo_image: + dependency: transitive + description: + name: octo_image + sha256: "34faa6639a78c7e3cbe79be6f9f96535867e879748ade7d17c9b1ae7536293bd" + url: "https://pub.dev" + source: hosted + version: "2.1.0" package_config: dependency: transitive description: @@ -1530,6 +1546,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.5.1" + rxdart: + dependency: transitive + description: + name: rxdart + sha256: "5c3004a4a8dbb94bd4bf5412a4def4acdaa12e12f269737a5751369e12d1a962" + url: "https://pub.dev" + source: hosted + version: "0.28.0" safe_local_storage: dependency: transitive description: @@ -1743,6 +1767,46 @@ packages: url: "https://pub.dev" source: hosted version: "7.0.0" + sqflite: + dependency: transitive + description: + name: sqflite + sha256: "2d7299468485dca85efeeadf5d38986909c5eb0cd71fd3db2c2f000e6c9454bb" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + sqflite_android: + dependency: transitive + description: + name: sqflite_android + sha256: "78f489aab276260cdd26676d2169446c7ecd3484bbd5fead4ca14f3ed4dd9ee3" + url: "https://pub.dev" + source: hosted + version: "2.4.0" + sqflite_common: + dependency: transitive + description: + name: sqflite_common + sha256: "761b9740ecbd4d3e66b8916d784e581861fd3c3553eda85e167bc49fdb68f709" + url: "https://pub.dev" + source: hosted + version: "2.5.4+6" + sqflite_darwin: + dependency: transitive + description: + name: sqflite_darwin + sha256: "96a698e2bc82bd770a4d6aab00b42396a7c63d9e33513a56945cbccb594c2474" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + sqflite_platform_interface: + dependency: transitive + description: + name: sqflite_platform_interface + sha256: "8dd4515c7bdcae0a785b0062859336de775e8c65db81ae33dd5445f35be61920" + url: "https://pub.dev" + source: hosted + version: "2.4.0" stack_trace: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 3e42ee2..b78c30f 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -115,7 +115,7 @@ dependencies: flutter_webrtc: ^0.12.5+hotfix.1 slide_countdown: ^2.0.2 video_compress: ^3.1.3 - extended_image: ^9.0.9 + cached_network_image: ^3.4.1 dev_dependencies: flutter_test: