✨ Drag to upload
This commit is contained in:
		| @@ -30,6 +30,13 @@ | ||||
|             </intent-filter> | ||||
|         </receiver> | ||||
|  | ||||
|         <provider | ||||
|                 android:name="com.superlist.super_native_extensions.DataProvider" | ||||
|                 android:authorities="dev.solsynth.solian.SuperClipboardDataProvider" | ||||
|                 android:exported="true" | ||||
|                 android:grantUriPermissions="true" > | ||||
|         </provider> | ||||
|  | ||||
|         <activity | ||||
|                 android:name=".MainActivity" | ||||
|                 android:exported="true" | ||||
|   | ||||
| @@ -33,7 +33,9 @@ void main() async { | ||||
|     appRunner: () async { | ||||
|       WidgetsFlutterBinding.ensureInitialized(); | ||||
|  | ||||
|       await protocolHandler.register('solink'); | ||||
|       if (!PlatformInfo.isWeb) { | ||||
|         await protocolHandler.register('solink'); | ||||
|       } | ||||
|  | ||||
|       await Firebase.initializeApp( | ||||
|         options: DefaultFirebaseOptions.currentPlatform, | ||||
|   | ||||
| @@ -179,7 +179,7 @@ class AccountProvider extends GetxController { | ||||
|  | ||||
|   Future<void> registerPushNotifications() async { | ||||
|     final AuthProvider auth = Get.find(); | ||||
|     if (!await auth.isAuthorized) throw Exception('unauthorized'); | ||||
|     if (!await auth.isAuthorized) return; | ||||
|  | ||||
|     late final String? token; | ||||
|     late final String provider; | ||||
|   | ||||
| @@ -1,21 +1,40 @@ | ||||
| import 'dart:convert'; | ||||
| import 'dart:io'; | ||||
| import 'dart:isolate'; | ||||
| import 'dart:typed_data'; | ||||
|  | ||||
| import 'package:crypto/crypto.dart'; | ||||
| import 'package:get/get.dart'; | ||||
| import 'package:path/path.dart'; | ||||
| import 'package:solian/platform.dart'; | ||||
| import 'package:solian/providers/auth.dart'; | ||||
| import 'package:solian/services.dart'; | ||||
| import 'package:image/image.dart' as img; | ||||
|  | ||||
| Future<String> calculateFileSha256(File file) async { | ||||
|   final bytes = await Isolate.run(() => file.readAsBytesSync()); | ||||
|   final digest = await Isolate.run(() => sha256.convert(bytes)); | ||||
| Future<String> calculateBytesSha256(Uint8List data) async { | ||||
|   Digest digest; | ||||
|   if (PlatformInfo.isWeb) { | ||||
|     digest = sha256.convert(data); | ||||
|   } else { | ||||
|     digest = await Isolate.run(() => sha256.convert(data)); | ||||
|   } | ||||
|   return digest.toString(); | ||||
| } | ||||
|  | ||||
| Future<String> calculateFileSha256(File file) async { | ||||
|   Uint8List bytes; | ||||
|   if (PlatformInfo.isWeb) { | ||||
|     bytes = await file.readAsBytes(); | ||||
|   } else { | ||||
|     bytes = await Isolate.run(() => file.readAsBytesSync()); | ||||
|   } | ||||
|   return await calculateBytesSha256(bytes); | ||||
| } | ||||
|  | ||||
| Future<double> calculateFileAspectRatio(File file) async { | ||||
|   if (PlatformInfo.isWeb) { | ||||
|     return 1; | ||||
|   } | ||||
|   final bytes = await Isolate.run(() => file.readAsBytesSync()); | ||||
|   final decoder = await Isolate.run(() => img.findDecoderForData(bytes)); | ||||
|   if (decoder == null) return 1; | ||||
| @@ -25,7 +44,10 @@ Future<double> calculateFileAspectRatio(File file) async { | ||||
| } | ||||
|  | ||||
| class AttachmentProvider extends GetConnect { | ||||
|   static Map<String, String> mimetypeOverrides = {'mov': 'video/quicktime'}; | ||||
|   static Map<String, String> mimetypeOverrides = { | ||||
|     'mov': 'video/quicktime', | ||||
|     'mp4': 'video/mp4' | ||||
|   }; | ||||
|  | ||||
|   @override | ||||
|   void onInit() { | ||||
| @@ -45,7 +67,8 @@ class AttachmentProvider extends GetConnect { | ||||
|     return resp; | ||||
|   } | ||||
|  | ||||
|   Future<Response> createAttachment(File file, String hash, String usage, | ||||
|   Future<Response> createAttachment( | ||||
|       Uint8List data, String path, String hash, String usage, | ||||
|       {double? ratio}) async { | ||||
|     final AuthProvider auth = Get.find(); | ||||
|     if (!await auth.isAuthorized) throw Exception('unauthorized'); | ||||
| @@ -55,13 +78,12 @@ class AttachmentProvider extends GetConnect { | ||||
|       timeout: const Duration(minutes: 3), | ||||
|     ); | ||||
|  | ||||
|     final filePayload = | ||||
|         MultipartFile(await file.readAsBytes(), filename: basename(file.path)); | ||||
|     final fileAlt = basename(file.path).contains('.') | ||||
|         ? basename(file.path).substring(0, basename(file.path).lastIndexOf('.')) | ||||
|         : basename(file.path); | ||||
|     final fileExt = basename(file.path) | ||||
|         .substring(basename(file.path).lastIndexOf('.') + 1) | ||||
|     final filePayload = MultipartFile(data, filename: basename(path)); | ||||
|     final fileAlt = basename(path).contains('.') | ||||
|         ? basename(path).substring(0, basename(path).lastIndexOf('.')) | ||||
|         : basename(path); | ||||
|     final fileExt = basename(path) | ||||
|         .substring(basename(path).lastIndexOf('.') + 1) | ||||
|         .toLowerCase(); | ||||
|  | ||||
|     // Override for some files cannot be detected mimetype by server-side | ||||
|   | ||||
| @@ -88,7 +88,8 @@ class _PersonalizeScreenState extends State<PersonalizeScreen> { | ||||
|       final file = File(image.path); | ||||
|       final hash = await calculateFileSha256(file); | ||||
|       attachResp = await provider.createAttachment( | ||||
|         file, | ||||
|         await file.readAsBytes(), | ||||
|         file.path, | ||||
|         hash, | ||||
|         'p.$position', | ||||
|         ratio: await calculateFileAspectRatio(file), | ||||
|   | ||||
| @@ -178,12 +178,6 @@ class _AttachmentListState extends State<AttachmentList> { | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|   void initState() { | ||||
|     super.initState(); | ||||
|     getMetadataList(); | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     if (widget.attachmentsId.isEmpty) { | ||||
| @@ -236,7 +230,8 @@ class _AttachmentListState extends State<AttachmentList> { | ||||
|           const radius = BorderRadius.all(Radius.circular(16)); | ||||
|           return Container( | ||||
|             decoration: BoxDecoration( | ||||
|               border: Border.all(color: Theme.of(context).dividerColor, width: 1), | ||||
|               border: | ||||
|                   Border.all(color: Theme.of(context).dividerColor, width: 1), | ||||
|               borderRadius: radius, | ||||
|             ), | ||||
|             child: ClipRRect( | ||||
|   | ||||
| @@ -1,6 +1,10 @@ | ||||
| import 'dart:async'; | ||||
| import 'dart:io'; | ||||
| import 'dart:isolate'; | ||||
| import 'dart:math' as math; | ||||
| import 'dart:typed_data'; | ||||
|  | ||||
| import 'package:crypto/crypto.dart'; | ||||
| import 'package:file_picker/file_picker.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:flutter_animate/flutter_animate.dart'; | ||||
| @@ -10,6 +14,7 @@ import 'package:solian/exts.dart'; | ||||
| import 'package:solian/models/attachment.dart'; | ||||
| import 'package:solian/providers/auth.dart'; | ||||
| import 'package:solian/providers/content/attachment.dart'; | ||||
| import 'package:super_drag_and_drop/super_drag_and_drop.dart'; | ||||
|  | ||||
| class AttachmentPublishPopup extends StatefulWidget { | ||||
|   final String usage; | ||||
| @@ -24,8 +29,7 @@ class AttachmentPublishPopup extends StatefulWidget { | ||||
|   }); | ||||
|  | ||||
|   @override | ||||
|   State<AttachmentPublishPopup> createState() => | ||||
|       _AttachmentPublishPopupState(); | ||||
|   State<AttachmentPublishPopup> createState() => _AttachmentPublishPopupState(); | ||||
| } | ||||
|  | ||||
| class _AttachmentPublishPopupState extends State<AttachmentPublishPopup> { | ||||
| @@ -51,7 +55,8 @@ class _AttachmentPublishPopupState extends State<AttachmentPublishPopup> { | ||||
|  | ||||
|       try { | ||||
|         await uploadAttachment( | ||||
|           file, | ||||
|           await file.readAsBytes(), | ||||
|           file.path, | ||||
|           hash, | ||||
|           ratio: await calculateFileAspectRatio(file), | ||||
|         ); | ||||
| @@ -77,7 +82,8 @@ class _AttachmentPublishPopupState extends State<AttachmentPublishPopup> { | ||||
|     const ratio = 16 / 9; | ||||
|  | ||||
|     try { | ||||
|       await uploadAttachment(file, hash, ratio: ratio); | ||||
|       await uploadAttachment(await file.readAsBytes(), file.path, hash, | ||||
|           ratio: ratio); | ||||
|     } catch (err) { | ||||
|       context.showErrorDialog(err); | ||||
|     } | ||||
| @@ -100,7 +106,7 @@ class _AttachmentPublishPopupState extends State<AttachmentPublishPopup> { | ||||
|     for (final file in files) { | ||||
|       final hash = await calculateFileSha256(file); | ||||
|       try { | ||||
|         await uploadAttachment(file, hash); | ||||
|         await uploadAttachment(await file.readAsBytes(), file.path, hash); | ||||
|       } catch (err) { | ||||
|         context.showErrorDialog(err); | ||||
|       } | ||||
| @@ -134,7 +140,12 @@ class _AttachmentPublishPopupState extends State<AttachmentPublishPopup> { | ||||
|     } | ||||
|  | ||||
|     try { | ||||
|       await uploadAttachment(file, hash, ratio: ratio); | ||||
|       await uploadAttachment( | ||||
|         await file.readAsBytes(), | ||||
|         file.path, | ||||
|         hash, | ||||
|         ratio: ratio, | ||||
|       ); | ||||
|     } catch (err) { | ||||
|       context.showErrorDialog(err); | ||||
|     } | ||||
| @@ -142,11 +153,13 @@ class _AttachmentPublishPopupState extends State<AttachmentPublishPopup> { | ||||
|     setState(() => _isBusy = false); | ||||
|   } | ||||
|  | ||||
|   Future<void> uploadAttachment(File file, String hash, {double? ratio}) async { | ||||
|   Future<void> uploadAttachment(Uint8List data, String path, String hash, | ||||
|       {double? ratio}) async { | ||||
|     final AttachmentProvider provider = Get.find(); | ||||
|     try { | ||||
|       final resp = await provider.createAttachment( | ||||
|         file, | ||||
|         data, | ||||
|         path, | ||||
|         hash, | ||||
|         widget.usage, | ||||
|         ratio: ratio, | ||||
| @@ -208,10 +221,14 @@ class _AttachmentPublishPopupState extends State<AttachmentPublishPopup> { | ||||
|   @override | ||||
|   void initState() { | ||||
|     super.initState(); | ||||
|  | ||||
|     revertMetadataList(); | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|   void dispose() { | ||||
|     super.dispose(); | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     const density = VisualDensity(horizontal: 0, vertical: 0); | ||||
| @@ -219,134 +236,165 @@ class _AttachmentPublishPopupState extends State<AttachmentPublishPopup> { | ||||
|     return SafeArea( | ||||
|       child: SizedBox( | ||||
|         height: MediaQuery.of(context).size.height * 0.85, | ||||
|         child: Column( | ||||
|           crossAxisAlignment: CrossAxisAlignment.start, | ||||
|           children: [ | ||||
|             Text( | ||||
|               'attachmentAdd'.tr, | ||||
|               style: Theme.of(context).textTheme.headlineSmall, | ||||
|             ).paddingOnly(left: 24, right: 24, top: 32, bottom: 16), | ||||
|             if (_isBusy) const LinearProgressIndicator().animate().scaleX(), | ||||
|             Expanded( | ||||
|               child: Builder(builder: (context) { | ||||
|                 if (_isFirstTimeBusy && _isBusy) { | ||||
|                   return const Center( | ||||
|                     child: CircularProgressIndicator(), | ||||
|                   ); | ||||
|                 } | ||||
|  | ||||
|                 return ListView.builder( | ||||
|                   itemCount: _attachments.length, | ||||
|                   itemBuilder: (context, index) { | ||||
|                     final element = _attachments[index]; | ||||
|                     var fileType = element!.mimetype.split('/').firstOrNull; | ||||
|                     fileType ??= 'unknown'; | ||||
|                     return Container( | ||||
|                       padding: | ||||
|                           const EdgeInsets.only(left: 16, right: 8, bottom: 16), | ||||
|                       child: Row( | ||||
|                         children: [ | ||||
|                           Expanded( | ||||
|                             child: Column( | ||||
|                               crossAxisAlignment: CrossAxisAlignment.start, | ||||
|                               children: [ | ||||
|                                 Text( | ||||
|                                   element.alt, | ||||
|                                   overflow: TextOverflow.ellipsis, | ||||
|                                   maxLines: 1, | ||||
|                                   style: const TextStyle( | ||||
|                                       fontWeight: FontWeight.bold), | ||||
|                                 ), | ||||
|                                 Text( | ||||
|                                   '${fileType[0].toUpperCase()}${fileType.substring(1)} · ${formatBytes(element.size)}', | ||||
|                                 ), | ||||
|                               ], | ||||
|                             ), | ||||
|                           ), | ||||
|                           IconButton( | ||||
|                             style: TextButton.styleFrom( | ||||
|                               shape: const CircleBorder(), | ||||
|                               foregroundColor: Theme.of(context).primaryColor, | ||||
|                             ), | ||||
|                             icon: const Icon(Icons.more_horiz), | ||||
|                             onPressed: () { | ||||
|                               showDialog( | ||||
|                                 context: context, | ||||
|                                 builder: (context) { | ||||
|                                   return AttachmentEditorDialog( | ||||
|                                     item: element, | ||||
|                                     onDelete: () { | ||||
|                                       setState( | ||||
|                                           () => _attachments.removeAt(index)); | ||||
|                                       widget.onUpdate(_attachments | ||||
|                                           .map((e) => e!.id) | ||||
|                                           .toList()); | ||||
|                                     }, | ||||
|                                     onUpdate: (item) { | ||||
|                                       setState( | ||||
|                                           () => _attachments[index] = item); | ||||
|                                       widget.onUpdate(_attachments | ||||
|                                           .map((e) => e!.id) | ||||
|                                           .toList()); | ||||
|                                     }, | ||||
|                                   ); | ||||
|                                 }, | ||||
|                               ); | ||||
|                             }, | ||||
|                           ), | ||||
|                         ], | ||||
|                       ), | ||||
|         child: DropRegion( | ||||
|           formats: Formats.standardFormats, | ||||
|           hitTestBehavior: HitTestBehavior.opaque, | ||||
|           onDropOver: (event) { | ||||
|             if (event.session.allowedOperations.contains(DropOperation.copy)) { | ||||
|               return DropOperation.copy; | ||||
|             } else { | ||||
|               return DropOperation.none; | ||||
|             } | ||||
|           }, | ||||
|           onPerformDrop: (event) async { | ||||
|             for (final item in event.session.items) { | ||||
|               final reader = item.dataReader!; | ||||
|               for (final format | ||||
|                   in Formats.standardFormats.whereType<FileFormat>()) { | ||||
|                 if (reader.canProvide(format)) { | ||||
|                   reader.getFile(format, (file) async { | ||||
|                     final data = await file.readAll(); | ||||
|                     await uploadAttachment( | ||||
|                       data, | ||||
|                       file.fileName ?? 'attachment', | ||||
|                       await calculateBytesSha256(data), | ||||
|                     ); | ||||
|                   }, | ||||
|                 ); | ||||
|               }), | ||||
|             ), | ||||
|             const Divider(thickness: 0.3, height: 0.3), | ||||
|             SizedBox( | ||||
|               height: 64, | ||||
|               child: SingleChildScrollView( | ||||
|                 scrollDirection: Axis.horizontal, | ||||
|                 child: Wrap( | ||||
|                   spacing: 8, | ||||
|                   runSpacing: 0, | ||||
|                   alignment: WrapAlignment.center, | ||||
|                   runAlignment: WrapAlignment.center, | ||||
|                   children: [ | ||||
|                     ElevatedButton.icon( | ||||
|                       icon: const Icon(Icons.add_photo_alternate), | ||||
|                       label: Text('attachmentAddGalleryPhoto'.tr), | ||||
|                       style: const ButtonStyle(visualDensity: density), | ||||
|                       onPressed: () => pickPhotoToUpload(), | ||||
|                     ), | ||||
|                     ElevatedButton.icon( | ||||
|                       icon: const Icon(Icons.add_road), | ||||
|                       label: Text('attachmentAddGalleryVideo'.tr), | ||||
|                       style: const ButtonStyle(visualDensity: density), | ||||
|                       onPressed: () => pickVideoToUpload(), | ||||
|                     ), | ||||
|                     ElevatedButton.icon( | ||||
|                       icon: const Icon(Icons.photo_camera_back), | ||||
|                       label: Text('attachmentAddCameraPhoto'.tr), | ||||
|                       style: const ButtonStyle(visualDensity: density), | ||||
|                       onPressed: () => takeMediaToUpload(false), | ||||
|                     ), | ||||
|                     ElevatedButton.icon( | ||||
|                       icon: const Icon(Icons.video_camera_back_outlined), | ||||
|                       label: Text('attachmentAddCameraVideo'.tr), | ||||
|                       style: const ButtonStyle(visualDensity: density), | ||||
|                       onPressed: () => takeMediaToUpload(true), | ||||
|                     ), | ||||
|                     ElevatedButton.icon( | ||||
|                       icon: const Icon(Icons.file_present_rounded), | ||||
|                       label: Text('attachmentAddFile'.tr), | ||||
|                       style: const ButtonStyle(visualDensity: density), | ||||
|                       onPressed: () => pickFileToUpload(), | ||||
|                     ), | ||||
|                   ], | ||||
|                 ).paddingSymmetric(horizontal: 12), | ||||
|                   }, onError: (error) { | ||||
|                     print('Error reading value $error'); | ||||
|                   }); | ||||
|                 } | ||||
|               } | ||||
|             } | ||||
|           }, | ||||
|           child: Column( | ||||
|             crossAxisAlignment: CrossAxisAlignment.start, | ||||
|             children: [ | ||||
|               Text( | ||||
|                 'attachmentAdd'.tr, | ||||
|                 style: Theme.of(context).textTheme.headlineSmall, | ||||
|               ).paddingOnly(left: 24, right: 24, top: 32, bottom: 16), | ||||
|               if (_isBusy) const LinearProgressIndicator().animate().scaleX(), | ||||
|               Expanded( | ||||
|                 child: Builder(builder: (context) { | ||||
|                   if (_isFirstTimeBusy && _isBusy) { | ||||
|                     return const Center( | ||||
|                       child: CircularProgressIndicator(), | ||||
|                     ); | ||||
|                   } | ||||
|  | ||||
|                   return ListView.builder( | ||||
|                     itemCount: _attachments.length, | ||||
|                     itemBuilder: (context, index) { | ||||
|                       final element = _attachments[index]; | ||||
|                       var fileType = element!.mimetype.split('/').firstOrNull; | ||||
|                       fileType ??= 'unknown'; | ||||
|                       return Container( | ||||
|                         padding: const EdgeInsets.only( | ||||
|                             left: 16, right: 8, bottom: 16), | ||||
|                         child: Row( | ||||
|                           children: [ | ||||
|                             Expanded( | ||||
|                               child: Column( | ||||
|                                 crossAxisAlignment: CrossAxisAlignment.start, | ||||
|                                 children: [ | ||||
|                                   Text( | ||||
|                                     element.alt, | ||||
|                                     overflow: TextOverflow.ellipsis, | ||||
|                                     maxLines: 1, | ||||
|                                     style: const TextStyle( | ||||
|                                         fontWeight: FontWeight.bold), | ||||
|                                   ), | ||||
|                                   Text( | ||||
|                                     '${fileType[0].toUpperCase()}${fileType.substring(1)} · ${formatBytes(element.size)}', | ||||
|                                   ), | ||||
|                                 ], | ||||
|                               ), | ||||
|                             ), | ||||
|                             IconButton( | ||||
|                               style: TextButton.styleFrom( | ||||
|                                 shape: const CircleBorder(), | ||||
|                                 foregroundColor: Theme.of(context).primaryColor, | ||||
|                               ), | ||||
|                               icon: const Icon(Icons.more_horiz), | ||||
|                               onPressed: () { | ||||
|                                 showDialog( | ||||
|                                   context: context, | ||||
|                                   builder: (context) { | ||||
|                                     return AttachmentEditorDialog( | ||||
|                                       item: element, | ||||
|                                       onDelete: () { | ||||
|                                         setState( | ||||
|                                             () => _attachments.removeAt(index)); | ||||
|                                         widget.onUpdate(_attachments | ||||
|                                             .map((e) => e!.id) | ||||
|                                             .toList()); | ||||
|                                       }, | ||||
|                                       onUpdate: (item) { | ||||
|                                         setState( | ||||
|                                             () => _attachments[index] = item); | ||||
|                                         widget.onUpdate(_attachments | ||||
|                                             .map((e) => e!.id) | ||||
|                                             .toList()); | ||||
|                                       }, | ||||
|                                     ); | ||||
|                                   }, | ||||
|                                 ); | ||||
|                               }, | ||||
|                             ), | ||||
|                           ], | ||||
|                         ), | ||||
|                       ); | ||||
|                     }, | ||||
|                   ); | ||||
|                 }), | ||||
|               ), | ||||
|             ) | ||||
|           ], | ||||
|               const Divider(thickness: 0.3, height: 0.3), | ||||
|               SizedBox( | ||||
|                 height: 64, | ||||
|                 child: SingleChildScrollView( | ||||
|                   scrollDirection: Axis.horizontal, | ||||
|                   child: Wrap( | ||||
|                     spacing: 8, | ||||
|                     runSpacing: 0, | ||||
|                     alignment: WrapAlignment.center, | ||||
|                     runAlignment: WrapAlignment.center, | ||||
|                     children: [ | ||||
|                       ElevatedButton.icon( | ||||
|                         icon: const Icon(Icons.add_photo_alternate), | ||||
|                         label: Text('attachmentAddGalleryPhoto'.tr), | ||||
|                         style: const ButtonStyle(visualDensity: density), | ||||
|                         onPressed: () => pickPhotoToUpload(), | ||||
|                       ), | ||||
|                       ElevatedButton.icon( | ||||
|                         icon: const Icon(Icons.add_road), | ||||
|                         label: Text('attachmentAddGalleryVideo'.tr), | ||||
|                         style: const ButtonStyle(visualDensity: density), | ||||
|                         onPressed: () => pickVideoToUpload(), | ||||
|                       ), | ||||
|                       ElevatedButton.icon( | ||||
|                         icon: const Icon(Icons.photo_camera_back), | ||||
|                         label: Text('attachmentAddCameraPhoto'.tr), | ||||
|                         style: const ButtonStyle(visualDensity: density), | ||||
|                         onPressed: () => takeMediaToUpload(false), | ||||
|                       ), | ||||
|                       ElevatedButton.icon( | ||||
|                         icon: const Icon(Icons.video_camera_back_outlined), | ||||
|                         label: Text('attachmentAddCameraVideo'.tr), | ||||
|                         style: const ButtonStyle(visualDensity: density), | ||||
|                         onPressed: () => takeMediaToUpload(true), | ||||
|                       ), | ||||
|                       ElevatedButton.icon( | ||||
|                         icon: const Icon(Icons.file_present_rounded), | ||||
|                         label: Text('attachmentAddFile'.tr), | ||||
|                         style: const ButtonStyle(visualDensity: density), | ||||
|                         onPressed: () => pickFileToUpload(), | ||||
|                       ), | ||||
|                     ], | ||||
|                   ).paddingSymmetric(horizontal: 12), | ||||
|                 ), | ||||
|               ) | ||||
|             ], | ||||
|           ), | ||||
|         ), | ||||
|       ), | ||||
|     ); | ||||
| @@ -365,8 +413,7 @@ class AttachmentEditorDialog extends StatefulWidget { | ||||
|       required this.onUpdate}); | ||||
|  | ||||
|   @override | ||||
|   State<AttachmentEditorDialog> createState() => | ||||
|       _AttachmentEditorDialogState(); | ||||
|   State<AttachmentEditorDialog> createState() => _AttachmentEditorDialogState(); | ||||
| } | ||||
|  | ||||
| class _AttachmentEditorDialogState extends State<AttachmentEditorDialog> { | ||||
|   | ||||
| @@ -1,5 +1,4 @@ | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:flutter_animate/flutter_animate.dart'; | ||||
| import 'package:font_awesome_flutter/font_awesome_flutter.dart'; | ||||
| import 'package:get/get.dart'; | ||||
| import 'package:solian/controllers/chat_events_controller.dart'; | ||||
|   | ||||
| @@ -10,7 +10,9 @@ | ||||
| #include <flutter_acrylic/flutter_acrylic_plugin.h> | ||||
| #include <flutter_secure_storage_linux/flutter_secure_storage_linux_plugin.h> | ||||
| #include <flutter_webrtc/flutter_web_r_t_c_plugin.h> | ||||
| #include <irondash_engine_context/irondash_engine_context_plugin.h> | ||||
| #include <sentry_flutter/sentry_flutter_plugin.h> | ||||
| #include <super_native_extensions/super_native_extensions_plugin.h> | ||||
| #include <url_launcher_linux/url_launcher_plugin.h> | ||||
|  | ||||
| void fl_register_plugins(FlPluginRegistry* registry) { | ||||
| @@ -26,9 +28,15 @@ void fl_register_plugins(FlPluginRegistry* registry) { | ||||
|   g_autoptr(FlPluginRegistrar) flutter_webrtc_registrar = | ||||
|       fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterWebRTCPlugin"); | ||||
|   flutter_web_r_t_c_plugin_register_with_registrar(flutter_webrtc_registrar); | ||||
|   g_autoptr(FlPluginRegistrar) irondash_engine_context_registrar = | ||||
|       fl_plugin_registry_get_registrar_for_plugin(registry, "IrondashEngineContextPlugin"); | ||||
|   irondash_engine_context_plugin_register_with_registrar(irondash_engine_context_registrar); | ||||
|   g_autoptr(FlPluginRegistrar) sentry_flutter_registrar = | ||||
|       fl_plugin_registry_get_registrar_for_plugin(registry, "SentryFlutterPlugin"); | ||||
|   sentry_flutter_plugin_register_with_registrar(sentry_flutter_registrar); | ||||
|   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) url_launcher_linux_registrar = | ||||
|       fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin"); | ||||
|   url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar); | ||||
|   | ||||
| @@ -7,7 +7,9 @@ list(APPEND FLUTTER_PLUGIN_LIST | ||||
|   flutter_acrylic | ||||
|   flutter_secure_storage_linux | ||||
|   flutter_webrtc | ||||
|   irondash_engine_context | ||||
|   sentry_flutter | ||||
|   super_native_extensions | ||||
|   url_launcher_linux | ||||
| ) | ||||
|  | ||||
|   | ||||
| @@ -13,6 +13,7 @@ import firebase_messaging | ||||
| import flutter_local_notifications | ||||
| import flutter_secure_storage_macos | ||||
| import flutter_webrtc | ||||
| import irondash_engine_context | ||||
| import livekit_client | ||||
| import macos_window_utils | ||||
| import package_info_plus | ||||
| @@ -21,6 +22,7 @@ import protocol_handler_macos | ||||
| import sentry_flutter | ||||
| import shared_preferences_foundation | ||||
| import sqflite | ||||
| import super_native_extensions | ||||
| import url_launcher_macos | ||||
| import video_player_avfoundation | ||||
| import wakelock_plus | ||||
| @@ -34,6 +36,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { | ||||
|   FlutterLocalNotificationsPlugin.register(with: registry.registrar(forPlugin: "FlutterLocalNotificationsPlugin")) | ||||
|   FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin")) | ||||
|   FlutterWebRTCPlugin.register(with: registry.registrar(forPlugin: "FlutterWebRTCPlugin")) | ||||
|   IrondashEngineContextPlugin.register(with: registry.registrar(forPlugin: "IrondashEngineContextPlugin")) | ||||
|   LiveKitPlugin.register(with: registry.registrar(forPlugin: "LiveKitPlugin")) | ||||
|   MacOSWindowUtilsPlugin.register(with: registry.registrar(forPlugin: "MacOSWindowUtilsPlugin")) | ||||
|   FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin")) | ||||
| @@ -42,6 +45,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { | ||||
|   SentryFlutterPlugin.register(with: registry.registrar(forPlugin: "SentryFlutterPlugin")) | ||||
|   SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) | ||||
|   SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin")) | ||||
|   SuperNativeExtensionsPlugin.register(with: registry.registrar(forPlugin: "SuperNativeExtensionsPlugin")) | ||||
|   UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) | ||||
|   FVPVideoPlayerPlugin.register(with: registry.registrar(forPlugin: "FVPVideoPlayerPlugin")) | ||||
|   WakelockPlusMacosPlugin.register(with: registry.registrar(forPlugin: "WakelockPlusMacosPlugin")) | ||||
|   | ||||
							
								
								
									
										48
									
								
								pubspec.lock
									
									
									
									
									
								
							
							
						
						
									
										48
									
								
								pubspec.lock
									
									
									
									
									
								
							| @@ -824,6 +824,22 @@ packages: | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "1.0.4" | ||||
|   irondash_engine_context: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: irondash_engine_context | ||||
|       sha256: cd7b769db11a2b5243b037c8a9b1ecaef02e1ae27a2d909ffa78c1dad747bb10 | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "0.5.4" | ||||
|   irondash_message_channel: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: irondash_message_channel | ||||
|       sha256: b4101669776509c76133b8917ab8cfc704d3ad92a8c450b92934dd8884a2f060 | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "0.7.0" | ||||
|   js: | ||||
|     dependency: transitive | ||||
|     description: | ||||
| @@ -1104,6 +1120,14 @@ packages: | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "6.0.2" | ||||
|   pixel_snap: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: pixel_snap | ||||
|       sha256: "677410ea37b07cd37ecb6d5e6c0d8d7615a7cf3bd92ba406fd1ac57e937d1fb0" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "0.1.5" | ||||
|   platform: | ||||
|     dependency: transitive | ||||
|     description: | ||||
| @@ -1453,6 +1477,30 @@ packages: | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "3.1.2" | ||||
|   super_clipboard: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: super_clipboard | ||||
|       sha256: cdab725bac26655ebd189f4d202d694a8cbc1c21e0f0478ccd7829c71716f09b | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "0.8.17" | ||||
|   super_drag_and_drop: | ||||
|     dependency: "direct main" | ||||
|     description: | ||||
|       name: super_drag_and_drop | ||||
|       sha256: "8e00c6082646076f80b972b39d9c27b5311082ea1e8add5fa370ce11c410f7de" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "0.8.17" | ||||
|   super_native_extensions: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: super_native_extensions | ||||
|       sha256: fa55d452d34b7112453afbb9fa4d13c0527ff201630d10d86546497179030544 | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "0.8.17" | ||||
|   synchronized: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|   | ||||
| @@ -51,6 +51,7 @@ dependencies: | ||||
|   sqflite: ^2.3.3+1 | ||||
|   protocol_handler: ^0.2.0 | ||||
|   markdown: ^7.2.2 | ||||
|   super_drag_and_drop: ^0.8.17 | ||||
|  | ||||
| dev_dependencies: | ||||
|   flutter_test: | ||||
|   | ||||
| @@ -12,10 +12,12 @@ | ||||
| #include <flutter_acrylic/flutter_acrylic_plugin.h> | ||||
| #include <flutter_secure_storage_windows/flutter_secure_storage_windows_plugin.h> | ||||
| #include <flutter_webrtc/flutter_web_r_t_c_plugin.h> | ||||
| #include <irondash_engine_context/irondash_engine_context_plugin_c_api.h> | ||||
| #include <livekit_client/live_kit_plugin.h> | ||||
| #include <permission_handler_windows/permission_handler_windows_plugin.h> | ||||
| #include <protocol_handler_windows/protocol_handler_windows_plugin_c_api.h> | ||||
| #include <sentry_flutter/sentry_flutter_plugin.h> | ||||
| #include <super_native_extensions/super_native_extensions_plugin_c_api.h> | ||||
| #include <url_launcher_windows/url_launcher_windows.h> | ||||
| #include <video_player_win/video_player_win_plugin_c_api.h> | ||||
|  | ||||
| @@ -32,6 +34,8 @@ void RegisterPlugins(flutter::PluginRegistry* registry) { | ||||
|       registry->GetRegistrarForPlugin("FlutterSecureStorageWindowsPlugin")); | ||||
|   FlutterWebRTCPluginRegisterWithRegistrar( | ||||
|       registry->GetRegistrarForPlugin("FlutterWebRTCPlugin")); | ||||
|   IrondashEngineContextPluginCApiRegisterWithRegistrar( | ||||
|       registry->GetRegistrarForPlugin("IrondashEngineContextPluginCApi")); | ||||
|   LiveKitPluginRegisterWithRegistrar( | ||||
|       registry->GetRegistrarForPlugin("LiveKitPlugin")); | ||||
|   PermissionHandlerWindowsPluginRegisterWithRegistrar( | ||||
| @@ -40,6 +44,8 @@ void RegisterPlugins(flutter::PluginRegistry* registry) { | ||||
|       registry->GetRegistrarForPlugin("ProtocolHandlerWindowsPluginCApi")); | ||||
|   SentryFlutterPluginRegisterWithRegistrar( | ||||
|       registry->GetRegistrarForPlugin("SentryFlutterPlugin")); | ||||
|   SuperNativeExtensionsPluginCApiRegisterWithRegistrar( | ||||
|       registry->GetRegistrarForPlugin("SuperNativeExtensionsPluginCApi")); | ||||
|   UrlLauncherWindowsRegisterWithRegistrar( | ||||
|       registry->GetRegistrarForPlugin("UrlLauncherWindows")); | ||||
|   VideoPlayerWinPluginCApiRegisterWithRegistrar( | ||||
|   | ||||
| @@ -9,10 +9,12 @@ list(APPEND FLUTTER_PLUGIN_LIST | ||||
|   flutter_acrylic | ||||
|   flutter_secure_storage_windows | ||||
|   flutter_webrtc | ||||
|   irondash_engine_context | ||||
|   livekit_client | ||||
|   permission_handler_windows | ||||
|   protocol_handler_windows | ||||
|   sentry_flutter | ||||
|   super_native_extensions | ||||
|   url_launcher_windows | ||||
|   video_player_win | ||||
| ) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user