Compare commits

..

No commits in common. "cb2b71d194131e79226913430a29f33a59aaa2a0" and "dad869967ea464f45ccd485f9ac24b34a62083fa" have entirely different histories.

18 changed files with 77 additions and 346 deletions

View File

@ -12,9 +12,9 @@ post {
body:json { body:json {
{ {
"alias": "BaLoading", "alias": "Meltdown",
"name": "BaLoading", "name": "Meltdown",
"attachment_id": "2JCI2uh21mKkfk9P", "attachment_id": "IpDPHEbWDDCbBofX",
"pack_id": 3 "pack_id": 4
} }
} }

View File

@ -155,7 +155,6 @@
"writePostTypeStory": "Post a story", "writePostTypeStory": "Post a story",
"writePostTypeArticle": "Write an article", "writePostTypeArticle": "Write an article",
"writePostTypeQuestion": "Ask a question", "writePostTypeQuestion": "Ask a question",
"writePostTypeVideo": "Post a video",
"fieldPostPublisher": "Post publisher", "fieldPostPublisher": "Post publisher",
"fieldPostContent": "What happened?!", "fieldPostContent": "What happened?!",
"fieldPostTitle": "Title", "fieldPostTitle": "Title",
@ -618,6 +617,5 @@
"postQuestionUnansweredWithReward": "Unanswered Question, reward source points {}", "postQuestionUnansweredWithReward": "Unanswered Question, reward source points {}",
"postQuestionAnswered": "Answered Question", "postQuestionAnswered": "Answered Question",
"postQuestionAnswerSelect": "Select as Answer", "postQuestionAnswerSelect": "Select as Answer",
"postQuestionAnswerSelected": "Answer has been selected, reward has been applied.", "postQuestionAnswerSelected": "Answer has been selected, reward has been applied."
"postVideoUpload": "Upload Video"
} }

View File

@ -139,7 +139,6 @@
"writePostTypeStory": "发动态", "writePostTypeStory": "发动态",
"writePostTypeArticle": "写文章", "writePostTypeArticle": "写文章",
"writePostTypeQuestion": "提问题", "writePostTypeQuestion": "提问题",
"writePostTypeVideo": "发视频",
"fieldPostPublisher": "帖子发布者", "fieldPostPublisher": "帖子发布者",
"fieldPostContent": "发生什么事了?!", "fieldPostContent": "发生什么事了?!",
"fieldPostTitle": "标题", "fieldPostTitle": "标题",
@ -617,6 +616,5 @@
"postQuestionAnswered": "已解答的问题", "postQuestionAnswered": "已解答的问题",
"postQuestionAnswerTitle": "精选解答", "postQuestionAnswerTitle": "精选解答",
"postQuestionAnswerSelect": "选择解答", "postQuestionAnswerSelect": "选择解答",
"postQuestionAnswerSelected": "解答已选择,奖励已发放。", "postQuestionAnswerSelected": "解答已选择,奖励已发放。"
"postVideoUpload": "上传视频"
} }

View File

@ -2,6 +2,7 @@ PODS:
- Alamofire (5.10.2) - Alamofire (5.10.2)
- connectivity_plus (0.0.1): - connectivity_plus (0.0.1):
- Flutter - Flutter
- FlutterMacOS
- croppy (0.0.1): - croppy (0.0.1):
- Flutter - Flutter
- device_info_plus (0.0.1): - device_info_plus (0.0.1):
@ -179,7 +180,7 @@ PODS:
- in_app_review (2.0.0): - in_app_review (2.0.0):
- Flutter - Flutter
- Kingfisher (8.2.0) - Kingfisher (8.2.0)
- livekit_client (2.3.6): - livekit_client (2.3.5):
- Flutter - Flutter
- flutter_webrtc - flutter_webrtc
- WebRTC-SDK (= 125.6422.06) - WebRTC-SDK (= 125.6422.06)
@ -236,7 +237,7 @@ PODS:
DEPENDENCIES: DEPENDENCIES:
- Alamofire - Alamofire
- connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`) - connectivity_plus (from `.symlinks/plugins/connectivity_plus/darwin`)
- croppy (from `.symlinks/plugins/croppy/ios`) - croppy (from `.symlinks/plugins/croppy/ios`)
- device_info_plus (from `.symlinks/plugins/device_info_plus/ios`) - device_info_plus (from `.symlinks/plugins/device_info_plus/ios`)
- file_picker (from `.symlinks/plugins/file_picker/ios`) - file_picker (from `.symlinks/plugins/file_picker/ios`)
@ -299,7 +300,7 @@ SPEC REPOS:
EXTERNAL SOURCES: EXTERNAL SOURCES:
connectivity_plus: connectivity_plus:
:path: ".symlinks/plugins/connectivity_plus/ios" :path: ".symlinks/plugins/connectivity_plus/darwin"
croppy: croppy:
:path: ".symlinks/plugins/croppy/ios" :path: ".symlinks/plugins/croppy/ios"
device_info_plus: device_info_plus:
@ -373,7 +374,7 @@ EXTERNAL SOURCES:
SPEC CHECKSUMS: SPEC CHECKSUMS:
Alamofire: 7193b3b92c74a07f85569e1a6c4f4237291e7496 Alamofire: 7193b3b92c74a07f85569e1a6c4f4237291e7496
connectivity_plus: 2a701ffec2c0ae28a48cf7540e279787e77c447d connectivity_plus: 18382e7311ba19efcaee94442b23b32507b20695
croppy: b6199bc8d56bd2e03cc11609d1c47ad9875c1321 croppy: b6199bc8d56bd2e03cc11609d1c47ad9875c1321
device_info_plus: bf2e3232933866d73fe290f2942f2156cdd10342 device_info_plus: bf2e3232933866d73fe290f2942f2156cdd10342
DKImagePickerController: 946cec48c7873164274ecc4624d19e3da4c1ef3c DKImagePickerController: 946cec48c7873164274ecc4624d19e3da4c1ef3c
@ -403,7 +404,7 @@ SPEC CHECKSUMS:
image_picker_ios: c560581cceedb403a6ff17f2f816d7fea1421fc1 image_picker_ios: c560581cceedb403a6ff17f2f816d7fea1421fc1
in_app_review: a31b5257259646ea78e0e35fc914979b0031d011 in_app_review: a31b5257259646ea78e0e35fc914979b0031d011
Kingfisher: 323e5c4ec7983aaace12af655a7b51a7f88a599d Kingfisher: 323e5c4ec7983aaace12af655a7b51a7f88a599d
livekit_client: 148b2cf67a09aaf475ba8e5bf1667fe10dc35f81 livekit_client: dcc5fd47ba69c98fc6baeb12e862c9d43807d976
media_kit_libs_ios_video: a5fe24bc7875ccd6378a0978c13185e1344651c1 media_kit_libs_ios_video: a5fe24bc7875ccd6378a0978c13185e1344651c1
media_kit_native_event_loop: e6b2ab20cf0746eb1c33be961fcf79667304fa2a media_kit_native_event_loop: e6b2ab20cf0746eb1c33be961fcf79667304fa2a
media_kit_video: 5da63f157170e5bf303bf85453b7ef6971218a2e media_kit_video: 5da63f157170e5bf303bf85453b7ef6971218a2e

View File

@ -145,7 +145,6 @@ class PostWriteController extends ChangeNotifier {
'stories': 'writePostTypeStory', 'stories': 'writePostTypeStory',
'articles': 'writePostTypeArticle', 'articles': 'writePostTypeArticle',
'questions': 'writePostTypeQuestion', 'questions': 'writePostTypeQuestion',
'videos': 'writePostTypeVideo',
}; };
static const kAttachmentProgressWeight = 0.9; static const kAttachmentProgressWeight = 0.9;
@ -198,7 +197,6 @@ class PostWriteController extends ChangeNotifier {
PostWriteMedia? thumbnail; PostWriteMedia? thumbnail;
List<PostWriteMedia> attachments = List.empty(growable: true); List<PostWriteMedia> attachments = List.empty(growable: true);
DateTime? publishedAt, publishedUntil; DateTime? publishedAt, publishedUntil;
SnAttachment? videoAttachment;
Future<void> fetchRelatedPost( Future<void> fetchRelatedPost(
BuildContext context, { BuildContext context, {
@ -509,7 +507,6 @@ class PostWriteController extends ChangeNotifier {
if (replyingPost != null) 'reply_to': replyingPost!.id, if (replyingPost != null) 'reply_to': replyingPost!.id,
if (repostingPost != null) 'repost_to': repostingPost!.id, if (repostingPost != null) 'repost_to': repostingPost!.id,
if (reward != null) 'reward': reward, if (reward != null) 'reward': reward,
if (videoAttachment != null) 'video': videoAttachment!.rid,
}, },
onSendProgress: (count, total) { onSendProgress: (count, total) {
progress = baseProgressVal + (count / total) * (kPostingProgressWeight / 2); progress = baseProgressVal + (count / total) * (kPostingProgressWeight / 2);
@ -636,11 +633,6 @@ class PostWriteController extends ChangeNotifier {
notifyListeners(); notifyListeners();
} }
void setVideoAttachment(SnAttachment? value) {
videoAttachment = value;
notifyListeners();
}
void reset() { void reset() {
publishedAt = null; publishedAt = null;
publishedUntil = null; publishedUntil = null;

View File

@ -23,9 +23,6 @@ class SnPostContentProvider {
if (out[i].body['thumbnail'] != null) { if (out[i].body['thumbnail'] != null) {
rids.add(out[i].body['thumbnail']); rids.add(out[i].body['thumbnail']);
} }
if (out[i].body['video'] != null) {
rids.add(out[i].body['video']);
}
if (out[i].repostTo != null) { if (out[i].repostTo != null) {
out[i] = out[i].copyWith( out[i] = out[i].copyWith(
repostTo: await _preloadRelatedDataSingle(out[i].repostTo!), repostTo: await _preloadRelatedDataSingle(out[i].repostTo!),
@ -39,7 +36,6 @@ class SnPostContentProvider {
preload: SnPostPreload( preload: SnPostPreload(
thumbnail: attachments.where((ele) => ele?.rid == out[i].body['thumbnail']).firstOrNull, thumbnail: attachments.where((ele) => ele?.rid == out[i].body['thumbnail']).firstOrNull,
attachments: attachments.where((ele) => out[i].body['attachments']?.contains(ele?.rid) ?? false).toList(), attachments: attachments.where((ele) => out[i].body['attachments']?.contains(ele?.rid) ?? false).toList(),
video: attachments.where((ele) => ele?.rid == out[i].body['video']).firstOrNull,
), ),
); );
} }
@ -57,9 +53,6 @@ class SnPostContentProvider {
if (out.body['thumbnail'] != null) { if (out.body['thumbnail'] != null) {
rids.add(out.body['thumbnail']); rids.add(out.body['thumbnail']);
} }
if (out.body['video'] != null) {
rids.add(out.body['video']);
}
if (out.repostTo != null) { if (out.repostTo != null) {
out = out.copyWith( out = out.copyWith(
repostTo: await _preloadRelatedDataSingle(out.repostTo!), repostTo: await _preloadRelatedDataSingle(out.repostTo!),
@ -71,7 +64,6 @@ class SnPostContentProvider {
preload: SnPostPreload( preload: SnPostPreload(
thumbnail: attachments.where((ele) => ele?.rid == out.body['thumbnail']).firstOrNull, thumbnail: attachments.where((ele) => ele?.rid == out.body['thumbnail']).firstOrNull,
attachments: attachments.where((ele) => out.body['attachments']?.contains(ele?.rid) ?? false).toList(), attachments: attachments.where((ele) => out.body['attachments']?.contains(ele?.rid) ?? false).toList(),
video: attachments.where((ele) => ele?.rid == out.body['video']).firstOrNull,
), ),
); );

View File

@ -1,3 +1,4 @@
import 'package:animations/animations.dart';
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:flutter_expandable_fab/flutter_expandable_fab.dart'; import 'package:flutter_expandable_fab/flutter_expandable_fab.dart';
@ -6,8 +7,10 @@ import 'package:go_router/go_router.dart';
import 'package:material_symbols_icons/symbols.dart'; import 'package:material_symbols_icons/symbols.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:styled_widget/styled_widget.dart'; import 'package:styled_widget/styled_widget.dart';
import 'package:surface/providers/config.dart';
import 'package:surface/providers/post.dart'; import 'package:surface/providers/post.dart';
import 'package:surface/providers/sn_network.dart'; import 'package:surface/providers/sn_network.dart';
import 'package:surface/screens/post/post_detail.dart';
import 'package:surface/types/post.dart'; import 'package:surface/types/post.dart';
import 'package:surface/widgets/app_bar_leading.dart'; import 'package:surface/widgets/app_bar_leading.dart';
import 'package:surface/widgets/dialog.dart'; import 'package:surface/widgets/dialog.dart';
@ -94,6 +97,8 @@ class _ExploreScreenState extends State<ExploreScreen> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final cfg = context.read<ConfigProvider>();
return AppScaffold( return AppScaffold(
floatingActionButtonLocation: ExpandableFab.location, floatingActionButtonLocation: ExpandableFab.location,
floatingActionButton: ExpandableFab( floatingActionButton: ExpandableFab(
@ -182,27 +187,6 @@ class _ExploreScreenState extends State<ExploreScreen> {
), ),
], ],
), ),
Row(
children: [
Text('writePostTypeVideo').tr(),
const Gap(20),
FloatingActionButton(
heroTag: null,
tooltip: 'writePostTypeVideo'.tr(),
onPressed: () {
GoRouter.of(context).pushNamed('postEditor', pathParameters: {
'mode': 'videos',
}).then((value) {
if (value == true) {
_refreshPosts();
}
});
_fabKey.currentState!.toggle();
},
child: const Icon(Symbols.video_call),
),
],
),
], ],
), ),
body: RefreshIndicator( body: RefreshIndicator(

View File

@ -18,7 +18,6 @@ import 'package:surface/providers/sn_network.dart';
import 'package:surface/types/attachment.dart'; import 'package:surface/types/attachment.dart';
import 'package:surface/types/post.dart'; import 'package:surface/types/post.dart';
import 'package:surface/widgets/account/account_image.dart'; import 'package:surface/widgets/account/account_image.dart';
import 'package:surface/widgets/attachment/attachment_item.dart';
import 'package:surface/widgets/loading_indicator.dart'; import 'package:surface/widgets/loading_indicator.dart';
import 'package:surface/widgets/markdown_content.dart'; import 'package:surface/widgets/markdown_content.dart';
import 'package:surface/widgets/navigation/app_scaffold.dart'; import 'package:surface/widgets/navigation/app_scaffold.dart';
@ -26,9 +25,6 @@ import 'package:surface/widgets/post/post_media_pending_list.dart';
import 'package:surface/widgets/post/post_meta_editor.dart'; import 'package:surface/widgets/post/post_meta_editor.dart';
import 'package:surface/widgets/dialog.dart'; import 'package:surface/widgets/dialog.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:uuid/uuid.dart';
import '../../widgets/attachment/attachment_input.dart';
class PostEditorExtra { class PostEditorExtra {
final String? text; final String? text;
@ -247,10 +243,6 @@ class _PostEditorScreenState extends State<PostEditorScreen> {
controller: _writeController, controller: _writeController,
onTapPublisher: _showPublisherPopup, onTapPublisher: _showPublisherPopup,
), ),
'videos' => _PostVideoEditor(
controller: _writeController,
onTapPublisher: _showPublisherPopup,
),
_ => const Placeholder(), _ => const Placeholder(),
}, },
), ),
@ -695,115 +687,3 @@ class _PostQuestionEditor extends StatelessWidget {
); );
} }
} }
class _PostVideoEditor extends StatelessWidget {
final PostWriteController controller;
final Function? onTapPublisher;
const _PostVideoEditor({required this.controller, this.onTapPublisher});
void _selectVideo(BuildContext context) async {
final video = await showDialog<SnAttachment?>(
context: context,
builder: (context) => AttachmentInputDialog(
title: 'postVideoUpload'.tr(),
pool: 'interactive',
mediaType: SnMediaType.video,
),
);
if (!context.mounted) return;
if (video == null) return;
controller.setVideoAttachment(video);
}
@override
Widget build(BuildContext context) {
return Column(
children: [
Material(
color: Theme.of(context).colorScheme.surfaceContainerHigh,
child: InkWell(
child: Row(
children: [
AccountImage(content: controller.publisher?.avatar, radius: 20),
const Gap(8),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(controller.publisher?.nick ?? 'loading'.tr()).bold(),
Text('@${controller.publisher?.name}'),
],
),
),
],
).padding(horizontal: 12, vertical: 8),
onTap: () {
onTapPublisher?.call();
},
),
),
const Gap(16),
TextField(
controller: controller.titleController,
decoration: InputDecoration.collapsed(
hintText: 'fieldPostTitle'.tr(),
border: InputBorder.none,
),
style: Theme.of(context).textTheme.titleLarge,
onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
).padding(horizontal: 16),
const Gap(8),
TextField(
controller: controller.descriptionController,
decoration: InputDecoration.collapsed(
hintText: 'fieldPostDescription'.tr(),
border: InputBorder.none,
),
maxLines: null,
keyboardType: TextInputType.multiline,
style: Theme.of(context).textTheme.bodyLarge,
onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
).padding(horizontal: 16),
const Gap(12),
Container(
margin: const EdgeInsets.only(left: 16, right: 16),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(16),
border: Border.all(color: Theme.of(context).dividerColor),
),
child: InkWell(
borderRadius: BorderRadius.circular(16),
child: AspectRatio(
aspectRatio: 16 / 9,
child: controller.videoAttachment == null
? Center(
child: Row(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(Icons.add),
const Gap(4),
Text('postVideoUpload'.tr()),
],
),
)
: ClipRRect(
borderRadius: BorderRadius.circular(16),
child: AttachmentItem(
data: controller.videoAttachment!,
heroTag: const Uuid().v4(),
),
),
),
onTap: () {
if (controller.videoAttachment != null) return;
_selectVideo(context);
},
),
),
],
);
}
}

View File

@ -1,6 +1,7 @@
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:gap/gap.dart'; import 'package:gap/gap.dart';
import 'package:go_router/go_router.dart';
import 'package:material_symbols_icons/symbols.dart'; import 'package:material_symbols_icons/symbols.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:styled_widget/styled_widget.dart'; import 'package:styled_widget/styled_widget.dart';

View File

@ -89,7 +89,6 @@ class SnPostPreload with _$SnPostPreload {
const factory SnPostPreload({ const factory SnPostPreload({
required SnAttachment? thumbnail, required SnAttachment? thumbnail,
required List<SnAttachment?>? attachments, required List<SnAttachment?>? attachments,
required SnAttachment? video,
}) = _SnPostPreload; }) = _SnPostPreload;
factory SnPostPreload.fromJson(Map<String, Object?> json) => factory SnPostPreload.fromJson(Map<String, Object?> json) =>

View File

@ -1567,7 +1567,6 @@ SnPostPreload _$SnPostPreloadFromJson(Map<String, dynamic> json) {
mixin _$SnPostPreload { mixin _$SnPostPreload {
SnAttachment? get thumbnail => throw _privateConstructorUsedError; SnAttachment? get thumbnail => throw _privateConstructorUsedError;
List<SnAttachment?>? get attachments => throw _privateConstructorUsedError; List<SnAttachment?>? get attachments => throw _privateConstructorUsedError;
SnAttachment? get video => throw _privateConstructorUsedError;
/// Serializes this SnPostPreload to a JSON map. /// Serializes this SnPostPreload to a JSON map.
Map<String, dynamic> toJson() => throw _privateConstructorUsedError; Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
@ -1585,13 +1584,9 @@ abstract class $SnPostPreloadCopyWith<$Res> {
SnPostPreload value, $Res Function(SnPostPreload) then) = SnPostPreload value, $Res Function(SnPostPreload) then) =
_$SnPostPreloadCopyWithImpl<$Res, SnPostPreload>; _$SnPostPreloadCopyWithImpl<$Res, SnPostPreload>;
@useResult @useResult
$Res call( $Res call({SnAttachment? thumbnail, List<SnAttachment?>? attachments});
{SnAttachment? thumbnail,
List<SnAttachment?>? attachments,
SnAttachment? video});
$SnAttachmentCopyWith<$Res>? get thumbnail; $SnAttachmentCopyWith<$Res>? get thumbnail;
$SnAttachmentCopyWith<$Res>? get video;
} }
/// @nodoc /// @nodoc
@ -1611,7 +1606,6 @@ class _$SnPostPreloadCopyWithImpl<$Res, $Val extends SnPostPreload>
$Res call({ $Res call({
Object? thumbnail = freezed, Object? thumbnail = freezed,
Object? attachments = freezed, Object? attachments = freezed,
Object? video = freezed,
}) { }) {
return _then(_value.copyWith( return _then(_value.copyWith(
thumbnail: freezed == thumbnail thumbnail: freezed == thumbnail
@ -1622,10 +1616,6 @@ class _$SnPostPreloadCopyWithImpl<$Res, $Val extends SnPostPreload>
? _value.attachments ? _value.attachments
: attachments // ignore: cast_nullable_to_non_nullable : attachments // ignore: cast_nullable_to_non_nullable
as List<SnAttachment?>?, as List<SnAttachment?>?,
video: freezed == video
? _value.video
: video // ignore: cast_nullable_to_non_nullable
as SnAttachment?,
) as $Val); ) as $Val);
} }
@ -1642,20 +1632,6 @@ class _$SnPostPreloadCopyWithImpl<$Res, $Val extends SnPostPreload>
return _then(_value.copyWith(thumbnail: value) as $Val); return _then(_value.copyWith(thumbnail: value) as $Val);
}); });
} }
/// Create a copy of SnPostPreload
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$SnAttachmentCopyWith<$Res>? get video {
if (_value.video == null) {
return null;
}
return $SnAttachmentCopyWith<$Res>(_value.video!, (value) {
return _then(_value.copyWith(video: value) as $Val);
});
}
} }
/// @nodoc /// @nodoc
@ -1666,15 +1642,10 @@ abstract class _$$SnPostPreloadImplCopyWith<$Res>
__$$SnPostPreloadImplCopyWithImpl<$Res>; __$$SnPostPreloadImplCopyWithImpl<$Res>;
@override @override
@useResult @useResult
$Res call( $Res call({SnAttachment? thumbnail, List<SnAttachment?>? attachments});
{SnAttachment? thumbnail,
List<SnAttachment?>? attachments,
SnAttachment? video});
@override @override
$SnAttachmentCopyWith<$Res>? get thumbnail; $SnAttachmentCopyWith<$Res>? get thumbnail;
@override
$SnAttachmentCopyWith<$Res>? get video;
} }
/// @nodoc /// @nodoc
@ -1692,7 +1663,6 @@ class __$$SnPostPreloadImplCopyWithImpl<$Res>
$Res call({ $Res call({
Object? thumbnail = freezed, Object? thumbnail = freezed,
Object? attachments = freezed, Object? attachments = freezed,
Object? video = freezed,
}) { }) {
return _then(_$SnPostPreloadImpl( return _then(_$SnPostPreloadImpl(
thumbnail: freezed == thumbnail thumbnail: freezed == thumbnail
@ -1703,10 +1673,6 @@ class __$$SnPostPreloadImplCopyWithImpl<$Res>
? _value._attachments ? _value._attachments
: attachments // ignore: cast_nullable_to_non_nullable : attachments // ignore: cast_nullable_to_non_nullable
as List<SnAttachment?>?, as List<SnAttachment?>?,
video: freezed == video
? _value.video
: video // ignore: cast_nullable_to_non_nullable
as SnAttachment?,
)); ));
} }
} }
@ -1716,8 +1682,7 @@ class __$$SnPostPreloadImplCopyWithImpl<$Res>
class _$SnPostPreloadImpl implements _SnPostPreload { class _$SnPostPreloadImpl implements _SnPostPreload {
const _$SnPostPreloadImpl( const _$SnPostPreloadImpl(
{required this.thumbnail, {required this.thumbnail,
required final List<SnAttachment?>? attachments, required final List<SnAttachment?>? attachments})
required this.video})
: _attachments = attachments; : _attachments = attachments;
factory _$SnPostPreloadImpl.fromJson(Map<String, dynamic> json) => factory _$SnPostPreloadImpl.fromJson(Map<String, dynamic> json) =>
@ -1735,12 +1700,9 @@ class _$SnPostPreloadImpl implements _SnPostPreload {
return EqualUnmodifiableListView(value); return EqualUnmodifiableListView(value);
} }
@override
final SnAttachment? video;
@override @override
String toString() { String toString() {
return 'SnPostPreload(thumbnail: $thumbnail, attachments: $attachments, video: $video)'; return 'SnPostPreload(thumbnail: $thumbnail, attachments: $attachments)';
} }
@override @override
@ -1751,14 +1713,13 @@ class _$SnPostPreloadImpl implements _SnPostPreload {
(identical(other.thumbnail, thumbnail) || (identical(other.thumbnail, thumbnail) ||
other.thumbnail == thumbnail) && other.thumbnail == thumbnail) &&
const DeepCollectionEquality() const DeepCollectionEquality()
.equals(other._attachments, _attachments) && .equals(other._attachments, _attachments));
(identical(other.video, video) || other.video == video));
} }
@JsonKey(includeFromJson: false, includeToJson: false) @JsonKey(includeFromJson: false, includeToJson: false)
@override @override
int get hashCode => Object.hash(runtimeType, thumbnail, int get hashCode => Object.hash(runtimeType, thumbnail,
const DeepCollectionEquality().hash(_attachments), video); const DeepCollectionEquality().hash(_attachments));
/// Create a copy of SnPostPreload /// Create a copy of SnPostPreload
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@ -1779,8 +1740,7 @@ class _$SnPostPreloadImpl implements _SnPostPreload {
abstract class _SnPostPreload implements SnPostPreload { abstract class _SnPostPreload implements SnPostPreload {
const factory _SnPostPreload( const factory _SnPostPreload(
{required final SnAttachment? thumbnail, {required final SnAttachment? thumbnail,
required final List<SnAttachment?>? attachments, required final List<SnAttachment?>? attachments}) = _$SnPostPreloadImpl;
required final SnAttachment? video}) = _$SnPostPreloadImpl;
factory _SnPostPreload.fromJson(Map<String, dynamic> json) = factory _SnPostPreload.fromJson(Map<String, dynamic> json) =
_$SnPostPreloadImpl.fromJson; _$SnPostPreloadImpl.fromJson;
@ -1789,8 +1749,6 @@ abstract class _SnPostPreload implements SnPostPreload {
SnAttachment? get thumbnail; SnAttachment? get thumbnail;
@override @override
List<SnAttachment?>? get attachments; List<SnAttachment?>? get attachments;
@override
SnAttachment? get video;
/// Create a copy of SnPostPreload /// Create a copy of SnPostPreload
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.

View File

@ -165,16 +165,12 @@ _$SnPostPreloadImpl _$$SnPostPreloadImplFromJson(Map<String, dynamic> json) =>
? null ? null
: SnAttachment.fromJson(e as Map<String, dynamic>)) : SnAttachment.fromJson(e as Map<String, dynamic>))
.toList(), .toList(),
video: json['video'] == null
? null
: SnAttachment.fromJson(json['video'] as Map<String, dynamic>),
); );
Map<String, dynamic> _$$SnPostPreloadImplToJson(_$SnPostPreloadImpl instance) => Map<String, dynamic> _$$SnPostPreloadImplToJson(_$SnPostPreloadImpl instance) =>
<String, dynamic>{ <String, dynamic>{
'thumbnail': instance.thumbnail?.toJson(), 'thumbnail': instance.thumbnail?.toJson(),
'attachments': instance.attachments?.map((e) => e?.toJson()).toList(), 'attachments': instance.attachments?.map((e) => e?.toJson()).toList(),
'video': instance.video?.toJson(),
}; };
_$SnBodyImpl _$$SnBodyImplFromJson(Map<String, dynamic> json) => _$SnBodyImpl( _$SnBodyImpl _$$SnBodyImplFromJson(Map<String, dynamic> json) => _$SnBodyImpl(

View File

@ -6,22 +6,12 @@ import 'package:material_symbols_icons/symbols.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:styled_widget/styled_widget.dart'; import 'package:styled_widget/styled_widget.dart';
import 'package:surface/providers/sn_attachment.dart'; import 'package:surface/providers/sn_attachment.dart';
import 'package:surface/types/attachment.dart';
import 'package:surface/widgets/dialog.dart'; import 'package:surface/widgets/dialog.dart';
class AttachmentInputDialog extends StatefulWidget { class AttachmentInputDialog extends StatefulWidget {
final String? title; final String? title;
final bool? analyzeNow; final bool? analyzeNow;
final SnMediaType? mediaType; const AttachmentInputDialog({super.key, required this.title, this.analyzeNow = false});
final String pool;
const AttachmentInputDialog({
super.key,
required this.title,
required this.pool,
this.analyzeNow = false,
this.mediaType = SnMediaType.image,
});
@override @override
State<AttachmentInputDialog> createState() => _AttachmentInputDialogState(); State<AttachmentInputDialog> createState() => _AttachmentInputDialogState();
@ -30,18 +20,13 @@ class AttachmentInputDialog extends StatefulWidget {
class _AttachmentInputDialogState extends State<AttachmentInputDialog> { class _AttachmentInputDialogState extends State<AttachmentInputDialog> {
final _randomIdController = TextEditingController(); final _randomIdController = TextEditingController();
XFile? _file; XFile? _thumbnailFile;
double? _progress;
void _pickMedia() async { void _pickImage() async {
final picker = ImagePicker(); final picker = ImagePicker();
final result = switch (widget.mediaType) { final result = await picker.pickImage(source: ImageSource.gallery);
SnMediaType.image => await picker.pickImage(source: ImageSource.gallery),
SnMediaType.video => await picker.pickVideo(source: ImageSource.gallery),
_ => await picker.pickMedia(),
};
if (result == null) return; if (result == null) return;
setState(() => _file = result); setState(() => _thumbnailFile = result);
} }
bool _isBusy = false; bool _isBusy = false;
@ -61,20 +46,15 @@ class _AttachmentInputDialogState extends State<AttachmentInputDialog> {
if (!mounted) return; if (!mounted) return;
context.showErrorDialog(err); context.showErrorDialog(err);
} }
} else if (_file != null) { } else if (_thumbnailFile != null) {
try { try {
final place = await attach.chunkedUploadInitialize(await _file!.length(), _file!.name, widget.pool, null); final attachment = await attach.directUploadOne(
(await _thumbnailFile!.readAsBytes()).buffer.asUint8List(),
final attachment = await attach.chunkedUploadParts( _thumbnailFile!.path,
_file!, 'interactive',
place.$1, null,
place.$2,
analyzeNow: widget.analyzeNow ?? false, analyzeNow: widget.analyzeNow ?? false,
onProgress: (value) {
setState(() => _progress = value);
},
); );
if (!mounted) return; if (!mounted) return;
Navigator.pop(context, attachment); Navigator.pop(context, attachment);
} catch (err) { } catch (err) {
@ -87,7 +67,7 @@ class _AttachmentInputDialogState extends State<AttachmentInputDialog> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return AlertDialog( return AlertDialog(
title: Text(widget.title ?? 'attachmentInputDialog'.tr()), title: Text(widget.title ?? 'attachmentInputDialog').tr(),
content: Column( content: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
@ -106,33 +86,22 @@ class _AttachmentInputDialogState extends State<AttachmentInputDialog> {
const Gap(24), const Gap(24),
Text('attachmentInputNew').tr().fontSize(14), Text('attachmentInputNew').tr().fontSize(14),
Card( Card(
child: Column( child: ListTile(
children: [
ListTile(
contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
leading: const Icon(Symbols.add_photo_alternate), leading: const Icon(Symbols.add_photo_alternate),
trailing: const Icon(Symbols.chevron_right), trailing: const Icon(Symbols.chevron_right),
title: Text('addAttachmentFromAlbum').tr(), title: Text('addAttachmentFromAlbum').tr(),
subtitle: _file == null ? Text('unset').tr() : Text('waitingForUpload').tr(), subtitle: _thumbnailFile == null ? Text('unset').tr() : Text('waitingForUpload').tr(),
onTap: () { onTap: () {
_pickMedia(); _pickImage();
}, },
), ),
],
), ),
),
if (_isBusy)
LinearProgressIndicator(
value: _progress,
borderRadius: BorderRadius.all(Radius.circular(8)),
).padding(top: 16),
], ],
), ),
actions: [ actions: [
TextButton( TextButton(
onPressed: _isBusy onPressed: _isBusy ? null : () {
? null
: () {
Navigator.pop(context); Navigator.pop(context);
}, },
child: Text('dialogDismiss').tr(), child: Text('dialogDismiss').tr(),

View File

@ -28,7 +28,6 @@ import 'package:surface/types/attachment.dart';
import 'package:surface/types/post.dart'; import 'package:surface/types/post.dart';
import 'package:surface/types/reaction.dart'; import 'package:surface/types/reaction.dart';
import 'package:surface/widgets/account/account_image.dart'; import 'package:surface/widgets/account/account_image.dart';
import 'package:surface/widgets/attachment/attachment_item.dart';
import 'package:surface/widgets/attachment/attachment_list.dart'; import 'package:surface/widgets/attachment/attachment_list.dart';
import 'package:surface/widgets/dialog.dart'; import 'package:surface/widgets/dialog.dart';
import 'package:surface/widgets/link_preview.dart'; import 'package:surface/widgets/link_preview.dart';
@ -211,7 +210,6 @@ class PostItem extends StatelessWidget {
if (onDeleted != null) {} if (onDeleted != null) {}
}, },
).padding(horizontal: 12, top: 8, bottom: 8), ).padding(horizontal: 12, top: 8, bottom: 8),
if (data.preload?.video != null) _PostVideoPlayer(data: data).padding(horizontal: 12, bottom: 8),
Container( Container(
width: double.infinity, width: double.infinity,
margin: const EdgeInsets.only(bottom: 4, left: 12, right: 12), margin: const EdgeInsets.only(bottom: 4, left: 12, right: 12),
@ -295,7 +293,6 @@ class PostItem extends StatelessWidget {
if (onDeleted != null) onDeleted!(); if (onDeleted != null) onDeleted!();
}, },
).padding(horizontal: 12, vertical: 8), ).padding(horizontal: 12, vertical: 8),
if (data.preload?.video != null) _PostVideoPlayer(data: data).padding(horizontal: 12, bottom: 8),
if (data.type == 'question') _PostQuestionHint(data: data).padding(horizontal: 16, bottom: 8), if (data.type == 'question') _PostQuestionHint(data: data).padding(horizontal: 16, bottom: 8),
if (data.body['title'] != null || data.body['description'] != null) if (data.body['title'] != null || data.body['description'] != null)
_PostHeadline( _PostHeadline(
@ -1523,29 +1520,3 @@ class _PostGetInsightPopupState extends State<_PostGetInsightPopup> {
); );
} }
} }
class _PostVideoPlayer extends StatelessWidget {
final SnPost data;
const _PostVideoPlayer({required this.data});
@override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
borderRadius: const BorderRadius.all(Radius.circular(8)),
border: Border.all(
color: Theme.of(context).dividerColor,
width: 1,
),
),
child: AspectRatio(
aspectRatio: 16 / 9,
child: ClipRRect(
borderRadius: const BorderRadius.all(Radius.circular(8)),
child: AttachmentItem(data: data.preload!.video!, heroTag: 'post-video-${data.id}'),
),
),
);
}
}

View File

@ -95,7 +95,6 @@ class PostMediaPendingList extends StatelessWidget {
context: context, context: context,
builder: (context) => AttachmentInputDialog( builder: (context) => AttachmentInputDialog(
title: 'attachmentSetThumbnail'.tr(), title: 'attachmentSetThumbnail'.tr(),
pool: 'interactive',
analyzeNow: true, analyzeNow: true,
), ),
); );

View File

@ -2,6 +2,7 @@ PODS:
- bitsdojo_window_macos (0.0.1): - bitsdojo_window_macos (0.0.1):
- FlutterMacOS - FlutterMacOS
- connectivity_plus (0.0.1): - connectivity_plus (0.0.1):
- Flutter
- FlutterMacOS - FlutterMacOS
- croppy (0.0.1): - croppy (0.0.1):
- FlutterMacOS - FlutterMacOS
@ -142,7 +143,7 @@ PODS:
- HotKey - HotKey
- in_app_review (2.0.0): - in_app_review (2.0.0):
- FlutterMacOS - FlutterMacOS
- livekit_client (2.3.6): - livekit_client (2.3.5):
- flutter_webrtc - flutter_webrtc
- FlutterMacOS - FlutterMacOS
- WebRTC-SDK (= 125.6422.06) - WebRTC-SDK (= 125.6422.06)
@ -189,7 +190,7 @@ PODS:
DEPENDENCIES: DEPENDENCIES:
- bitsdojo_window_macos (from `Flutter/ephemeral/.symlinks/plugins/bitsdojo_window_macos/macos`) - bitsdojo_window_macos (from `Flutter/ephemeral/.symlinks/plugins/bitsdojo_window_macos/macos`)
- connectivity_plus (from `Flutter/ephemeral/.symlinks/plugins/connectivity_plus/macos`) - connectivity_plus (from `Flutter/ephemeral/.symlinks/plugins/connectivity_plus/darwin`)
- croppy (from `Flutter/ephemeral/.symlinks/plugins/croppy/macos`) - croppy (from `Flutter/ephemeral/.symlinks/plugins/croppy/macos`)
- device_info_plus (from `Flutter/ephemeral/.symlinks/plugins/device_info_plus/macos`) - device_info_plus (from `Flutter/ephemeral/.symlinks/plugins/device_info_plus/macos`)
- file_picker (from `Flutter/ephemeral/.symlinks/plugins/file_picker/macos`) - file_picker (from `Flutter/ephemeral/.symlinks/plugins/file_picker/macos`)
@ -243,7 +244,7 @@ EXTERNAL SOURCES:
bitsdojo_window_macos: bitsdojo_window_macos:
:path: Flutter/ephemeral/.symlinks/plugins/bitsdojo_window_macos/macos :path: Flutter/ephemeral/.symlinks/plugins/bitsdojo_window_macos/macos
connectivity_plus: connectivity_plus:
:path: Flutter/ephemeral/.symlinks/plugins/connectivity_plus/macos :path: Flutter/ephemeral/.symlinks/plugins/connectivity_plus/darwin
croppy: croppy:
:path: Flutter/ephemeral/.symlinks/plugins/croppy/macos :path: Flutter/ephemeral/.symlinks/plugins/croppy/macos
device_info_plus: device_info_plus:
@ -307,7 +308,7 @@ EXTERNAL SOURCES:
SPEC CHECKSUMS: SPEC CHECKSUMS:
bitsdojo_window_macos: 44e3b8fe3dd463820e0321f6256c5b1c16bb6a00 bitsdojo_window_macos: 44e3b8fe3dd463820e0321f6256c5b1c16bb6a00
connectivity_plus: 0a976dfd033b59192912fa3c6c7b54aab5093802 connectivity_plus: 18382e7311ba19efcaee94442b23b32507b20695
croppy: 25a638bd7d05411d8c697f481568f261037694fc croppy: 25a638bd7d05411d8c697f481568f261037694fc
device_info_plus: 1b14eed9bf95428983aed283a8d51cce3d8c4215 device_info_plus: 1b14eed9bf95428983aed283a8d51cce3d8c4215
file_picker: e716a70a9fe5fd9e09ebc922d7541464289443af file_picker: e716a70a9fe5fd9e09ebc922d7541464289443af
@ -333,7 +334,7 @@ SPEC CHECKSUMS:
HotKey: 400beb7caa29054ea8d864c96f5ba7e5b4852277 HotKey: 400beb7caa29054ea8d864c96f5ba7e5b4852277
hotkey_manager_macos: 1e2edb0c7ae4fe67108af44a9d3445de41404160 hotkey_manager_macos: 1e2edb0c7ae4fe67108af44a9d3445de41404160
in_app_review: a6a031b9acd03c7d103e341aa334adf2c493fb93 in_app_review: a6a031b9acd03c7d103e341aa334adf2c493fb93
livekit_client: 0ad107154753a5a76802d2222c040223ad049499 livekit_client: 91c68237edede55f8891a166a28c1daec8a1e4b1
media_kit_libs_macos_video: b3e2bbec2eef97c285f2b1baa7963c67c753fb82 media_kit_libs_macos_video: b3e2bbec2eef97c285f2b1baa7963c67c753fb82
media_kit_native_event_loop: 81fd5b45192b72f8b5b69eaf5b540f45777eb8d5 media_kit_native_event_loop: 81fd5b45192b72f8b5b69eaf5b540f45777eb8d5
media_kit_video: c75b07f14d59706c775778e4dd47dd027de8d1e5 media_kit_video: c75b07f14d59706c775778e4dd47dd027de8d1e5

View File

@ -214,14 +214,6 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.4.3" version: "0.4.3"
chalkdart:
dependency: transitive
description:
name: chalkdart
sha256: "08c910ee341fcdd1e46f20ddce59b13c1d85f5d97f2fd2f12014c46ede670e40"
url: "https://pub.dev"
source: hosted
version: "2.3.2"
characters: characters:
dependency: transitive dependency: transitive
description: description:
@ -274,10 +266,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: connectivity_plus name: connectivity_plus
sha256: "04bf81bb0b77de31557b58d052b24b3eee33f09a6e7a8c68a3e247c7df19ec27" sha256: "8a68739d3ee113e51ad35583fdf9ab82c55d09d693d3c39da1aebab87c938412"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "6.1.3" version: "6.1.2"
connectivity_plus_platform_interface: connectivity_plus_platform_interface:
dependency: transitive dependency: transitive
description: description:
@ -370,10 +362,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: device_info_plus name: device_info_plus
sha256: "72d146c6d7098689ff5c5f66bcf593ac11efc530095385356e131070333e64da" sha256: e3fc9a65820fef83035af8ee8c09004a719d5d1d54e6de978fcb0d84bbeb241a
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "11.3.0" version: "11.2.2"
device_info_plus_platform_interface: device_info_plus_platform_interface:
dependency: transitive dependency: transitive
description: description:
@ -498,10 +490,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: file_picker name: file_picker
sha256: cacfdc5abe93e64d418caa9256eef663499ad791bb688d9fd12c85a311968fba sha256: c9943dd7d702ab4199d199bc151a2d79c86db031a02ad84566dab58c494d2adc
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "8.3.2" version: "8.3.1"
file_saver: file_saver:
dependency: "direct main" dependency: "direct main"
description: description:
@ -838,10 +830,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: flutter_webrtc name: flutter_webrtc
sha256: e917067abeef2400e6a7a03db53a6e1418551e54809f18ab80447ac323eb77e4 sha256: "4c76069f724f79dc6e8739c8f16c2ee00ca30a1f8e3aa75c0e830f8183278f03"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.12.8" version: "0.12.7"
freezed: freezed:
dependency: "direct dev" dependency: "direct dev"
description: description:
@ -894,10 +886,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: go_router name: go_router
sha256: "04539267a740931c6d4479a10d466717ca5901c6fdfd3fcda09391bbb8ebd651" sha256: "9b736a9fa879d8ad6df7932cbdcc58237c173ab004ef90d8377923d7ad731eaa"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "14.8.0" version: "14.7.2"
google_fonts: google_fonts:
dependency: "direct main" dependency: "direct main"
description: description:
@ -942,10 +934,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: home_widget name: home_widget
sha256: "7430f7549d42cef2e729bd3c779de748b93f1eb78b1abfe6bca8fffd1cfce3e9" sha256: b313e3304c0429669fddf1286e1fbf61a64b873f38ba30b3eb890ef0d7560b12
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.7.0+1" version: "0.7.0"
hotkey_manager: hotkey_manager:
dependency: "direct main" dependency: "direct main"
description: description:
@ -1198,10 +1190,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: livekit_client name: livekit_client
sha256: "0cfb2f48eff7a93ea8927696dc6f218aebd2fcd1fcc1b1a7b2f53ff3597fdb52" sha256: "02b4653d903852d0ae86b15fbe4324747606dae6410fe860d0c07a11c79988de"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.3.6" version: "2.3.5"
logging: logging:
dependency: transitive dependency: transitive
description: description:
@ -1254,10 +1246,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: material_symbols_icons name: material_symbols_icons
sha256: "1403944c2a68dbdf934fca2b2115a372e70a4a185c136ab71a9d16e2108d0b14" sha256: "89aac72d25dd49303f71b3b1e70f8374791846729365b25bebc2a2531e5b86cd"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.2805.1" version: "4.2801.1"
media_kit: media_kit:
dependency: "direct main" dependency: "direct main"
description: description:
@ -1390,10 +1382,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: package_info_plus name: package_info_plus
sha256: "67eae327b1b0faf761964a1d2e5d323c797f3799db0e85aa232db8d9e922bc35" sha256: c447a3c3e7be4addf129b8f9ab6a4bd5d166b78918223e223b61fddf4d07e254
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "8.2.1" version: "8.2.0"
package_info_plus_platform_interface: package_info_plus_platform_interface:
dependency: transitive dependency: transitive
description: description:
@ -2259,10 +2251,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: webrtc_interface name: webrtc_interface
sha256: "10fc6dc0ac16f909f5e434c18902415211d759313c87261f1e4ec5b4f6a04c26" sha256: abec3ab7956bd5ac539cf34a42fa0c82ea26675847c0966bb85160400eea9388
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.2.1" version: "1.2.0"
win32: win32:
dependency: transitive dependency: transitive
description: description:

View File

@ -16,7 +16,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
# In Windows, build-name is used as the major, minor, and patch parts # In Windows, build-name is used as the major, minor, and patch parts
# of the product and file versions while build-number is used as the build suffix. # of the product and file versions while build-number is used as the build suffix.
version: 2.3.2+65 version: 2.3.2+64
environment: environment:
sdk: ^3.5.4 sdk: ^3.5.4