diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 506bff91..bdbcfe89 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -295,6 +295,8 @@ PODS: - super_native_extensions (0.0.1): - Flutter - SwiftyGif (5.4.5) + - syncfusion_flutter_pdfviewer (0.0.1): + - Flutter - url_launcher_ios (0.0.1): - Flutter - volume_controller (0.0.1): @@ -347,6 +349,7 @@ DEPENDENCIES: - sqflite_darwin (from `.symlinks/plugins/sqflite_darwin/darwin`) - sqlite3_flutter_libs (from `.symlinks/plugins/sqlite3_flutter_libs/darwin`) - super_native_extensions (from `.symlinks/plugins/super_native_extensions/ios`) + - syncfusion_flutter_pdfviewer (from `.symlinks/plugins/syncfusion_flutter_pdfviewer/ios`) - url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`) - volume_controller (from `.symlinks/plugins/volume_controller/ios`) - wakelock_plus (from `.symlinks/plugins/wakelock_plus/ios`) @@ -464,6 +467,8 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/sqlite3_flutter_libs/darwin" super_native_extensions: :path: ".symlinks/plugins/super_native_extensions/ios" + syncfusion_flutter_pdfviewer: + :path: ".symlinks/plugins/syncfusion_flutter_pdfviewer/ios" url_launcher_ios: :path: ".symlinks/plugins/url_launcher_ios/ios" volume_controller: @@ -539,6 +544,7 @@ SPEC CHECKSUMS: sqlite3_flutter_libs: 83f8e9f5b6554077f1d93119fe20ebaa5f3a9ef1 super_native_extensions: b763c02dc3a8fd078389f410bf15149179020cb4 SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4 + syncfusion_flutter_pdfviewer: 90dc48305d2e33d4aa20681d1e98ddeda891bc14 url_launcher_ios: 694010445543906933d732453a59da0a173ae33d volume_controller: 3657a1f65bedb98fa41ff7dc5793537919f31b12 wakelock_plus: e29112ab3ef0b318e58cfa5c32326458be66b556 diff --git a/lib/widgets/content/cloud_files.dart b/lib/widgets/content/cloud_files.dart index 5c8aad91..b5ced433 100644 --- a/lib/widgets/content/cloud_files.dart +++ b/lib/widgets/content/cloud_files.dart @@ -13,6 +13,7 @@ import 'package:island/utils/format.dart'; import 'package:island/widgets/content/audio.dart'; import 'package:material_symbols_icons/symbols.dart'; import 'package:styled_widget/styled_widget.dart'; +import 'package:syncfusion_flutter_pdfviewer/pdfviewer.dart'; import 'package:url_launcher/url_launcher_string.dart'; import 'package:island/widgets/data_saving_gate.dart'; @@ -49,72 +50,109 @@ class CloudFileWidget extends HookConsumerWidget { var ratio = meta['ratio'] is num ? (meta['ratio'] as num).toDouble() : 1.0; if (ratio == 0) ratio = 1.0; - Widget cloudImage() => UniversalImage(uri: uri, blurHash: blurHash, fit: fit); + Widget cloudImage() => + UniversalImage(uri: uri, blurHash: blurHash, fit: fit); Widget cloudVideo() => CloudVideoWidget(item: item); Widget dataPlaceHolder(IconData icon) => _DataSavingPlaceholder( - icon: icon, - onTap: () { - unlocked.value = true; - }, - ); + icon: icon, + onTap: () { + unlocked.value = true; + }, + ); + + if (item.mimeType == 'application/pdf') { + return Stack( + children: [ + SizedBox(height: 600, child: SfPdfViewer.network(uri)), + Positioned( + top: 8, + left: 8, + child: Container( + padding: const EdgeInsets.all(4), + decoration: BoxDecoration( + color: Colors.black54, + borderRadius: BorderRadius.circular(8), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Icon(Symbols.picture_as_pdf, size: 16, color: Colors.white), + const SizedBox(width: 4), + const Text( + 'PDF', + style: TextStyle(color: Colors.white, fontSize: 12), + ), + ], + ), + ), + ), + ], + ); + } var content = switch (item.mimeType?.split('/').firstOrNull) { 'image' => AspectRatio( - aspectRatio: ratio, - child: (useInternalGate && dataSaving && !unlocked.value) ? dataPlaceHolder(Symbols.image) : cloudImage(), - ), + aspectRatio: ratio, + child: + (useInternalGate && dataSaving && !unlocked.value) + ? dataPlaceHolder(Symbols.image) + : cloudImage(), + ), 'video' => AspectRatio( - aspectRatio: ratio, - child: (useInternalGate && dataSaving && !unlocked.value) ? dataPlaceHolder(Symbols.play_arrow) : cloudVideo(), - ), + aspectRatio: ratio, + child: + (useInternalGate && dataSaving && !unlocked.value) + ? dataPlaceHolder(Symbols.play_arrow) + : cloudVideo(), + ), 'audio' => Center( - child: ConstrainedBox( - constraints: BoxConstraints( - maxWidth: math.min(360, MediaQuery.of(context).size.width * 0.8), - ), - child: UniversalAudio(uri: uri, filename: item.name), + child: ConstrainedBox( + constraints: BoxConstraints( + maxWidth: math.min(360, MediaQuery.of(context).size.width * 0.8), ), + child: UniversalAudio(uri: uri, filename: item.name), ), + ), _ => Column( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Icon( - Symbols.insert_drive_file, - size: 48, + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon( + Symbols.insert_drive_file, + size: 48, + color: Theme.of(context).colorScheme.onSurfaceVariant, + ), + const Gap(8), + Text( + item.name, + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: TextStyle( + fontSize: 14, color: Theme.of(context).colorScheme.onSurfaceVariant, ), - const Gap(8), - Text( - item.name, - maxLines: 1, - overflow: TextOverflow.ellipsis, - style: TextStyle( - fontSize: 14, - color: Theme.of(context).colorScheme.onSurfaceVariant, - ), + ), + Text( + formatFileSize(item.size), + style: TextStyle( + fontSize: 12, + color: Theme.of(context).colorScheme.onSurfaceVariant, ), - Text( - formatFileSize(item.size), - style: TextStyle( - fontSize: 12, - color: Theme.of(context).colorScheme.onSurfaceVariant, - ), - ), - const Gap(8), - TextButton.icon( - onPressed: () { - launchUrlString( - 'https://solian.app/files/${item.id}', - mode: LaunchMode.externalApplication, - ); - }, - icon: const Icon(Symbols.launch), - label: Text('openInBrowser').tr(), - ), - ], - ).padding(all: 8), + ), + const Gap(8), + TextButton.icon( + onPressed: () { + launchUrlString( + 'https://solian.app/files/${item.id}', + mode: LaunchMode.externalApplication, + ); + }, + icon: const Icon(Symbols.launch), + label: Text('openInBrowser').tr(), + ), + ], + ).padding(all: 8), }; if (heroTag != null) { @@ -140,8 +178,11 @@ class _DataSavingPlaceholder extends StatelessWidget { child: Column( mainAxisSize: MainAxisSize.min, children: [ - Icon(icon, size: 36, - color: Theme.of(context).colorScheme.onSurfaceVariant), + Icon( + icon, + size: 36, + color: Theme.of(context).colorScheme.onSurfaceVariant, + ), const Gap(8), Text( 'dataSavingHint'.tr(), @@ -154,6 +195,7 @@ class _DataSavingPlaceholder extends StatelessWidget { ); } } + class CloudVideoWidget extends HookConsumerWidget { final SnCloudFile item; const CloudVideoWidget({super.key, required this.item}); @@ -352,35 +394,40 @@ class ProfilePictureWidget extends ConsumerWidget { this.fallbackColor, }); -@override + @override Widget build(BuildContext context, WidgetRef ref) { final serverUrl = ref.watch(serverUrlProvider); final String? id = file?.id ?? fileId; - final fallback = Icon( - fallbackIcon ?? Symbols.account_circle, - size: radius, - color: fallbackColor ?? Theme.of(context).colorScheme.onPrimaryContainer, - ).center(); + final fallback = + Icon( + fallbackIcon ?? Symbols.account_circle, + size: radius, + color: + fallbackColor ?? Theme.of(context).colorScheme.onPrimaryContainer, + ).center(); return ClipRRect( - borderRadius: borderRadius == null - ? BorderRadius.all(Radius.circular(radius)) - : BorderRadius.all(Radius.circular(borderRadius!)), + borderRadius: + borderRadius == null + ? BorderRadius.all(Radius.circular(radius)) + : BorderRadius.all(Radius.circular(borderRadius!)), child: Container( width: radius * 2, height: radius * 2, color: Theme.of(context).colorScheme.primaryContainer, - child: id == null - ? fallback - : DataSavingGate( - bypass: true, - placeholder: fallback, - content: () => UniversalImage( - uri: '$serverUrl/drive/files/$id', - fit: BoxFit.cover, + child: + id == null + ? fallback + : DataSavingGate( + bypass: true, + placeholder: fallback, + content: + () => UniversalImage( + uri: '$serverUrl/drive/files/$id', + fit: BoxFit.cover, + ), ), - ), ), ); } diff --git a/lib/widgets/post/compose_dialog.dart b/lib/widgets/post/compose_dialog.dart index 0f198acb..5d376c81 100644 --- a/lib/widgets/post/compose_dialog.dart +++ b/lib/widgets/post/compose_dialog.dart @@ -13,8 +13,14 @@ import 'package:island/widgets/post/compose_card.dart'; class PostComposeDialog extends HookConsumerWidget { final SnPost? originalPost; final PostComposeInitialState? initialState; + final bool isBottomSheet; - const PostComposeDialog({super.key, this.originalPost, this.initialState}); + const PostComposeDialog({ + super.key, + this.originalPost, + this.initialState, + this.isBottomSheet = false, + }); static Future show( BuildContext context, { @@ -23,11 +29,14 @@ class PostComposeDialog extends HookConsumerWidget { }) { return showDialog( context: context, - useRootNavigator: false, + useRootNavigator: true, builder: - (context) => PostComposeDialog( - originalPost: originalPost, - initialState: initialState, + (context) => Padding( + padding: EdgeInsets.all(16), + child: PostComposeDialog( + originalPost: originalPost, + initialState: initialState, + ), ), ); } diff --git a/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc index 9b5ba0e9..d6aee932 100644 --- a/linux/flutter/generated_plugin_registrant.cc +++ b/linux/flutter/generated_plugin_registrant.cc @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -80,6 +81,9 @@ void fl_register_plugins(FlPluginRegistry* registry) { g_autoptr(FlPluginRegistrar) super_native_extensions_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "SuperNativeExtensionsPlugin"); super_native_extensions_plugin_register_with_registrar(super_native_extensions_registrar); + g_autoptr(FlPluginRegistrar) syncfusion_pdfviewer_linux_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "SyncfusionPdfviewerLinuxPlugin"); + syncfusion_pdfviewer_linux_plugin_register_with_registrar(syncfusion_pdfviewer_linux_registrar); g_autoptr(FlPluginRegistrar) tray_manager_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "TrayManagerPlugin"); tray_manager_plugin_register_with_registrar(tray_manager_registrar); diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake index c66d5982..6ff0e93a 100644 --- a/linux/flutter/generated_plugins.cmake +++ b/linux/flutter/generated_plugins.cmake @@ -20,6 +20,7 @@ list(APPEND FLUTTER_PLUGIN_LIST screen_retriever_linux sqlite3_flutter_libs super_native_extensions + syncfusion_pdfviewer_linux tray_manager url_launcher_linux volume_controller diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 9f7f9ed2..92655d2d 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -39,6 +39,7 @@ import sign_in_with_apple import sqflite_darwin import sqlite3_flutter_libs import super_native_extensions +import syncfusion_pdfviewer_macos import tray_manager import url_launcher_macos import volume_controller @@ -80,6 +81,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin")) Sqlite3FlutterLibsPlugin.register(with: registry.registrar(forPlugin: "Sqlite3FlutterLibsPlugin")) SuperNativeExtensionsPlugin.register(with: registry.registrar(forPlugin: "SuperNativeExtensionsPlugin")) + SyncfusionFlutterPdfViewerPlugin.register(with: registry.registrar(forPlugin: "SyncfusionFlutterPdfViewerPlugin")) TrayManagerPlugin.register(with: registry.registrar(forPlugin: "TrayManagerPlugin")) UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) VolumeControllerPlugin.register(with: registry.registrar(forPlugin: "VolumeControllerPlugin")) diff --git a/pubspec.lock b/pubspec.lock index 53fc61b6..aae45a5e 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -2506,6 +2506,78 @@ packages: url: "https://pub.dev" source: hosted version: "0.4.1" + syncfusion_flutter_core: + dependency: transitive + description: + name: syncfusion_flutter_core + sha256: "6a01bd0da768003f59a9c51ae28145a61372abb59e3e588ef33c86db3579bccf" + url: "https://pub.dev" + source: hosted + version: "31.1.22" + syncfusion_flutter_pdf: + dependency: transitive + description: + name: syncfusion_flutter_pdf + sha256: "5954260288621f1070356b206abeece7f38fcc754cfbf6755f766f356280a0ec" + url: "https://pub.dev" + source: hosted + version: "31.1.22" + syncfusion_flutter_pdfviewer: + dependency: "direct main" + description: + name: syncfusion_flutter_pdfviewer + sha256: "4bb3cdff34fe937430fdd0b5d6488f6a68efa8cfe08c73cc4b19d2a879dde057" + url: "https://pub.dev" + source: hosted + version: "31.1.21" + syncfusion_flutter_signaturepad: + dependency: transitive + description: + name: syncfusion_flutter_signaturepad + sha256: "4b6d7fc679fe1fb2978bd78bd35ca4542c1d4868330687f56177685e5fb19a04" + url: "https://pub.dev" + source: hosted + version: "31.1.22" + syncfusion_pdfviewer_linux: + dependency: transitive + description: + name: syncfusion_pdfviewer_linux + sha256: "8cb32caead6d7d307f39b840d7106796c339667193317a389ca44273b52f6687" + url: "https://pub.dev" + source: hosted + version: "31.1.22" + syncfusion_pdfviewer_macos: + dependency: transitive + description: + name: syncfusion_pdfviewer_macos + sha256: "9bb6c349920ddce69182617875fc1ec8d7788be9bb642ddde5443af4f98b983c" + url: "https://pub.dev" + source: hosted + version: "31.1.22" + syncfusion_pdfviewer_platform_interface: + dependency: transitive + description: + name: syncfusion_pdfviewer_platform_interface + sha256: "25084055ccff99a6ec2378d2fce6b4a8a0fece9fe01bc7ca509aebc9e1f0e3b7" + url: "https://pub.dev" + source: hosted + version: "31.1.22" + syncfusion_pdfviewer_web: + dependency: transitive + description: + name: syncfusion_pdfviewer_web + sha256: "5b60de98f59c9bbfe952c1e7345a35a274a41d61889ea93fa4327e4b0ea12c9a" + url: "https://pub.dev" + source: hosted + version: "31.1.22" + syncfusion_pdfviewer_windows: + dependency: transitive + description: + name: syncfusion_pdfviewer_windows + sha256: f57717111505d48a6e14b42a3ad908e5b0fbfaee03b07803365bc105d49e54d5 + url: "https://pub.dev" + source: hosted + version: "31.1.22" synchronized: dependency: transitive description: @@ -2926,4 +2998,4 @@ packages: version: "3.1.3" sdks: dart: ">=3.9.0 <4.0.0" - flutter: ">=3.35.0" + flutter: ">=3.35.1" diff --git a/pubspec.yaml b/pubspec.yaml index 63c12be0..ed920666 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -159,6 +159,7 @@ dependencies: talker_dio_logger: ^5.0.1 talker_riverpod_logger: ^5.0.1 app_links: ^6.4.1 + syncfusion_flutter_pdfviewer: ^31.1.21 dev_dependencies: flutter_test: diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index 1eb1ed01..73c96506 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -85,6 +86,8 @@ void RegisterPlugins(flutter::PluginRegistry* registry) { registry->GetRegistrarForPlugin("Sqlite3FlutterLibsPlugin")); SuperNativeExtensionsPluginCApiRegisterWithRegistrar( registry->GetRegistrarForPlugin("SuperNativeExtensionsPluginCApi")); + SyncfusionPdfviewerWindowsPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("SyncfusionPdfviewerWindowsPlugin")); TrayManagerPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("TrayManagerPlugin")); UrlLauncherWindowsRegisterWithRegistrar( diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index ed2828b2..c6964180 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -27,6 +27,7 @@ list(APPEND FLUTTER_PLUGIN_LIST share_plus sqlite3_flutter_libs super_native_extensions + syncfusion_pdfviewer_windows tray_manager url_launcher_windows volume_controller