✨ Link expand
This commit is contained in:
		| @@ -227,6 +227,12 @@ PODS: | ||||
|   - livekit_client (2.2.4): | ||||
|     - Flutter | ||||
|     - WebRTC-SDK (= 125.6422.04) | ||||
|   - media_kit_libs_ios_video (1.0.4): | ||||
|     - Flutter | ||||
|   - media_kit_native_event_loop (1.0.0): | ||||
|     - Flutter | ||||
|   - media_kit_video (0.0.1): | ||||
|     - Flutter | ||||
|   - nanopb (2.30910.0): | ||||
|     - nanopb/decode (= 2.30910.0) | ||||
|     - nanopb/encode (= 2.30910.0) | ||||
| @@ -248,6 +254,8 @@ PODS: | ||||
|     - PromisesObjC (= 2.4.0) | ||||
|   - protocol_handler_ios (0.0.1): | ||||
|     - Flutter | ||||
|   - screen_brightness_ios (0.1.0): | ||||
|     - Flutter | ||||
|   - SDWebImage (5.19.6): | ||||
|     - SDWebImage/Core (= 5.19.6) | ||||
|   - SDWebImage/Core (5.19.6) | ||||
| @@ -263,9 +271,8 @@ PODS: | ||||
|   - TOCropViewController (2.7.4) | ||||
|   - url_launcher_ios (0.0.1): | ||||
|     - Flutter | ||||
|   - video_player_avfoundation (0.0.1): | ||||
|   - volume_controller (0.0.1): | ||||
|     - Flutter | ||||
|     - FlutterMacOS | ||||
|   - wakelock_plus (0.0.1): | ||||
|     - Flutter | ||||
|   - WebRTC-SDK (125.6422.04) | ||||
| @@ -287,17 +294,21 @@ DEPENDENCIES: | ||||
|   - image_cropper (from `.symlinks/plugins/image_cropper/ios`) | ||||
|   - image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`) | ||||
|   - livekit_client (from `.symlinks/plugins/livekit_client/ios`) | ||||
|   - media_kit_libs_ios_video (from `.symlinks/plugins/media_kit_libs_ios_video/ios`) | ||||
|   - media_kit_native_event_loop (from `.symlinks/plugins/media_kit_native_event_loop/ios`) | ||||
|   - media_kit_video (from `.symlinks/plugins/media_kit_video/ios`) | ||||
|   - package_info_plus (from `.symlinks/plugins/package_info_plus/ios`) | ||||
|   - pasteboard (from `.symlinks/plugins/pasteboard/ios`) | ||||
|   - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`) | ||||
|   - permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`) | ||||
|   - pointer_interceptor_ios (from `.symlinks/plugins/pointer_interceptor_ios/ios`) | ||||
|   - protocol_handler_ios (from `.symlinks/plugins/protocol_handler_ios/ios`) | ||||
|   - 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 (from `.symlinks/plugins/sqflite/darwin`) | ||||
|   - url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`) | ||||
|   - video_player_avfoundation (from `.symlinks/plugins/video_player_avfoundation/darwin`) | ||||
|   - volume_controller (from `.symlinks/plugins/volume_controller/ios`) | ||||
|   - wakelock_plus (from `.symlinks/plugins/wakelock_plus/ios`) | ||||
|  | ||||
| SPEC REPOS: | ||||
| @@ -362,6 +373,12 @@ EXTERNAL SOURCES: | ||||
|     :path: ".symlinks/plugins/image_picker_ios/ios" | ||||
|   livekit_client: | ||||
|     :path: ".symlinks/plugins/livekit_client/ios" | ||||
|   media_kit_libs_ios_video: | ||||
|     :path: ".symlinks/plugins/media_kit_libs_ios_video/ios" | ||||
|   media_kit_native_event_loop: | ||||
|     :path: ".symlinks/plugins/media_kit_native_event_loop/ios" | ||||
|   media_kit_video: | ||||
|     :path: ".symlinks/plugins/media_kit_video/ios" | ||||
|   package_info_plus: | ||||
|     :path: ".symlinks/plugins/package_info_plus/ios" | ||||
|   pasteboard: | ||||
| @@ -374,6 +391,8 @@ EXTERNAL SOURCES: | ||||
|     :path: ".symlinks/plugins/pointer_interceptor_ios/ios" | ||||
|   protocol_handler_ios: | ||||
|     :path: ".symlinks/plugins/protocol_handler_ios/ios" | ||||
|   screen_brightness_ios: | ||||
|     :path: ".symlinks/plugins/screen_brightness_ios/ios" | ||||
|   share_plus: | ||||
|     :path: ".symlinks/plugins/share_plus/ios" | ||||
|   shared_preferences_foundation: | ||||
| @@ -382,8 +401,8 @@ EXTERNAL SOURCES: | ||||
|     :path: ".symlinks/plugins/sqflite/darwin" | ||||
|   url_launcher_ios: | ||||
|     :path: ".symlinks/plugins/url_launcher_ios/ios" | ||||
|   video_player_avfoundation: | ||||
|     :path: ".symlinks/plugins/video_player_avfoundation/darwin" | ||||
|   volume_controller: | ||||
|     :path: ".symlinks/plugins/volume_controller/ios" | ||||
|   wakelock_plus: | ||||
|     :path: ".symlinks/plugins/wakelock_plus/ios" | ||||
|  | ||||
| @@ -423,6 +442,9 @@ SPEC CHECKSUMS: | ||||
|   image_cropper: 37d40f62177c101ff4c164906d259ea2c3aa70cf | ||||
|   image_picker_ios: c560581cceedb403a6ff17f2f816d7fea1421fc1 | ||||
|   livekit_client: d079c5f040d4bf2b80440ff0ae997725a183e4bc | ||||
|   media_kit_libs_ios_video: a5fe24bc7875ccd6378a0978c13185e1344651c1 | ||||
|   media_kit_native_event_loop: e6b2ab20cf0746eb1c33be961fcf79667304fa2a | ||||
|   media_kit_video: 5da63f157170e5bf303bf85453b7ef6971218a2e | ||||
|   nanopb: 438bc412db1928dac798aa6fd75726007be04262 | ||||
|   package_info_plus: 58f0028419748fad15bf008b270aaa8e54380b1c | ||||
|   pasteboard: 982969ebaa7c78af3e6cc7761e8f5e77565d9ce0 | ||||
| @@ -432,6 +454,7 @@ SPEC CHECKSUMS: | ||||
|   PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47 | ||||
|   PromisesSwift: 9d77319bbe72ebf6d872900551f7eeba9bce2851 | ||||
|   protocol_handler_ios: a5db8abc38526ee326988b808be621e5fd568990 | ||||
|   screen_brightness_ios: 715ca807df953bf676d339f11464e438143ee625 | ||||
|   SDWebImage: a79252b60f4678812d94316c91da69ec83089c9f | ||||
|   share_plus: 8875f4f2500512ea181eef553c3e27dba5135aad | ||||
|   shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78 | ||||
| @@ -439,7 +462,7 @@ SPEC CHECKSUMS: | ||||
|   SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4 | ||||
|   TOCropViewController: 80b8985ad794298fb69d3341de183f33d1853654 | ||||
|   url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe | ||||
|   video_player_avfoundation: 7c6c11d8470e1675df7397027218274b6d2360b3 | ||||
|   volume_controller: 531ddf792994285c9b17f9d8a7e4dcdd29b3eae9 | ||||
|   wakelock_plus: 78ec7c5b202cab7761af8e2b2b3d0671be6c4ae1 | ||||
|   WebRTC-SDK: c3d69a87e7185fad3568f6f3cff7c9ac5890acf3 | ||||
|  | ||||
|   | ||||
| @@ -6,12 +6,14 @@ import 'package:flutter/material.dart'; | ||||
| import 'package:flutter_acrylic/flutter_acrylic.dart'; | ||||
| import 'package:get/get.dart'; | ||||
| import 'package:go_router/go_router.dart'; | ||||
| import 'package:media_kit/media_kit.dart'; | ||||
| import 'package:protocol_handler/protocol_handler.dart'; | ||||
| import 'package:provider/provider.dart'; | ||||
| import 'package:solian/bootstrapper.dart'; | ||||
| import 'package:solian/firebase_options.dart'; | ||||
| import 'package:solian/platform.dart'; | ||||
| import 'package:solian/providers/attachment_uploader.dart'; | ||||
| import 'package:solian/providers/link_expander.dart'; | ||||
| import 'package:solian/providers/stickers.dart'; | ||||
| import 'package:solian/providers/theme_switcher.dart'; | ||||
| import 'package:solian/providers/websocket.dart'; | ||||
| @@ -31,6 +33,7 @@ import 'package:flutter_web_plugins/url_strategy.dart' show usePathUrlStrategy; | ||||
|  | ||||
| void main() async { | ||||
|   WidgetsFlutterBinding.ensureInitialized(); | ||||
|   MediaKit.ensureInitialized(); | ||||
|  | ||||
|   await Future.wait([ | ||||
|     _initializeFirebase(), | ||||
| @@ -126,5 +129,6 @@ class SolianApp extends StatelessWidget { | ||||
|     Get.lazyPut(() => RealmProvider()); | ||||
|     Get.lazyPut(() => ChatCallProvider()); | ||||
|     Get.lazyPut(() => AttachmentUploaderController()); | ||||
|     Get.lazyPut(() => LinkExpandController()); | ||||
|   } | ||||
| } | ||||
|   | ||||
							
								
								
									
										65
									
								
								lib/models/link.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								lib/models/link.dart
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,65 @@ | ||||
| class LinkMeta { | ||||
|   int id; | ||||
|   DateTime createdAt; | ||||
|   DateTime updatedAt; | ||||
|   DateTime? deletedAt; | ||||
|   String entryId; | ||||
|   String? icon; | ||||
|   String url; | ||||
|   String? title; | ||||
|   String? image; | ||||
|   String? video; | ||||
|   String? audio; | ||||
|   String? description; | ||||
|   String? siteName; | ||||
|  | ||||
|   LinkMeta({ | ||||
|     required this.id, | ||||
|     required this.createdAt, | ||||
|     required this.updatedAt, | ||||
|     required this.deletedAt, | ||||
|     required this.entryId, | ||||
|     required this.icon, | ||||
|     required this.url, | ||||
|     required this.title, | ||||
|     required this.image, | ||||
|     required this.video, | ||||
|     required this.audio, | ||||
|     required this.description, | ||||
|     required this.siteName, | ||||
|   }); | ||||
|  | ||||
|   factory LinkMeta.fromJson(Map<String, dynamic> json) => LinkMeta( | ||||
|         id: json['id'], | ||||
|         createdAt: DateTime.parse(json['created_at']), | ||||
|         updatedAt: DateTime.parse(json['updated_at']), | ||||
|         deletedAt: json['deleted_at'] != null | ||||
|             ? DateTime.parse(json['deleted_at']) | ||||
|             : null, | ||||
|         entryId: json['entry_id'], | ||||
|         icon: json['icon'], | ||||
|         url: json['url'], | ||||
|         title: json['title'], | ||||
|         image: json['image'], | ||||
|         video: json['video'], | ||||
|         audio: json['audio'], | ||||
|         description: json['description'], | ||||
|         siteName: json['site_name'], | ||||
|       ); | ||||
|  | ||||
|   Map<String, dynamic> toJson() => { | ||||
|         'id': id, | ||||
|         'created_at': createdAt.toIso8601String(), | ||||
|         'updated_at': updatedAt.toIso8601String(), | ||||
|         'deleted_at': deletedAt?.toIso8601String(), | ||||
|         'entry_id': entryId, | ||||
|         'icon': icon, | ||||
|         'url': url, | ||||
|         'title': title, | ||||
|         'image': image, | ||||
|         'video': video, | ||||
|         'audio': audio, | ||||
|         'description': description, | ||||
|         'site_name': siteName, | ||||
|       }; | ||||
| } | ||||
							
								
								
									
										25
									
								
								lib/providers/link_expander.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								lib/providers/link_expander.dart
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,25 @@ | ||||
| import 'dart:convert'; | ||||
| import 'dart:developer'; | ||||
|  | ||||
| import 'package:get/get.dart'; | ||||
| import 'package:solian/models/link.dart'; | ||||
| import 'package:solian/services.dart'; | ||||
|  | ||||
| class LinkExpandController extends GetxController { | ||||
|   final Map<String, LinkMeta?> _cachedResponse = {}; | ||||
|  | ||||
|   Future<LinkMeta?> expandLink(String url) async { | ||||
|     final target = utf8.fuse(base64).encode(url); | ||||
|     if (_cachedResponse.containsKey(target)) return _cachedResponse[target]; | ||||
|     final client = ServiceFinder.configureClient('dealer'); | ||||
|     final resp = await client.get('/api/links/$target'); | ||||
|     if (resp.statusCode != 200) { | ||||
|       log('Unable to expand link ($url), status: ${resp.statusCode}, response: ${resp.body}'); | ||||
|       _cachedResponse[target] = null; | ||||
|       return null; | ||||
|     } | ||||
|     final result = LinkMeta.fromJson(resp.body); | ||||
|     _cachedResponse[target] = result; | ||||
|     return result; | ||||
|   } | ||||
| } | ||||
| @@ -252,7 +252,7 @@ class _AttachmentEditorPopupState extends State<AttachmentEditorPopup> { | ||||
|         .listMetadata(widget.initialAttachments ?? List.empty()) | ||||
|         .then((result) { | ||||
|       setState(() { | ||||
|         _attachments = result; | ||||
|         _attachments = List.from(result, growable: true); | ||||
|         _isBusy = false; | ||||
|         _isFirstTimeBusy = false; | ||||
|       }); | ||||
|   | ||||
| @@ -1,14 +1,14 @@ | ||||
| import 'package:cached_network_image/cached_network_image.dart'; | ||||
| import 'package:chewie/chewie.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:flutter_animate/flutter_animate.dart'; | ||||
| import 'package:get/get.dart'; | ||||
| import 'package:media_kit/media_kit.dart'; | ||||
| import 'package:media_kit_video/media_kit_video.dart'; | ||||
| import 'package:solian/models/attachment.dart'; | ||||
| import 'package:solian/platform.dart'; | ||||
| import 'package:solian/services.dart'; | ||||
| import 'package:solian/widgets/sized_container.dart'; | ||||
| import 'package:url_launcher/url_launcher_string.dart'; | ||||
| import 'package:video_player/video_player.dart'; | ||||
|  | ||||
| class AttachmentItem extends StatefulWidget { | ||||
|   final String parentId; | ||||
| @@ -231,22 +231,20 @@ class _AttachmentItemVideo extends StatefulWidget { | ||||
| } | ||||
|  | ||||
| class _AttachmentItemVideoState extends State<_AttachmentItemVideo> { | ||||
|   VideoPlayerController? _playerController; | ||||
|   ChewieController? _chewieController; | ||||
|   late final _player = Player( | ||||
|     configuration: const PlayerConfiguration( | ||||
|       logLevel: MPVLogLevel.error, | ||||
|     ), | ||||
|   ); | ||||
|  | ||||
|   late final _controller = VideoController(_player); | ||||
|  | ||||
|   bool _showContent = false; | ||||
|  | ||||
|   Future<void> _startLoad() async { | ||||
|     final ratio = widget.item.metadata?['ratio'] ?? 16 / 9; | ||||
|     _playerController = VideoPlayerController.networkUrl( | ||||
|       Uri.parse( | ||||
|         ServiceFinder.buildUrl('files', '/attachments/${widget.item.rid}'), | ||||
|       ), | ||||
|     ); | ||||
|     _playerController!.initialize(); | ||||
|     _chewieController = ChewieController( | ||||
|       aspectRatio: ratio, | ||||
|       videoPlayerController: _playerController!, | ||||
|     await _player.open( | ||||
|       Media(ServiceFinder.buildUrl('files', '/attachments/${widget.item.rid}')), | ||||
|       play: false, | ||||
|     ); | ||||
|     setState(() => _showContent = true); | ||||
|   } | ||||
| @@ -259,17 +257,10 @@ class _AttachmentItemVideoState extends State<_AttachmentItemVideo> { | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|   void dispose() { | ||||
|     _playerController?.dispose(); | ||||
|     _chewieController?.dispose(); | ||||
|     super.dispose(); | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     final ratio = widget.item.metadata?['ratio'] ?? 16 / 9; | ||||
|     if (!_showContent || _chewieController == null) { | ||||
|     if (!_showContent) { | ||||
|       return GestureDetector( | ||||
|         child: AspectRatio( | ||||
|           aspectRatio: ratio, | ||||
| @@ -307,8 +298,15 @@ class _AttachmentItemVideoState extends State<_AttachmentItemVideo> { | ||||
|       ); | ||||
|     } | ||||
|  | ||||
|     return Chewie( | ||||
|       controller: _chewieController!, | ||||
|     return Video( | ||||
|       aspectRatio: ratio, | ||||
|       controller: _controller, | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|   void dispose() { | ||||
|     _player.dispose(); | ||||
|     super.dispose(); | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -73,7 +73,7 @@ class _AttachmentListState extends State<AttachmentList> { | ||||
|     int portrait = 0, square = 0, landscape = 0; | ||||
|     for (var entry in _attachmentsMeta) { | ||||
|       if (entry == null) continue; | ||||
|       if (entry!.metadata?['ratio'] != null) { | ||||
|       if (entry.metadata?['ratio'] != null) { | ||||
|         if (entry.metadata?['ratio'] is int) { | ||||
|           consistentValue ??= entry.metadata?['ratio'].toDouble(); | ||||
|         } else { | ||||
|   | ||||
| @@ -1,6 +1,7 @@ | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:get/get.dart'; | ||||
| import 'package:solian/models/event.dart'; | ||||
| import 'package:solian/widgets/link_expansion.dart'; | ||||
| import 'package:solian/widgets/markdown_text_content.dart'; | ||||
|  | ||||
| class ChatEventMessage extends StatelessWidget { | ||||
| @@ -53,11 +54,28 @@ class ChatEventMessage extends StatelessWidget { | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   Widget _buildLinkExpansion() { | ||||
|     final body = EventMessageBody.fromJson(item.body); | ||||
|     return LinkExpansion(content: body.text); | ||||
|   } | ||||
|  | ||||
|   Widget _buildBody(BuildContext context) { | ||||
|     if (isMerged) { | ||||
|       return _buildContent(context).paddingOnly(left: 52); | ||||
|       return Column( | ||||
|         crossAxisAlignment: CrossAxisAlignment.start, | ||||
|         children: [ | ||||
|           _buildContent(context).paddingOnly(left: 4), | ||||
|           _buildLinkExpansion(), | ||||
|         ], | ||||
|       ).paddingOnly(left: 48); | ||||
|     } else { | ||||
|       return _buildContent(context); | ||||
|       return Column( | ||||
|         crossAxisAlignment: CrossAxisAlignment.start, | ||||
|         children: [ | ||||
|           _buildContent(context), | ||||
|           _buildLinkExpansion(), | ||||
|         ], | ||||
|       ); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   | ||||
							
								
								
									
										108
									
								
								lib/widgets/link_expansion.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										108
									
								
								lib/widgets/link_expansion.dart
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,108 @@ | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:flutter_markdown/flutter_markdown.dart'; | ||||
| import 'package:get/get.dart'; | ||||
| import 'package:solian/providers/link_expander.dart'; | ||||
| import 'package:url_launcher/url_launcher_string.dart'; | ||||
|  | ||||
| class LinkExpansion extends StatelessWidget { | ||||
|   final String content; | ||||
|  | ||||
|   const LinkExpansion({super.key, required this.content}); | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     final linkRegex = RegExp( | ||||
|       r'(?:(?:https?|ftp):\/\/|www\.)' | ||||
|       r'(?:[-_a-z0-9]+\.)*(?:[-a-z0-9]+\.[-a-z0-9]+)' | ||||
|       r'[^\s<]*' | ||||
|       r'[^\s<?!.,:*_~]', | ||||
|     ); | ||||
|     final matches = linkRegex.allMatches(content); | ||||
|     if (matches.isEmpty) { | ||||
|       return const SizedBox(); | ||||
|     } | ||||
|  | ||||
|     final LinkExpandController expandController = Get.find(); | ||||
|  | ||||
|     return Column( | ||||
|       children: matches.map((x) { | ||||
|         return FutureBuilder( | ||||
|           future: expandController.expandLink(x.group(0)!), | ||||
|           builder: (context, snapshot) { | ||||
|             if (!snapshot.hasData) { | ||||
|               return const SizedBox(); | ||||
|             } | ||||
|  | ||||
|             final isRichDescription = [ | ||||
|               "solsynth.dev", | ||||
|             ].contains(Uri.parse(snapshot.data!.url).host); | ||||
|  | ||||
|             return GestureDetector( | ||||
|               child: Card( | ||||
|                 child: Column( | ||||
|                   crossAxisAlignment: CrossAxisAlignment.start, | ||||
|                   children: [ | ||||
|                     if ([ | ||||
|                       snapshot.data!.icon != null && | ||||
|                           (snapshot.data!.icon?.startsWith('http') ?? false), | ||||
|                       snapshot.data!.siteName != null | ||||
|                     ].any((x) => x)) | ||||
|                       Row( | ||||
|                         children: [ | ||||
|                           if (snapshot.data!.icon != null && | ||||
|                               (snapshot.data!.icon?.startsWith('http') ?? | ||||
|                                   false)) | ||||
|                             ClipRRect( | ||||
|                               borderRadius: const BorderRadius.all( | ||||
|                                 Radius.circular(8), | ||||
|                               ), | ||||
|                               child: Image.network( | ||||
|                                 snapshot.data!.icon!, | ||||
|                                 width: 32, | ||||
|                                 height: 32, | ||||
|                               ), | ||||
|                             ).paddingOnly(right: 8), | ||||
|                           if (snapshot.data!.siteName != null) | ||||
|                             Text( | ||||
|                               snapshot.data!.siteName!, | ||||
|                               style: Theme.of(context).textTheme.labelLarge, | ||||
|                             ), | ||||
|                         ], | ||||
|                       ).paddingOnly(bottom: 8), | ||||
|                     if (snapshot.data!.image != null && | ||||
|                         (snapshot.data!.image?.startsWith('http') ?? false)) | ||||
|                       ClipRRect( | ||||
|                         borderRadius: const BorderRadius.all( | ||||
|                           Radius.circular(8), | ||||
|                         ), | ||||
|                         child: Image.network( | ||||
|                           snapshot.data!.image!, | ||||
|                         ), | ||||
|                       ).paddingOnly(bottom: 8), | ||||
|                     Text( | ||||
|                       snapshot.data!.title ?? 'No Title', | ||||
|                       maxLines: 1, | ||||
|                       overflow: TextOverflow.fade, | ||||
|                       style: Theme.of(context).textTheme.bodyLarge, | ||||
|                     ), | ||||
|                     if (snapshot.data!.description != null && isRichDescription) | ||||
|                       MarkdownBody(data: snapshot.data!.description!) | ||||
|                     else if (snapshot.data!.description != null) | ||||
|                       Text( | ||||
|                         snapshot.data!.description!, | ||||
|                         maxLines: 3, | ||||
|                         overflow: TextOverflow.ellipsis, | ||||
|                       ), | ||||
|                   ], | ||||
|                 ).paddingAll(12), | ||||
|               ), | ||||
|               onTap: () { | ||||
|                 launchUrlString(x.group(0)!); | ||||
|               }, | ||||
|             ); | ||||
|           }, | ||||
|         ); | ||||
|       }).toList(), | ||||
|     ); | ||||
|   } | ||||
| } | ||||
| @@ -11,6 +11,8 @@ | ||||
| #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 <media_kit_libs_linux/media_kit_libs_linux_plugin.h> | ||||
| #include <media_kit_video/media_kit_video_plugin.h> | ||||
| #include <pasteboard/pasteboard_plugin.h> | ||||
| #include <url_launcher_linux/url_launcher_plugin.h> | ||||
|  | ||||
| @@ -30,6 +32,12 @@ 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) media_kit_libs_linux_registrar = | ||||
|       fl_plugin_registry_get_registrar_for_plugin(registry, "MediaKitLibsLinuxPlugin"); | ||||
|   media_kit_libs_linux_plugin_register_with_registrar(media_kit_libs_linux_registrar); | ||||
|   g_autoptr(FlPluginRegistrar) media_kit_video_registrar = | ||||
|       fl_plugin_registry_get_registrar_for_plugin(registry, "MediaKitVideoPlugin"); | ||||
|   media_kit_video_plugin_register_with_registrar(media_kit_video_registrar); | ||||
|   g_autoptr(FlPluginRegistrar) pasteboard_registrar = | ||||
|       fl_plugin_registry_get_registrar_for_plugin(registry, "PasteboardPlugin"); | ||||
|   pasteboard_plugin_register_with_registrar(pasteboard_registrar); | ||||
|   | ||||
| @@ -8,11 +8,14 @@ list(APPEND FLUTTER_PLUGIN_LIST | ||||
|   flutter_acrylic | ||||
|   flutter_secure_storage_linux | ||||
|   flutter_webrtc | ||||
|   media_kit_libs_linux | ||||
|   media_kit_video | ||||
|   pasteboard | ||||
|   url_launcher_linux | ||||
| ) | ||||
|  | ||||
| list(APPEND FLUTTER_FFI_PLUGIN_LIST | ||||
|   media_kit_native_event_loop | ||||
| ) | ||||
|  | ||||
| set(PLUGIN_BUNDLED_LIBRARIES) | ||||
|   | ||||
| @@ -18,15 +18,17 @@ import flutter_webrtc | ||||
| import gal | ||||
| import livekit_client | ||||
| import macos_window_utils | ||||
| import media_kit_libs_macos_video | ||||
| import media_kit_video | ||||
| import package_info_plus | ||||
| import pasteboard | ||||
| import path_provider_foundation | ||||
| import protocol_handler_macos | ||||
| import screen_brightness_macos | ||||
| import share_plus | ||||
| import shared_preferences_foundation | ||||
| import sqflite | ||||
| import url_launcher_macos | ||||
| import video_player_avfoundation | ||||
| import wakelock_plus | ||||
|  | ||||
| func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { | ||||
| @@ -43,14 +45,16 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { | ||||
|   GalPlugin.register(with: registry.registrar(forPlugin: "GalPlugin")) | ||||
|   LiveKitPlugin.register(with: registry.registrar(forPlugin: "LiveKitPlugin")) | ||||
|   MacOSWindowUtilsPlugin.register(with: registry.registrar(forPlugin: "MacOSWindowUtilsPlugin")) | ||||
|   MediaKitLibsMacosVideoPlugin.register(with: registry.registrar(forPlugin: "MediaKitLibsMacosVideoPlugin")) | ||||
|   MediaKitVideoPlugin.register(with: registry.registrar(forPlugin: "MediaKitVideoPlugin")) | ||||
|   FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin")) | ||||
|   PasteboardPlugin.register(with: registry.registrar(forPlugin: "PasteboardPlugin")) | ||||
|   PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) | ||||
|   ProtocolHandlerMacosPlugin.register(with: registry.registrar(forPlugin: "ProtocolHandlerMacosPlugin")) | ||||
|   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")) | ||||
|   FVPVideoPlayerPlugin.register(with: registry.registrar(forPlugin: "FVPVideoPlayerPlugin")) | ||||
|   WakelockPlusMacosPlugin.register(with: registry.registrar(forPlugin: "WakelockPlusMacosPlugin")) | ||||
| } | ||||
|   | ||||
							
								
								
									
										216
									
								
								pubspec.lock
									
									
									
									
									
								
							
							
						
						
									
										216
									
								
								pubspec.lock
									
									
									
									
									
								
							| @@ -206,14 +206,6 @@ packages: | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "2.0.3" | ||||
|   chewie: | ||||
|     dependency: "direct main" | ||||
|     description: | ||||
|       name: chewie | ||||
|       sha256: "2243e41e79e865d426d9dd9c1a9624aa33c4ad11de2d0cd680f826e2cd30e879" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "1.8.3" | ||||
|   cli_util: | ||||
|     dependency: transitive | ||||
|     description: | ||||
| @@ -286,14 +278,6 @@ packages: | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "3.0.5" | ||||
|   csslib: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: csslib | ||||
|       sha256: "706b5707578e0c1b4b7550f64078f0a0f19dec3f50a178ffae7006b0a9ca58fb" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "1.0.0" | ||||
|   cupertino_icons: | ||||
|     dependency: "direct main" | ||||
|     description: | ||||
| @@ -893,14 +877,6 @@ packages: | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "1.0.5" | ||||
|   html: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: html | ||||
|       sha256: "3a7812d5bcd2894edf53dfaf8cd640876cf6cef50a8f238745c8b8120ea74d3a" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "0.15.4" | ||||
|   http: | ||||
|     dependency: transitive | ||||
|     description: | ||||
| @@ -1165,6 +1141,78 @@ packages: | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "0.11.1" | ||||
|   media_kit: | ||||
|     dependency: "direct main" | ||||
|     description: | ||||
|       name: media_kit | ||||
|       sha256: "3289062540e3b8b9746e5c50d95bd78a9289826b7227e253dff806d002b9e67a" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "1.1.10+1" | ||||
|   media_kit_libs_android_video: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: media_kit_libs_android_video | ||||
|       sha256: "9dd8012572e4aff47516e55f2597998f0a378e3d588d0fad0ca1f11a53ae090c" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "1.3.6" | ||||
|   media_kit_libs_ios_video: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: media_kit_libs_ios_video | ||||
|       sha256: b5382994eb37a4564c368386c154ad70ba0cc78dacdd3fb0cd9f30db6d837991 | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "1.1.4" | ||||
|   media_kit_libs_linux: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: media_kit_libs_linux | ||||
|       sha256: e186891c31daa6bedab4d74dcdb4e8adfccc7d786bfed6ad81fe24a3b3010310 | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "1.1.3" | ||||
|   media_kit_libs_macos_video: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: media_kit_libs_macos_video | ||||
|       sha256: f26aa1452b665df288e360393758f84b911f70ffb3878032e1aabba23aa1032d | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "1.1.4" | ||||
|   media_kit_libs_video: | ||||
|     dependency: "direct main" | ||||
|     description: | ||||
|       name: media_kit_libs_video | ||||
|       sha256: "3688e0c31482074578652bf038ce6301a5d21e1eda6b54fc3117ffeb4bdba067" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "1.0.4" | ||||
|   media_kit_libs_windows_video: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: media_kit_libs_windows_video | ||||
|       sha256: "7bace5f35d9afcc7f9b5cdadb7541d2191a66bb3fc71bfa11c1395b3360f6122" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "1.0.9" | ||||
|   media_kit_native_event_loop: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: media_kit_native_event_loop | ||||
|       sha256: a605cf185499d14d58935b8784955a92a4bf0ff4e19a23de3d17a9106303930e | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "1.0.8" | ||||
|   media_kit_video: | ||||
|     dependency: "direct main" | ||||
|     description: | ||||
|       name: media_kit_video | ||||
|       sha256: c048d11a19e379aebbe810647636e3fc6d18374637e2ae12def4ff8a4b99a882 | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "1.2.4" | ||||
|   meta: | ||||
|     dependency: transitive | ||||
|     description: | ||||
| @@ -1509,6 +1557,62 @@ packages: | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "0.28.0" | ||||
|   safe_local_storage: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: safe_local_storage | ||||
|       sha256: ede4eb6cb7d88a116b3d3bf1df70790b9e2038bc37cb19112e381217c74d9440 | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "1.0.2" | ||||
|   screen_brightness: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: screen_brightness | ||||
|       sha256: ed8da4a4511e79422fc1aa88138e920e4008cd312b72cdaa15ccb426c0faaedd | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "0.2.2+1" | ||||
|   screen_brightness_android: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: screen_brightness_android | ||||
|       sha256: "3df10961e3a9e968a5e076fe27e7f4741fa8a1d3950bdeb48cf121ed529d0caf" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "0.1.0+2" | ||||
|   screen_brightness_ios: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: screen_brightness_ios | ||||
|       sha256: "99adc3ca5490b8294284aad5fcc87f061ad685050e03cf45d3d018fe398fd9a2" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "0.1.0" | ||||
|   screen_brightness_macos: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: screen_brightness_macos | ||||
|       sha256: "64b34e7e3f4900d7687c8e8fb514246845a73ecec05ab53483ed025bd4a899fd" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "0.1.0+1" | ||||
|   screen_brightness_platform_interface: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: screen_brightness_platform_interface | ||||
|       sha256: b211d07f0c96637a15fb06f6168617e18030d5d74ad03795dd8547a52717c171 | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "0.1.0" | ||||
|   screen_brightness_windows: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: screen_brightness_windows | ||||
|       sha256: "9261bf33d0fc2707d8cf16339ce25768100a65e70af0fcabaf032fc12408ba86" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "0.1.3" | ||||
|   sdp_transform: | ||||
|     dependency: transitive | ||||
|     description: | ||||
| @@ -1786,6 +1890,22 @@ packages: | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "0.3.1" | ||||
|   universal_platform: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: universal_platform | ||||
|       sha256: "64e16458a0ea9b99260ceb5467a214c1f298d647c659af1bff6d3bf82536b1ec" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "1.1.0" | ||||
|   uri_parser: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: uri_parser | ||||
|       sha256: "6543c9fd86d2862fac55d800a43e67c0dcd1a41677cb69c2f8edfe73bbcf1835" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "2.0.2" | ||||
|   url_launcher: | ||||
|     dependency: "direct main" | ||||
|     description: | ||||
| @@ -1866,46 +1986,6 @@ packages: | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "2.1.4" | ||||
|   video_player: | ||||
|     dependency: "direct main" | ||||
|     description: | ||||
|       name: video_player | ||||
|       sha256: e30df0d226c4ef82e2c150ebf6834b3522cf3f654d8e2f9419d376cdc071425d | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "2.9.1" | ||||
|   video_player_android: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: video_player_android | ||||
|       sha256: "4de50df9ee786f5891d3281e1e633d7b142ef1acf47392592eb91cba5d355849" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "2.6.0" | ||||
|   video_player_avfoundation: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: video_player_avfoundation | ||||
|       sha256: d1e9a824f2b324000dc8fb2dcb2a3285b6c1c7c487521c63306cc5b394f68a7c | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "2.6.1" | ||||
|   video_player_platform_interface: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: video_player_platform_interface | ||||
|       sha256: "236454725fafcacf98f0f39af0d7c7ab2ce84762e3b63f2cbb3ef9a7e0550bc6" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "6.2.2" | ||||
|   video_player_web: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: video_player_web | ||||
|       sha256: "6dcdd298136523eaf7dfc31abaf0dfba9aa8a8dbc96670e87e9d42b6f2caf774" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "2.3.2" | ||||
|   vm_service: | ||||
|     dependency: transitive | ||||
|     description: | ||||
| @@ -1914,6 +1994,14 @@ packages: | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "14.2.4" | ||||
|   volume_controller: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: volume_controller | ||||
|       sha256: c71d4c62631305df63b72da79089e078af2659649301807fa746088f365cb48e | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "2.0.8" | ||||
|   wakelock_plus: | ||||
|     dependency: "direct main" | ||||
|     description: | ||||
|   | ||||
| @@ -2,7 +2,7 @@ name: solian | ||||
| description: "The Solar Network App" | ||||
| publish_to: "none" | ||||
|  | ||||
| version: 1.2.1+18 | ||||
| version: 1.2.1+19 | ||||
|  | ||||
| environment: | ||||
|   sdk: ">=3.3.4 <4.0.0" | ||||
| @@ -67,9 +67,10 @@ dependencies: | ||||
|   collection: ^1.18.0 | ||||
|   firebase_crashlytics: ^4.0.4 | ||||
|   firebase_analytics: ^11.2.1 | ||||
|   video_player: ^2.9.1 | ||||
|   chewie: ^1.8.3 | ||||
|   firebase_performance: ^0.10.0+4 | ||||
|   media_kit: ^1.1.10+1 | ||||
|   media_kit_video: ^1.2.4 | ||||
|   media_kit_libs_video: ^1.0.4 | ||||
|  | ||||
| dev_dependencies: | ||||
|   flutter_test: | ||||
|   | ||||
| @@ -15,9 +15,12 @@ | ||||
| #include <flutter_webrtc/flutter_web_r_t_c_plugin.h> | ||||
| #include <gal/gal_plugin_c_api.h> | ||||
| #include <livekit_client/live_kit_plugin.h> | ||||
| #include <media_kit_libs_windows_video/media_kit_libs_windows_video_plugin_c_api.h> | ||||
| #include <media_kit_video/media_kit_video_plugin_c_api.h> | ||||
| #include <pasteboard/pasteboard_plugin.h> | ||||
| #include <permission_handler_windows/permission_handler_windows_plugin.h> | ||||
| #include <protocol_handler_windows/protocol_handler_windows_plugin_c_api.h> | ||||
| #include <screen_brightness_windows/screen_brightness_windows_plugin.h> | ||||
| #include <share_plus/share_plus_windows_plugin_c_api.h> | ||||
| #include <url_launcher_windows/url_launcher_windows.h> | ||||
|  | ||||
| @@ -40,12 +43,18 @@ void RegisterPlugins(flutter::PluginRegistry* registry) { | ||||
|       registry->GetRegistrarForPlugin("GalPluginCApi")); | ||||
|   LiveKitPluginRegisterWithRegistrar( | ||||
|       registry->GetRegistrarForPlugin("LiveKitPlugin")); | ||||
|   MediaKitLibsWindowsVideoPluginCApiRegisterWithRegistrar( | ||||
|       registry->GetRegistrarForPlugin("MediaKitLibsWindowsVideoPluginCApi")); | ||||
|   MediaKitVideoPluginCApiRegisterWithRegistrar( | ||||
|       registry->GetRegistrarForPlugin("MediaKitVideoPluginCApi")); | ||||
|   PasteboardPluginRegisterWithRegistrar( | ||||
|       registry->GetRegistrarForPlugin("PasteboardPlugin")); | ||||
|   PermissionHandlerWindowsPluginRegisterWithRegistrar( | ||||
|       registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin")); | ||||
|   ProtocolHandlerWindowsPluginCApiRegisterWithRegistrar( | ||||
|       registry->GetRegistrarForPlugin("ProtocolHandlerWindowsPluginCApi")); | ||||
|   ScreenBrightnessWindowsPluginRegisterWithRegistrar( | ||||
|       registry->GetRegistrarForPlugin("ScreenBrightnessWindowsPlugin")); | ||||
|   SharePlusWindowsPluginCApiRegisterWithRegistrar( | ||||
|       registry->GetRegistrarForPlugin("SharePlusWindowsPluginCApi")); | ||||
|   UrlLauncherWindowsRegisterWithRegistrar( | ||||
|   | ||||
| @@ -12,14 +12,18 @@ list(APPEND FLUTTER_PLUGIN_LIST | ||||
|   flutter_webrtc | ||||
|   gal | ||||
|   livekit_client | ||||
|   media_kit_libs_windows_video | ||||
|   media_kit_video | ||||
|   pasteboard | ||||
|   permission_handler_windows | ||||
|   protocol_handler_windows | ||||
|   screen_brightness_windows | ||||
|   share_plus | ||||
|   url_launcher_windows | ||||
| ) | ||||
|  | ||||
| list(APPEND FLUTTER_FFI_PLUGIN_LIST | ||||
|   media_kit_native_event_loop | ||||
| ) | ||||
|  | ||||
| set(PLUGIN_BUNDLED_LIBRARIES) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user