Compare commits

...

4 Commits

Author SHA1 Message Date
4a9ccc7c7a Save attachment to album 2024-12-08 00:25:53 +08:00
76cf08830b 🐛 Fix unable to repost 2024-12-07 23:40:26 +08:00
2cbb7fb29e 🐛 Optimized desktop titlebar styling 2024-12-07 22:34:02 +08:00
c55db308a1 🐛 Bug fixes for previous changes 2024-12-07 22:27:07 +08:00
21 changed files with 487 additions and 376 deletions

View File

@ -9,11 +9,13 @@
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" /> <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.BLUETOOTH" android:maxSdkVersion="30" /> <uses-permission android:name="android.permission.BLUETOOTH" android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" android:maxSdkVersion="30" /> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="29" />
<application <application
android:label="Solian" android:label="Solian"
android:name="${applicationName}" android:name="${applicationName}"
android:icon="@mipmap/ic_launcher"> android:icon="@mipmap/ic_launcher"
android:requestLegacyExternalStorage="true">
<activity <activity
android:name=".MainActivity" android:name=".MainActivity"
android:exported="true" android:exported="true"

View File

@ -385,5 +385,7 @@
"accountStatusLastSeen": "Last seen at {}", "accountStatusLastSeen": "Last seen at {}",
"postArticle": "Article on the Solar Network", "postArticle": "Article on the Solar Network",
"articleWrittenAt": "Written at {}", "articleWrittenAt": "Written at {}",
"articleEditedAt": "Edited at {}" "articleEditedAt": "Edited at {}",
"attachmentSaved": "Saved to album",
"openInAlbum": "Open in album"
} }

View File

@ -385,6 +385,7 @@
"accountStatusLastSeen": "最后一次在 {} 上线", "accountStatusLastSeen": "最后一次在 {} 上线",
"postArticle": "Solar Network 上的文章", "postArticle": "Solar Network 上的文章",
"articleWrittenAt": "发表于 {}", "articleWrittenAt": "发表于 {}",
"articleEditedAt": "编辑于 {}" "articleEditedAt": "编辑于 {}",
"attachmentSaved": "已保存到相册",
"openInAlbum": "在相册中打开"
} }

View File

@ -52,14 +52,14 @@ PODS:
- Firebase/Messaging (11.4.0): - Firebase/Messaging (11.4.0):
- Firebase/CoreOnly - Firebase/CoreOnly
- FirebaseMessaging (~> 11.4.0) - FirebaseMessaging (~> 11.4.0)
- firebase_analytics (11.3.5): - firebase_analytics (11.3.6):
- Firebase/Analytics (= 11.4.0) - Firebase/Analytics (= 11.4.0)
- firebase_core - firebase_core
- Flutter - Flutter
- firebase_core (3.8.0): - firebase_core (3.8.1):
- Firebase/CoreOnly (= 11.4.0) - Firebase/CoreOnly (= 11.4.0)
- Flutter - Flutter
- firebase_messaging (15.1.5): - firebase_messaging (15.1.6):
- Firebase/Messaging (= 11.4.0) - Firebase/Messaging (= 11.4.0)
- firebase_core - firebase_core
- Flutter - Flutter
@ -110,6 +110,9 @@ PODS:
- flutter_webrtc (0.12.2): - flutter_webrtc (0.12.2):
- Flutter - Flutter
- WebRTC-SDK (= 125.6422.06) - WebRTC-SDK (= 125.6422.06)
- gal (1.0.0):
- Flutter
- FlutterMacOS
- GoogleAppMeasurement (11.4.0): - GoogleAppMeasurement (11.4.0):
- GoogleAppMeasurement/AdIdSupport (= 11.4.0) - GoogleAppMeasurement/AdIdSupport (= 11.4.0)
- GoogleUtilities/AppDelegateSwizzler (~> 8.0) - GoogleUtilities/AppDelegateSwizzler (~> 8.0)
@ -225,6 +228,7 @@ DEPENDENCIES:
- flutter_native_splash (from `.symlinks/plugins/flutter_native_splash/ios`) - flutter_native_splash (from `.symlinks/plugins/flutter_native_splash/ios`)
- flutter_udid (from `.symlinks/plugins/flutter_udid/ios`) - flutter_udid (from `.symlinks/plugins/flutter_udid/ios`)
- flutter_webrtc (from `.symlinks/plugins/flutter_webrtc/ios`) - flutter_webrtc (from `.symlinks/plugins/flutter_webrtc/ios`)
- gal (from `.symlinks/plugins/gal/darwin`)
- image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`) - image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`)
- livekit_client (from `.symlinks/plugins/livekit_client/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_libs_ios_video (from `.symlinks/plugins/media_kit_libs_ios_video/ios`)
@ -288,6 +292,8 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/flutter_udid/ios" :path: ".symlinks/plugins/flutter_udid/ios"
flutter_webrtc: flutter_webrtc:
:path: ".symlinks/plugins/flutter_webrtc/ios" :path: ".symlinks/plugins/flutter_webrtc/ios"
gal:
:path: ".symlinks/plugins/gal/darwin"
image_picker_ios: image_picker_ios:
:path: ".symlinks/plugins/image_picker_ios/ios" :path: ".symlinks/plugins/image_picker_ios/ios"
livekit_client: livekit_client:
@ -330,9 +336,9 @@ SPEC CHECKSUMS:
DKPhotoGallery: b3834fecb755ee09a593d7c9e389d8b5d6deed60 DKPhotoGallery: b3834fecb755ee09a593d7c9e389d8b5d6deed60
file_picker: 09aa5ec1ab24135ccd7a1621c46c84134bfd6655 file_picker: 09aa5ec1ab24135ccd7a1621c46c84134bfd6655
Firebase: cf1b19f21410b029b6786a54e9764a0cacad3c99 Firebase: cf1b19f21410b029b6786a54e9764a0cacad3c99
firebase_analytics: fa7e5b20c2b58042e3301f5112a473d365bd490c firebase_analytics: 2815af29d49c1a994652abd37a5b001a88bc7b75
firebase_core: 9efc3ecf689cdbc90f13f4dc58108c83ea46b266 firebase_core: 418aed674e9a0b8b6088aec16cde82a811f6261f
firebase_messaging: 6bf60adb4b33a848d135e16bc363fb4924f98fba firebase_messaging: 98619a0572d82cfb3668e78859ba9f1110e268c9
FirebaseAnalytics: 3feef9ae8733c567866342a1000691baaa7cad49 FirebaseAnalytics: 3feef9ae8733c567866342a1000691baaa7cad49
FirebaseCore: e0510f1523bc0eb21653cac00792e1e2bd6f1771 FirebaseCore: e0510f1523bc0eb21653cac00792e1e2bd6f1771
FirebaseCoreInternal: f47dd28ae7782e6a4738aad3106071a8fe0af604 FirebaseCoreInternal: f47dd28ae7782e6a4738aad3106071a8fe0af604
@ -342,6 +348,7 @@ SPEC CHECKSUMS:
flutter_native_splash: e8a1e01082d97a8099d973f919f57904c925008a flutter_native_splash: e8a1e01082d97a8099d973f919f57904c925008a
flutter_udid: a2482c67a61b9c806ef59dd82ed8d007f1b7ac04 flutter_udid: a2482c67a61b9c806ef59dd82ed8d007f1b7ac04
flutter_webrtc: 1a53bd24f97bcfeff512f13699e721897f261563 flutter_webrtc: 1a53bd24f97bcfeff512f13699e721897f261563
gal: 61e868295d28fe67ffa297fae6dacebf56fd53e1
GoogleAppMeasurement: 987769c4ca6b968f2479fbcc9fe3ce34af454b8e GoogleAppMeasurement: 987769c4ca6b968f2479fbcc9fe3ce34af454b8e
GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7 GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7
GoogleUtilities: 26a3abef001b6533cf678d3eb38fd3f614b7872d GoogleUtilities: 26a3abef001b6533cf678d3eb38fd3f614b7872d

View File

@ -39,6 +39,8 @@
<string>Grant access to Photo Library will allow Solian record audio for your post.</string> <string>Grant access to Photo Library will allow Solian record audio for your post.</string>
<key>NSPhotoLibraryUsageDescription</key> <key>NSPhotoLibraryUsageDescription</key>
<string>Grant access to Photo Library will allow Solian upload photo or video for your post.</string> <string>Grant access to Photo Library will allow Solian upload photo or video for your post.</string>
<key>NSPhotoLibraryAddUsageDescription</key>
<string>Grant access to Photo Library will allow Solian download photo to album for you.</string>
<key>UIApplicationSupportsIndirectInputEvents</key> <key>UIApplicationSupportsIndirectInputEvents</key>
<true/> <true/>
<key>UIBackgroundModes</key> <key>UIBackgroundModes</key>

View File

@ -206,7 +206,7 @@ class PostWriteController extends ChangeNotifier {
tags = List.from(post.tags.map((ele) => ele.alias)); tags = List.from(post.tags.map((ele) => ele.alias));
attachments.addAll(post.preload?.attachments?.map((ele) => PostWriteMedia(ele)) ?? []); attachments.addAll(post.preload?.attachments?.map((ele) => PostWriteMedia(ele)) ?? []);
if (post.preload?.thumbnail != null) { if (post.preload?.thumbnail != null && (post.preload?.thumbnail?.rid.isNotEmpty ?? false)) {
thumbnail = PostWriteMedia(post.preload!.thumbnail); thumbnail = PostWriteMedia(post.preload!.thumbnail);
} }
@ -220,7 +220,7 @@ class PostWriteController extends ChangeNotifier {
if (reposting != null) { if (reposting != null) {
final post = await pt.getPost(reposting); final post = await pt.getPost(reposting);
replyingPost = post; repostingPost = post;
} }
} catch (err) { } catch (err) {
if (!context.mounted) return; if (!context.mounted) return;

View File

@ -7,7 +7,7 @@ import 'package:styled_widget/styled_widget.dart';
import 'package:surface/providers/sn_network.dart'; import 'package:surface/providers/sn_network.dart';
import 'package:surface/types/attachment.dart'; import 'package:surface/types/attachment.dart';
import 'package:surface/widgets/app_bar_leading.dart'; import 'package:surface/widgets/app_bar_leading.dart';
import 'package:surface/widgets/attachment/attachment_detail.dart'; import 'package:surface/widgets/attachment/attachment_zoom.dart';
import 'package:surface/widgets/attachment/attachment_item.dart'; import 'package:surface/widgets/attachment/attachment_item.dart';
import 'package:surface/widgets/dialog.dart'; import 'package:surface/widgets/dialog.dart';
import 'package:uuid/uuid.dart'; import 'package:uuid/uuid.dart';

View File

@ -1,279 +0,0 @@
import 'package:dismissible_page/dismissible_page.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:gap/gap.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:photo_view/photo_view.dart';
import 'package:photo_view/photo_view_gallery.dart';
import 'package:provider/provider.dart';
import 'package:styled_widget/styled_widget.dart';
import 'package:surface/providers/sn_network.dart';
import 'package:surface/providers/user_directory.dart';
import 'package:surface/types/attachment.dart';
import 'package:surface/widgets/account/account_image.dart';
import 'package:surface/widgets/universal_image.dart';
import 'package:uuid/uuid.dart';
class AttachmentZoomView extends StatefulWidget {
final Iterable<SnAttachment> data;
final int? initialIndex;
final List<String?>? heroTags;
const AttachmentZoomView({
super.key,
required this.data,
this.initialIndex,
this.heroTags,
});
@override
State<AttachmentZoomView> createState() => _AttachmentZoomViewState();
}
class _AttachmentZoomViewState extends State<AttachmentZoomView> {
late final PageController _pageController =
PageController(initialPage: widget.initialIndex ?? 0);
void _updatePage() {
setState(() {});
}
@override
void initState() {
super.initState();
_pageController.addListener(_updatePage);
Future.delayed(const Duration(milliseconds: 100), _updatePage);
}
@override
void dispose() {
_pageController.removeListener(_updatePage);
_pageController.dispose();
super.dispose();
}
Color get _unFocusColor =>
Theme.of(context).colorScheme.onSurface.withOpacity(0.75);
@override
Widget build(BuildContext context) {
final sn = context.read<SnNetworkProvider>();
final uuid = Uuid();
final metaTextStyle = GoogleFonts.roboto(
fontSize: 12,
color: _unFocusColor,
height: 1,
);
return DismissiblePage(
onDismissed: () {
Navigator.of(context).pop();
},
direction: DismissiblePageDismissDirection.down,
backgroundColor: Colors.transparent,
isFullScreen: true,
child: Stack(
children: [
Builder(builder: (context) {
if (widget.data.length == 1) {
final heroTag = widget.heroTags?.first ?? uuid.v4();
return Hero(
tag: 'attachment-${widget.data.first.rid}-$heroTag',
child: PhotoView(
key: Key(
'attachment-detail-${widget.data.first.rid}-$heroTag'),
backgroundDecoration:
BoxDecoration(color: Colors.transparent),
imageProvider: UniversalImage.provider(
sn.getAttachmentUrl(widget.data.first.rid),
),
),
);
}
return PhotoViewGallery.builder(
pageController: _pageController,
scrollPhysics: const BouncingScrollPhysics(),
builder: (context, idx) {
final heroTag = widget.heroTags?.elementAt(idx) ?? uuid.v4();
return PhotoViewGalleryPageOptions(
imageProvider: UniversalImage.provider(
sn.getAttachmentUrl(widget.data.elementAt(idx).rid),
),
heroAttributes: PhotoViewHeroAttributes(
tag: 'attachment-${widget.data.first.rid}-$heroTag',
),
);
},
itemCount: widget.data.length,
loadingBuilder: (context, event) => Center(
child: SizedBox(
width: 20.0,
height: 20.0,
child: CircularProgressIndicator(
value: event == null
? 0
: event.cumulativeBytesLoaded /
(event.expectedTotalBytes ?? 1),
),
),
),
backgroundDecoration: BoxDecoration(color: Colors.transparent),
);
}),
Align(
alignment: Alignment.bottomCenter,
child: IgnorePointer(
child: Container(
height: 300,
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.bottomCenter,
end: Alignment.topCenter,
colors: [
Theme.of(context).colorScheme.surface,
Colors.transparent,
],
),
),
),
),
),
Positioned(
left: 16,
right: 16,
bottom: 16 + MediaQuery.of(context).padding.bottom,
child: Material(
color: Colors.transparent,
child: Builder(builder: (context) {
final ud = context.read<UserDirectoryProvider>();
final item = widget.data.elementAt(
widget.data.length > 1
? _pageController.page?.round() ?? 0
: 0,
);
final account = ud.getAccountFromCache(item.accountId);
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (item.accountId > 0)
Row(
children: [
IgnorePointer(
child: AccountImage(
content: account!.avatar,
radius: 19,
),
),
const Gap(8),
Expanded(
child: IgnorePointer(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'attachmentUploadBy'.tr(),
style:
Theme.of(context).textTheme.bodySmall,
),
Text(
account.nick,
style:
Theme.of(context).textTheme.bodyMedium,
),
],
),
),
),
if (widget.data.length > 1)
IgnorePointer(
child: Text(
'${(_pageController.page?.round() ?? 0) + 1}/${widget.data.length}',
style: GoogleFonts.robotoMono(fontSize: 13),
).padding(right: 8),
),
],
),
const Gap(4),
IgnorePointer(
child: Text(
item.alt,
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: const TextStyle(
fontSize: 15,
fontWeight: FontWeight.w500,
),
),
),
const Gap(2),
IgnorePointer(
child: Wrap(
spacing: 6,
children: [
if (item.metadata['exif'] == null)
Text(
'#${item.rid}',
style: metaTextStyle,
),
if (item.metadata['exif']?['Model'] != null)
Text(
'attachmentShotOn'.tr(args: [
item.metadata['exif']?['Model'],
]),
style: metaTextStyle,
).padding(right: 2),
if (item.metadata['exif']?['ShutterSpeed'] != null)
Text(
item.metadata['exif']?['ShutterSpeed'],
style: metaTextStyle,
).padding(right: 2),
if (item.metadata['exif']?['ISO'] != null)
Text(
'ISO${item.metadata['exif']?['ISO']}',
style: metaTextStyle,
).padding(right: 2),
if (item.metadata['exif']?['Aperture'] != null)
Text(
'f/${item.metadata['exif']?['Aperture']}',
style: metaTextStyle,
).padding(right: 2),
if (item.metadata['exif']?['Megapixels'] != null &&
item.metadata['exif']?['Model'] != null)
Text(
'${item.metadata['exif']?['Megapixels']}MP',
style: metaTextStyle,
)
else
Text(
'${item.size} Bytes',
style: metaTextStyle,
),
Text(
'${item.metadata['width']}x${item.metadata['height']}',
style: metaTextStyle,
),
if (item.metadata['ratio'] != null)
Text(
(item.metadata['ratio'] as num)
.toStringAsFixed(2),
style: metaTextStyle,
),
Text(
item.mimetype,
style: metaTextStyle,
),
],
),
),
],
);
}),
),
),
],
),
);
}
}

View File

@ -6,7 +6,7 @@ import 'package:flutter/material.dart';
import 'package:gap/gap.dart'; import 'package:gap/gap.dart';
import 'package:responsive_framework/responsive_framework.dart'; import 'package:responsive_framework/responsive_framework.dart';
import 'package:surface/types/attachment.dart'; import 'package:surface/types/attachment.dart';
import 'package:surface/widgets/attachment/attachment_detail.dart'; import 'package:surface/widgets/attachment/attachment_zoom.dart';
import 'package:surface/widgets/attachment/attachment_item.dart'; import 'package:surface/widgets/attachment/attachment_item.dart';
import 'package:uuid/uuid.dart'; import 'package:uuid/uuid.dart';

View File

@ -0,0 +1,356 @@
import 'dart:io';
import 'package:dio/dio.dart';
import 'package:dismissible_page/dismissible_page.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:gal/gal.dart';
import 'package:gap/gap.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:material_symbols_icons/symbols.dart';
import 'package:path/path.dart' show extension;
import 'package:photo_view/photo_view.dart';
import 'package:photo_view/photo_view_gallery.dart';
import 'package:provider/provider.dart';
import 'package:styled_widget/styled_widget.dart';
import 'package:surface/providers/sn_network.dart';
import 'package:surface/providers/user_directory.dart';
import 'package:surface/types/attachment.dart';
import 'package:surface/widgets/account/account_image.dart';
import 'package:surface/widgets/dialog.dart';
import 'package:surface/widgets/universal_image.dart';
import 'package:url_launcher/url_launcher_string.dart';
import 'package:uuid/uuid.dart';
class AttachmentZoomView extends StatefulWidget {
final Iterable<SnAttachment> data;
final int? initialIndex;
final List<String?>? heroTags;
const AttachmentZoomView({
super.key,
required this.data,
this.initialIndex,
this.heroTags,
});
@override
State<AttachmentZoomView> createState() => _AttachmentZoomViewState();
}
class _AttachmentZoomViewState extends State<AttachmentZoomView> {
late final PageController _pageController = PageController(initialPage: widget.initialIndex ?? 0);
void _updatePage() {
setState(() {
if (_isCompletedDownload) {
setState(() => _isCompletedDownload = false);
}
});
}
bool _isDownloading = false;
bool _isCompletedDownload = false;
double? _progressOfDownload = 0;
Future<void> _saveToAlbum(int idx) async {
final sn = context.read<SnNetworkProvider>();
final item = widget.data.elementAt(idx);
final url = sn.getAttachmentUrl(item.rid);
if (kIsWeb || Platform.isLinux) {
await launchUrlString(url);
return;
}
if (!await Gal.hasAccess(toAlbum: true)) {
if (!await Gal.requestAccess(toAlbum: true)) return;
}
setState(() => _isDownloading = true);
var extName = extension(item.name);
if (extName.isEmpty) extName = '.png';
final imagePath = '${Directory.systemTemp.path}/${item.uuid}$extName';
await Dio().download(
url,
imagePath,
onReceiveProgress: (count, total) {
setState(() => _progressOfDownload = count / total);
},
);
bool isSuccess = false;
try {
await Gal.putImage(imagePath, album: 'Solar Network');
setState(() {
isSuccess = true;
_isDownloading = false;
_isCompletedDownload = isSuccess;
});
} catch (e) {
if (!mounted) return;
context.showErrorDialog(e);
}
if (!mounted) return;
context.showSnackbar(
'attachmentSaved'.tr(),
action: SnackBarAction(
label: 'openInAlbum'.tr(),
onPressed: () async => Gal.open(),
),
);
}
@override
void initState() {
super.initState();
_pageController.addListener(_updatePage);
Future.delayed(const Duration(milliseconds: 100), _updatePage);
}
@override
void dispose() {
_pageController.removeListener(_updatePage);
_pageController.dispose();
super.dispose();
}
Color get _unFocusColor => Theme.of(context).colorScheme.onSurface.withOpacity(0.75);
@override
Widget build(BuildContext context) {
final sn = context.read<SnNetworkProvider>();
final uuid = Uuid();
final metaTextStyle = GoogleFonts.roboto(
fontSize: 12,
color: _unFocusColor,
height: 1,
);
return DismissiblePage(
onDismissed: () {
Navigator.of(context).pop();
},
direction: DismissiblePageDismissDirection.down,
backgroundColor: Colors.transparent,
isFullScreen: true,
child: Scaffold(
body: Stack(
children: [
Builder(builder: (context) {
if (widget.data.length == 1) {
final heroTag = widget.heroTags?.first ?? uuid.v4();
return Hero(
tag: 'attachment-${widget.data.first.rid}-$heroTag',
child: PhotoView(
key: Key('attachment-detail-${widget.data.first.rid}-$heroTag'),
backgroundDecoration: BoxDecoration(color: Colors.transparent),
imageProvider: UniversalImage.provider(
sn.getAttachmentUrl(widget.data.first.rid),
),
),
);
}
return PhotoViewGallery.builder(
pageController: _pageController,
scrollPhysics: const BouncingScrollPhysics(),
builder: (context, idx) {
final heroTag = widget.heroTags?.elementAt(idx) ?? uuid.v4();
return PhotoViewGalleryPageOptions(
imageProvider: UniversalImage.provider(
sn.getAttachmentUrl(widget.data.elementAt(idx).rid),
),
heroAttributes: PhotoViewHeroAttributes(
tag: 'attachment-${widget.data.first.rid}-$heroTag',
),
);
},
itemCount: widget.data.length,
loadingBuilder: (context, event) => Center(
child: SizedBox(
width: 20.0,
height: 20.0,
child: CircularProgressIndicator(
value: event == null ? 0 : event.cumulativeBytesLoaded / (event.expectedTotalBytes ?? 1),
),
),
),
backgroundDecoration: BoxDecoration(color: Colors.transparent),
);
}),
Align(
alignment: Alignment.bottomCenter,
child: IgnorePointer(
child: Container(
height: 300,
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.bottomCenter,
end: Alignment.topCenter,
colors: [
Theme.of(context).colorScheme.surface,
Colors.transparent,
],
),
),
),
),
),
Positioned(
left: 16,
right: 16,
bottom: 16 + MediaQuery.of(context).padding.bottom,
child: Material(
color: Colors.transparent,
child: Builder(builder: (context) {
final ud = context.read<UserDirectoryProvider>();
final item = widget.data.elementAt(
widget.data.length > 1 ? _pageController.page?.round() ?? 0 : 0,
);
final account = ud.getAccountFromCache(item.accountId);
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (item.accountId > 0)
Row(
children: [
IgnorePointer(
child: AccountImage(
content: account!.avatar,
radius: 19,
),
),
const Gap(8),
Expanded(
child: IgnorePointer(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'attachmentUploadBy'.tr(),
style: Theme.of(context).textTheme.bodySmall,
),
Text(
account.nick,
style: Theme.of(context).textTheme.bodyMedium,
),
],
),
),
),
if (widget.data.length > 1)
IgnorePointer(
child: Text(
'${(_pageController.page?.round() ?? 0) + 1}/${widget.data.length}',
style: GoogleFonts.robotoMono(fontSize: 13),
).padding(right: 8),
),
InkWell(
onTap: _isDownloading
? null
: () => _saveToAlbum(widget.data.length > 1 ? _pageController.page?.round() ?? 0 : 0),
child: Container(
padding: const EdgeInsets.all(6),
child: !_isDownloading
? !_isCompletedDownload
? const Icon(Symbols.save_alt)
: const Icon(Symbols.download_done)
: SizedBox(
width: 24,
height: 24,
child: CircularProgressIndicator(
value: _progressOfDownload,
strokeWidth: 3,
),
),
),
),
],
),
const Gap(4),
IgnorePointer(
child: Text(
item.alt,
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: const TextStyle(
fontSize: 15,
fontWeight: FontWeight.w500,
),
),
),
const Gap(2),
IgnorePointer(
child: Wrap(
spacing: 6,
children: [
if (item.metadata['exif'] == null)
Text(
'#${item.rid}',
style: metaTextStyle,
),
if (item.metadata['exif']?['Model'] != null)
Text(
'attachmentShotOn'.tr(args: [
item.metadata['exif']?['Model'],
]),
style: metaTextStyle,
).padding(right: 2),
if (item.metadata['exif']?['ShutterSpeed'] != null)
Text(
item.metadata['exif']?['ShutterSpeed'],
style: metaTextStyle,
).padding(right: 2),
if (item.metadata['exif']?['ISO'] != null)
Text(
'ISO${item.metadata['exif']?['ISO']}',
style: metaTextStyle,
).padding(right: 2),
if (item.metadata['exif']?['Aperture'] != null)
Text(
'f/${item.metadata['exif']?['Aperture']}',
style: metaTextStyle,
).padding(right: 2),
if (item.metadata['exif']?['Megapixels'] != null && item.metadata['exif']?['Model'] != null)
Text(
'${item.metadata['exif']?['Megapixels']}MP',
style: metaTextStyle,
)
else
Text(
'${item.size} Bytes',
style: metaTextStyle,
),
Text(
'${item.metadata['width']}x${item.metadata['height']}',
style: metaTextStyle,
),
if (item.metadata['ratio'] != null)
Text(
(item.metadata['ratio'] as num).toStringAsFixed(2),
style: metaTextStyle,
),
Text(
item.mimetype,
style: metaTextStyle,
),
],
),
),
],
);
}),
),
),
],
),
),
);
}
}

View File

@ -16,6 +16,7 @@ import 'package:surface/widgets/post/post_media_pending_list.dart';
class ChatMessageInput extends StatefulWidget { class ChatMessageInput extends StatefulWidget {
final ChatMessageController controller; final ChatMessageController controller;
const ChatMessageInput({super.key, required this.controller}); const ChatMessageInput({super.key, required this.controller});
@override @override
@ -74,9 +75,7 @@ class ChatMessageInputState extends State<ChatMessageInput> {
media.name, media.name,
'interactive', 'interactive',
null, null,
mimetype: media.raw != null && media.type == PostWriteMediaType.image mimetype: media.raw != null && media.type == PostWriteMediaType.image ? 'image/png' : null,
? 'image/png'
: null,
); );
final item = await attach.chunkedUploadParts( final item = await attach.chunkedUploadParts(
@ -110,10 +109,7 @@ class ChatMessageInputState extends State<ChatMessageInput> {
widget.controller.sendMessage( widget.controller.sendMessage(
'messages.new', 'messages.new',
_contentController.text, _contentController.text,
attachments: _attachments attachments: _attachments.where((e) => e.attachment != null).map((e) => e.attachment!.rid).toList(),
.where((e) => e.attachment != null)
.map((e) => e.attachment!.rid)
.toList(),
relatedId: _editingMessage?.id, relatedId: _editingMessage?.id,
quoteId: _replyingMessage?.id, quoteId: _replyingMessage?.id,
editingMessage: _editingMessage, editingMessage: _editingMessage,
@ -167,15 +163,12 @@ class ChatMessageInputState extends State<ChatMessageInput> {
TweenAnimationBuilder<double>( TweenAnimationBuilder<double>(
tween: Tween(begin: 0, end: _progress), tween: Tween(begin: 0, end: _progress),
duration: Duration(milliseconds: 300), duration: Duration(milliseconds: 300),
builder: (context, value, _) => builder: (context, value, _) => LinearProgressIndicator(value: value, minHeight: 2),
LinearProgressIndicator(value: value, minHeight: 2),
) )
else if (_isBusy) else if (_isBusy)
const LinearProgressIndicator(value: null, minHeight: 2), const LinearProgressIndicator(value: null, minHeight: 2),
Padding( Padding(
padding: _attachments.isNotEmpty padding: _attachments.isNotEmpty ? const EdgeInsets.only(top: 8) : EdgeInsets.zero,
? const EdgeInsets.only(top: 8)
: EdgeInsets.zero,
child: PostMediaPendingList( child: PostMediaPendingList(
attachments: _attachments, attachments: _attachments,
isBusy: _isBusy, isBusy: _isBusy,
@ -187,18 +180,18 @@ class ChatMessageInputState extends State<ChatMessageInput> {
}, },
onUpdateBusy: (state) => setState(() => _isBusy = state), onUpdateBusy: (state) => setState(() => _isBusy = state),
), ),
).height(_attachments.isNotEmpty ? 80 + 8 : 0, animate: true).animate( )
const Duration(milliseconds: 300), Curves.fastEaseInToSlowEaseOut), .height(_attachments.isNotEmpty ? 80 + 8 : 0, animate: true)
.animate(const Duration(milliseconds: 300), Curves.fastEaseInToSlowEaseOut),
SingleChildScrollView( SingleChildScrollView(
physics: const NeverScrollableScrollPhysics(), physics: const NeverScrollableScrollPhysics(),
child: Padding( child: Padding(
padding: _replyingMessage != null padding: _replyingMessage != null ? const EdgeInsets.only(top: 8) : EdgeInsets.zero,
? const EdgeInsets.only(top: 8)
: EdgeInsets.zero,
child: _replyingMessage != null child: _replyingMessage != null
? MaterialBanner( ? MaterialBanner(
padding: const EdgeInsets.only(left: 16.0), padding: const EdgeInsets.only(left: 16.0),
leading: const Icon(Symbols.reply), leading: const Icon(Symbols.reply),
backgroundColor: Colors.transparent,
content: SingleChildScrollView( content: SingleChildScrollView(
physics: const NeverScrollableScrollPhysics(), physics: const NeverScrollableScrollPhysics(),
child: Column( child: Column(
@ -223,18 +216,18 @@ class ChatMessageInputState extends State<ChatMessageInput> {
) )
: const SizedBox.shrink(), : const SizedBox.shrink(),
), ),
).height(_replyingMessage != null ? 54 + 8 : 0, animate: true).animate( )
const Duration(milliseconds: 300), Curves.fastEaseInToSlowEaseOut), .height(_replyingMessage != null ? 54 + 8 : 0, animate: true)
.animate(const Duration(milliseconds: 300), Curves.fastEaseInToSlowEaseOut),
SingleChildScrollView( SingleChildScrollView(
physics: const NeverScrollableScrollPhysics(), physics: const NeverScrollableScrollPhysics(),
child: Padding( child: Padding(
padding: _editingMessage != null padding: _editingMessage != null ? const EdgeInsets.only(top: 8) : EdgeInsets.zero,
? const EdgeInsets.only(top: 8)
: EdgeInsets.zero,
child: _editingMessage != null child: _editingMessage != null
? MaterialBanner( ? MaterialBanner(
padding: const EdgeInsets.only(left: 16.0), padding: const EdgeInsets.only(left: 16.0),
leading: const Icon(Symbols.edit), leading: const Icon(Symbols.edit),
backgroundColor: Colors.transparent,
content: SingleChildScrollView( content: SingleChildScrollView(
physics: const NeverScrollableScrollPhysics(), physics: const NeverScrollableScrollPhysics(),
child: Column( child: Column(
@ -259,8 +252,9 @@ class ChatMessageInputState extends State<ChatMessageInput> {
) )
: const SizedBox.shrink(), : const SizedBox.shrink(),
), ),
).height(_editingMessage != null ? 54 + 8 : 0, animate: true).animate( )
const Duration(milliseconds: 300), Curves.fastEaseInToSlowEaseOut), .height(_editingMessage != null ? 54 + 8 : 0, animate: true)
.animate(const Duration(milliseconds: 300), Curves.fastEaseInToSlowEaseOut),
SizedBox( SizedBox(
height: 56, height: 56,
child: Row( child: Row(
@ -271,13 +265,10 @@ class ChatMessageInputState extends State<ChatMessageInput> {
controller: _contentController, controller: _contentController,
decoration: InputDecoration( decoration: InputDecoration(
isCollapsed: true, isCollapsed: true,
hintText: 'fieldChatMessage'.tr(args: [ hintText: 'fieldChatMessage'.tr(args: [widget.controller.channel?.name ?? 'loading'.tr()]),
widget.controller.channel?.name ?? 'loading'.tr()
]),
border: InputBorder.none, border: InputBorder.none,
), ),
onTapOutside: (_) => onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
FocusManager.instance.primaryFocus?.unfocus(),
onSubmitted: (_) { onSubmitted: (_) {
if (_isBusy) return; if (_isBusy) return;
_sendMessage(); _sendMessage();

View File

@ -15,7 +15,7 @@ import 'package:url_launcher/url_launcher_string.dart';
import 'package:path/path.dart'; import 'package:path/path.dart';
import 'package:uuid/uuid.dart'; import 'package:uuid/uuid.dart';
import 'attachment/attachment_detail.dart'; import 'attachment/attachment_zoom.dart';
class MarkdownTextContent extends StatelessWidget { class MarkdownTextContent extends StatelessWidget {
final String content; final String content;

View File

@ -105,7 +105,14 @@ class AppRootScaffold extends StatelessWidget {
if (!kIsWeb && if (!kIsWeb &&
(Platform.isWindows || Platform.isLinux || Platform.isMacOS)) (Platform.isWindows || Platform.isLinux || Platform.isMacOS))
Container( Container(
color: Theme.of(context).colorScheme.surface, decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
color: Theme.of(context).dividerColor,
width: 1 / devicePixelRatio,
),
),
),
child: WindowTitleBarBox(child: MoveWindow()), child: WindowTitleBarBox(child: MoveWindow()),
), ),
ConnectionIndicator(), ConnectionIndicator(),

View File

@ -3,7 +3,6 @@ import 'dart:math' as math;
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:material_symbols_icons/symbols.dart'; import 'package:material_symbols_icons/symbols.dart';
import 'package:popover/popover.dart'; import 'package:popover/popover.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
@ -148,6 +147,7 @@ class PostItem extends StatelessWidget {
if (data.repostTo != null) if (data.repostTo != null)
_PostQuoteContent(child: data.repostTo!).padding( _PostQuoteContent(child: data.repostTo!).padding(
horizontal: 12, horizontal: 12,
bottom: data.preload?.attachments?.isNotEmpty ?? false ? 12 : 0,
), ),
if (data.visibility > 0) if (data.visibility > 0)
_PostVisibilityHint(data: data).padding( _PostVisibilityHint(data: data).padding(
@ -311,8 +311,6 @@ class _PostHeadline extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
if (isEnlarge) { if (isEnlarge) {
final sn = context.read<SnNetworkProvider>(); final sn = context.read<SnNetworkProvider>();
final textScaler = TextScaler.linear(1.5);
return Column( return Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
@ -606,26 +604,33 @@ class _PostQuoteContent extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Container( return GestureDetector(
decoration: BoxDecoration( child: Container(
borderRadius: const BorderRadius.all(Radius.circular(8)), decoration: BoxDecoration(
border: Border.all( borderRadius: const BorderRadius.all(Radius.circular(8)),
color: Theme.of(context).dividerColor, border: Border.all(
width: 1, color: Theme.of(context).dividerColor,
width: 1,
),
),
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
child: Column(
children: [
_PostContentHeader(
data: child,
isCompact: true,
showMenu: false,
onDeleted: () {},
).padding(bottom: 4),
_PostContentBody(data: child),
],
), ),
), ),
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), onTap: () {
child: Column( GoRouter.of(context).pushNamed('postDetail', pathParameters: {
children: [ 'slug': child.id.toString(),
_PostContentHeader( });
data: child, },
isCompact: true,
showMenu: false,
onDeleted: () {},
).padding(bottom: 4),
_PostContentBody(data: child),
],
),
); );
} }
} }

View File

@ -13,7 +13,7 @@ import 'package:provider/provider.dart';
import 'package:styled_widget/styled_widget.dart'; import 'package:styled_widget/styled_widget.dart';
import 'package:surface/controllers/post_write_controller.dart'; import 'package:surface/controllers/post_write_controller.dart';
import 'package:surface/providers/sn_network.dart'; import 'package:surface/providers/sn_network.dart';
import 'package:surface/widgets/attachment/attachment_detail.dart'; import 'package:surface/widgets/attachment/attachment_zoom.dart';
import 'package:surface/widgets/dialog.dart'; import 'package:surface/widgets/dialog.dart';
class PostMediaPendingList extends StatelessWidget { class PostMediaPendingList extends StatelessWidget {

View File

@ -14,6 +14,7 @@ import firebase_core
import firebase_messaging import firebase_messaging
import flutter_udid import flutter_udid
import flutter_webrtc import flutter_webrtc
import gal
import livekit_client import livekit_client
import media_kit_libs_macos_video import media_kit_libs_macos_video
import media_kit_video import media_kit_video
@ -37,6 +38,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
FLTFirebaseMessagingPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseMessagingPlugin")) FLTFirebaseMessagingPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseMessagingPlugin"))
FlutterUdidPlugin.register(with: registry.registrar(forPlugin: "FlutterUdidPlugin")) FlutterUdidPlugin.register(with: registry.registrar(forPlugin: "FlutterUdidPlugin"))
FlutterWebRTCPlugin.register(with: registry.registrar(forPlugin: "FlutterWebRTCPlugin")) FlutterWebRTCPlugin.register(with: registry.registrar(forPlugin: "FlutterWebRTCPlugin"))
GalPlugin.register(with: registry.registrar(forPlugin: "GalPlugin"))
LiveKitPlugin.register(with: registry.registrar(forPlugin: "LiveKitPlugin")) LiveKitPlugin.register(with: registry.registrar(forPlugin: "LiveKitPlugin"))
MediaKitLibsMacosVideoPlugin.register(with: registry.registrar(forPlugin: "MediaKitLibsMacosVideoPlugin")) MediaKitLibsMacosVideoPlugin.register(with: registry.registrar(forPlugin: "MediaKitLibsMacosVideoPlugin"))
MediaKitVideoPlugin.register(with: registry.registrar(forPlugin: "MediaKitVideoPlugin")) MediaKitVideoPlugin.register(with: registry.registrar(forPlugin: "MediaKitVideoPlugin"))

View File

@ -36,5 +36,7 @@
<string>Grant access to Photo Library will allow Solian record audio for your post.</string> <string>Grant access to Photo Library will allow Solian record audio for your post.</string>
<key>NSPhotoLibraryUsageDescription</key> <key>NSPhotoLibraryUsageDescription</key>
<string>Grant access to Photo Library will allow Solian upload photo or video for your post.</string> <string>Grant access to Photo Library will allow Solian upload photo or video for your post.</string>
<key>NSPhotoLibraryAddUsageDescription</key>
<string>Grant access to Photo Library will allow Solian download photo to album for you.</string>
</dict> </dict>
</plist> </plist>

View File

@ -13,10 +13,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: _flutterfire_internals name: _flutterfire_internals
sha256: "71c01c1998c40b3af1944ad0a5f374b4e6fef7f3d2df487f3970dbeadaeb25a1" sha256: eae3133cbb06de9205899b822e3897fc6a8bc278ad4c944b4ce612689369694b
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.3.46" version: "1.3.47"
_macros: _macros:
dependency: transitive dependency: transitive
description: dart description: dart
@ -530,74 +530,74 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: firebase_analytics name: firebase_analytics
sha256: "8be7c45091f01cc15130edf8201ed4f4b7b022a38424ed9aac6b9a6d7c45bb09" sha256: "366140abb55418ea23060b779893fa997c2d8e1974a4d1cc4d9590933b65c5fd"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "11.3.5" version: "11.3.6"
firebase_analytics_platform_interface: firebase_analytics_platform_interface:
dependency: transitive dependency: transitive
description: description:
name: firebase_analytics_platform_interface name: firebase_analytics_platform_interface
sha256: "111e288dd332ce13e1ec96b54f5dca0601fe6e75bc251f74fd6f70096f3fbf09" sha256: "8e987cf977c0c8f4ad02d9950a9b25b1a9606899f37b66a322a43af05be0246b"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.2.7" version: "4.2.8"
firebase_analytics_web: firebase_analytics_web:
dependency: transitive dependency: transitive
description: description:
name: firebase_analytics_web name: firebase_analytics_web
sha256: "7c3c80b4e223565ddbda3eaacc42712ba6de53410f8ae18738c807aa8af6b910" sha256: "0b64ef9060d394bba3d3b4777f49ee098efeeea7b0afb04663c956de6a3da170"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.5.10+4" version: "0.5.10+5"
firebase_core: firebase_core:
dependency: "direct main" dependency: "direct main"
description: description:
name: firebase_core name: firebase_core
sha256: "2438a75ad803e818ad3bd5df49137ee619c46b6fc7101f4dbc23da07305ce553" sha256: fef81a53ba1ca618def1f8bef4361df07968434e62cb204c1fb90bb880a03da2
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.8.0" version: "3.8.1"
firebase_core_platform_interface: firebase_core_platform_interface:
dependency: transitive dependency: transitive
description: description:
name: firebase_core_platform_interface name: firebase_core_platform_interface
sha256: e30da58198a6d4b49d5bce4e852f985c32cb10db329ebef9473db2b9f09ce810 sha256: b94b217e3ad745e784960603d33d99471621ecca151c99c670869b76e50ad2a6
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "5.3.0" version: "5.3.1"
firebase_core_web: firebase_core_web:
dependency: transitive dependency: transitive
description: description:
name: firebase_core_web name: firebase_core_web
sha256: f967a7138f5d2ffb1ce15950e2a382924239eaa521150a8f144af34e68b3b3e5 sha256: "9e69806bb3d905aeec3c1242e0e1475de6ea6d48f456af29d598fb229a2b4e5e"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.18.1" version: "2.18.2"
firebase_messaging: firebase_messaging:
dependency: "direct main" dependency: "direct main"
description: description:
name: firebase_messaging name: firebase_messaging
sha256: "4d0968ecb860d7baa15a6e2af3469ec5b0d959e51c59ce84a52b0f7632a4aa5a" sha256: "151a3ee68736abf293aab66d1317ade53c88abe1db09c75a0460aebf7767bbdf"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "15.1.5" version: "15.1.6"
firebase_messaging_platform_interface: firebase_messaging_platform_interface:
dependency: transitive dependency: transitive
description: description:
name: firebase_messaging_platform_interface name: firebase_messaging_platform_interface
sha256: a2cb3e7d71d40b6612e2d4e0daa0ae759f6a9d07f693f904d14d22aadf70be10 sha256: f331ee51e40c243f90cc7bc059222dfec4e5df53125b08d31fb28961b00d2a9d
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.5.48" version: "4.5.49"
firebase_messaging_web: firebase_messaging_web:
dependency: transitive dependency: transitive
description: description:
name: firebase_messaging_web name: firebase_messaging_web
sha256: "1554e190f0cd9d6fe59f61ae0275ac12006fdb78b07669f1a260d1a9e6de3a1f" sha256: efaf3fdc54cd77e0eedb8e75f7f01c808828c64d052ddbf94d3009974e47d30f
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.9.4" version: "3.9.5"
fixnum: fixnum:
dependency: transitive dependency: transitive
description: description:
@ -647,10 +647,10 @@ packages:
dependency: "direct dev" dependency: "direct dev"
description: description:
name: flutter_launcher_icons name: flutter_launcher_icons
sha256: "619817c4b65b322b5104b6bb6dfe6cda62d9729bd7ad4303ecc8b4e690a67a77" sha256: "31cd0885738e87c72d6f055564d37fabcdacee743b396b78c7636c169cac64f5"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.14.1" version: "0.14.2"
flutter_lints: flutter_lints:
dependency: "direct dev" dependency: "direct dev"
description: description:
@ -754,6 +754,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.0.0" version: "4.0.0"
gal:
dependency: "direct main"
description:
name: gal
sha256: "54c9b72528efce7c66234f3b6dd01cb0304fd8af8196de15571d7bdddb940977"
url: "https://pub.dev"
source: hosted
version: "2.3.0"
gap: gap:
dependency: "direct main" dependency: "direct main"
description: description:
@ -1078,10 +1086,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: material_symbols_icons name: material_symbols_icons
sha256: a783133f87c58e10b1cc19797f7c3192ff9c2bab301c4ade90312d8f2aed01b2 sha256: "64404f47f8e0a9d20478468e5decef867a688660bad7173adcd20418d7f892c9"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.2800.2" version: "4.2801.0"
media_kit: media_kit:
dependency: "direct main" dependency: "direct main"
description: description:
@ -1262,10 +1270,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: path_provider_foundation name: path_provider_foundation
sha256: f234384a3fdd67f989b4d54a5d73ca2a6c422fa55ae694381ae0f4375cd1ea16 sha256: "4843174df4d288f5e29185bd6e72a6fbdf5a4a4602717eed565497429f179942"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.4.0" version: "2.4.1"
path_provider_linux: path_provider_linux:
dependency: transitive dependency: transitive
description: description:
@ -1827,10 +1835,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: url_launcher_ios name: url_launcher_ios
sha256: e43b677296fadce447e987a2f519dcf5f6d1e527dc35d01ffab4fff5b8a7063e sha256: "16a513b6c12bb419304e72ea0ae2ab4fed569920d1c7cb850263fe3acc824626"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "6.3.1" version: "6.3.2"
url_launcher_linux: url_launcher_linux:
dependency: transitive dependency: transitive
description: description:
@ -1843,10 +1851,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: url_launcher_macos name: url_launcher_macos
sha256: "769549c999acdb42b8bcfa7c43d72bf79a382ca7441ab18a808e101149daf672" sha256: "17ba2000b847f334f16626a574c702b196723af2a289e7a93ffcb79acff855c2"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.2.1" version: "3.2.2"
url_launcher_platform_interface: url_launcher_platform_interface:
dependency: transitive dependency: transitive
description: description:

View File

@ -95,6 +95,7 @@ dependencies:
popover: ^0.3.1 popover: ^0.3.1
sliver_tools: ^0.2.12 sliver_tools: ^0.2.12
bitsdojo_window: ^0.1.6 bitsdojo_window: ^0.1.6
gal: ^2.3.0
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:

View File

@ -12,6 +12,7 @@
#include <firebase_core/firebase_core_plugin_c_api.h> #include <firebase_core/firebase_core_plugin_c_api.h>
#include <flutter_udid/flutter_udid_plugin_c_api.h> #include <flutter_udid/flutter_udid_plugin_c_api.h>
#include <flutter_webrtc/flutter_web_r_t_c_plugin.h> #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 <livekit_client/live_kit_plugin.h>
#include <media_kit_libs_windows_video/media_kit_libs_windows_video_plugin_c_api.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 <media_kit_video/media_kit_video_plugin_c_api.h>
@ -34,6 +35,8 @@ void RegisterPlugins(flutter::PluginRegistry* registry) {
registry->GetRegistrarForPlugin("FlutterUdidPluginCApi")); registry->GetRegistrarForPlugin("FlutterUdidPluginCApi"));
FlutterWebRTCPluginRegisterWithRegistrar( FlutterWebRTCPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("FlutterWebRTCPlugin")); registry->GetRegistrarForPlugin("FlutterWebRTCPlugin"));
GalPluginCApiRegisterWithRegistrar(
registry->GetRegistrarForPlugin("GalPluginCApi"));
LiveKitPluginRegisterWithRegistrar( LiveKitPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("LiveKitPlugin")); registry->GetRegistrarForPlugin("LiveKitPlugin"));
MediaKitLibsWindowsVideoPluginCApiRegisterWithRegistrar( MediaKitLibsWindowsVideoPluginCApiRegisterWithRegistrar(

View File

@ -9,6 +9,7 @@ list(APPEND FLUTTER_PLUGIN_LIST
firebase_core firebase_core
flutter_udid flutter_udid
flutter_webrtc flutter_webrtc
gal
livekit_client livekit_client
media_kit_libs_windows_video media_kit_libs_windows_video
media_kit_video media_kit_video